如何用Python编写foldr(右折叠)生成器?
Python的如何用Python编写foldr(右折叠)生成器?,python,recursion,fold,Python,Recursion,Fold,Python的reduce是一个左折叠,这意味着它是尾部递归的,它的使用可以被巧妙地重写为一个循环。但是,Python没有用于执行正确折叠的内置函数。由于右折叠最自然地使用递归编写(Python不像函数语言那样喜欢递归),因此我对编写生成器方面的右折叠(foldr)感兴趣 如何做到这一点?非常具体地说,如何在Python2.7中实现这一点 编辑:我应该提到,foldr的一个好处是,您有时可以在无限列表上进行折叠,而不会有生吞活剥堆栈的风险。我希望看到保留此属性的答案 例如,Haskell的fol
reduce
是一个左折叠,这意味着它是尾部递归的,它的使用可以被巧妙地重写为一个循环。但是,Python没有用于执行正确折叠的内置函数。由于右折叠最自然地使用递归编写(Python不像函数语言那样喜欢递归),因此我对编写生成器方面的右折叠(foldr
)感兴趣
如何做到这一点?非常具体地说,如何在Python2.7中实现这一点
编辑:我应该提到,foldr
的一个好处是,您有时可以在无限列表上进行折叠,而不会有生吞活剥堆栈的风险。我希望看到保留此属性的答案
例如,Haskell的foldr
在输入和输出方面都是惰性的,可以允许短路“阶跃”函数来处理长/无限输入:
foldr (&&) True (repeat False) -- gives False
如果给定itertools.repeat(某些值)
,则在输入端使用list
/reversed
/等的任何Python变体都将挂起
请注意,在同一示例中,Python的reduce
由于严格性而受阻:
reduce(lambda x, y: x and y, itertools.repeat(False), True) # hangs
因此,python中的一个简单生成器(没有适当的错误检查): e、 g: 与文档中的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
[编辑]
因此,纯粹作为一种思维练习,没有什么实际价值,只要你折叠的功能之间有一些合作,就有可能推迟。。。e、 g:
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
在无限列表中折叠或
,在找到真值时返回
>>> print(foldr(lambda a, b: bool(a) or bool(b), forever(), False))
True
您必须捕获适当的异常,但应该知道如何迭代执行:
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)
如果要使用生成器定义函数,为什么不使用以下命令
def foldr(op, lst):
return reduce(op, reversed(lst))
我想这就是你想要的:
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
的双重调用而出现的副作用函数会让人感到惊讶,但这应该足以让您有一个想法。我想为了做到这一点,您的生成器必须yield
生成一个yield
s生成一个。。。。如果传递的参数是一个空列表,yield
s初始值。。。坦白说,这听起来有点让人头疼。。。Python可以使用递归,只要它具有合理的深度,但我不能完全确定递归和生成器是否能很好地协同工作……haskell foldr的定义就是reduce,foldl将在无限列表中失败。Python的reduce
是一个左折叠,所以它就像haskell的foldl'
(注意素数)。是的,foldl
及其变体将在无限列表中失败,这就是为什么人们可能希望使用正确的折叠。要理解,不幸的是,函数调用在python中并不懒惰,因此如果要将foldr
实现为f(x,f(x,f(x,…))
则所有函数都将立即得到评估,因此即使函数定义为saydef lor(a,b):a或b
,但或是惰性的b
参数不是惰性的,也会在无限列表中失败。@achampion谢谢。在研究这个问题/答案的过程中,我意识到了这一点。在选择一个答案之前,我会给出更多的时间思考。如果代码> L>代码>本身就是一个生成器?@ 3NOCH,你不能逆转生成器/迭代器,所以它将取决于你认为什么是可接受的替代品?你添加一个例子,它如何在无限列表中工作?你怎么能在无限的列表上做FURDR?@那么,无限输入时会发生什么情况呢?在处理n
元素后,foldr是否会间歇性返回或短路?如果lst
是一个发生器呢?那么您需要对iterables进行操作<代码>l=列表(lst)
。您需要展开整个生成器才能执行foldr。
def foldr(op, lst):
return reduce(op, reversed(lst))
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))