'reduce' 应用三参数函数的列表的 pythonic 方法是什么?

Which is the pythonic way to 'reduce' a list applying a three-parameter function?

我已经习惯了 FP/haskell-like reduce 的概念,已经在 Python 中实现为内置库:

from functools import reduce
lst = [1,2,3,4,5]
reduce(lambda x, y: x*y, lst)
# result: 120

然而,这不适用于具有三个参数的 lambda:

from functools import reduce
lst = [1,2,3,4,5]
reduce(lambda x, y, z: x*y+z, lst)
# expected result: (1*2+3) * 4 + 5
# actual result: 
#   TypeError: <lambda>() missing 1 required positional argument: 'z'

基本原理是保持“前一个结果是下一个第一个参数”的规则。

是否有内置实现或clever/simple功能构造组合来实现这样的目标?


ps。当心pseudo-duplicates

一个相当简单的实现是

import inspect


def nreduce(fn, lst):
    nargs = len(inspect.signature(fn).parameters)
    args = list(lst)
    while len(args) >= nargs:
        next_args = [args.pop(0) for x in range(nargs)]
        args.insert(0, fn(*next_args))
    return args


lst = [1, 2, 3, 4, 5]
print(nreduce(lambda x, y, z: x * y + z, lst))

如果你想花哨的话,你可以使用一个deque来让刚刚减少的值插入到列表的左边更快:

import collections

def nreduce(fn, lst):
    nargs = len(inspect.signature(fn).parameters)
    arg_queue = collections.deque(lst)
    while len(arg_queue) >= nargs:
        next_args = [arg_queue.popleft() for x in range(nargs)]
        arg_queue.appendleft(fn(*next_args))
    return list(arg_queue)

如果 lst 的长度不能被 fn 的元数整除,剩余的元素也将被 returned。

还有一个实现可以与任何可迭代对象一起工作,而无需复制到其他列表或队列中(不会 return lst 的其余部分,只是它消耗的价值):

def nreduce(fn, lst):
    nargs = len(inspect.signature(fn).parameters)
    lst_iter = iter(lst)
    next_args = []
    while True:
        try:
            while len(next_args) < nargs:
                next_args.append(next(lst_iter))
        except StopIteration:
            break
        next_args = [fn(*next_args)]
    return next_args