Conteúdos da cadeira

Recursão, corte e procura em Prolog

Listas e aritmética com is, corte verde, findall e um labirinto por procura em profundidade.

Markdown

Perguntar sobre esta página

ChatGPTClaudePerplexityGeminiCopiar e abrir

Envia o link e pede à IA para ler a página. No Gemini, cola a pergunta copiada.

Ver pergunta para copiar
Nesta página

Com factos, regras e unificação já respondes a queries sobre relações. Falta o resto do kit: listas para coleções, aritmética para contas, o corte para podar a procura, o findall para juntar todas as respostas, e a procura propriamente dita. É a recursão outra vez, agora com cláusulas em vez de equações.

Listas e aritmética

Listas usam [Cabeça|Cauda], como o (x:xs) do Haskell. Somar uma lista é caso base mais passo recursivo:

soma([], 0).
soma([H|T], S) :- soma(T, R), S is H + R.

A query ?- soma([1,2,3], S). resolve soma([2,3], R), depois soma([3], R), até soma([], 0), e no regresso calcula S is 3 + 0, depois 2 + 3, depois 1 + 5: S = 6. O is avalia o lado direito como expressão aritmética e unifica com a esquerda; sem ele, S = H + R ligaria S à expressão por avaliar, não ao número. Regra prática: tudo o que é conta usa is, e todas as variáveis do lado direito têm de estar ligadas quando ele corre.

Corte e findall

Por omissão, o Prolog explora todas as alternativas ao pedir mais soluções. O corte, !, poda: descarta as alternativas daquela cláusula a partir dali.

max(X, Y, X) :- X >= Y, !.
max(_, Y, Y).

Com o corte, max(3, 5, M) usa a primeira cláusula, falha no teste, e o corte nunca chega a correr; a segunda dá M = 5. Com max(7, 5, M), o teste passa, o corte elimina a segunda cláusula, e M = 7 sem deixar alternativa pendente. Este é um corte verde: só remove soluções redundantes, não muda as respostas. Um corte que mude respostas é vermelho, e é fonte clássica de erros.

Para juntar todas as respostas numa lista em vez de as pedir uma a uma, usa findall:

?- findall(C, pai(ana, C), Cs).
Cs = [leo].

O segundo argumento é o objetivo, o primeiro diz o que colecionar de cada solução. Com os factos da página anterior, só leo é filho da Ana.

Exemplo completo: sair do labirinto

Modela o labirinto como ligações e procura um caminho acumulando visitados para não andar em círculos:

ligado(a, b). ligado(b, c). ligado(b, d). ligado(c, e). ligado(d, e).

caminho(X, X, _, [X]).
caminho(X, Z, Visitados, [X|Resto]) :-
  ligado(X, Y),
  \+ member(Y, Visitados),
  caminho(Y, Z, [Y|Visitados], Resto).

(member/2 vem de library(lists); em SICStus carrega-se com :- use_module(library(lists))..) A query ?- caminho(a, e, [a], C). faz procura em profundidade: de a vai a b (única saída); de b tenta c primeiro (ordem das cláusulas); de c chega a e, que unifica com o caso base. Responde C = [a, b, c, e], e ao pedir mais soluções retrocede até b, tenta d, e encontra o segundo caminho [a, b, d, e].

Repara nos três travões que impedem a procura de se perder: a lista de visitados com negação (não revisita), o caso base (para ao chegar), e a ordem das cláusulas (decide que caminho sai primeiro). Quando um programa de procura entra em ciclo, um destes três é quase sempre o culpado.

Ver o ficheiro no GitHub

À tua maneira

Escolhe como preferes ler.

Aparência
Ajustar cores e largura
Cor de destaque do tema FEUP
Tipo de letra

Álgebra, lógica e uma ideia de cada vez.

As tuas escolhas ficam guardadas neste navegador.

Pesquisar

Escreve para pesquisar em todo o site.

para escolher · Enter para abrir · Esc para fechar

Atalhos de teclado

Clica numa tecla para a mudar. Esc cancela. Backspace desativa.

PesquisarCtrl / Cmd K

Os atalhos não interferem enquanto escreves. Tab e Enter funcionam sempre.