Kürzlich habe ich eine Funktion geschrieben, um bestimmte Sequenzen mit nicht trivialen Einschränkungen zu generieren. Das Problem kam mit einer natürlichen rekursiven Lösung. Nun kommt es vor, dass selbst bei relativ kleinen Eingaben die Sequenzen mehrere Tausend sind. Daher würde ich meinen Algorithmus lieber als Generator verwenden, anstatt ihn zum Füllen einer Liste mit allen Sequenzen zu verwenden.
Hier ist ein Beispiel. Angenommen, wir möchten alle Permutationen eines Strings mit einer rekursiven Funktion berechnen. Der folgende naive Algorithmus verwendet ein zusätzliches Argument 'Speicher' und fügt ihm eine Permutation hinzu, wenn er eine findet:
def getPermutations(string, storage, prefix=""):
if len(string) == 1:
storage.append(prefix + string) # <-----
else:
for i in range(len(string)):
getPermutations(string[:i]+string[i+1:], storage, prefix+string[i])
storage = []
getPermutations("abcd", storage)
for permutation in storage: print permutation
(Bitte kümmern Sie sich nicht um Ineffizienz, dies ist nur ein Beispiel.)
Jetzt möchte ich meine Funktion in einen Generator verwandeln, dh eine Permutation ergeben, anstatt sie an die Speicherliste anzuhängen:
def getPermutations(string, prefix=""):
if len(string) == 1:
yield prefix + string # <-----
else:
for i in range(len(string)):
getPermutations(string[:i]+string[i+1:], prefix+string[i])
for permutation in getPermutations("abcd"):
print permutation
Dieser Code funktioniert nicht (die Funktion verhält sich wie ein leerer Generator).
Vermisse ich etwas Gibt es eine Möglichkeit, den obigen rekursiven Algorithmus in einen Generator umzuwandeln, ohne ihn durch einen iterativen zu ersetzen ?
yield from getPermutations(string[:i] + string[i+1:])
, was in vielerlei Hinsicht effizienter ist!yield from
müssen Sie das Akkumulatorargument (prefix
) verwenden.string[i],string[:i]+string[i+1:]
Paare zurückgibt . Dann wäre es:for letter,rest in first_letter_options(string): for perm in getPermuations(rest): yield letter+perm
Dies vermeidet die
len(string)
tiefe Rekursion und ist im Allgemeinen eine gute Möglichkeit, mit Generatoren innerhalb von Generatoren umzugehen:flatten
ermöglicht es uns, den Fortschritt in einem anderen Generator fortzusetzen, indem wir ihn einfachyield
bearbeiten, anstatt ihn zu durchlaufen undyield
jedes Element manuell zu bearbeiten.Python 3.3 ergänzt
yield from
die Syntax, die eine natürliche Delegierung an einen Subgenerator ermöglicht:quelle
Der innere Aufruf von getPermutations - es ist auch ein Generator.
Sie müssen dies mit einer for-Schleife durchlaufen (siehe @ MizardX-Posting, das mich um Sekunden verdrängt hat!)
quelle