Baixe o app para aproveitar ainda mais
Prévia do material em texto
AULA 4 INTELIGÊNCIA ARTIFICIAL E ROBÓTICA Prof. Luciano Frontino de Medeiros 2 TEMA 1 – PROGRAMAÇÃO EM LÓGICA E PROLOG Como vimos anteriormente, os agentes/robôs devem processar os dados provenientes dos sensores para tomar decisões e acionar os atuadores respectivos. O processamento envolve, então, um programa que implementa as funções do robô, de forma que ele possa desempenhar o comportamento esperado. Para tarefas mais simples, a programação pode envolver aspectos relativos à forma com que o robô irá se comportar. Para tarefas complexas que envolvam algum nível de raciocínio por parte do robô, alguns recursos disponíveis em IA podem ser úteis, como a programação em lógica. No entanto, plataformas mais robustas devem ser utilizadas para fazer com que tais recursos possam ser executados, como por exemplo a Raspberry Pi (<https://www.raspberrypi.org/>). Assim, a programação em lógica é um tipo de programação que se utiliza de representações lógicas sobre o ambiente e permite deduzir novos conhecimentos a partir dos conhecimentos já existentes nesta representação. A programação em lógica tem suas raízes na lógica estudada na Filosofia. A partir do século XIX, Boole (1815-1864) criou a ferramenta que hoje é chamada de “álgebra booleana”. Já no século XIX, o matemático alemão Gottlob Frege (1848- 1925) criou a primeira versão de um cálculo de predicados da lógica, auxiliando a formalização exata do raciocínio dedutivo sobre conceitos matemáticos (Palazzo, 1997, p. 1). A fundamentação teórica da lógica computacional estava pronta já no início da Segunda Guerra Mundial. No entanto, apenas com a evolução dos computadores, a partir dos anos 1950, é que foram feitas as primeiras implementações. Na década de 1960, surgiram programas para efetuar a prova automática de teoremas. No entanto, apenas em 1972 surge a primeira implementação por meio do grupo de pesquisa de Alain Colmerauer, da Universidade Aix-Marseille, com o nome de Prolog (acrônimo de “Programmation en Logique”). A linguagem foi mais tarde formalizada por pesquisadores da Universidade de Edimburgo, que é a referência para as implementações atuais (Palazzo, 1997, p. 2). Prolog lida com o conceito de programa em lógica. Quando um problema pode ser expresso por meio de um conjunto finito de um tipo especial de sentenças lógicas denominadas de cláusulas, temos um programa em lógica. O 3 programador simplesmente especifica o problema que deve ser solucionado. O controle fica a cargo do sistema de programação em lógica utilizado (Palazzo, 1997, p. 3). Em Prolog, a tarefa de escrever um programa é bastante diferente de escrever em uma linguagem de programação tal com C, Java (ou mesmo linguagens visuais). O “programa” do Prolog já está pronto, e o programador deve apenas especificar os dados. Esses dados devem ser expressos por meio de uma forma bastante específica. Por isso é que um problema em lógica é visto como um tipo de base de dados. Desde o seu advento, a programação em Prolog tem sido aplicada a diversas áreas, como sistemas de apoio à decisão, simulações educacionais, tutoriais inteligentes, problemas matemáticos, planejamento e roteirização, resolução de problemas, uso em regras de negócio e processamento de linguagem natural, dentre outras (Medeiros, 2018, p. 100). Uma cláusula em Prolog pode ser de três tipos (Medeiros, 2018, p. 101): • Fatos: denotam uma verdade incondicional, a respeito do ambiente que está sendo representado. • Regras: definem as condições que devem ser satisfeitas para que uma declaração seja considerada verdadeira. A partir de uma regra, é possível derivar novos fatos. • Consulta: é feita a consulta ou pergunta ao programa, de forma a verificar a verdade do conhecimento que está contido. No Quadro 1, há exemplos de cláusulas em Prolog. Quadro 1 – Exemplos de cláusulas em Prolog. Cláusula Exemplo Descrição Fato pai(joão, luiz). Este fato é traduzido como “João é o pai de Luiz”. Regra filho(X,Y) :- pai(Y,X). Se X é filho de Y, então Y é pai de X. O sinal “:-“ funciona como uma “seta à esquerda”, donde a conclusão à direita é obtida a partir das premissas que estão na direita. Consulta ?- filho(luiz, joão). True A consulta tenta obter se, a partir dos fatos e das regras existentes na base de conhecimento, Luiz é filho de João. A regra acima deduz um novo fato, a partir do fato e da regra anterior. Fonte: Medeiros, 2018, p. 101. 4 A dinâmica de programação em Prolog requer que o programador alimente os fatos e as regras no sistema PROLOG. Logo após, são feitas as consultas, como se fossem perguntas relativas à dedução de novos conhecimentos, a partir dos conhecimentos que existem na base de conhecimento. Uma cláusula é dividida nos seguintes componentes: • Corpo: lista de objetivos separados por vírgulas, que devem ser interpretadas por conjunções. • Cabeça: contém o resultado do corpo, inferido a partir dos objetivos. Um fato contém apenas “cabeça”. A consulta só possui o “corpo”. Uma regra contém tanto cabeça quanto corpo (Medeiros, 2018, p. 101). Algumas regras sintáticas devem ser levadas em consideração quando se escreve um programa em Prolog. No Quadro 1, pode-se notar que as constantes são palavras iniciadas com letra minúscula, enquanto que as variáveis devem ser escritas com a primeira letra em maiúscula. Portanto, o fator chave que diferencia, em Prolog, uma variável de uma constante, é a primeira letra da variável, que aparece sempre em maiúsculas (Medeiros, 2018, p. 102). Uma das plataformas para Prolog mais utilizadas hoje em dia é SWI-Prolog (<http://www.swi-prolog.org>). Desde a sua criação, em 1987, é muito usada em pesquisa, educação e também para aplicações comerciais. O software SWI- Prolog é disponibilizado, inclusive, para uso concomitante com outras linguagens de programação, tais como C++, C#, Java Python etc., o que potencializa a abordagem de resolução de problemas em IA (Medeiros, 2018, p. 102). Figura 1 – Tela do interpretador SWI-PROLOG com exemplo de consulta um programa de cálculo de fatorial Fonte: Medeiros, 2018, p. 103. 5 A dinâmica de programação em Prolog pode ser melhor compreendida a partir de exemplos, o que será feito nos próximos temas. TEMA 2 – COLORAÇÃO DE MAPAS O exemplo da coloração de mapas ilustra o uso do Prolog na busca de soluções nas quais há restrições a serem seguidas (Medeiros, 2018, p. 103). Uma atividade típica de coloração de mapas requer que cada região do mapa, seja um país, estado ou qualquer área delimitada, tenha uma cor específica. As cores podem ser diversas, desde que as regiões que fazem fronteira umas com as outras não tenham a mesma cor. A Figura 2 traz um exemplo de coloração do mapa da América do Sul, mostrando uma solução de cores, com a restrição de que cada país tenha uma cor diferente de outro que esteja na vizinhança. Figura 2 – Mapa da América do Sul Crédito: RRZ/Shutterstock. Para simplificar na abordagem do problema com Prolog, usaremos o mapa simplificado da Figura 2 com 5 regiões. Pode-se notar que as regiões “1” e “4” fazem fronteira com todas as outras regiões; já a região “2” faz fronteira com as regiões “1”, “3” e “4”; a região “5” faz fronteira somente com as regiões “1” e “4”. As regiões “3” e “5” podem ter a mesma cor, pois não fazem fronteira uma com a outra (Medeiros, 2018, p. 105). Uma solução para este problema pode ser o grupo de cores visto na Figura 4. 6 Figura 3 – Exemplo para coloração de mapas com 5 regiões Figura 4 – Solução possível com grupo de cores que atende à restrição de coloração Então, devemos construir um programa Prolog que irá processar uma base de conhecimento que tenha os fatos relativos às regiões e cores do mapa. Suponha que as coressão geradas de forma aleatória. O programa Prolog deverá responder se tal conjunto de cores está em conflito ou não. Precisamos então fazer a representação desse mundo da coloração de mapas, na forma de fatos, na base de conhecimento (Medeiros, 2018, p. 106). Podemos identificar três conceitos a partir da análise do problema, que poderão ser traduzidos em cláusulas de um programa Prolog: • Cor: uma cor específica que uma região pode ter. • Adjacência: duas regiões que fazem fronteira uma com a outra. • Conflito: se um grupo de cores está em conflito com a restrição ou não. Um recurso que facilita a descrição em fatos do programa Prolog é traduzir o mapa da Figura 2 na representação de uma árvore de adjacência ou vizinhança, 7 como pode ser visto na Figura 4. Toma-se como ponto de partida a região “1”, expandindo-se para as outras regiões na forma de nós de uma árvore, e cada uma em sequência, sem haver repetição, expandindo no nível mais raso. Essa forma facilita a tradução em fatos para o programa Prolog. Figura 5 – Representação das adjacências do mapa na forma de uma árvore Portanto, a primeira tarefa na escrita do programa será representar em linguagem Prolog essas adjacências. Para isso, escrevemos a palavra “adjacente”, com dois argumentos, um para a primeira região e outro para a segunda. Sete cláusulas são escritas inicialmente. Depois, é feita a inversão das regiões, com mais sete cláusulas, pois o programa Prolog irá entender apenas numa direção, e é necessário complementar com a adjacência inversa. Por exemplo, caso ficasse apenas a primeira coluna, o programa saberia que a região “1” é adjacente à região “2”. Entretanto, não saberia que a região “2” é adjacente à região “1”: adjacente(1,2). adjacente(2,1). adjacente(1,3). adjacente(3,1). adjacente(1,4). adjacente(4,1). adjacente(1,5). adjacente(5,1). adjacente(2,3). adjacente(3,2). adjacente(2,4). adjacente(4,2). adjacente(3,4). adjacente(4,3). adjacente(4,5). adjacente(5,4). O próximo passo é definir a cláusula “cor”, de forma que tenha três argumentos: região, cor e grupo. Como ilustração, temos dois grupos de cores, “a” e “b”, traduzidos na forma de fatos, conforme vemos a seguir (pode haver qualquer outra combinação): cor(1,vermelho,a). cor(2,azul,a). cor(3,verde,a). cor(4,amarelo,a). 8 cor(5,azul,a). cor(1,vermelho,b). cor(2,azul,b). cor(3,verde,b). cor(4,azul,b). cor(5,verde,b). Até agora expressamos os fatos neste programa Prolog. Para saber se existe a mesma cor em regiões adjacentes, é necessário definir uma regra que permite identificar o conflito. A regra pode ser expressa da seguinte forma: conflito(Grupo):- adjacente(X,Y), cor(X,Cor,Grupo), cor(Y,Cor,Grupo). Foi criada, portanto, a cláusula “conflito”, que irá responder se existe um conflito em um grupo de cores, conforme a variável “Grupo”. As cláusulas utilizadas como premissas são “adjacente” e “cor”. A regra de conflito é interpretada da seguinte forma: “duas regiões adjacentes estão em conflito se as cores destas regiões são as mesmas”. Com exceção das variáveis “X” e “Y”, a variável “Cor” e “Grupo” são as mesmas nas duas cláusulas de “cor” (Medeiros, 2018, p. 108). Dessa forma, quando o programa Prolog for executado, para uma consulta: ?- conflito(a). false Note a substituição por uma constante dentro da cláusula “conflito”. No caso do grupo “a”, o programa deduz que não há conflito. Já no caso do grupo “b” acontece o contrário: ?- conflito(b). true O programa completo, que chamaremos de “mapas.pl”, será então: adjacente(1,2). adjacente(2,1). adjacente(1,3). adjacente(3,1). adjacente(1,4). adjacente(4,1). adjacente(1,5). adjacente(5,1). adjacente(2,3). adjacente(3,2). adjacente(2,4). adjacente(4,2). adjacente(3,4). adjacente(4,3). adjacente(4,5). adjacente(5,4). cor(1,vermelho,a). cor(2,azul,a). cor(3,verde,a). 9 cor(4,amarelo,a). cor(5,azul,a). cor(1,vermelho,b). cor(2,azul,b). cor(3,verde,b). cor(4,azul,b). cor(5,verde,b). conflito(Grupo):- adjacente(X,Y),cor(X,Cor,Grupo),cor(Y,Cor,Grupo). Quando executamos no SWI-PROLOG, conforme a Figura 5, constatamos a partir da execução das consultas de existe conflito nos conjuntos de cores presentes na base de conhecimento. Figura 6 – Execução do programa “mapas.pl” no SWI-PROLOG As consultas 1 e 2 mostram o uso direto das constantes dos grupos “a” e “b”. A consulta 3 mostra o uso de uma variável (Z) ao invés de uma constante. A pergunta associada aqui seria: “Existe algum grupo de cores que apresente conflito?”. TEMA 3 – TORRE DE HANOI Um problema bastante conhecido na IA é denominado Torres de Hanói. Nesse problema, o objetivo é colocar as peças no último pino, de forma que, na movimentação, uma peça maior não fique sobre uma peça menor. Podemos resolvê-lo a partir de uma estratégia de busca em profundidade. A expressão do programa em Prolog para essa finalidade é bastante simples. Utilizamos uma configuração com três peças (Figura 7) para simplificar a explicação da resolução desse problema em Prolog (Medeiros, 2018, p. 115). 10 Existem três pinos (“1”, “2”, e “3”) e três peças (“a”, “b” e “c”). Elas estão empilhadas no primeiro pino, de forma crescente quanto ao tamanho. O objetivo é fazer com que essa mesma pilha seja colocada no pino 3 (Figura 7). Podemos utilizar o pino central para auxiliar na transferência das peças até o pino-objetivo. Entretanto, há uma restrição: durante a movimentação das peças, uma peça menor não pode ficar sob uma peça maior (Figura 8). Figura 7 – Problema da Torre de Hanói com três peças Figura 8 – Um estado proibido na resolução do problema das Torres de Hanoi Fonte: Medeiros, 2018, p. 115. A resolução desse problema pode ser vista na Figura 9. As peças são movimentadas para os pinos seguintes, respeitando a restrição colocada, de forma a se reproduzir no último pino a configuração do primeiro pino. 11 Figura 9 – Resolução passo a passo do problema A Figura 9 mostra a resolução do problema. As peças são colocadas, utilizando-se o pino central como auxiliar, de maneira a reproduzir no pino objetivo a mesma pilha do estado inicial. No programa em Prolog, utilizaremos o comando reservado “write” para imprimir um determinado texto ou valor de variável. Ao final do uso de “write”, podemos mudar a impressão para a próxima linha utilizando “nl”. Para resolver o problema, não é necessário criar fatos referentes a cada peça sendo movimentada. Basta que movimentemos sempre a peça que esteja no topo de um pino. Portanto, criaremos uma cláusula “move” que indicará a movimentação de uma peça que esteja no topo de um pino para outro pino indicado como argumento: move(N,X,Y,Z) 12 Nessa cláusula, N é o número de peças para movimentar, X é o pino inicial, Y é o último pino e Z é o pino intermediário. A cláusula “move” será chamada de forma recursiva, sendo a raiz outra cláusula “move” que imprimirá o comando de movimentação da peça do topo: move(1,X,Y,_) Esse comando imprimirá a mensagem “Mova a peça do topo de X para Y”. Note que o último argumento é dummy (_), pois não nos interessa para a impressão da mensagem. Assim, o programa a seguir perfaz a movimentação de acordo com a restrição imposta ao problema. Note que um dos critérios da execução de forma recursiva continuar é que N seja maior do que 1 (verifique a segunda regra “move”), expressa como “N>1”. move(1,X,Y,_) :- write('Mova o disco do topo de '), write(X), write(' para '), write(Y), nl. move(N,X,Y,Z) :- N>1, M is N-1, move(M,X,Z,Y), move(1,X,Y,_), move(M,Z,Y,X). Na execução do programa, fazemos a chamada a move(3,1,3,2), indicando 3 peças a movimentar, apartir do pino 1 até o pino 3, utilizando o pino 2 como auxiliar. Verifique a resolução abaixo feita pelo PROLOG com o passo a passo da figura 4-22: ?- move(3,1,3,2). Mova a peça do topo de 1 para 3 Mova a peça do topo de 1 para 2 Mova a peça do topo de 3 para 2 Mova a peça do topo de 1 para 3 Mova a peça do topo de 2 para 1 Mova a peça do topo de 2 para 3 Mova a peça do topo de 1 para 3 O programa pode resolver problemas com 3 peças, e também pode ser extrapolado para mais. Concluindo, o exemplo da Torre de Hanói mostra a versatilidade da linguagem Prolog na abordagem de problemas. Na robótica educacional, pode-se imaginar um exemplo físico de uma Torre de Hanói com um braço robótico fazendo a movimentação das peças. Por meio de sensores ou 13 câmeras, o robô pode identificar a peça a ser movimentada e fazer o movimento acionando o braço. Porém, como mencionamos no início, será preciso contar com uma plataforma mais robusta, que tenha condições de rodar um programa Prolog. TEMA 4 – CUSTO DE CAMINHOS Nos casos em que um robô que se desloca por algum ambiente precisa avaliar qual o melhor caminho a ser seguido, é necessário que ele calcule os caminhos possíveis para que então escolha aquele que será o mais viável. Pode- se fazer em Prolog um programa para se resolver este tipo de problema. Uma forma de representação para esse problema é simbolizar os pontos onde deve chegar o robô como nós (pontos) e a ligação entre cada par de nós como sendo os caminhos que ele pode percorrer. Na Figura 10 há um exemplo. Existem 6 nós pelos quais o robô precisa passar, com as possíveis conexões (caminhos) entre eles. Note que cada caminho tem um valor específico, que pode ser a distância ou algum valor que represente o curso para ir de um nó a outro. Figura 10 – Exemplo para o problema de avaliação de curso de caminho Fonte: Elaborado com base em Palazzo, 1997. Adotando-se o ponto de saída como sendo o nó “A” e o ponto de chegada como o nó “F”, o robô deve percorrer cada caminho entre os nós para sair de “A” e chegar até “F”. Pode-se notar que existem vários caminhos para se fazer o trajeto. Portanto, com Prolog, pode-se escrever um programa que avalie os caminhos possíveis, para que depois se possa tomar a decisão sobre qual deverá ser seguido. A ideia geral é que, a cada caminho sondado, o programa vá somando os custos (números de cada caminho), chegando ao final com um valor determinado. 14 Em primeiro lugar, deve-se representar cada caminho possível como um fato no programa Prolog. Isso é feito colocando uma cláusula que será denominada de “arco”, com o nó de saída e o nó de chegada. arco(a,b,3). arco(a,c,4). arco(a,d,6). arco(b,d,2). arco(c,f,5). arco(c,d,3). arco(d,e,2). arco(e,f,3). Lembre-se de que cada nó é representado por uma letra, e que eles devem ser representados por letra minúscula, pois são constantes. Para simplificar, nesse exemplo estamos utilizando apenas uma direção, sem representar os caminhos inversos. A partir dos fatos registrados, devemos representar as regras relativas aos caminhos que ligam entre os nós. O ponto de partida é que cada arco já é um caminho. Um caminho pode ser feito de dois, três ou mais arcos. Portanto, haverá uma cláusula “caminho” que faz a ligação de um nó até outro: caminho(U,V,L) :- arco(U,V,L). Assim, a regra nos diz que um caminho para ir do nó “U” ao nó “V”, com o custo “L”, é o próprio arco para ir do nó “U” ao nó “V”, que tem o custo “L”. Por indução, pode-se adotar o caminho que tem dois arcos: caminho(U,W,L) :- arco(U,W,M),arco(W,V,N),L = M+N. O custo “L” agora é a soma dos custos dos dois caminhos “M” e “N”. E assim segue sucessivamente, para três arcos: caminho(U,W,L) :- arco(U,W,M),arco(W,X,N),arco(X,V,O), L = M+N+O. No entanto, essa abordagem requer que montemos várias regras, para contemplar os caminhos possíveis. E pode ser que um caminho possível com mais arcos não venha a ser contemplado, o que não irá resolver o problema. Entretanto, em Prolog há uma forma de escrever as regras de forma a economizar a escrita. São necessárias apenas duas regras: uma que determine o menor caminho (com um arco) e outra que chame a si mesma para contemplar os outros caminhos combinados possíveis: caminho(U,V,L) :- arco(U,V,L). 15 caminho(U,V,L) :- arco(U,W,M),caminho(W,V,N), L = M+N. Note que a primeira regra é a mesma vista anteriormente para um caminho. Já a segunda está mostrando que um caminho pode ser combinado de um arco mais um caminho. Essa regra é chamada de recursiva. Ela pode ser chamada continuamente enquanto houver caminhos e arcos possíveis, até o arco que está sendo considerado. Assim, o programa Prolog completo pode ser visto a seguir: arco(a,b,3). arco(a,c,4). arco(a,d,6). arco(b,d,2). arco(c,f,5). arco(c,d,3). arco(d,e,2). arco(e,f,3). caminho(U,V,L) :- arco(U,V,L). caminho(U,V,L) :- arco(U,W,M),caminho(W,V,N), L = M+N. A execução desse programa no SWI-PROLOG é mostrada na Figura 11, feita por meio da consulta: ?- caminho(a,f,L). Essa consulta pergunta qual o caminho para ir de “a” até “f”. A variável L é colocada para mostrar o raciocínio de Prolog na identificação dos custos dos caminhos. Note que não é feita a soma dos valores, apenas mostramos cada arco como parcela da soma. Para somar os valores efetivamente, deve-se utilizar o comando “L is M+N”, ao invés de “L = M+N”. Figura 11 – Execução do programa para saber o custo de caminhos possíveis da Figura 10 16 TEMA 5 – RACIOCÍNIO LÓGICO Para ilustrar o poder de raciocínio que a linguagem Prolog pode proporcionar, vamos apresentar agora o programa da árvore genealógica. Esta classe de problema pode ser estendida para outros contextos, quando for necessário que um robô raciocine sobre um determinado conjunto hierárquico de fatos. Um sistema em Prolog poderá proporcionar informações sobre parentescos complexos somente com regras que definam tais parentescos relativos aos fatos cadastrados na base. Veja a árvore genealógica descrita na Figura 12. Figura 12 – Exemplo para o problema da árvore genealógica Fonte: Medeiros, 2018, p. 109. Primeiramente, é necessário definir uma cláusula que mostre o relacionamento do parentesco direto existente entre duas pessoas da árvore. Assim, estabelece-se uma cláusula “genitor” com dois argumentos: o primeiro sendo o genitor do segundo, de acordo com a árvore. Pela análise das relações na árvore, podemos então começar nosso programa em Prolog contendo os seguintes fatos (Medeiros, 2018, p. 110): genitor(carlos,maria). genitor(luiza,maria). genitor(carlos,fernando). genitor(luiza,paulo). genitor(maria,leticia). genitor(maria,eduardo). genitor(eduardo,roberto). 17 Podemos fazer consultas ao Prolog, carregando o programa e perguntando, por exemplo, “quem é o genitor de Fernando?”. Introduzimos uma variável (X) no campo referente ao primeiro argumento e procedemos à consulta: ?- genitor(X,fernando). X=carlos. Da mesma forma, se quiséssemos saber “de quem Luiza é genitor?”, colocamos novamente a consulta no Prolog através de uma variável no segundo argumento: ?- genitor(luiza,Y). Y=paulo. Caso exista a necessidade de saber “quem é genitor de quem?”, basta colocar as variáveis nos dois argumentos da cláusula “genitor”, com o Prolog retornando todas as instâncias existentes na base. Note o ponto-e-vírgula ao final de cada par resultante. Na interface de Prolog, é necessário clicar a barra de espaço para identificar possíveis soluções adicionais: ?- genitor(X,Y). X = carlos, Y = maria ; X = luiza, Y = maria ; X = carlos, Y = fernando ; X = luiza, Y = paulo ; X = maria, Y = leticia ; X = maria, Y = eduardo ; X = eduardo, Y = roberta. Como um fato representado na base de dados do Prolog, a cláusula “genitor(carlos,maria)” identifica (apenas) quecarlos é genitor de maria. Não sabemos ainda se “carlos” é considerado pai, pois isso não está de forma explícita na base de fatos. Poderíamos criar uma nova cláusula “pai” que indicaria “carlos” como pai de “maria” tal como “pai(carlos,maria)”. Entretanto, se seguíssemos esta estratégia, deveríamos indicar para cada fato cadastrado um relacionamento “pai”, ou mesmo “mãe”, dessa forma. Adotaremos uma maneira diferente, indicando apenas o gênero de cada pessoa. Para obtermos isso, é necessário estipular uma cláusula que indique a informação de quem na base é considerado masculino ou feminino. Assim, as seguintes cláusulas permitem essa definição: 18 masculino(carlos). masculino(fernando). masculino(paulo). masculino(eduardo). feminino(roberta). feminino(luiza). feminino(maria). feminino(leticia). Poderíamos agora, em função da base de fatos cadastrada neste programa exemplo, identificar diferentes relacionamentos a partir de regras, ao invés de escrevermos fatos indefinidamente. Por exemplo, podemos indicar a cláusula “pai” com a seguinte regra: pai(X,Y) :- genitor(X,Y), masculino(X). Assim, quando executarmos o programa com esta regra, o Prolog retornará todos aqueles que são genitores e definidos na base de fatos como masculinos. No caso de uma cláusula “mãe”, podemos analogamente definir com a seguinte regra: mae(X,Y) :- genitor(X,Y), feminino(X). Para identificar uma relação mais complexa, como por exemplo a relação “irmão”, precisamos pensar sobre como a árvore genealógica pode nos dar esta informação. Na Figura 11, podemos identificar que “maria” e “fernando" são irmãos, assim como “leticia” e “eduardo". Portanto, podemos definir uma regra segundo a qual dois indivíduos X, Y são irmãos quando apresentam o mesmo genitor. Portanto, uma regra que pode retornar esta informação seria: irmao(X,Y) :- genitor(Z,X), genitor(Z,Y), X \= Y. Assim, Z é o mesmo genitor para X e Y. Note que colocamos ao final da regra uma cláusula para dizer que X é diferente de Y (X \= Y); senão, o Prolog pode retornar X como irmão de X. Para o exemplo de uma relação mais complexa, como poderíamos criar uma regra que identificasse o avô? Pela Figura 11, podemos ver que Carlos é avô de Leticia ou Eduardo. Podemos identificar a regra “avo” encadeando os genitores no corpo da regra: avo(X,Y) :- genitor(X,Z),genitor(Z,Y),masculino(X). 19 Quando executamos a consulta para saber “quem é avô de quem?”, obtemos o seguinte: ?- avo(X,Y). X = carlos, Y = leticia ; X = carlos, Y = eduardo ; Uma consulta em Prolog pode ainda conter cláusulas combinadas. Por exemplo, caso queiramos responder à pergunta “Quem é neto de Carlos?”, podemos escrever a consulta combinando duas cláusulas: ?- genitor(carlos,Y), genitor(Y,Z). Y = maria, Z = leticia ; Y = maria, Z = eduardo ; Como visto no caso do custo de caminho, a linguagem Prolog permite o uso de recursividade. Imagine que queremos saber, na árvore genealógica, quem são os antepassados de “Roberta”. Podemos começar com o genitor de Roberta, Eduardo. Assim, é necessário definir a relação “antepassado” em função de “genitor”: antepassado(X,Y) :- genitor(X,Y). Essa regra nos diz que alguém é antepassado de outrem se esse alguém também for genitor de outrem. Assim, Eduardo e Roberta são incluídos aqui, juntamente com todos os outros fatos. Entretanto, o genitor do genitor também é um antepassado (avô ou avó). Para definirmos a regra que inclua também esses indivíduos, precisamos definir “antepassado” da seguinte forma: antepassado(X,Y) :- genitor(X,Z), genitor(Z,Y). Podemos fazer agora a consulta, após as duas regras: ?- antepassado(X,roberta). X = eduardo ; X = maria ; Porém, podemos identificar na Figura 11 que “Carlos” e “Luiza” também são antepassados e ficaram de fora do resultado da consulta. É necessário adicionar mais uma regra que permita incluí-los na consulta: antepassado(X,Y) :- 20 genitor(X,Z), genitor(Z,U), genitor(U,Y). O resultado agora contempla os antepassados faltantes: ?- antepassado(X,roberta). X = eduardo ; X = maria ; X = carlos ; X = luiza ; No caso do exemplo, não é necessário retroceder. Porém, em árvores mais extensas, deveríamos explicitar cada regra conforme aumentasse o grau de ancestralidade, o que torna o programa dependente do contexto. Para explicitar um conjunto de regras que contemplasse qualquer grau de ancestralidade sem a necessidade de incluir regras para contemplar uma a uma, podemos construir as regras de forma recursiva: antepassado(X,Y) :- genitor(X,Y). antepassado(X,Y) :- genitor(X,Z),antepassado(Z,Y). Note que há apenas duas regras: a primeira identifica a relação direta do genitor como o primeiro antepassado; as próximas são contempladas pela segunda regra, na qual “antepassado” está tanto na cabeça quanto no corpo da regra. Isso evita a necessidade de reescrever cada grau de ancestralidade conforme a descrição da árvore genealógica. Temos, portanto, o seguinte programa em Prolog que resume o exercício da linguagem feito aqui: % Programa da Árvore Genealógica % Fatos genitor(carlos,maria). genitor(luiza,maria). genitor(carlos,fernando). genitor(luiza,paulo). genitor(maria,leticia). genitor(maria,eduardo). genitor(eduardo,roberta). masculino(carlos). masculino(fernando). masculino(paulo). masculino(eduardo). feminino(roberta). feminino(luiza). feminino(maria). feminino(leticia). 21 % Regras pai(X,Y) :- genitor(X,Y), masculino(X). mae(X,Y) :- genitor(X,Y), feminino(X). irmao(X,Y) :- genitor(Z,X), genitor(Z,Y), X \= Y. avo(X,Y) :- genitor(X,Z),genitor(Z,Y),masculino(X). antepassado(X,Y) :- genitor(X,Y). antepassado(X,Y) :- genitor(X,Z),antepassado(Z,Y). 22 REFERÊNCIAS MEDEIROS, L. F. DE. Inteligência artificial aplicada. Curitiba: InterSaberes, 2018. PALAZZO, L. A. M. Introdução a Prolog. Pelotas: Ed. UCPel, 1997.
Compartilhar