mas nem sempre existirá uma versão recursiva de um código iterativo.
Na verdade, qualquer algoritmo iterativo pode ser convertido para um recursivo e vice-versa. A questão é que geralmente uma das versões é mais adequada para determinado contexto, e isso depende de vários fatores (muitos citados no texto).
Mas concordo que só porque pode, não quer dizer que você deve fazê-lo. Tem casos em que recursão é melhor, tem casos que é pior, então o importante é sempre entender bem os prós e contras e escolher conforme a situação.
Mas também é possível realizar essa mesma tarefa de forma recursiva que torna o código até um pouco mais claro (e performático em alguns casos).
Para a versão recursiva ser mais performática, depende de vários fatores, como por exemplo a implementação da linguagem. No caso do Python, não tem como, já que a implementação atual não otimiza recursão em cauda. Fazendo um teste rápido:
from timeit import timeit
def fatorial_iter(n):
resultado = 1
for i in range(1, n + 1):
resultado = resultado * i
return resultado
def fatorial_rec(n):
if n == 0:
return 1
else:
return n * fatorial_rec(n - 1)
# executa 100 mil vezes cada teste
params = { 'number' : 100000, 'globals': globals() }
n = 100
# imprime os tempos em segundos
print(timeit('fatorial_iter(n)', **params))
print(timeit('fatorial_rec(n)', **params))
Os tempos exatos podem variar de acordo com o hardware, mas enfim, na minha máquina foi (tempos em segundos - quanto menor, mais rápido):
0.7660143249995599
1.53468600800079
Ou seja, a versão recursiva foi 2 vezes mais lenta.
Claro que para poucas execuções com números pequenos, a diferença será imperceptível. Mas se tiver que fazer vários cálculos com números grandes, começa a fazer diferença. Ainda daria para fazer otimizações, como usar o lru_cache
para cachear os resultados, mas ainda tem outro problema: a versão recursiva pode estourar a pilha de execução. Basta testar as funções que criei acima com um valor mais alto:
def fatorial_iter(n):
resultado = 1
for i in range(1, n + 1):
resultado = resultado * i
return resultado
def fatorial_rec(n):
if n == 0:
return 1
else:
return n * fatorial_rec(n - 1)
n = 1000
# iterativo: ok
print(fatorial_iter(n))
# recursivo: erro (RecursionError)
print(fatorial_rec(n))
A versão iterativa roda sem problemas, já a recursiva dá erro: RecursionError: maximum recursion depth exceeded in comparison
. Isso porque cada chamada recursiva fica empilhada, até que se chegue no caso base (quando n
é zero) e elas comecem a retornar. E como há um limite do tamanho da pilha, valores grandes podem estourá-la. Com iteração não ocorre esse problema porque é apenas uma chamada de função.