Nome: ? | Matrícula: ?

Programação funcional

Acumuladores e recursão de cauda

Dizemos que uma função usa recursão de cauda quando as chamadas recursivas que ela executa são a última instrução executada antes de retornar. Quando uma função recursiva usa recursão de cauda, alguns compiladores e interpretadores são capazes de otimizar a função, transformando-a em uma função iterativa. Desta forma, as chamadas recursivas não ocupam espaço na pilha de chamadas.

Para transformar uma função recursiva em uma função com recursão de cauda, frequentemente é necessário adicionar um parâmetro à função, que funciona como acumulador. A cada chamada recursiva, o acumulador é atualizado com o resultado parcial da função e, no final, a função retorna o valor do acumulador.

Por exemplo, considere a função length, implementada com recursão tradicional:

(defun length (lista)
  (cond
    ((null lista) 0)
    (t (+ 1 (length (cdr lista))))))

A mesma função pode ser implementada usando recursão de cauda, com a ajuda de uma função auxiliar length-acum:

(defun length-acum (lista acum)
  (cond
    ((null lista) acum)
    (t (length-acum (cdr lista) (+ 1 acum)))))

(defun length (lista)
  (length-acum lista 0))

Outros exemplos de funções em LISP, com e sem recursão de cauda, estão nas anotações da aula anterior.

Exercícios

Para cada um dos exercícios a seguir, implemente uma função usando recursão de cauda. Implemente funções auxiliares quando necessário. Adicione testes no final para verificar se a sua implementação está correta.

soma-lista

Implemente a função (soma-lista lista), que retorna a soma dos elementos da lista.

function(str, info) { return multiEval(str, info); }

fatorial

Implemente a função (fatorial n), que retorna o valor de n! (n fatorial).

function(str, info) { return multiEval(str, info); }

fibonacci

Implemente a função (fibonacci n), que retorna o n-ésimo número da sequência de Fibonacci.

function(str, info) { return multiEval(str, info); }

inverte

Implemente a função (inverte lista), que retorna uma lista com os mesmos elementos do argumento, porém em ordem inversa. Exemplo: (inverte '(q w e)) ==> '(e w q)

function(str, info) { return multiEval(str, info); }

concatena

Implemente a função (concatena l1 l2), que recebe duas listas, l1 e l2, e retorna uma lista com os elementos de l1 na sequência, seguidos dos elementos de l2 na sequência. Exemplo: (concatena '(m u n) '(d o)) ==> '(m u n d o)

function(str, info) { return multiEval(str, info); }