Prolog (Programação Lógica) é umalinguagem de programação que se enquadra no paradigma de Programação em Lógica Matemática. É uma linguagem de uso geral que é especialmente associada com ainteligência artificial elinguística computacional. Consiste numa linguagem puramente lógica, que pode ser chamada deProlog puro, e numa linguagem concreta, a qual acrescenta o Prolog puro com componentesextra-lógicos.
A aplicação do provador de teoremas trata estas cláusulas como procedimentos
para mostrar/resolverH, mostrar/resolverB1 and … andBn.
O Prolog puro foi então estendido para incluir anegação por falha, na qual condições negativas da forma not(Bi) são mostradas por tentativa e falha para resolver as condições positivas correspondentes Bi).
O nomeProlog para a linguagem concreta foi escolhido porPhilippe Roussel como uma abreviação de “PROgrammation enLOGique”. Foi criada em meados de 1972 porAlain Colmerauer e Philippe Roussel, baseados no conceito deRobert Kowalski da interpretação procedimental das cláusulas de Horn. A motivação para isso veio em parte da vontade de reconciliar o uso da lógica como uma linguagem declarativa de representação do conhecimento com a representação procedimental do conhecimento, que era popular na América do Norte no final da década de 1960 para início de 1970.
Muito do desenvolvimento moderno do Prolog veio dos projetos decomputadores da quinta geração (FGCS), que desenvolveu uma variante do Prolog chamadaKernel Language para seu primeirosistema operacional.
Apesar do longo tempo de desenvolvimento, Prolog ainda não é uma linguagem portável, já que cada implementação usa rotinas completamente diferentes e incompatíveis entre si. Por exemplo, um programa trivial que faz um loop de ler uma linha da console e escreve a mesma linha, terminando quando for entrada uma linha vazia, é impossível de ser escrito de forma que qualquer interpretador consiga rodar.
A linguagem de programação Prolog nasceu de um projeto que não tinha por foco a implementação de uma linguagem de programação, mas o processamento de linguagens naturais. Na Universidade de Marselha, Alain Colmerauer e Robert Pasero trabalhavam na parte de linguagem natural e Jean Trudel e Philippe Roussel trabalhavam na parte de dedução do projeto. Interessado pelo método de resolução SL, Trudel persuadiu um dos seus inventores, Robert Kowalski, que se uniu ao projeto. O projeto resultou em uma versão preliminar da linguagem Prolog em fins de 1971 sendo que a versão definitiva apareceu em fins de 1972[1].
O Prolog é uma linguagem declarativa, significando que em vez de o programa estipular a maneira de chegar à solução, passo a passo, (como nas linguagens procedimentais ou imperativas), limita-se a fornecer uma descrição do problema que se pretende computar. Usa uma coleçãobase de dados defatos e de relações lógicas (regras) que exprimem o domínio relacional do problema a resolver.
Um programa pode rodar num modo interativo, a partir de consultas (queries) formuladas pelo usuário, usando a base de dados (os 'fatos') e as regras relacionais (essencialmente implicações lógicas: se.. então), e o mecanismo de unificação para produzir (por uma cadeia de deduções lógicas) a solução.
Prolog não empregatipos de dados do mesmo modo que as linguagens de programação mais comuns normalmente fazem. Todos os dados são tratados como sendo de um único tipo,Termo, cuja natureza depende da forma como esse termo foi declarado. Ou seja, os elementos léxicos utilizados na sua declaração determinam se esse termo será um número, um texto, uma variável, uma estrutura complexa e assim por diante.
Com exceção de átomos numéricos, funções ou predicados construídos, os nomes em Prolog para constantes, variáveis, funções e predicados, não têm nenhum significado intrínseco e podem ser escolhidos livremente pelo programador. Em geral, duas notações distintas denotarão ou serão objetos distintos. Como em qualquer linguagem de programação, a cada nome deve ser dado um escopo.
Em Prolog, as regras de escopo são:
O escopo de uma variável é a asserção (fato, regra, ou consulta) na qual aparece.
O escopo de qualquer outro nome (constante, nome de função, ou nome de predicado) é todo o programa.
Isto significa que um nome de variável pode ser utilizado e reutilizado a vontade no programa para denotar variáveis diferentes, enquanto qualquer outra notação representa, ou é, o mesmo objeto para o programa todo.
As constantes de texto são introduzidas por meio de átomos. Um átomo é uma sequência constituída de letras, números eunderscore, mas iniciando com uma letra minúscula. Se um átomo não alfanumérico é necessário, pode-se usar qualquer sequência entre aspas simples (ex: 'um átomo contendo espaços').
Um átomo pode ser definido das seguintes maneiras:
começando com letra minúscula:
pedro henrique_iv
como uma sequência de caracteres entre aspas simples:
Um número é uma sequência de dígitos, permitindo também os sinais de. (para números reais),- (número negativo) ee (notação científica). Algumas implementações do Prolog não fazem distinção entre inteiros e números reais.
Variáveis são declaradas da mesma forma que átomos, porém iniciando com uma letra maiúscula ou underscore. No ambiente Prolog uma variável não é umcontêiner cujo valor pode ser atribuído (como ocorre naslinguagens imperativas). Seu comportamento é mais próximo de um padrão, que é incrementalmente especificado pelaunificação. Em outras palavras, uma variável Prolog é como uma incógnita, cujo valor é desconhecido a princípio mas, após descoberto, não sofre mais mudanças.
Um tipo especial de variável, avariável anônima (explicada mais adiante), é uma expressão que significa 'qualquer variável', e é escrita como um único subtraço (_).
Termos compostos são a única forma de se expressar estruturas de dados complexas em Prolog. Um termo composto consiste de uma cabeça, também chamada funtor (que é obrigatoriamente um átomo) e parâmetros (de quaisquer tipos) listados entre parênteses e separados por vírgulas.
O número de parâmetros, chamadoaridade do termo, é significativo. Um termo é identificado por sua cabeça e aridade, normalmente escrita como funtor/aridade. Átomos e números também podem ser identificados dessa forma, como um termo de aridade zero (ex: um_atomo/0).
Uma lista não é um tipo de dados à parte, mas sim definida por uma construção recursiva (usando o termo '.'/2):
o átomo [] é uma lista vazia;
seT é uma lista eH é um elemento, então o termo '.'(H,T) é uma lista.
O primeiro elemento, chamado cabeça, éH, que é seguida pelo conteúdo do restante da lista,T, também chamado de cauda.A lista [1, 2, 3] seria representada internamente como '.'(1, '.'(2, '.'(3, []))).Um atalho sintático é [H |T], que é mais usado para construir regras.Uma lista pode ser processada como um todo processando o primeiro elemento, e em seguida o restante da lista, de formarecursiva.
Para conveniência do programador, as listas podem ser construídas e destruídas de várias formas.
Enumerando os elementos: [abc, 1, f(X),Y, g(A,rst)]
Precedendo-a com um elemento: [abc |L1]
Precedendo-a com múltiplos elementos: [abc, 1, f(X) |L2]
Expandindo o termo: '.'(abc, '.'(1, '.'(f(X), '.'(Y, '.'(g(A,rst), [])))))
Strings são normalmente escritas como uma sequência de caracteres entre aspas. É comum serem representadas internamente como listas de códigos de caracteres, em geral utilizando a codificação local ouUnicode, se o sistema dá suporte a Unicode. O ISO Prolog também permite que strings sejam representadas por uma lista de átomos com um único caractere.
Programar em Prolog é bem diferente de programar em uma linguagem procedimental. Em Prolog se fornece fatos e regras para uma base de dados; então se executam consultas ou (queries) a essa base de dados. A unidade básica do Prolog é o predicado, que é postulado verdadeiro. Um predicado consiste de uma cabeça e um número de argumentos. Por exemplo:
gato(tom).
Isso informa à base de dados o fato que 'tom' é um 'gato'. Formalmente, 'gato' é a cabeça e 'tom' é o único argumento do predicado. Alguns exemplos de consultas que podem ser feitas ao interpretador Prolog baseado nesse fato:
tom é um gato?
?-gato(tom).yes.
que coisas (conhecidas) são gatos?
?-gato(X).X=tom;yes.
Predicados são normalmente definidos para expressar algum fato sobre o mundo que o programa deve conhecer. Na maioria dos casos, o uso de predicados requer uma certa convenção. Por exemplo, qual das duas versões abaixo significaria que José é o pai de Ana?
pai(ana,jose).pai(jose,ana).
Em ambos os casos 'pai' é a cabeça e 'ana' e 'jose' são argumentos. Entretanto, no primeiro caso Ana vem primeiro na lista de argumentos, e no segundo, quem vem primeiro é José (a ordem nos argumentos é importante). O primeiro caso é um exemplo de definição na ordemVerbo Sujeito Objeto, e o segundo, na ordemVerbo Objeto Sujeito. Como Prolog não entende português, ambas as versões estão corretas de acordo com seu escopo; no entanto é uma boa prática de programação escolher uma única convenção para ser usada no mesmo programa, para evitar escrever algo como
pai(jose,ana).pai(maria,joao).
Alguns predicados são pre-definidos na própria linguagem, permitindo que os programas Prolog desempenhem atividades rotineiras (como entrada/saída, uso de gráficos e outros tipos de comunicação com o sistema operacional). Por exemplo, o predicadowrite pode ser usado para saída na tela. Então,
O segundo tipo de predicado no Prolog é a regra, também chamada de "cláusula". Um exemplo de uma regra é:
luz(acesa):-interruptor(ligado).
O ":-" significa "se"; essa regra significa que luz(acesa) é verdadeiro se interruptor(ligado) é verdadeiro. Regras podem também fazer uso de variáveis, como por exemplo,
avo(X,Z):-pai(X,Y),pai(Y,Z).
(X é avô de Z se X é pai de Y e Y é pai de Z)
Isso significa "se alguém é pai de outra pessoa, que por sua vez é pai de uma terceira, então ele é avô". O antecedente e o consequente estão na ordem inversa do que é normalmente encontrado na notação da lógica: o consequente é escrito primeiro e é chamado acabeça da regra, o antecedente é chamadocorpo. A conjunção (e) é escrita como ",", enquanto a disjunção (ou) é escrita como ";". Também é possível colocar múltiplos predicados em um mesmo corpo, unindo seus antecedentes por disjunção, como por exemplo:
a:-b;c;d.
que é equivalente às três regras separadas:
a:-b.a:-c.a:-d.
No entanto não são permitidas regras como:
a;b:-c.
Ou seja, "se c então a ou b". Isso é devido à restrição àscláusulas de Horn.
Uma maneira de simular tal regra, usando o operador de negação, é:
Regrasrecursivas devem ser permitidas a fim de tornar a linguagem útil para muitas aplicações. Um predicado definido por uma regra recursiva deve necessariamente ter, no mínimo uma definição não recursiva. Se isto não acontecer, a definição é logicamente mal-formada e o programa ficaria emlaço infinito.Um exemplo de regra recursiva seria a definição de uma base de dados sobre relações familiares que responda questões sobre ancestralidade. Isto pode ser definido da seguinte forma:
Além disso, é necessário tomar cuidado com a ordem na qualunificações(ver Unificação em Prolog) são procurados para objetivos. Se invertermos a ordem nas regras recursivas do predicado ancestral, isto é
Uma função pode ser definida por recursão em cauda da seguinte maneira:
f(x): se c(x) então g(x) senão f(h(x))
onde:- é algum tipo de condição sobre o valor de;- são funções definidas com um argumento não definido na função.
A mesma sentença pode ser escrita em Prolog desta forma:
f(X,Z):-c(X),g(X,Z).f(X,Z):-h(X,Y),f(Y,Z).
Recursão em cauda é importante e preferível sobre recursão não cauda pois pode ser implementada como uma iteração que é computada usando uma pilha estática.
Recursão em não cauda usa um espaço linear na pilha, consequentemente evitado caso não seja necessário. Em alguns casos é possível otimizar a implementação.Considere o esquema:
f(n): se n = 0 então a senão k(n, f(n - 1))
Isto pode ser escrito em Prolog usando recursão não em cauda:
f(0,a).f(X,Y):-X1isX-1,f(X1,Y1),k(X,Y1,Y).
Este código requer espaço linear na pilha para guardar resultados temporários de chamadas recursivas. Usando um acumulador, o código abaixo pode ser escrito usando recursão em cauda:
Quando o interpretador recebe uma consulta, ele tenta encontrar predicados que se encaixam na consulta, sejam eles fatos diretos ou regras que possuem o termo consultado como conclusão. Por exemplo:
irmaos(X,Y):-filho(X,Z),filho(Y,Z).
que em Lógica de Primeira ordem é:XYZ((filho(X,Z)^filho(Y,Z))irmãos(X,Y))
De acordo com essa base, a seguinte consulta é avaliada como verdadeira:
?-irmaos(ana,erica).yes.
O interpretador chega a esse resultado utilizando a regrairmaos(X,Y),unificandoana comX eerica comY. Isso significa que a consulta pode ser expandida parafilho(ana,Z),filho(erica,Z). Aresolução dessa conjunção é feita procurando-se todos os pais possíveis paraana. Entretanto,filho(ana,marcia) não leva a uma solução viável, porque seZ for substituído pormarcia,filho(erica,marcia) deveria ser verdadeiro, e nenhum fato afirma (nem há nenhuma regra que possa satisfazer) que isso está presente. Então, em vez disso,Z é substituído portomas, descobrindo-se queerica eana são irmãos de qualquer forma. O código
filho(X,Y):-pai(Y,X).
pode parecer suspeito. Afinal, nem só pais têm filhos. No entanto esse código significa, na verdade, que todo o pai tem filhos (da mesma forma que a regra seguinte significa que toda a mãe tem filhos). Para descobrir se alguém é pai, pode-se usar o código
?-pai(X,_).
ou
pai(X):-pai(X,_).
que simplesmente não se importa com quem é o filho (o underscore "_" é uma variável anônima).
Tipicamente, uma consulta é avaliada como falsa no caso de não estar presente nenhuma regra positiva ou fato que dê suporte ao termo proposto. Isso é chamadohipótese do mundo fechado; assume-se que tudo o que é importante saber está na base de dados, de modo que não existe um mundo exterior que pode possuir evidências desconhecidas. Em outras palavras, se um fato não é conhecido ser verdadeiro (ou falso), assume-se que ele é falso.
Uma regra como
legal(X):-\+ilegal(X).
pode ser avaliada somente pela busca exaustiva de todas as coisas que são ilegais e comparando elas com X, e se nenhum fato ilegal for descoberto ser o mesmo que X, X é legal. Isso é chamadonegação por falha. O operador prefixo \+/1 (muitos dialetos do Prolog possuem pré-definido o comandonot/1) usado acima implementa a negação por falha em compiladores ISO Prolog.
Backtracking é um procedimento dentro dalinguagem Prolog. Uma busca inicialem um programa nesta linguagem segue o padrãoBusca em profundidade (depth-first search), ou seja, aárvore é percorrida sistematicamente de cima para baixo e da esquerda para direita. Quando essa pesquisa falha, ou é encontrado umnó terminal da árvore, entra em funcionamento o mecanismo de backtracking. Esse procedimento faz com que o sistema retorne pelo mesmo caminho percorrido com a finalidade de encontrar soluções alternativas.
Exemplo:
Considerando uma base de dados família, fazemos a seguinte consulta:
?-pai(roberto,X),mae(vera,X)
O compilador tenta satisfazer o primeiro objetivo. Quando conseguir, tenta satisfazer o segundo. Caso não consiga, ele retorna ao ponto onde encontrou a solução para o primeiro objetivo (backtracking).
O comandocut permite indicar ao Prolog quais sub-objetivos já satisfeitos não necessitam ser reconsiderados ao se realizar umbacktracking. Isto é, ele aborta o processo de backtracking.O uso do comando cut é importante porque permite que o programa rode mais rápido, sem perder tempo com sub-objetivos que não contribuem para determinar a resposta do objetivo principal. Além disso, o programa ocupará menos memória, pois não será necessário armazenar todos os sub-objetivos considerados (pontos do backtracking). Em alguns casos, o cut evita que o programa entre emlaço infinito.
Exemplo (o código abaixo faz a consulta ao banco e para na primeira ocorrência de filho do sexo masculino):
primogenito(X,Y):-pai(Y,X),masculino(X),!
Algumas das principais aplicações do cut são as seguintes:
• Unificação de padrões, de forma que quando um padrão é encontrado os outros padrões possí-veis são descartados • Na implementação da negação como regra de falha • Para eliminar da árvore de pesquisa soluções alternativas quando uma só é suficiente • Para encerrar a pesquisa quando a continuação iria conduzir a uma pesquisa infinita, etc.
Sintaticamente o uso do cut em uma cláusula tem a aparência de um objetivo sem nenhum argumento,representado por um ponto de exclamação "!".
Inversamente ao comandocut, o predicado pré-definidofail sempre falha. O operador de corte pode ser combinado com o predicadofail para produzir uma falha forçada.Uma conjunção de objetivos da forma
é usada para informar ao PROLOG:se a execução chegou até esse ponto, então pode abandonar a tentativa de satisfazer a regra. A conjunção falha devido aofail, e o objetivo-pai falha devido ao corte.
Exemplo (Ana gosta de mamíferos, exceto de gatos):
Prolog é uma linguagem deprogramação lógica, portanto em teoria o programador não deveria ter de se preocupar com o modo como ela executa. Entretanto, às vezes é prudente levar em conta como oalgoritmo de inferência funciona, para evitar que o programa Prolog execute por um tempo denecessariamente longo (ou mesmo infinito).
Por exemplo, pode-se escrever um código para contar o número de elementos em uma lista.
elems([],0).elems([H|T],X):-elems(T,Y),XisY+1.
Isso simplesmente diz:Se a lista está vazia, o número de elementos é zero.Se a lista não é vazia, então X é um a mais que Y, que por sua vez é o número de elementos no restante da lista, excluído-se seu primeiro elemento.
Nesse caso, existe uma distinção clara entre os casos no antecedente das regras. Mas no caso a seguir, onde se decide por continuar ou não jogando em um cassino:
Se você tem dinheiro, você continua jogando. Se você perdeu todo o dinheiro, você precisa pegar emprestado, ou então não é possível jogar mais.temdinheiro(X) pode ser uma função muito custosa - por exemplo, ela pode acessar sua conta de banco na internet para verificar seu saldo, o que leva tempo. Entretanto, o mesmo pode-se dizer de temcredito(X).
Em teoria, as implementações Prolog poderiam avaliar essas regras fora de ordem, de modo que elas poderiam ser escritas como:
Isso está correto, porque as duas opções se excluem mutuamente. Entretanto, verificar se você precisa de um empréstimo não é necessário se você já sabe que tem dinheiro. Então, na prática, implementações Prolog vão verificar a primeira regra primeiro (de fato, a maioria delas vai sempre tentar as regras na ordem em que estão presentes na base). Pode-se usar o operador de corte para informar ao interpretador para não testar a segunda opção se a primeira é suficiente.Por exemplo:
Isso é chamado umoperador de corteverde. O ! simplesmente informa ao interpretador para parar de buscar por alternativas. Mas deve-se notar que se você precisa de um empréstimo será necessário verificar a segunda regra, e isso será feito. Verificando o temdinheiro na segunda regra é desnecessário, já que é conhecido o fato de que você não tem, ou a segunda regra não seria sequer avaliada. Então pode-se modificar o código para
Isso é chamado umoperador de cortevermelho, porque é arriscado fazer isso. O programa agora depende da colocação correta do operador de corte e da ordem das regras para determinar seu significado lógico. Acidentes de recorta-e-cola por exemplo são comuns e difíceis de detectar. Se as regras forem misturadas, você pode terminar estourando seu cartão de crédito antes de gastar seu dinheiro.
Existe uma notação especial chamadagramática de cláusulas definidas (definite clause grammar - DCGs). Uma regra definida via -->/2 em vez de :-/2 é expandida pelo pré-processador (expand_term/2, uma facilidade análoga às macros em outras linguagens) de acordo com algumas regras de reescrita, resultando em cláusulas Prolog ordinárias. DCGs são usadas para escreverparsers e geradores de listas, e também provém uma interface conveniente para operações sobre diferenças de listas.
A resolução do problemaTorre de Hanói implementado em prolog.
hanoi(N):-move(N,left,center,right).move(0,_,_,_):-!.move(N,A,B,C):-MisN-1,move(M,A,C,B),inform(A,B),move(M,C,B,A).inform(X,Y):-write('move a disc from the '),write(X),write(' pole to the '),write(Y),write(' pole'),nl.
Visual Prolog (http://www.visual-prolog.com/), também conhecido como PDC Prolog e Turbo Prolog. Visual Prolog é um dialeto de Prolog fortemente tipado que é consideravelmente diferente do Prolog padrão. Foi desenvolvido e divulgado como Turbo Prolog enquanto sob controle da Borland, mas hoje é desenvolvido pela empresa Danish PDC (Prolog Development Center) que foi quem o criou.
BEDREGAL, Benjamín Callejas; ACIOLY, Benedito Melo. Lógica para Ciência da Computação, versão preliminar 2006.
Sterling, Leon; Shapiro, Ehud (1986).The Art of Prolog. Advanced Programming Techniques. Cambridge: The MIT Press. 437 páginas.ISBN0-262-19250-0 !CS1 manut: Nomes múltiplos: lista de autores (link)
Referências
↑BERGIN, Thomas J.; GIBSON, Richard G. (1996).History of Programming Languages II. New York: ACM Press, Addison-Wesley. 864 páginas.ISBN0-201-89502-1 !CS1 manut: Nomes múltiplos: lista de autores (link)