'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
我已经习惯了 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