Können Generatoren rekursiv sein?
Ich naiv versucht, erstellen Sie eine rekursive generator. Hat nicht funktioniert. Dies ist, was ich getan habe:
def recursive_generator(lis):
yield lis[0]
recursive_generator(lis[1:])
for k in recursive_generator([6,3,9,1]):
print(k)
Alles, was ich bekam, war das erste Element 6
.
Gibt es eine Möglichkeit, um diesen code zu arbeiten? Im wesentlichen die übertragung der yield
Befehl, um die Ebene nach oben in eine Rekursion Schema?
- Du bist nicht nachgeben, wenn Sie nennen Sie es noch einmal. Es trifft die erste Ausbeute, nicht sehen, eine weitere yield-Anweisung, und wird beendet.
- Sie müssen entweder
yield from another_generator()
oder die Rendite jedes element ein, indem man sich ausdrücklich in der while-Schleife. Obanother_generator()
ist oder nicht "rekursiv" in Ihrer Terminologie -, dass spielt keine Rolle. - Möglich, Duplikat der Python: wie man eine rekursive generator-Funktion
Du musst angemeldet sein, um einen Kommentar abzugeben.
Versuchen Sie dies:
Sollte ich darauf hinweisen, das nicht funktioniert, weil ein Fehler in der Funktion. Es sollte wahrscheinlich ein Häkchen, das
lis
ist nicht leer, wie unten gezeigt:Den Fall, Sie sind auf Python 2.7 und nicht
yield from
, prüfen Sie diese Frage aus., Warum Ihr code hat nicht die Arbeit machen
In Ihrem code, der generator-Funktion:
Die zweite Instanz der iterator, der einen rekursiv erstellt, ist nie der Iteration. Das ist, warum Sie bekam nur das erste Element der Liste.
Einer generator-Funktion ist nützlich, um automatisch ein iterator-Objekt (ein Objekt implementiert, dass das iterator-Protokoll), aber dann müssen Sie Durchlaufen es: entweder manuell Aufruf der
next()
- Methode auf das Objekt oder durch eine loop-Anweisung, wird automatisch der iterator-Protokoll.So können wir rekursiv aufrufen ein generator?
Die Antwort ist ja. Nun zurück zu deinem code, wenn Sie wirklich möchten, tun Sie dies mit einer generator-Funktion, ich denke, Sie könnten versuchen:
Hinweis: die Einzelteile in umgekehrter Reihenfolge, so dass Sie möglicherweise verwenden möchten
some_list.reverse()
vor dem Aufruf der generator das erste mal.Die wichtige Sache zu beachten in diesem Beispiel ist: die generator-Funktion ruft rekursiv sich selbst in eine für Schleife, das sieht ein iterator und verwendet automatisch die iteration Protokoll auf, also es wird tatsächlich Werte aus.
Dies funktioniert, aber ich denke, das ist wirklich nicht nützlich. Wir sind mit einem generator-Funktion zum iterieren über eine Liste und Holen Sie die Elemente aus, ein zu einer Zeit, aber... die Liste ist durchsuchbar selbst, so dass keine Notwendigkeit für Generatoren!
Natürlich verstehe ich es, das ist nur ein Beispiel, vielleicht gibt es nützliche Anwendungen dieser Idee.
Ein weiteres Beispiel
Let ' s recycle dem vorherigen Beispiel (für lazyness). Können sagen, wir müssen drucken Sie die Elemente in einer Liste hinzufügen zu jedem Artikel die Anzahl der vorherigen Elemente (nur ein willkürliches Beispiel, nicht unbedingt sinnvoll).
Würde der code sein:
Nun, wie Sie sehen können, wird die generator-Funktion ist tatsächlich etwas tun, vor dem zurückgeben der Liste der Elemente UND die Verwendung von Rekursion beginnt Sinn zu machen. Dennoch, nur ein dummes Beispiel, aber Sie bekommen die Idee.
Hinweis: natürlich, in diesem dummen Beispiel die Liste wird voraussichtlich nur zahlen enthalten. Wenn Sie wirklich wollen, zu gehen und versuchen es zu brechen, einfach ein string in some_list und Spaß haben. Wieder, dies ist nur ein Beispiel, nicht Produktion code!
Rekursive Generatoren sind nützlich für die Traversierung, nicht-linearen Strukturen. Lassen Sie zum Beispiel ein binärer Baum ist entweder Keiner oder ein Tupel von Wert, Links Baum, rechts Baum. Eine rekursive generator ist der einfachste Weg, um den Besuch aller Knoten. Beispiel:
Bearbeiten: ersetzen
if tree
mitif tree is not None
zu fangen, die anderen falsche Werte als Fehler.Edit 2: darum, die rekursive Aufrufe in try: Klausel (Kommentar von @jpmc26).
Für schlechte Knoten, der code oben nur die Protokolle der ValueError und geht weiter. Wenn, zum Beispiel,
(9,None,None)
wird ersetzt durch(9,None)
ist, wird der AusgangEher typisch wäre zu reraisen, nach der Anmeldung, wodurch der output sein
Wird die traceback-gibt den Pfad von der Wurzel zu den schlechten Knoten. Könnte man wrap-das original
visit(tree)
nennen zu reduzieren, wird die traceback-der Pfad: (root, rechts, rechts, Links, Links).Wenn die rekursive Aufrufe enthalten sind in den versuchen: Klausel, die den Fehler recaught, relogged, und reraised auf jeder Ebene des Baumes.
Die mehrere logging-Berichte sind wahrscheinlich mehr Lärm als helfen. Will man den Pfad der schlechten Knoten, könnte es am einfachsten sein, wickeln Sie jeden rekursiven Aufruf in eigenen versuchen: Klausel und werfen eine neue ValueError auf jeder Ebene, mit dem konstruierter Pfad so weit.
Fazit: wenn man nicht eine Ausnahme für die Ablaufsteuerung (as kann getan werden, mit IndexError, zum Beispiel) das Vorhandensein und die Platzierung von versuchen: Aussagen, hängt davon ab, die Fehler gemeldet haben will.
else
block auf dertry
/except
; es wäre einfacher zu bewegen, dass code in dietry
block, wäre es nicht?Bis zu Python 3.4 der generator-Funktion verwendet, um zu erhöhen
StopIteration
Ausnahme, wenn es fertig ist.Für den rekursiven Fall sonstiger Ausnahmen (z.B.
IndexError
) erhoben werden früher alsStopIteration
daher wir es manuell hinzufügen.Beachten Sie, dass
for
Schleife fangenStopIteration
Ausnahme.Mehr über dieses hier
Ja, Sie haben rekursive Generatoren. Aber Sie leiden unter den gleichen Rekursionstiefe einschränken als andere rekursive Funktionen.
Diese Schleife wird auf etwa 3000 (für mich), bevor es abstürzt.
Jedoch mit einigen Tricks können Sie eine Funktion erstellen, die speist einen generator zu sich selbst. Dies ermöglicht es Ihnen zu schreiben-Generatoren wie Sie sind rekursiv aber nicht: https://gist.github.com/3noch/7969f416d403ba3a54a788b113c204ce