Descobrindo o Prolog
(http://www.linhadecodigo.com.br/artigo/1697/descobrindo-o-prolog.aspx em 24/06/2021)
Informações Gerais
O Prolog foi criado em 1972, na Universidade de Marseille, França. Desde então tem sido utilizada para aplicações de computação simbólica, como banco de dados relacionais, compreensão de linguagens naturais (português, inglês, etc.), automação de projetos, análise de estruturas bioquímicas e sistemas especialistas. Como podemos ver, o Prolog se tornou uma referência quando se trata de linguagem de programação voltada para inteligência artificial e linguística computacional.
Características
O Prolog é uma linguagem declarativa, ou seja, ao invés de o programa estipular a maneira de chegar à solução passo-a-passo, como acontece nas linguagens procedimentais ou orientadas a objeto, ele fornece uma descrição do problema que se pretende computar utilizando uma coleção de fatos e regras (lógica) que indicam como deve ser resolvido o problema proposto. Como podemos ver, o Prolog é mais direcionado ao conhecimento do que aos próprios algoritmos.
Além de ser uma linguagem declarativa, outro fato que o difere das outras linguagens é a questão de não possuir estruturas de controle (if-else, do-while, for, switch) presentes na maioria das linguagens de programação. Para isso utilizamos métodos lógicos para declarar como o programa deverá atingir o seu objetivo.
Um programa em Prolog pode rodar em um modo interativo, o usuário poderá formular queries utilizando os fatos e as regras para produzir a solução através do mecanismo de unificação.
Conceitos Básicos
As categorias de dados comumente existentes em outras linguagens, não são em empregados ao Prolog. Todos os dados são tratados como sendo de um único tipo, conhecido como termo, que pode ser uma constante, uma variável ou um termo composto. Em seguida conheceremos os principais elementos da linguagem para podermos fazer pequenos exemplos em Prolog.
Fatos
Como vimos, programar em Prolog é bem diferente de programar em uma linguagem procedimental. Em Prolog são fornecidos os fatos e as regras para uma base de dados, que serão posteriormente executadas consultas (queries) em cima da base de dados.
A estrutura de um fato é formada por um predicado, seus argumentos (objetos) e finalizamos a instrução com um ponto(.) equivalente ao ponto-vírgula das linguagens comuns de programação. Veja a seguir:
predicado(argumento1,argumento2…).
O predicado é a relação sobre os quais os objetos irão interagir.
Exemplos:
amiga(joana, maria).
Definimos uma relação de amizade entre dois objetos, joana e maria.
homem(jose).
Note que quando usamos apenas um objeto, o predicado passa a ser uma característica do próprio objeto.
Então apenas para concluir, é importante ressaltar que os nomes dos predicados e dos objetos devem sempre começar por letra minúscula como devem ter notado nos exemplos anteriores. Os predicados são escritos primeiro, seguido pelos objetos que são separados por vírgula. Também é importante lembrar de que a ordem dos objetos poderá interferir no resultado de uma aplicação.
Questões
A sintaxe das questões varia conforme os compiladores existentes, mas basicamente é um fato antecedido de um ponto de interrogação ou o comando apropriado para o tipo de compilador. Por exemplo:
?- amiga(joana,maria).
Quando uma questão é feita, o Prolog realiza uma busca na sua base de conhecimento, procurando um fato que seja igual ao da questão. Se o fato for encontrado é retornado “YES”, caso contrário o programa retornará a saída “NO”.
Variáveis
No Prolog uma variável não é um contêiner cujo valor pode ser atribuído como ocorre nas outras linguagens. Inicialmente uma variável é uma incógnita, cujo valor é desconhecido, mas quando instanciamos um objeto a ela, a mesma não poderá ser mais modificada. Vejamos um exemplo.
Primeiramente forneceremos uma seqüência de fatos para a base de conhecimento:
gosta(joão,flores).
gosta(joão,maria).
gosta(paulo,maria).
Em seguida realizaremos uma questão:
?- gosta(joão,X).
O que acontece quando compilarmos essa instrução?
Inicialmente a variável X encontra-se com um valor desconhecido (não-instanciado), o Prolog procura então por um fato na base de conhecimentos que se iguale a questão, ou seja, ele irá procurar algum fato que tenha o mesmo predicado, o mesmo número de argumentos e que o primeiro argumento seja “joão”. É importante saber que a busca é realizada na ordem em que os fatos foram passados. Então no nosso exemplo, o objeto flores será atribuída à variável X.
Para concluir, devo lembrar que as variáveis devem começar sempre com letra maiúscula ou o caractere underscore. É importante lembrar que uma vez que uma variável é instanciada, ela não poderá mudar o objeto.
Conjunção e Disjunção
Para montarmos uma lógica mais específica nas nossas questões, podemos utilizar os conceitos de conjunção e disjunção. Equivalente ao “AND” e o “OR” de outras linguagens, podemos realizar uma consulta mais avançada separando os fatos por vírgulas no caso de conjunção (AND), ou ponto e vírgula no caso de disjunção (OR).
Dados os seguintes fatos:
amiga(joana,maria).
amiga(clara,maria).
Podemos realizar a seguinte questão:
?- amiga(joana,X),amiga(clara,X).
No exemplo anterior fornecemos dois fatos, e realizamos uma questão cruzando-os. Como maria é amiga de joana e carla, a variável X receberá o objeto Maria, e a questão retornará “YES”.
Agora vamos ver um outro exemplo aproveitando os mesmo fatos:
?- amiga(joana,X);amiga(roberta,X).
Nesse exemplo utilizamos o operador de disjunção (OR), ou seja, se uma das questões coincidirem com os fatos, será retornada “YES”. No nosso exemplo, será retornado “YES” e o X será atribuído ao objeto “maria”, pois apresentam o mesmo predicado, o mesmo número de objetos, e o primeiro objeto, é o mesmo do existente na base de dados.
Regras
Utilizamos as regras para fazermos a construção de questões complexas. Especificamos situações que podem ser verdadeiras se algumas condições forem satisfeitas.
Para utilizarmos uma regra, usamos o símbolo “:-” que indica uma condição (se). Vamos exemplificar o uso da regra com um exemplo simples.
Dados os fatos:
pai(arthur,silvio).
pai(arthur,carlos).
pai(carlos,xico).
pai(silvio,ricardo).
Utilizaremos a seguinte regra:
avo(X,Z) :- pai(X,Y), pai(Y,Z).
Isso significa que se alguém é pai de uma pessoa, que por sua vez é pai de outra pessoa, então ele é avô. Vamos realizar uma querie para conferir a regra:
?- avo(arthur,xico),avo(arthur,ricardo).
Retornará a saída: “YES”
Ou seja, “arthur” é avô de “xico” e “ricardo”, pois “arthur” é pai de “silvio” e “carlos”, que por sua vez são pais de “xico” e “ricardo”.
Operadores
Podemos utilizar operadores para construirmos regras ainda mais específicas, no Prolog existem tanto os operadores relacionais quanto os aritméticos.
Entre os operadores relacionais temos:
Igualdade: =
Diferença: \= (em alguns compiladores o operador de diferença é “<>”)
Menor que: <
Maior que: >
Menor ou igual: =< (alguns compiladores seguem a versão “>=”)
Maior ou igual: >=
Vamos construir um pequeno exemplo com operadores relacionais para verificar se o número passado é positivo ou negativo.
Para isso, construiremos a seguinte regra:
positivo(numero) :- numero > 0.
Em seguida realizaremos uma consulta:
?- positivo(2).
Que retornará “Yes”.
Agora vamos aos operadores ariméticos. São eles:
+, -, *, /, mod, is.
O operador é utilizado quando quisermos obter o módulo, e o operador is é utilizado para obtermos um resultado.
Verifique a seguinte regra:
o_dobro(Numero,Result) :- Result is Numero * 2.
Realizaremos a questão:
?- o_dobro(5,X).
Obteremos o resultado:
X = 10
É importante lembrar que uma variável terá que receber o valor da operação aritmética através do operador “is”.
Entrada e Saída
Já vimos como atribuir os fatos, como construir regras e fazer questões sobre elas, mas tudo que recebemos como retorno, foi um “Yes” ou “No”. Assim como nas outras linguagens, o Prolog também possui propriedades de entrada e saída de dados, são eles read() e write().
Vamos ver um pequeno exemplo:
ola :- read(X), write(“Olá “), write(X).
Faremos a chamada:
?- ola. “Luciano”.
Note que a regra não possuiu nenhum parâmetro, portanto colocamos apenas o nome da regra sem atributos, e em seguida o dado a ser lido pelo comando read(X), lembrando que cada instrução sempre é finalizada com um ponto(.).
É importante ressaltar que dados equivalentes ao tipo “String” de outras linguagens, devem sempre ser utilizados entre apóstrofos, caso contrário, quando digitássemos o nome “Luciano”, o compilador iria aceitar a entrada como uma variável não alocada, e imprimiria um valor estranho na saída.
Experimentado
Para fazer alguns testes usaremos o ambiente IDE Web localizado no link – LINK.