这是针对这种情况的非 itertools 方法。
首先想象一下,有一个版本一次functools.reduce
从一个可迭代对象中获取 3 个项目。我们称这个假设函数为reduce3
。
如果存在,我们可以执行以下操作:
reduce3(lambda a, b, c: (a+b)*c, numbers)
如果我们要查看这个操作的中间结果,我们会得到类似的结果:
1, 2, 3, 4, 5, 6 # Initial list
9, 4, 5, 6 # Step 1
65, 6 # Step 2
(65 + 6) * ?? # Step 3
所以这几乎是我们想要的,除了在第 3 步中没有第 3 项要乘以。事实上,这将发生在任何偶数长度的列表中。1
那么,如果它的长度是偶数,那么让我们将 a 添加到列表中:
if not len(numbers) % 2:
numbers.append(1)
在此之后,第三步将是:
(65 + 6)*1
正确答案为 71。
不幸的是,这个神奇的功能并不存在。但是,我们可以修改原始列表以模仿此功能。我们只需要获取数字列表并将连续的数字对(不包括第一个元素)分组为元组。此外,如果列表是偶数长度,我们需要将元素添加1
到末尾。
本质上,让我们编写一个函数preprocess()
来[1, 2, 3, 4, 5, 6]
变成[1, (2, 3), (4, 5), (6, 1)]
.
def preprocess(myList):
my_output = [myList[0], *zip(numbers[1::2], numbers[2::2])]
if not len(myList) % 2:
my_output.append((myList[-1], 1))
return my_output
print(preprocess(numbers))
#[1, (2, 3), (4, 5), (6, 1)]
现在我们可以reduce
处理列表:
from functools import reduce
result = reduce(lambda a, b: (a+b[0])*b[1], preprocess(numbers))
print(result)
#71
需要 2 个输入-reducer
一个数字和一个元组。它将数字添加到元组的第一个元素,并将结果乘以第二个元素。结果是另一个数字,然后将其传递给下一个reduce操作。
更新
这里是一个通用的实现reduceN
。由传入的函数的N
长度决定,因此可以推广到任意数量的函数。
from itertools import islice # couldn't get away from itertools this time
def reduceN(functions, iterable, initializer=None):
it = iter(iterable)
n = len(functions)
if initializer is None:
value = next(it)
else:
value = initializer
elements = list(islice(it, n))
while(elements):
for fn, el in zip(functions, elements):
value = fn(value, el)
elements = list(islice(it, n))
return value
我们可以使用它来循环应用任意数量的函数。所以原来的例子:
from operator import add, mul
numbers = [1, 2, 3, 4, 5, 6]
functions = [add, mul]
print(reduceN(functions, numbers))
#71
如果我们从 中删除最后一个元素numbers
:
print(reduceN(functions=functions, iterable=[1, 2, 3, 4, 5]))
#65