Prolog
Prolog | |
---|---|
Paradigma | lógico, declarativo |
Surgido em | 1972 |
Criado por | Alain Colmerauer e Robert Kowalski |
Principais implementações | GNU Prolog, Quintus, SICStus, SWI-Prolog, YAP |
Dialetos | ISO Prolog, Edinburgh Prolog |
Influenciou | Mercury, Oz |
Prolog (Programação Lógica) é uma linguagem 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 a inteligência artificial e linguística computacional. Consiste numa linguagem puramente lógica, que pode ser chamada de Prolog puro, e numa linguagem concreta, a qual acrescenta o Prolog puro com componentes extra-lógicos.
O uso Prolog puro foi originalmente restrito em provas do teorema da resolução com Cláusulas de Horn do formato
H :- B1, …, Bn.
.
A aplicação do provador de teoremas trata estas cláusulas como procedimentos
- para mostrar/resolver
H
, mostrar/resolverB1
and … andBn
.
O Prolog puro foi então estendido para incluir a negaçã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 nome Prolog para a linguagem concreta foi escolhido por Philippe Roussel como uma abreviação de “PROgrammation en LOGique”. Foi criada em meados de 1972 por Alain Colmerauer e Philippe Roussel, baseados no conceito de Robert 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 de computadores da quinta geração (FGCS), que desenvolveu uma variante do Prolog chamada Kernel Language para seu primeiro sistema 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.
História
[editar | editar código-fonte]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].
Características
[editar | editar código-fonte]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ção base de dados de fatos 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.
O Prolog é baseado num subconjunto do cálculo de predicados de primeira ordem, o que é definido por cláusulas de Horn. A execução de um programa em Prolog é efetivamente a prova de um teorema por resolução de primeira ordem. Alguns conceitos fundamentais são unificação, recursão, e backtracking.
Tipos de dados
[editar | editar código-fonte]Prolog não emprega tipos 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.
Escopo dos identificadores
[editar | editar código-fonte]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.
Átomos
[editar | editar código-fonte]As constantes de texto são introduzidas por meio de átomos. Um átomo é uma sequência constituída de letras, números e underscore, 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:
'quem é você?' 'eu não sei'.
Números
[editar | editar código-fonte]Um número é uma sequência de dígitos, permitindo também os sinais de . (para números reais), - (número negativo) e e (notação científica). Algumas implementações do Prolog não fazem distinção entre inteiros e números reais.
exemplos:
321 3.21
Variáveis
[editar | editar código-fonte]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 é um contêiner cujo valor pode ser atribuído (como ocorre nas linguagens imperativas). Seu comportamento é mais próximo de um padrão, que é incrementalmente especificado pela unificaçã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, a variável anônima (explicada mais adiante), é uma expressão que significa 'qualquer variável', e é escrita como um único subtraço (_).
exemplos:
X Nome Rei_da_Espanha
Termos compostos
[editar | editar código-fonte]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, chamado aridade 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).
Listas
[editar | editar código-fonte]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;
- se T é uma lista e H é 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 forma recursiva.
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), [])))))
- O predicado append
Strings
[editar | editar código-fonte]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 ou Unicode, 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.
Fatos
[editar | editar código-fonte]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 ordem Verbo Sujeito Objeto, e o segundo, na ordem Verbo 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 predicado write
pode ser usado para saída na tela. Então,
write('Olá').
vai exibir a palavra 'Olá' na tela.
Regras
[editar | editar código-fonte]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 a cabeça da regra, o antecedente é chamado corpo. 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 às cláusulas de Horn.
Uma maneira de simular tal regra, usando o operador de negação, é:
a:-c,not(b).
Regras Recursivas
[editar | editar código-fonte]Regras recursivas 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 em laç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:
ancestral(X,Y) :- mãe(X,Y).
ancestral(X,Y) :- pai(X,Y).
ancestral(X,Y) :- mãe(X,Z),ancestral(Z,Y).
ancestral(X,Y) :- pai(X,Z),ancestral(Z,Y).
Além disso, é necessário tomar cuidado com a ordem na qual unificações(ver Unificação em Prolog) são procurados para objetivos. Se invertermos a ordem nas regras recursivas do predicado ancestral, isto é
ancestral(X,Y) :- ancestral(Z,Y),mãe(X,Z).
ancestral(X,Y) :- ancestral(Z,Y),pai(X,Z).
a consulta resultará uma recursão infinita.
Recursão em cauda
[editar | editar código-fonte]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 não em cauda
[editar | editar código-fonte]Recursão não em cauda pode ser definida pela sentença
f(x): se c(x) então g(x) senão k(x, f(h(x)))
Isto significa que o valor da chamada de recursão é modificado após sua computação. Em Prolog, esta função pode ser implementada assim:
f(X, Y) :- c(X), g(X, Y).
f(X, Y) :- h(X, X1), f(X1, Y1), k(X, Y1, Y).
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) :- X1 is X - 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:
f(X, Y) :- f(X, 1, a, Y).
f(X, M, ACC, ACC) :- M > N.
f(X, M, ACC, Y) :- M <= N, k(M, ACC, ACC1), M1 is M + 1, f(N, M1, ACC1, Y)
Avaliação
[editar | editar código-fonte]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))
filho(X,Y) :- pai(Y,X).
filho(X,Y) :- mae(Y,X).
mae(marcia, ana).
pai(tomas, ana).
pai(tomas, erica).
pai(marcos, tomas).
De acordo com essa base, a seguinte consulta é avaliada como verdadeira:
?- irmaos(ana, erica).
yes.
O interpretador chega a esse resultado utilizando a regra irmaos(X,Y), unificando ana com X e erica com Y. Isso significa que a consulta pode ser expandida para filho(ana,Z), filho(erica,Z). A resolução dessa conjunção é feita procurando-se todos os pais possíveis para ana. Entretanto, filho(ana,marcia) não leva a uma solução viável, porque se Z for substituído por marcia, 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 por tomas, descobrindo-se que erica e ana 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).
Negação
[editar | editar código-fonte]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 é chamado hipó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 é chamado negação por falha. O operador prefixo \+/1 (muitos dialetos do Prolog possuem pré-definido o comando not/1) usado acima implementa a negação por falha em compiladores ISO Prolog.
Operadores de Controle
[editar | editar código-fonte]Backtracking
[editar | editar código-fonte]Backtracking é um procedimento dentro da linguagem Prolog. Uma busca inicial em um programa nesta linguagem segue o padrão Busca 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 um nó 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).
Comando Cut
[editar | editar código-fonte]O comando cut permite indicar ao Prolog quais sub-objetivos já satisfeitos não necessitam ser reconsiderados ao se realizar um backtracking. 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 em laço infinito.
cabeça :- objetivo<sub>1</sub>, ..., objetivo<sub>n</sub>, !, objetivo<sub>n+1</sub>, ..., objetivo<sub>n+m</sub>
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 "!".
Comando Fail
[editar | editar código-fonte]Inversamente ao comando cut, o predicado pré-definido fail sempre falha. O operador de corte pode ser combinado com o predicado fail para produzir uma falha forçada. Uma conjunção de objetivos da forma
cabeça :- objetivo<sub>1</sub>, ..., objetivo<sub>n</sub>, !, fail.
é 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 ao fail, e o objetivo-pai falha devido ao corte.
Exemplo (Ana gosta de mamíferos, exceto de gatos):
mamifero(X) :- gato(X).
mamifero(X) :- cachorro(X).
mamifero(X) :- rato(X).
gato(tom).
rato(jerry).
cachorro(spike).
gosta(ana,X) :- gato(X),!,fail.
gosta(ana,X) :- mamifero(X).
%consultas
%gosta(ana,tom). % false
%gosta(ana,jerry). % true.
%gosta(ana,X). % false - resultado inadequado
Execução
[editar | editar código-fonte]Prolog é uma linguagem de programaçã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 o algoritmo 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), X is Y + 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:
jogar(X) :- temdinheiro(X).
jogar(X) :- temcredito(X), \+ temdinheiro(X).
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:
jogar(X) :- temcredito(X), \+ temdinheiro(X).
jogar(X) :- temdinheiro(X).
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:
jogar(X) :- temdinheiro(X),!.
jogar(X) :- temcredito(X), \+ temdinheiro(X).
Isso é chamado um operador de corte verde. 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
jogar(X) :- temdinheiro(X),!.
jogar(X) :- temcredito(X).
Isso é chamado um operador de corte vermelho, 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.
DCGs e parsing
[editar | editar código-fonte]Existe uma notação especial chamada gramá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 escrever parsers e geradores de listas, e também provém uma interface conveniente para operações sobre diferenças de listas.
Exemplos de código
[editar | editar código-fonte]Seguem alguns exemplos de programas escritos em ISO-Prolog.
Hello World
[editar | editar código-fonte]Um exemplo de uma consulta:
?- write('Hello World!'), nl.
Hello World!
true.
?-
Quicksort
[editar | editar código-fonte]O algoritmo do quicksort, usado para ordenação de listas.
split(H, [A|X], [A|Y], Z) :-
order(A, H), split(H, X, Y, Z).
split(H, [A|X], Y, [A|Z]) :-
not(order(A, H)), split(H, X, Y, Z).
split(_, [], [], []).
quicksort([], X, X).
quicksort([H|T], S, X) :-
split(H, T, A, B),
quicksort(A, S, [H|Y]),
quicksort(B, Y, X).
Torres de Hanoi
[editar | editar código-fonte]A resolução do problema Torre de Hanói implementado em prolog.
hanoi(N) :- move(N, left, center, right).
move(0, _, _, _) :- !.
move(N, A, B, C) :-
M is N-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.
Mergesort
[editar | editar código-fonte]Algoritmo de ordenação de listas Merge sort implementado em prolog.
split([], K, [], []).
split(XS, K, [], XS) :-
K < 1.
split([X|XS], K, [X|YS], ZS) :-
K >= 1,
P is K -1,
split(XS, P, YS, ZS).
merge1([], [], []).
merge1(XS, [], XS).
merge1([], YS, YS).
merge1([X|XS], [Y|YS], [X|ZS]) :-
X =< Y,
merge1(XS, [Y|YS], ZS).
merge1([X|XS], [Y|YS], [Y|ZS]) :-
Y < X,
merge1([X|XS], YS, ZS).
mergesort([], []).
mergesort([X], [X]).
mergesort([X, Y], [X, Y]) :-
X =< Y, !.
mergesort([X, Y], [Y, X]) :-
X > Y, !.
mergesort(XS, ZS) :-
length(XS, L),
L > 0,
K is L / 2,
split(XS, K, XS1, XS2),
mergesort(XS1, YS1),
mergesort(XS2, YS2),
merge1(YS1, YS2, ZS), !.
Implementações
[editar | editar código-fonte]- LPA Prolog (http://www.lpa.co.uk/)
- Open Prolog (http://www.cs.tcd.ie/open-prolog/)
- Ciao Prolog (http://www.clip.dia.fi.upm.es/Software/Ciao)
- GNU Prolog (http://www.gprolog.org)
- YAP Prolog (https://web.archive.org/web/20040706004345/http://www.ncc.up.pt/~vsc/Yap/)
- SWI Prolog (http://www.swi-prolog.org)
- Strawberry Prolog (http://www.dobrev.com/)
- SICStus Prolog (http://www.sics.se/sicstus/)
- Amzi! Prolog (http://www.amzi.com/)
- B-Prolog (http://www.probp.com/)
- tuProlog (http://tuprolog.sourceforge.net/) - Código Aberto integrável ao Java
- XSB (http://xsb.sourceforge.net/)
- Trinc Prolog (http://www.trinc-prolog.com)
- hProlog (http://www.cs.kuleuven.ac.be/~bmd/hProlog/)
- ilProlog (https://web.archive.org/web/20090830031456/http://www.pharmadm.com/dmax.asp)
- CxProlog (http://ctp.di.fct.unl.pt/~amd/cxprolog/)
- NanoProlog (http://ctp.di.fct.unl.pt/~amd/cxprolog/)
- Visual Prolog (http://visual-prolog.com/)
- Schelog (https://ds26gte.github.io/schelog/index.html)
Extensões
[editar | editar código-fonte]- 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.
Referências
[editar | editar código-fonte]- Runnable examples
- A Prolog interpreter in a Java applet
- Prolog: The ISO standard
- Fundamental Prolog Tutorial
- Prolog Tutorial
- Visual Prolog Tutorial
- Visual Prolog Examples
- Prolog Development Center
- Alain Colmerauer's and Philippe Roussel's account of the birth of Prolog
- 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. ISBN 0-262-19250-0
- ↑ BERGIN, Thomas J.; GIBSON, Richard G. (1996). History of Programming Languages II. New York: ACM Press, Addison-Wesley. 864 páginas. ISBN 0-201-89502-1