Buscar

002 LAB1

Prévia do material em texto

Laboratório 1
Introdução à Linguagem de Programação
PROLOG
Edmilson Marmo Moreira
Universidade Federal de Itajubá - UNIFEI
Instituto de Engenharia de Sistemas e Tecnologias da Informação - IESTI
“Creio no Deus que fez os homens e não no deus que os homens fizeram”.
Karr
1 Considerações Iniciais
A linguagem de programação Prolog, abreviação de PROgramming in LOGic, é uma
linguagem de programação declarativa, que foi concebida por Colmerauer e Roussel em
1972, sendo sua primeira versão feita em Fortran por Battani e Meloniem 1973. Desde
então tem sido utilizada para aplicações de computação simbólica, como banco de da-
dos relacionais, compreensão de linguagem natural, automação de projetos, análise de
estruturas bioqúımicas e sistemas especialistas.
Linguagens de programação espećıficas raramente são boas para todos os tipos de
problema. Fortran (significando FORmula TRANslation), por exemplo, é utilizado prin-
cipalmente por cientistas e matemáticos. Muitas implementações de Prolog não têm a ha-
bilidade de tratar problemas “numericamente complicados” ou “processamento de texto”;
em vez disto, Prolog foi projetado para cuidar de “problemas lógicos”, isto é, problemas
para os quais uma decisão tem que ser tomada de uma forma ordenada. Prolog tenta
fazer com que o computador “raciocine” ao encontro de uma solução .
Como o nome implica, Prolog depende da manipulação lógica, entretanto não é a
única, pois, existem outras linguagens no campo geral da “programação lógica”. Tais
linguagens operam com lógica de proposição, conhecida também por lógica de pre-
dicado ou cálculo proposicional. Por exemplos, as seguintes declarações são fatos:
Pessoas com tênis para basquete jogam basquete.
A pessoaXX tem tênis para basquete.
1
Matemática Discreta - Notas de Aula - Laboratório 01 - 2
O que pode ser deduzido, calculado, computado e determinado destes fatos é que a
pessoaXX joga basquete.
O Prolog faz com que o computador cuide da parte dedutiva. De fato, Prolog tem
uma máquina de dedução interna que automaticamente percorre os fatos e constrói ou
testa conclusões lógicas.
2 Linguagens Imperativas × Linguagens Declarativas
Uma vez que Prolog é uma linguagem declarativa, ela exige que se declare regras e
fatos a respeito de determinados śımbolos, para depois perguntar-lhe se uma determinada
meta segue de uma forma lógica destas regras e fatos. A máquina dedutiva que faz
esta verificação é parte do próprio Prolog. Muitas das caracteŕısticas únicas do Prolog
envolvem interações de procedimentos com o núcleo declarativo do Prolog.
Nas linguagens imperativas, como C++, é necessário, além de declarar fatos e regras,
utilizar a linguagem para informar ao compilador como procurar uma solução, onde olhar,
quando parar, e assim por diante. Poucas linhas de uma linguagem declarativa podem
fazer tanto trabalho quanto muitas linhas de uma linguagem imperativa.
A distinção entre as linguagens imperativas e as linguagens declarativas é uma das ra-
zões pela qual uma implementação do Prolog (como o Turbo Prolog) seja uma ferramenta
tão boa para aplicações de Inteligência Artificial .
3 Estrutura de um Programa Prolog
O conjunto de declarações que constituem um programa em Prolog é também conhe-
cido como um banco de dados Prolog. Os itens em um banco de dados Prolog têm
uma entre duas formas, conhecidas em Prolog como fatos e regras .
Os fatos de Prolog permitem a definição de predicados por meio da declaração de
quais itens pertencentes a algum conjunto universo (ou domı́nio) satisfazem os predicados.
Como exemplo, suponha que se deseja criar um programa em Prolog que descreva as
cadeias alimentares em uma determinada região ecológica. Pode-se começar com um
predicado binário come. Descreve-se, então, o predicado fornecendo os pares de elementos
do domı́nio que tornam come verdadeiro. Pode-se ter, então, os seguintes fatos no banco
de dados:
come(urso, peixe).
come(urso, raposa).
Matemática Discreta - Notas de Aula - Laboratório 01 - 3
come(veado, grama).
Nesse exemplo, “urso”, “peixe”, “raposa”, “veado” e “grama” são constantes, pois represen-
tam elementos espećıficos do conjunto universo. Como o domı́nio propriamente dito nunca
é especificado, exceto através dos predicados, neste ponto pode-se considerar o conjunto
universo como consistindo em “urso”, “peixe”, “raposa”, “veado” e “grama”. É responsabi-
lidade do usuário manter uma compreensão e utilização consistente dos predicados em um
programa Prolog. Assim, come(urso, peixe) poderia ser usado para representar o fato
de que os ursos comem peixes ou de que os peixes comem ursos! Por convenção come(X,
Y) significa que “X come Y ”.
3.1 Partes do Programa
A maioria dos programas Prolog (executados no Turbo Prolog) são organizados em
quatro partes principais:
CLAUSES: Os fatos que se constroem a partir dos objetivos e relacionamentos estão des-
critos nesta seção. As cláusulas também contém regras e outros elementos que serão
discutidos posteriormente.
PREDICATES: Predicados são os relacionamentos. O termo “predicado” é emprestado da
lógica formal, que é uma das ráızes do Prolog. Sempre que o programa for utilizar
um determinado predicado nas cláusulas, será preciso declará-lo formalmente na
seção de predicados.
DOMAINS: O Turbo Prolog necessita de mais de um ńıvel de explicação antes que se diga
que o programa está completo. É preciso informar os argumentos que o predicado
vai utilizar. Para manter mı́nima a utilização da memória do computador e tornar
os programas mais fáceis de serem depurados.
GOAL: Esta é a seção que informa o que se deseja descobrir ou o que se deseja que o
computador faça com as informações fornecidas nas três seções anteriores.
Normalmente, um programa tem ao menos os predicados e as cláusulas, mas é posśıvel
ter um programa que seja apenas uma seção de meta.
O Turbo Prolog não diferencia letras maiúsculas de minúsculas, a não ser em śımbolos,
onde os iniciados por maiúscula são considerados variáveis. Para criar uma padronização,
aconselha-se a escrever os programas usando sempre letra minúscula, deixando as letras
maiúsculas somente para as variáveis.
Matemática Discreta - Notas de Aula - Laboratório 01 - 4
3.2 O Primeiro Exemplo
A seguir tem-se o exemplo de um programa simples em Prolog. Este programa possui
as 3 partes básicas (Domains, Predicates e Clauses). Ele é bastante conhecido e foi
proposto no livro de Clocksin e Mellish (1984).
/*
* Primeiro Programa
*/
Domains
tipo_nome = symbol
Predicates
gosta (tipo_nome, tipo_nome).
Clauses
gosta (maria, queijo).
gosta (jose, goiabada).
gosta (maria, livro).
gosta (pedro, livro).
Para iniciar o programa é necessário apresentar as seguintes questões ao interpretador
Prolog:
gosta (maria, dinheiro).
gosta (jose, queijo).
gosta (maria, livro).
gosta (pedro, livro).
4 Variáveis
Uma variável em Prolog sempre começa com uma letra maiúscula. Uma variável pode
estar instanciada, quando estiver assumindo o valor de um objeto, ou não-instanciada, caso
contrário.
Considerando a seguinte base de conhecimento:
come(urso, peixe).
come(urso, raposa).
come(veado, grama).
Matemática Discreta - Notas de Aula - Laboratório 01 - 5
Ao ser realizada a questão: “come(urso, X).”, a variável X está inicialmente não-
instanciada. O Prolog procura então na base de conhecimento por um fato que se
iguale a questão, isto é, que tenha o mesmo predicado, o mesmo número de argumentos
e que o primeiro argumento seja urso. A procura é feita na ordem em que os fatos são
encontrados na base de conhecimento. Ao encontrar o primeiro fato, o Prolog verifica
se todos os requisitos estão cumpridos e instancia a variável X com o objeto peixe.
No Turbo Prolog, se estiver sendo realizado uma buscaatravés da janela de diálogo
(um goal externo), após encontrar o primeiro valor de X, o programa continua procurando
na base de conhecimento por outros valores e, no final, apresenta uma lista com todas as
respostas. Porém, se for um goal interno (dentro do programa), o Prolog só encontrará a
primeira resposta, a não ser que seja realizado um tratamento apropriado no mecanismo
de recursão. Isto será visto mais adiante.
Outro tipo de variável conveniente em algumas situações é a variável anônima, tam-
bém conhecida como variável vazia. Esta variável é escrita apenas com um travessão. A
variável anônima é utilizada nos mesmos locais em que são utilizadas as variáveis-padrão,
mas nunca é inicializada com um valor determinado.
O exemplo a seguir apresenta uma meta que utiliza uma variável anônima.
come(urso,_).
Neste caso, o resultado será yes, ou seja, urso come alguém. Mas, pelo fato de ter
sido utilizado uma variável vazia, não há informação de quem ele come. O Turbo Prolog
apenas procura um fato que tem o predicado come e dois argumentos (urso como primeiro
argumento e qualquer coisa como segundo argumento). Ele não precisa saber mais.
A aridade de um predicado é o número de argumentos que aquele predicado possui.
O predicado come possui aridade 2. Programas que utilizam predicados com aridades
maiores poderão trabalhar com metas mais complexas.
5 Metas Compostas
As metas podem ser ainda mais complexas. Por exemplo, as metas também podem
lidar com fatos múltiplos. Caso se proponha a meta:
come(urso,peixe) and come(urso,raposa).
estará sendo perguntado: é verdade pelas cláusulas que urso come peixe e urso come
raposa? Esta meta composta será considerada verdadeira apenas se as duas metas indi-
viduais forem verdadeiras.
Matemática Discreta - Notas de Aula - Laboratório 01 - 6
Pelo fato de muitas outras implementações Prolog utilizarem uma v́ırgula para sig-
nificar a conjunção, o Turbo Prolog também oferece esta opção, de modo que a meta
composta acima poderá ser escrita como:
come(urso,peixe), come(urso,raposa).
Também é posśıvel construir metas compostas utilizado o operador de disjunção OR.
Da mesma forma que uma v́ırgula pode substituir AND, um ponto-e-v́ırgula pode tomar o
lugar de OR. Uma meta semelhante a anterior, utilizando o operador de disjunção, pode
ser descrita da seguinte forma:
come(urso,peixe) or come(urso,raposa).
Existem diversos outros operadores que permitem produzir metas compostas, in-
cluindo os operadores < e >, como, por exemplo, a meta a seguir:
come(urso, X) and X > "peixe".
6 Regras e Retrocesso
O Prolog tem muito mais a oferecer do que apenas os fatos elementares e variáveis do
exemplo anterior. As regras são utilizadas para construir relações entre fatos, explicitando
as dependências entre eles. Ao contrário dos fatos, que são incondicionais, as regras
especificam coisas que podem ser verdadeiras se algumas condições forem satisfeitas. As
regras possuem duas partes:
1. O corpo, que define as condições e se encontra na parte direita da regra; e
2. A cabeça, que define a conclusão e se encontra na parte esquerda da regra.
A cabeça e o corpo são separados pelo comando IF, mas também é posśıvel usar
śımbolo :- que possui o mesmo significado. Uma regra sempre é terminada com um
ponto final.
O exemplo a seguir ilustra o uso de regras:
/*
* Segundo Exemplo
*/
Matemática Discreta - Notas de Aula - Laboratório 01 - 7
Domains
tipo_nome = symbol
Predicates
homem (tipo_nome).
mulher (tipo_nome).
genitor(tipo_nome, tipo_nome).
irma (tipo_nome, tipo_nome).
Clauses
/* fatos */
genitor (antonio, leonardo).
genitor (tereza, leonardo).
genitor (constantino, karina).
genitor (regina, karina).
genitor (constantino, luiz).
genitor (regina, luiz).
mulher (tereza).
mulher (karina).
mulher (regina).
homem (antonio).
homem (leonardo).
homem (constantino).
homem (luiz).
/* regras */
irma (X, Y) :- genitor (Z, X),
genitor (Z, Y),
mulher (X),
X <> Y.
Ao ser proposta a meta “irma (karina, luiz).”, o Turbo Prolog inicialmente pro-
cura por um fato igual a questão. Após não encontrar o fato, encontra uma regra que
declara como é a relação irma. Neste momento, instancia as variáveis da regra:
X = karina e Y = luiz
Depois o Prolog tenta descobrir se a regra é valida, verificando se a parte condicional da
regra é verdadeira. O goal original transforma-se em um conjunto de sub-metas dadas
Matemática Discreta - Notas de Aula - Laboratório 01 - 8
pela regra. Se todas as sub-metas da regra forem verdadeiras, a cabeça da regra e o goal
original também serão, e o Prolog retorna yes.
O retrocesso (backtracking) é um mecanismo usado pelo Prolog para encontrar fatos
ou regras adicionais que satisfaçam uma meta quando a tentativa corrente de unificação
falha, em questões com sub-metas. O Prolog utiliza marcadores para definir os pontos
onde uma nova tentativa de solução deve ser procurada. Toda vez que é realizada uma
questão, o Prolog tenta solucioná-la comparando a questão com os fatos na base de
conhecimento. Quando a questão possui muitas sub-metas, a falha em uma busca pode
acontecer. Neste momento, o Prolog precisa de uma maneira de identificar os pontos onde
deve tentar modificar a solução corrente para procurar uma resposta certa. O retrocesso
é a implementação deste mecanismo.
7 Entrada e Sáıda
O Turbo Prolog fornece ao programador os seguintes predicados padrões para a en-
trada e sáıda de dados:
WRITE: usado para escrever uma mensagem no dispositivo padrão de sáıda, geralmente a
janela de diálogo. O argumento deste comando pode ser tanto uma variável como
um objeto.
READLN: usado para ler uma string ou um śımbolo do dispositivo padrão de entrada,
geralmente o teclado, e guardá-lo em uma variável.
O Turbo Prolog possui outros predicados de leitura, um para cada tipo de objeto a
ser lido. São eles:
READINT: usado para ler um número inteiro.
READCHAR: usado para ler um caracter.
READREAL: usado para a leitura de um número em ponto flutuante.
O exemplo a seguir ilustra o uso dos predicados de entrada e sáıda:
/*
* Terceiro Exemplo
*/
Domains
Matemática Discreta - Notas de Aula - Laboratório 01 - 9
nome, fone = symbol
Predicates
telefone (nome, fone)
Clauses
telefone ("Alberto", "921-1234").
telefone ("Joaquim", "921-2345").
telefone ("Eduardo", "921-3456").
Goal
write("Entre com o nome: "),
readln(Nome),
telefone(Nome, Telefone),
write("O telefone de ", Nome, " e ", Telefone).
8 Controle de Retrocesso
O Turbo Prolog tem uma série de predicados intŕınsecos, bem como comandos, que
o tornam uma linguagem de programação de uso geral e não apenas uma implementação
lógica. Neste contexto, há duas importantes funções de controle de retrocesso: cut “!” e
“fail”. O controle do backtracking é importante, pois é uma das maneiras de provocar
repetição, sendo outra maneira o uso da recursão.
O predicado fail sempre retorna uma falha na unificação. Com isso, força o Pro-
log a fazer o retrocesso, repetindo os predicados anteriores ao predicado fail. Por ser
muito importante, o uso deste predicado para forçar o backtracking é chamado de método
“Backtrack After Fail” de controle.
Como foi discutido anteriormente, existem dois tipos de metas em Turbo Prolog:
externa e interna. Uma meta interna tem a mesma sintaxe que uma meta externa. A
diferença é que a meta interna é localizada dentro do código do programa. Quando exis-
te uma meta interna o Prolog apresenta apenas uma resposta, o primeiro predicado da
lista, caso exista. O predicado fail obriga o Prolog a retroceder até o começo da meta e
começar de novo as procuras de concordância. Desta forma, Prolog gerará uma lista com
todas as alternativas encontradas que satisfaçam a consulta realizada.
Outro predicado importante para o controle da recursão éo cut “!”. Ao contrário do
predicado fail, em algumas situações é preciso impedir que o programa volte atrás em
uma decisão tomada (backtracking).
Matemática Discreta - Notas de Aula - Laboratório 01 - 10
O corte é usado para interromper a recursão em dois casos:
• Nas condições anteriores a ele na mesma regra.
• Nas regras abaixo daquelas em que ele aparece.
Por exemplo, seja a regra que não contenha corte com o seguinte formato:
<a> :- <b>, <c>, <d>.
Tal regra diz, na interpretação declarativa, que <a> é verdadeiro se <b>, <c> e <d> forem
verdadeiros. Isto equivale a dizer que se <b>, <c> e <d> forem verdadeiros, então <a> é
verdadeiro.
O corte pode ser traduzido por “quando e só quando”. Assim sendo, uma frase com o
formato:
<a> :- <b>, <c>, <d>, !, <e>, <f>.
deve receber a interpretação do tipo, se <b>, <c> e <d> são verdadeiros, então <a> é
verdadeiro quando e só quando <e> e <f> são verdadeiros. Em outras palavras, o Prolog
tenta satisfazer as sub-metas <b>, <c> e <d>, podendo realizar o backtracking entre estas
cláusulas até que <d> seja satisfeita, o que causará a passagem da busca do Prolog da
esquerda para direita do corte. Depois dessa passagem, o Prolog pode tentar satisfazer as
cláusulas <e> e <f>, tendo ou não que realizar o backtracking para isso, até o momento que
a cláusula <e> falha. Neste caso, não é mais posśıvel voltar para escolher outra unificação
para a cláusula <d> e toda a regra <a> falha.
Os cortes são chamados de verdes quando não mudam o resultado da busca ou ver-
melhos quando mudam o resultado. O corte verde é usado para eliminar os marcadores
do backtracking, que são ponteiros que ocupam espaço na memória.
O programa Prolog a seguir ilustra o uso dos cortes verdes. Os predicados do programa
definem a seguinte função:
f(x) =

