peradores Numéricos

Relacionais
Operadores numéricos relacionais são aqueles usadospara realizar testes quantitativos entre dois números. Sãoeles:

=             \=

<             >

=<           >=

  Ou seja, os símbolos são: igual (X = Y), diferente(X \= Y), menor (X < Y), maior (X > Y), menorou igual (X =< Y) e maior ou igual (X >= Y). Observe que osímbolo menor ou igual segue a ordem inversa de seu nome.
 

Aritméticos

Operadores aritméticos são os utilizados na aritméticabásica:

+              -

*              /

mod            is

Ou seja: soma (X + Y), subtração (X - Y), multiplicação(X * Y), divisão (X / Y), módulo (X modY) e resultado (X is equação ). Por exemplo:

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.

  Estruturas de Dados

As estruturas de dados ocorrem na forma:
pred(arg, estrut(atrib1,...,atrib n)).

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:

Lista
Cabeça
Cauda
[tem,alguem,ai]
tem
[alguem,ai]
[prolog]
prolog
[ ]
[[tem,alguem],ai]
[tem,alguem]
[ai]
[gosta(maria,trem),maria]
gosta(maria,trem)
[maria]
[A+B,33]
A+B
[33]
[ ]
-
-

  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.

Predicados Incorporados


  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:

                                 ?- oi.
                                 ola.
                                 Resposta: ola

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:

                                ?- esc(ola).
                                ola
                                ola

                                 [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 .

Negação


not(X)

É o predicado em que tem sucesso quando X falha. Por exemplo:

?- not(macho(mariana)).
yes

Repetição


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.
 
 

Regras Gramaticais


  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 ;
no
Cut

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 3
Antes 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
 


Diagrama 1


 


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:

  1. Aplicar H para reduzir a complexidade do argumento;
  2. Aplicar a função nela mesma no seu argumento;
  3. Aplicar G para obter o valor correto do argumento original
Não podemos esquecer o "famoso" exemplo do fatorial. Mostraremos como calcular o fatorial recursivo em PROLOG.
% 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.