递归生成器和send()

Ric*_*eur 2 python recursion generator

有没有人知道send()在递归使用时如何与生成器一起工作?我希望将值传递给当前的生成器,然后可以将其传递给递归生成器......但似乎情况并非如此?一些示例代码:

def Walk(obj):
  recurse = (yield obj)
  if not recurse:
    print 'stop recurse:', recurse
    return

  if isinstance(obj, list):
    print 'is list:', obj
    for item in obj:
      print 'item loop:', item
      walker = Walk(item)
      for x in walker:
        print 'item walk:', x
        recurse = (yield x)
        print 'item walk recurse:', recurse
        walker.send(recurse)

root = ['a', ['b.0', ['b.0.0']]]

walker = Walk(root)
for i, x in enumerate(walker):
  print i, x
  print 'send true'
  walker.send(True)
Run Code Online (Sandbox Code Playgroud)

所需的输出应该是每个级别递归的每个值:

0 ['a', ['b.0', ['b.0.0']]]
1 'a'
2 ['b.0', ['b.0.0']]
3 'b.0'
4 ['b.0.0']
5 'b.0.0'
Run Code Online (Sandbox Code Playgroud)

最终发生的事情是:

0 ['a', ['b.0', ['b.0.0']]]
send true
is list: ['a', ['b.0', ['b.0.0']]]
item loop: a
item walk: a
item walk recurse: None
stop recurse: None
Run Code Online (Sandbox Code Playgroud)

看起来内部循环recurse = (yield)并不等待发送值.或者其他的东西.它还不是很清楚内循环recurse值是如何得到的None; 它的来电者打电话send().

最终,目标基本上是递归地遍历树结构,但让最顶层的调用者能够指定何时递归到子结构.例如,

walker = Walk(root)
for node in walker:
  if CriteriaMet(node):
    walker.send(True)
  else:
    walker.send(False)
Run Code Online (Sandbox Code Playgroud)

Jer*_*own 5

要实现的重要一点是send()也消耗!
来自http://docs.python.org/reference/expressions.html#generator.send:

恢复执行并将值"发送"到生成器函数中.value参数成为当前yield表达式的结果.send()方法返回生成器产生的下一个值,如果生成器退出而不产生另一个值,则引发StopIteration.当调用send()来启动生成器时,必须使用None作为参数调用它,因为没有可以接收该值的yield表达式.

这是一个快速重新编写代码以使其按预期输出的方法:

def Walk(obj):
  recurse = (yield obj)
  if not recurse:
    #print 'stop recurse:', recurse
    return

  if isinstance(obj, list):
    #print 'is list:', obj
    for item in obj:
      #print 'item loop:', item
      walker = Walk(item)

      recurse = None #first send must be None
      while True:
        try:
          x = walker.send(recurse)
        except StopIteration:
          break
        #print 'item walk:', x
        recurse = (yield x)
        #print 'item walk recurse:', recurse

root = ['a', ['b.0', ['b.0.0']]]

walker = Walk(root)
i = 0
x = walker.next()
while True:
  print i, x
  try:
    x = walker.send(True)
  except StopIteration:
    break
  i += 1
Run Code Online (Sandbox Code Playgroud)

输出:

0 ['a', ['b.0', ['b.0.0']]]
1 a
2 ['b.0', ['b.0.0']]
3 b.0
4 ['b.0.0']
5 b.0.0
Run Code Online (Sandbox Code Playgroud)