Estruturas de Dados em Prolog

filósofo

Listas, Functors e a arte de organizar informações


1. Listas: A Estrutura Fundamental

Em Prolog, listas são a estrutura de dados mais utilizada. Diferente de arrays em linguagens imperativas, as listas em Prolog são recursivas por natureza: uma lista é composta por uma cabeça (primeiro elemento) e uma cauda (o resto da lista). Essa estrutura é representada como [H|T], onde H é o primeiro elemento e T é uma lista com os demais.

A sintaxe é simples: [1, 2, 3] é uma lista de três elementos. A lista vazia é representada por []. O poder das listas em Prolog vem do casamento de padrões com [H|T], que permite extrair e processar elementos de forma elegante.

Manipulação de Listas com Padrões

O casamento de padrões com listas é uma das características mais poderosas do Prolog. Por exemplo, a consulta [X|Y] = [1, 2, 3] unifica X = 1 e Y = [2, 3]. Isso permite escrever predicados recursivos de forma natural:

Essa estrutura recursiva é a base para praticamente todas as operações com listas em Prolog.

Predicados Clássicos para Listas

O Prolog oferece diversos predicados embutidos para manipulação de listas, e muitos outros podem ser facilmente definidos:

  • append/3: append(L1, L2, L3) — L3 é a concatenação de L1 e L2.
  • member/2: member(X, L) — X é elemento de L.
  • length/2: length(L, N) — N é o comprimento de L.
  • reverse/2: reverse(L, R) — R é L invertida.
  • sort/2: sort(L, S) — S é a lista ordenada de L (sem duplicatas).
  • maplist/2: aplica um predicado a todos os elementos de uma lista.

Algoritmos Recursivos em Listas: Merge Sort

O Merge Sort é um exemplo clássico de algoritmo recursivo em listas. A implementação em Prolog é elegante e declarativa:

Esse algoritmo demonstra como a recursão, o casamento de padrões e a divisão de listas se combinam para criar soluções poderosas com poucas linhas de código.


2. Estruturas (Functors): Objetos em Prolog

Além das listas, o Prolog permite criar estruturas compostas usando functors. Um functor é um nome que agrupa outros termos, funcionando como um “objeto” ou “registro”. Por exemplo:

Functors podem ser aninhados, criando estruturas mais complexas:

As estruturas em Prolog são imutáveis e funcionam como registros em outras linguagens. Elas são particularmente úteis para representar dados estruturados e podem ser usadas com unificação e casamento de padrões.

Aplicação Prática com Estruturas

Considere um sistema de biblioteca com livros e empréstimos:

Com essas estruturas, podemos criar consultas poderosas:

A combinação de functors e listas permite modelar domínios complexos de forma natural e consultá-los de maneira declarativa, sem a necessidade de estruturas de controle complexas.


3. Conclusão

As listas e estruturas (functors) são as bases da modelagem de dados em Prolog. As listas oferecem uma maneira flexível e recursiva de agrupar elementos, enquanto os functors permitem criar objetos estruturados com campos nomeados.

A verdadeira força de Prolog vem da integração dessas estruturas com o mecanismo de busca e unificação: você pode definir dados, criar relações entre eles e consultar essas relações de forma natural e declarativa. Combine isso com recursão e você terá um toolkit poderoso para resolver problemas complexos.

Para aprofundar, pratique com:

  • Predicados para manipulação de listas (map, filter, fold, etc.)
  • Estruturas para representar dados do mundo real (pedidos, clientes, produtos)
  • Consultas complexas usando findall, setof e bagof
  • Algoritmos recursivos avançados em listas

Exercícios de Recursividade em Prolog

filósofo

A melhor forma de aprender recursividade em Prolog é praticando! Neste artigo, vamos resolver três exercícios clássicos que cobrem diferentes aspectos da recursão: operações aritméticas, conversão de bases e caminhamento em grafos. Cada exercício inclui dicas, solução completa, testes e desafios extras.

Exercício 4.1: Produto de Números Naturais

