Recursão Iterativa: Tail-Recursion

Um outro exemplo de procedimento recursivo é o algoritmo de Euclides:

(define (gcd a b)
  (if (= b 0) a
      (gcd b (modulo a b))))

Compare o processo gerado com o anterior.

(gcd 1071 462)
(gcd 462 147)
(gcd 147 21)
(gcd 21 0)
21

Perceba que no caso do gcd, a computação não exige o armazenamento do contexto antes das chamadas recursivas: toda a informação necessária encontra-se nos argumentos da função.

Ambos os procedimentos denotam recursões lineares, ou seja, onde a invocação da função ocorre uma única vez durante a sua avaliação. Entretanto, enquanto fac toma uma quantidade de memória proporcional ao argumento de entrada, o processo descrito por gcd exige espaço constante e portanto constitui ainda uma iteração linear.

O que diferencia os dois procedimentos é o fato de que a última expressão no corpo de gcd é a invocação de uma função. Quando isso acontece dizemos que a função possui uma tail-call (chamada de cauda ou terminal) e quando a função evocada é recursiva, dizemos que ela é tail-recursive. Esse fenômeno possibilida a aplicação da chamada Tail Call Optimization, que elimina a necessidade de ocupar espaço na pilha de execução do programa.

A especificação da linguagem Scheme exige que implementações sejam properly tail-recursive, o que significa que o número de execuções de funções em tail-calls sequenciais é ilimitado.