Regras em Prolog: Indo Além dos Fatos

filósofo
Fatos são a base do conhecimento em Prolog, mas eles sozinhos são limitados. Se precisássemos declarar cada relação manualmente — como “avô”, “bisavô”, “irmão” — nossa base de conhecimento seria imensa e repetitiva. Regras são a solução: elas nos permitem derivar novos conhecimentos a partir dos fatos existentes. Neste artigo, vamos aprender como criar regras e transformar uma simples árvore genealógica em um sistema poderoso de consultas.

1. O que são regras e por que são essenciais?

Uma regra em Prolog é uma afirmação condicional: “X é verdadeiro se Y e Z forem verdadeiros”. Enquanto fatos são verdades absolutas, regras são verdades dependentes — elas só são verdadeiras quando suas condições são satisfeitas.

🧠 Analogia: Se os fatos são como “Adão é pai de Seth” e “Seth é pai de Enos”, as regras são como “Alguém é avô se for pai do pai de alguém”. As regras capturam o conhecimento geral, não apenas casos específicos.

As regras são essenciais porque permitem:

  • Evitar repetição: Não precisamos declarar cada avô explicitamente.
  • Generalização: A regra funciona para qualquer pessoa na árvore.
  • Manutenção: Se adicionarmos novos fatos, as regras funcionam automaticamente.
  • Abstração: Expressamos conceitos de alto nível (avô, irmão) em termos de conceitos mais básicos (pai).

2. Sintaxe: cabeça e corpo

A estrutura básica de uma regra é:

  • Cabeça: É o predicado que estamos definindo (ex: avo(X, Y)).
  • :-: Lê-se “se” ou “se … então”.
  • Corpo: Uma conjunção de objetivos (fatos ou regras) que devem ser verdadeiros para que a cabeça seja verdadeira.
📝 Atenção: A regra termina com ponto final (.), assim como os fatos. A cabeça e o corpo podem ter variáveis que serão compartilhadas.

3. Exemplo clássico: a relação “avô”

A definição de “avô” em Prolog é elegantemente simples:

Interpretação: X é avô de Y se X é pai de Z e Z é pai de Y. A variável Z é compartilhada entre os dois objetivos, representando o pai intermediário (a “ponte” entre avô e neto).

📖 Leitura lógica: “X é avô de Y se existe uma pessoa Z tal que X é pai de Z e Z é pai de Y.”

4. O significado lógico de :- (se)

O operador :- é a espinha dorsal das regras em Prolog. Ele expressa uma implicação lógica:

  • Lado direito (corpo): as condições que devem ser satisfeitas.
  • Lado esquerdo (cabeça): a conclusão que podemos inferir.
🧩 Lógica: A regra diz: “Se o corpo é verdadeiro, então a cabeça é verdadeira.” O Prolog usa essa regra de trás para frente: quando você pergunta se a cabeça é verdadeira, ele tenta provar o corpo.

5. Adicionando a regra à árvore genealógica

Nosso arquivo familia.pl atualizado com a regra:

No SWI-Prolog ou SWISH, carregue o arquivo:

6. Consultando a nova regra: avo/2

Agora podemos fazer perguntas que antes seriam impossíveis apenas com fatos:

🔹 Quem é o avô de Enos?

🔹 Quem são os netos de Adão?

🔹 Quem são todos os avós e netos?

💡 Poder da regra: A mesma regra funciona para todas as pessoas na árvore, sem que tenhamos declarado um único fato “avô” explicitamente!

7. Como o Prolog executa uma regra (passo a passo)

Quando você pergunta avo(adão, enos), o Prolog:

Passo 1: Unifica a cabeça da regra avo(X, Y) com a consulta avo(adão, enos)X = adão, Y = enos.
Passo 2: Substitui as variáveis no corpo: pai(adão, Z), pai(Z, enos).
Passo 3: Tenta satisfazer o primeiro objetivo: pai(adão, Z). Encontra pai(adão, seth)Z = seth.
Passo 4: Tenta o segundo objetivo com Z = seth: pai(seth, enos) → é verdade!
Passo 5: Todos os objetivos do corpo são verdadeiros → a regra é satisfeita → a consulta retorna true (com X = adão).
🧠 Entenda: Se o segundo objetivo falhasse (ex: pai(seth, X) não encontrasse enos), o Prolog volta atrás (backtracking) e tenta outra solução para o primeiro objetivo.

