Niedawno napisałem funkcję do generowania pewnych sekwencji z nietrywialnymi ograniczeniami. Problem pojawił się z naturalnym rozwiązaniem rekurencyjnym. Teraz zdarza się, że nawet dla stosunkowo niewielkich danych wejściowych sekwencje są po kilka tysięcy, dlatego wolałbym użyć mojego algorytmu jako generatora, zamiast wypełniać listę wszystkimi sekwencjami.
Oto przykład. Załóżmy, że chcemy obliczyć wszystkie permutacje łańcucha z funkcją rekurencyjną. Poniższy naiwny algorytm pobiera dodatkowy argument `` pamięć '' i dołącza do niego permutację, gdy tylko ją znajdzie:
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
(Proszę nie przejmować się nieefektywnością, to tylko przykład).
Teraz chcę przekształcić moją funkcję w generator, tj. Aby uzyskać permutację zamiast dołączać ją do listy pamięci:
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
Ten kod nie działa (funkcja zachowuje się jak pusty generator).
Czy coś mi brakuje? Czy istnieje sposób na przekształcenie powyższego algorytmu rekurencyjnego w generator bez zastępowania go algorytmem iteracyjnym ?
yield from getPermutations(string[:i] + string[i+1:])
, co jest bardziej wydajne na wiele sposobów!