Problema: Defina um predicado recursivo prod(X, Y, P) que calcule o produto de dois números naturais X e Y usando apenas soma e subtração.

Raciocínio

Sabemos que X * Y = X + X + X + … (Y vezes). Usando recursão:

  • Caso base: X * 0 = 0
  • Passo recursivo: X * Y = X + X * (Y-1)

Diagrama do Processo

Solução em Prolog

Consultas de Teste

Desafios Adicionais

  1. Modifique o predicado para funcionar com números negativos.
  2. Implemente potencia(Base, Expoente, Resultado) usando o mesmo padrão recursivo.
  3. Implemente divisao(Dividendo, Divisor, Quociente, Resto) usando apenas subtração.

Exercício 4.2: Número Natural em Binário

Problema: Defina um predicado recursivo binario(N, B) que converta um número natural N para sua representação binária B (como lista de bits).

Raciocínio

Para converter um número para binário:

  • Caso base: N = 0 → [0]; N = 1 → [1]
  • Passo recursivo: O bit menos significativo é N mod 2; o resto é a representação binária de N // 2.

Exemplo: 13 em binário é 1101.

Solução em Prolog

Consultas de Teste

Desafios Adicionais

  1. Implemente binario_str(N, S) que retorna uma string binária em vez de lista.
  2. Implemente decimal(B, N) que converte uma lista de bits de volta para decimal.
  3. Implemente binario_completo(N, Bits, Tamanho) que retorna a representação com um número fixo de bits (ex: 8 bits).

Exercício 4.3: Mapa de Estradas

Problema: Dado um mapa de estradas com distâncias, defina um predicado dist(A, B, D) que calcule a distância total entre duas cidades A e B, considerando caminhos diretos e indiretos.

Raciocínio

Temos fatos estrada(Origem, Destino, Km). Queremos encontrar a distância total entre duas cidades:

  • Caso base: Existe uma estrada direta entre A e B.
  • Passo recursivo: Existe uma estrada de A para C, e um caminho de C para B.

Diagrama do Mapa

Solução em Prolog

Problema: Loops Infinitos!

O predicado acima pode entrar em loops infinitos devido a ciclos no grafo. Para resolver isso, mantemos uma lista de visitados:

Consultas de Teste

Desafios Adicionais

  1. Caminho mais curto: Implemente menor_dist(A, B, D) que encontra a menor distância entre A e B (use findall/3 e min_list/2).
  2. Lista de cidades: Modifique o predicado para retornar a lista de cidades no caminho, além da distância.
  3. Grafo com pesos negativos: Como você lidaria com estradas de “mão única” ou pesos negativos?
  4. Todas as rotas: Implemente todas_rotas(A, B, R) que retorna todas as rotas possíveis entre A e B (sem loops).

Conclusão: A Importância da Recursão em Prolog

Os exercícios acima demonstram três áreas onde a recursão em Prolog é essencial:

  • Operações aritméticas: A recursão permite definir operações matemáticas a partir de conceitos mais simples, como soma e subtração.
  • Conversão e processamento de dados: A recursão é natural para processar estruturas hierárquicas, como a representação binária de números.
  • Exploração de grafos: A recursão é a ferramenta ideal para navegar em estruturas interconectadas, encontrando caminhos e calculando distâncias.

A beleza da recursão em Prolog está na sua simplicidade declarativa: você descreve o caso base e a regra de redução, e o Prolog cuida da execução, backtracking e busca de soluções. Isso permite que problemas complexos sejam expressos em poucas linhas de código.

Para dominar a recursão em Prolog, pratique com diferentes tipos de problemas:

  • Estruturas de lista: soma, inversão, busca, ordenação
  • Árvores: percurso, altura, busca
  • Grafos: caminhos, ciclos, conectividade
  • Matemática: sequências, séries, relações

Lembre-se sempre: todo predicado recursivo precisa de um caso base e de uma garantia de progresso (a chamada recursiva deve operar sobre uma instância menor do problema). Com isso, você estará pronto para explorar todo o poder da programação lógica!