O documento discute recursão em Prolog, apresentando:
1) Uma explicação de como a recursão funciona, com cada chamada à regra criando uma nova cópia com variáveis únicas;
2) Um exemplo detalhado mostrando como escrever um predicado recursivo para encontrar objetos aninhados;
3) Como a recursão é executada passo a passo em diferentes níveis através da unificação de variáveis entre as regras.
2. Aventura em Prolog 2
Aventura em Prolog
Recursão
Como a recursão funciona
Pragmática
Exercícios
3. Aventura em Prolog 3
Recursão
É a habilidade de uma unidade de
código chamar a ela mesma,
repetidamente, se necessário
É frequentemente uma maneira muito
poderosa e conveniente de representar
certos construtores de programação
Em Prolog, a recursão ocorre quando
um predicado contém um objetivo que
se refere a ele mesmo
4. Aventura em Prolog 4
Recursão
Toda vez que uma regra é chamada,
Prolog usa o corpo da regra para criar
uma nova consulta com novas variáveis
Uma vez que a consulta é uma nova
cópia cada vez que é chamada, não faz
diferença se uma regra chama outra
regra ou a ela mesma
5. Aventura em Prolog 5
Recursão
Uma definição recursiva sempre tem
pelo menos duas partes
A condição limite (caso base): define um
caso simples que sabemos ser verdadeiro
O caso recursivo: simplifica o problema
primeiro removendo uma camada de
complexidade, e então chamando ela
mesma
Em cada nível, a condição limite é testada,
se verdadeira, a recursão pára, se não, a
recursão continua
6. Aventura em Prolog 6
Exemplo – Nani Search
Objetos próximos ou aninhados
O predicado local/2 nos diz que a lanterna
está na escrivaninha e que a escrivaninha
está no escritório, mas não nos indica que a
lanterna está no escritório
?- local(lanterna,escritório).
no
Usando recursão, iremos escrever um
novo predicado esta_contido_em/2, o
qual irá “cavar” através de camadas de
objetos aninhados
7. Aventura em Prolog 7
Exemplo – Nani Search
Para tornar o problema mais
interessante, primeiro iremos adicionar
mais itens aninhados no jogo
Iremos continuar usando o predicado
local/2 para colocar objetos na
escrivaninha, que por sua vez, podem
conter outros objetos dentro deles
local(envelope, escrivaninha).
local(selo, envelope).
local(chave, envelope).
8. Aventura em Prolog 8
Exemplo – Nani Search
Para listar todas as coisas no escritório:
Primeiro devemos listar os objetos que
estão diretamente no escritório, como a
escrivaninha
Depois devemos listar os objetos que estão
na escrivaninha
A seguir objetos que estão dentro dos
objetos que estão na escrivaninha
9. Aventura em Prolog 9
Exemplo – Nani Search
A regra:
Um objeto, O1, está contido em outro
objeto, O2, se O1 está diretamente
localizado em O2 Esta é a condição limite
Um objeto, O1, está contido em outro
objeto, O2, se algum objeto intermediário,
X, está localizado em O2 e O1 está contido
em X Aqui é onde nós simplificamos e
fazemos a recursão
10. Aventura em Prolog 10
Exemplo – Nani Search
Em Prolog:
Caso base:
esta_contido_em(O1, O2):-
local(O1, O2).
Caso recursivo:
esta_contido_em(O1, O2):-
local(X, O2),
esta_contido_em(O1, X).
11. Aventura em Prolog 11
Exemplo – Nani Search
Testando...
?- esta_contido_em(X, escritório).
X = escrivaninha ;
X = computador ;
X = lanterna ;
X = envelope ;
X = selo ;
X = chave ;
no
?- esta_contido_em(envelope, escritório).
yes
?- esta_contido_em(maçã, escritório).
no
12. Aventura em Prolog 12
Como a Recursão Funciona
Como em todas as chamadas a regras,
as variáveis em uma regra são únicas,
ou o escopo delas é a regra
Em um caso recursivo, isto significa que
cada chamada à regra, em cada nível,
possui seu próprio conjunto único de
variáveis
Assim, os valores de X, O1 e O2 no primeiro
nível da recursão são diferentes daqueles
dos segundo nível
13. Aventura em Prolog 13
Como a Recursão Funciona
Porém, a unificação entre um objetivo e
a cabeça de uma cláusula obrigue um
relacionamento entre as variáveis de
diferentes níveis
Acompanhe a execução da consulta a
seguir...
?- esta_contido_em(XQ, escritório).
14. Aventura em Prolog 14
esta_contido_em(XQ, escritório).
No primeiro nível de recursão:
esta_contido_em(O11, O21) :-
local(X1, O21),
esta_contido_em(O11, X1).
XQ = _01
O11 = _01
O21 = escritório
X1 = _02
Reescrita:
esta_contido_em(_01, escritório) :-
local(_02, escritório),
esta_contido_em(_01, _02).
local/2 satisfeito com _02 = escrivaninha
esta_contido_em(_01, escrivaninha)
Este objetivo unifica com a cabeça de uma nova cópia
da cláusula, no próximo nível de recursão. Após a
unificação, as variáveis são:
XQ = _01 O11 = _01 O12 = _01
O21 = escritório O22 = escrivaninha
X1 = escrivaninha X2 = _03
15. Aventura em Prolog 15
Como a Recursão Funciona
Quando a recursão encontra uma solução,
como em ‘envelope’, todos os O1s e X0
imediatamente assumem um valor – utilize o
modo de debug para acompanhar a execução
Ao escrever um predicado recursivo, é
essencial garantir que a condição limite seja
checada em cada nível, em outro caso, o
programa poderá entrar em loop infinito
A maneira mais simples de fazer isso, é sempre
definir a condição limite primeiro, garantindo que
ela sempre será tentada antes do caso recursivo
16. Aventura em Prolog 16
Pragmática
Considere que o objetivo local(X,Y) será
satisfeito por todas as cláusulas de
local/2
Por outro lado, os objetivos local(X,
escritório) e local(envelope, X) serão
satisfeitos por poucas cláusulas
Observe no slide a seguir duas versões
para a regra esta_contido_em/2...
17. Aventura em Prolog 17
Pragmática
Ambos darão respostas corretas, mas a
performance de cada um irá depender da
consulta:
A consulta esta_contido_em(X, escritório) irá
executar mais rápido na primeira versão
O2 é linkada fazendo com que a busca por local(X,
O2) seja mais fácil do que a busca por duas
variáveis não linkadas
Por razões similares, a consulta esta_contido_em
(chave, X) seja mais rapidamente executada na
segunda versão
esta_contido_em(O1, O2):-
local(X, O2),
esta_contido_em(O1, X).
esta_contido_em(O1, O2):-
local(O1, X),
esta_contido_em(X, O2).
18. Aventura em Prolog 18
Exercícios
Banco de dados genealógico
Utilize recursão para escrever o predicado
ancestral/2
Utilize ancestral/2 para encontrar todos os
ancestrais e todos os descendentes de uma
pessoa
Escreva um predicado descendente/2 que
seja otimizado para descendentes, oposto a
ancestral/2, que deve ser otimizado para
ancestrais
19. Aventura em Prolog 19
O que vem a seguir?
Estruturas de Dados
Unificação
...