Animal Game

 

Conteúdo

Descrição do Exercício

Implementação

Pontos de PROLOG Relevantes para a Resolução

Meta Programação

Considerações Finais

 

  

Descrição do Exercício

Implemente o jogo em que o computador deve descobrir o animal que você imaginou através de perguntas/respostas.

?- go.

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

 

Voltar ao início

 

Implementação

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').

 

Voltar ao início

 

Pontos de PROLOG relevantes para a resolução

 

Meta Programação

Entrada/Saída

Recursão

Listas

 

Voltar ao início

 

Meta Programação

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:

    1. The Art of Prolog - Advanced Programming Techiniques
    2. Leon Sterling & Ehud Shapiro - The MIT Press, 1986.

    3. Meta-Interpretadores Prolog
    4. João Luiz Franco & Maria Carolina Monard

      Notas Didáticas do ICMC/USP/São Carlos, No 6, novembro 1992.

    5. outros livros de sobre a linguagem Prolog.

 

 Voltar ao início

 

Considerações Finais

 

 

Voltar ao início