8. Regra vs. Fatos: elegância e concisão

Com fatos explícitos (ruim):

Com regra (bom):

A regra é mais elegante porque:

  • Concisão: uma linha substitui muitas.
  • Generalidade: funciona para qualquer número de pessoas.
  • Manutenibilidade: se a definição de avô mudar, só precisamos alterar um lugar.
  • Corretude: a lógica é clara e verificável.

9. Predicados definidos pelo usuário vs. primitivos

Predicados Primitivos (built-in)Predicados Definidos pelo Usuário
Já vêm com o Prolog.São criados por você.
Ex: write/1, listing/1, findall/3.Ex: avo/2, neto/2, irmao/2.
São implementados em C/linguagem de baixo nível.São implementados em Prolog puro (fatos + regras).
Não podem ser modificados.Você pode adicionar ou alterar quantas regras quiser.
Exemplo: ?- listing(pai).Exemplo: ?- avo(adão, X).
💡 Dica: A maioria dos seus programas Prolog será composta de seus próprios predicados, construídos a partir de fatos e regras. Os predicados primitivos são ferramentas auxiliares.

10. Resumo: o poder das regras

O que aprendemos:

  • Regras são definições condicionais: cabeça :- corpo.
  • O corpo é uma conjunção de objetivos que devem ser verdadeiros.
  • Regras permitem derivar conhecimento a partir de fatos.
  • São mais concisas, gerais e fáceis de manter do que fatos explícitos.
  • O Prolog executa regras unificando a cabeça e depois provando o corpo.
  • Predicados definidos pelo usuário são a espinha dorsal de programas Prolog.

Com regras, sua árvore genealógica se transforma em um sistema de inferência completo. Você pode fazer perguntas complexas sem precisar declarar cada relação individualmente. No próximo artigo, vamos explorar regras com múltiplas definições e recursão — onde as regras se tornam ainda mais poderosas!

Variáveis Compartilhadas e Anônimas em Prolog

filósofo
Agora que já sabemos fazer consultas básicas com variáveis, vamos explorar dois conceitos fundamentais: variáveis compartilhadas (que conectam diferentes partes de uma consulta) e a variável anônima (que ignora valores que não nos interessam). Entender esses dois tipos de variáveis é essencial para escrever consultas poderosas e elegantes em Prolog.

0. Revisão: variáveis em consultas

Como vimos no artigo anterior, variáveis em Prolog começam com letra maiúscula (ex: X, Pessoa, Filho) e representam valores desconhecidos que o sistema tenta descobrir através da unificação.

Atualise o arquivo familia.pl

Como atualizar:

      1. Edite familia.pl (Em SWI opção Edit)
      2. Salve as alterações (No arquivo pl opção Save buffer)
      3. Em SWI-Prolog dê “Reload modified files”
      4. Agora suas alterações serão percebidas no SWI

Mas o que acontece quando usamos a mesma variável em diferentes partes da consulta? E como podemos “ignorar” um valor que não nos interessa? É exatamente isso que vamos explorar agora!

1. Variáveis Compartilhadas: conectando objetivos

Uma variável compartilhada é uma variável que aparece em múltiplos objetivos (subconsultas) dentro de uma mesma consulta. Quando a mesma variável aparece em lugares diferentes, o Prolog exige que ela tenha o mesmo valor em todos os lugares.

🧩 Ideia central: Variáveis compartilhadas são a “cola” que conecta diferentes partes da sua consulta. Elas permitem que você expresse relações complexas como “X é pai de Y e Y é pai de Z” — onde Y é compartilhado entre os dois objetivos.

Exemplo clássico: “Quem é o avô de Enos?”

Para descobrir o avô de Enos, precisamos encontrar alguém que seja pai do pai de Enos. Em Prolog, isso se traduz em dois objetivos com uma variável compartilhada:

Aqui, a variável Y é compartilhada entre os dois objetivos. Ela representa o “pai de Enos” no primeiro objetivo e o “filho de X” no segundo. O Prolog precisa encontrar um valor para Y que satisfaça ambas as condições.

2. Como o Prolog resolve essa consulta (passo a passo)

