Python的reduce
是左折,这意味着它是尾递归的,它的用法可以巧妙地重写为循环。但是,Python没有内置函数来进行正确的折叠。由于右侧折叠最自然地用递归编写(并且Python不像函数式语言那样递归),我有兴趣根据生成器编写正确的折叠(foldr
)
如何做到这一点?而且非常具体,如何在Python 2.7中完成?
编辑:我应该提到foldr
的好处之一就是你有时可以折叠无限列表,而不会有堆积活着的风险。我想看看保留这个属性的答案。
例如,Haskell的foldr
在输入和输出上都是惰性的,并且可以允许短路"步骤"用于处理长/无限输入的函数:
foldr (&&) True (repeat False) -- gives False
使用list
/ reversed
/ etc的任何Python变体。如果给定itertools.repeat(some_value)
,则输入将挂起。
请注意,Python的reduce
因为严格而在同一个例子中窒息:
reduce(lambda x, y: x and y, itertools.repeat(False), True) # hangs
答案 0 :(得分:2)
python中的一个简单生成器(没有适当的错误检查):
def foldr(op, lst):
l, x = reversed(list(lst)), None
for i in l:
if not x:
x = i
continue
x = op(x, i)
yield x
e.g:
>>> from operator import mul
>>> for i in foldr(mul, [1,2,3,4]):
... print i
24
24
12
几乎与文档中“大致相当”的reduce实现完全相同:
def foldr(function, iterable, initializer=None):
it = reversed(list(iterable))
if initializer is None:
try:
initializer = next(it)
except StopIteration:
raise TypeError('foldr() of empty sequence with no initial value')
accum_value = initializer
for x in it:
accum_value = function(accum_value, x)
yield accum_value
[编辑] 纯粹作为一种心灵的锻炼而且实际价值很小,只要你折叠的功能之间有一些合作就可以推迟......例如:
class Defer(object):
def __init__(self, func, *args):
self.func = func
self.args = args
def __bool__(self):
return self.func(*self.args)
def __int__(self):
return self.func(*self.args)
def foldr(function, iterable, initializer):
it = iter(iterable)
try:
return function(next(it), Defer(foldr, function, it, initializer))
except StopIteration:
return initializer
然后只要该函数转换为正确的类型,您就可以推迟计算,但是这对本机运算符不起作用,因此不确定它的确有用:
>>> print(foldr(lambda a, b: int(a)*int(b), [1,2,3,4], 1))
24
定义永久生成器:
from itertools import repeat
def forever():
yield False
yield True
for i in repeat(False):
yield i
在无限列表中折叠or
,在找到True
>>> print(foldr(lambda a, b: bool(a) or bool(b), forever(), False))
True
答案 1 :(得分:1)
您必须捕获适当的异常,但应该知道如何迭代地执行此操作:
def foldr(a, b, l):
if isinstance(l, Iterator):
it = reversed(list(l))
else:
it = reversed(l)
try:
nxt = next(it)
except StopIteration:
return
c = a(nxt, b)
stop = object()
while nxt is not stop:
yield c
nxt = next(it, stop)
c = a(nxt, c) if nxt is not stop else c
from operator import truediv
for c in (foldr(truediv, 1, [1, 2, 3, 4, 5, 6, 7, 8])):
print(c)
答案 2 :(得分:0)
如果要使用生成器定义函数,为什么不使用以下内容?
def foldr(op, lst):
return reduce(op, reversed(lst))
答案 3 :(得分:0)
我认为您需要这样的东西
def foldr(fn, seq, init):
it = iter(seq)
try:
x = next(it)
except StopIteration:
try:
for elem in init:
yield elem
except TypeError:
yield init
else:
try:
for elem in fn(x, foldr(fn, it, init)):
yield elem
except TypeError:
yield fn(x, foldr(fn, it, init))
这还不是完全可以用于生产,因为它将很快达到Python堆栈限制,并且由于双重调用fn
而在具有副作用功能的情况下令人惊讶,但是它足以给你一个主意。