0 se x < 3,
2 se x ≥ 3 e x < 6,
4 se x ≥ 6
Matemática Discreta - Notas de Aula - Laboratório 01 - 11
/*
* Quarto Exemplo
*/
Domains
numero = integer
Predicates
f (numero, numero)
Clauses
f(X,0) :- X < 3,!.
f(X,2) :- 3 <= X, X < 6,!.
f(X,4) :- 6 <= X,!.
Neste exemplo, se forem retirados os cortes as respostas serão as mesmas. Pode-se
notar que com o uso do corte, elimina-se a passagem do programa por regras que não
serão úteis para resolução do problema.
A mesma função poderia ser definida através de predicados com cortes vermelhos,
como no código seguinte:
/*
* Quinto Exemplo
*/
Domains
numero = integer
Predicates
f (numero, numero)
Clauses
f(X,0) :- X < 3,!.
f(X,2) :- X < 6,!.
f(X,4) :- 6 <= X,!.
Neste exemplo, caso não houvesse cortes, o Prolog, dependendo da consulta, apresen-
taria mais de um resultado . Por exemplo, para a meta:
f(2, Y).
Matemática Discreta - Notas de Aula - Laboratório 01 - 12
seria apresentado as respostas:
Y=0
Y=2
Y=4
3 Solutions
pois, a busca casaria com cada fato existente na base de conhecimento.
Finalmente é preciso considerar que ao se utilizar o predicado de corte em conjunto
com falhas, provocadas pelo predicado fail ou por uma regra, obtém-se um poderoso
método de controle. Neste método, as falhas (do fail ou de uma regra) são usadas
para forçar o backtracking até que uma condição espećıfica seja encontrada, quando se
usa o corte para terminá-lo. Esse método é parecido com as estruturas de repetição das
linguagens imperativas. O próximo exemplo ilustra esta situação:
/*
* Sexto Exemplo
*/
Domains
tipo_nome = symbol
Predicates
aluno(tipo_nome).
escreve_ate(tipo_nome).
controle_do_corte(tipo_nome, tipo_nome).
Clauses
aluno(alice).
aluno(beatriz).
aluno(karina).
aluno(lucas).
aluno(mateus).
aluno(roberto).
escreve_ate (Nome_final) :-
aluno (Nome),
write (Nome),
nl,
controle_do_corte(Nome, Nome_final), !.
Matemática Discreta - Notas de Aula - Laboratório 01 - 13
controle_do_corte(Nome_2, Nome_final_2) :-
Nome_2 = Nome_final_2.
A regra controle_do_corte falha quando os nomes não são iguais, provocando back-
tracking e evitando o corte do predicado, até que o nome corrente da busca seja igual ao
final, dado pelo usuário.
9 Recursão
Outro método muito usado para causar a repetição em programas escritos em Prolog
é a recursão. Toda recursão é composta por duas regras: a primeira, que define a condição
de parada da recursão; e a segunda, onde é feita a chamada à função recursiva. O próximo
programa calcula o fatorial de um número, ilustrando o uso da recursividade em Prolog:
/*
* Setimo Exemplo
*/
Domains
tipo_numero = integer
Predicates
fatorial (tipo_numero, tipo_numero).
Clauses
fatorial (0, Resultado) :- Resultado = 1,!.
fatorial (Numero, Resultado) :-
Numero_menos_1 = Numero - 1,
fatorial(Numero_menos_1, Resultado_parcial),
Resultado = Numero * Resultado_parcial.
10 Listas
A lista é uma estrutura de dados simples largamente utilizada em programação não-
numérica. Uma lista é uma sequência de ı́tens como: ana, pedro, paulo, joao. Esta lista
pode ser representada em Prolog como:
[ana, pedro, paulo, joao].
Matemática Discreta - Notas de Aula - Laboratório 01 - 14
A lista vazia é representada por []. A lista não-vazia contém duas partes:
• A cabeça da lista, ou seja, o primeiro elemento da lista.
• A cauda da lista, ou seja, a lista sem o primeiro elemento.
Por exemplo, para a lista anterior, ana é a cabeça da lista e [pedro, paulo, joao]
é a cauda da lista, que também é uma lista.
No Turbo Prolog, para que um fato ou uma regra possa ter uma lista como argumento,
deve-se definir na seção Domains quais listas serão usadas no programa e de que tipo de
objetos essas listas serão compostas. Por exemplo, para se definir um tipo que é uma lista
de inteiros, declara-se:
lista_inteiros = integer*
Aqui, * indica que lista_inteiros é uma lista, composta por elementos do tipo inteiro.
Uma lista é armazenada na memória como uma árvore binária, onde o primeiro ele-
mento é guardado junto com um ponteiro para o resto da lista.
Variáveis são muitas vezes usadas para representar elementos desconhecidos e elemen-
tos genéricos de uma lista. Por exemplo:
[A, B, pedro]
O primeiro e o segundo elementos desta lista são genéricos ou desconhecidos e, por con-
seguinte, estão representados por variáveis. O terceiro elemento é conhecido e está repre-
sentado pelo objeto pedro.
Um número indeterminado de elementos genéricos na cauda de uma lista pode ser
representado por uma barra vertical (|) seguida de uma variável, como no exemplo a
seguir, indicando que após o segundo elemento há uma quantidade ignorada de elementos
desconhecidos ou genéricos:
[rosa, margarida | X]
A barra vertical pode ser traduzida por “resto da lista”. Isto faz com que [5,9,7|X]
seja considerada como a lista cujo primeiro elemento é 5, o segundo elemento é 9, o terceiro
elemento é 7 e o resto dos elementos é X.
Um padrão de lista muito importante é aquele cujo o primeiro elemento é uma variável
e cujo resto da lista é também uma variável. Exemplos deste padrão são:
Matemática Discreta - Notas de Aula - Laboratório 01 - 15
[X|Y]
[Primeiro|Resto]
[Topo|Cauda]
Tais listas representam qualquer lista com pelo menos um elemento. Considere o caso
de [X|Y]. Este padrão pode ser instanciado a lista [ana] de um único elemento desde que
X represente o valor ana e Y represente a lista vazia.
O exemplo a seguir imprime uma lista usando recursão:
/*
* Oitavo Exemplo
*/
Domains
nome = symbol
lista_de_nomes = nome*
Predicates
alunos(lista_de_nomes).
imprime_lista(lista_de_nomes).
Clauses
/* fatos */
alunos([karina, beatriz, alice, lucas, mateus, marcelo, andre, roberto]).
/* regras */
imprime_lista([]).
imprime_lista([Cabeca|Cauda]) :- write (Cabeca),
nl, imprime_lista(Cauda).
Goal
alunos (X),
write("A lista com todos os alunos declarados e’:"),
nl, imprime_lista(X).
Um ponto importante a ser considerado é a forma de se concatenarduas listas. Su-
pondo a seguinte relação:
concatena[L1, L2, L3]
Matemática Discreta - Notas de Aula - Laboratório 01 - 16
Onde L1 e L2 são duas listas e L3 é o resultado da concatenação de L1 e L2. Por
exemplo:
concatena([a,b],[c,d],[a,b,c,d]).
é verdade, entretanto a consulta:
concatena([a,b],[c,d],[c,d,a,b]).
é falsa.
Na definição da relação para concatenação de lista, é preciso tratar dois casos, depen-
dendo do primeiro argumento X:
1. Se o primeiro argumento é uma lista vazia, então o segundo e o terceiro argumentos
precisam ser a mesma lista. Isto é definido pelo seguinte fato Prolog:
concatena([],L,L).
2. Se o primeiro elemento não é uma lista vazia, então ele tem cabeça e cauda. Seja o
primeiro argumento a seguinte lista genérica [X|L1]. O resultado da concatenação
desta lista com a lista L2 é uma lista [X|L3], onde L3 é a concatenação de L1 (cauda
da primeira lista) com L2 (segunda lista). Em Prolog, esta regra é escrita como:
concatena([X|L1], L2, [X|L3]) :- concatena(L1, L2, L3).
O exemplo a seguir ilustra o que foi exposto:
/*
* Nono Exemplo
*/
Domains
nome = symbol
lista_de_nomes = nome*
Predicates
concatena (lista_de_nomes,lista_de_nomes,lista_de_nomes).
clauses
/* regras */
concatena ([],L,L).
concatena ([N|L1],L2,[N|L3]) :-
concatena (L1,L2,L3).
Matemática Discreta - Notas de Aula - Laboratório 01 - 17
11 Bancos de Dados Dinâmicos em Prolog
O Turbo Prolog permite a construção de bancos de dados dinâmicos, isto é, que po-
dem ser alterados e armazenados em disco para nova utilização. Esses bancos armazenam
fatos. Os predicados que definem os fatos usados no banco de dados dinâmico devem ser
declarados na parte Database do programa e não na Predicates. Assim, um mesmo pro-
grama pode ter fatos declarados na parte de Predicates, que só podem ser alterados com
a modificação do código fonte do programa, e fatos guardados em um arquivo separado,
que podem ser carregados, modificados e salvos.
O Turbo Prolog fornece ao programador os seguintes predicados para manipular ban-
cos de dados dinâmicos:
AssertA: Acrescenta um fato ao banco de dados armazenado na memória do computa-
dor. Ele acrescenta o novo fato no ińıcio das cláusulas de mesmo predicado.
AssertZ: Semelhante ao anterior, porém adiciona o novo fato após as cláusulas de mesmo
predicado.
Retract: É um predicado usado para retirar um fato do banco de dados. Ele retirará o
primeiro fato que casar com o seu argumento.
FindAll: Predicado que procura no banco de dados todos os fatos que combinem com
seu primeiro argumento no predicado passado pelo seu segundo argumento, e os
coloca em uma lista, que é seu terceiro argumento.
Consult: Utilizado para carregar um banco de dados do disco para a memória. Este
predicado recebe como argumento o nome do arquivo a ser carregado. Uma obser-
vação que deve ser feita é que este predicado carregará o banco de dados do disco
concatenando-o com o da memória. Por exemplo, se o usuário carregar duas vezes
o mesmo banco de dados, ele ficará na memória com duas cópias de todos os fatos
carregados, uma após a outra.
Save: Transfere todos os fatos existentes na memória para um arquivo.
11.1 Um Exemplo de Aplicação
A seguir tem-se o exemplo de um programa com bando de dados em Prolog. O
programa implementa uma agenda de telefones com uma interface com o usuário através
de menus de texto. Ele guarda um nome e um número para cada entrada no banco de
dados.
Matemática Discreta - Notas de Aula - Laboratório 01 - 18
/*
* Um Exemplo de Banco de Dados em Prolog
*/
Domains
tipo_nome, tipo_telefone = string.
Database
agenda(tipo_nome, tipo_telefone).
Predicates
menu (integer). /* gerencia a interacao com o usuario.*/
processa (integer). /* processa a escolha do usuario. */
erro. /* processa erro na escolha da opcao do menu. */
adiciona. /* adiciona novo item na agenda */
deleta. /* retira item da agenda */
procura. /* procura item na agenda */
carrega. /* carrega agenda de um arquivo */
salva. /* salva a agenda em um arquivo. */
Clauses
/* Clausula que controla a interacao com o usuario. */
menu(Escolha):- Escolha = 6,
write ("Terminado!").
menu(_):- nl,
write("Escolha uma opcao: "), nl,
write("1. Adiciona um telefone ao Banco de Dados"), nl,
write("2. Deleta um telefone do Banco de Dados"), nl,
write("3. Procura um telefone"), nl,
write("4. Carrega o arquivo do Banco de Dados"),nl,
write("5. Salva o Banco de Dados em um arquivo"), nl,
write("6. Termina. "),nl,
write(" "),nl,
readint(Escolha),
processa (Escolha), menu(Escolha).
/* Clausulas que processam a escolha do usuario. */
processa (1) :- adiciona.
processa (2) :- deleta.
Matemática Discreta - Notas de Aula - Laboratório 01 - 19
processa (3) :- procura.
processa (4) :- carrega.
processa (5) :- salva.
/* se a escolha foi 6, confirma saida. */
processa (6) :- write ("Tem certeza que deseja terminar? (s/n)"),
readln(Resposta),
frontchar (Resposta,’s’,_).
processa(6) :- menu(0).
/* processa entradas invalidas */
processa (Escolha) :- Escolha < 1, erro.
processa (Escolha) :- Escolha > 6, erro.
erro :- write ("Por favor entre numeros entre 1 e 6."),
nl,
write ("Tecle algo para continuar."),
readchar (_).
/* Clausulas que implementam as rotinas de manipulacao do DB.*/
/* adiciona um telefone */
adiciona :- write ("Adicionando um telefone ao B D."), nl,
write ("Entre o nome: "),
readln (Nome),
write ("Entre o telefone dele:"),
readln (Telefone),
assertz (agenda (Nome, Telefone)),
write ("Telefone adicionado ao B D."),
nl.
/* deleta um telefone do BD */
deleta :- write ("Deletando um telefone do B D."), nl,
write ("Entre o nome: "),
readln (Nome),
write ("Entre o telefone dele: "),
readln (Telefone),
retract (agenda (Nome, Telefone)),
Matemática Discreta - Notas de Aula - Laboratório 01 - 20
write ("Telefone retirado do B D."),
nl.
deleta :- write ("Telefone nao encontrado!!!"), nl.
/* procura um telefone */
procura :- write ("Procurando um telefone no B D."), nl,
write ("Entre o nome: "),
readln (Nome),
agenda (Nome, Telefone),
write ("O telefone do(a) ", Nome, " e’ ",Telefone,"."),
nl.
procura :- write ("Telefone nao encontrado!!!"), nl.
/* carrega o arquivo com o banco de dados */
carrega :- write ("Carregando o arquivo com o B D."), nl,
write ("Qual o nome do arquivo? "),
readln(Nome_do_arquivo),
consult(Nome_do_arquivo),
write("Carregado!"),
nl.
/* salva o arquivo com o banco de dados */
salva :- write ("Salvando o Banco de Dados."), nl,
write ("Qual o nome do arquivo? "),
readln(Nome_do_arquivo),
save(Nome_do_arquivo),
write("Salvo!"),
nl.
/* Inicia o programa */
Goal
menu(0).
Matemática Discreta - Notas de Aula - Laboratório 01 - 21
Finalmente, o predicado pré-definido Findall gera uma lista com todas as alternativas
encontradas que satisfaça a uma determinada consulta. O próximo exemplo ilustra o uso
deste predicado.
/*
* Exemplo do uso de Findall
*/
Domains
ListaNomes = symbol*.
Predicates
Aluno (symbol, integer).
TesteFindall.
MostraLista (ListaNomes).
clauses
Aluno(pedro, 9).
Aluno(joao, 10).
Aluno(joaquim, 9).
MostraLista([]).
MostraLista([X|Y]) :- write (X), nl, MostraLista(Y).
TesteFindall :-
Findall(X, Aluno(X,_), L),
MostraLista(L),
nl.
12 Exerćıcios
1. Sejam pai e mae as relações como definidas nos fatos abaixo:
pai(joao, joaquim).
pai(edmilson, lucas).
pai(edmilson, mateus).
pai(joaquim, bruno).
mae(karina, lucas).
mae(karina, mateus).
Matemática Discreta - Notas de Aula - Laboratório 01 - 22
mae(silvia, bruno).
mae(laura, joaquim).
Qual será a resposta de Prolog para as seguintes consultas:
(a) pai(joao, X).
(b) pai(X, joaquim).
(c) pai(joao, X), pai(X, bruno).
2. Em relação aos fatos da questão anterior, formule em Prolog as seguintes consultas
sobre as relações pai e mae:
(a) Quem é o pai de bruno?
(b) Maria tem filhos?
(c) Quem é oavô de bruno?
3. Defina a relação neto a partir das relações pai e mae.
4. Faça uma regra que receba um nome, procurando-o na base de dados e imprimindo
Encontrei todas as vezes que ele aparecer na base.
5. Implemente regras para o tratamento de listas que:
(a) Calcule o comprimento de uma lista.
(b) Insira um elemento em uma lista.
(c) Retire um elemento de uma lista.
(d) Substitua elementos em uma lista.
(e) Encontre o n-ésimo elemento de uma lista.
(f) Inverta uma lista.
6. Defina os seguintes predicados Prolog:
(a) listapar(Lista) - que é verdadeiro quando o número de elementos da lista
for par.
(b) listaimpar(Lista) - que é verdadeiro quando o número de elementos da lista
for ı́mpar.
7. Descreva um programa Prolog para verificar se uma determinada lista de caracteres
(char) é um paĺındromo. Um paĺındromo é uma palavra ou número cuja leitura é
a mesma, quer se faça da esquerda para a direita, ou da direita para a esquerda.
Matemática Discreta - Notas de Aula - Laboratório 01 - 23
8. Simule o programa Prolog a seguir, apresentando sua sáıda, e explique qual é o
objetivo dos seus predicados:
Domains
ListaInt=integer*
Predicates
Prova1(ListaInt, ListaInt).
Prova2(ListaInt, ListaInt, ListaInt).
Clauses
Prova1(A,B) :- Prova2(C,[D,E|F],A),
write(D," ",E," ",F),nl,
D < E, !,
Prova2(C,[E,D|F],G),
Prova1(G,B).
Prova1(A,A).
Prova2([],L,L).
Prova2([A|B],C,[A|D]):-Prova2(B,C,D).
Goal
write("Saida:"),nl,
Prova1([2,3,4,1],X),
write(X),nl.
9. Faça um programa em PROLOG que leia um banco de dados de nome “Dados.txt”
e imprima uma lista de todas as pessoas que torcem para um determinado clube.
Em seguida, deve ser apresentado o total de torcedores daquele clube na base. O
arquivo “Dados.txt” possui fatos para o predicado Pessoa(symbol,Lista) em que
Lista é uma lista de symbol e o primeiro parâmetro representa o nome da pessoa
e o segundo parâmetro representa os times preferidos.
Referências
CLOCKSIN, W. F.; MELLISH, C. S. Programming in Prolog. Berlin: Springer-Verlag,
1984.
GERSTING, J. L. Fundamentos Matemáticos Para a Ciência da Computação. 4a. ed.
Rio de Janeiro: Livros Técnicos e Cient́ıficos Editora S.A., 2001. 538 p.
ROBINSON, P. R. Turbo Prolog - Guia do Usuário. São Paulo: McGraw-Hill, 1988.
287 p.

Continue navegando