Recentemente ho scritto una funzione per generare determinate sequenze con vincoli non banali. Il problema è arrivato con una soluzione ricorsiva naturale. Ora succede che, anche per input relativamente piccoli, le sequenze sono diverse migliaia, quindi preferirei usare il mio algoritmo come generatore invece di usarlo per riempire un elenco con tutte le sequenze.
Ecco un esempio. Supponiamo di voler calcolare tutte le permutazioni di una stringa con una funzione ricorsiva. Il seguente algoritmo ingenuo accetta un argomento aggiuntivo "archiviazione" e gli aggiunge una permutazione ogni volta che ne trova uno:
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
(Per favore non preoccuparti dell'inefficienza, questo è solo un esempio.)
Ora voglio trasformare la mia funzione in un generatore, ovvero produrre una permutazione invece di aggiungerla all'elenco di archiviazione:
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
Questo codice non funziona (la funzione si comporta come un generatore vuoto).
Mi sto perdendo qualcosa? C'è un modo per trasformare l'algoritmo ricorsivo di cui sopra in un generatore senza sostituirlo con uno iterativo ?
yield from getPermutations(string[:i] + string[i+1:])
, che è più efficiente in molti modi!