使用收益递归


82

有什么方法可以混合使用递归和yield语句吗?例如,无限数生成器(使用递归)将类似于:

def infinity(start):
    yield start
    # recursion here ...

>>> it = infinity(1)
>>> next(it)
1
>>> next(it)
2

我试过了:

def infinity(start):
    yield start
    infinity(start + 1)

def infinity(start):
    yield start
    yield infinity(start + 1)

但是他们都没有做我想要的事情,第一个在屈服后停止start,第二个在屈服之后start,然后是发电机,然后停了下来。

注意:请知道,您可以使用while循环来做到这一点:

def infinity(start):
    while True:
        yield start
        start += 1

我只想知道这是否可以递归进行。


请参阅[here] [1],以获取有关我刚才提出的问题的良好答案。[1]:stackoverflow.com/questions/5704220/...
sizzzzlerz

注意:执行此操作的正确方法是使用itertools.count自己的解决方案,而不是基于循环或其他方式推出自己的解决方案。
彼得·维克托林

8
@PetrViktorin这只是一个例子,生成无限数根本不​​是真正的问题
juliomalegria 2012年

Answers:


153

是的,您可以这样做:

def infinity(start):
    yield start
    for x in infinity(start + 1):
        yield x

但是,一旦达到最大递归深度,这将出错。

从Python 3.3开始,您将可以使用

def infinity(start):
    yield start
    yield from infinity(start + 1)

如果您只是递归地调用生成器函数而不循环或生成yield from它,那么您要做的就是构建一个新的生成器,而无需实际运行函数主体或产生任何结果。

有关更多详细信息,请参见PEP 380


12
但是似乎yield from仍然存在递归限制:(
Jo So

2
始终会有递归限制
无线电控制的

12

在某些情况下,对于生成器,最好使用堆栈而不是递归。应该可以使用堆栈和while循环来重写递归方法。

这是一个使用回调并可以使用堆栈逻辑重写的递归方法的示例:

def traverse_tree(callback):
    # Get the root node from somewhere.
    root = get_root_node()
    def recurse(node):
        callback(node)
        for child in node.get('children', []):
            recurse(child)
    recurse(root)

上面的方法遍历一个节点树,其中每个节点都有一个children可能包含子节点的数组。遇到每个节点时,都会发出回调并将当前节点传递给它。

该方法可以通过这种方式使用,在每个节点上打印出一些属性。

def callback(node):
    print(node['id'])
traverse_tree(callback)

使用堆栈代替并将遍历方法编写为生成器

# A stack-based alternative to the traverse_tree method above.
def iternodes():
    stack = [get_root_node()]
    while stack:
        node = stack.pop()
        yield node
        for child in reversed(node.get('children', [])):
            stack.append(child)

(请注意,如果您想要与原始遍历顺序相同的遍历顺序,则需要颠倒子顺序,因为附加到堆栈的第一个子节点将是最后一个弹出的子节点。)

现在,您可以获得与traverse_tree上述相同的行为,但是使用了生成器:

for node in iternodes():
    print(node['id'])

这不是一种千篇一律的解决方案,但对于某些生成器,用堆栈处理代替递归可能会得到不错的结果。


3
好答案!python 2.7中的yield不能真正与递归一起使用,但是通过手动管理堆栈,您可以获得相同的效果。
00prometheus

0
def lprint(a):
    if isinstance(a, list):
        for i in a:
            yield from lprint(i)
    else:
        yield a

b = [[1, [2, 3], 4], [5, 6, [7, 8, [9]]]]
for i in lprint(b):
    print(i)

什么b啊 尽量不要只留下代码答案...一点澄清和解释将有助于把事情放到上下文中并更好地理解您的答案
Tomerikoo

对于i在LPRINT的(a):打印(ⅰ)
ЮрийБлинков

为什么不编辑答案,这样更清晰?您可以点击edit答案下方的小标签或点击此处进行操作。另外,正如我所说的,尝试添加一些有关如何以及为何解决该问题的解释
Tomerikoo

-3

因此,基本上,您只需要在需要递归调用函数的位置添加一个for循环即可。这适用于Python 2.7。


1
尽管此答案需要更多详细信息,但实际上与Sven Marnach接受的答案一致,请参见他的第一部分代码……
Coffee_Table
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.