Генераторный набор в цикле перезаписывается - PullRequest
1 голос
/ 04 мая 2019

Я пытаюсь написать функцию для построения сплющенного дерева генераторов на основе списка.Поэтому, если у меня есть список элементов, я хочу начать с пустого генератора и вызвать функцию для первого элемента и пустого генератора, а затем вызвать функцию для второго элемента и вывод первого вызова функции, а затемвызов функции для третьего элемента и вывод второго вызова функции и т. д. Важно отметить, что я не хочу на самом деле оценивать что-либо до тех пор, пока 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

UNFORTUNATELY , когда я пытаюсьиспользовать цикл, чтобы заставить это работать на tail с произвольной длиной:

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

Это не работает. Вместо этого переменная tree устанавливается на результат fooвызвал пустую возможность, как будто это была единственная оцененная вещь! Я понятия не имею, что происходит, хотя у меня есть догадка , что это потому, что есть указатель или что-то еще.

Буду признателен за любую помощь!

Ответы [ 2 ]

2 голосов
/ 05 мая 2019

Звонок на рекурсию понятен здесь:

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 голос
/ 05 мая 2019

Чтобы избежать проблемы с областью действия генератора, вы можете создать функцию, которая будет иметь собственную область действия:

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)
...