Animal Game |
Pontos de PROLOG Relevantes para a Resolução
Implemente o jogo em que o computador deve descobrir o animal que você imaginou através de perguntas/respostas.
Does the animal have the following attribute: has_hair? yes.
Does the animal have the following attribute: eats_meat? yes.
Does the animal have the following attribute: has_tawny_color? yes.
Does the animal have the following attribute: has_dark_spots? no.
Does the animal have the following attribute: has_black_stripes? no.
Does the animal have the following attribute: has_hooves? yes.
Does the animal have the following attribute: has_long_neck? yes.
Does the animal have the following attribute: has_long_legs? yes.
I guess that the animal is: giraffe
yes
O jogo aqui descrito está resolvido em três versões diferentes, sendo que cada uma possui uma característica particular, e diferente formato para a representação e utilização do conhecimento. A complexidade da Versão 1 e Versão 2 são semelhantes, porém a Versão 3 é um pouco mais complexa por utilizar meta-programação. Todas as versões são executadas da mesma maneira:
?- go.
O conhecimento implementado foi baseado nas seguintes definições (podendo ser ampliado ou modificado, caso necessário):
I – Se o animal tem pelo
então é um mamífero.
II – Se o animal dá leite
então é um mamífero.
III – Se o animal tem penas
então é uma ave.
IV – Se o animal voa e bota ovos
então é uma ave.
V – Se o animal é um mamífero e come carne
então é um carnívoro.
VI – Se o animal é um mamífero, tem dentes pontudos, garras e seus olhos são frontais
então é um carnívoro.
VII – Se o animal é um mamífero e tem casco
então é um ungulado.
VIII – Se o animal é um mamífero, rumina e tem dedos pares
então é um ungulado.
IX – Se o animal é carnívoro, tem cor amarelo-tostado e manchas escuras
então é um leopardo.
X – Se o animal é carnívoro, tem cor amarelo-tostado e listras pretas
então é um tigre.
XI – Se o animal é ungulado, tem pernas longas, pescoço comprido, cor amarelo-tostado e
manchas escuras
então é uma girafa.
XII – Se o animal é ungulado, tem cor branca e listras pretas
então é uma zebra.
XIII – Se o animal é uma ave, tem pernas longas, pescoço comprido e é preto e branco
então é um avestruz.
XIV – Se o animal é uma ave, não voa, nada e é preto e branco
então é um pinguim.
XV – Se o animal é uma ave e um bom voador
então é um albatroz.
As três versões foram implementadas em LPA-Prolog, e devido ao fato de alguns predicados (referentes às respostas do usuário fornecidas às perguntas feitas pelo sistema) terem que ser gravados na base de dados em tempo de execução, houve a necessidade de se definir esses predicados como dinâmicos, pois, a priori, não existe nenhum predicado desse tipo na base de dados.
As respostas foram armazenadas na base de dados, a fim de que não fosse feita ao usuário a mesma pergunta mais de uma vez. Uma vez feita, ela (juntamente com a resposta) é armazenada na base de dados utilizando o predicado pré-definido assert/1. Porém, antes da pergunta ser feita, é verificado se ela já foi perguntada. Se sim, obtém-se a resposta dada anteriormente; e senão, a pergunta é feita ao usuário.
O predicado pré-definido retract/1 é utilizado para excluir, da base de dados, as respostas geradas em tempo de execução do programa, de modo que o próximo usuário inicie o seu programa "do zero", ou seja, sem que o programa lembre as respostas dadas pelo usuário anterior.
O predicado limpa_base/0 (contido nas três versões) executa o predicado retract(X) zero ou mais vezes, ou seja, até que não existam mais fatos X na base de dados, visto que o retract/1 elimina um fato de cada vez.
Na Versão 1 o conhecimento sobre o domínio está representado na forma de listas, na qual são armazenadas todas as características sobre os animais e/ou classes e subclasses de um determinado animal. Para encontrar um determinado animal, é verificado primeiro as características pertencentes a sua classe (espécie), e posteriormente, características específicas, até que se encontre (através da hierarquia) um determinado animal. As perguntas sobre se o animal a ser "adivinhado" possui ou não determinada característica são feitas percorrendo-se todos os elementos da lista em questão.
Na Versão 2 o conhecimento está representado em forma de regras de produção, e cada tipo de animal é, a priori, uma hipótese a ser testada. Para a colocação das características referentes às classes de animais (tais como, mamífero, ave, etc.) foram feitas regras separadas, para maior clareza do programa. Ao se verificar se o animal em questão possui determinada característica, faz-se uma busca pela base de dados para ver se já existe resposta para essa característica, e caso não exista, é feita ao usuário a pergunta e armazenada sua resposta.
Na Versão 3 o conhecimento está representado na forma de regras de produção, e tanto os animais quanto suas classes usam o mesmo predicado animal/1 (isso é uma forma de tornar o programa mais genérico). Essas regras chamam tanto outras regras quanto características específicas. O programa tenta provar, utilizando meta-programação, se um determinado animal é a solução do problema. Para tanto, quando necessário, são feitas perguntas ao usuário, e validadas e armazenadas suas respostas. Após o sistema fornecer a resposta final, ele apaga as respostas armazenadas em tempo de execução e está pronto para ser utilizado novamente em outra "adivinhação".
Para maior clareza desta Versão 3, abaixo estão explicados alguns predicados utilizados:
perguntável/1: indica se uma meta deve ou não ser perguntada ao usuário, pois nem sempre uma meta deve ser pergunta, sendo que em alguns casos, uma meta deve realmente falhar.
pergunta/3: possui a característica, o texto da pergunta e o tipo da resposta. Neste exemplo, o tipo é sempre s-n, pois utiliza-se apenas perguntas com resposta sim ou não.
pergunte/3: escreve para o usuário a pergunta, obtém sua resposta e valida essa resposta. Caso a validação falhe, a pergunta é feita novamente ao usuário.
valida_resp/2: verifica se a resposta fornecida pelo usuário se encontra entre as possíveis respostas permitidas.
prove/1: meta-programa que controla a execução do programa, através da execução de suas metas e sub-metas. Esse predicado possui seis possibilidades de regras, onde:
clause(Meta,Corpo): predicado pré-definido que recupera uma cláusula do programa consultado. Se é bem sucedido, Meta é a cabeça da cláusula recuperada e Corpo unifica com o corpo da cláusula. No caso de um fato, Corpo unifica com o átomo true. No caso de regras, o corpo pode conter uma ou mais sub-metas.
prove(true).
prove((MetaA,MetaB)):-
prove(MetaA),
prove(MetaB).
prove((MetaA;MetaB)):-
prove(MetaA);
prove(MetaB).
prove(Meta):-
sistema(Meta),
!,
Meta.
prove(Meta):-
clause(Meta,Corpo),
prove(Corpo), !.
prove(Meta):-
perguntavel(Meta),
not(ja_perguntada(Meta)),
!,
faz_pergunta(Meta,Resp),
lembre_resposta(Meta,Resp),
(Resp == 'sim'; Resp == 's').
Pontos de PROLOG relevantes para a resolução
Meta Programação
Entrada/Saída
Recursão
Listas
Meta-programas são programas que tratam outros programas como dados. Eles analisam, transformam e simulam outros programas. Interpretadores e compiladores são exemplos de meta-programas.
A meta-programação em Prolog é muito simples, e essa facilidade deve-se, principalmente, a dois fatores. O primeiro deles é a equivalência entre programas e dados: ambos são termos Prolog. A capacidade de manipulação simbólica é outra característica que torna Prolog uma linguagem poderosa para meta-programação.
Meta-interpretadores são uma classe particular de meta-programas. Um meta-interpretador para uma linguagem é um interpretador para a linguagem escrito na própria linguagem. Portanto, um meta-interpretador Prolog é um interpretador para a linguagem Prolog escrito em Prolog. A facilidade para escrever meta-interpretadores é uma poderosa característica de uma linguagem de programação, pois possibilita a construção de um ambiente de programação integrado e dá acesso ao processo computacional da linguagem.
Basicamente, um meta-interpretador Prolog toma um programa Prolog – juntamente com um meta Prolog – e executa a meta com respeito ao programa, isto é, o meta-interpretador tenta provar que a meta segue-se logicamente do programa. Porém, para ter real interesse, um meta-interpretador Prolog não deve comportar-se exatamente como o interpretador Prolog original, e sim oferecer alguma funcionalidade adicional, tal como gerar uma árvore de prova ou rastrear a execução de programas Prolog.
Uma das principais utilidades de meta-interpretadores é na construção de Sistemas Especialistas.
Geralmente, um Sistema Especialista é decomposto em Motor de Inferência e Base de Conhecimento. Algumas Bases de Conhecimento podem ser escritas diretamente em Prolog, pois a linguagem Prolog possui seu próprio Motor de Inferência. Neste caso, tem-se uma Base de Conhecimento executável.
Entretanto, existem diversas características importantes na implementação de Sistemas Especialistas que não são fornecidas pela linguagem Prolog, tais como mecanismo de explicação ou raciocínio com incerteza.
Um exemplo simples de meta-interpretador:
prove(true).
prove((A,B)) :- prove(A), prove(B).
prove(A) :- clause(A,C), prove(C).
Para maiores informações sobre meta-interpretadores ver em:
Leon Sterling & Ehud Shapiro - The MIT Press, 1986.
João Luiz Franco & Maria Carolina Monard
Notas Didáticas do ICMC/USP/São Carlos, No 6, novembro 1992.