我正在尝试编写一个函数来基于列表构建生成器的扁平树。因此,如果我有一个项目列表,我想从一个空的生成器开始,并在第一个项目和空的生成器上调用一个函数,然后在第二个项目上调用该函数,然后再调用第一个函数的输出,然后调用第三个项目上的函数以及第二个函数调用的输出,依此类推。 重要的是,在最终生成器上调用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
的结果,好像它是唯一要评估的东西!我不知道发生了什么,尽管我有一个预感,因为有指针或其他东西。
任何帮助将不胜感激!
答案 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)