发电机组循环被覆盖

时间:2019-05-04 20:51:34

标签: python pointers iterator generator

我正在尝试编写一个函数来基于列表构建生成器的扁平树。因此,如果我有一个项目列表,我想从一个空的生成器开始,并在第一个项目和空的生成器上调用一个函数,然后在第二个项目上调用该函数,然后再调用第一个函数的输出,然后调用第三个项目上的函数以及第二个函数调用的输出,依此类推。 重要的是,在最终生成器上调用next之前,我并不想实际评估任何内容

因此,如果我们在列表和生成器上调用的函数称为foo,(显然,它也输出生成器),则项目列表为list ...

现在,我拥有的是一个看起来像这样的原型:

>>> tree = iter([{}])
>>> tree = chain.from_iterable((foo(list[0], p) for p in tree))
>>> tree = chain.from_iterable((foo(list[1], p) for p in tree))
>>> tree = chain.from_iterable((foo(list[2], p) for p in tree))
>>> list(tree)

那确实有效。它可以正确评估所有内容,最重要的是不会不必要地评估任何内容(以整数开头的行在实际存在时会打印出日志:

>>> next(tree)
Called on 0
Called on 1
Called on 2
Result A
>>> next(tree)
Called on 1
Called on 2
Result B

不可思议地,当我尝试使用循环使它在具有任意长度的tail上工作时:

tree = iter([{}])
for item in list:
    tree = chain.from_iterable((foo(item, p) for p in tree))

它不起作用。相反,将tree变量设置为{<1> empty 可能性上调用的foo的结果,好像它是唯一要评估的东西!我不知道发生了什么,尽管我有一个预感,因为有指针或其他东西。

任何帮助将不胜感激!

2 个答案:

答案 0 :(得分:2)

此处的递归要求很明确:

import itertools as it


def lazy_reduce(list_, tree_base, i=None):
  if i is None:
    i = len(list_)

  if i < 0:
    return iter(tree_base)

  return it.chain.from_iterable(
    foo(list_[i], p)
    for p in lazy_reduce(list_, tree_base, i - 1)
  )

答案 1 :(得分:1)

为避免生成器范围出现问题,您可以创建一个具有自己范围的函数:

def add_item(tree, item):
    return chain.from_iterable((foo(item, p) for p in tree))

tree = iter([{}])
for item in list:
    tree = add_item(tree, item)