Prolog/Recursão
Origem: Wikilivros, livros abertos por um mundo aberto.
[editar] Recursão
Regras recursivas devem ser permitidas a fim de tornar a linguagem útil para muitas aplicações. Um predicado definido por uma regra recursiva deve necessariamente ter, no mínimo uma definição não recursiva. Se isto não acontecer, a definição é logicamente mal-formada e o programa ficaria em laço infinito.
Um exemplo de regra recursiva seria a definição de uma base de dados sobre relações familiares que responda questões sobre ancestralidade. Isto pode ser definido da seguinte forma:
ancestral(X,Y) :- mãe(X,Y). ancestral(X,Y) :- pai(X,Y). ancestral(X,Y) :- mãe(X,Z),ancestral(Z,Y). ancestral(X,Y) :- pai(X,Z),ancestral(Z,Y).
Além disso, é necessário tomar cuidado com a ordem na qual unificações(ver Unificação em Prolog) são procurados para objetivos. Se invertermos a ordem nas regras recursivas do predicado ancestral, isto é
ancestral(X,Y) :- ancestral(Z,Y),mãe(X,Z). ancestral(X,Y) :- ancestral(Z,Y),pai(X,Z).
a consulta resultará uma recursão infinita.
Outro exemplo mais concreto seria:
nascido(curitiba, joão). nascido(paris, jean). cidade(curitiba, paraná). cidade(paris, frança). naturalidade(X,Y):- nascido(K,Y), cidade(K,X).
universo(venus, perto_n_possui_cauda). universo(cometa, perto_possui_cauda). astros(venus, venus). astros(cometa, cometa).
espaco(X,Y):- universo(K,Y), astros(K,X).
[editar] Recursão em cauda
Uma função f pode ser definida por recursão em cauda da seguinte maneira:
f(x): se c(x) então g(x) senão f(h(x))
onde: - c(x) é algum tipo de condição sobre o valor de x; - g(x),h(x) são funções definidas com um argumento não definido na função f.
A mesma sentença pode ser escrita em Prolog desta forma:
f(X, Z) :- c(X), g(X, Z). f(X, Z) :- h(X, Y), f(Y, Z).
Recursão em cauda é importante e preferível sobre recursão não cauda pois pode ser implementada como uma iteração que é computada usando uma pilha estática.
[editar] Recursão não em cauda
Recursão não em cauda pode ser definida pela sentença
f(x): se c(x) então g(x) senão k(x, f(h(x)))
Isto significa que o valor da chamada de recursão é modificado após sua computação. Em Prolog, esta função pode ser implementada assim:
f(X, Y) :- c(X), g(X, Y). f(X, Y) :- h(X, X1), f(X1, Y1), k(X, Y1, Y).
Recursão em não cauda usa um espaço linear na pilha, conseqüentemente evitado caso não seja necessário. Em alguns casos é possível otimizar a implementação. Considere o esquema:
f(n): se n = 0 então a senão k(n, f(n - 1))
Isto pode ser escrito em Prolog usando recursão não em cauda:
f(0, a). f(X, Y) :- X1 is X - 1, f(X1, Y1), k(X, Y1, Y).
Este código requer espaço linear na pilha para guardar resultados temporários de chamadas recursivas. Usando um acumulador, o código abaixo pode ser escrito usando recursão em cauda:
f(X, Y) :- f(X, 1, a, Y). f(X, M, ACC, ACC) :- M > N. f(X, M, ACC, Y) :- M <= N, k(M, ACC, ACC1), M1 is M + 1, f(N, M1, ACC1, Y)