O Operador de Corte ‘!’ em Prolog

Controlando o backtracking com poder e responsabilidade

O operador de corte, representado por !, é uma das
ferramentas mais poderosas — e perigosas — do Prolog. Ele permite controlar o backtracking,
eliminando alternativas desnecessárias e otimizando a execução. Mas como toda ferramenta poderosa,
seu uso requer cuidado: um corte mal colocado pode eliminar soluções válidas e alterar
a semântica declarativa do programa.

O Problema: Backtracking Desnecessário

Considere a função matemática f(X) definida por partes, como no Programa 3.2:

% f(X) = 0 se X < 5
% f(X) = 1 se 5 =< X < 10
% f(X) = 2 se X >= 10

f(X, 0) :- X < 5.
f(X, 1) :- X >= 5, X < 10.
f(X, 2) :- X >= 10.

As regras são mutuamente exclusivas: para qualquer X, apenas uma delas
será verdadeira. No entanto, o Prolog não sabe disso. Ao consultar ?- f(3, Y)., ele:

  1. Tenta a primeira regra: X < 5 é verdadeiro, então Y = 0.
  2. Se pedirmos mais soluções (;), ele retrocede e tenta a segunda regra,
    testando X >= 5 (que falha).
  3. Depois tenta a terceira regra, que também falha.

Esse backtracking inútil desperdiça tempo, especialmente em programas com muitas regras
ou bases de dados grandes. Precisamos de uma forma de dizer ao Prolog: “se esta regra funcionou,
não tente as outras
”.

A Solução: O Operador de Corte (!)

O corte é um predicado especial que sempre é bem-sucedido, mas com
um efeito colateral: ele descarta todos os pontos de escolha criados
antes de sua posição dentro da mesma regra e descarta as cláusulas restantes para aquele
predicado. Veja o Programa 3.3 com cortes:

f(X, 0) :- X < 5, !.
f(X, 1) :- X >= 5, X < 10, !.
f(X, 2) :- X >= 10.

Agora, quando consultamos ?- f(3, Y).:

  • A primeira regra é ativada, X < 5 é verdadeiro.
  • O corte ! é executado: ele descarta as alternativas para f/2
    (as regras 2 e 3) e também descarta qualquer backtracking dentro da regra (embora não haja).
  • O Prolog não tenta mais as outras regras, economizando tempo.

Árvore de Busca: Antes e Depois do Corte

Visualize a diferença na árvore de busca para ?- f(3, Y).:

SEM CORTE
├─ f(3, Y)
│ ├─ Regra 1: X < 5 ✅ → Y = 0 (solução)
│ │ └─ ↩️ backtracking
│ ├─ Regra 2: X >= 5 ❌ (falha)
│ └─ Regra 3: X >= 10 ❌ (falha)

COM CORTE
├─ f(3, Y)
│ ├─ Regra 1: X < 5 ✅ → ! (solução)
│ │ └─ ✂️ pontos de escolha descartados
│ └─ (Regras 2 e 3 não são tentadas)

Estrutura Condicional com Corte (Programa 3.4)

O corte é frequentemente usado para implementar estruturas condicionais do tipo
if-then-else:

if(Condition, Then, Else) :-
  Condition, !,
  Then.
if(_, _, Else) :-
  Else.

A primeira cláusula tenta a condição. Se for bem-sucedida, o corte previne que a segunda
cláusula seja usada, e executa Then. Se a condição falhar, a primeira cláusula falha,
e o Prolog tenta a segunda (o Else).

Exemplo prático:
?- if(X < 5, write('pequeno'), write('grande')).
• Se X for instanciado com um valor, ele escreve a mensagem correta.
• O corte garante que apenas uma das mensagens seja escrita.

Os Perigos do Corte

Atenção: O corte não é negação e pode eliminar soluções válidas
se usado incorretamente.

Um exemplo clássico de erro:

% ERRADO — este corte elimina soluções legítimas
pai(X, Y) :- homem(X), !, filho(Y, X).

Aqui, o corte impede que o Prolog tente outras cláusulas de pai/2 mesmo que homem(X)
seja verdadeiro, eliminando potencialmente outros pais que poderiam ser encontrados em outras regras.

O corte é uma “fuga do paradigma declarativo puro”.
Em Prolog puro, a ordem das cláusulas não afeta o resultado (apenas a eficiência).
Com o corte, a ordem passa a importar semanticamente. Programas com cortes
são mais difíceis de ler, depurar e manter.

Boas Práticas com Corte

  • Use o corte apenas quando as cláusulas são mutuamente exclusivas e você tem certeza
    de que as alternativas não devem ser consideradas.
  • Coloque o corte após a condição que determina a exclusividade, não antes.
  • Prefira estruturas condicionais com -> (o operador condicional do Prolog)
    em vez de cortes manuais, quando possível.
  • Documente o uso do corte com comentários explicando por que ele está ali.

Exercícios Propostos

  1. Função sinal: Escreva a função sinal(X, S) que retorna S = -1
    se X < 0, S = 0 se X = 0, e S = 1 se X > 0.
    Use cortes para otimizar.
  2. Classificação de idade: Crie idade_class(X, C) onde C é
    'crianca' se X < 12, 'adolescente' se 12 =< X < 18,
    e 'adulto' se X >= 18. Use cortes.
  3. Teste sem cortes: Execute as mesmas funções sem cortes e compare
    o comportamento quando você pede soluções alternativas com ;.
  4. Desafio: Implemente a estrutura if/3 apresentada acima e teste com
    diferentes condições, incluindo casos em que a condição tem múltiplas soluções.

Conclusão

O operador de corte é uma ferramenta valiosa para otimização e controle
do backtracking, mas deve ser usado com moderação e consciência. Ele quebra a pureza
declarativa do Prolog, tornando o programa mais procedural. Em muitas situações, a ordenação
adequada das cláusulas
e o uso de cortes verdes (que apenas otimizam, sem
alterar o resultado) são suficientes. Use o corte quando a eficiência realmente importar, mas sempre
com a certeza de que ele não eliminará soluções corretas.

Deixe um comentário