Рекурсия с использованием yield
есть ли способ смешать рекурсию и 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-loop:
def infinity(start):
while True:
yield start
start += 1
Я просто хочу знать, можно ли это сделать рекурсивно.
3 ответов
Да, вы можете сделать это:
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
- ing это, все, что вы делаете, это построить новый генератор, фактически не запуская тело функции или ничего не давая.
посмотреть PEP 380 для дальнейшего подробности.
в некоторых случаях может быть предпочтительнее использовать стек вместо рекурсии для генераторов. Это должно быть возможно переписать рекурсивный метод, используя стек и цикл 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'])
это не универсальное решение, но для некоторых генераторов вы можете получить хороший результат, заменяющий обработку стека для рекурсии.
поэтому в основном вам просто нужно добавить цикл for В где вам нужно вызвать свою функцию рекурсивно. Это относится к Python 2.7.