Recentemente, escrevi uma função para gerar certas sequências com restrições não triviais. O problema veio com uma solução recursiva natural. Agora acontece que, mesmo para uma entrada relativamente pequena, as sequências são de vários milhares, portanto, eu preferiria usar meu algoritmo como um gerador em vez de usá-lo para preencher uma lista com todas as sequências.
Aqui está um exemplo. Suponha que desejamos calcular todas as permutações de uma string com uma função recursiva. O seguinte algoritmo ingênuo pega um argumento extra 'armazenamento' e anexa uma permutação a ele sempre que encontra um:
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
(Não se preocupe com a ineficiência, este é apenas um exemplo.)
Agora, quero transformar minha função em um gerador, ou seja, produzir uma permutação em vez de anexá-la à lista de armazenamento:
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
Este código não funciona (a função se comporta como um gerador vazio).
Estou esquecendo de algo? Existe uma maneira de transformar o algoritmo recursivo acima em um gerador sem substituí-lo por um iterativo ?
yield from getPermutations(string[:i] + string[i+1:])
, que é mais eficiente de várias maneiras!