Rekurencja za pomocą yield

Czy jest jakiś sposób na zmieszanie rekurencji z yield stwierdzeniem? Na przykład generator liczb nieskończonych (wykorzystujący rekurencję) byłby czymś w rodzaju:

def infinity(start):
    yield start
    # recursion here ...

>>> it = infinity(1)
>>> next(it)
1
>>> next(it)
2

Próbowałem:

def infinity(start):
    yield start
    infinity(start + 1)

I

def infinity(start):
    yield start
    yield infinity(start + 1)

Ale żaden z nich nie zrobił tego, co chciałem, pierwszy zatrzymał się po tym, jak dał start, a drugi dał start, potem generator i zatrzymał się.

Uwaga: proszę, wiem, że możesz to zrobić za pomocą pętli while:

def infinity(start):
    while True:
        yield start
        start += 1

Chcę tylko wiedzieć, czy to można wykonać rekurencyjnie.

Author: juliomalegria, 2012-01-24

3 answers

Tak, możesz to zrobić:

def infinity(start):
    yield start
    for x in infinity(start + 1):
        yield x

Spowoduje to błąd po osiągnięciu maksymalnej głębokości rekurencji.

Począwszy od Pythona 3.3, będziesz mógł używać

def infinity(start):
    yield start
    yield from infinity(start + 1)

Jeśli wywołasz funkcję generatora rekurencyjnie bez zapętlania jej lub yield from - ing it, wszystko co robisz, to zbudujesz nowy generator, bez rzeczywistego uruchamiania ciała funkcji lub dawania czegokolwiek.

[[3]} Zobacz PEP 380 aby uzyskać więcej informacji.
 101
Author: Sven Marnach,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2016-07-13 22:26:37

W niektórych przypadkach lepiej byłoby użyć stosu zamiast rekurencji dla generatorów. Powinno być możliwe przepisanie metody rekurencyjnej przy użyciu stosu i pętli while.

Oto przykład metody rekurencyjnej, która wykorzystuje wywołanie zwrotne i może być przepisana za pomocą logiki stosu:

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)

Powyższa metoda przemierza drzewo węzłów, gdzie każdy węzeł ma tablicę children, która może zawierać węzły potomne. Gdy każdy węzeł jest napotkany, wywołanie zwrotne jest wydawane i bieżący węzeł jest przekazywany za to.

Metoda może być użyta w ten sposób, wypisując pewne właściwości na każdym węźle.

def callback(node):
    print(node['id'])
traverse_tree(callback)

Zamiast tego użyj stosu i napisz metodę traversal jako generator

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

(zauważ, że jeśli chcesz mieć taką samą kolejność przechodzenia jak pierwotnie, musisz odwrócić kolejność dzieci, ponieważ pierwsze dziecko dołączone do stosu będzie ostatnim, które wyskoczy.)

Teraz możesz uzyskać takie samo zachowanie jak traverse_tree powyżej, ale z generatorem:

for node in iternodes():
    print(node['id'])

To nie jest uniwersalnym rozwiązaniem, ale dla niektórych generatorów można uzyskać ładny wynik zastępujący przetwarzanie stosu dla rekurencji.

 9
Author: t.888,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2018-06-18 13:19:51

Więc w zasadzie wystarczy dodać pętlę for w gdzie trzeba wywołać funkcję rekurencyjnie. Dotyczy to Pythona 2.7.

 -2
Author: Bubble Bubble Bubble Gut,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2016-12-28 21:45:38