1.45k likes | 1.58k Views
Universidade Federal de São Carlos Departamento de Computação. Programação Lógica. ProLog. Prof. Dr. Antonio Francisco do Prado e-mail: prado@dc.ufscar.br. Entrada e SaÃda. Manipulando Base de Dados. Operadores e Listas. Programação Lógica. Linguagem PROLOG. ExercÃcios.
E N D
Universidade Federal de São CarlosDepartamento de Computação Programação Lógica ProLog Prof. Dr. Antonio Francisco do Pradoe-mail: prado@dc.ufscar.br
Entrada e Saída Manipulando Base de Dados Operadores e Listas Programação Lógica Linguagem PROLOG Exercícios Fatos, Regras e Controle de Corte Outros Exemplos Metodologia de Programação Lógica Fuzzy
Programação Lógica Raízes • O uso da lógica na representação do raciocínio remonta os estudos de Boole (1815-1864) e de De Morgan (1806-1871) sobre a “Álgebra de Boole”; • Em 1879 surge a primeira versão do “Cálculo de Predicados” com o matemático alemão Göttlob Frege, onde era oferecida uma representação adequada para a formalização do raciocínio dedutivo; • Em 1930, em estudos simultâneos, o alemão Kurt Gödel e o francês Jacques Herbrand demonstraram que o mecanismo de prova do Cálculo de Predicados poderia oferecer uma prova formal de toda proposição logicamente verdadeira; • Ainda na década de 30 diversos estudos, entre eles os de Alan Turing e Alonzo Church, aproximaram muito o Cálculo de Predicado com a forma como hoje é conhecido; • Em 1939 toda fundamentação teórica básica da lógica computacional já estava pronta, faltava apenas uma meio prático para realizar o imenso volume de computações necessárias aos procedimentos de prova;
Somente nos meados da década de 50 que o desenvolvimento dos computadores permitiu experiências mais significativas com o Cálculo de Predicados; • Em 1958 uma forma simplificada do Cálculo de Predicados denominada forma Clausal começou a despertar o interesse dos estudiosos. Nessa forma de cálculo era empregado um tipo muito simples de sentença lógica, a Cláusula; • Também nessa época (1960) Dag Prawitz propôs um novo tipo de operação sobre os objetos do Cálculo de Predicados, que foi mais tarde conhecido como Unificação; • Apesar de toda base teórica para a Programação Lógica estar pronta, ela somente se tornou possível a partir da pesquisa sobre prova matemática de teoremas, particularmente no desenvolvimento do Princípio da Resolução por J.A. Robinson (1965); • A expressão “Programação Lógica” (Logic Programming) surge com Robert Kowalski (1974) e designa o uso da lógica como uma linguagem de programação de computadores. OBS: muitos outros pesquisadores colaboraram para o aparecimento da Programação Lógica. No texto apenas foram relatados alguns deles.
Conceitos da Programação Lógica • Uma das principais idéias em Programação Lógica é que um algoritmo é constituído por dois elementos disjuntos: • Lógica: corresponde à definição do que deve ser solucionado; • Controle: estabelece como a solução pode ser obtida; • A tarefa do programador é somente descrever (especificar) o componente lógico do algoritmo, deixando o controle da execução para ser exercido pelo sistema de programação em lógica utilizado; • Um Programa em Lógica é então a representação de determinado problema ou situação expressa através de um conjunto finito de um tipo especial de sentenças lógicas, denominadas cláusulas; • O paradigma fundamental da programação em lógica é o da Programação Declarativa, em oposição à Programação Procedimental típica das linguagens convencionais; • O ponto focal da Programação em Lógica consiste em identificar a noção de computação com a noção de dedução, onde a execução de programas se reduzem à pesquisa da refutação das sentenças do programa em conjunto com a negação da sentença que expressa a consulta, seguindo a regra: "uma refutação é a dedução de uma contradição".
Os termos "programação em lógica" e "programação Prolog" tendem a ser empregados indistintamente. Deve-se, entretanto, destacar que a linguagem Prolog é apenas uma particular abordagem da Programação em Lógica; • Pode-se expressar conhecimento (programas e/ou dados) em Prolog por meio de cláusulas de dois tipos: • Fatos: denota uma verdade incondicional; • Regras: definem as condições que devem ser satisfeitas para que uma certa declaração seja considerada verdadeira. Características da Programação Lógica • Os sistemas de Programação em Lógica em geral e a linguagem Prolog em particular possuem as seguintes propriedades: • Funcionam simultaneamente como linguagem de programação e de especificação; • Possuem capacidade dedutiva; • Operam de forma não-determinística; • Permitem a representação de relações reversíveis; • Permitem interpretação declarativa, operacional e procedimental; • São naturalmente recursivas.
Aplicações da Programação Lógica • As principais aplicações da Programação em Lógica são: • Sistemas Baseados em Conhecimento (SBCs) • Sistemas de Bases de Dados (BDs); • Sistemas Especialistas (SEs); • Processamento da Linguagem Natural (PLN); • Educação; • Modelagem de Arquiteturas Não-Convencionais. Linguagens Convencionais x Lógicas
Entrada e Saída Manipulando Base de Dados Operadores e Listas Programação Lógica Linguagem PROLOG Exercícios Fatos, Regras e Controle de Corte Outros Exemplos Metodologia de Programação Lógica Fuzzy
Linguagem PROLOG Linguagens Funcionais x Lógicas Conclusão Tanto as linguagens Funcionais como Lógicas são importantes e empregadas principalmente em Sistemas Transformacionais, Inteligência Artificial (IA) e Robótica.
Histórico • Criado por Alain Colmerauer por volta de 1970 na Universidade de Marselha, França. • Seu objetivo inicial era servir de apoio a sistemas de Linguagem Natural. Algol 60 Algol 68 Prolog Programação Prolog Para executar um programa em prolog, primeiro deve-se criar um arquivo com a extensão correspondente do interpretador ( .ari, .pl, etc). Depois, execute no interpretador correspondente. Em seu menu acione a opção File e Consult para carregar o programa no interpretador. Se houver algum erro no programa, este será mostrado na tela, senão o programa está pronto para ser executado (interpretado).
A programação Prolog consiste fundamentalmente em: • Declarar fatos ou assertivas; • Estabelecer regras ou procedimentos; e • Fazer perguntas. • Os fatos e as regras são fornecidos e a linguagem usa dedução para obter respostas para as questões.
Considerações sobre o aprendizado do aluno Temos observado que o estilo procedimental de programação, talvez por ser o primeiro a ser ensinado, interfere de certa forma, na correta aprendizagem de linguagens como Lisp ou Prolog por parte dos estudantes. Tentaremos minimizar essa interferência submetendo o aluno a resolução de conjuntos de problemas semelhantes, principalmente problemas envolvendo listas. É essencial que o aluno tente compreender a lógica de programação Prolog, entendendo mecanismos de unificação, backtracking entre outros. Também é importante que o aluno tente resolver os exercícios propostos no ultimo capítulo, para consolidar o seu aprendizado.
Entrada e Saída Manipulando Base de Dados Operadores e Listas Programação Lógica Linguagem PROLOG Exercícios Fatos, Regras e Controle de Corte Outros Exemplos Metodologia de Programação Lógica Fuzzy
Fatos, Regras e Controle de Corte Introdução • Um programa Prolog consiste de : • Definição de fatos arespeito de objetos de dados e suas relações; • Definição de regras a respeito de objetos de dados e suas relações; • Interrogação a respeito de objetos de dados e suas relações. Um objeto de dados em Prolog é definido de acordo com a seguinte árvore : objetos objetos simples estruturas variáveis constantes átomos números
Átomo é a estrutura mais simples do Prolog, eles são construídos por cadeias de letras, dígitos e caracteres especiais( _, +, *). Todo átomo deve possuir a primeira letra minúscula. Exemplos de átomos : x_y, maria, copa2002. • Números em Prolog incluem números inteiros e números reais. A sintaxe é bem simples : 1, -20. • Variáveis são cadeias de letras, dígitos e caracteres sempre começando com letra maiúscula. • Estruturas são objetos de dados que têm vários componentes, podendo cada um deles, por sua vez, ser uma outra estrutura. • Por exemplo, uma data pode ser vista como um conjunto de dia, mês e ano. Embora composta por vários componentes, estruturas são tratadas no programa como objetos simples. A combinação dos componentes de um objeto simples é feita através de seu funtor. No caso de data, o funtor pode ser data e pode – se escrever 7 de setembro de 2002 da seguinte maneira : • data(7, setembro, 2002) • Note que os componentes 7, setembro e 2002 são todos constantes(2 inteiros e 1 átomo)
Fatos, Regras e Controle de Corte tom pam liz bob pat ann jim Fatos Um Fato denota uma verdade incondicional. Uma relação pode ser especificada por um fato. Sintaxe: predicado(arg1[,arg2,...,arg n]). Argumentos (arg1...arg n) são objetos quaisquer e Predicado é a relação que une esses objetos.
progenitor(pam,bob). %pam é um dos progenitores de bob progenitor(tom,bob). progenitor(tom,liz). %tom é um dos progenitores de liz progenitor(bob,ann). progenitor(bob,pat). progenitor(pat,jim). Em Prolog usam-se maiúsculas para variáveis e minúsculas para átomos. Questões: ?-progenitor(X,Y). X=pam , Y=bob ; X=tom , Y=bob ; X=tom , Y=liz ; X=bob , Y=ann ; X=bob , Y=pat ; X=pat , Y=jim ; variável átomo ?-progenitor(bob,pat). yes ?-progenitor(liz,pat). no ?-progenitor(X,liz). X=tom ; no ?-progenitor(bob,X). X=ann X=pat
X Progenitor Avos Y Progenitor jim Quem são os avós de Jim? (1) Quem é progenitor de Jim? (Por exemplo, Y) e (2) Quem é progenitor de Y? (Por exemplo, X). "Encontre X e Y tais que X é progenitor de Y e Y é progenitor de Jim". Questões: ?-progenitor(X, Y), progenitor(Y, jim). X=bob Y=pat ; • Quem é neto de Tom? Questões: • ?-progenitor(tom, X), progenitor(X, Y). • X=bob Y=ann; • X=bob Y=pat.
Definição de uma extensão da base anterior • (característica do objeto): • mulher(pam). • homem(tom). • homem(bob). • mulher(liz). • mulher(pat). • mulher(ann). • homem(jim). Regras As regras definem as condições que devem ser satisfeitas para que uma certa declaração seja considerada verdadeira. Sintaxe: pred(arg/Var,arg/Var) :- pred(arg/Var,arg/Var). cabeça se corpo (conclusão) (condição) Onde o símbolo :- indica uma condição (se) e separa a regra em conclusão ou cabeça da regra e condição ou corpo da regra.
Definir a relação filho. • Poderia ser definida como: • filho(bob,tom). • filho(bob,pam). • ... • Entretanto existe uma maneira mais elegante, que seguiria a seguinte declaração: • Para todo X e Y • Y é filho de X se • X é progenitor de Y. • filho(Y, X) :- progenitor(X, Y). • "Para todo X e Y, se X é progenitor de Y, então Y é filho de X". • Questões: • ?-filho(jim,pat). yes filho(Y, X) :- progenitor(X, Y) cabeça se corpo (conclusão) (condição)
Frases Compostas robo(peter). capaz_de_fazer(peter,armas). machuca(armas,pedro). homem(pedro). proibido_fazer(R,B):-robo(R),capaz_de_fazer(R,B), machuca(B,H),humano(H). Questão ? - proibido_fazer(R,B). R = peter , B = armas Significa: R está proibido de fazer um ato B se R for um robô e R for capaz de fazer B e B machuca H e H for humano. Para todo R e B proibido_fazer(R,B) se existe H tal que robo(R) e capaz_de_fazer(R,B) e machuca(B,H) e humano(H). humano(H):-homem(H). Hífen Usa-se o hífen para indicar irrelevância de um objeto Exemplo: aniversario(maria,data(25,janeiro,1979)). aniversario(joao,data(5,janeiro,1956)). signo(Pessoa,aquario):-aniversario(Pessoa,data(Dia,janeiro,_)), Dia >= 20. ? - signo(Pessoa,aquario). Pessoa = maria; no
Conjunção e Disjunção Em prolog o operador de conjunção e ( , ) implica na necessidade de aceitação de todas as condições, enquanto o operador de disjunção ou ( ; ) permite a aceitação de uma ou outra condição. Estes operadores permitem a composição de fatos. Exemplos: amiga(X):-(X = maria; X = joana). disjunção ou • Definição de uma regra avos: • Usando-se a conjunção podemos definir o predicado avos seguindo a seguinte declaração: Para todo X e Z • X é progenitor de Y e • Y é progenitor de Z. • "Para todo X e Z, se X é progenitor de Y, e Y é progenitor de Z, então X é um dos avos de Z". • avos(X,Z):-progenitor(X,Y) , progenitor(Y,Z). conjunção e Questões: ?-avos(bob,jim). yes ?-avos(liz,jim). no ?-avos(pam,X). X=ann ; X=pat
Z progenitor progenitor X Y mulher irmã • Definição de uma regra mãe: mae(X,Y):-progenitor(X,Y),mulher(X). Questões: ?-mae(pat,jim). yes ?-mae(bob,ann). no • Definição de uma regra irmã: irma(X,Y):-progenitor(Z,X),progenitor(Z,Y),mulher(X). Questões: ?-irma(ann,pat). yes ?-irma(X,pat). X=ann ; X=pat ; no • Definição de uma regra diferente: diferente(X,Y):-X\==Y. %X é diferente de Y
bob Antepassados indiretos Antepassados diretos pat jim • Definição de uma regra irmã usando a regra diferente: irma(X,Y):-progenitor(Z,X),progenitor(Z,Y),diferente(X,Y). Questão: ?-irma(X,pat). X=ann ; no • Definição de uma regra tia: tia(X,Y):-progenitor(Z,Y),irma(X,Z). Questão: ?-tia(liz,X). X=ann ; X=pat ; no Recursão • Definição de uma regra antepassado • Necessita de duas regras: antepassados diretos e antepassados indiretos.
Primeira regra bastante simples: • Para todo X e Z • X é antepassado de Z se • X é progenitor de Z. • antepassado(X, Z) :- progenitor(X, Z). • Segunda regra complicada. A cadeia de progenitores pode se estender indefinidamente: • antepassado(X, Z) :- progenitor(X, Y), • progenitor(Y, Z). • antepassado(X, Z) :- progenitor(X, Y1), • progenitor(Y1, Y2), • progenitor(Y2, Z). • antepassado(X, Z) :- progenitor(X, Y1), • progenitor(Y1, Y2), • progenitor(Y2, Y3), • progenitor(Y3, Z). • ....
X Progenitor Y Antepassado Antepassado Z • Solução: definir a regra recursivamente • Para todo X e Z • X é antepassado de Z se • existe um Y tal que • X é progenitor de Y e • Y é antepassado de Z. • A segunda regra fica: antepassado(X, Z) :- progenitor(X, Y), • antepassado(Y, Z). Questões: ?-antepassado(X,liz). X=tom ; no ?-antepassado(X,pat). X=bob ; X=pam ; X=tom ; no
Controle de Corte Há ocasiões em que, por um ou outro motivo, desejamos controlar a execução do programa. Para isso, utilizamos o mecanismo de corte. Operador Cut • Algumas das principais aplicações do cut são as seguintes: • Unificação de padrões, de forma que quando um padrão é encontrado os outros padrões possíveis são descartados (quando uma determinada regra é a última a ser analisada e haveria problemas na continuidade da verificação das demais regras); • Para eliminar da árvore de pesquisa soluções alternativas quando uma só é suficiente; • Especificar regras mutuamente exclusivas, expressas na forma: Se P então Q senão R; • Como indicativo do caso limite ou para evitar que a pesquisa prossiga indefinidamente através do backtracking. • Simbolizado pela exclamação (“!”). • Seu uso deve ser considerado pelas seguintes razões: • Execução mais rápida do programa (não desperdiça tempo tentanto satisfazer objetivos que não contribuirão para a solucão desejada); • Economiza memória (corta a ávore de pesquisa).
Exemplos: • Podemos simular a programação procedimental em Prolog. Aqui simularemos a estrutura if-then-else: • ifThenElse(X,Y,Z) • "Se X for verdadeiro, então execute Y, senão execute Z". • ifThenElse(X, Y, _) :- X, !, Y. • ifThenElse(_, _, Z) :- Z. • Questão: • ?-ifThenElse(X, Y is Z+1, Y is 0). • OBS: este programa emprega meta-variáveis (variáveis que podem ser instanciadas com chamadas a predicados) • Exemplo do operador Cut: amigo(joana,ana). amigo(maria,ana). amigo(pedro,jose). amigo(pedro,ana). • um_unico_amigo(X,Y):-amigo(X,Y),!. Questões: ?- um_unico_amigo(X,ana). X = joana ?- um_unico_amigo(pedro,X). X = jose • Mínimo entre dois números: • minimo(X,Y,X):-X =< Y, !. • minimo(X,Y,Y):-X > Y, !. Questões: ?- minimo(10,3,Min). Min = 3 ?- minimo(12,56,Min). Min = 12
Fail O predicado Fail, força um retrocesso, como se indicasse uma falha. Junto com o corte (Cut), acelera a avaliação de regras economizando memória. Exemplos: • Exemplo do operador Fail: cliente(ana,123,bradesco). cliente(jose,456,itau). executa :- cliente(Nome,Conta,Agencia), write(Nome),write(’ tem conta ’),write(Conta), write(’ na agencia ’),write(Agencia),nl,fail. • Neste caso fail força um backtracking e repete a a impressão. Questões: ?-executa. ana tem conta 123 na agencia bradesco jose tem conta 456 na agencia itau no Not (X) O operador unário not define uma forma particular de negação denominada "negação por falha”.
Definição de uma regra para exemplificar o not: • estudante(jorge). • casado(jose). • estudante_solteiro(X):-not casado(X), estudante(X). Questões: ?-estudante_solteiro(jorge). yes ?-estudante_solteiro(jose). no Cuidados com o Cut e a Negação • O uso do cut pode levar a perda da correspondência entre o significado declarativo e a interpretação operacional do programa: • Se não houver cuts no programa pode-se trocar a ordem das cláusulas e objetivos que seu significado declarativo não será alterado (há apenas uma possível perda de performance); • Se houver cuts essa alteração pode afetar o significado declarativo levando a resultados inesperados. • O uso da negação também pode levar a resultados inesperados. Qual o problema do programa abaixo? Questões: r(a). q(b). p(X) :- not r(X). ?-p(X), q(X). no ?-q(X), p(X). X = b.
Outros Exemplos de Regra • Definição das regras positivo e negativo: positivo(X):-X>=0. %X é maior ou igual a 0 negativo(X):-X<0. %X é menor que 0 Questões: ?-positivo(7). yes ?-negativo(10). no • Máximo entre 2 números: max(X,Y,X):-X>=Y. max(X,Y,Y):-X<Y. • Máximo entre 2 números usando corte: max(X,Y,X):-X>=Y,!. max(X,Y,Y). Questão: ?-max(10,3,Max). Max=10 • Definição das regras par e impar: par(X):-X mod 2 =:=0 % o mod de 2 é igual a 0 impar(X):-par(X),!,fail. impar(X). Questões: ?-par(6). yes ?-impar(6). no
Y=f(X) • Definição de uma função f(X): • f(X, 0):- X < 3. • f(X, 2):- 3=< X, X < 6. • f(X, 4):- 6 =< X. • Definição de uma função f(X) com corte: • f(X, 0):- X < 3, ! . • f(X, 2):- 3 =< X, X < 6, ! . • f(X, 4). Questões: ?-f(1,Y),2<Y. no ?-f(7,Y). Y = 4 • Definição de uma regra para fatorial: • fatorial(0, 1):- !. • fatorial(N, F):- N1 is N - 1, %Atribuição • fatorial(N1, F1), • F is F1 * N. Questão: ?- fatorial(4, F). F = 24
Exemplo de recursividade Para o exemplo anterior da regra do Fatorial recursivo, é mostrado a execução passo a passo, note que em todos os passos(com excessão do 5) há unificação com a segunda regra. (primeira regra é critério de parada). Questão: ?- fatorial(4, F). F = 24 Passo 1 : fatorial(4, F) { N = 4, N1 =3, F = F1 * 4, fatorial(N1, F1) } F = 6 Passo 2 : fatorial(3, F) { N = 3, N1 =2, F = F1 * 3, fatorial(N1, F1) } F = 2 Passo 3 : fatorial(2, F) { N = 2, N1 =1, F = F1 * 2, fatorial(N1, F1) } F = 1 Passo 4 : fatorial(1, F) { N = 1, N1 =0, F = F1 * 1, fatorial(N1, F1) } F = 1 Passo 5 : fatorial(0, F) {F = 1 }
Definição de uma regra para fatorial com recursão de cauda: • fact(N,Fn):-fact(N,Fn,0,1). • fact(N,Fn,N,Fn):-!. • fact(N,Fn,I,P):-Novoi is I+1,Novop is P*Novoi, • fact(N,Fn,Novoi,Novop). Questão: ?- fact(6, F). F = 720 Recursão de cauda. A recursividade é a última. • Definição de uma regra para fibonacci: • fib(1, 1). • fib(2, 1). • fib(N, F):- N > 2, • N1 is N - 1, fib(N1, F1), • N2 is N - 2, fib(N2, F2), • F is F1 + F2. Questão: ?- fib(6, F). F = 8 ; no
Entrada e Saída Manipulando Base de Dados Operadores e Listas Programação Lógica Linguagem PROLOG Exercícios Fatos, Regras e Controle de Corte Outros Exemplos Metodologia de Programação Lógica Fuzzy
Operadores e Listas Operadores Aritméticos Exemplo: • Cálculo de um número elevado ao quadrado: • ao_quadrado(X,Y):-Y is X*X. Questão: ?- ao_quadrado(6,X). X=36
Operadores de Comparação Exemplo: • Definição de uma regra intervalo aberto: • interv_aberto(K,X1,X2):-K > X1,K < X2. Questões: ?- interv_aberto(5,0,5). no ?- interv_aberto(2,0,5). yes
Listas As listas são estruturas de dados dinâmicas com as quais é possível a construção de vetores e matrizes. Sintaxe: pred([elem,...],[],[elem[elem,...],...],arg,...). Onde elem pode ser qualquer tipo sintático. Listas são compostas por cabeça e cauda. A cabeça de uma lista pode ser qualquer objeto Prolog e a cauda deve ser uma lista. Como a cauda, por sua vez, é uma lista, ela á a lista vazia ou tem sua própria cabeça e cauda. Sintaxe para Listas A lista vazia é denotada por [] ; a lista que tem cabeça X e cauda Y é denotada por [X|Y] . Dentro das listas, os elementos são separados por vírgula. Exemplo : Lista Cabeça Cauda [gosto,de, vinho] gosto [de, vinho] [X,Y|Z] X [Y|Z] [[o,gato]] [o,gato] []
Lista 1 Lista 2 Unificação [mesa] [X|Y] X / mesa Y / [ ] [a,b,c,d] [X,Y|Z] X / a Y / b Z / [c,d] [[ana, Y] | Z] [[X, foi], [ao, cinema]] X / ana Y / foi Z / [[ao, cinema]] [ano, bissexto] [X,Y|Z] X / ano Y/ bissexto Z / [ ] Unificação em listas Em Prolog, a mais importante operação envolvendo termos é chamada unificação. Dois termos T e S unificam se : 1. Se T e S são constantes, então unificam se e só se S e T são o mesmo objeto. 2. Se S for uma variável e T qualquer termo, então unificam, e S é instanciado com T ; vicer – versa, com a variável T instanciada com S. 3. Se S e T são estruturas, eles unificam se e só se S e T tem o mesmo funtor principal e todos os elemtentos correspondentes unificam. A seguir são mostrados vários exemplos de unificação em listas :
Lista 1 Lista 2 Unificação [ano, bissexto] [X,Y,Z] não unifica (aridade diferente) [data(7,Z,W), hoje] [X,Y] X / data(7,Z,W) Y / hoje [data(7,W,1993), hoje] [data(7,X,Y), Z] X / W Y / 1993 Z / hoje Busca recursiva Freqüentemente é necessário procurar por algum termo Prolog ; isto resulta em uma busca recursiva. Para verificar se um elemento pertence à uma lista, usamos o seguinte predicado: pertence(X, [X | _]). % verifica se X esta na cabeça pertence(X, [_ | Y]):- pertence(X, Y). % verifica se X % esta na cauda Questões: ?- pertence(a, [h, a, b]). yes ?- pertence( a, [hoje, amanha]). no ?- pertence(X, [a, b, c]). X = a; X = b; X = c; no
Goal ?- X = [1,2,3],pertence(a,X). no ?- pertence( a, X),X = [1,2,3]. X = [a |_] %pode ser o 1º elemento de X X = [_ , a | _] %pode ser o 2º elemento de X X = [_ , _ , a | _] %pode ser o 3º elemento de X . . . %computação infinita! Sequência de ligação -> da esquerda para a direita Conclusão Escolha o “subgoal” com o menor número de soluções como “goal” a adivinhar. No caso anterior o “goal” a adivinhar correto é X = [1,2,3], porque tem apenas uma solução e esta solução não satisfaz pertence(a,X), porque “a” não está na lista [1,2,3].
Controle em Prolog 1. Iniciar pelo goal 2. Enquanto o goal corrente não é vazio faça 2.1. Comece pelo subgoal mais à esquerda 2.2. Se uma regra aplica ao subgoal então 2.2.1. Para esta regra, usando unificadores genéricos, volte para 2.1 Senão backtrack fim-se fim-enquanto Exemplo: anexa([ ],Y,Y ). anexa([H | X],Y,[H| Z]):- anexa(X, Y,Z). prefix(X,Z):-anexa(X,Y,Z). sufix(Y,Z):-anexa(X,Y,Z).
1º Subgoal ?- sufix ( [ a ] , L ) , prefix ( L , [ a , b , c ] ). L = [ a ] ; %sem backtraing X = [ ] Y = [ b , c ] O fato sufix ( Y´ , Z´ ) :- anexa ( X´ , Y´ , Z´ ) aplica ao subgoal mais à esquerda sufix ( [ a ] , L ) A substituição Y´ -> [ a ] , Z´ -> L Unifica a cabeça da regra com o subgoal sufix ( [ a ] , L ) Tem-se então: sufix ( [ a ] , L ) if anexa ( _1 , [ a ] , L ) nome Prolog Substituindo pela condição tem-se: anexa ( _1 , [ a ] , L ) , prefix ( L , [ a , b , c ] ) O fato anexa ( [ ] , Y´´ , Y´´ ) aplica ao novo subgoal mais á esquerda: [ ] -> _1 , Y´´ -> [ a ] , Y´´ -> L Uma vez que o fato consiste de uma cabeça e nenhuma condição, o novo goal corrente torna-se: prefix ( [ a ] , [ a , b , c ] ) substituída -> L Tem-se então: prefix ( [ a ] , [ a , b , c ] ) if anexa ( [ a ] , _2 , [ a , b , c ] ) . anexa ( [ a ] , _2 , [ a , b , c ] ) if anexa ( [ ] , _2 , [ b , c ] ). anexa ( [ ] , _2 , [ b , c ] ) 2 -> [ b , c ] anexa ( [ ] , [ b , c ] , [ b , c ] ) . L = [ a ] ;
Modos de Chamada Usando o exemplo do pertence([arg1], [arg2]) mostrado anteriormente, o que aconteceria se as interrogações fossem : ?- pertence(a,X) % ou ?- pertence(X,Y) Pode – se observar que cada uma delas tem “infinitas” respostas, uma vez que existem infinitas listas que validam estas interrogações para o programa pertence. Portanto, é necessário definir se os argumentos devem estar ou não instanciados. Usaremos a seguinte notação para documentar as formas corretas de interrogação : modo(<arg – 1>, <arg – 2>, .....,<arg – n>) onde : <arg – i > = + se <arg – i > deve estar instanciado. <arg – i > = - se <arg – i > deve ser variável livre. <arg – i > = ? se <arg – i > puder ser qualquer um dos casos. Exemplo : o exemplo pertence ([arg1], [arg2]) tem o modo(? , +).
Outros Exemplos de Lista • Verificação se um elemento de uma lista não pertence a outra lista, modo(+, +) : nao_pertence(X,Y):- \+ (pertence(Z,X),pertence(Z,Y)). Questões: ?- nao_pertence([a,b,c],[d,e,f]). • yes ?-nao_pertence([a,b],[b,c]). no • Soma dos elementos de uma lista, modo(+, ?) : • soma([ ], 0). • soma( [Elem | Cauda], S):- soma(Cauda, S1), • S is S1 + Elem. Questão: • ?- soma([1, 2, 3, 4, 5, 6], S). • S = 21 • Verificação se uma lista está ordenada, modo(+) : ordenada([ ]). • ordenada([X,Y | Resto]):-X=<Y,ordenada([Y | Resto]). Questões: • ?-ordenada([1,5,6,6,9,12]). • yes ?-ordenada([3,2,1]). no
Verificação se um termo é uma lista, modo(+) : eh_lista([ ]). • eh_lista(X):- not var(X), • not atom(X). Onde os predicados: • var(X) - Verdadeiro se o termo X é uma variável atom(X) - Verdadeiro se o termo X é um átomo Questões: ?- eh_lista([a, b, c, d, e, f]). • yes • ?- eh_lista([ ]). • yes ?- eh_lista( 2 ) no • Duplicação dos elementos de uma lista, modo(+,?) : duplica([ ], [ ]). • duplica( [X | L], [X, X | L1]):- duplica(L, L1). Questão: ?- duplica([1, 2, 3], L). • L = [1, 1, 2, 2, 3, 3]
Concatenação de duas listas, modo(+,?,?) : concatena([ ], Lista, Lista). • concatena([ Elem | Lista1], Lista2, [Elem | Lista3]):- • concatena(Lista1, Lista2, Lista3). • Questão: ?- concatena([um, dois], [tres, quatro], L). • L = [um, dois, tres, quatro] • Também pode – se usar o modo modo(-,-,+) para decompor uma lista em duas sublistas. • Maior elemento de uma lista numérica, modo(+,?) : • max([X], X). % Maior elemento de uma lista com 1 • % elemento é o próprio elemento • max([X, Y | Cauda], Max):- X >= Y, ! , • max([X | Cauda], Max). • max([X, Y | Cauda], Max):- max([Y | Cauda], Max). Questão: • ?- max([1, 10, 100, -1, 20], M). • M = 100 ; • no • N_ésimo elemento de uma lista, modo(?,?,+) : • n_esimo(1, Elem, [Elem | _]). • n_esimo(N, Elem, [ _ | Cauda]):- n_esimo(M, Elem, Cauda), • N is M + 1. Questão: • ?- n_esimo(4, Elem, [a, b, c, d, e]). • Elem = d ; • no
Inversão de uma lista, modo(+,?) : • inverter([ ],[ ]). • inverter([Elem | Lista1], Lista3):-inverter(Lista1,Lista2), • concatena(Lista2,[Elem], Lista3). Questões: • ?- inverter([a,b,c], L). ?-inverter([1,2,3,4,5,6],L). • L = [c,b,a] L = [6,5,4,3,2,1] • Número de elementos em uma lista, modo(+,?) : • tamanho([ ],0). • tamanho([_ | R], N):-tamanho(R, N1),N is N1+1. Questão: • ?- tamanho([a,b,c,d,e], N). • N = 5 • Seleção de determinados elementos de uma lista, em uma lista separada, identificados pela sua posição, modo(+,+,?) : seleciona([ ], _, [ ]). • seleciona([M | N], L, [X | Y]):-n_esimo(M, X, L), seleciona(N, L, Y). Questão: • ?- seleciona([2, 4], [a, b, c, d, e], L). • L = [b, d] ; • no
Ultimo elemento de uma lista, modo(+,?) : ultimo([Elem], Elem). • ultimo([_|Cauda], Elem) :- ultimo(Cauda, Elem). Questões: • ?- ultimo([casa, bola, carro], X) • X = carro ; • no • Elementos consecutivos em uma lista, modo(?,?,+) : • consecutivos(E1,E2, [E1, E2|_]). • consecutivos(E1, E2, [_|Cauda]) :- • consecutivos(E1, E2, Cauda). Questões: • ?- consecutivos(a, b, [d, e, f, g, a, b]). • yes • ?- consecutivos(X, Y, [d, e, f, g]). • X = d • Y = e ; • X = e • Y = f ; • X = f • Y = g ; • no
Inserção de um elemento na 1ª posição de uma lista, modo(?,?,?) : • inserir(Elem, Lista, [Elem | Lista]). Questão: • ?- inserir(a, [b, c, d], L). • L = [a, b, c, d] • Exclusão de um elemento de uma lista, modo(?,+,?) ou modo(+,?,+) : • del(X,[X | Corpo],Corpo). • del(X,[Y | Corpo], [Y | Corpo1]):-del(X,Corpo,Corpo1). Questão: • ?- del(b,[a,b,c], L). • L = [a,c] ; • no • Permutação de uma lista, modo(+,?) : • permuta([ ],[ ]). • permuta(L,[X | P]):-del(X,L,L1),permuta(L1,P). Questão: • ?- permuta([vermelho,azul,verde], L). • L = [vermelho,azul,verde] ; • L = [vermelho,verde,azul] ; • L = [azul,vermelho,verde] ; • L = [azul,verde,vermelho] ; • L = [verde,vermelho,azul] ; • L = [verde,azul,vermelho] ; • no