Aritméticos
Operadores aritméticos são os utilizados na aritméticabásica:
ao_quadrado(X,Y) :-
Y is X*X.
E a questão:
?- ao_quadrado(6,X).
X = 36
Deste modo, percebemos que há a possibilidade de construçãode diversas funções matemáticas mais complexas a partir de regras e operadores aritméticos.
ou
pred(estrut(atrib1,...,atrib n), arg).
As estruturas servem para aninhar dados de forma organizada, estruturada.Elas permitem a distinção de atributos para um argumento.Um exemplo de uma estrutura simples seria:
ficha(num,func(nome,end,cargo)).
Os átomos nome , end , cargo sãoos componentes da estrutura func , o que permite o armazenamentode atributos do funcionário de uma empresa, por exemplo. Mas podemosfazer mais níveis de aninhamento:
ficha(num,func(dados_pes(nome,end),cargo(funcao,salario(basico,descontos,vantagens)))).
Podemos ainda construir expressões com variáveis
dotipo:
pred(estrut(atrib,Var),Var).
que podem ser representadas na forma:
Como podemos ver, a estrutura de dados já é criadaem qualquer tipo de comando Prolog, de forma que devemos ter em mente sempreesta estruturação para mais facilmente compreendermos a funçãodo comando que estamos analisando ou trabalhando.
Obviamente as estruturas em Prolog não resumem-se àforma
de seus comandos. Podemos construir estruturas de dados mais complexaspara
auxiliar na implementação de aplicações.
Listas
A forma mais adequada para estruturação de dados emProlog são as listas. Como o nome diz, são estruturas dinâmicasde armazenamento de dados. Elas são utilizadas na construçãode vetores e matrizes dinâmicos de caracteres, números, equaisquer outros dados utilizados. Apresentam-se na forma:
pred([elem,...],[],[elem[elem,..],...],arg,...).
onde elem pode ser qualquer tipo sintático.
Um conceito importante para a utilização das listasé o cabeça-corpo ( | ). Ele é usado para isolarmembros de uma lista. Por exemplo, dada uma lista na forma:
lista([a,b,c,d,e]).
Aplicando-se a questão:
?- lista([X | Y]).
X=a
Y=[b,c,d,e]
Como podemos observar, o operador | permite a separaçãoda cabeça da lista de seu corpo. Isso permite o isolamento dos membrosda lista, facilitando sua análise.
Exemplos de listas:
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
As listas são tipos peculiares de estruturas de dados quetambém podem ser representadas através de árvores.Esse tipo de representação é muito útil quandotrabalhamos com altos níveis de aninhamento. Como, por exemplo:
ficha(123,func(dados_pes([["Silva","Fulano da"],["solteiro",0]],["ruaTal",99,301]),cargo( ["continuo"],[minimo,1]))).
A árvore para representar esta linha de código seria:
Podemos observar, neste exemplo extremado, a aparente complexidadeprovocada pela leitura da linha de código que é desfeitaquando representada em árvore. Nesta representaçãovemos uma nova simbologia com a visualização do ponto ( .), que faz a indicação dos nodos da lista; e da lista vazia( [] ), que indica o final da lista (NULL). Como dito anteriormente,este é um bom recurso para a visualização de expressõescom um maior nível de complexidade, facilitando sua análisee compreensão.
As listas são o principal tipo de estrutura de dados em Prolog,com as quais é possível a construção de vetorese matrizes, com a vantagem de ser uma estrutura de memória dinâmica.
Prolog está provido de predicados básicos que possibilitam
uma estrutura mínima de funcionamento, como os de entrada e saída,
consulta
e inserção . Aqui é importanteobservar que
estes predicados podem variar de uma versão para outrade compiladores/interpretadores
Prolog. Os predicados aqui apresentadossão de uma versão
standard, o que implica que poderãonão existir ou estarem
implementados sob diferentes formas em outrasversões mais sofisticadas
de Prolog.
Entrada e Saída
get0(X) / get(X)
São os predicados utilizados para leitura de caracteres
escritosno teclado e comparação com a variável X.
A diferençaentre ambos é que get0 pega todos os caracteres
digitadose get salta os caracteres não-imprimíveis.
Sua utilização,no entanto, é restrita à leitura
de um caracter por vez.
read(X)
Este predicado destina-se à leitura de termos do teclado.Todo o termo escrito deve terminar com um ponto ( . ). Este termo podeser uma palavra isolada, uma lista de letras ou ainda um string de caracteres.Por exemplo, dada a regra:
oi :- read(X), write('Resposta: '), write(X).
E a questão:
O termo ola é encarado aqui como um argumento que seráassociado à variável X. Por outro lado:
?- oi.
Ola.
Resposta: _00A0
Isso demonstra que Ola, neste contexto, foi considerado uma variávelque está sendo associada à X. Já no caso de listade caracteres, temos:
?- oi.
"Ola, como vai?".
Resposta: [79,108,97,44,32,99,111,109,111,32,118,97,105,63]
Neste caso, o termo é uma lista com os caracteres que estãoentre aspas. Se ao invés de aspas colocarmos apóstrofos,teremos outro tipo de termo:
?- oi.
'Ola, como vai?'.
Resposta: Ola, como vai?
Vemos, assim, que há diversas variantes para a utilizaçãodo
read, dependendo da aplicação que temos em vista.
put(X)
Imprime apenas um caracter por vez. A variável X deve
conterum inteiro correspondente ao caracter que se quer imprimir.
write(X)
Imprime termos associados a X. São válidas as mesmasobservações feitas para read. Por exemplo, seja a regra:
esc(X) :- write(ola), nl, write(X), nl, write("Ola"),nl, write('Ola').
E a questão:
[79,108,97]
Ola
display(X)
Tem funções idênticas a write, porémdistingue os temos sintáticos. Por exemplo, dada a regra:
esc2 :- write(a+b*c), tab(5), display(a+b*c), nl, write([oi,oi]),tab(5), display([oi,oi]).
E a questão:
?-esc2.
a + b *c '+'(a,'*'(b,c))
[oi,oi]'.'(oi,'.'(oi,[]))
Vemos, pois, que o predicado display apresenta os termos na ordemde
tratamento que Prolog dá a eles.
nl
Provoca a quebra da linha, sendo que o próximo comando
deimpressão iniciará numa nova linha.
tab(X)
Coloca uma quantidade de espaços em branco determinada
pelovalor de X.
skip(X)
Processa até que o caracter igual a X seja encontrado.
Consulta e Inserção
consult(X)
Realiza uma consulta a um determinado banco de dados X. Esta consultaconsiste em carregar o conteúdo de X para a memória. Umavez consultado o banco de dados, os fatos e regras nele contidos poderãoser mencionados. O banco de dados X geralmente é um arquivo texto:
?-avo(Avo,gabriel).
no
?-consult('familia.ari').
yes
?-avo(Avo,gabriel).
Avo= fulgencio
Nesse exemplo, a primeira questão não foi possívelser
respondida pela falta do banco de dados 'familia.ari'. Uma vez consultado,ele
fica residente na memória e a questão pode ser respondida.
reconsult(X)
É um predicado complementar a consult . Ele tem
afunção de trocar fatos e regras que tenham sido modificadosde
um banco de dados X residente na memória (já consultado).O
predicado consult pode ser usado para a releitura do banco dedados,
mas ele será replicado na memória, o que promoveráum
erro de avaliação das regras. O predicado reconsulttem
a vantagem de alterar apenas o que foi modificado no arquivo do bancode
dados.
listing(A)
Permite a listagem de uma determinado predicado que estásendo utilizado. Por exemplo:
?- listing(avo).
avo(Avo,Neto):-
macho(Avo),
casal(Avo,_),
filho(Filho,Avo),
pai(Filho,Neto).
asserta(X) / assertz(X)
Estes predicados servem para inserir um novo fato X no banco dedados residente na memória. (Observe que a alteraçãoé somente a nível de memória). A diferençaentre ambos é que asserta insere o fato no início do bancode dados e assertz faz a inserção ao final do banco. Porexemplo:
?- consult('familia.ari').
yes
?- listing(filha).
filha(juliana,marcos).
filha(juliana,mariana).
?- asserta(filha(marcia,marcos)).
yes
?- assertz(filha(marcia,mariana)).
yes
?- listing(filha).
filha(marcia,marcos).
filha(juliana,marcos).
filha(juliana,mariana).
filha(marcia,mariana).
Nesse exemplo, foi inserido um fato no início do banco
dedados e outro no final. Na última listagem podemos observar os
fatosjá existentes junto aos novos.
retract(X)
Realiza a retirada de um fato X do banco de dados existente na memória.É o oposto a asserta / assertz .
not(X)
É o predicado em que tem sucesso quando X falha. Por exemplo:
?- not(macho(mariana)).
yes
repeat
Realiza a repetição da regra até que a próximacláusula
seja válida. Por exemplo, dada a regra:
pega2(X) :- repeat, get0(X).
pega(X) :- pega2(X), X > 32, !.
A regra pega será repetida até que a entrada seja um caracterimprimível.
Prolog provê recursos especiais para facilitar a implementaçãode
regras gramaticais de gramáticas livres de contexto.
Para exemplificar, podemos estruturar as regras de frases simplesem
português: esta frase divide-se em sintagma nominal e sintagmaverbal;
o sintagma nominal pode ser formado por um artigo e por um substantivo;o
sintagma verbal pode ser formado por apenas um verbo ou por um verboe outro
sintagma nominal. Na forma Prolog que vimos até agora, issopoderia
ser descrito assim:
frase(S0,S) :- sn(S0,S1), sv(S1,S).
sn(S0,S) :- art(S0,S1), subs(S1,S).
sv(S0,S) :- verbo(S0,S).
sv(S0,S) :- verbo(S0,S1), sn(S1,S).
art([o|S],S).
art([a|S],S).
subs([homem|S],S).
subs([flor|S],S).
verbo([colhe|S],S).
verbo([canta|S],S).
Ou, na forma facilitada:
frase --> sn, sv.
sn --> art, subs.
sv --> verbo.
sv --> verbo, sn.
art --> [o].
art --> [a].
subs --> [homem].
subs --> [mulher].
subs --> [flor].
verbo --> [colhe].
verbo --> [canta].
Uma questão poderia ser:
?- frase([a,mulher,colhe,a,flor],[]).
yes
Como vemos, a construção de gramáticas torna-sesimplificada
e clara, conforme a notação usual. Prolog é,portanto,
uma ferramenta por excelência para a estruturaçãode
gramáticas, possibilitando a implementação de linguagenspara
diversas aplicações.
Controle de Programa
Podemos controlar a execução de um programa pela ordenação das clausulas e os objetivos dentro de cada clausula.
Backtracking
Quando você efetua uma consulta e o objetivo não é satisfeito usamos o mecanismo Backtracking do PROLOG para tentar ressatisfazer um objetivo, encontrando uma solução alternativa. Quando ocorrer conjunções em uma consulta ele satisfaz na ordem em que foram escritos (da esquerda para direita).
Inicializa-se o backtracking numa consulta digitando (;).
backtracking automático é um conceito de programação útil porque isto desobriga o programador da responsabilidade de programar explicitamente backtracking. Por outro lado, a falta de controle pode causar ineficiência em um programa.
Exemplo:
bebe(lorenzo,coca). bebe(lorenzo,cerveja). bebe(lorenzo,agua). bebe(marcia,fanta). bebe(marcia,gim). bebe(marcia,tonica). | ?- bebe(X,_). X = lorenzo ; X = lorenzo ; X = lorenzo ; X = marcia ; X = marcia ; X = marcia ; noCut
Controla-se a execução de um programa através da ordenação de clausulas e objetivos.
Prolog determina automaticamente backtracking se isso for necessário para satisfazer um objetivo. Portanto, algumas vezes queremos controlar, ou prevenir, backtracking. Faremos isto usando a facilidade do cut.
Iremos ver o comportamento de um simples programa, onde a execução do backtracking é desnecessária. Identificaremos alguns pontos na qual o uso do backtracking é inútil e nos leva a ineficiência.
Considere o exemplo:
Regra 1: if X<3 then Y=0
Regra 2: if 3 < = X and X< 6 then Y=2
Regra 3: if 6<= X then Y=4
Em Prolog isso pode ser escrito como uma relação binária: f(X,Y)
Como as seguintes:
f(X,0) :- X<3. % Regra 1 f(X,2) :- 3 =< X, X<6. % Regra 2 f(X,4) :- 6 =< X. % Regra 3Antes de executar f(X,Y) é executado X.
A seguir mostraremos um experimento com este programa. O experimento revelará algumas origens da ineficiência do programa. Removeremos cada origem usando cut.
Vamos analisar o que acontece quando a seguinte questão é apresentada:
?- f(1,Y), 2 < Y.Quando executamos a primeiro passo, f(1,Y), Y torna-se zero. Logo o segundo passo torna-se 2 < 0 , o qual falha. Isto está certo, mas antes admitimos que a lista de objetivos não é satisfeita. PROLOG tenta, através de backtracking, duas tentativas inúteis
Das três regras sobre a relação de f ser mutuamente exclusivos uma delas é a que obtém maior sucesso. Portanto, nós sabemos, mas PROLOG não, que logo que uma das regras é satisfeita, as outras não serão usadas, do mesmo modo ele é levado ao erro. No exemplo da figura a regra 1 sabe como suceder na questão indicada por cut. A fim de prevenir futilidades do backtracking como nesta questão, nós temos que informar ao PROLOG explicitamente para não efetuar backtracking. Nós podemos fazer isto usando o mecanismo cut. O cut é escrito como (!) e é inserido entre objetivos como um tipo de pseudo-objetivo. Nosso programa rescrito com cut, é:
f(X,0) :- X<3, !. f(X,2) :- 3=<X, X<6, !. f(X,4) :- 6=<X.O símbolo (!) agora prevenirá backtracking no ponto em que aparece no programa.
Recursividade
Recursividade é uma outra maneira de se compor novas funções. Para definir uma função recursiva é necessário executar uma pequena analise sobre o argumento da função que deve ser o valor inicial do argumento e neste caso a definição especificará o valor da função aplicado para aquele argumento. Então, devemos designar um índice de complexidade para cada possível argumento, e a definição da função em um argumento particular onde será permitido o uso do valor dos valores da função para argumentos de pequena complexidade. O valor inicial deve ser de mínima complexidade.
Definição recursiva da função F:
F(K) = constante
F(X) = G(F(H(X)),X)
Onde K é de complexidade mínima e a complexidade de H(X) é sempre menor que de X. Assim para obter-se a complexidade mínima seguiremos os seguintes passos:
% fatorial f(0,X) :- !, X = 1. f(X,Y) :- Xnext is X - 1, f( Xnext, Ynext), Y is Ynext * X.Fail
'Maria gosta de animais menos cobras'. Como poderíamos escrever
isso em PROLOG ? É fácil de expressar essa declaração:
Maria gosta de qualquer X se X for um animal. Em PROLOG:
gosta(maria,X) :- animal(X).
Mas temos que excluir cobras. Isto pode ser feito usando uma fórmula diferente:
Se X é uma cobra então 'maria gosta X' não é verdadeiro,
do contrário se X é um animal então maria gosta X.
No caso de um argumento não ser verdadeiro PROLOG usa um controle de programa chamada Fail que sempre falha, assim o objetivo principal é forçado a falhar.
O exemplo acima escrito em PROLOG fica da seguinte forma:
gosta (maria,X) :- cobra(X), fail.
gosta (maria,X) :- animal(X).
Se X é cobra, então fail irá ocasionar a falha da regra.
Combinação Cut - Fail
Usa-se quando uma porção do seu programa inclui alguma coisa que nunca deve ser verdadeira, então o cut-fail pode eliminar a clausula daquela porção do seu programa. O cut previne que PROLOG tente ressatisfazer tal regra e perca tempo tentando encontrar soluções alternativas. Exemplos:
gosta(maria,X) :- X=cobra, !, fail. gosta(maria,X) :- animal(X). animal(cobra). animal(coelho). animal(passaro).
Bibliografia
BRATKO , Ivan. Prolog, - Programming for Artificial Intelligence.
2.ª edição.