Uso de fail para Iteração em Prolog

Gerando todas as respostas automaticamente com backtracking forçado

Em Prolog, quando fazemos uma consulta que tem múltiplas respostas, o comportamento padrão é mostrar a primeira solução e aguardar o usuário pressionar ; (ponto-e-vírgula) para ver a próxima. Mas e se quisermos ver todas as respostas de uma vez, sem interação manual? É aí que entra o predicado fail/0.

O Problema: Respostas Sob Demanda

Considere a base de fatos:

d(0).
d(1).

A consulta ?- d(X). retorna:

?- d(X).
X = 0 % aguarda ‘;’ para continuar
% usuário pressiona ‘;’
X = 1
% fim

Para ver as duas respostas, o usuário precisa interagir. Em muitas situações — como ao gerar relatórios, listar todos os registros de um banco de dados ou depurar — queremos que o programa imprima todas as soluções automaticamente.

A Solução: fail + Efeitos Colaterais

O predicado fail/0 é um predicado pré-definido que sempre falha. Quando usado em conjunto com efeitos colaterais (como write/1 e nl/0), ele força o backtracking repetidamente, gerando todas as soluções.

Programa 3.5: Gerando Todas as Combinações Binárias

bin :-
  d(A), d(B), d(C),
  write([A, B, C]), nl,
  fail.

Vamos executar:

?- bin.
[0, 0, 0]
[0, 0, 1]
[0, 1, 0]
[0, 1, 1]
[1, 0, 0]
[1, 0, 1]
[1, 1, 0]
[1, 1, 1]
false

Todas as 8 combinações foram impressas automaticamente, sem necessidade de pressionar
; nenhuma vez!

Passo a Passo da Execução

Entenda como o Prolog processa bin :- d(A), d(B), d(C), write, nl, fail.:

  1. Gera: d(A) unifica com 0 (primeira solução).
  2. Gera: d(B) unifica com 0.
  3. Gera: d(C) unifica com 0.
  4. Processa: write([0,0,0]) imprime a lista, nl pula linha.
  5. Falha: fail força o backtracking para o ponto de escolha mais recente — d(C).
  6. Próxima: d(C) unifica com 1 (segunda solução).
  7. Repete: O ciclo continua: escreve [0,0,1], falha, backtracking…
  8. Esgotamento: Quando todas as combinações de d(A), d(B), d(C) se esgotam,
    o fail não tem mais para onde voltar, e a regra bin falha, retornando
    false.
Padrão clássico: Gerar → Processar → Falhar
Este é o padrão de iteração em Prolog. A geração produz valores, o processamento (write, nl) os exibe, e o fail força a busca pela próxima combinação.

Comparação com Loops de Outras Linguagens

O padrão gerar → processar → fail é análogo a loops em linguagens imperativas:

Python

for a in [0,1]:
  for b in [0,1]:
    for c in [0,1]:
      print([a,b,c])

Prolog

bin :-
  d(A), d(B), d(C),
  write([A,B,C]), nl,
  fail.

Em Python, o loop é explícito com for. Em Prolog, o backtracking substitui o loop
— a iteração é conduzida pelo mecanismo de busca do Prolog, não por uma construção de controle explícita.

⚠️ Cuidados com fail

Atenção: O uso incorreto de fail pode criar loops infinitos se não houver uma base que esgote as possibilidades.

Por exemplo, se você usar fail com um predicado que tem infinitas soluções (como between(0, inf, X)), o programa nunca terminará. Sempre certifique-se de que a parte “geradora” tem um número finito de soluções, ou inclua uma condição de parada.

Dica: Se você precisa processar todas as soluções e parar após um determinado número, combine fail com between/3 ou use o predicado forall/2 (que é uma abstração segura sobre esse padrão).

Exercícios Propostos

  1. Listar todos os números binários de 4 bits: Modifique o programa bin
    para gerar combinações de 4 dígitos ([A,B,C,D]).
  2. Listar todos os filhos de um pai: Crie uma base de fatos pai(pedro, joao).,
    pai(pedro, maria)., etc., e use fail para listar todos os filhos de Pedro.
  3. Listar todas as capitais de estados: Com fatos capital(estado, cidade),
    use fail para imprimir todas as capitais.
  4. Contar soluções: Use fail com um contador (inc/1)
    para contar quantas soluções existem para uma consulta.
  5. Desafio: Crie um predicado listar_solucoes(Goal) que recebe uma meta
    como argumento (usando call/1) e imprime todas as soluções usando fail.

Conclusão

O fail é uma ferramenta elegante que transforma o backtracking em um mecanismo de iteração. Combinado com efeitos colaterais, ele permite gerar, processar e exibir todas as soluções de uma consulta automaticamente.
Essa é uma das características que tornam o Prolog único: o fluxo de controle é guiado pela busca lógica, e não por instruções passo-a-passo. Use fail com sabedoria, e ele se tornará um aliado poderoso para explorar todas as respostas que seus programas podem oferecer.

Deixe um comentário