【问题标题】:Recursion using yield使用 yield 递归
【发布时间】:2020-12-02 15:08:36
【问题描述】:

有没有办法混合递归和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

我只是想知道这是否可以递归完成。

【问题讨论】:

  • 查看 [这里][1] 以获得我不久前提出的这个问题的一个很好的答案。 [1]:stackoverflow.com/questions/5704220/…
  • 注意:正确的做法是使用itertools.count,而不是推出自己的解决方案,基于循环或其他。
  • @PetrViktorin 这只是一个例子,生成无限数根本不​​是真正的问题

标签: python python-2.7 recursion yield


【解决方案1】:

是的,您可以这样做:

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-ing 它,你所做的只是构建一个新的生成器,而不实际运行函数体或产生任何东西。

更多详情请参阅PEP 380

【讨论】:

  • 但似乎yield from仍有递归限制:(
  • 总会有递归限制
【解决方案2】:

在某些情况下,对生成器使用堆栈而不是递归可能更可取。应该可以使用堆栈和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'])

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

【讨论】:

  • 不错的答案! python 2.7中的yield不能真正与递归一起使用,但是通过手动管理堆栈可以获得相同的效果。
【解决方案3】:

所以基本上你只需要在需要递归调用函数的地方添加一个for循环。这适用于 Python 2.7。

【讨论】:

  • 虽然这个答案需要更多细节,但实际上符合 Sven Marnach 接受的答案,请看他的第一段代码...
【解决方案4】:
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?尽量不要留下仅代码的答案...稍微澄清和解释将有助于将事情放在上下文中并更好地理解您的答案
  • for i in lprint(a): print(i)
  • 为什么不编辑答案,让它更清楚?您可以通过单击答案下方的小edit 标签或单击here 来完成。另外,正如我所说,尝试添加一些解释,说明如何以及为什么解决问题
猜你喜欢
  • 2016-05-10
  • 2020-06-16
  • 1970-01-01
  • 2013-05-24
  • 2014-01-18
  • 2011-03-21
  • 2013-05-24
相关资源
最近更新 更多