Passo 1: O Prolog começa com o primeiro objetivo: pai(Y, enos). Ele procura na base de fatos por um pai cujo segundo argumento seja enos.
Passo 2: Com Y = seth, o Prolog agora tenta resolver o segundo objetivo: pai(X, seth). Ele procura por alguém que seja pai de Seth.
Passo 3: O Prolog unifica X = adão e apresenta a resposta.
💡 Observação importante: A ordem dos objetivos importa! Se escrevêssemos pai(X, Y), pai(Y, enos), o Prolog começaria procurando um pai X de Y (qualquer um), depois verificaria se Y é pai de Enos. O resultado seria o mesmo, mas a ordem pode afetar a eficiência.

3. Por que variáveis compartilhadas são importantes?

Variáveis compartilhadas são a base para construir consultas multi-objetivo em Prolog. Elas permitem:

  • Encadear relações: avô, bisavô, irmão, tio, etc.
  • Filtrar resultados: encontrar pessoas que satisfazem múltiplas condições.
  • Comparar valores: verificar se duas variáveis têm o mesmo valor.
  • Expressar restrições: “X é pai de Y E Y é pai de Z” → X é avô de Z.

Sem variáveis compartilhadas, cada objetivo seria independente e não poderíamos expressar relações entre eles.

4. A Variável Anônima (_): quando o valor não importa

Às vezes, queremos fazer uma pergunta onde um dos valores não nos interessa. Por exemplo: “Quem já foi pai (de alguém)?” — não importa quem é o filho, só queremos saber os pais.

Para isso, usamos a variável anônima, representada por um underscore (_). Ela diz ao Prolog: “Ignore este valor; não me importa qual é.”

✨ Características da variável anônima:
  • Começa com _ (ex: _, _Algo).
  • Cada ocorrência de _ é independente — duas ocorrências de _ não são compartilhadas.
  • Não gera respostas (o Prolog não mostra seu valor).
  • Não unifica com outras variáveis (cada _ é único).

Exemplo: “Quem já procriou?” (quem é pai de alguém)

O Prolog encontra todas as pessoas que aparecem como primeiro argumento de pai/2, independentemente de quem é o filho. O _ simplesmente “engole” o segundo argumento.

⚠️ Atenção: Cada ocorrência de _ é independente. Em pai(_, _), a primeira ocorrência ignora o pai e a segunda ignora o filho — a consulta pergunta “existe alguma relação pai/2?” e sempre retorna true.

5. Variável Anônima vs. Variável Nomeada: qual a diferença?

CaracterísticaVariável Nomeada (X, Y)Variável Anônima (_)
SintaxeComeça com maiúsculaComeça com _
CompartilhamentoO mesmo nome = mesma variável (compartilhada)Cada _ é independente
Aparece na respostaSim, o valor é mostradoNão, o valor é ignorado
Quando usarQuando o valor é importante ou precisa ser compartilhadoQuando o valor não importa ou você quer evitar respostas desnecessárias
Exemplopai(X, abel) → mostra quem é o paipai(_, abel) → pergunta “Abel tem pai?” (true/false)

Exemplo comparativo

6. Variável anônima com nome: _Algo

Você pode usar _ seguido de um nome (ex: _Filho) para indicar que o valor não importa, mas quer dar um nome descritivo para melhor legibilidade. O comportamento é o mesmo: a variável é anônima e não gera respostas.

Neste caso, _Filho é anônima (começa com _), mas o nome ajuda a entender que se trata do filho. O Prolog não mostrará o valor de _Filho.

7 Quando usar cada tipo de variável?

Use variável nomeada (compartilhada) quando:

  • Você precisa conectar diferentes partes da consulta.
  • O valor da variável é importante e você quer vê-lo na resposta.
  • Você quer comparar valores entre diferentes objetivos.

Use variável anônima (_) quando:

  • O valor não é relevante para a resposta (ex: “quem é pai de alguém?”).
  • Você quer evitar poluir a resposta com valores desnecessários.
  • Você está fazendo uma consulta de “existência” (true/false).
  • Quer melhorar a legibilidade sem gerar respostas extras.

Resumo:

  • Variáveis compartilhadas (mesmo nome em múltiplos objetivos) permitem encadear relações e expressar consultas complexas.
  • Variáveis anônimas (_) ignoram valores que não nos interessam, tornando as consultas mais limpas e eficientes.
  • Escolher o tipo certo de variável torna suas consultas mais expressivas e fáceis de entender.