Рекурсия с использованием урожая
Есть ли способ смешивания рекурсии и оператора 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
Да, вы можете сделать это:
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
-в ее, все, что вы делаете, это сборка нового генератора без фактического запуска тела функции или получения чего-либо.
Подробнее см. PEP 380.
Ответ 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'])
Это не универсальное решение, но для некоторых генераторов вы можете получить хороший результат, заменив обработку стека на рекурсию.
Ответ 3
def lprint(a):
if isinstance(a, list):
for i in a:
yield from lprint(i)
else:
yield a
a = [[1, [2, 3], 4], [5, 6, [7, 8, [9]]]]
for i in lprint(b):
print(i)
Ответ 4
Поэтому вам просто нужно добавить цикл for в , где вам нужно будет рекурсивно вызывать вашу функцию. Это относится к Python 2.7.