如何使用列表理解来计算列表的累积乘积

时间:2020-06-07 00:34:23

标签: python list list-comprehension

我正在尝试将以下循环转换为理解力。

给问题一个input_list = [1, 2, 3, 4, 5] 返回一个列表,其中每个元素都是所有元素的倍数,直到该索引从左到右开始。

因此返回列表将为[1, 2, 6, 24, 120]

我有正常循环(并且正在工作):

l2r = list()
for i in range(lst_len):
    if i == 0:
        l2r.append(lst_num[i])
    else:
        l2r.append(lst_num[i] * l2r[i-1])

7 个答案:

答案 0 :(得分:10)

Python 3.8+解决方案:

lst = [1, 2, 3, 4, 5]

curr = 1
out = [(curr:=curr*v) for v in lst]
print(out)

打印:

[1, 2, 6, 24, 120]

其他解决方案(使用itertools.accumulate):

from itertools import accumulate

out = [*accumulate(lst, lambda a, b: a*b)]
print(out)

答案 1 :(得分:4)

好吧,您可以这样做(a)

import math

orig = [1, 2, 3, 4, 5]
print([math.prod(orig[:pos]) for pos in range(1, len(orig) + 1)])

这将生成您想要的:

[1, 2, 6, 24, 120]

基本上是通过运行从1到列表大小的计数器来工作的,在每个点上计算出该位置之前的所有项的乘积:

pos   values    prod
===  =========  ====
 1   1             1
 2   1,2           2
 3   1,2,3         6
 4   1,2,3,4      24
 5   1,2,3,4,5   120

(a)请记住,这在运行时效率较低,因为它会计算每个元素的完整产品(而不是缓存最近获得的产品)。您可以在 still 使代码更紧凑(通常是使用列表推导的原因)的同时避免这种情况,例如:

def listToListOfProds(orig):
    curr = 1
    newList = []
    for item in orig:
        curr *= item
        newList.append(curr)
    return newList

print(listToListOfProds([1, 2, 3, 4, 5]))

这显然不是列表理解,但是仍然具有优点,因为它不会使您需要计算的代码杂乱无章。

人们似乎经常不喜欢Python中的函数解决方案,这仅仅是因为该语言表达能力强,并且 诸如列表推导等功能都可以用最少的源代码完成很多工作。

但是,除了函数本身之外,此解决方案还具有单行列表理解的相同优势,因为它占用一行:-)

此外,您可以随时随意更改功能( )(例如,如果在更高版本的Python版本中找到了更好的方法),无需更改调用它的代码中所有不同的位置。

答案 2 :(得分:1)

如果一个迭代取决于较早版本的状态,则不应将此 变成列表理解!

如果目标是单一目标,那么@AndrejKesely的itertools.accumulate()是一个很好的解决方案(+1),因此有很多解决方案。这是滥用functools.reduce()的矿井:

from functools import reduce

lst = [1, 2, 3, 4, 5]

print(reduce(lambda x, y: x + [x[-1] * y], lst, [lst.pop(0)]))

但是就列表理解而言,@ AndrejKesely基于分配表达式的解决方案是错误的做法(-1)。这是一种更加独立的理解,不会渗入周围的范围:

lst = [1, 2, 3, 4, 5]

seq = [a.append(a[-1] * b) or a.pop(0) for a in [[lst.pop(0)]] for b in [*lst, 1]]

print(seq)

但这仍然是错误的选择!这是基于similar problem,它也由于错误的原因而被投票。

答案 3 :(得分:1)

递归函数可能会有所帮助。

input_list = [ 1, 2, 3, 4, 5]

def cumprod(ls, i=None):
    i = len(ls)-1 if i is None else i
    if i == 0:
        return 1
    return ls[i] * cumprod(ls, i-1)

output_list = [cumprod(input_list, i) for i in range(len(input_list))]

output_list 的值为 [1, 2, 6, 24, 120]


这个方法可以在python3.8中使用海象运算符进行压缩

input_list = [ 1, 2, 3, 4, 5]

def cumprod_inline(ls, i=None):
    return 1 if (i := len(ls)-1 if i is None else i) == 0 else ls[i] * cumprod_inline(ls, i-1)

output_list = [cumprod_inline(input_list, i) for i in range(len(input_list))]

output_list 的值为 [1, 2, 6, 24, 120]


因为您打算在列表推导中使用它,所以无需为 i 参数提供默认值。这样就无需检查 i 是否为 None

input_list = [ 1, 2, 3, 4, 5]

def cumprod_inline_nodefault(ls, i):
    return 1 if i == 0 else ls[i] * cumprod_inline_nodefault(ls, i-1)

output_list = [cumprod_inline_nodefault(input_list, i) for i in range(len(input_list))]

output_list 的值为 [1, 2, 6, 24, 120]


最后,如果你真的想把它保留在一个独立的列表理解行中,你可以按照方法说明 here 使用递归 lambda 调用

input_list = [ 1, 2, 3, 4, 5]

output_list = [(lambda func, x, y: func(func,x,y))(lambda func, ls, i: 1 if i == 0 else ls[i] * func(func, ls, i-1),input_list,i) for i in range(len(input_list))]

output_list 的值为 [1, 2, 6, 24, 120]

它完全是过度设计的,几乎难以辨认,但是嘿!它有效,只是为了好玩。

答案 4 :(得分:0)

对于您的列表,数字可能不是故意的,从1开始是连续的。但是对于故意使用该模式的情况,可以使用内置方法factorial()

from math import factorial

input_list = [1, 2, 3, 4, 5]
l2r = [factorial(i) for i in input_list]

print(l2r)

输出:

[1, 2, 6, 24, 120]

答案 5 :(得分:0)

软件包numpy具有许多内置的列表理解的快速实现。例如,要获得累积产品:

>>> import numpy as np
>>> np.cumprod([1, 2, 3, 4, 5])
array([  1,   2,   6,  24, 120])

上面的代码返回一个numpy的数组。如果您不熟悉numpy,则可能更喜欢仅获取普通的python列表:

>>> list(np.cumprod([1, 2, 3, 4, 5]))
[1, 2, 6, 24, 120]

答案 6 :(得分:0)

使用迭代工具和运算符:

from itertools import accumulate
import operator as op

ip_lst = [1,2,3,4,5]
print(list(accumulate(ip_lst, func=op.mul)))