Académique Documents
Professionnel Documents
Culture Documents
PROLOG
Luiz A. M. Palazzo
EDUCAT
Editora da Universidade Catlica de Pelotas
Pelotas, 1997
1997 LUIZ A. M. PALAZZO
SUMRIO
1. LGICA E PROGRAMAO DE COMPUTADORES
1.1 AS RAZES
1.2 PROGRAMAO EM LGICA
1.3 APLICAES
1.4 A QUINTA GERAO
1.5 PORQUE ESTUDAR PROLOG
RESUMO
2. A LINGUAGEM PROLOG
2.1 FATOS
2.2 REGRAS
2.3 CONSTRUES RECURSIVAS
2.4 CONSULTAS
2.5 O SIGNIFICADO DOS PROGRAMAS PROLOG
RESUMO
EXERCCIOS
3. SINTAXE E SEMNTICA
3.1 OBJETOS
3.2 UNIFICAO
3.3 SEMNTICA DECLARATIVA E SEMNTICA PROCEDIMENTAL
3.4 SEMNTICA OPERACIONAL
RESUMO
EXERCCIOS
4. OPERADORES E ARITMTICA
4.1 OPERADORES
4.2 ARITMTICA
RESUMO
EXERCCIOS
5. PROCESSAMENTO DE LISTAS
5.1 REPRESENTAO DE LISTAS
5.2 OPERAES SOBRE LISTAS
5.3 OUTROS EXEMPLOS
RESUMO
EXERCCIOS
6. CONTROLE
6.1 BACKTRACKING
6.2 O OPERADOR "CUT"
6.3 APLICAES DO CUT
6.4 NEGAO POR FALHA
6.5 CUIDADOS COM O CUT E A NEGAO
RESUMO
EXERCCIOS
7. ESTRUTURAS DE DADOS
7.1 RECUPERAO DE INFORMAES
7.2 ABSTRAO DE DADOS
7.3 UM AUTMATO FINITO NO-DETERMINSTICO
7.4 PLANEJAMENTO DE ROTEIROS AREOS
RESUMO
EXERCCIOS
8. ENTRADA E SADA
8.1 ARQUIVOS DE DADOS
8.2 PROCESSAMENTO DE ARQUIVOS DE TERMOS
8.3 PROCESSAMENTO DE CARACTERES
8.4 CONVERSO DE TERMOS
8.5 LEITURA DE PROGRAMAS
RESUMO
EXERCCIOS
9. PREDICADOS EXTRALGICOS
9.1 TIPOS DE TERMOS
9.2 CONSTRUO E DECOMPOSIO DE TERMOS
9.3 EQUIVALNCIAS E DESIGUALDADES
9.4 PROGRAMAS OU BASES DE DADOS?
9.5 RECURSOS PARA O CONTROLE DE PROGRAMAS
1
1
2
4
6
8
9
11
11
14
17
19
21
22
22
24
24
27
28
30
30
31
33
33
36
38
39
41
41
42
48
49
50
51
51
52
56
57
58
60
60
62
62
64
65
67
69
69
71
71
73
77
78
79
80
80
82
82
84
85
86
89
89
91
91
93
93
95
96
97
99
99
103
103
105
105
105
105
106
107
109
109
110
111
111
112
114
116
117
122
123
124
125
125
127
129
130
133
133
138
139
140
140
143
146
150
151
151
153
153
158
160
161
162
APNDICE A
A.2 SEMNTICA MODELO-TEORTICA
A.3 SEMNTICA PROVA-TEORTICA
BIBLIOGRAFIA
162
165
167
170
178
178
179
182
189
191
Tanto pelo privilgio da amizade de vrios anos, como pela condio de colega profissional
do prof. Luiz Antonio Palazzo, j h muito acompanho sua contribuio cultura em Cincia
da Computao na regio em que trabalhamos (zona sul do Rio Grande do Sul). Com graduao e ps-graduao pela UFRGS, vem marcando sua atuao desde a poca de estudante,
tanto no meio acadmico como na comunidade em geral, por uma postura de vanguarda na
busca de tecnologias para um uso racional e eficiente da computao. Sem dvida, este livro
permitir que um nmero maior de pessoas se beneficiem de sua larga experincia no ofcio
de ensinar.
A estrutura do livro mescla o contexto histrico da Inteligncia Artificial (IA) com o estudo
do Prolog, uma das mais difundidas linguagens para Programao em Lgica. O contedo,
por sua vez, tem como ponto alto contemplar uma rigorosa conceituao formal, cujo emprego caracterizado por exemplos claros e significativos.
O emprego das linguagens para Programao em Lgica ganhou significativo impulso com o
projeto Japons de Sistemas Computacionais de Quinta Gerao (1982-1992), o qual investigou alternativas de hardware e software para atender o desenvolvimento de aplicaes que
contemplavam metas ambiciosas, tais como reconhecimento de imagens, processamento da
linguagem natural, processamento de conhecimento, etc.
As linguagens para Programao em Lgica, a exemplo do Prolog, outrora empregadas
principalmente na prototipao, j podem ser utilizadas para resolver, com bom desempenho,
complexos problemas reais de IA. Isto se tornou possvel pela disponibilidade de processadores poderosos a custos reduzidos, bem como pela disseminao do uso de arquiteturas paralelas.
Neste trabalho so ressaltadas, com muita propriedade, as vantagens do emprego da lgica
clausal para programao de computadores, resgatando a "elegncia" das linguagens para
Programao em Lgica, nas quais o programador tem como principal preocupao a especificao em Prolog do problema a ser resolvido, ficando a cargo do sistema computacional
a gerncia dos mecanismos de busca das possveis solues.
Esta obra moderna, das poucas em portugus no seu estilo, vem preencher uma lacuna editorial, trazendo a estudantes e profissionais da cincia da computao uma abordagem ampla,
porm no menos crtica e objetiva, das perspectivas do uso da Programao em Lgica.
Adenauer Corra Yamin
Pelotas, RS
1.1 AS RAZES
O uso da lgica na representao dos processos de raciocnio remonta aos estudos de Boole (18151864) e de De Morgan (1806-1871), sobre o que veio a ser mais tarde chamado "lgebra de Boole".
Como o prprio nome indica, esses trabalhos estavam mais prximos de outras teorias matemticas do
que propriamente da lgica. Deve-se ao matemtico alemo Gttlob Frege no seu "Begriffsschrift"
(1879) a primeira verso do que hoje denominamos clculo de predicados, proposto por ele como uma
ferramenta para formalizar princpios lgicos. Esse sistema oferecia uma notao rica e consistente
que Frege pretendia adequada para a representao de todos os conceitos matemticos e para a formalizao exata do raciocnio dedutivo sobre tais conceitos, o que, afinal, acabou acontecendo.
No final do sculo passado a matemtica havia atingido um estgio de desenvolvimento mais do que
propcio explorao do novo instrumento proposto por Frege. Os matemticos estavam abertos a
novas reas de pesquisa que demandavam profundo entendimento lgico assim como procedimentos
sistemticos de prova de teoremas mais poderosos e eficientes do que os at ento empregados. Alguns dos trabalhos mais significativos deste perodo foram a reconstruo axiomtica da geometria
abstrata por David Hilbert, a aritimtica proposta por Giuseppe Peano e a explorao intuitiva da teoria geral dos conjuntos, por Georg Cantor, que tambm produziu a iluminada teoria dos nmeros
transfinitos. O relacionamento entre lgica e matemtica foi profundamente investigado por Alfred
North Whitehead e Bertrand Russel, que em "Principia Mathematica" (1910) demonstraram ser a lgica um instrumento adequado para a representao formal de grande parte da matemtica.
Um passo muito importante foi dado em 1930, em estudos simultneos, porm independentes, realizados pelo alemo Kurt Gdel e o francs Jacques Herbrand. Ambos, em suas dissertaes de doutorado, demonstraram que o mecanismo de prova do clculo de predicados poderia oferecer uma prova
formal de toda proposio logicamente verdadeira. O resultado de maior impacto foi entretanto produzido por Gdel, em 1931, com a descoberta do "teorema da incompleteza dos sistemas de formalizao da aritmtica". A prova deste teorema se baseava nos denominados paradoxos de autoreferncia (declaraes do tipo: "Esta sentena falsa", que no podem ser provadas nem verdadeiras
1 Na realidade, de uma certa classe de pensamento correto.
1
nem falsas). Em 1934, Alfred Tarski produziu a primeira teoria semntica rigorosamente formal do
clculo de predicados, introduzindo conceitos precisos para "satisfatibilidade", "verdade" (em uma
dada interpretao), "conseqncia lgica" e outras noes relacionadas. Ainda na dcada de 30, diversos outros estudos - entre os quais os de Alan Turing, Alonzo Church e outros - aproximaram
muito o clculo de predicados da forma com que hoje conhecido e estudado.
No incio da Segunda Guerra Mundial, em 1939, toda a fundamentao terica bsica da lgica computacional estava pronta. Faltava apenas um meio prtico para realizar o imenso volume de computaes necessrias aos procedimentos de prova. Apenas exemplos muito simples podiam ser resolvidos
manualmente. O estado de guerra deslocou a maior parte dos recursos destinados pesquisa terica,
nos EUA, Europa e Japo para as tcnicas de assassinato em massa. Foi somente a partir da metade
dos anos 50 que o desenvolvimento da ento novssima tecnologia dos computadores conseguiu oferecer aos pesquisadores o potencial computacional necessrio para a realizao de experincias mais
significativas com o clculo de predicados.
Em 1958, uma forma simplificada do clculo de predicados denominada forma clausal comeou a
despertar o interesse dos estudiosos do assunto. Tal forma empregava um tipo particular muito simples de sentena lgica denominada clusula. Uma clusula uma (possivelmente vazia) disjuno de
literais. Tambm por essa poca, Dag Prawitz (1960) props um novo tipo de operao sobre os objetos do clculo de predicados, que mais tarde veio a ser conhecida por unificao. A unificao se
revelou fundamental para o desenvolvimento de sistemas simblicos e de programao em lgica.
A programao em lgica em sistemas computacionais, entretanto, somente se tornou realmente possvel a partir da pesquisa sobre prova automtica de teoremas, particularmente no desenvolvimento do
Princpio da Resoluo por J. A. Robinson (1965). Um dos primeiros trabalhos relacionando o Princpio da Resoluo com a programao de computadores deve-se a Cordell C. Green (1969) que mostrou como o mecanismo para a extrao de respostas em sistemas de resoluo poderia ser empregado
para sintetizar programas convencionais.
A expresso "programao em lgica" (logic programming, originalmente em ingls) devido a Robert Kowalski (1974) e designa o uso da lgica como linguagem de programao de computadores.
Kowalski identificou, em um particular procedimento de prova de teoremas, um procedimento computacional, permitindo uma interpretao procedimental da lgica e estabelecendo as condies que
nos permitem entend-la como uma linguagem de programao de uso geral. Este foi um avano essencial, necessrio para adaptar os conceitos relacionados com a prova de teoremas s tcnicas computacionais j dominadas pelos programadores. Aperfeioamentos realizados nas tcnicas de implementao tambm foram de grande importncia para o emprego da lgica como linguagem de programao. O primeiro interpretador experimental foi desenvolvido por um grupo de pesquisadores
liderados por Alain Colmerauer na Universidade de Aix-Marseille (1972) com o nome de Prolog, um
acrnimo para "Programmation en Logique". Seguindo-se a este primeiro passo, implementaes mais
"praticas" foram desenvolvidas por Battani e Meloni (1973), Bruynooghe (1976) e, principalmente,
David H. D. Warren, Lus Moniz Pereira e outros pesquisadores da Universidade de Edimburgo
(U.K.) que, em 1977, formalmente definiram o sistema hoje denominado "Prolog de Edimburgo",
usado como referncia para a maioria das atuais implementaes da linguagem Prolog. Deve-se tambm a Warren a especificao da WAM (Warren Abstract Machine), um modelo formal empregado
at hoje na pesquisa de arquiteturas computacionais orientadas programao em lgica.
da execuo para ser exercido pelo sistema de programao em lgica utilizado. Em outras palavras, a
tarefa do programador passa a ser simplesmente a especificao do problema que deve ser solucionado, razo pela qual as linguagens lgicas podem ser vistas simultaneamente como linguagens para
especificao formal e linguagens para a programao de computadores.
Um programa em lgica ento a representao de determinado problema ou situao expressa atravs de um conjunto finito de um tipo especial de sentenas lgicas denominadas clusulas. Ao contrrio de programas em Pascal ou C, um programa em lgica no a descrio de um procedimento para
se obter a soluo de um problema. Na realidade o sistema utilizado no processamento de programas
em lgica inteiramente responsvel pelo procedimento a ser adotado na sua execuo. Um programa
em lgica pode tambm ser visto alternativamente como uma base de dados, exceto que as bases de
dados convencionais descrevem apenas fatos tais como "Oscar um avestruz", enquanto que as sentenas de um programa em lgica possuem um alcance mais genrico, permitindo a representao de
regras como em "Todo avestruz um pssaro", o que no possui correspondncia em bases de dados
convencionais. Na figura abaixo se procura explicitar as principais diferenas entre programao convencional e programao em lgica.
PROGRAMAS CONVENCIONAIS
Processamento Numrico
Solues Algortmicas
Estruturas de Controle e Conhecimento Integradas
Difcil Modificao
Somente Respostas Totalmente Corretas
Somente a Melhor Soluo Possvel
PROGRAMAS EM LGICA
Processamento Simblico
Solues Heursticas
Estruturas de Controle e Conhecimento Separadas
Fcil Modificao
Incluem Respostas Parcialmente Corretas
Incluem Todas as Solues Possveis
dimento podem alternativamente, em diferentes chamadas representar ora parmetros de entrada, ora de sada. Os procedimentos podem assim ser projetados para atender a mltiplos propsitos. A execuo pode ocorrer em qualquer sentido, dependendo do contexto. Por exemplo, o
mesmo procedimento para inserir um elemento no topo de uma pilha qualquer pode ser usado,
em sentido contrrio, para remover o elemento que se encontrar no topo desta pilha.
Trplice Interpretao dos Programas em Lgica: Um programa em lgica pode ser seman-
ticamente interpretado de trs modos distintos: (1) por meio da semntica declarativa, inerente
lgica, (2) por meio da semntica procedimental, onde as clusulas dos programas so vistas
como entrada para um mtodo de prova e, (3) por meio da semntica operacional, onde as clusulas so vistas como comandos para um procedimento particular de prova por refutao. Essas
trs interpretaes so intercambiveis segundo a particular abordagem que se mostrar mais
vantajosa ao problema que se tenta solucionar.
1.3 APLICAES
Um dos primeiros usos da programao em lgica foi a representao e anlise de subconjuntos da
linguagem natural. Esta foi inclusive a aplicao que motivou Alain Colmerauer a desenvolver a pri4
meira implementao da linguagem Prolog. Logo em seguida, outros pesquisadores da rea da inteligncia artificial propuseram diversas novas aplicaes para o novo instrumento. Alguns dos primeiros
trabalhos com Prolog envolviam a formulao de planos e a escrita de compiladores, por Pereira e
Warren (1977), prova de teoremas em geometria por R. Welhan (1976) e a soluo de problemas de
mecnica, por Bundy et al. (1979). As aplicaes relatadas desde ento, multiplicaram-se velozmente.
Concentraremos aqui a ateno em um conjunto das principais reas investigadas com o concurso da
programao em lgica.
Sistemas Baseados em Conhecimento (SBCs): Ou knowledge-based systems, so sistemas
que aplicam mecanismos automatizados de raciocnio para a representao e inferncia de conhecimento. Tais sistemas costumam ser identificados como simplesmente "de inteligncia artificial aplicada" e representam uma abrangente classe de aplicaes da qual todas as demais seriam aproximadamente subclasses. A tecnologia dos SBCs foi identificada na Inglaterra pelo
Relatrio Alvey (1982) como uma das quatro tecnologias necessrias completa explorao
dos computadores de quinta gerao. As outras seriam: interface homem-mquina (MMI), integrao de circuitos em ultra-grande escala (ULSI) e engenharia de software (SE). O relacionamento entre SBCs e a nova gerao de computadores , na verdade, altamente simbitica, cada
uma dessas reas necessria para a realizao do completo potencial da outra.
Sistemas de Bases de Dados (BDs): Uma particularmente bem definida aplicao dos SBCs
3 Assim denominadas em homenagem a Alfred Horn, que primeiro lhes estudou as propriedades, em 1951.
5
tes sobre a estrutura de sentenas em linguagem natural fossem formuladas como objetivos ao
sistema, e (3) que diferentes procedimentos de prova aplicados a representaes lgicas da linguagem natural correspondiam a diferentes estratgias de anlise.
Educao: A programao em lgica poder vir a oferecer no futuro uma contribuio bastante
significativa ao uso educacional de computadores. Esta proposta foi testada em 1978 quando
Kowalski introduziu a programao em lgica na Park House Middle School em Wimbledon,
na Inglaterra, usando acesso on-line aos computadores do Imperial College. O sucesso do empreendimento conduziu a um projeto mais abrangente denominado "Lgica como Linguagem
de Programao para Crianas", inaugurado em 1980 na Inglaterra com recursos do Conselho
de Pesquisa Cientfica daquele pas. Os resultados obtidos desde ento tem mostrado que a programao em lgica no somente assimilada mais facilmente do que as linguagens convencionais, como tambm pode ser introduzida at mesmo a crianas na faixa dos 10 a 12 anos, as
quais ainda se beneficiam do desenvolvimento do pensamento lgico-formal que o uso de linguagens como o Prolog induz.
Arquiteturas No-Convencionais: Esta rea vem se tornando cada vez mais um campo extre-
mamente frtil para o uso da programao em lgica especialmente na especificao e implementao de mquinas abstratas de processamento paralelo. O paralelismo pode ser modelado
pela programao em lgica em variados graus de atividade se implementado em conjunto com
o mecanismo de unificao. Duas implementaes iniciais nesse sentido foram o Parlog, desenvolvido em 1984 por Clark e Gregory, e o Concurrent Prolog (CP), por Shapiro em 1983. O
projeto da Quinta Gerao, introduzido na prxima seo, foi fortemente orientado ao uso da
programao em lgica em sistemas de processamento paralelo.
Muitas outras aplicaes poderiam ainda ser citadas, principalmente na rea da inteligncia artificial,
que tem no Prolog e no Lisp as suas duas linguagens mais importantes. Novas tecnologias de hardware e software tais como sistemas massivamente paralelos, redes de computadores, assistentes inteligentes, bases de dados semnticas, etc., tornam o uso do Prolog (e de outras linguagens baseadas em
lgica) cada vez mais atraentes
declarar que os objetivos do projeto foram plenamente atingidos. Na Figura 1.2 mostrada uma
adaptao em portugus do diagrama "de intenes" apresentado por Fuchi, no Fifth Generation
Computer Systems Congress de 1981 (FGCS'81), o congresso que deu a conhecer ao mundo um dos
mais ambiciosos projetos da histria da computao.
ANO 1
ANO 5
Network
ANO 10
(Reduo a chips)
(comparveis s mquinas
de grande porte de 1981)
Mquina Prolog +
(Novo Software)
LISP
APL
Smalltalk
PS, etc.
Programao:
em lgica e
funcional
Nova Linguagem
Simbolismo em Alto
Nvel:
Database Machine
Planejamento
Programao
Prova de Teoremas
Jogos
Segundo o relatrio de Shapiro e Warren, um dos primeiros passos do projeto consistiu em definir
uma linguagem de programao em lgica que ao mesmo tempo fosse adequada ao paralelismo do
hardware e aos requisitos sofisticados especificados para o software. Baseada no Parlog e no Cuncurrent Prolog, uma equipe de pesquisadores liderada por Kazunori Ueda desenvolveu a linguagem GHC
(Guarded Horn Clauses), que deu origem KL0 (Kernel Language Zero). Um refinamento dessa verso beta4, realizado pela equipe de Takashi Chikayama produziu, em 1987, a linguagem KL1. Todos
os sub-projetos do FGCS foram revistos para trabalhar com essa linguagem. Em 1988 os primeiros
prottipos do computador de quinta gerao foram construdos, recebendo o nome genrico de Parallel Inference Machines (PIMs). Tais computadores possuiam arquitetura massivamente paralela e
tinham velocidade de processamento calculada em MLIPS (milhes de inferncias lgicas por segundo). Uma dessas mquinas, denominada Multi-PSI foi apresentada com grande sucesso no FGCS'88.
4 Uma verso distribuida a grupos selecionados de usurios para teste e depurao.
7
A linguagem KL1 foi empregada para escrever o sistema operacional PIMOS (Parallel Inference Machine Operating System), em 1988. importante ressaltar aqui que a linguagem KL1 uma linguagem de muito alto nvel5 e, ao mesmo tempo, uma linguagem de mquina, isto , adequada programao a nvel de registradores, posies de memria e portas lgicas. As verses mais recentes do
PIMOS provam definitivamente que KL1 (agora j KL2) uma linguagem muito mais adequada do
que as linguagens convencionais para a construo de software bsico em mquinas paralelas. Outras
linguagens de programao foram - e ainda vem sendo - pesquisadas. Por exemplo, uma linguagem de
programao em lgica com restries denominada GDCC foi projetada em um nvel ainda mais alto
que a KL1. Uma outra linguagem, denominada "Quixote" foi produzida para lidar com bases de dados
dedutivas e orientadas a objetos. Para o gerenciamento de sistemas paralelos distribudos foi especificada a linguagem Kappa-P. Todas essas linguagens, com as quais - ou com seus dialetos - todos certamente estaremos em contato num futuro prximo, esto baseadas nos conceitos e resultados da pesquisa em programao em lgica.
Tecnicamente considera-se que o projeto atingiu a primeira parte de seus objetivos: diversos computadores paralelos foram construdos. Tais computadores so denominados coletivamente de mquinas
de inferncia paralela (PIMs), incorporam a linguagem KL1 e o sistema operacional PIMOS. Alm
disso as mquinas PIM mais recentemente construdas lograram atingir um pico de desempenho da
ordem de 1 gigalips (1 bilho de inferncias lgicas por segundo), o que era um dos objetvos concretos do projeto considerados mais difceis de atingir.
A segunda parte do projeto, entretanto, a construo de mquinas orientadas bases de dados (database machines) foi menos claramente abordada. Tal objetivo foi reformulado a partir do sucesso obtido com a construo de linguagens de programao em lgica concorrente para a construo de implementaes baseadas em KL1 na mesma plataforma de hardware das mquinas PIM.
De um modo geral, entretanto, considera-se que o projeto demonstrou ser a tecnologia PIM bem sucedida em novas aplicaes envolvendo paralelismo em diversas reas, especialmente computao nonumrica e inteligncia artificial. Em suma, segundo o relatrio Shapiro-Warren:
"(...) uma ponte foi construda entre a computao paralela e as aplicaes envolvendo inteligncia artificial. Entretanto, as duas extremidades finais da ponte ainda se encontram por
concluir e a ponte em si mais frgil do que poderia ter sido. sem dvida ainda muito cedo
para se esperar que a ponte seja inaugurada recebendo uma grande aclamao."
6 As atuais tecnologias de integrao de circuitos (VLSI/ULSI) tendem a atingir os limites fsicos alm dos quais se tornam
economicamente inviveis.
8
RESUMO
A programao em lgica, tal como a conhecemos hoje, tem suas razes no clculo de predica-
dos, proposto por Frege em 1879. Diversos estudos posteriores foram de grande importncia
para sua evoluo, com destaque para as investigaes de Herbrand, Gdel, Tarski, Prawitz,
Robinson e Green;
A primeira implementao da linguagem Prolog foi realizada por Alain Colmerauer e sua equi7 Ao nvel da linguagem coloquial falada ou escrita, por exemplo.
9
onais so as seguintes:
(1) Processamento simblico,
(2) Solues heursticas,
(3) Estruturas de controle e conhecimento separadas,
(4) Fcil modificao,
(5) Incluem respostas parcialmente corretas, e
(6) Incluem todas as solues possveis;
ciou em 1982 e foi oficialmente concludo em maio de 1992. Apesar de ficarem aqum do esperado, os resultados produzidos permitem claramente antever o papel preponderante que a programao em lgica dever representar nos futuros sistemas computacionais;
10
2. A LINGUAGEM PROLOG
A principal utilizao da linguagem Prolog reside no domnio da programao simblica, nonumrica, sendo especialmente adequada soluo de problemas, envolvendo objetos e relaes entre
objetos. O advento da linguagem Prolog reforou a tese de que a lgica um formalismo conveniente
para representar e processar conhecimento. Seu uso evita que o programador descreva os procedimentos necessrios para a soluo de um problema, permitindo que ele expresse declarativamente
apenas a sua estrutura lgica, atravs de fatos, regras e consultas. Algumas das principais caractersticas da linguagem Prolog so:
uma linguagem orientada ao processamento simblico;
Representa uma implementao da lgica como linguagem de programao;
Apresenta uma semntica declarativa inerente lgica;
Permite a definio de programas reversveis, isto , programas que no distinguem entre os
2.1 FATOS
Considere a rvore genealgica mostrada na Figura 2.1. possvel definir, entre os objetos (indivduos) mostrados, uma relao denominada progenitor que associa um indivduo a um dos seus progenitores. Por exemplo, o fato de que Joo um dos progenitores de Jos pode ser denotado por:
progenitor(joo, jos).
onde progenitor o nome da relao e joo e jos so os seus argumentos. Por razes que se tornaro
claras mais tarde, escreve-se aqui nomes de pessoas (como Joo) iniciando com letra minscula. A
relao progenitor completa, como representada na figura acima pode ser definida pelo seguinte programa Prolog:
progenitor(maria, jos).
progenitor(joo, jos).
progenitor(joo, ana).
progenitor(jos, jlia).
progenitor(jos, ris).
progenitor(ris, jorge).
O programa acima compe-se de seis clusulas, cada uma das quais denota um fato acerca da relao
progenitor. Se o programa for submetido a um sistema Prolog, este ser capaz de responder algumas
questes sobre a relao ali representada. Por exemplo: "Jos o progenitor de ris?". Uma consulta
como essa deve ser formulada ao sistema precedida por um "?-". Esta combinao de sinais denota
que se est formulando uma pergunta. Como h um fato no programa declarando explicitamente que
11
Maria
Joo
Jos
Jlia
Ana
ris
Jorge
Uma outra questo poderia ser: "Ana um dos progenitores de Jorge?". Nesse caso o sistema responde "no", porque no h nenhuma clusula no programa que permita deduzir tal fato.
?-progenitor(ana, jorge).
no
A questo "Lus progenitor de Maria?" tambm obteria a resposta "no", porque o programa nem
sequer conhece algum com o nome Lus.
?-progenitor(lus, maria).
no
Perguntas mais interessantes podem tambm ser formuladas, por exemplo: "Quem progenitor de
ris?". Para fazer isso introduz-se uma varivel, por exemplo "X" na posio do argumento correspondente ao progenitor de ris. Desta feita o sistema no se limitar a responder "sim" ou "no", mas
ir procurar (e informar caso for encontrado) um valor de X que torne a assertiva "X progenitor de
ris" verdadeira.
?-progenitor(X, ris).
X=jos
Da mesma forma a questo "Quem so os filhos de Jos?" pode ser formulada com a introduo de
uma varivel na posio do argumento correspondente ao filhos de Jos. Note que, neste caso, mais de
uma resposta verdadeira pode ser encontrada. O sistema ir fornecer a primeira que encontrar e
aguardar manifestao por parte do usurio. Se este desejar outras solues deve digitar um ponto-evrgula (;), do contrrio digita um ponto (.), o que informa ao sistema que a soluo fornecida suficiente.
?-progenitor(jos, X).
X=jlia;
X=ris;
no
Aqui a ltima resposta obtida foi "no" significando que todas as solues vlidas j foram fornecidas. Uma questo mais geral para o programa seria: "Quem progenitor de quem?" ou, com outra
formulao: "Encontre X e Y tal que X progenitor de Y". O sistema, em resposta, ir fornecer (enquanto se desejar, digitando ";") todos os pares progenitor-filho at que estes se esgotem (quando
ento responde "no") ou at que se resolva encerrar a apresentao de novas solues (digitando ".").
No exemplo a seguir iremos nos satisfazer com as trs primeiras solues encontradas.
?-progenitor(X, Y).
12
X=maria Y=jos;
X=joo Y=jos;
X=joo Y=ana.
Pode-se formular questes ainda mais complicadas ao programa, como "Quem so os avs de Jorge?". Como nosso programa no possui diretamente a relao av, esta consulta precisa ser dividida
em duas etapas, como pode ser visto na Figura 2.2. A saber:
(1) Quem progenitor de Jorge? (Por exemplo, Y) e
(2) Quem progenitor de Y? (Por exemplo, X)
Esta consulta em Prolog escrita como uma seqncia de duas consultas simples, cuja leitura pode
ser: "Encontre X e Y tais que X progenitor de Y e Y progenitor de Jorge".
?-progenitor(X, Y), progenitor(Y, jorge).
X=jos Y=ris
X
progenitor
Y
av
progenitor
Jorge
Observe que se mudarmos a ordem das consultas na composio, o significado lgico permanece o
mesmo, apesar do resultado ser informado na ordem inversa:
?-progenitor(Y, jorge), progenitor(X, Y).
Y=ris X=jos
Ainda uma outra pergunta poderia ser: "Jos e Ana possuem algum progenitor em comum?". Novamente necessrio decompor a questo em duas etapas, formulando-a alternativamente como: "Encontre um X tal que X seja simultaneamente progenitor de Jos e Ana".
?-progenitor(X, jos), progenitor(X, ana).
X=joo
Por meio dos exemplos apresentados at aqui acredita-se ter sido possvel ilustrar os seguintes pontos:
Uma relao como progenitor pode ser facilmente definida em Prolog estabelecendo-se as tu-
O usurio pode facilmente consultar o sistema Prolog sobre as relaes definidas em seu pro-
grama;
Um programa Prolog constitudo de clusulas, cada uma das quais encerrada por um ponto
(.);
Os argumentos das relaes podem ser objetos concretos (como jlia e ris) ou objetos genri-
cos (como X e Y). Objetos concretos em um programa so denominados tomos, enquanto que
os objetos genricos so denominados variveis;
13
Consultas ao sistema so constitudas por um ou mais objetivos, cuja seqncia denota a sua
conjuno;
Uma resposta a uma consulta pode ser positiva ou negativa, dependendo se o objetivo corres-
pondente foi alcanado ou no. No primeiro caso dizemos que a consulta foi bem-sucedida e,
no segundo, que a consulta falhou;
Se vrias respostas satisfizerem a uma consulta, ento o sistema Prolog ir fornecer tantas
2.2 REGRAS
O programa da rvore genealgica pode ser facilmente ampliado de muitas maneiras interessantes.
Inicialmente vamos adicionar informao sobre o sexo das pessoas ali representadas. Isso pode ser
feito simplesmente acrescentando os seguintes fatos ao programa:
masculino(joo).
masculino(jos).
masculino(jorge).
feminino(maria).
feminino(jlia).
feminino(ana).
feminino(ris).
A prxima extenso ao programa ser a introduo da relao filho como o inverso da relao progenitor. Pode-se definir a relao filho de modo semelhante utilizada para definir a relao progenitor,
isto fornecendo uma lista de fatos, cada um dos quais fazendo referncia a um par de pessoas tal que
uma seja filho da outra. Por exemplo:
filho(jos, joo).
Entretanto podemos definir a relao "filho" de uma maneira muito mais elegante, fazendo o uso do
fato de que ela o inverso da relao progenitor e esta j est definida. Tal alternativa pode ser baseada na seguinte declarao lgica:
Para todo X e Y
Y filho de X se
X progenitor de Y.
Essa formulao j se encontra bastante prxima do formalismo adotado em Prolog. A clusula correspondente, com a mesma leitura acima, :
filho(Y, X) :- progenitor(X, Y).
que tambm pode ser lida como: "Para todo X e Y, se X progenitor de Y, ento Y filho de X".
Clusulas Prolog desse tipo so denominadas regras. H uma diferena importante entre regras e fatos. Um fato sempre verdadeiro, enquanto regras especificam algo que "pode ser verdadeiro se algumas condies forem satisfeitas". As regras tem:
Uma parte de concluso (o lado esquerdo da clusula), e
14
O smbolo ":-" significa "se" e separa a clusula em concluso, ou cabea da clusula, e condio ou
corpo da clusula, como mostrado no esquema abaixo. Se a condio expressa pelo corpo da clusula - progenitor (X, Y) - verdadeira ento, segue como conseqncia lgica que a cabea - filho(Y,
X) - tambm o . Por outro lado, se no for possvel demonstrar que o corpo da clusula verdadeiro,
o mesmo ir se aplicar cabea.
filho(Y, X) :- progenitor(X, Y)
A maioria dos sistemas Prolog, na ausncia de caracteres ASCII adequados, emprega o smbolo composto ":-" para denotar a implicao "". Aqui, por uma questo de clareza, adotaremos este ltimo
smbolo, que o normalmente empregado na programao em lgica com clusulas definidas.
A utilizao das regras pelo sistema Prolog ilustrada pelo seguinte exemplo: vamos perguntar ao
programa se Jos filho de Maria:
?-filho(jos, maria).
No h nenhum fato a esse respeito no programa, portanto a nica forma de considerar esta questo
aplicando a regra correspondente. A regra genrica, no sentido de ser aplicvel a quaisquer objetos
X e Y. Logo pode ser aplicada a objetos particulares, como jos e maria. Para aplicar a regra, Y ser
substitudo por jos e X por maria. Dizemos que as variveis X e Y se tornaram instanciadas para:
X=maria e Y=jos
onde a vrgula entre as duas condies indica a sua conjuno, significando que, para satisfazer o
corpo da regra, ambas as condies devem ser verdadeiras. A relao av, apresentada anteriormente
na Figura 2.2, pode agora ser definida em Prolog por:
av(X, Z) :- progenitor(X, Y), progenitor(Y, Z).
Neste ponto interessante comentar alguma coisa sobre o layout dos programas Prolog. Estes podem
ser escritos quase que com total liberdade, de modo que podemos inserir espaos e mudar de linha
onde e quando melhor nos aprouver. Em geral, porm, desejamos produzir programas de boa aparncia, elegantes e sobretudo fceis de ser lidos. Com essa finalidade, normalmente se prefere escrever a
cabea da clusula e os objetivos da condio cada um em uma nova linha. Para destacar a concluso,
identamos os objetivos. A clusula av, por exemplo, seria escrita:
av(X, Z) :progenitor(X, Y),
progenitor(Y, Z).
15
Adicionaremos ainda uma ltima relao ao nosso programa para exemplificar mais uma particularidade da linguagem Prolog. Uma clusula para a relao irm se embasaria na seguinte declarao
lgica:
Para todo
X
X e
X
X e Y
irm de Y se
Y possuem um progenitor comum e
do sexo feminino.
Deve-se atentar para a forma sob a qual o requisito "X e Y possuem um progenitor comum" foi expressa. A seguinte formulao lgica foi adotada: "Algum Z deve ser progenitor de X e esse mesmo Z deve
tambm ser progenitor de Y". Uma forma alternativa, porm menos elegante, de representar a mesma
condio seria: "Z1 progenitor de X e Z2 progenitor de Y e Z1 igual a Z2". Se consultarmos o
sistema com "Jlia irm de ris?" , obteremos, como esperado, um "sim" como resposta. Poderamos ento concluir que a relao irm, conforme anteriormente definida, funciona corretamente, entretanto, h uma falha muito sutil que se revela quando perguntamos: "Quem irm de ris?". O sistema ir nos fornecer duas respostas:
?-irm(X, ris).
X=jlia;
X=ris
dando a entender que ris irm de si prpria. Isso no certamente o que se tinha em mente na definio de irm, entretanto, de acordo com a regra formulada, a resposta obtida pelo sistema perfeitamente lgica. Nossa regra sobre irms no menciona que X e Y no devem ser os mesmos para que X
seja irm de Y. Como isso no foi requerido, o sistema, com toda razo, assume que X e Y podem
denotar a mesma pessoa e ir achar que toda pessoa do sexo feminino que possui um progenitor
irm de si prpria.
Para corrigir esta distoro necessrio acrescentar a condio de que X e Y devem ser diferentes.
Isso pode ser feito de diversas maneiras, conforme se ver mais adiante. Por enquanto vamos assumir
que uma relao diferente(X, Y) seja reconhecida pelo sistema como verdadeira se e somente se X e Y
no forem iguais. A regra para a relao irm fica ento definida por:
irm(X, Y) :progenitor(Z, X),
progenitor(Z,Y),
feminino(X),
diferente(X, Y).
condies dadas;
Por meio de consultas podemos interrogar o programa acerca de que coisas so verdadeiras;
As clusulas Prolog so constitudas por uma cabea e um corpo. O corpo uma lista de objeti-
vos separados por vrgulas que devem ser interpretadas como conjunes;
Fatos so clusulas que s possuem cabea, enquanto que as consultas s possuem corpo e as
16
Ao longo de uma computao, uma varivel pode ser substituda por outro objeto. Dizemos
As variveis so assumidas como universalmente quantificadas nas regras e nos fatos e existen-
Maria
progenitor
Joo
progenitor
Jlia
(a)
ris
progenitor
antepassado direto
Jorge
(b)
antepassado indireto
Por outro lado, a segunda regra mais complicada, porque a cadeia de progenitores poderia se estender indefinidamente. Uma primeira tentativa seria escrever uma clusula para cada posio possvel
na cadeia. Isso conduziria a um conjunto de clusulas do tipo:
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). ... etc.
Isso conduziria a um programa muito grande e que, de qualquer modo, somente funcionaria at um
determinado limite, isto , somente forneceria antepassados at uma certa profundidade na rvore
17
genealgica de uma famlia, porque a cadeia de pessoas entre o antepassado e seu descendente seria
limitada pelo tamanho da maior clusula definindo essa relao. H entretanto uma formulao elegante e correta para a relao antepassado que no apresenta qualquer limitao. A idia bsica
definir a relao em termos de si prpria, empregando um estilo de programao em lgica denominado recursivo:
Para todo X e Z
X antepassado de Z se
existe um Y tal que
X progenitor de Y e
Y antepassado de Z.
Assim possvel construir um programa completo para a relao antepassado composto de duas regras: uma para os antepassados diretos e outra para os indiretos. Reescrevendo as duas juntas tem-se:
antepassado(X, Z) :progenitor(X, Z).
antepassado(X, Z) :progenitor(X, Y),
antepassado(Y, Z).
Tal definio pode causar certa surpresa, tendo em vista a seguinte pergunta: Como possvel ao definir alguma coisa empregar essa mesma coisa se ela ainda no est completamente definida? Tais
definies so denominadas recursivas e do ponto de vista da lgica so perfeitamente corretas e inteligveis, o que deve ficar claro, pela observao da Figura 2.4. Por outro lado o sistema Prolog deve
muito do seu potencial de expressividade capacidade intrnseca que possui de utilizar facilmente
definies recursivas. O uso de recurso , em realidade, uma das principais caractersticas herdadas
da lgica pela linguagem Prolog.
X
progenitor
Y
antepassado
antepassado
H ainda uma questo importante a ser respondida: Como realmente o sistema Prolog utiliza o programa para encontrar as informaes procuradas? Uma explicao informal ser fornecida na prxima
seo, antes porm vamos reunir todas as partes do programa que foi sendo gradualmente ampliado
pela adio de novos fatos e regras. A forma final do programa mostrada na Figura 2.5. O programa
ali apresentado define diversas relaes: progenitor, masculino, feminino, antepassado, etc. A relao
antepassado, por exemplo, definida por meio de duas clusulas. Dizemos que cada uma delas sobre a relao antepassado. Algumas vezes pode ser conveniente considerar o conjunto completo de
clusulas sobre a mesma relao. Tal conjunto de clusulas denominado um predicado.
18
Na Figura 2.5, as duas regras sobre a relao antepassado foram distinguidas com os nomes [pr1] e
[pr2] que foram adicionados como comentrios ao programa. Tais nomes sero empregados adiante
como referncia a essas regras. Os comentrios que aparecem em um programa so normalmente ignorados pelo sistema Prolog, servindo apenas para melhorar a legibilidade do programa impresso. Os
comentrios se distinguem do resto do programa por se encontrarem includos entre os delimitadores
especiais "/*" e "*/". Um outro mtodo, mais conveniente para comentrios curtos, utiliza o caracter
de percentual "%": todo o texto informado entre o "%" e o final da linha interpretado como comentrio. Por exemplo:
/* Isto um comentrio. */
% E isto tambm.
progenitor(maria, jos).
progenitor(joo, jos).
progenitor(joo, ana).
progenitor(jos, jlia).
progenitor(jos, ris).
progenitor(ris, jorge).
masculino(joo).
masculino(jos).
masculino(jorge).
feminino(maria).
feminino(ana).
feminino(jlia).
feminino(ris).
filho(Y, X) :progenitor(X,Y).
me(X,Y) :progenitor(X, Y),
feminino(X).
av(X, Z) :progenitor(X, Y),
progenitor(Y, Z).
irm(X, Y) :progenitor(Z, X),
progenitor(Z, Y),
feminino(X),
diferente(X, Y).
antepassado(X, Z) :progenitor(X, Z).
antepassado(X, Z) :progenitor(X, Y),
antepassado(Y, Z).
%
%
%
%
%
%
%
%
%
%
%
%
%
%
%
%
%
%
Y
X
X
X
X
X
X
Y
X
X
X
X
X
X
X
X
Y
filho de X se
progenitor de Y.
me de Y se
progenitor de Y e
do sexo feminino.
av de Z se
progenitor de Y e
progenitor de Z.
irm de Y se
tem um progenitor, Z que
tambm progenitor de Y e
do sexo feminino e
e Y so diferentes.
antepassado de Z se
progenitor de Z.
antepassado de Z se
progenitor de Y e
antepassado de Z.
[pr1]
[pr2]
2.4 CONSULTAS
Uma consulta em Prolog sempre uma seqncia composta por um ou mais objetivos. Para obter a
resposta, o sistema Prolog tenta satisfazer todos os objetivos que compem a consulta, interpretandoos como uma conjuno. Satisfazer um objetivo significa demonstrar que esse objetivo verdadeiro,
assumindo que as relaes que o implicam so verdadeiras no contexto do programa. Se a questo
tambm contm variveis, o sistema Prolog dever encontrar ainda os objetos particulares que, atribudos s variveis, satisfazem a todos os sub-objetivos propostos na consulta. A particular instanciao das variveis com os objetos que tornam o objetivo verdadeiro ento apresentada ao usurio. Se
no for possvel encontrar, no contexto do programa, nenhuma instanciao comum de suas variveis
que permita derivar algum dos sub-objetivos propostos ento a resposta ser "no".
Uma viso apropriada da interpretao de um programa Prolog em termos matemticos a seguinte:
O sistema Prolog aceita os fatos e regras como um conjunto de axiomas e a consulta do usurio como
um teorema a ser provado. A tarefa do sistema demonstrar que o teorema pode ser provado com
base nos axiomas representados pelo conjunto das clusulas que constituem o programa. Essa viso
19
O primeiro axioma pode ser reescrito como: "Para todo X, se X um homem ento X falvel". Nessa
mesma linha o exemplo pode ser escrito em Prolog como se segue:
falvel(X) :homem(X).
homem(scrates).
?-falvel(X).
X=scrates
Sabe-se que progenitor(jos, ris) um fato. Usando esse fato e a regra [pr1], podemos concluir antepassado(jos, ris). Este um fato derivado. No pode ser encontrado explcito no programa, mas
pode ser derivado a partir dos fatos e regras ali presentes. Um passo de inferncia como esse pode ser
escrito em uma forma mais complexa como:
progenitor(jos, ris)
antepassado(jos, ris)
que pode ser lido assim: "de progenitor(jos, ris) segue, pela regra [pr1] que antepassado(jos,
ris)". Alm disso sabemos que progenitor(joo, jos) fato. Usando este fato e o fato derivado, antepassado(jos, ris), podemos concluir, pela regra [pr2], que o objetivo proposto, antepassado(joo,
ris) verdadeiro. O processo completo, formado por dois passos de inferncia, pode ser escrito:
progenitor(jos, ris)
antepassado(jos, ris)
e
progenitor(joo, jos) e antepassado(jos, ris)
antepassado(joo, ris)
Mostrou-se assim o que pode ser uma seqncia de passos de inferncia usada para satisfazer um objetivo. Tal seqncia denomina-se seqncia de prova. A extrao de uma seqncia de prova do
contexto formado por um programa e uma consulta obtida pelo sistema na ordem inversa da empregada acima. Ao invs de iniciar a inferncia a partir dos fatos, o Prolog comea com os objetivos e ,
usando as regras, substitui os objetivos correntes por novos objetivos at que estes se tornem fatos.
Dada por exemplo a questo: "Joo antepassado de ris?", o sistema tenta encontrar uma clusula
no programa a partir da qual o oibjetivo seja conseqncia imediata. Obviamente, as nicas clusulas
relevantes para essa finalidade so [pr1] e [pr2], que so sobre a relao antepassado, porque so as
nicas cujas cabeas podem ser unificadas com o objetivo formulado. Tais clusulas representam dois
caminhos alternativos que o sistema pode seguir. Inicialmente o Prolog ir tentar a que aparece em
primeiro lugar no programa:
antepassado(X, Z) :- progenitor(X, Z).
uma vez que o objetivo antepassado(joo, ris), as variveis na regra devem ser instanciadas por
X=joo e Y=ris. O objetivo inicial, antepassado(joo, ris) ento substitudo por um novo objetivo:
progenitor(joo, ris)
No h, entretanto, nenhuma clusula no programa cuja cabea possa ser unificada com progenitor(joo, ris), logo este objetivo falha. Ento o Prolog retorna ao objetivo original (backtracking)
para tentar um caminho alternativo que permita derivar o objetivo antepassado(joo, ris). A regra
[pr2] ento tentada:
antepassado(X, Z) :progenitor(X, Y),
20
antepassado(Y, Z).
Encontrando-se agora face a dois objetivos, o sistema tenta satisfaz-los na ordem em que esto formulados. O primeiro deles fcil: progenitor(joo, Y) pode ser unificado com dois fatos do programa:
progenitor(joo, jos) e progenitor(joo, ana). Mais uma vez, o caminho a ser tentado deve corresponder ordem em que os fatos esto escritos no programa. A varivel Y ento instanciada com jos
nos dois objetivos acima, ficando o primeiro deles imediatamente satisfeito. O objetivo remanescente
ento:
antepassado(jos, ris).
Para satisfazer tal objetivo, a regra [pr1] mais uma vez empregada. Essa segunda aplicao de [pr1],
entretanto, nada tem a ver com a sua utilizao anterior, isto , o sistema Prolog usa um novo conjunto
de variveis na regra cada vez que esta aplicada. Para indicar isso iremos renomear as variveis em
[pr1] nessa nova aplicao, da seguinte maneira:
antepassado(X', Z') :progenitor(X', Z').
A cabea da regra deve ento ser unificada como o nosso objetivo corrente, que antepassado(jos,
ris). A instanciao de X'e Y' fica: X'=jos e Y'=ris e o objetivo corrente substitudo por:
progenitor(jos, ris)
Esse objetivo imediatamente satisfeito, porque aparece no programa como um fato. O sistema encontrou ento um caminho que lhe permite provar, no contexto oferecido pelo programa dado, o objetivo originalmente formulado, e portanto responde "sim".
RESUMO
A programao em Prolog consiste em estabelecer relaes entre objetos e em formular con-
Um programa Prolog formado por clusulas. H trs tipos de clusulas: fatos ou assertivas,
Uma relao pode ser especificada por meio de fatos, que estabelecem as tuplas de objetos que
satisfazem a relao, por meio de regras, que estabelecem condies para a satisfao das relaes, ou por meio de combinaes de fatos e regras descrevendo a relao;
Denomina-se predicado ao conjunto de fatos e regras empregados para descrever uma determi-
nada relao;
Interrogar um programa acerca de suas relaes por meio de uma consulta corresponde a con-
Trs tipos de semntica so atribudas aos programas Prolog: declarativa, procedimental e ope-
racional. O programador deve empreg-las conforme o problema a ser resolvido, tirando proveito da situao apresentada.
EXERCCIOS
2.1 Amplie o programa apresentado na Figura 2.5 para representar as relaes tio, prima, cunhado e
sogra.
2.2 Programe a relao descendente(X, Y), onde X descendente de Y.
2.3 Escreva um programa Prolog para representar o seguinte:
Joo nasceu em Pelotas e Jean nasceu em Paris.
Pelotas fica no Rio Grande do Sul.
Paris fica na Frana.
S gacho quem nasceu no Rio Grande do Sul.
22
E
2
D
5
4
5
significando que h um arco de custo T entre os nodos R e S. Por exemplo, arco(A, B, 3) descreve um arco de custo 3 entre os nodos A e B. Assuma tambm que o relacionamento mais(X, Y,
Z) vale quando X+Y=Z. Defina o relacionamento custo(U, V, L) de forma a expressar que existe
um caminho de custo L entre os nodos U e V.
23
3. SINTAXE E SEMNTICA
Prolog um nome comum para uma famlia de sistemas que implementam a lgica de predicados
como linguagem de programao. Algumas destas implementaes, como o Prolog de Edimburgo e o
IC-Prolog, so bastante conhecidas nos meios acadmicos. Outras, como o microProlog, o QuintusProlog e o Arity Prolog ganharam popularidade em diferentes segmentos. No presente texto se adota,
visando maior clareza, uma sintaxe genrica, capaz de ser facilmente adaptada a qualquer ambiente
Prolog.
Objeto
Simples
Varivel
Constante
tomo
Estrutura
Nmero
3.1 OBJETOS
Na Figura 3.1 apresenta-se uma classificao dos objetos em Prolog. O sistema reconhece o tipo de
um objeto no programa por meio de sua forma sinttica. Isso possvel porque a sintaxe do Prolog
especifica formas diferentes para cada tipo de objeto. Na sintaxe aqui adotada, comum maioria das
implementaes, variveis sempre iro iniciar com letras maisculas, enquanto que as constantes nonumricas, ou tomos, iniciam com letras minsculas. Nenhuma informao adicional, tal como tipos
de dados precisa ser fornecida para que o sistema reconhea a informao com a qual est lidando.
3.1.1 TOMOS E NMEROS
No captulo anterior viu-se informalmente alguns exemplos simples de tomos e variveis. Em geral,
entretanto, estes podem assumir formas mais complexas. O alfabeto bsico adotado aqui para a linguagem Prolog consiste dos seguintes smbolos:
Pontuao:
().'"
Conetivos:
,
;
:-
Letras:
a, b, c, ..., z, A, B, C, ..., Z
Dgitos:
0, 1, 2, ..., 9
Especiais:
(conjuno)
(disjuno)
(implicao)
24
x_y
mostraMenu
a_b_1_2
::=
=/=
======>
...
++++
c. Como cadeias de caracteres quaisquer, podendo inclusive incluir espaos em branco, desde que
delimitados por apstrofos ('). Por exemplo:
'D. Pedro I'
'representao de conhecimento'
'13 de outubro de 1993'
'Robert Kowalski'
Um certo cuidado necessrio na formao de tomos do tipo (b.) porque algumas cadeias de caracteres especiais podem possuir um significado pr definido para o sistema Prolog subjacente, como costuma acontecer, por exemplo, com as cadeias '==' e '=\=' .
Os nmeros usados em Prolog compreendem os nmeros inteiros e os nmeros reais. A sintaxe dos
nmeros inteiros bastante simples, como pode ser visto nos exemplos abaixo:
1 1812 0 -273
Nem todos os nmeros inteiros podem ser representados em um computador, portanto o escopo de
variao dos nmeros inteiros est limitado a um intervalo entre algum menor e algum maior nmero,
dependendo da implementao. Normalmente a variao permitida nas implementaes correntes
suficiente para atender todas as necessidades do usurio.
O tratamento dos nmeros reais tambm varia de implementao para implementao. Ser adotada
aqui a sintaxe natural e consagrada, que faz uso do ponto decimal explcito.
3.14159 0.000023 -273.16
Os nmeros reais no so, na verdade, muito utilizados em programas Prolog tpicos. A razo disso
que o Prolog uma linguagem orientada ao processamento simblico, no-numrico, em oposio s
linguagens "devoradoras de nmeros", como por exemplo o Fortran. Na computao simblica, nmeros inteiros so frequentemente empregados, por exemplo, para contar os itens em uma lista, mas a
necessidade de nmeros reais bastante pequena, virtualmente inexistente.
3.1.2 VARIVEIS
Variveis Prolog so cadeias de letras, dgitos e do caracter sublinhado (_), devendo iniciar com este
ou com uma letra maiscula. O caracter "_", sozinho, representa uma varivel annima, isto , sem
interesse para um determinado procedimento. Exemplos de variveis so:
X
Resultado
Objeto2
Lista_de_Associados
_var35
_194
_
(varivel annima)
O escopo lxico de nomes de variveis apenas uma clusula. Isso quer dizer que, por exemplo, se o
nome X25 ocorre em duas clusulas diferentes, ento ele est representando duas variveis diferentes.
Por outro lado, toda ocorrncia de X25 dentro da mesma clusula quer significar a mesma varivel.
25
Essa situao diferente para as constantes: o mesmo tomo sempre significa o mesmo objeto ao
longo de todo o programa.
3.1.3 ESTRUTURAS
Objetos estruturados, ou simplesmente estruturas, so objetos que possuem vrios componentes. Os
prprios componentes podem, por sua vez, ser tambm estruturas. Por exemplo, uma data pode ser
vista como uma estrutura com trs componentes: dia, mes e ano. Mesmo que sejam formadas por diversos componentes as estruturas so tratadas no programa como objetos simples. Para combinar os
componentes em uma estrutura necessrio empregar um functor. Um functor um smbolo funcional (um nome de funo) que permite agrupar diversos objetos em um nico objeto estruturado. Um
functor adequada ao exemplo dado data, ento a data correspondente a 13 de outubro de 1993, cuja
estrutura est presente na Figura 3.2, pode ser escrita como:
data(13, outubro, 1993)
data
13
out.
1993
data
functor
argumentos
(a)
(b)
Na figura acima, em (a) temos a representao de data sob a forma de rvore e em (b) a forma como
escrita em Prolog. Todos os componentes no exemplo so constantes (dois inteiros e um tomo), entretanto, podem tambm ser variveis ou outras estruturas. Um dia qualquer de maro de 1996, por
exemplo, pode ser representado por:
data(Dia, maro, 1996)
Note que "Dia" uma varivel e pode ser instanciada para qualquer objeto em algum ponto da execuo.
Sintaticamente todos os objetos em Prolog so denominados termos. O conjunto de termos Prolog, ou
simplesmente termos, o menor conjunto que satisfaz s seguintes condies:
Toda constante um termo;
Toda varivel um termo;
Se t1, t2, ..., tn so termos e f um tomo, ento f(t1, t2, ..., tn) tambm um termo, onde o
tomo f desempenha o papel de um smbolo funcional n-rio. Diz-se ainda que a expresso f(t1,
t2, ..., tn) um termo funcional Prolog.
Todos os objetos estruturados podem ser representados como rvores. A raiz da rvore o functor e
os ramos que dela partem so os argumentos ou componentes. Se algum dos componentes for tambm
uma rvore, ento ele passa a constituir uma sub-rvore do objeto estruturado completo. Por exemplo,
na Figura 3.3 mostrada a estrutura em rvore correspondente expresso:
(a + b) * (c - 5)
De acordo com a sintaxe dos termos Prolog, anteriormente apresentada, e tomando os smbolos "*",
"+" e "-" como functores, a expresso dada pode ser escrita:
*(+(a, b), -(c, 5))
26
*
+
Este , naturalmente, um termo legal em Prolog, entretanto, no a forma trivial com a qual estamos
acostumados. Normalmente se ir preferir a notao usual, infixa, como utilizada na matemtica. Na
verdade a linguagem Prolog admite as duas formas, prefixa e infixa, para a escrita de expresses aritmticas. Detalhes sobre operadores e definio de operadores especiais sero abordados mais adiante.
3.2 UNIFICAO
Na seo anterior foi visto como os objetos podem ser utilizados na representao de objetos de dados
complexos. A operao mais importante entre dois termos Prolog denominada unificao. A unificao pode, por si s, produzir alguns resultados interessantes. Dados dois termos, diz-se que eles
unificam se:
(1) Eles so idnticos, ou
(2) As variveis de ambos os termos podem ser instanciadas com objetos de maneira que, aps a
substituio das variveis por esses objetos, os termos se tornam idnticos.
Por exemplo, os termos data(D, M, 1994) e data(X, maro, A) unificam. Uma instanciao que torna
os dois termos idnticos :
D instanciada com X;
M instanciada com maro;
A instanciada com 1994.
Por outro lado, os termos data(D, M, 1994) e data(X, Y, 94) no unificam, assim como no unificam
data(X, Y, Z) e ponto(X, Y, Z). A unificao um processo que toma dois termos como entrada e
verifica se eles podem ser unificados. Se os termos no unificam, dizemos que o processo falha. Se
eles unificam, ento o processo bem-sucedido e as variveis dos termos que participam do processo
so instanciadas com os valores encontrados para os objetos, de modo que os dois termos participantes se tornam idnticos. Vamos considerar novamente a unificao entre duas datas. O requisito para
que essa operao se efetue informada ao sistema Prolog pela seguinte consulta, usando o operador
"=":
?-data(D, M, 1994) = data(X, maro, A)
J foi mencionada a instanciao D=X, M=maro e A=1994, que obtm a unificao. H, entretanto,
outras instanciaes que tambm tornam os termos idnticos. Duas delas so:
D=1, X=1, M=maro, A=1994
D=terceiro, X=terceiro, M=maro, A=1994
Essas duas instanciaes so consideradas menos gerais do que a primeira, uma vez que restringem o
valor das variveis D e X mais fortemente do que seria necessrio.. Para tornar os dois termos do
exemplo idnticos, basta que D e X tenham o mesmo valor, seja qual for esse valor. A unificao em
Prolog sempre resulta na instanciao mais geral, isto , a que limita o mnimo possvel o escopo de
valores das variveis, deixando a maior liberdade possvel s instanciaes posteriores. As regras
27
Se S e T so estruturas, unificam somente se: (1) S e T tem o mesmo functor principal, e (2) to-
dos os seus componentes correspondentes tambm unificam. A instanciao resultante determinada pela unificao dos componentes.
Essa ltima regra pode ser exemplificada pelo processo de unificao dos termos
tringulo(ponto(1, 1), A, ponto(2, 3))
com
tringulo(X, ponto(4, Y), ponto(2, Z))
ponto
ponto
tringulo
ponto
ponto
O processo de unificao comea pela raiz (o functor principal). Como ambos os functores unificam,
o processo parte para a unificao dos argumentos, onde a unificao dos pares de argumentos correspondentes ocorre. Assim o processo completo pode ser visto como a seguinte seqncia de operaes
de unificao simples:
tringulo = tringulo
ponto(1, 1) = X
A = ponto(4, Y)
ponto(2, 3) = ponto(2, Z)
O processo completo de unificao bem sucedido porque todas as unificaes na seqncia acima
tambm o so. A instanciao resultante :
X = ponto(1, 1)
A = ponto(4, Y)
Z = 3
onde P, Q e R possuem a sintaxe de termos Prolog. Duas alternativas para a leitura declarativa dessa
clusula so:
P verdadeira se Q e R so verdadeiras
e
De Q e R segue P
Assim a diferena entre as leituras declarativa e procedimental reside principalmente no fato que essa
ltima no apenas define o relacionamento lgico existente entre a cabea e o corpo da clusula,
como tambm exige a existncia de uma ordem na qual os objetivos sero processados.
A semntica declarativa dos programas determina se um dado objetivo verdadeiro e, se for, paera
que valores de variveis isto se verifica. Para definir precisamente o significado declarativo precisamos introduzir o conceito de instncia de uma clusula. Uma instncia de uma clusula C essa
mesma clusula C com cada uma de suas variveis substituda por algum termo. Uma variante de uma
clusula C uma instncia dessa mesma clusula C com cada uma de suas variveis substituda por
outra varivel. Considere, por exemplo, a clusula:
temFilho(X) :- progenitor(X, Y).
"Um objetivo G verdadeiro (isto , satisfatvel ou segue logicamente do programa) se e somente se h uma clusula C no programa e uma instncia I de C tal que: (1) A cabea de I idntica a G, e (2) todos os objetivos
no corpo de I so verdadeiros."
Essa definio pode ser estendida para as consultas como se segue: Em geral uma consulta ao sistema
Prolog uma lista de objetivos separados por vrgulas. Uma lista de objetivos verdadeira se todos os
objetivos nela contidos so verdadeiros para alguma instanciao de suas variveis. Os valores atribudos s variveis que tornam os objetivos da lista simultaneamente verdadeiros correspondem sua
instanciao mais geral.
Uma vrgula entre os objetivos significa a conjuno destes objetivos, isto , todos devem ser satisfeitos. A linguagem Prolog tambm aceita a disjuno de objetivos: basta que um s dentre os objetivos da disjuno seja satisfeito para que todo o conjunto seja considerado satisfeito. A operao de
disjuno representada pelo ponto-e-vrgula (;). Por exemplo, a clusula abaixo:
P :- Q; R.
29
em caso contrrio;
objetivos
executor
sucesso/falha
instanciaes
RESUMO
At aqui estudou-se um tipo de Prolog bsico, denominado tambm de Prolog "puro". Esta denominao devida ao fato de corresponder muito de perto lgica de predicados de primeira ordem. Extenses cujo objetivo adequar a linguagem a necessidades prticas sero estudadas mais adiante. Os
pontos mais importantes do presente captulo so:
Objetos simples, em Prolog, so tomos, variveis e nmeros. Objetos estruturados, ou estrutu-
As estruturas so construdas por meio de functores. Cada functor definido por meio de seu
O escopo lxico das variveis em um programa uma clusula. O mesmo nome de varivel em
As estruturas Prolog podem ser sempre representadas por meio de rvores. Prolog pode ser
A operao de unificao toma dois termos e tenta torn-los idnticos por meio da instanciao
Quando a unificao bem sucedida, resulta na instanciao mais geral das variveis envolvi-
das;
Uma vrgula entre os objetivos significa a sua conjuno, enquanto que um ponto-e-vrgula si-
contexto de um dado programa. A sada desse procedimento o valor-verdade da lista de objetivos com a respectiva instanciao de sua variveis. O procedimento permite o retorno automtico (backtracking) para o exame de novas alternativas;
pode afetar a eficincia de um programa. Uma ordenao inadequada pode mesmo conduzir a
chamadas recursivas infinitas;
EXERCCIOS
3.1 Quais dos seguintes objetos esto sintaticamente corretos e a que tipo de objeto pertencem?
a.
b.
c.
d.
e.
f.
g.
h.
i.
j.
Daniela
daniela
'Daniela'
_daniela
'Daniela vai a Paris'
vai(daniela, paris)
8118
2(X, Y)
+(sul, oeste)
trs(Cavalos(Baios))
3.2 Sugira uma representao para retngulos, quadrados, crculos e elipses, usando uma abordagem
similar apresentada na Figura 3.4. Procure obter a representao mais geral possvel, por exemplo, um quadrado um caso especial de retngulo e um crculo pode ser considerado um caso especial de elipse.
3.3 Quais das prximas operaes de unificao sero bem sucedidas e quais iro falhar? Para as que
forem bem sucedidas, quais so as instanciaes de variveis resultantes?
a.
b.
c.
d.
e.
ponto(A, B) = ponto(1, 2)
ponto(A, B) = ponto(X, Y, Z)
mais(2, 2) = 4
+(2, D) = +(E, 2)
t(p(-1,0), P2, P3) = t(P1, p(1, 0), p(0, Y))
3.4 Defina uma representao Prolog para segmentos de reta no plano expressos em funo dos
31
pontos limites. Que termo ir representar qualquer segmento de reta vertical em X=5?
3.5 Supondo que um retngulo seja representado pelo termo:
retngulo(SupEsq, InfDir)
onde SupEsq representa o ponto superior esquerdo e InfDir o ponto inferior direito de um retngulo em uma tela de vdeo (1280 x 1024), defina a relao
quadrado(R, ...)
Como iria o sistema Prolog responder as seguintes questes? Quando vrias respostas so possveis, d pelo menos duas:
a.
b.
c.
d.
?-f(s(1), A).
?-f(s(s(1)), dois).
?-f(s(s(s(s(s(s(1)))))), C).
?-f(D, trs).
32
4. OPERADORES E ARITMTICA
4.1 OPERADORES
Na matemtica costuma-se escrever expresses como
2*a + b*c
onde + e * so operadores e 2, a, b e c so argumentos. Em particular, + e * so denominados operadores infixos porque se localizam entre os dois argumentos que operam. Tais expresses so representadas por rvores como na Figura 4.1 e podem ser escritas, se for desejado, sob a forma de termos
Prolog, com os smbolos + e * como functores:
+(*(2, a), *(b, c))
Tal notao tambm aceita pelo Prolog, entretanto, trata-se apenas da representao externa deste
objeto, que ser automaticamente convertida para a forma convencional dos termos Prolog. Na sada,
entretanto, o termo ser novamente convertido para a forma externa, com os operadores infixos.
Assim, as expresses matemticas so manipuladas pelo Prolog como meras extenses notacionais e
nenhum novo princpio para a estruturao de objetos est sendo proposto. Se for escrito a+b, o sistema ir reconhecer e manipular tal expresso exatamente como se houvesse sido escrito +(a, b). Para
que o sistema entenda apropriadamente expresses tais como a+b*c, necessrio existir uma prioridade de execuo entre os operadores. Assim o operador + executado prioritariamente ao operador
*. essa prioridade de execuo que decide qual a interpretao correta da expresso. Por exemplo, a
expresso a+b*c poderia em princpio ser entendida como:
+(a, *(b, c)) ou *(+(a, b), c)
A regra geral que o operador de maior prioridade seja o functor principal do termo. Se expresses
contendo + e * devem ser entendidas segundo as convenes usuais, ento + deve ter maior precedncia que *. Assim a expresso a+b*c deve ser entendida como a+(b*c). Se outra interpretao pretendida, deve ser indicada explicitamente com o uso de parnteses, como em (a+b)*c.
O programador Prolog pode tambm definir os seus prprios operadores, isto , definir tomos tais
como tem e suporta como se fossem operadores infixos e ento escrever no programa fatos como:
pedro tem informaes
assoalho suporta mesa
tem(pedro, informaes)
suporta(assoalho, mesa)
A definio de novos operadores realizada pela insero no programa de um certo tipo especial de
clusulas, denominadas diretivas, que atuam como definidoras de operadores. Uma expresso definidora de um operador deve aparecer no programa antes de qualquer expresso que contenha esse operador. Por exemplo, o operador tem pode ser definido pela diretiva:
:- op(600, xfx, tem).
Isso informa ao sistema que se deseja usar tem como um operador de prioridade 600 e cujo tipo
"xfx", que designa uma classe de operadores infixos. A forma de especificao, "xfx", sugere que o
operador, denotado por "f", deva ser colocado entre dois argumentos, denotados por "x".
Deve-se notar que as definies de operadores no especificam qualquer operao ou ao. Em princpio, nenhuma operao sobre objetos associada definio de operadores. Os operadores so
normalmente empregados como functores, isto , somente para combinar objetos em estruturas e no
para executar alteraes sobre tais objetos, apesar do termo "operador" sugerir essa execuo. Os
nomes dos operadores so tomos e sua prioridade encontra-se delimitada por valores inteiros cujo
intervalo depende da implementao. Assumiremos aqui que esse intervalo varie entre 1 e 1200. H
trs tipos bsicos de operadores, conforme a tabela abaixo:
Tabela 4.1 Tipos de Operadores Prolog
OPERADORES
Infixos
Prefixos
Posfixos
xfx
fx
xf
TIPO
xfy
fy
yf
yfx
-
A notao dos especificadores de tipo foi projetada para refletir a estrutura da expresso, onde f representa o operador e x e y representam os argumentos. Um f aparecendo entre os argumentos indica
que o operador infixo. As formas prefixo e posfixo possuem apenas um argumento que segue ou
precede o operador respectivamente.
H uma diferena entre x e y. Para explic-la necessrio introduzir a noo de prioridade de argumento. Se um argumento estiver entre parnteses, ou for um objeto simples, ento sua prioridade
zero. Se um argumento uma estrutura, ento sua prioridade igual prioridade de seu functor principal. O x representa um argumento cuja prioridade obrigatoriamente menor do que a do operador,
enquanto que y representa um argumento cuja prioridade menor ou igual prioridade do operador.
Essas regras auxiliam a evitar ambigidades em expresses com muitos operadores de mesma prioridade. Por exemplo, a expresso:
a - b - c
normalmente entendida como (a-b)-c e no como a-(b-c). Para atingir a interpretao usual, o operador "-" tem que ser definido como yfx. A Figura 4.2 mostra como isso ocorre.
Na figura 4.2, assumindo que "-" tem a prioridade 500, se "-" for do tipo yfx, ento a interpretao (b)
invlida, porque a precedncia de (b-c) tem de ser obrigatoriamente menor do que a precedncia de
"-". Como ou outro exemplo, considere o operador prefixo not. Se not for definido como fy, ento a
expresso
not not p
vlida. Por outro lado, se not for definido como fx a expresso ilegal, porque o argumento do primeiro not not p, que tem a mesma prioridade que o not. Neste ltimo caso a expresso precisa ser
escrita entre parnteses:
not(not p)
34
prioridade zero
(a-b)
(b-c)
prioridade 500
prioridade 500
(a)
(b)
[':-', '?-']).
[+, -, not]).
O uso de operadores pode melhorar muito a legibilidade de alguns programas. Como um exemplo,
vamos assumir que estejamos escrevendo um programa para a manipulao de expresses booleanas e
que em tal programa desejamos estabelecer uma das leis de equivalncia de De Morgan:
(A B) <===> A B
Entretanto uma boa prtica em programao Prolog tentar reter a maior semelhana possvel entre a
notao original do problema e a notao usada noi programa. Em nosso exemplo isso pode ser obtido
por meio do uso de operadores. Um conjunto adequado de operadores para o nosso propsito pode ser
definido como:
::::-
op(
op(
op(
op(
800,
700,
600,
500,
xfx, <===> ).
xfy, ).
xfy, ).
fy, ).
que, conforme estabelecido anteriormente, pode ser entendido como mostrado na figura abaixo:
35
<===>
4.2 ARITMTICA
A linguagem Prolog principalmente utilizada - como j se viu - para a computao simblica, onde
as necessidades de clculo so comparativamente modestas. Assim, o instrumental da linguagem
Prolog destinado a computaes numricas algo simples em comparao com outras linguagens
destinadas especificamente para esse fim, como por exemplo o Pascal-SC. Alguns dos operadores prdefinidos, anteriormente vistos podem ser usados para computao numrica. Tais operadores so
mostrados na Tabela 4.2.
Tais operadores, excepcionalmente, executam uma certa operao. Mesmo em tais casos, entretanto,
necessrio introduzir uma indicao adicional para executar a ao necessria. O sistema sabe como
conduzir a operao denotada pelos operadores, entretanto isso no suficiente para conduzir a seqncia da ao.
Tabela 4.2 Operadores pr-definidos para computao numrica
OPERADOR
+
*
/
div
mod
^
PRIORIDADE
500
500
400
400
400
300
200
TIPO
yfx
yfx
yfx
yfx
yfx
xfx
xfy
SIGNIFICADO
adio
subtrao
multiplicao
diviso
diviso inteira
resto da diviso inteira
potenciao
A consulta mostrada a seguir, assim como a resposta obtida, representam uma tentativa ingnua de
obter computao numrica:
?-X = 1 + 2.
X = 1 + 2
e no X = 3 como se poderia esperar. A razo simples: a expresso "1 + 2" denota simplesmente um
termo Prolog, onde + o functor e 1 e 2 so os argumentos. No h nada no termo acima que efetivamente obrigue o Prolog a ativar a operao de adio. Um operador pr-definido especial "is" fornecido para ordenar a execuo da operao representada, forando as computaes numricas envolvidas. A maneira correta de se obter o resultado da adio proposta acima :
?-X is 1 + 2.
X = 3
A adio aqui executada por um procedimento especial associado ao operador "is". Tais procedimentos so denominados procedimentos embutidos. No h concordncia geral sobre notao aritmtica em Prolog, de forma que diferentes implementaes da linguagem podem utilizar notaes algo
diferentes. Por exemplo, o operador "/" pode denotar diviso inteira ou diviso em ponto flutuante,
36
dependendo da implementao. Aqui, "/" denotar a diviso em ponto flutuante, enquanto que o operador "div" denotar a diviso inteira. Exemplificando, na consulta abaixo:
?-X is 3/2, Y is 3 div 2.
X=1.5
Y=1
O argumento esquerda do operador "is" deve ser um objeto simples. O argumento direita deve ser
uma expresso aritmtica, composta de operadores aritmticos, variveis e nmeros. Uma vez que o
operador "is" ir forar a execuo da operao indicada, todas as variveis contidas na expresso
devem estar instanciadas com nmeros no momento da execuo de tal objetivo. A prioridade dos
operadores aritmticos pr-definidos (ver Figura 4.4) tal que a associatividade dos argumentos com
os operadores a mesma normalmente usada em matemtica. Os parnteses podem ser usados para
indicar associaes diferentes. Note que +, -, *, / e div so definidos como yfx, o que significa que a
execuo se dar da esquerda para a direita. Por exemplo, X is 5-2-1 interpretado como X is (5-2)-1.
A aritmtica tambm envolvida na comparao de valores numricos. Por exemplo, a verificao se
o produto de 277 por 37 maior que 10000 pode ser especificada pelo objetivo:
?-277 * 37 > 10000.
sim
Note que de forma semelhante ao operador "is", o operador ">" tambm fora a avaliao de expresses. Suponha-se, por exemplo, uma relao denominada "nasceu", que relaciona nomes de pessoas
com seus respectivos anos de nascimento. Ento possvel recuperar os nomes das pessoas nascidas
entre 1970 e 1980 inclusive, com a seguinte questo:
?-nasceu(Nome, Ano),
Ano >= 1970,
Ano =< 1980.
PRIORIDADE
700
700
700
700
700
700
TIPO
xfx
xfx
xfx
xfx
xfx
xfx
SIGNIFICADO
maior que
menor que
maior ou igual a
menor ou igual a
valores iguais
valores diferentes
Note que a diferena existente entre o operador de unificao e o operador =:=, por exemplo, nos
objetivos X = Y e X =:= Y. O primeiro objetivo ir ocasionar a unificao dos objetos X e Y, instanciando, se for o caso, alguma varivel em X e Y. Por outro lado, X =:= Y ocasiona a avaliao aritmtica sem causar a instanciao de nenhuma varivel. As diferenas se tornam claras nos exemplos a
seguir:
?-1+2 =:= 2+1.
sim
?-1+2 = 2+1.
no
?-1+A = B+2.
A=2 B=1
FUNO
abs(X)
acos(X)
asin(X)
atan(X)
cos(X)
exp(X)
ln(X)
log(X)
sin(X)
sqrt(X)
tan(X)
round(X,N)
Pi
Random
SIGNIFICADO
Valor absoluto de X
Arco-cosseno de X
Arco-seno de X
Arco-tangente de X
Cosseno de X
Valor de "e" elevado a X
Logaritmo natural de X
Logaritmo decimal de X
Seno de X
Raiz quadrada de X
Tangente de X
Arredonda X para N casas decimais
Valor de p com 15 casas decimais
Um nmero aleatrio entre 0 e 1
Como um exemplo mais complexo, suponha o problema de computar o mximo divisor comum de
dois nmeros. Dados dois inteiros positivos, X e Y, seu mximo divisor comum D pode ser encontrado segundo trs casos distintos:
(1) Se X e Y so iguais, ento D igual a X;
(2) Se X<Y, ento D igual ao mdc entre X e a diferena X-Y;
(3) Se X>Y, ento cai-se no mesmo caso (2), com X substitudo por Y e vice-versa.
As trs clusulas Prolog que que expressam os trs casos acima so:
mdc(X, X, X).
mdc(X, Y, D) :X < Y,
Y1 is Y-X,
mdc(X, Y1, D).
mdc(X, Y, D) :X > Y,
mdc(Y, X, D).
Naturalmente, o ltimo objetivo na terceira clusula poderia ser de modo equivalente substitudo por:
X1 is X-Y,
mdc(X1, Y, D).
Um ltimo exemplo ser dado para recursivamente calcular o fatorial de um nmero inteiro dado. O
programa :
fatorial(0, 1).
fatorial(X, Y) :X1 is X-1,
fatorial(X1, Y1),
Y is X*Y1.
interessante notar aqui que o processo recursivo mantm latentes todas as operaes aritmticas at
que o fato "fatorial(0, 1)" seja alcanado, quando ento, todas as operaes pendentes so executadas
para fornecer em Y o fatorial desejado.
RESUMO
A notao para definio de operadores permite ao programador adequar a sintaxe de seus pro-
38
Novos operadores so definidos por meio da diretiva "op", que estabelece o nome do operador,
vos sintticos que oferecem a possibilidade de se escrever termos Prolog em uma sintaxe alternativa;
EXERCCIOS
4.1 Assumindo as seguintes definies de operadores:
:- op(300, xfx, joga).
:- op(200, xfy, e).
Como estes termos so interpretados pelo Prolog? Qual o functor principal de cada termo e
qual a sua estrutura?
4.2 Sugira uma apropriada definio dos operadores "era" e "do" para que seja possvel a escrita de
clusulas como:
vera era secretria do departamento.
e
paulo era professor do curso.
Como ir este programa responder as seguintes questes, considerando ser + um operador infixo
do tipo yfx (como usual).
a.
b.
c.
d.
?-t(0+1, A).
?-t(0+1+1, B).
?-t(1+0+1+1+1, C).
?-t(D, 1+1+1+0).
4.4 Defina os operadores "se", "ento", "seno" e ":=" de modo que seja vlido o termo:
se X>Y ento Z := X seno Z := Y
Escolha a precedncia dos operadores de modo que "se" venha a ser o functor principal. Depois
defina a relao "se" como um mini-interpretador para um tipo de comando se-ento da forma:
se V1>V2 ento Var:=V3 seno Var:=V4
onde V1, V2, V3 e V4 so nmeros (ou variveis instanciadas com nmeros) e Var uma varivel. O significado da relao "se" deve ser: "se o valor de V1 maior que o valor de V2, ento
Var instanciada com V3, seno Var instanciada com V4. Um exemplo do uso do miniinterpretador seria:
?-X=2, Y=3, V2 is 2*X, V4 is 4*X,
se Y > V2 ento Z:=Y seno Z:=V4,
se Z > 5 ento W=1 seno W=0.
X=2 Y=3 Z=8 W=1
entre(N1, N2, X)
que, para dois inteiros dados, N1 e N2, produz atravs de backtracking todos os inteiros X que
satisfazem a restrio
N1 X N2
4.6 Estude a definio de um "mundo de polgonos" onde os objetos so definidos em funo das
coordenadas de seus vrtices no plano. Indivduos desse universo seriam tringulos, retngulos,
quadrados, etc. Por exemplo o termo:
tringulo((1,1), (1,2), (2,2))
definiria um tringulo cujos vrtices seriam os pontos (1,1), (1,2) e (2, 2) em um sistema de coordenadas cartesianas.
Formule as propriedades bsicas de cada objeto atravs de relaes unrias, tais como:
issceles(X)
ou
"D distncia entre os centros geomtricos de A e B".
Pense numa verso deste programa para gerar trajetrias de figuras planas ao longo de curvas de
equaes dadas.
40
5. PROCESSAMENTO DE LISTAS
Uma importante classe de estruturas de dados em Prolog composta de expresses simblicas, tambm denominadas "S-Expresses", que permitem a representao de listas de tamanho indefinido
como tipos de rvores onde os ramos, tambm denominados sub-rvores, so reunidos entre parnteses e outros delimitadores para formar sequncias de objetos. A analogia entre listas aninhadas e rvores fundamental para o perfeito entendimento de algumas operaes realizadas em listas. A sintaxe
das listas em Prolog uma variante da sintaxe empregada em LISP, que uma linguagem tradicionalmente empregada em inteligncia artificial e computao simblica. No presente captulo abordase a representao em listas, a codificao em Prolog de diversas operaes e a construo de algumas
aplicaes empregando estruturas em listas.
5.1 REPRESENTAO DE LISTAS
Listas so estruturas simples de dados, largamente empregadas em computao no-numrica. Uma
lista uma sequncia de qualquer nmero de itens, como: brasil, uruguai, argentina, paraguai. Uma
lista deste tipo pode ser escrita em Prolog como:
[brasil, uruguai, argentina, paraguai]
Esta, entretanto, apenas a aparncia externa das listas. Como j foi visto, todos os objetos estruturados em Prolog so na realidade rvores e as listas seguem a regra. Como representar listas como objetos Prolog? Dois casos devem ser considerados: a lista vazia e a lista no-vazia. No primeiro caso, a
lista representada simplesmente como um tomo, []. No segundo, a lista deve ser pensada como
constituda de dois componentes: uma "cabea" e um "corpo". Por exemplo, na lista dada, a cabea
"brasil" e o corpo a lista [uruguai, argentina, paraguai].
Em geral, a cabea pode ser qualquer objeto Prolog - como uma rvore ou uma varivel. O corpo,
entretanto, deve ser obrigatoriamente uma lista. A cabea e o corpo so combinados numa estrutura
por meio de um functor especial. A escolha desse functor depende da implementao considerada da
linguagem Prolog. Aqui ser assumido o ponto "" que o smbolo funcional adotado com maior
frequncia na representao de listas nas diversas implementaes Prolog:
(Cabea, Corpo)
Uma vez que a varivel Corpo representa, por sua vez, uma lista, esta pode ser vazia ou possuir a sua
prpria cabea e corpo. Portanto, para a representao de listas de qualquer tamanho, nenhum princpio adicional necessrio. O exemplo de lista dado ento representado pelo termo Prolog:
(brasil, (uruguai, (argentina, (paraguai, [])))).
brasil
uruguai
argentina
paraguai
[]
Na Figura 5.1 apresenta-se a correspondente estrutura em rvore. Note que a lista vazia aparece no
termo acima. Isso ocorre porque o ltimo "corpo" uma lista de um nico item [paraguai], que possui
uma lista vazia como seu corpo:
41
Este exemplo mostra como o princpio geral para a estruturao de objetos Prolog tambm se aplica a
listas de qualquer tamanho. Como o exemplo tambm mostra, a notao direta com o uso do functor "
" pode produzir expresses bastante confusas. Por esta razo o sistema Prolog oferece uma notao
simplificada para listas, permitindo que as mesmas sejam escritas como sequncias de itens separados
por vrgulas e includos entre parenteses retos. O programador pode empregar qualquer notao, todavia, a que utiliza parenteses retos normalmente preferida. Segundo tal notao, um termo da forma
[H|T] tratado como uma lista de cabea H e corpo T. Listas do tipo [H|T] so estruturas muito comuns
em programao no-numrica. Deve recordar-se que o corpo de uma lista sempre outra lista, mesmo
que seja vazia. Os seguintes exemplos devem servir para demonstrar tais ideias:
[X | Y]
ou [X
| [Y | Z]]
[X, Y, Z]
b, c, d]
b, c, d]
para a identificao de predicados. Por exemplo grfico/3 denota uma relao denominada grfico
com trs argumentos. Esse detalhamento s vezes importante. Nome e aridade so os elementos
necessrios e suficientes para a perfeita identificao de um predicado.
5.2.1 CONSTRUO DE LISTAS
A primeira necessidade para a manipulao de listas ser capaz de constru-las a partir de seus elementos bsicos: uma cabea e um corpo. Tal relao pode ser escrita em um nico fato:
cons(X, Y, [X | Y]).
Por exemplo:
?-cons(a, b, Z).
Z=[a | b]
Durante a unificao a varivel X instanciada com a, Y com b e Z com [X|Y], que por sua vez
instanciada com [a|b], devido aos valores de X e Y. Se X for um elemento e Y uma lista, ento [X|Y]
uma nova lista com X como primeiro elemento. Por exemplo:
?-cons(a, [b, c], Z).
Z=[a, b, c]
?-cons(a, [], Z).
Z=[a]
de modo que o predicado cons/3 no resolve o problema de concatenar duas listas numa terceira.
Mais adiante ser estudado o predicado conc/3 que realiza tal funo.
5.2.2
Vamos implementar um tipo de relao de ocorrncia que estabelece se determinado objeto membro
de uma lista, como em:
membro(X, L)
mas a declarao
membro(b, [a, [b, c]])
que pode ser representada em Prolog por meio de duas clusulas. A primeira, um fato, estabelece a
primeira condio: X membro de L, se X a cabea de L. A segunda, uma regra que ser empregada
quando X no cabea de L, uma chamada recursiva que diz que X ainda pode ser membro de L,
desde que seja membro do corpo de L. Em Prolog:
membro(X, [X|C]).
membro(X, [Y|C]) :membro(X, C).
Note-se que o corpo da lista na primeira clusula sempre um resultado sem qualquer interesse, o
mesmo ocorrendo com a cabea da lista na segunda. possvel ento empregar variveis annimas e
escrever o predicado de forma mais elegante:
membro(X, [X|_]).
membro(X, [_|C]) :membro(X, C).
Novamente, dois casos devem ser considerados para a definio de conc/3, dependendo do primeiro
argumento L1:
(1) Se o primeiro argumento uma lista vazia, ento o segundo e o terceiro argumentos devem ser
43
a mesma lista. Chamando tal lista de L, essa situao pode ser representada pelo seguinte fato
Prolog:
conc([], L, L).
(2) Se o primeiro argumento de conc/3 for uma lista no-vazia, ento porque ela possui uma cabea e um corpo e pode ser denotada por [X|L1]. A concatenao de [X|L1] com uma segunda
lista L2, produzir uma terceira lista com a mesma cabea X da primeira e um corpo L3 que a
concatenao do corpo da primeira lista, L1, com toda a segunda, L2. Isso pode ser visto na figura 5.2, e se representa em Prolog por meio da regra:
conc([X|L1], L2, [X|L3]) :conc(L1, L2, L3).
L1
L2
L3
O programa conc/3, apesar de muito simples, tambm muito flexvel e pode ser usado em inmeras
aplicaes. Por exemplo, ele pode ser usado no sentido inverso ao que foi originalmente projetado
para decompor uma lista em duas partes:
?- conc(L1, L2, [a, b, c]).
L1=[] L2=[a, b, c];
L1=[a] L2=[b, c];
L1=[a, b] l2=[c];
L1=[a, b, c] L2=[];
no
Esse resultado mostra que sempre possvel decompor uma lista de n elementos em n+1 modos, todos
eles obtidos pelo programa atravs de backtracking. Podemos tambm usar o programa para procurar
por um determinado padro numa lista. Por exemplo, podemos encontrar os meses antes e depois de
um determinado mes:
?-M=[jan,fev,mar,abr,mai,jun,jul,ago,set,out,nov,dez], conc(Antes, [mai | Depois], M).
Antes=[jan,fev,mar,abr] Depois=[jun,jul,ago,set,out,nov, dez]
e tambm achar o sucessor e o predecessor imediatos (os vizinhos) de um determinado item da lista:
?-conc(_, [X, g, Y | _], [a, b, c, d, e, f, g, h]).
X=f Y=h
possvel ainda apagar de uma lista todos os elementos que se seguem a um determinado padro. No
exemplo abaixo, retira-se da lista dos dias da semana a sexta-feira e todos os dias que a seguem.
?-conc(Trab, [sex | _], [seg,ter,qua,qui,sex,sab,dom]).
Trab=[seg,ter,qua,qui]
44
A prpria relao de ocorrncia, membro/2, vista na seo anterior pode ser reprogramada em funo
de conc/3:
membro1(X, L) :conc(_, [X|_], L).
Essa clusula nos diz que X membro de uma lista L se L puder ser decomposta em duas outras listas
onde a cabea da segunda X. Na verdade, membro1/2 define a mesma relao que membro/2, apenas se adotou um nome diferente para estabelecer uma distino entre ambas.
5.2.4 REMOO DE ELEMENTOS DE UMA LISTA
A remoo de um elemento X de uma lista L pode ser programada atravs da relao:
remover(X, L, L1)
onde L1 a mesma lista L com o elemento X removido. A relao remover/3 pode ser definida de
maneira similar relao de ocorrncia. Novamente so dois casos a estudar:
(1) Se X a cabea da lista L, ento L1 ser o seu corpo;
(2) Se X est no corpo de L, ento L1 obtida removendo X desse corpo.
Em Prolog, isso escrito da seguinte maneira:
remover(X, [X|C], C).
remover(X, [Y|C], [Y|D]) :remover(X, C, D).
Assim como a relao membro/2, remover/3 tambm no-determinstica por natureza. Se h diversas
ocorrncias de X em L, a relao remove/3 capaz de retirar cada uma delas atravs do mecanismo de
backtracking do Prolog. Evidentemente, em cada execuo do programa remove/3 retiramos somente
uma das ocorrncias de X, deixando as demais intocveis. Por exemplo:
?-remover(a, [a, b, a, a], L).
L=[b, a, a];
L=[a, b, a];
L=[a, b, a];
no
remover/3 ir falhar se a lista L no contiver nenhuma ocorrncia do elemento X. Essa relao pode
ser ainda usada no sentido inverso para inserir um novo item em qualquer lugar da lista. Por exemplo,
pode-se formular a questo: "Qual a lista L, da qual retirando-se 'a' , obtem-se a lista [b, c, d]?"
?-remover(a, L, [b, c, d]).
L=[a, b, c, d];
L=[b, a, c, d];
L=[b, c, a, d];
L=[b, c, d, a];
no
De modo geral, pode inserir-se um elemento X nalgum lugar de uma lista L, resultando numa
nova lista L1, com o elemento X inserido na posio desejada, por meio da clusula:
inserir(X, L, L1) :remover(X, L1, L).
Em membro1/2 foi obtida uma forma alternativa para a relao de ocorrncia, utilizando o predicado
conc/3. Pode-se obter a mesma relao por meio de remover/3:
membro2(X, L) :remover(X, L, _).
45
A relao que inverte uma lista, isto , que organiza os seus elementos na ordem inversa til para os
mais diversos propsitos. Abaixo temos alguns exemplos de inverso:
inverter([a, b, c], [c, b, a]).
inverter([], []).
inverter([a, [b, c], d], [d, [b, c], a])
Dentre os diversos mecanismos lgicos capazes de inverter uma lista, o denominado "inverso ingnua" baseia-se numa abordagem muito direta, embora o seu tempo de execuo seja proporcional ao
quadrado do tamanho da lista:
(1) Tomar o primeiro elemento da lista;
(2) Inverter o restante;
(3) Concatenar a lista formada pelo primeiro elemento ao inverso do restante.
Em Prolog, escreve-se:
inverter([], []).
inverter([X|Y], Z) :inverter(Y, Y1),
conc(Y1, [X], Z).
Esse programa, juntamente com o predicado conc/3, costuma ser empregue como um teste de benchmark para sistemas Prolog. Quando o nmero de inferncias lgicas, ou chamadas de objetivos
Prolog dividido pelo nmero de segundos gastos, o nmero obtido mede a velocidade do sistema
Prolog em LIPS (logic inferences per second). A inverso de listas pode, entretanto ser obtida de
modo mais eficiente por meio de um predicado auxiliar, iterativo, aux/3, tornando o tempo de execuo apenas linearmente proporcional ao tamanho da lista a inverter:
inverter(X, Y) :aux([], X, Y).
aux(L, [], L).
aux(L, [X|Y], Z) :aux([X|L], Y, Z).
5.2.6 SUBLISTAS
Iremos considerar agora a relao sublista/2 que possui como argumentos uma lista S e uma lista L
tais que S ocorre em L como sublista. Assim, verdadeira a afirmao:
sublista([c, d, e], [a, b, c, d, e, f])
O programa Prolog para a relao sublista/2 pode se basear na mesma idia explorada na definio do
predicado membro1/2, com a diferena que, desta vez, a relao mais genrica, podendo ser formulada por:
S sublista de L se:
(1) L pode ser decomposta em duas listas, L1 e L2, e
(2) L2 pode ser decomposta em S e L3.
Como foi visto anteriormente, a relao conc/3 pode ser usada para a decomposio de listas. Assim a
formulao acima pode ser expressa em Prolog por:
sublista(S, L) :conc(L1, L2, L),
conc(S, L3, L2).
O programa sublista/2 pode ser usado de modo bastante flexvel em diversas aplicaes. Apesar de ter
sido projetado para verificar se alguma lista ocorre como sublista de outra, ele pode, por exemplo, ser
46
O programa permutar/2 deve novamente basear-se na considerao de dois casos, dependendo da lista
a ser permutada:
(1) Se a primeira lista vazia, ento a segunda tambm ;
(2)
Se a primeira lista no-vazia, ento possui a forma [X|L] e uma permutao de tal lista pode ser construda
primeiro permutando L para obter L1e depois inserindo X em qualquer posio de L1, conforme a Figura
5.3.
L
permutar L
L1
inserir X obtendo uma
permutao de [X | L].
O uso normal da relao permutar/2 seria como no exemplo dado anteriormente, permutar([a, b, c],
P). Uma tentativa diferente seria propor ao sistema:
?-permutar(L, [a, b, c]).
Aqui o programa dado ir, de incio, obter em L as seis permutaes existentes para [a, b, c], mas
depois, se o usurio pedir mais solues, o programa nunca ir responder "no", entrando em um lao
infinito na tentativa de encontrar outra permutao onde j no h mais nenhuma. Assim, algum cuidado necessrio no uso desta relao.
47
Por exemplo:
?-tamanho([a, b, c, d, e], X).
X=5
Outras vezes necessrio selecionar exatamente o ensimo elemento de uma lista. O predicado ensimo/3, a seguir, realiza esta funo:
enesimo(1, X, [X|_]).
enesimo(N, X, [_|Y]) :enesimo(M, X, Y),
N is M+1.
Outra necessidade frequente reunir numa lista separada determinados elementos de uma lista,
identificados pela sua posio. Isso obtido pelo predicado seleciona/3, abaixo, que por sua vez emprega a relao ensimo/3:
seleciona([], _, []).
seleciona([M|N], L, [X|Y]) :ensimo(M, X, L),
seleciona(N, L, Y).
Por exemplo:
?-seleciona([2, 4], [a, b, c, d, e], L).
L=[b, d]
48
O somatrio e o produtrio de uma lista so dados respectivamente pelas relaes soma/2 e produto/2
abaixo. Observe o artifcio empregado na definio de produto/2, para garantir que o produtrio de
uma lista vazia seja zero.
soma([], 0).
soma([X|Y], S) :S is R+X,
soma(Y, R).
produto([], 0).
produto([X], X).
produto(L, P) :prod(L, P).
prod([], 1).
prod([X|Y], P) :P is Q*X,
prod(Y, Q).
Este ltimo exemplo, apesar da interpretao declarativa correta, no domnio dos inteiros positivos,
poder no funcionar corretamente em todas as implementaes Prolog devido a caractersticas operacionais particulares de irreversibilidade dos operadores aritmticos.
5.3.4 INTERSECO DE LISTAS
O predicado intersec/3, a seguir, computa a interseco de duas listas numa terceira:
intersec([X|Y], L, [X|Z]) :membro(X, L),
intersec(Y, L, Z).
intersec([_|X], L, Y) :intersec(X, L, Y).
intersec(_, _, []).
Por exemplo:
?-intersec([a, b, c, d], [aa, b, d], L).
L=[b, d]
RESUMO
Listas so estruturas freqentemente usadas em Prolog. Estas podem ser vazias (representadas pelo tomo []),
ou constitudas por uma cabea (seu primeiro elemento) e um corpo (os demais);
A notao usual para listas emprega o functor "" (ponto) reunindo dois argumentos, a cabea e
o corpo, em uma nica lista. Por exemplo, (a, (b, (c, []))) representa a lista [a, b, c];
H uma notao simplificada em Prolog que permite a representao de listas na forma [H|T],
A cabea de uma lista pode ser qualquer termo Prolog, entretanto o corpo de uma lista sempre
49
H uma correspondncia entre listas e estruturas em rvore, permitindo que listas sejam ele-
Operaes comuns sobre listas apresentadas no presente captulo foram: construo, ocorrn-
5.1 Escreva um programa denominado acomoda/2 cujo primeiro argumento uma lista permitindo
listas como elementos, tal como [a, [a, [b, c]], b. [c, d]], e cujo segundo argumento outra lista
com todos os elementos da primeira acomodados numa nica lista, como [a, a, b, c, b, c, d].
Por exemplo:
?-acomoda([a, [b], [c, d]], L).
L=[a, b, c, d]
Examine a reversibilidade do predicado obtido. O que possvel obter por meio de backtracking?
5.2 Qual o nmero de inferncias necessrio para computar o inverso de uma lista pelo mtodo da
inverso ingnua? Use-o para medir a velocidade em LIPS do seu sistema Prolog.
5.3 Escreva um programa que inverta uma lista de elementos e que tambm, recursivamente, inverta
esses prprios elementos quando eles forem listas.
5.4 Escreva um programa denominado
escore(X, Y, A, B)
que verdadeiro se X uma lista cujos elementos invertidos produzem a mesma ordem original.
Por exemplo:
?-palndromo([a, X, a, r, Y]).
X=r Y=a
onde L uma lista de nmeros, Max o maior destes nmeros, Min o menor, Med a sua mdia
aritmtica e DP o seu desvio padro.
5.8 Escreva um programa denominado
ordena(X, Y)
50
6. CONTROLE
Como j foi visto, o programador pode controlar a execuo de seu programa atravs da reordenao
das clusulas ou de objetivos no interior destas. Neste captulo estudar-se- um outro instrumento para
o controle de programas - denominado "cut" - que se destina a prevenir a execuo do backtracking
quando este no for desejado. Tambm se introduzir a "negao por falha", uma forma operacional
da negao lgica. Exemplos sero apresentados com a finalidade de ilustrar os conceitos desenvolvidos.
6.1 BACKTRACKING
Na execuo dos programas Prolog, a evoluo da busca por solues assume a forma de uma rvore denominada "rvore de pesquisa" ou "search tree" - que percorrida sistematicamente de cima para
baixo (top-down) e da esquerda para direita, segundo o mtodo denominado "depth-first search" ou
"pesquisa primeiro em profundidade". A Figura 6.1 ilustra esta idia. Ali representada a rvore correspondente execuo do seguinte programa abstrato, onde a, b, c, etc. possuem a sintaxe de termos
Prolog:
a :- b.
a :- c.
a :- d.
b :- e.
b :- f.
f :- g.
f :- h.
f :- i.
d.
O programa representado na figura acima ser bem sucedido somente quando o nodo d for atingido,
uma vez que este o nico fato declarado no programa. De acordo com a ordenao das clusulas, d
ser tambm o ltimo nodo a ser visitado no processo de execuo. O caminho percorrido dado
abaixo
a, b, e, (b), f, g, (f), h, (f), i, (f), (b), (a), c, (a), d
51
Como foi visto, os objetivos em um programa Prolog podem ser bem-sucedidos ou falhar. Para um
objetivo ser bem-sucedido ele deve ser unificado com a cabea de uma clusula do programa e todos
os objetivos no corpo desta clusula devem tambm ser bem-sucedidos. Se tais condies no ocorrerem, ento o objetivo falha.
Quando um objetivo falha, em um nodo terminal da rvore de pesquisa, o sistema Prolog aciona o
mecanismo de backtracking, retornando pelo mesmo caminho percorrido, na tentativa de encontrar
solues alternativas. Ao voltar pelo caminho j percorrido, todo o trabalho executado desfeito. O
seguinte exemplo, sobre o predicado gosta/2 pode ajudar a esclarecer tais idias.
gosta(joo, jazz).
gosta(joo, renata).
gosta(joo, lasanha).
gosta(renata, joo).
gosta(renata, lasanha).
O significado intuitivo do predicado gosta(X, Y) "X gosta de Y". Supondo o conhecimento acima,
queremos saber do que ambos, joo e renata, gostam. Isto pode ser formulado pelos objetivos:
gosta(joo, X), gosta(renata, X).
O sistema Prolog tenta satisfazer o primeiro objetivo, desencadeando a seguinte execuo top-down:
1. Encontra que joo gosta de jazz
2. Instancia X com "jazz"
3. Tenta satisfazer o segundo objetivo, determinando se "renata gosta de jazz"
4. Falha, porque no consegue determinar se renata gosta de jazz
5. Realiza um backtracking na repetio da tentativa de satisfazer gosta(joo, X), esquecendo o
valor "jazz"
6. Encontra que joo gosta de renata
7. Instancia X com "renata"
8. Tenta satisfazer o segundo objetivo determinando se "renata gosta de renata"
9. Falha porque no consegue demonstrar que renata gosta de renata
10.Realiza um backtracking, mais uma vez tentando satisfazer gosta(joo, X), esquecendo o valor
"renata"
11.Encontra que joo gosta de lasanha
12.Instancia X com "lasanha"
13.Encontra que "renata gosta de lasanha"
14. bem-sucedido, com X instanciado com "lasanha"
O backtracking automtico uma ferramenta muito poderosa e a sua explorao de grande utilidade
para o programador. s vezes, entretanto, ele pode se transformar em fonte de ineficincia. A seguir
se introduzir um mecanismo para "podar" a rvore de pesquisa, evitando o backtracking quando este
for indesejvel.
6.2 O OPERADOR "CUT"
O papel desempenhado pelo operador "cut", de extrema importncia para semntica operacional dos
programas Prolog, permitindo declarar ramificaes da rvore de pesquisa que no devem ser retomadas no backtracking. Seu uso deve ser considerado pelas seguintes razes:
(i) O programa ir executar mais rapidamente, porque no ir desperdiar tempo tentando satisfa52
veis so descartados
Sintaticamente o uso do cut em uma clusula tem a aparncia de um objetivo sem nenhum argumento,
representado por um ponto de exclamao "!".
Vamos estudar agora o comportamento de um pequeno programa que realiza algum backtracking desnecessrio. Identificaremos onde isso ocorre e mostraremos como a eficincia do programa pode ser
melhorada. Considere a funo cujo grfico apresentado na Figura 6.2.
4
3
2
Y=F(X)
1
0
0
9 10
ento Y = 0
(2) Se 3 X e X < 6,
ento Y = 2
(3) Se 6 X,
ento Y = 4
que podem ser escritas em Prolog como uma relao binria f(X, Y), como se segue:
f(X, 0) :- X < 3.
f(X, 2) :- 3 =< X, X < 6.
f(X, 4) :- 6 =< X.
Este programa assume que antes de f(X, Y) ser avaliada, X deve obrigatoriamente estar instanciada
para algum nmero, como requerido pelos operadores de comparao. Faremos duas experincias
com esse programa. Em cada uma delas ser identificada uma fonte de ineficincia no programa, que
ser removida com o uso do cut.
6.2.1 EXCLUSO MTUA
Vamos analisar o que ocorre quando a seguinte questo formulada:
?-f(1, Y), 2 < Y
53
Na execuo do primeiro objetivo, f(1, Y), Y instanciada com 0, de forma que o segundo objetivo
passa a ser 2 < 0, que obviamente falha e, por meio de backtracking, conduz avaliao das outras
duas regras que, por sua vez, tambm iro falhar. Esse raciocnio direto, entretanto, antes de tentar
as duas ltimas regras, j sabamos (ns humanos) que elas no funcionariam. A execuo completa
mostrada na Figura 6.3.
(1)
(2)
1 =< 3
2<0
Y=0
(3)
3 =< 1
1<6
2<2
6 =< 1
2<4
Y=2
Y=4
<--- cut: Aqui j sabemos que (2) e (3) sempre iro falhar.
2<0
No exemplo apresentado na figura acima, no ponto indicado por "cut" no desdobramento da regra 1,
j conhecemos o seu intervalo de aplicao e sabemos que, se este estiver correto e o restante da regra
falhar, no h sentido em explorar outra alternativa. Para prevenir o sistema de apresentar um
backtracking desnecessrio, devemos indicar isto especificamente, o que feito atravs do mecanismo
de corte. Este representado explicitamente por um "!" e inserido entre os objetivos como uma espcie de pseudo-objetivo que sempre bem sucedido quando ativado na direo top-down, mas que
sempre falha quando atingido atravs de backtracking, ocasionando ainda, como efeito colateral, a
falha de todas as demais clusulas do predicado onde o cut declarado. O programa do exemplo, reescrito com cuts assume o seguinte aspecto:
f(X, 0) :- X < 3, !.
f(X, 2) :- 3 =< X, X < 6, !.
f(X, 4) :- 6 =< X.
Aqui o smbolo "!" evita o backtracking nos pontos em que aparece no programa. Se agora novamente
fosse formulada a consulta ?-f(1, Y), 2<Y., o sistema Prolog iria inicialmente produzir o mesmo desvio mais esquerda apresentado na Figura 6.3. O caminho produzido na rvore de pesquisa ir falhar
no objetivo 2<0. O Prolog ir ento tentar o backtracking, mas no alm do ponto marcado com um
"!" no programa. Os desvios correspondentes s regras (2) e (3) no so gerados. O novo programa,
equipado com cuts, , em geral, de execuo mais eficiente do que a verso original, que no possui
cuts. Esta ir certamente produzir os mesmos resultados, apesar de ser menos eficientes. Pode-se dizer, neste caso, que a introduo de cuts afetou somente a interpretao operacional do programa, sem
interferir na sua interpretao declarativa. Veremos a seguir que o uso do cut pode afetar tambm o
significado declarativo do programa.
6.2.2 INTERFERINDO COM A INTERPRETAO DECLARATIVA
Efetuaremos uma segunda experincia, agora sobre a segunda verso do nosso programa. Seja a seguinte consulta, j acompanhada da soluo:
?-f(7, Y).
Y=4
54
Vamos analisar o que aconteceu. Todas as trs regras foram tentadas antes da resposta ter sido obtida,
produzindo a seguinte seqncia de objetivos:
(1) Tenta a regra (1): 7<3 falha. Aciona o backtracking e tenta a regra (2). O cut no foi atingido.
(2) Tenta a regra (2): 3=<7 bem-sucedido, mas 7<6 falha. Aciona o backtracking e tenta a regra
(3). O cut no foi atingido.
(3) Tenta a regra (3): 6=<7 bem-sucedido.
A sequncia permite identificar uma segunda fonte de ineficincia no programa. Primeiro estabelecido que X<3 no verdadeiro, pois 7<3 falha. O objetivo seguinte 3=<X que, se o primeiro objetivo falhou, s pode ser verdadeiro, pois a negao dele. Portanto, o segundo teste redundante e o
objetivo correspondente pode ser omitido. O mesmo pode ser dito do objetivo 6=<X na regra (3). Isso
conduz a uma formulao ainda mais econmica do programa:
f(X, 0) :- X < 3, !.
f(X, 2) :- X < 6, !.
f(X, 4).
Este programa produz os mesmos resultados que a verso original, mas da forma mais eficiente vista
at agora. O que aconteceria entretanto se os cuts fossem removidos? O programa fica:
f(X, 0) :- X < 3.
f(X, 2) :- X < 6.
f(X, 4).
que pode produzir mltiplas solues, as quais nem sempre estaro corretas. Por exemplo:
?-f(1, Y).
Y=0; Y=2; Y=4;
no
importante notar aqui que, diferentemente da segunda verso, na terceira os cuts no afetam somente o comportamento operacional do programa, mas tambm o seu significado declarativo. Uma
idia mais precisa do funcionamento do mecanismo de corte do Prolog o seguinte:
Vamos denominar "objetivo pai" o objetivo que unifica com a cabea da clusula que contm
o cut. Quando o cut encontrado, como um objetivo, ele sempre bem-sucedido, mas elimina
do sistema a pesquisa via backtracking de todas as clusulas entre o objetivo pai e o cut.
Por exemplo, considere-se a clusula:
H :- B1, B2, ..., Bm, !, ..., Bn.
Vamos assumir que ela tenha sido ativada por um objetivo G, que unifica com H. Ento G um objetivo pai. No momento em que o cut encontrado o sistema j possui alguma soluo para os objetivos
B1, ..., Bm. Quando o cut executado, a soluo para B1, ..., Bm fica "congelada" e todas as demais
solues possveis so descartadas. Alm disso, o objetivo G agora passa a se limitar a essa clusula.
Qualquer tentativa de unificar G com com a cabea de alguma outra clusula fica impedida de se realizar. Vamos aplicar tais regras ao seguinte exemplo:
C :- P, Q, R, !, S, T, U.
C :- V.
A :- B, C, D.
?-A.
onde A, B, C, etc. possuem a sintaxe de termos Prolog. O cut ir afetar a execuo do objetivo C da
seguinte maneira: O backtracking possvel na seqncia de objetivos P, Q, R, entretanto, to logo o
cut alcanado todas as solues alternativas para os objetivos P, Q, R so descartadas. A clusula
alternativa para C, C V, tambm descartada, entretanto, o backtracking ainda possvel na lista de
objetivos S, T, U.
O objetivo pai da clusula contendo o cut C em A B, C, D. Portanto o cut ir afetar somente a
55
execuo de C, sendo completamente invisvel do ponto de vista de A. Assim o backtracking automtico continua ativo independentemente do cut na clusula usada para satisfazer o objetivo C.
6.3 APLICAES DO CUT
Apresenta-se nesta seo alguns exemplos de pequenas aplicaes empregando o operador cut, visando ilustrar o seu uso em programas reais.
6.3.1 MXIMO DE DOIS NMEROS
O procedimento para encontrar o maior de dois nmeros pode ser programado como uma relao
max(X, Y, Max), onde Max=X se X for maior ou igual a Y e Max=Y se este for maior que X. Isto
pode ser escrito em Prolog por meio das seguintes clusulas:
max(X, Y, X) :- X >= Y.
max(X, Y, Y) :- X < Y.
Essas duas regras so mutuamente exclusivas. Se a primeira for bem sucedida, ento a segunda certamente ir falhar e vice-versa. Portanto uma forma mais econmica de representar o mesmo programa
com o uso do cut seria:
max(X, Y, X) :- X >= Y, !.
max(X, Y, Y).
Essa soluo no-determinstica. Se X ocorre vrias vezes, ento qualquer ocorrncia pode ser encontrada. Vamos agora mudar o predicado membro/2, tornando-o um programa determinstico que ir
sempre encontrar a primeira ocorrncia de X. A modificao a fazer simples: apenas evitamos o
backtracking to logo X tenha sido encontrado, o que acontece quando a primeira clusula bemsucedida. O programa modificado fica:
membro(X, [X|_]) :- !.
membro(X, [_|Y]) :membro(X, Y).
mais fcil inserir X como primeiro elemento, de modo que X se torna a cabea de L1 quando se
verifica a sua ausncia na lista. Em Prolog escreve-se:
adicionar(X, L, L) :membro(X, L), !.
adicionar(X, L, L1).
Esse exemplo instrutivo, porque no possvel programar facilmente a "adio sem duplicatas" sem
o uso do cut ou de alguma outra construo dele derivada. Portanto o cut necessrio aqui para especificar a relao correta e no somente para incrementar a eficincia do programa.
6.3.4 IF-THEN-ELSE
A programao procedimental estruturada pode ser simulada em Prolog. Neste exemplo a estrutura ifthen-else descrita atravs da relao:
ifThenElse(X, Y, Z)
que deve ser interpretada da seguinte maneira: "Se X for verdadeiro, ento execute Y, seno execute
Z". O programa Prolog :
ifThenElse(X, Y, _) X, !, Y.
ifThenElse(_, _, Z) Z.
Deve-se notar que este programa emprega meta-variveis (variveis que podem ser instanciadas com
chamadas a predicados), o que no diretamente representado em todas as implementaes Prolog.
Em sendo possvel, o exemplo abaixo ilustra a utilizao de tal programa:
?-ifThenElse(X, Y is Z+1, Y is 0).
Aqui, se o predicado representado por X for verdadeiro, a varivel Y ser instanciada com "Z+1",
caso contrrio, se X for falso, Y ser instanciada com o valor zero.
O emprego de meta-variveis requer alguma experincia em programao em lgica. Tal recurso no
deve ser aplicado indiscriminadamente sob pena de comprometer a legibilidade e o perfeito entendimento do programa. Por outro lado, se bem utilizado, um recurso muito poderoso nas mos de um
bom programador.
6.4 NEGAO POR FALHA
"Maria gosta de todos os animais, menos de cobras". Como podemos dizer isto em Prolog? fcil
expressar uma parte dessa declarao: Maria gosta de X se X um animal, isto :
gosta(maria, X) animal(X).
mas necessrio ainda excluir as cobras. Isto pode ser conseguido empregando-se uma formulao
diferente:
Se X uma cobra,
ento no verdade que maria gosta de X
seno se X um animal, ento maria gosta de X.
Podemos dizer que alguma coisa no verdadeira em Prolog por meio de um predicado pr-definido
57
especial, "fail", que sempre falha, forando o objetivo pai a falhar. A formulao acima pode ser dada
em Prolog com o uso do fail da seguinte maneira:
gosta(maria, X) :cobra(X), !, fail.
gosta(maria, X) :animal(X).
Aqui a primeira regra se encarrega das cobras. Se X uma cobra, ento o cut evita o backtracking
(assim excluindo a segunda regra) e o fail ir ocasionar a falha da clusula. As duas regras podem ser
escritas de modo mais compacto como uma nica clusula, por meio do uso do conetivo ";":
gosta(maria, X) :cobra(X), !, fail;
animal(X).
Pode-se usar essa mesma idia para definir a relao diferente(X, Y) que, se for verdadeira, significa
que X e Y no unificam. Isto pode ser formulado por:
Se X e Y unificam
ento diferente(X, Y) falha
seno diferente(X, Y) bem-sucedido.
Em Prolog:
diferente(X, X) :- !, fail.
diferente(X, Y).
onde "true" um objetivo pr-definido que sempre bem-sucedido. Esses exemplos indicam que seria
til dispor de um objetivo unrio "not" tal que not(Objetivo) seja verdadeiro se Objetivo no for verdadeiro. A relao not/1 pode ser definida da seguinte maneira:
Se Objetivo bem-sucedido
ento not(Objetivo) falha
seno not(Objetivo) bem-sucedido.
A relao not/1 pr definida na maioria das implementaes Prolog e se comporta como o procedimento apresentado acima. Vamos assumir ainda, como ocorre na maioria das vezes, que o not seja
definido como um operador prefixo, de modo que podemos escrever not(cobra(X)) como not cobra(X).
Deve ser notado que a relao not/1, definida como negao por falha, como foi feito, no corresponde exatamente negao da lgica matemtica. Essa diferena pode ocasionar um comportamento
inesperado do programa, se o not for usado sem cuidado. Apesar disso, o not um instrumento muito
til e pode ser utilizado com vantagem no lugar do cut. Os dois exemplos dados anteriormente poderiam ser escritos com o uso do not da seguinte maneira:
gosta(maria, X) :animal(X), not cobra(X).
diferente(X, Y) :not (X = Y).
que certamente so formulaes melhores que as anteriores. So mais naturais e mais fceis de ler.
6.5 CUIDADOS COM O CUT E A NEGAO
As vantagens e desvantagens do uso do cut foram ilustradas por meio de exemplos nas sees anteriores. Vamos resumir primeiro as vantagens:
58
Por meio do cut podemos freqentemente aumentar a eficincia dos programas Prolog. A idia
dizer explicitamente ao sistema: "No tente outras alternativas pois elas esto destinadas a
falhar".
a resposta ser possivelmente "sim", entretanto isso no deve ser entendido como se o Prolog estivesse dizendo que "joo no humano", mas na verdade que "no h informao suficiente no programa
que permita provar que joo humano". Isso acontece porque no processamento do objetivo not/1 o
Prolog no tenta prov-lo diretamente. Ao invs disso ele tenta provar o oposto e, se o oposto no
pode ser provado, ento ele assume que o objetivo not /1 bem-sucedido.
Tal raciocnio baseado na denominada "Hiptese do Mundo Fechado". Segundo tal hiptese, o
mundo fechado no sentido que "tudo o que existe est no programa ou pode ser dele derivado". Assim, se alguma coisa no est no programa (ou no pode ser dele derivada), ento no verdadeira e
consequentemente a sua negao verdadeira. Isso demanda cuidados especiais por parte do programador, uma vez que normalmente no se assume que "o mundo fechado", isto , no colocando
explicitamente a clusula humano(joo), no se estava querendo dizer que "joo no humano".
Finalmente, considere o seguinte programa:
59
r(a).
q(b).
p(X) :- not r(X).
o sistema Prolog responder X=b, entretanto se a mesma consulta fosse formulada de modo "aparentemente" equivalente:
?-p(X), q(X).
a resposta seria "no". Convidamos o leitor a estabelecer o "trace" do programa de modo a entender
porque obtivemos respostas diferentes. A diferena chave entre as duas consultas reside no fato de
que, no primeiro caso., a varivel X j est instanciada quando p(X) executado, o que no ocorre no
segundo caso.
RESUMO
O uso do cut evita o backtracking. Esse recurso empregado tanto para aumentar a eficincia
A eficincia aumentada dizendo explicitamente ao Prolog, por meio do cut, para no explorar
Por meio do cut possvel formular concluses mutuamente exclusivas por meio de regras da
forma:
O cut torna possvel introduzir a "negao por falha": not X definido em funo da falha de
X;
Dois predicados especiais pr-definidos so de grande utilidade em certos casos: o true/0 que
H alguma reserva quanto ao uso do cut. Sua insero em um programa pode destruir a corres-
O operador unrio not define uma forma particular de negao denominada "negao por fa-
lha", que no corresponde exatamente negao da lgica matemtica, de modo que o seu uso
tambm requer cuidados especiais.
EXERCCIOS
6.2 A seguinte relao classifica nmeros em trs classes: positivo, nulo ou negativo:
classe(N, positivo) :- N > 0.
classe(0, nulo).
classe(N, negativo) :- N < 0.
60
usando cuts.
que reparte uma lista de nmeros em duas listas: uma de nmeros positivos (incluindo o zero) e
outra de nmeros negativos. Por exemplo:
reparte([3,-1,0,5,-2], [3,0,5], [-1,-2]).
Proponha duas verses: uma com um nico cut e outra sem nenhum.
6.4 Defina o predicado:
unificvel(Lista1, Termo, Lista2)
onde Lista2 a lista de todos os elementos de Lista1 que unificam com Termo, deixando-os no
instanciados na resposta. Por exemplo:
?-unificvel([X, b, t(Y)], t(a), Lista).
Lista=[X, t(Y)]
Note que X e Y devem permanecer no-instanciados, apesar de a unificao com t(a) causar sua
instanciao. Dica: use not (Termo1=Termo2). Se Termo1=Termo2 for bem-sucedido, ento not
(Termo1=Termo2) falha e a instanciao realizada desfeita.
61
7. ESTRUTURAS DE DADOS
A possibilidade de empregar em Prolog estruturas de dados com unificao, backtracking e aritmtica
tornam essa linguagem de programao extremamente poderosa. No presente captulo estudaremos
estruturas de dados complexas por meio de exemplos de programas: recuperao de informao estruturada em uma base de dados, a simulao de um autmato no-determinstico e um planejamento
de roteiros de viagens. Tambm se introduzir o conceito de abstrao de dados em Prolog.
7.1 RECUPERAO DE INFORMAES
O exerccio apresentado a seguir desenvolve a habilidade de representar e estruturar objetos de dados
e tambm ilustra a viso do Prolog como uma linguagem natural de consulta a bases de dados. Considere a figura 7.1.
famlia
Pl
p essoa
Ari
p essoa
Pl
data
Ana
trab
p essoa
Pl
data
Ada
trab
Pl
data
nt
17
ibn
06
rbz
18
05
1500
11
1100
02
65
68
91
Uma base de dados pode ser naturalmente representada em Prolog como um conjunto de fatos. Por
exemplo, uma base de dados sobre famlias pode ser representada de modo que cada famlia seja descrita como um termo. A Figura 7.1 mostra como a informao sobre cada famlia pode ser estruturada
em um termo famlia/3, com a seguinte forma:
famlia(Pai, Me, Filhos)
onde Pai e Me so pessoas e Filhos uma lista de pessoas. Cada pessoa , por sua vez, representada
por uma estrutura com quatro componentes: nome, sobrenome, data de nascimento e trabalho. A data
de nascimento fornecida como um termo estruturado data(Dia, Mes, Ano). O trabalho, ou fornecido por um termo trab(Empresa, Salrio), ou pela constante nt, indicando que a pessoa em questo no
trabalha. A famlia exemplificada pode ento ser armazenada na base de dados como uma clusula do
tipo:
famlia(pessoa(ari, pl, data(17,05,65), trab(ibn,1500)),
pessoa(ana, pl, data(06,11,68), trab(rbs,1100)),
[pessoa(ada, pl, data(18,02,91), nt)] )
A base de dados poderia ser vista ento como uma seqncia de fatos, descrevendo todas as famlias
que interessam ao programa. A linguagem Prolog , na verdade, muito adequada para a recuperao
da informao desejada a partir de uma base de dados. Um detalhe muito interessante que os objetos
desejados no precisam ser completamente especificados. Pode-se simplesmente indicar a estrutura
62
dos objetos que interessam e deixar os componentes particulares apenas indicados. Por exemplo, se
queremos recuperar todas as famlias "Oliveira", basta especificar:
?-famlia(pessoa(_, oliveira, _, _), _, _).
As possibilidades de consulta so as mais diversas. Com esses exemplos queremos demonstrar que
possvel especificar os objetos de interesse, no pelo seu contedo, mas sim pela sua estrutura, sobre a
qual restringimos os componentes conforme nossas necessidades e/ou disponibilidades, deixando os
demais indefinidos. Na Figura 7.2 apresentado um programa demonstrando algumas das relaes
que podem ser estabelecidas em funo de uma base de dados estruturada na forma definida por famlia/3:
pai(X) :famlia(X, _, _).
me(X) :famlia(_, X, _).
filho(X) :famlia(_, _, Filhos),
membro(X, Filhos).
membro(X, [X|_]).
membro(X, [_|Y]) :membro(X, Y).
existe(Pessoa) :pai(Pessoa);
me(Pessoa);
filho(Pessoa).
nasceu(pessoa(_, _, Data, _), Data).
salrio(pessoa(_, _, _, trab(_,S)), S).
salrio(pessoa(_, _, _, nt), 0).
Algumas aplicaes para os procedimentos mostrados na figura acima podem ser encontrados nas
seguintes consultas base de dados:
Achar o nome e sobrenome de todas as pessoas existentes na base de dados:
?-existe(pessoa(Nome, Sobrenome, _, _)).
Achar as pessoas nascidas aps 1965 cujo salrio maior do que 5000:
?- existe(Pessoa),
nasceu(Pessoa, data(_,_,A)),
A > 65,
salrio(Pessoa, Salrio),
Salrio > 5000.
Para calcular o total da renda familiar, pode ser til definir a soma dos salrios de uma lista de pessoas como uma relao de dois argumentos:
63
total(L, T)
Esta relao nos permite interrogar a base de dados para saber a renda familiar de cada famlia:
?-famlia(Pai, Me, FIlhos), total([Pai, Me | Filhos], RFam).
Outro objeto do qual podemos selecionar componentes pessoa/4. Alguns exemplos so:
empresa(pessoa(_, _, _, trab(Empr,_)), Empr).
sobrenome(pessoa(_, Sobrenome, _, _), Sobrenome).
Uma vez que as relaes seletoras estejam definidas, o usurio pode esquecer a forma particular usada
na representao de sua estrutura original. Para criar e manipular tal informao necessrio somente
conhecer os nomes das relaes seletoras e empregar tais nomes ao longo do programa. No caso de
representaes complicadas, isso muito mais simples do que usar a representao original de modo
implcito. No exemplo da relao famlia/3, o usurio no precisa saber que os filhos so representados por uma lista.
O uso de relaes seletoras tambm torna os programas mais fceis de modificar. Suponha que fosse
desejado aumentar a eficincia de um programa, mudando a forma de representar sua informao.
Tudo que necessrio fazer mudar as definies das relaes seletoras e o restante do programa
funcionar sem qualquer alterao com a nova representao.
64
s1
s2
a
nulo
b
nulo
s4
s3
No exemplo ali apresentado, s1, s2, s3 e s4 so os "estados" do autmato. A partir do estado inicia, s1
no exemplo dado, o autmato muda de estado para estado medida em que vai lendo o string de entrada. As transies de estado do autmato dependem do smbolo de entrada correntemente lido, conforme indicado pelas legendas dos arcos no grafo de transio.
Uma transio ocorre toda vez que um smbolo do string de entrada lido. Note que a transio, como
representada na Figura 7.3 no-determinstica. Se o autmato estiver em s1, e o smbolo de entrada
"a" , ento a transio pode ser realizada tanto para s1 quanto para s2. Alguns arcos so rotulados
como "nulo" para denotar o "smbolo nulo". Tais arcos correspondem ao que se denomina "movimentos silenciosos" do autmato. Esses so denominados "silenciosos" porque eles ocorrem sem que
haja qualquer leitura de smbolos a partir do string de entrada e o observador, visualizando o autmato
como uma "caixa-preta" no capaz de notar que uma transio de estado ocorreu. O estado s3 representado em negrito para denotar que este um "estado terminal", onde possvel encerrar a ao
do autmato. Dizemos que o autmato "aceitou" o string de entrada se h um caminho de transies
no grafo tal que:
(1) Comea no estado inicial,
(2) Termina no estado final, e
(3) As legendas dos arcos ao longo do caminho de transies correspondem ao string de entrada.
Fica inteiramente a critrio do autmato decidir quais das possveis transies sero executadas num
dado instante. Em particular, o autmato pode escolher entre realizar ou no um movimento silencioso, se este for possvel a partir do estado corrente. Os autmatos abstratos no-determinsticos desse
tipo possuem ainda uma propriedade "mgica": se h possibilidade de uma escolha ocorrer, esta
feita do modo "correto", isto , de um modo que conduza aceitao do string de entrada, se tal modo
existir. O autmato da Figura 7.3 ir, por exemplo, aceitar os strings "ab" e "aabaab", mas ir rejeitar
65
os strings "abb" e "abba". fcil demonstrar que o autmato aceita qualquer string que termina em
"ab" e rejeita todos os demais. Autmatos como esse podem ser descritos por meio de trs relaes:
(1) Uma relao unria, final/1, que define os estados finais do autmato;
(2) Uma relao de trs argumentos, trans/3, que define as transies de estado de forma que
trans(S1, X, S2)
significa que uma transio do estado S1 para o estado S2 possvel quando o smbolo de entrada X for lido;
(3) Uma relao binria, silncio(S1, S2), significando que um "movimento silencioso" possvel
de S1 para S2.
Para o autmato apresentado na Figura 7.3 essas trs relaes podem ser formuladas em Prolog da
seguinte maneira:
final(s3).
trans(s1,
trans(s1,
trans(s1,
trans(s2,
trans(s3,
a,
a,
b,
b,
b,
s1).
s2).
s1).
s3).
s4).
silncio(s2, s4).
silncio(s3, s1).
Representaremos os strings de entrada como listas, de modo que o string "aab" ser representado por
[a, a, b]. Dada a descrio do autmato, o simulador processar um determinado string de entrada e
decidir se este deve ser aceito ou rejeitado. Por definio, os autmatos no-determinsticos aceitam
um dado string se, partindo de um estado inicial, aps ter lido o string completo o autmato pode estar
em seu estado final. O simulador programado por meio de uma relao binria, aceita/2, que define
a aceitao de um determinado string a partir de um estado inicial. Assim a relao
aceita(Estado, String).
verdadeira se o autmato, a partir do de um estado inicial "Estado", aceita o string "String". A relao aceita/2 pode ser definida por meio de trs clusulas, que correspondem aos trs casos seguintes:
(1) O string vazio, [], aceito a partir de um determinado estado S se S um estado final;
(2) Um string no-vazio aceito a partir de um estado S, se a leitura do primeiro smbolo no string
pode conduzir o autmato a algum estado S1 e o resto do string aceito a partir de S1;
(3) Um string aceito a partir de um estado S, se o autmato pode realizar um movimento silencioso de S para S1, e ento aceitar o string completo a partir de S1.
Esses trs casos originam as seguintes clusulas:
aceita(S, []) :final(S).
aceita(S, [X | R]) :trans(S, X, S1), aceita(S1, R).
aceita(S, L) :silncio(S, S1), aceita(S1, L).
Por meio dessa relao possvel perguntar se um determinado string aceito pelo autmato. Por
exemplo:
?-aceita(s1, [a, a, a, b]).
sim
Como foi visto anteriormente, os programas Prolog so frequentemente capazes de solucionar problemas mais gerais do que aqueles para os quais foram originalmente concebidos. No presente caso,
podemos por exemplo perguntar ao simulador a partir de quais estados ele aceitaria um determinado
string:
66
Outra possibilidade seria perguntar quais so os strings de trs smbolos que so aceitos pelo autmato a partir de um determinado estado:
?-aceita(s1, [X, Y, Z]).
X=a Y=a Z=b;
X=b Y=a Z=b;
no
possvel ainda realizar diversos outros experimentos envolvendo questes ainda mais gerais, como
por exemplo: "a partir de que estados o autmato aceitar strings de tamanho sete?", etc. Experimentos ainda mais complexos podem inclusive requerer modificaes na estrutura do autmato, mudando
as relaes final/1, trans/3 e silncio/2.
7.4 PLANEJAMENTO DE ROTEIROS AREOS
Na presente seo iremos construir um programa para auxiliar o planejamento de roteiros areos.
Apesar de bastante simples, o programa ser capaz de responder questes tais como:
Em que dias da semana h vos entre o Rio e Munique?
Como se pode chegar a Tquio partindo de Porto Alegre numa tera-feira?
Tenho que visitar Montevidu, Buenos Aires e Assuno, partindo de Braslia numa tera-feira
noite e chegando ao Rio na sexta-feira para o fim-de-semana. Em que seqncia deve ser realizada a viagem de forma que eu no tenha de fazer mais de um vo por dia?
O programa ser desenvolvido em funo de uma base de dados possuindo informaes sobre os vos,
representada por meio de uma relao com trs argumentos:
horrio(Cidade1, Cidade2, ListaDeVos).
Os horrios so representados como objetos estruturados com dois componentes, horas e minutos,
separados por ":". O problema principal ser encontrar uma rota exata entre duas cidades, partindo em
um determinado dia da semana. Isso ser programado como uma relao de quatro argumentos:
rota(Cidade1, Cidade2, Dia, Rota)
onde Rota uma seqncia de vos que satisfaz aos seguintes critrios:
(1) O ponto de partida da Rota Cidade1;
(2) O ponto de chegada da Rota Cidade2;
(3) Todos os vos so no mesmo dia Dia;
(4) Todos os vos em Rota esto na relao horrio/3;
(5) H tempo suficiente para as transferncias de vo.
A rota representada por uma lista de termos estruturados na forma:
De-Para : CdVo : Partida
67
um estado final. O planejador de vo encontra uma rota entre a cidade de partida e a cidade
destino da viagem.
No portanto surpresa que a relao rota/4 possa ser definida de maneira semelhante relao aceita/2. Uma vez que agora no h "movimentos silenciosos", devemos nos concentrar em dois casos:
(1) Vo Direto: Se h um vo direto entre as cidades C1 e C2, ento a rota consiste em um nico
vo:
rota(C1, C2, Dia, [C1-C2:CodVo:Partida]) :vo(C1,C2,Dia,CodVo,Partida,Chegada).
(2) Vo com Conexes: A rota entre C1 e C2 consiste em: primeiro um vo de C1 para alguma cidade intermediria, C3, seguida por uma rota entre C3 e C2. Alm disso, deve haver tempo suficiente entre a chegada de um vo e a partida do seguinte para a transferncia de avio:
rota(C1,C2,Dia,[C1-C3:CodVo1:Partida1 | Rota]) :rota(C2, C3, Dia, Rota),
vo(C1, C3, Dia, CodVo1, Partida1, Chegada1),
partida(Rota, Partida2),
transferncia(Chegada1, Partida2).
68
RESUMO
Uma base de dados pode ser naturalmente representada em Prolog como um conjunto de fatos;
Os mecanismos de consulta e unificao do Prolog podem ser usados com grande flexibilidade
na recuperao de informao estruturada em uma base de dados. Adicionalmente, procedimentos utilitrios podem ser facilmente desenvolvidos para melhorar a comunicao com a
base de dados;
O conceito de abstrao de dados pode ser visto como uma tcnica de programao que facilita
o uso de estruturas de dados muito complexas e contribui para a legibilidade dos programas.
muito natural para a linguagem Prolog lidar com os princpios bsicos da abstrao de dados;
O mesmo problema pode muitas vezes ser abordado de diversas maneiras distintas, pela varia-
Muitas vezes o passo chave para a soluo de um problema a generalizao desse problema.
7.1 Escreva as consultas necessrias para extrair as seguintes informaes da base de dados "famlia":
(a) As famlias que no tem filhos;
69
70
8. ENTRADA E SADA
Neste captulo estudaremos alguns recursos embutidos, presentes na maioria das implementaes
Prolog, destinados leitura e gravao de dados em arquivos. Tais recursos podem tambm ser empregados pelo usurio para a formatao de objetos no programa, de modo a atingir alguma representao externa desejada para tais objetos. Tambm introduziremos os recursos para a leitura de programas e para a construo e decomposio de tomos e termos.
8.1 ARQUIVOS DE DADOS
O mtodo de comunicao entre o usurio e o programa que estivemos usando at agora consiste em
consultas realizadas pelo usurio que so respondidas pelo programa por meio de instanciaes de
variveis. Esse mtodo simples e prtico e, apesar de sua simplicidade, suficiente para obter a
entrada e sada de informaes. Muitas vezes, entretanto, tal mtodo no suficientemente adequado
tendo em vista a sua rigidez. Extenses a esse mtodo bsico tornam-se necessrias nos seguintes
casos:
Entrada de dados sob forma diferente das consultas, por exemplo, sob a forma de sentenas em
linguagem natural,
do usurio.
Arquivo
Arquivo
1
Fontes de
Entrada
3
Programa
Prolog
Fontes de
Sada
Arquivo
Arquivo
A qualquer momento da execuo de um programa Prolog, somente dois arquivos esto ativos: um
para entrada e outro para sada. Esses dois arquivos se denominam respectivamente "fonte de entrada
corrente" e "fonte de sada corrente.. No incio da execuo essas duas fontes correspondem ao terminal do usurio. A fonte de entrada corrente pode ser mudada a qualquer momento para um outro arquivo qualquer, digamos "novoArqEnt", por meio do objetivo:
see(novoArqEnt).
Esse objetivo sempre bem sucedido (a menos que haja alguma coisa errada com NovoArqEnt. Um
exemplo tpico de utilizao do predicado see/1 a seguinte seqncia de objetivos, que l alguma
coisa de um certo arquivo, "arq1", e ento retorna ao terminal do usurio:
...
see(arq1).
l_do_arquivo(Informao).
see(user).
...
A fonte de sada corrente pode tambm ser mudada por um objetivo da forma:
tell(novoArqSai).
Uma seqncia de objetivos para enviar alguma informao para "arq3" e depois redirecionar a sada
para o terminal do usurio poderia ser:
...
tell(arq3).
grava_no_arquivo(Informao).
tell(user).
...
Dois outros predicados pr-definidos que devem ser mencionados aqui so seen/0 e told/0, cujo efeito
fechar os arquivos correntes de entrada e sada respectivamente.
Os arquivos podem ser processados somente na forma sequencial. nesse sentido, todos os arquivos se
comportam da mesma maneira que o terminal do usurio. Cada requisio para a leitura de alguma
coisa a partir de alguma fonte de entrada ir ocasionar a leitura a partir da posio corrente dessa
fonte de entrada. Aps a leitura, a posio corrente dessa fonte de entrada ser, naturalmente, o prximo item que ainda no foi lido, de forma que uma nova requisio de leitura ir iniciar a ser executada iniciando nessa nova posio corrente. Se uma requisio de leitura feita para o fim do arquivo,
ento a informao devolvida ser a constante "end_of_file", indicandio que o fim do arquivo foi
atingido. Uma vez que alguma informao foi lida, no possvel l-la novamente a menos que se
retome a leitura do arquivo a partir do incio.
A sada de informaes ocorre de maneira similar. Cada requisio de sada ir adicionar a informao requisitada no final da fonte de sada corrente. Da mesma forma que na leitura, no possvel
retornar e reescrever sobre a poro do arquivo que j foi escrita.
Todos os arquivos so do tipo "texto", isto , arquivos de caracteres. Os caracteres podem ser letras,
dgitos, ou de algum tipo especial. Alguns desses ltimos so ditos ser "no-imprimveis" porque
quando so direcionados para o terminal do usurio eles no aparecem no vdeo. Podem, no entanto,
possuir algum outro efeito como o espaamento entre colunas e linhas, reposicionamento do cursor,
etc.
H duas maneiras diferentes de se utilizar os arquivos em Prolog, dependendo da forma que se deseja
empregar para os dados. A primeira delas considera o caracter como o elemento bsico do arquivo.
Assim uma requisio de entrada ou sada ocasionar a leitura ou escrita de um nico caracter. Os
predicados pr-definidos para tratar essa modalidade de arquivo so get/1, get0/1 e put/1.
A outra forma de utilizar arquivos em Prolog considerar unidades maiores de informao como elementos bsicos de entrada e sada. Tais unidades so os termos Prolog. Assim, cada requisio de
72
entrada ou sada desse tipo ir ocasionar a transferncia de um termo inteiro. Os predicados que executam a transferncia de termos so read/1 e write/1. Naturalmente, nesse caso, a informao dever
se encontrar numa forma que seja consistente com a sintaxe dos termos Prolog.
O tipo de organizao a ser escolhido para um determinado arquivo depende naturalmente do problema que se est tentando resolver, entretanto, sempre que a especificao do problema permitir, iremos
preferir trabalhar com arquivos de termos, que permitem a transferncia de uma unidade significativa
completa atravs de uma nica requisio. Por outro lado, h problemas cuja natureza determina o
emprego de alguma outra organizao. Um exemplo o processamento de sentenas em linguagem
natural para, digamos, estabelecer um dilogo com o usurio. Em tais casos os arquivos devero ser
vistos como seqncias de caracteres, uma vez que a linguagem natural no pode, normalmente, ser
reduzida para a forma de termos.
8.2 PROCESSAMENTO DE ARQUIVOS DE TERMOS
8.2.1 READ & WRITE
O predicado pr-definido read/1 usado para a leitura de termos a partir da fonte de entrada corrente.
O objetivo
read(X)
ir ocasionar a leitura do prximo termo T que ser unificado com X. Se X uma varivel, ento,
como resultado da leitura, X ser instanciada com T. Se a unificao no for possvel, ento o objetivo read(X) ir falhar. O predicado read/1 determinstico, significando que, em caso de falha, no
haver backtracking para a leitura de outro termo. cada termo, no arquivo de entrada, deve ser seguido
por um ponto e um espao ou "carriage-return". Se read(X) executado sobre o final do arquivo de
entrada, ento a varivel X ser instanciada com o termo "end_of_file".
O predicado pr-definido write/1 fornece a sada de um termo. Assim o objetivo write(X) ir ocasionar a escrita do termo X sobre a fonte de entrada corrente. X ser escrito com a mesma forma sinttica
padro utilizada pelo Prolog na apresentao de termos. Um recurso muito til do Prolog que o predicado write/1 "sabe" apresentar qualquer termo, independente de sua complexidade.
H ainda dois predicados adicionais para a formatao da sada. Eles so usados para inserir espaos e
linhas na fonte de sada. O objetivo tab(N) ir ocasionar a sada de "N" espaos. O predicado nl/0
(sem argumentos) ir ocasionar o incio de uma nova linha. os seguintes exemplos ilustram o uso dos
procedimentos estudados. Vamos assumir que temos um procedimento que computa o cubo de um
nmero dado:
cubo(N, C) :- C is N*N*N.
Suponha que desejamos empreg-lo para calcular os cubos de uma seqncia de nmeros. Isso pode
ser obtido por meio de uma seqncia de questes:
?-cubo(2, X).
X=8
?-cubo(5, Y).
Y=125
?-cubo(12, Z).
Z=1728
Aqui, para cada nmero necessrio formular um objetivo completo. Vamos agora modificar o programa de forma a "interiorizar" a ao, tornando mais suave o interface com o usurio. O programa
agora ir manter-se lendo um nmero e apresentando o seu cubo at que a constante "fim" seja lida da
fonte de entrada.
cubo :read(X), processa(X).
73
processa(fim) :- !.
processa(N) :C is N*N*N,
write(C),
cubo.
Esse um programa cujo significado declarativo difcil de formular, entretanto, a sua interpretao
operacional direta: "Para executar cubo/0, primeiro leia X e depois processe-o. Se X=fim, ento,
tudo j foi feito. Seno, calcule o cubo de X, escreva-o e chame recursivamente o procedimento
cubo/0 para o processamento de mais valores. Por exemplo:
?-cubo.
2.
8
5.
25
12.
1728
fim.
sim
escreveLista([]).
escreveLista([X | L]) :write(X), nl, escreveLista(L).
Se tivermos uma lista de listas, uma forma natural de sada escrever os elementos de cada lista em
uma linha. Um exemplo :
?-escreveLista2([[a, b, c], [d, e, f], [g, h, i]]).
a b c
d e f
g h i
sim
Uma lista de nmeros inteiros pode algumas vezes ser convenientemente apresentada sob a forma de
um grfico de barras. O procedimento barras(L) ir escrever uma lista nessa forma. Um exemplo do
seu uso seria:
?-barras([6, 7, 9, 12]).
sim
escreveFam(famlia(Pai, Me, Filhos)) :nl, nl, write('Pais:'), nl,
escrevePes(Pai), nl, escrevePes(Me), nl,
write('Filhos:'), nl,
escrevePesList(Filhos).
escrevePes(pessoa(Nome, SNome, dat(D,M,A), Trab)) :tab(10), write(Nome), tab(1), write(SNome),
write(', nasc: '),
write(D), write('/'), write(M), write('/'), write(A),
write(','), escreveTrab(Trab).
escrevePesList([]).
escrevePesList([P | L]) :escrevePes(P), nl, escrevePesList(L).
escreveTrab(nt) :write('no trabalha').
escreveTrab(trab(Emp, Sal)) :write('trab: '), write(Emp), write(', '),
write('sal: '), write(Sal).
O procedimento barras/1 pode ser definido da seguinte maneira, assumindo que a representao ' '
seja vlida no Prolog utilizado:
barras([]).
barras([N | L]) :quadrinho(N), nl, barras(L).
quadrinho(N) :N>0,
write(' '), N1 is N-1,quadrinho(N1).
quadrinho(N) :N=<0, !.
75
O termo acima contm, sem dvida, toda a informao, entretanto sob uma forma bastante confusa,
tornando difcil seguir as partes da informao que formam as unidades semnticas. Iramos, certamente, preferir que a informao fosse apresentada de outra maneira, por exemplo, na forma abaixo:
Pais:
ari pl, nasc: 16/05/65, trab: ibn, sal: 1500
ana pl, nasc: 06/11/68, trab: rbz, sal: 1100
Filhos:
ada pl, nasc: 18/02/91, no trabalha.
Tal formato pode ser obtido por meio do procedimento escreveFam/1 mostrado na Figura 8.2.
8.2.4 PROCESSAMENTO DE ARQUIVOS DE TERMOS
Uma tpica seqncia de objetivos para processar completamente um arquivo "A" se pareceria com o
seguinte:
... see(A), processaArq, see(user), ...
Aqui processaArq/0 um procedimento para ler e processar cada termo em A, um aps o outro, at
que o fim do arquivo seja encontrado. Um esquema tpico para processaArq o seguinte:
processaArq :read(Termo), processa(Termo).
processa(end_of_file) :- !.
processa(Termo) :trata(Termo), processaArq.
Aqui o procedimento trata/1 representa qualquer coisa que se deseje fazer com cada um dos termos
presentes no arquivo. Um exemplo poderia ser um procedimento para apresentar no terminal cada um
dos termos do arquivo, juntamente com o seu respectivo nmero de ordem. Vamos chamar tal procedimento mostraArq(N), onde N um argumento adicional para contar os termos lidos.
mostraArq(N) :read(Termo), mostra(1, Termo).
mostra(_, end_of_file) :- !.
mostra(N, Termo) :write(N), tab(2), write(Termo),
N1 is N+1,
mostraArq(N1).
outro exemplo de utilizao do esquema dado para o processamento de arquivos de termos o seguinte: Temos um arquivo denominado "arq1" que contm termos na forma:
item(Nro, Descrio, Preo, Fornecedor)
Cada termo descreve uma entrada num catlogo de itens. Desejamos produzir um outro arquivo que
contenha somente os itens fornecidos por um determinado fornecedor. Como o fornecedor nesse novo
arquivo ser sempre o mesmo, o seu nome somente precisa ser escrito no incio do arquivo, sendo
omitido nos demais termos. Denominaremos tal procedimento de
fazArq(Fornecedor)
Por exemplo, se o catlogo original armazenado em arq1 e desejamos produzir um arquivo arq2 com
todos os artigos fornecidos por 'Palmeira & Cia", ento usaremos o procedimento fazArq/1 da se76
guinte maneira:
..., see(arq1),tell(arq2),fazArq('Palmeira & Cia'),see(user),tell(user), ...
Note que no programa acima, o predicado processa/2 grava um ponto aps cada termo escrito em
arq2, de modo a possibilitar leituras posteriores desse arquivo por meio do comando read/1.
8.3 PROCESSAMENTO DE CARACTERES
Um caracter escrito na fonte de sada corrente por meio do objetivo:
put(C)
onde C o cdigo ASCII (um nmero entre 0 e 255) do caracter a ser escrito. Por exemplo, a consulta:
?-put(65), put(66), put(67).
produz a sada:
ABC
uma vez que 65 o cdigo ASCII de 'A', 66 de 'B' e 67 de 'C'. Por sua vez um caracter pode ser lido a
partir da fonte de entradacorrente por meio do objetivo:
get0(C)
que ocasiona a leitura do caracter corrente e torna a varivel C instanciada para com o cdigo ASCII
deste caracter. Uma variao do predicado get0/1 o get/1, que utilizado para a leitura apenas de
caracteres imprimveis, saltando sobre todos os caracteres no-imprimveis, particularmente espaos
em branco. Como um exemplo do uso de predicados que transferem caracteres, vamos definir um
procedimento comprime/0 para ler uma sentena da fonte de entrada corrente e apresentar essa sentena reformatada, de forma que mltiplos espaos em branco entre as palavras sejam substitudos por
um nico espao em branco (cdigo ASCII = 32). Para simplificar, vamos assumir que toda sentena
de entrada processada pelo procedimento comprime/0 termina com um ponto final (cdigo ASCII =
46) e que as palavras estejam separadas por um ou mais espaos em branco e nenhum outro caracter.
Uma entrada aceitvel seria:
Genialidade 1% de inspirao e 99% de transpirao.
O procedimento comprime/0 ter uma estrutura similar aos procedimentos para processamento de
arquivos estudados nas sees anteriores. Inicialmente ele vai ler o primeiro caracter e envi-lo sada e ento completar o processo, dependendo do caracter que for lido. A excluso mtua entre as trs
alternativas obtida por meio de cuts:
comprime :get0(C), put(C), continua(C).
77
continua(46) :- !.
continua(32) :!, get(C), put(C), continua(C).
continua(_) :comprime.
Um exemplo do primeiro tipo de aplicao seria a decomposio de tomos em tomos menores, com
tamanho pr-definido. Suponhamos que recebemos, de alguma fonte de entrada, tomos de tamanho
fixo de 13 caracteres, dos quais os oito primeiros correspondem ao CEP, os dois seguintes unidade
da federao (UF) e os trs ltimos sigla internacional de cidade. Por exemplo:
90120040rspoa e 70605220dfbsb
para simplificar, assume-se que cada sentena termina com um ponto final e que no h smbolos de
pontuao na sentena. O programa completo mostrado na Figura 8.4. O procedimento fazFrase/1 l
o caracter corrente, C, e ento transmite esse caracter ao procedimento fazResto para completar o
servio.
fazFrase(Lista) :get0(C), fazResto(C, Lista).
78
fazResto(46, []) :- !.
fazResto(32, Lista) :!, fazFrase(Lista).
fazResto(Let, [Pal | Lista]) :fazLetras(Let, Lets, Prox),
name(Pal, Lets),
fazResto(Prox, Lista).
fazLetras(46, [], 46) :- !.
fazLetras(32, [], 32) :- !.
fazLetras(Let, [Let | Lets], Prox) :get0(C), fazLetras(C, Lets, Prox).
onde:
(1) Let a letra corrente (j lida) da palavra que est sendo processada,
(2) Lets a lista de letras (comeando com Let), at o final da palavra, e
(3) Prox o caracter de entrada que imediatamente segue a palavra lida, podendo ser um branco ou
um ponto.
O programa fazFrase/1 pode ser usado para o processamento de textos em linguagem natural. As sentenas representadas como listas de palavras encontram-se em uma forma adequada para processamento adicional em Prolog. Um exemplo simples seria o tratamento de certas palavras do texto. Uma
tarefa muito mais difcil seria "entender" a sentena, isto , extrair dela o seu significado, representado por algum formalismo. Esta uma importante rea de pesquisa em inteligncia artificial.
8.5 LEITURA DE PROGRAMAS
possvel carregar programas no sistema Prolog por meio de dois predicados pr-definidos: consult/1
e reconsult/1. Diz-se ao Prolog para ler um programa que esteja contido em um arquivo "programa.log" da seguinte maneira:
?-consult('programa.log').
cujo efeito a leitura de todas as clusulas em programa.log de modo que estas possam ser usadas
pelo sistema para responder as consultas que se seguirem. Se um outro arquivo for "consultado" durante a mesma seo, as clusulas presentes nesse novo arquivo sero simplesmente adicionadas ao
final do conjunto de clusulas corrente. No necessrio, entretanto, gravar nosso programa em um
arquivo para depois carreg-lo no sistema. Ao invs de ler um arquivo o Prolog pode tambm aceitar o
nosso programa diretamente do terminal, que corresponde ao pseudo-arquivo "user". Obtemos isso
por meio de:
?-consult(user).
ter o mesmo efeito de consult com uma exceo: se houver clusulas em "programa" sobre alguma
relao j definida no sistema, a definio anterior ser substituda pelas novas clusulas presentes em
"programa". A diferena entre consult/1 e reconsult/1 que o primeiro sempre adiciona as novas clusulas, ao passo que o segundo redefine as relaes previamente definidas, sem afetar, entretanto, as
relaes para as quais no existem clusulas em "programa".
RESUMO
Entradas e sadas (alm das efetuadas em consultas ao programa) so executadas por meio de
predicados pr-definidos;
Os arquivos so sequenciais. H uma fonte de entrada corrente e uma fonte de sada corrente. O
Os arquivos so lidos ou gravados de dois modos diferentes: como uma seqncia de caracteres
O procedimento name(tomo, Lista) decompe e constri tomos. Lista a lista dos cdigos
EXERCCIOS
8.1 Seja arq um arquivo de termos. Defina um procedimento achaTermo(Termo) que apresenta no
terminal do usurio o primeiro termo em arq que unifica com Termo.
8.2 Seja arq um arquivo de termos. Escreva um procedimento achaTodos(Termo) que apresenta no
terminal todos os termos em arq que unificam com Termo.
8.3 Defina a relao comeaCom(tomo, Caracter), para verificar se tomo inicia com o caracter
Caracter.
8.4 Escreva um procedimento acha(PalavraChave, Sentena) que ir, a cada vez que for chamado,
localizar uma sentena na fonte de entrada corrente que contenha a palavra chave dada. A sen80
tena deve ser fornecida em sua forma original, representada como uma seqncia de caracteres
ou como um tomo. O programa fazFrase/2 apresentado neste captulo pode ser adequadamente
modificado para atender as necessidades deste exerccio.
8.5 Escreva um programa relatrio/0 para ler um arquivo de termos na forma cliente(Nome, Endereo, Telefone) e produzir um relatrio formatado da seguinte maneira:
NRO
001
002
.....
CLIENTE
XXX...
XXX...
.....
ENDEREO TELEFONE
XXX...
XXX....
XXX...
XXX...
.....
.....
8.6 Escreva um programa, plural(Palavra, Plural), para a formao do plural de palavras em portugues. Crie para isso uma base de regras de formao do plural de palavras. O resultado esperado
, por exemplo:
?-plural(pssaro, X).
X=pssaros
81
9. PREDICADOS EXTRALGICOS
Todas as implementaes Prolog oferecem, em maior ou menor quantidade, um certo nmero de predicados pr-definidos orientados a execuo de rotinas que, ou so necessrias com muita freqncia,
ou so de difcil programao, ou se destinam a um domnio particular realado pela implementao,
ou por todas essas razes em diferentes propores. No presente captulo se introduz alguns desses
predicados, que facilitam muito a construo de programas interativos e orientados a aplicaes concretas.
9.1 TIPOS DE TERMOS
Os termos Prolog podem assumir os mais diversos aspectos, desde simples constantes at estruturas
complexas altamente elaboradas. Se um termo uma varivel, ento esta pode ou no estar instanciada em algum momento da execuo do programa. Alm disso, se estiver instanciada, seu valor pode
ser uma constante, uma estrutura, etc. Algumas vezes pode ser de utilidade para o programador identificar de que tipo esse valor. Por exemplo, podemos querer adicionar os valores de duas variveis, X
e Y, por meio de:
Z is X + Y
Antes desse objetivo ser executado, X e Y devem ser instanciados com valores inteiros. Se no h
certeza de que tal instanciao ocorreu, ento deve-se fazer tal verificao antes de executar a oprerao aritmtica envolvida.
Com essa finalidade podemos utilizar o predicado pr-definido integer(X), que verdadeiro se X estiver instanciada com um varlor inteiro. O objetivo de adicionar X e Y ento pode ser protegido da
seguinte maneira, garantindo a validade dos operandos:
..., integer(X), integer(Y), Z is X + Y, ...
`Se X e Y no estiverem ambas instanciadas com valores inteiros, ento a operao aritmtica que se
segue ao teste no ser realizada. Os predicados pr-definidos para a classificao de dados comuns a
maioria das implementaes so os seguintes:
Predicado
Descrio
atom(X)
integer(X)
float(X)
number(X)
string(X)
atomic(X)
var(X)
nonvar(X)
82
?-X=1, classifica(X).
Tipo Atmico
---> Numero Inteiro
?-X=[], classifica(X).
Tipo Atmico
---> Lista Vazia
?-X=tio(jos), classifica(X).
Termo Estruturado
classifica(X) :var(X), !, nl, write('Varivel No-instanciada').
classifica(X) :atomic(X), !, nl, write('Tipo Atmico:'),
tipoAtomico(X).
classifica([_|_]) :!, nl, write('Lista').
classifica(X) :nl, write('Termo Estruturado').
tipoAtomico([]) :!, nl, tab(5), write('---> Lista Vazia').
tipoAtomico(X) :atom(X), !, nl, tab(5), write('---> tomo').
tipoAtomico(X) :integer(X), !, nl, tab(5),
write('---> Nmero Inteiro').
tipoAtomico(X) :float(X), !, nl, tab(5),
write('---> Nmero em Ponto Flutuante').
tipoAtomico(X) :string(X), !, nl, tab(5), write('---> String').
Vamos supor agora que se deseje contar quantas vezes um determinado tomo ocorre em uma lista de
objetos dada. Com esse propsito se definir o procedimento
conta(A, L, N)
onde A o tomo, L a lista e N o nmero de vezes que A ocorre em L. Uma primeira tentativa de
definir conta/3 seria:
conta(_, [], 0).
conta(A, [A | L],
!, conta(A,
conta(A, [_ | L],
conta(A, L,
Neste ltimo exemplo, X e Y foram ambas instanciadas com "a", e portanto obtivemos Nb=1 somente.
No era isso, entretanto que se tinha em mente na construo do procedimento conta/3. Na verdade o
que se queria era o nmero real de ocorrncias de um dado tomo e no o nmero de termos capazes
de unificar com esse tomo. De acordo com essa definio mais precisa da relao conta/3, devemos
verificar se a cabea da lista um tomo. A nova verso da relao conta a seguinte:
conta(_, [], 0).
conta(A, [B | L], N) :atom(B), A=B, !, conta(A, L, N1), N is N1+1.
conta(A, [_ | L], N) :-
83
conta(A, L, N).
bem-sucedido se L uma lista contendo como primeiro elemento o functor principal de Termo, seguido pelos seus argumentos. Os seguintes exemplos do uma idia do seu funcionamento:
?-f(a, b) =.. L.
L=[f, a, b]
?-T =.. [retngulo, 3, 5].
T=retngulo(3, 5)
?-Z =.. [p, X, f(X, Y)].
Z=p(X, f(X, Y))
Para melhor ilustrar a utilidade do operador =../2, vamos considerar um programa que manipula figuras geomtricas como quadrados, retngulos, tringulos, crculos, etc. Estas entidades podem ser representadas por meio de termos tais que o functor principal indica o tipo de figura e os argumentos
especificam o tamanho da figura, como em:
quadrado(Lado)
tringulo(Lado1, Lado2, Lado3)
crculo(Raio)
Uma operao sobre tais figuras poderia ser a ampliao das mesmas. Pode-se implement-la como
uma relao de trs argumentos
amplia(Fig, Fator, Fig1)
onde Fig e Fig1so figuras geomtricas do mesmo tipo (mesmo functor) e os parmetros de Fig1 so
os mesmos de Fig, multiplicados por Fator. Para maior simplicidade assumiremos que os parmetros
de Fig so previamente conhecidos, isto , instanciados com nmeros, o mesmo ocorrendo com Fator.
Uma maneira de programar a relao amplia/3 a seguinte:
amplia(quadrado(A), F, quadrado(A1)) :A1 is F * A.
amplia(crculo(R), F, circulo(R1)) :R1 is F * R.
amplia(retngulo(A, B), F, retngulo(A1, B1)) :A1 is F * A, B1 is F * B.
...
Esse procedimento funciona, mas um tanto grosseiro no caso em que h muitos tipos diferentes de
figuras. necessrio prever todos os tipos de figuras que podem acontecer, empregando uma clusula
para cada tipo, apesar de todos dizerem essencialmente a mesma coisa: tome os parmetros da figura
original e multiplique-os pelo fator de ampliao formando uma figura do mesmo tipo com os novos
parmetros. Uma tentativa (mal-sucedida) de manipular pelo menos todas as figuras de um nico argumento seria:
amplia(Tipo(Arg), F, Tipo(Arg1)) :Arg1 is Arg * F.
Entretanto, no permitido representar um functor em Prolog diretamente por meio de uma varivel,
ou seja, functores devem ser sempre tomos, portanto a varivel Tipo no seria aceita pela sintaxe da
linguagem. O mtodo correto utilizar o predicado =../2. Assim a relao amplia/3, genrica, pode ser
escrita como se segue:
amplia(Fig, F, Fig1) :Fig =.. [Tipo | Parmetros],
multLista(Parmetros, F, NovosParmetros),
Fig1 =.. [Tipo | NovosParmetros].
multLista([], _, []).
84
Os termos construdos com o predicado =../2 podem tambm ser executados como objetivos. A vantagem disto que o prprio programa pode, durante a execuo gerar e executar objetivos. Uma seqncia de objetivos ilustrando esse efeito poderia ser a seguinte:
...
obtenha(Functor),
compute(ListaDeArgumentos),
Obj =.. [Functor | ListaDeArgumentos],
Obj, ...
Aqui, obtenha/1 e compute/1correspondem a procedimentos definidos pelo usurio para obteros componentes do objetivo a ser construdo. O objetivo formado por meio do predicado =../2 e disparado
para execuo por meio da varivel que o nomeia, Obj.
Algumas implementaes da linguagem Prolog podem requerer que todos os objetivos que aparecem
no programa sejam tomos ou uma estrutura com um tomo como functor principal, de forma que
uma varivel, independentemente de sua eventual instanciao, pode no ser sintaticamente aceita
como um objetivo. Esse problema contornado por meio de outro predicado pr-definido, call/1, cujo
argumento um objetivo a ser executado. Assim o exemplo dado acima poderia ser reescrito como:
...
Obj =.. [Functor | ListaDeArgumentos]
call(Obj).
s vezes pode-se desejar extrair de um termo apenas o seu functor principal, ou um de seus argumentos. Em tais casos pode-se, naturalmente, empregar o predicado =../2, entretanto, pode ser mais
prtico e eficiente usar um dos outros dois predicados pr-definidos para a manipulao de termos:
functor/3 e arg/3, cujo significado o seguinte:
functor(Termo, Functor, Aridade)
Esse ltimo exemplo mostra uma aplicao especial do predicado functor/3. O objetivo functor(D,
data, 3) produz em D um termo "geral" cujo functor principal "data", com 3 argumentos. O termo
geral no sentido em que os trs argumentos so variveis no-instanciadas geradas pelo sistema Prolog. Por exemplo:
D=data(_02e, _02f, _030)
Essas trs variveis so ento instanciadas como no exemplo acima, por meio dos trs objetivos arg/3.
Relacionado a esse conjunto de predicados est o predicado name/2, para a construo e decomposio de tomos, introduzido no captulo anterior. Seu significado repetido aqui para manter completa
a seo:
name(tomo, Lista)
verdadeiro se Lista a lista dos cdigos ASCII correspondentes aos caracteres do tomo A.
9.3 EQUIVALNCIAS E DESIGUALDADES
85
At o momento, trs "tipos de igualdade" foram estudados, iniciando pela baseada na unificao, representada por:
X = Y
que verdadeira se X unifica com o valor da expresso aritmtica Expresso. Tem-se tambem:
Expreso1 =:= Expresso2
que verdadeira se os os valores das expresses aritmticas Expresso1 e Expresso2 so iguais. Se,
ao contrrio as expresses possuem valor diferente, escreve-se:
Expresso1 =\= Expresso2
Algumas vezes poder ser necessrio um tipo mais estrito de igualdade: a igualdade literal entre dois
termos. Esse tipo de igualdade implementado por meio de um predicado pr-definido escrito como o
operador infixo "==", de modo que
Termo1 == Termo2
verdadeira se os termos Termo1 e Termo2 so idnticos, isto , possuem exatamente a mesma estrutura e todos os componentes correspondentes so os mesmos. Em particular, os nomes das variveis devem tambm ser os mesmos. A relao complementar a no-identidade, escrita como:
Termo1 \== Termo2
sempre bem sucedido e, como efeito colateral, ocasiona a adio da clusula C na base de dados.
Por outro lado um objetivo
retract(C)
faz o oposto, isto , apaga uma clusula que unifica com C da base de dados. O dilogo abaixo exemplifica esses dois predicados:
?-crise.
no
?-assert(crise).
sim
86
?-crise.
sim
?-retract(crise).
sim
?-crise.
no
As clusulas inseridas por meio do predicado assert/1, atuam exatamente como se fossem parte do
programa original. O seguinte exemplo ilustra o uso de assert/1 e retract/1 como um mtodo para
controlar situaes que se modificam ao longo do tempo. Vamos assumir o programa abaixo, sobre as
condies do tempo:
bom :sol, not chuva.
instvel :sol, chuva.
deprimente :chuva, neblina.
chuva.
neblina.
O dilogo a seguir mostra como a base de dados pode ir sendo gradualmente atualizada:
?-bom.
no
?-deprimente.
sim.
?-retract(neblina).
sim
?-deprimente.
no
?-assert(sol)
sim
?-instvel.
sim
?-retract(chuva).
sim
?-bom
sim
Qualquer tipo de clusula pode ser objeto dos predicados assert/1 ou retract/1. No prximo exemplo
mostraremos que retract/1 tambm no-determinstico: um conjunto completo de clusulas pode ser
removido, por meio do mecanismo de bactracking, atravs de um nico objetivo retract/1. Vamos
assumir um programa com os seguintes fatos:
veloz(senna).
veloz(prost).
meiaBoca(alesi).
meiaBoca(barrichello).
lento(katayama).
lento(moreno).
87
no
Note que quando uma regra inserida na base de dados, por meio do predicado assert, as regras sintticas do Prolog exigem que esta seja fornecida entre parnteses.
Na introduo de uma clusula, podemos desejar especificar a posio na qual a clusula deve ser
inserida na base de dados. Os predicados asserta/1 e assertz/1 permitem controlar a posio de insero. O objetivo
asserta(C)
adiciona a clusula C no final da base de dados. O seguinte exemplo ilustra esses efeitos:
?-assert(p(a)), assertz(p(b)), asserta(p(c)).
sim
?-p(X).
X=c;
X=a;
X=b;
no
H uma relao entre consult/1 e assertz/1. "Consultar" um arquivo pode ser definido em termos de
assertz/1 da seguinte maneira: para "consultar" um arquivo, ler cada um dos seus termos (clusulas) e
inser-los no final da base de dados:
consult(X) :see(X), transfere(C), see(user).
transfere(end_of_file) :- !.
transfere(C) :read(C), assertz(C), transfere(C1).
J uma aplicao til do predicado asserta/1 armazenar respostas j computadas para consultas formuladas ao programa. Por exemplo, vamos considerar que o predicado
resolve(Problema, Soluo)
esteja definido. Podemos agora formular alguma consulta e requerer que a resposta seja lembrada para
consultas futuras:
?-resolve(prob1, Sol), asserta(resolve(prob1, Sol)).
?-produto(A, B, 8).
A=1 B=8;
A=2 B=4;
A=4 B=2;
A=8 B=1;
no
Uma advertncia sobre o uso indiscriminado de assert e retract deve ser feita aqui. Os exemplos dados
ilustram algumas aplicaes obviamente teis desses predicados, entretanto o seu uso requer um cuidado especial. O uso excessivo e descuidado de tais recursos no recomendado como um bom estilo
de programao, uma vez que se est na realidade modificando o programa original em tempo de execuo. Assim, relaes vlidas em um determinado momento, podem no mais ser vlidas em um
momento subsequente, isto , em momentos diferentes, a mesma consulta pode ter respostas diferentes. O uso abusivo de assert-retract pode obscurecer o significado do programa e dificultar a compreenso do que verdadeiro e o que no num dado instante. O comportamento resultante do programa
pode se tornar dificil de entender, de explicar e de confiar.
9.5 RECURSOS PARA O CONTROLE DE PROGRAMAS
A maioria dos recursos de controle de programas Prolog j foi apresentada anteriormente. Com vistas
a permitir uma viso conjunta de tais predicados, apresenta-se a seguir um resumo de todos eles:
cut: representado nos programas por "!", previne a execuo indesejada do mecanismo de
backtracking;
determinstico, isto , toda vez que alcanado por backtracking ele gera um caminho alternativo para a execuo. Seu comportamento ocorre como se ele houvesse sido definido por:
repeat.
repeat :- repeat.
Uma forma tpica de uso desse ltimo predicado ilustrada pelo procedimento quadrado/0, que l
uma seqncia de nmeros e fornece o seu quadrado. A seqncia dada por concluda quando for
lido o tomo "fim", que sinaliza o encerramento da execuo:
quadrado :repeat, read(X),
(X=fim, !; Y is X*X, write(X), fail).
A construo acima tambm muito empregada em programas interativos, que possuem diversas alternativas de execuo mutuamente exclusivas, como em um menu de opes:
executa :repeat, menu(X),
(X=fim, !; exec(X), fail).
zes, entretanto, deseja-se dispor de todos os objetos gerados, por exemplo, coletados em uma lista. .
Os predicados bagof/3 e setof/3 servem exatamente para tal propsito. O predicado findall/3 , em
algumas implementaes, oferecido como alternativa. O objetivo:
bagof(X, P, L)
ir produzir uma lista L de todos os objetos X que satisfazem ao objetivo P. Isto, naturalmente, s faz
sentido se X e P possuem alguma varivel em comum. Por exemplo, assumindo que temos em um
programa Prolog uma especificao que classifica letras em vogais
e consoante:
classe(a,
classe(b,
classe(c,
classe(d,
classe(e,
. . .
vog).
con).
con).
con).
vog).
Ento podemos obter a lista de todas as consoantes nessa especificao atravs do objetivo:
?-bagof(Letra, classe(Letra, con), Consoantes).
Consoantes=[b, c, d, ..., z]
Se, neste ltimo objetivo, a classe das letras no estivesse especificada, obter-se-ia, por meio de
backtracking, duas listas, uma correspondendo s vogais e outra s consoantes:
?-bagof(Letra, classe(Letra, Classe), Letras).
Classe=vog Letras=[a, e, i, o, u];
Classe=con Letras=[b, c, d, f, ..., z].
Se no houver soluo para P no objetivo bagof(X, P, L), ento este simplesmente falha. Se algum
objeto X encontrado repetidamente, ento todas as suas ocorrncias iro aparecer em L, o que conduz possibilidade de existncia de elementos duplicados em L. O predicado setof/3 similar ao bagof. O objetivo:
setof(X, P, L)
ir novamente produzir uma lista L dos objetos X que satisfazem a P, s que desta vez a lista L estar
ordenada e itens duplicados, se houver, sero eliminados. A ordem dos objetos estabelecida em funo de sua ordem alfabtica ou de acordo com a relao "<" se os objetos na lista form nmeros. Se os
objetos forem estruturas, ento seus functores principais so comparados para fins de ordenao. Se
estes so iguais, ento a deciso fica por conta dos primeiros argumentos diferentes a contar da esquerda.
No h restrio quanto ao tipo de objeto a ser coletado. Assim podemos, por exemplo construir uma
lista de pares da forma Classe/Letra de forma que as constantes apaream em primeiro lugar na lista
("con" antecede alfabeticamente "vog"):
?-setof(Classe/Letra, classe(Letra, Classe), Letras).
Letras=[con/b, con/c, ..., con/z, vog/a, ..., vog/u]
Um outro predicado dessa mesma famlia findall(X, P, L), que novamente produz a lista L de todos
os objetos X que satisfazem P. A diferena entre esse predicado e o bagof que todos os objetos X
so coletados sem considerar eventuais solues diferentes para as variveis em P que no so compartilhadas com X. Essa diferente ilustrada no seguinte exemplo:
?-findall(Letra, classe(Letra, Classe), Letras).
Letras=[a, b, c, ..., z]
Alm disso, se no h nenhum objeto X que satisfaa P, ento o predicado findall(X, P, L) resulta
bem-sucedido com L=[]. Caso o predicado findall/3 no se encontre entre os predicados pr-definidos
em uma determinada implementao Prolog, podemos program-lo facilmente da seguinte maneira:
findall(X, Objetivo, Lista) :call(Objetivo), assertz(soluo(X)), fail;
assertz(soluo(fim)), coleta(Lista).
90
coleta(Lista) :retract(soluo(X)), !,
(X==fim, !, Lista=[];
Lista=[X | Resto], coleta(Resto)).
No programa acima, todas as solues para o objetivo "Objetivo" so geradas por meio de
backtracking. Toda soluo gerada imediatamente includa na base de dados, de forma que no
perdida quando a prxima soluo encontrada. Depois de encontrar todas as solues, estas devem
ser coletadas em uma lista e retiradas da base de dados.
RESUMO
Uma implementao Prolog normalmente fornece um conjunto de predicados pr-definidos
para diversas operaes de uso frequente que nem sempre so de fcil codificao em Prolog
"puro";
O tipo de um termo Prolog pode ser testado por meio dos seguintes predicados pr-definidos:
var(X)
nonvar(X)
atom(X)
integer(X)
float(X)
atomic(X)
string(X)
X
X
X
X
X
X
X
Termos Prolog podem ser construdos os decompostos atravs dos seguintes predicados pr-
definidos:
gualdades:
X = Y
X is E
E1 =:= E2
E1 =\= E2
T1 == T2
T1 \== T2
X e Y unificam,
X o valor da expresso aritmtica E,
E1 e E2 tem o mesmo valor,
E1 e E2 tem valores diferentes,
T1 e T2 so idnticos,
T1 e T2 no so idnticos;
Um programa Prolog pode ser visto como uma base de dados relacional, que pode ser atualiza-
Todos os objetos que satisfazem uma dada condio podem ser coletados em uma lista por
EXERCCIOS
91
9.2 Defina o predicado bsico(Termo), que verdadeiro se Termo no possui nenhuma varivel noinstanciada.
9.3 Defina o relao subentende(Termo1, Termo2), que verdadeira se Termo1 "mais geral" que
Termo2. Por
exemplo:
?-subentende(X, c).
sim
?-subentende(g(X), g(t(Y))).
sim
?-subentende(f(X, Y), f(a, a)).
sim
?-subentende(f(X, X), f(a, b)).
no
9.4 Defina a relao copia(Termo, Cpia), que produz em Cpia uma cpia de Termo com todas as
suas variveis renomeadas. Isso pode ser facilmente programado empregando os predicados assert/1 e retract/1.
9.5 Use o predicado bagof/3 para definir a relao potncia(Conjunto, Subconjuntos), que computa o
conjunto de todos os subconjuntos de um dado conjunto, sendo todos os conjuntos representados
como listas. Por exemplo:
?-potncia([a, b, c], P).
P=[[], [a], [b], [c], [a, b], [a, c], [b, c], [a, b, c]]
92
Nome
Marcelo
Luiz
Gilda
Lcia
Paulo
Lina
Sexo
m
m
f
f
m
f
Pai
Luiz
Alfredo
Miguel
Luiz
Miguel
Francisco
Me
Gilda
Lina
Ana
Gilda
Ana
Jlia
93
Placa
ABC-4590
XYZ-1211
RTC-9004
LLZ-7533
Fabricante
Volkswagen
Ford
Fiat
GM
Proprietrio
Alfredo
Lina
Luiz
Gilda
Cor
azul
branco
vermelho
prata
Uma base de dados Prolog, formada a partir das tabelas 10.1(a) e (b), seria representada atravs dos
seguintes fatos:
pessoa(marcelo, m, luiz, gilda).
pessoa(luiz, m, alfredo, lina).
pessoa(gilda, f, miguel, ana).
pessoa(lcia, f, luiz, gilda).
pessoa(paulo, m, miguel, ana).
pessoa(lina, f, francisco, jlia).
carro(abc-4590,
carro(xyz-1211,
carro(rtc-9004,
carro(llz-7533,
Um ou mais atributos em cada relao possui a propriedade especial de serem nicos na tabela. Tais
atributos so denominados "chaves" e identificam os objetos acerca dos quais armazenamos informaes. Usualmente se costuma sublinhar os atributos que so chaves, por exemplo:
pessoa:
nome sexo pai me
10.1.2 RELAES BINRIAS
As relaes mais simples que existem so as relaes binrias, que associam um nico atributo a cada
chave. A relao pessoa/4, que possui a chave "Nome", seria assim dividida em 3 relaes:
Nome-Sexo
sexo(marcelo, m)
etc...
Nome-Pai
pai(marcelo, luiz)
...
Nome-Me
me(marcelo, gilda)
...
Placa-Proprietrio
pr(abc-4590, alfredo)
...
Placa-Cor
cor(abc-4590, azul)
...
94
que possui o seu prprio nome, mantendo entretanto em separado os atributos individuais ch1, ch2 e
ch3.
10.2 RECUPERAO DE INFORMAES
Recuperar informaes significa combinar e apresentar o contedo da base de dados em uma forma
que satisfaa nossas necessidades. Em bases de dados convencionais isto executado por um programa que atua sobre a base de dados. Em Prolog isto feito atravs da definio das condies de soluo em lgica. Por exemplo:
Quem possui um fiat?
?-carro(_, fiat, Prop, _).
Prop = luiz
e
?-pessoa(X, m, _, _), carro(Placa, ford, X, azul).
Supondo que haja um acesso direto quando se dispe da chave da tabela, fcil verificar que, no primeiro caso, sero realizadas 3000 tentativas de unificao na tabela de carros, dais quais apenas 10
sero bem sucedidas (s h 10 fords azuis), produzindo 10 acessos diretos tabela de pessoas para
verificar o sexo, num total de 3010 unificaes. No segundo caso, entretanto, sero realizadas primeiro 10000 tentativas de unificao na tabela de pessoas, das quais 5000 sero bem sucedidas. Para cada
uma dessas unificaes bem sucedidas, 3000 acessos devero ser feitos tabela de carros, uma vez
95
que no se dispe da chave, que "Placa". O nmero de tentativas de unificao realizadas aqui ser
portanto 5000 x 3000 + 10 = 15 000 010. Isso mostra porque as condies com o menor nmero de
solues possveis devem ser colocadas em primeiro lugar na formulao de consultas.
10.2.2 TABELAS VIRTUAIS
Uma das facilidades proporcionadas pelo Prolog no tratamento do modelo relacional a possibilidade
de definir novas tabelas sem ter de cri-las, empregando a implicao lgica. Tais tabelas so denominadas "tabelas virtuais". Por exemplo, uma tabela corDoCarro/2 que contm como argumentos
somente a placa e a cor de um carro pode ser definida da seguinte maneira:
corDoCarro(X, Y) carro(X, _, _, Y).
O conceito de tabelas virtuais uma adaptao das "relaes extratoras" introduzidas no captulo anterior. Um subconjunto do Prolog convencional, sem os smbolos funcionais e o tratamento de listas,
denominado Datalog, foi proposto com essa finalidade. Na verdade o uso de Prolog para representar
bases de dados relacionais, introduz novos conceitos e regras, ampliando o nvel da informao.. Considere por exemplo a questo:
Quem tem uma av que possui um ford branco?
Em Prolog as regras para definir as relaes av/2, corDoCarro/2, etc. so facilmente construdas e
incorporadas base de dados, transcendendo o modelo relacional. A questo apropriada poderia ser
construda assim:
?-av(X, P), carro(_, ford, X, branco).
P,
P,
P,
P,
Por exemplo:
?-esquece(carro(_, _, gilda, _)).
ir remover da base de dados todos os carros que pertencem a Gilda. Da mesma forma
memoriza(carro(flt-5455, honda, gilda, cor-de-rosa)).
O predicado esquece(X) ir excluir da base de dados todas as sentenas que unificam com X. Se for
desejada a excluso somente da primeira ocorrncia, deve ser usado o predicado esquece1(X). Ambos, esquece/1 e esquece1/1 so sempre bem sucedidos, garantindo o primeiro, com sua execuo, que
no h mais na base de dados nenhuma sentena que unifique com X e o segundo que a primeira sentena encontrada unificando com X foi removida. Por outro lado o predicado memoriza(X) inicia com
uma chamada a esquece/1, preservando assim a unicidade da chave estipulada em X. Deve ser tambm notado que esses predicados so extremamente poderosos e devem ser usados com absoluto cuidado para evitar "acidentes". Um cuidado interessante seria restringir a execuo de esquece/1, esquece1/1 e memoriza/1 a argumentos que possussem uma instanciao explcita para a chave da tupla a
esquecer ou memorizar.
10.4 MODELAGEM DE DADOS
Uma base de dados no somente uma coleo de dados ou entidades, mas tambm as associaes ou
relacionamentos entre eles. Tais associaes constituem o denominado "modelo de dados". A tecnologia de bases de dados vem oferecendo mtodos e ferramentas para a soluo de problemas em ambientes complexos e de grande porte. O projeto de modelos lgicos de dados um importante objetivo
nas reas de representao e aquisio de conhecimento. O que se verifica que a pura lgica de predicados um formalismo extremamente poderoso, de expressividade ou capacidade de representao
virtualmente ilimitada, de modo que freqentemente temos que impor restries linguagem empregada na modelagem, retornando porm lgica de predicados para explicar a semntica ou projetar
extenses no convencionais.
10.4.1 FORMAS NORMAIS
Como em toda modelagem, as nicas coisas importantes a serem modeladas so os invariantes fundamentais do domnio do problema. A mais importante propriedade dos invariantes que os objetos
pertencem a classes que podem ser armazenadas uniformemente como relaes.
Um outro princpio bsico aqui a evidncia de que um determinado dado em uma certa relao
funcionalmente dependente de outro. Um conjunto de dados B dito "funcionalmente dependente" de
um outro conjunto de dados A se para todo elemento a em A h um nico elemento b em B tal que b
est relacionado com a. As notaes mais empregadas so as seguintes:
A ---> B
A, B ---> C
significando respectivamente: "B funcionalmente dependente de A" e "C funcionalmente dependente da combinao de A e B". Por exemplo:
trabalhador ---> empregador
Devido ao fato de que as chaves so nicas, segue automaticamente que todos os atributos de uma
97
No usar a representao:
empregados(joo, [jos, jlia, jorge, josefina, jane]).
no necessitando o programador:
(1) Selecionar a lista de empregados de joo,
(2) Adicionar Jonas,
(3) Produzir uma nova lista,
(4) Apagar a tupla corrente, com a velha lista, e
(5) Produzir uma nova tupla, com a nova lista.
Um modelo na primeira forma normal deveria portanto ser:
empregador empregado.
Neste caso, cada empregado possui um nmero (a chave "empregado") e um nome (nomeEmpregado).
O empregado trabalha em um conjunto de projetos com nmeros (a chave "projeto") e nomes (nomeProjeto), dedicando a cada um certo nmero de "horas".
98
A anomalia nesta representao que nomeProjeto no funcionalmente dependente da chave (empregado, projeto) como um todo, mas apenas de uma parte dela (projeto). Assim a informao nomeProjeto armazenada muitas vezes mais do que o necessrio. Se o nome do projeto muda, todas as
ocorrncias de nomeProjeto devem ser alteradas, uma vez para cada empregado que nele trabalha. Um
modelo na segunda forma normal seria:
empregado nomeEmpregado
empregado projeto horas
projeto nomeProjeto
Aqui nomeProjeto armazenado uma nica vez para cada projeto e modificado atravs de uma nica
atualizao.
TERCEIRA FORMA NORMAL (3FN)
Um bom exemplo da 3FN ocorre quando a informao sobre uma pessoa, seu empregador e o endereo de seu empregador so armazenados. Se a relao
empregado empregador endereoEmpregador
existe, ento a entidade endereoEmpregador no funcionalmente dependente da chave "empregado" sozinha, mas na verdade de "empregador", que por sua vez dependente de "empregado". Como
nos casos anteriores, problemas de redundncia e de mltiplas atualizaes surgem, de modo que a
normalizao recomenda que a relao acima seja dividida em duas relaes independentes:
empregado empregador
empregador endereoEmpregador
Os princpios da normalizao podem ser aplicados manualmente para modelos pequenos, entretanto,
para grandes modelos a normalizao deve preferencialmente ser apoiada por ferramentas de engenharia de software.
10.5 ALM DO MODELO RELACIONAL
O modelo relacional puro nem sempre poderoso o bastante para modelagens avanadas, devido
falta de expressividade semntica . Por exemplo, o modelo relacional no requer que, para cada empregado, o atributo empregador corresponda a uma tupla existente na base de dados. Em modelos
reais h dois tipos de regras que relacionam as tabelas uma outra:
regras genricas, que definem novas tabelas virtuais que no so explicitamente armazenadas, e
regras restritoras, que estabelecem restries sobre o que permitido na base de dados.
Um exemplo de regras restritoras dada pelas dependncias funcionais, que especificam que atributos-chave so e devem ser nicos. Um outro exemplo seria uma regra como:
"Todos os elefantes so cor-de-cinza."
que deduz a cor de um elefante na base de dados, produzindo ainda uma restrio que garante que, nas
atualizaes subsequentes, nenhum elefante de outra cor ser armazenado na base de dados. Tais bases de dados so denominadas "dedutivas".
10.6 REDES SEMNTICAS
Questes de semntica so mais importantes para o projeto de uma base de conhecimento do que do
que mtodos para a codificao de dados. Quando os projetistas de base de dados adicionam mais
informao semntica s bases de dados, os modelos resultantes comeam a assemelhar-se aos sistemas de representao de conhecimento desenvolvidos pelos pesquisadores de inteligncia artificial.
Um desses esquemas de representao de conhecimento conhecido como "rede semntica". Uma
99
rede semntica um formalismo para representar fatos e relacionamentos entre fatos por meio de
relaes binrias. Por exemplo, na Figura 10.1, Jos, Joo e 555-2455 representam objetos. "telefone"
representa uma relao entre os objetos Jos e 555-2455, enquanto que "empregador" representa uma
relao entre Jos e Joo.
telefone
555-2455
Jos
Joo
empregador
Os relacionamentos individuais so conectados em uma rede, onde os objetos, por exemplo, "Jos",
so representados uma nica vez. Para relaes binrias, as redes semnticas so um excelente formalismo com uma notao grfica simples. Quando se tenta, entretanto, representar relaes n-rias em
redes semnticas -se forado a empregar construes artificiais, perdendo o formalismo das redes
semnticas grande parte dos seus atrativos.
Acredita-se que grande parte do raciocnio humano seja baseado em associaes lineares, de modo
que o modelo das redes semnticas tambm um interessante modelo do pensamento humano. Em
Prolog as relaes binrias so implementadas individualmente, repetindo os nomes dos objetos como
em:
telefone(jos, 555-2455).
empregador(jos, joo).
Armazenar uma rede semntica como uma rede com ponteiros um mtodo de implementao que
oferece rpido acesso no processo de associao. Em Prolog, na falta do conceito de ponteiro, as redes
so armazenadas como relaes binrias. Isto um pouco mais lento, mas muito flexvel, tanto para
recuperar informaes quanto para sua atualizao.
10.6.1 O CONCEITO DE CLASSE
To logo um objeto classificado, grande quantidade de conhecimento se torna disponvel a seu respeito. Uma "classe" a descrio de atributos e propriedades que so comuns a determinados indivduos, denominados os "membros" da classe. Jos, por exemplo, um objeto pertencente classe dos
empregados. Um "atributo" alguma coisa que pode assumir um valor. Telefone, por exemplo, um
atributo dos membros da classe dos empregados. Uma "propriedade" um atributo juntamente com
um valor. Por exemplo, uma rosa tem a propriedade cor = vermelha. Jos tem a propriedade telefone =
555-2455.
Uma classe pode ser vazia, por exemplo, a classe dos unicrnios, e duas classes com os mesmos elementos podem ser bastante diferentes, por exemplo, a classe dos diretores de pesquisa e a classe dos
possuidores de aqurios. So exemplos de classes:
animal
mamfero
baleia
elefante
100
tubaro
So exemplos de atributos:
cor
alimento
habitat
tamanho
temperamento
Uma classe pode ser subclasse de outra classe. Se S uma subclasse de C e x membro de S, ento x
tambm membro de C. Por exemplo, "mamfero" uma subclasse de "animal" e "elefante" uma
subclasse de "mamfero". Se Clyde um elefante (isto , um membro da classe elefante), ento Clyde
ao mesmo tempo membro da classe mamfero e portanto tambm membro da classe animal.
Se a classe possui um atributo, este compartilhado por todas as suas subclasses. Note que uma classe
pode ter um atributo, mesmo se no possui membros no momento. Valores de atributos inexistentes,
tais como o telefone de um elefante, so rejeitados como no significativos, no devendo ser empregado o tomo "nil".
De modo similar, se uma entidade possui um atributo que funcionalmente dependente dela, por
exemplo, "toda pessoa tem um nome", e o valor do atributo estiver faltando, o tomo apropriado para
representar isso "desconhecido" e no "nil" ou algo parecido. Se, por outro lado, um atributo no
funcionalmente dependente, tal como os filhos de uma pessoa, ento a sua ausncia deve ser pelo
tomo "nil" ou "nenhum" e no por "desconhecido".
Por exemplo: todos os animais tem uma cor, que varia. Portanto, todos os mamferos tem uma cor. Os
elefantes, portanto, tem uma cor, de modo que Clyde, que um elefante, tem tambm uma cor. Se a
classe tem uma propriedade, esta automaticamente herdada por todos os seus membros. Por exemplo:
"Todos os elefantes tem uma cor = cinza"
implica em:
"Se Clyde um elefante, ento Clyde tem uma cor = cinza"
O armazenamento de informao sobre classes em conjunto com informao sobre objetos, requer
alguns relacionamentos de uso geral, como os apresentados na figura 10.2
um tipo de
A
A subclasse de B
A entidade E um B
B adjetivo de A
A tem um atributo B
_um
E
A
tem
A
atributo
A
101
animal
umTipoDe
oxignio
inalante
umTipoDe
alimento
amendoim
Bonnie
umTipoDe
elefante
Um
Um
baleia
tubaro
cor
habitat
Clyde
umTipoDe
mamfero
habitat
cor
circo
cor
oceano
cinza
Seja ento a rede semntica mostrada na Figura 10.3. A informao ali representada pode ser adequadamente descrita atravs de um conjunto de clusulas Prolog. A declarao de operadores infixos
contribui para tornar o programa mais legvel. Define-se assim a sintaxe dos relacionamentos descritos na Figura 10.2 por meio da assertiva:
:- op(900, xfx, [UmTipoDe, Um, , tem, atributo]).
temUm
temUm
temUm
temUm
inalante.
alimento.
habitat.
cor.
UmTipoDe mamfero.
tem alimento=amendoim.
tem habitat=circo.
tem cor=cinza.
O primeiro destes axiomas o fecho transitivo de Um/2 e o segundo o fecho transitivo de tem/2.
Com o emprego deles possvel consultar a base de conhecimento em busca de questes de carter
geral tais como:
"Que propriedades possui Clyde?"
?-clyde tem Atr=Val.
Atr=alimento Val=amendoim;
Atr=habitat Val=circo;
Atr=cor Val=cinza;
Atr=inalante Val=oxignio;
no
102
RESUMO
Em bases de dados relacionais os dados so definidos por meio de relaes sobre domnios, e
os fatos individuais so representados como tuplas de valores extrados de tais domnios, cada
um deles representando um "atributo";
Pelo menos um dentre os atributos possui a caracterstica especial de ser "nico" em toda a ta-
bela de tuplas. Tal atributo denominado uma "chave" e identifica os objetos acerca dos quais
armazenada informao;
Duas facilidades importantes oferecidas pelo modelo relacional so as tabelas virtuais, que de-
A atualizao de base de dados deve ser projetada de modo a preservar a unicidade dos atribu-
avanada. Um modelo mais expressivo, empregado em inteligncia artificial o das redes semnticas;
Alguns dos relacionamentos empregados em redes semnticas so: UmTipoDe, Um, , tem e
temUm. Todos eles so binrios, e podem ser representados em Prolog por meio de operadores
infixos.
EXERCCIOS
Escreva uma consulta ao sistema Prolog respondendo "Que empregados possuem salrio superior
ao de seu gerente?"
10.2 Defina as seguintes relaes:
pas(X)
mar(X)
populao(X,Y)
fronteira(X, Y)
%
%
%
%
X
X
X
X
um pas
um mar
tem a populao Y
faz fronteira com Y
Escreva uma consulta ao sistema Prolog para responder a questo: "Que pas, banhado pelo mediterrneo, faz fronteira com um pas que faz fronteira com um pas cuja populao excede a populao da ndia?"
10.3 Modifique os predicados para a manipulao de bases de dados relacionais apresentados no presente captulo de forma que mltiplas chaves sejam armazenadas sem redundncia. (Dica: Use
tabelas virtuais).
10.4 Amplie a base de conhecimento sobre animais. Como representar um avestruz como membro da
classe dos pssaros se se definiu "voar" como uma propriedade dessa classe? Em outras palavras,
como introduzir o conceito de exceo nas propriedades herdadas por um objeto a partir de sua
classe?
103
10.5 Modele uma base de conhecimento, empregando redes semnticas para descrever automveis,
introduzindo os relacionamentos ParteDe(X, Y), que verdadeiro se X parte de Y (por exemplo: ParteDe(motor, carro)) e subconjDe(X, Y), que verdadeiro se X subconjunto de Y.
104
Por exemplo:
?-deriv(x*x, Y).
Y=1*X+X*1
Entretanto, certamente seria mais apreciada uma sada melhor, tal como 2*X ou simplesmente 2X. A
razo da apresentao inadequada do resultado que o Prolog no possui simplificao algbrica
inerente, entretanto esta pode ser facilmente implementada, como ser visto mais adiante neste mesmo
captulo.
11.2 MANIPULAO DE FRMULAS
Em uma linguagem de programao simblica, como Prolog, os programadores precisam considerar
as frmulas e no apenas os seus valores. Em geral as frmulas no envolvem apenas aritmtica, mas
podem ser combinadas arbitrariamente atravs dos mais variados operadores e operandos, de acordo
com o princpio recursivo da decomposio: "o valor de uma expresso o resultado da aplicao de
um operador ao resultado dos restantes".
Em linguagens como Pascal e Lisp este princpio recursivo parte da semntica da linguagem. Em
Prolog isto deve ser feito explicitamente, mas pode ser feito sem dificuldades por um predicado recursivamente definido. Este esquema geral e uma rplica do princpio recursivo da decomposio:
"Para resolver uma expresso, primeiro (i) resolva seus operandos, e depois (ii) aplique o
operador sobre os resultados obtidos".
11.3 OS OPERADORES REVISITADOS
105
Para lidar com uma expresso, necessrio ser capaz de manipular os seus subcomponentes. Na Tabela 11.1 relaciona-se um conjunto de operadores embutidos disponveis na maioria das implementaes Prolog. H-se que lembrar entretanto que internamente tais operadores so representados sob a
forma de termos funcionais, onde os operadores so functores. Por exemplo:
X+Y armazenado como '+'(X, Y)
O operador embutido =../2 (univ) capaz de atuar sobre uma expresso vista como uma lista de componentes:
X+Y =.. ['+', X, Y]
-X =.. ['-', X]
Por exemplo:
?- 3+2*7 =.. [X, Y, Z].
X='+' Y=3 Z=2*7
?-X =.. ['-', 3+5, 5*9].
X=3+5-5*9
Tambm so importantes neste contexto os predicados embutidos functor/3 e arg/3 (ver seo 9.2) que
atuam normalmente sobre operadores, empregando a notao funcional.
Tabela 11.1 Operaes Comuns em Prolog
(a) Operaes Binrias
X+Y Adio
X-Y Subtrao
X*Y Multiplicao
X/Y Diviso
X=Y Igual
X<>Y No igual
X>=Y Maior ou Igual
X=<Y Menor ou Igual
X<Y Menor que
X>Y Maior que
X and Y Conjuno
X or Y Disjuno
X impl Y Implicao
(b) Operaes Unrias
-X Negao Aritmtica
not X Negao Lgica
A avaliao das frmulas numricas escondida do usurio, apesar de poder ser definida em Prolog.
Sua implementao em Prolog til por duas razes: Primeiro para ensinar os princpios da avaliao
de frmulas em Prolog. Depois, pode vir a ser necessrio incluir regras de operao que no se comportam estritamente com a semntica do operador "is". Vamos agora implementar o operador "$" com
a finalidade de estender os efeitos de "is", de modo que a expresso seja esperada do lado esquerdo e
o valor direita, assim:
10 + 10 $ 20
106
que pode ser lido: "o valor de 10+10 20". O operador "$" estende o "is" tambm na avaliao de
variveis globais, armazenadas como valor(A, B). Por exemplo:
valor(a, 3).
valor(b, 7).
?-a*b*37 $ X.
X=777
O operador $ pode ser usado para implementar a atribuio ordinria de variveis globais como no
programa abaixo, onde o predicado esquece/1 o mesmo introduzido no captulo anterior e repetido
aqui como recordao:
:- op(901, xfx, ':=').
(V:=E) :E $ T, esquece(valor(V,X)), assert(valor(V, T)).
esquece(X) :esquece1(X), fail.
esquece(X).
esquece1(X) :retract(X).
esquece1(X).
X).
X).
0).
X).
-X).
0).
reduz(X=X, true).
reduz(X or true, true).
reduz(true or X, true).
reduz(X and false, false).
reduz(false and X, false).
reduz(X and true, X).
107
reduz(0*X,
reduz(X*1,
reduz(1*X,
reduz(0/X,
0).
X).
X).
0).
O algoritmo est correto, porm no completo. Tambm no possui eficincia tima porque ir tentar ressimplificar uma expresso que um algoritmo mais refinado reconheceria como j simplificada.
Tal refinamento ser deixado ao leitor a ttulo de exerccio.
simplifica(U, V) :simp(U, V, Teste). % Teste verdadeiro se V<>U.
simp(F, H, true) :reduz(F, G), !, simplifica(G, H).
simp(F, Z, true) :F=..[Op, X, Y],
simp(X, X1, mudaX),
simp(Y, Y1, mudaY),
membro(true, [mudaX, mudaY]), !,
G=..[Op, X1, Y1],
simplifica(G, Z).
simp(F, F, false).
O efeito do programa acima pode ser visualizado por meio dos seguintes exemplos:
?-simplifica(1*x-x*1, S).
S=0
?-simplifica(1*x+x*1, S).
S=x+x
reconhecido pela unificao com o padro X-X. Entretanto, uma classe de problemas resta ainda por
ser solucionada, que quando h subexpresses que poderiam ser movidas de acordo com as regras
comutativas e associativas, e ento reduzidas quando um padro unificvel for reconhecido. A expresso
(a+b+c)-b
sendo redutvel a
X=a+c
108
de modo que o problema de descobrir se uma determinada expresso emprega a mesma subexpresso
diversas vezes solucionado por:
comum(Z, U) :Z=..[Op, X, Y], ocorre(U, X), ocorre(U, Y).
Por exemplo:
?-comum((w+1+2*(w+1)), Z), fail.
w+1
w
1
no
11.6 INTEGRAO
Tem sido dito que a diferenciao uma tcnica, ao passo que a integrao uma arte. A tarefa de
integrao simblica objeto da engenharia de conhecimento, onde as especializaes humanas so
transferidas para sistemas computacionais. Uma primeira tentativa de obter integrao poderia ser por
meio da explorao da reversibilidade dos predicados Prolog :
integr(Y, Z) :deriv(Z, Y).
?-integr(1*x+x*1, Int).
Int=x*x
em um determinado momento ser ativado o objetivo number(Int). Entretanto, tal predicado prdefinido no inversvel. Se o fosse, deveria gerar nmeros instanciados (0, 1, 2, ...) , que so todos
integraes corretas de 0. Mas ao invs disso produz a penas a resposta "no".
Um outro problema diz respeito a reverso da simplificao. Se for tentado
?-integr(x+x, Int).
com vistas a obter x*x, nenhuma resposta obtida, porque x+x somente atingido aps uma simplificao. Se simplifica/2 estiver sendo executado de modo reverso, ir cair num lao recursivo infinito.
Entretanto, possvel modificar o predicado simplifica/2 para controlar a profundidade mxima da
recurso. Essa aplicao pode resultar em um sistema de integrao simblica bastante lento, mas
teoricamente completo, baseado no princpio da gerao e teste exaustivos. A construo de tal sistema deixada como um exerccio ao leitor.
RESUMO
A capacidade de programao simblica uma das principais caractersticas da linguagem
O princpio recursivo de decomposio que parte da semntica de linguagens tais como Pas-
cal e Lisp, deve ser explicitado em Prolog, o que pode feito com grande facilidade;
109
O operador "$" estende a semntica do operador "is" permitindo a avaliao de variveis glo-
peciais para ser eficiente. O predicado comum/2, baseado em ocorre/2, representa uma implementao simples com esse objetivo;
11.1 Escreva um programa de simplificao que nunca re-simplifique uma expresso j simplificada.
11.2 Estenda o predicado deriva/2, incluindo simplificao algbrica para lidar com as funes:
ln, exp, sin, cos, arctan e U^V
Aqui, c uma subexpresso comum que removida dos dois operandos principais:
(a+b+c+d)
-(a+c)
==>
==>
((a+b+d) + c)
-(a+c)
---------------------------(a+b+d) - a
==>
X - Y
110
que se espera dele. Um erro comum, cometido por alguns programadores negligenciar esse
critrio bvio em favor de outros, como por exemplo a eficincia;
No deve ser mais complicado do que o necessrio. Truques de programao que obscurecem o
significado do programa devem ser evitados;
ROBUSTEZ: Um bom programa deve ser "robusto". Isso significa que ele no deve ser aborta-
do facilmente quando o usurio entrar com dados incorretos ou inesperados. O programa deve,
no caso de tais erros, manter-se em execuo e comportar-se "racionalmente" (por exemplo:
relatando o erro ao usurio e solicitando nova entrada de dados).
o mnima aceitvel para um programa a sua listagem enriquecida com comentrios suficientes para o seu entendimento.
A importncia de cada critrio vai depender do problema, das circunstncias em que o programa
desenvolvido e do ambiente em que ser utilizado. No h dvida, entretanto, de que a correo deve
ser o critrio de mais alta prioridade. Aos critrios de transparncia, modificabilidade, robustez e documentao normalmente atribuda uma prioridade no mnimo igual ao requisito de eficincia.
111
H algumas regras gerais para atingir na prtica os critrios apresentados acima. Uma delas, muito
importante, primeiro "pensar" sobre o problema a ser resolvido e somente iniciar a codificao na
linguagem de programao escolhida depois de se ter formulado uma idia clara sobre o que deve ser
feito. Uma vez que um bom entendimento do problema foi desenvolvido e definida a sua soluo, a
codificao do programa torna-se fcil e rpida, havendo uma boa chance de se obter sem demora um
programa correto.
A formulao inicial obtida para a soluo do problema dever ento ser convertida para a linguagem
de programao escolha. Tal processo, entretanto, pode no ser uma tarefa fcil. Uma abordagem
consagrada a de utilizar o "princpio dos refinamentos sucessivos", que considera a soluo inicial
uma formulao em "alto nvel" e o programa finalmente obtido como uma soluo em "baixo nvel".
De acordo com o princpio dos refinamentos sucessivos, o programa final obtido por meio de uma
sequncia de transformaes ou refinamentos da soluo inicial. Inicia-se com a formulao em alto
nvel da soluo do problema e ento passa-se a transform-la de maneira que cada nova formulao
obtida equivalente anterior, porm expressa de forma mais detalhada. Em cada passo de refinamento os conceitos usados na formulao anterior so elaborados em maior detalhe e a sua representao vai se aproximando da linguagem de programao. Deve-se ter em mente que os refinamentos
se aplicam tanto s definies de procedimentos quanto s estruturas de dados. Nos estgios iniciais
normalmente se trabalha com unidades de informao mais abstratas, cuja estrutura refinada na medida em que avanamos com o processo. A estratgia dos refinamentos sucessivos possui as seguintes
vantagens:
Permite a formulao de uma soluo inicial nos termos mais relevantes ao problema,
Essa soluo inicial , por conseguinte, mais simples e sucinta, sendo a sua correo facilmente
verificvel, e
Cada passo de refinamento deve ser suficientemente pequeno para ser manejado intelectual-
mente. Assim a transformao da soluo em uma representao mais detalhada preserva com
mais facilidade a sua correo.
No caso da linguagem Prolog, pode-se pensar em tal processo como sendo o de refinamento de refinamento de relaes. Se, entretanto, a natureza do problema sugerir uma abordagem em termos algortmicos, tambm possvel pensar em refinamento de algoritmos, adotando ento a viso procedimental do Prolog.
Para refinar apropriadamente uma soluo em algum nvel de detalhamento e introduzir conceitos
adequados ao prximo, necessrio "ter idias". Portanto a programao uma atividade criativa,
especialmente para programadores iniciantes. medida em que a experincia em programao aumenta, esta se torna menos uma arte e mais uma tcnica. Assim, a questo principal : "Como ter idias?" A maioria das idias surge da experincia com problemas similares, cuja soluo conhecida. Se
no se conhece uma soluo direta, pode-se lanar mo de outros programas parecidos. Uma fonte de
idias nossa vida no dia-a-dia.. Por exemplo, se o problema a resolver classificar uma lista de
itens, pode-se obter uma idia considerando a questo: "Como proceder para classificar as provas de
uma turma de alunos pela ordem alfabtica do nome dos estudantes?".
12.2 COMO PENSAR EM PROLOG
Uma caracterstica importante da linguagem Prolog permitir que seus programas sejam pensados
tanto declarativa quanto procedimentalmente. Essas duas abordagens foram discutidas com algum
detalhe no captulo 3. A que ir se tornar mais eficiente e prtica depende, naturalmente, do problema
a resolver. A experincia tem mostrado que solues declarativas so usualmente mais fceis de desenvolver e possuem a clareza e limpidez da pura lgica. Por outro lado, podem tambm facilmente
originar programas ineficientes. Durante o processo de desenvolvimento de uma soluo, deve-se
112
buscar as idias adequadas para decompor um problema em subproblemas de soluo mais fcil. Uma
questo importante aqui : "Como encontrar os subproblemas apropriados?". Os princpios fundamentais para responder tal questo sero discutidos agora.
12.2.1 USO DE RECURSO
Na soluo de problemas envolvendo o processamento sequencial por meio de recurso, uma boa
heurstica aplicar pensamento indutivo e resolver os seguintes dois casos separadamente:
(1) Os casos triviais, ou bsicos, em que o argumento uma lista vazia ou unitria, e
(2) Os casos gerais, em que o argumento uma lista [Cabea|Corpo] e o problema assumido resolvido para "Corpo".
Em Prolog, essa tcnica utilizada frequentemente. Seja por exemplo o problema de processar uma
lista de itens de tal maneira que cada item seja operado por uma mesma regra de transformao:
transforma(Lista, F, NovaLista)
onde Lista a lista original, F uma regra de transformao e NovaLista a lista de todos os itens
transformados. O problema de transformar Lista em NovaLista pode ser subdividido em dois casos:
(1) Caso Bsico: Lista = []
Se Lista = [], ento NovaLista = [], independentemente de F.
(2) Caso Geral: Lista = [X | Resto]
Para transformar uma lista do tipo [X | Resto] em uma lista do tipo [NovoX | NovoResto],
transforme Resto, obtendo NovoResto e transforme X, obtendo NovoX.
Em Prolog:
transforma([], _, []).
transforma([X | Resto], F, [NovoX | NovoResto]) :G =.. [F, X, NovoX], call(G),
transforma(Resto, F, NovoResto).
A razo pela qual a recurso se aplica to naturalmente em Prolog reside no fato de que os objetos
estruturados, como rvores e listas, possuem uma organizao recursiva intrnseca. Uma lista, por
exemplo, ou vazia (caso bsico), ou possui uma cabea e um corpo (caso geral).
12.2.2 GENERALIZAO
Muitas vezes uma boa idia generalizar o problema original, de forma a permitir que a soluo do
problema generalizado seja formulada recursivamente. O problema original ento solucionado como
um caso especial da verso mais geral. A generalizao de uma relao envolve tipicamente a introduo de um ou mais argumentos extras. O maior problema, que pode requerer uma profunda intuio, : "Como encontrar a generalizao correta?". Como ilustrao examinaremos um clssico da
pesquisa em inteligncia artificial que o "problema das oito damas". O enunciado original desse
problema prope dispor oito damas em um tabuleiro de xadrez de maneira que nenhuma delas ataque
as demais. A relao correspondente poderia ser representada por:
oitoDamas(Posio)
que ser verdadeira se Posio representar uma posio do tabuleiro tal que nenhuma dama ataque as
restantes. Uma idia interessante, nesse caso generalizar o nmero de damas de oito para N, de forma que o nmero de damas se torna o argumento adicional.
nDamas(Posio, N)
113
A vantagem dessa generalizao que h uma formulao recursiva imediata para a relao nDamas/2:
(1) Caso Bsico: N = 0
Colocar "zero" damas em segurana trivial.
(2) Caso Geral: N > 0
Para colocar N damas em segurana no tabuleiro necessrio satisfazer as seguintes condies:
Obter uma configurao segura para N - 1 damas, e
Adicionar as damas restantes de forma que nenhuma delas ataque as demais.
Uma vez que o problema generalizado est solucionado, a soluo do problema original imediata:
oitoDamas(Posio) :nDamas(Posio, 8).
De modo geral tais problemas podem ser naturalmente ilustrados por meio de grafos, onde os
nodos correspondem a objetos e os arcos a relaes;
Algumas normas cuja observncia produz um bom estilo de programao em Prolog sero introduzidas a seguir: regras gerais, organizao tabular de procedimentos longos e o uso apropriado de comentrios.
12.3.1 REGRAS GERAIS PARA UM BOM ESTILO
As clusulas do programa devem ser curtas. Seu corpo no deve conter mais que uns poucos
proc1A :- a, b, c.
proc1B :- d, e, f.
proc1C :- g, h, i.
ao invs de
proc1 :- a, b, c, d, e, f, g, h, i.
114
Os procedimentos do programa devem tambm ser curtos (conter poucas clusulas), porque
Adotar nomes mnemnicos para procedimentos e variveis, indicando o significado das rela-
branco, e identao. Clusulas sobre o mesmo procedimento devem ser agrupadas conjuntamente. Deve haver linhas em branco entre os procedimentos. Cada objetivo deve ser escrito em
uma nova linha. Segundo Bratko [Bra 86]:
"Programas Prolog muitas vezes lembram poemas, devido ao apelo esttico produzido pelas
idias e formas que contm.".
Convenes de estilo desse tipo podem variar de programa para programa, uma vez que depen-
O operador cut deve ser usado com cuidado. Seu uso deve ser evitado quando no for absolu-
tamente necessrio. Se no for possvel evitar o uso de cuts, melhor usar apenas os cuts "verdes" e jamais os "vermelhos". Como foi discutido no captulo 6, um cut "verde" quando pode
ser removido sem alterar o significado declarativo da clusula em que se encontra. Caso contrrio o cut "vermelho";
O operador not, devido a sua relao com o cut tambm pode apresentar comportamento ines-
perado. necessrio ter completo conhecimento sobre a forma em que o not definido em
Prolog:
not(P) :- P, !, fail; true.
uma clusula. A legibilidade pode ser algumas vezes incrementada pela diviso da clusula que
contm o ";" em duas.
A implementao abaixo um contra-exemplo de definio da relao merge/3, empregando um estilo que deixa muito a desejar:
merge(L1, L2, L3) :L1 = [], !, L3 = L2;
L2 = [], !, L3 = L1;
L1 = [X | Resto1],
L2 = [Y | Resto2],
(X < Y, !, Z = X, merge(Resto1, L2, Resto3);
(Z = Y, merge(L1, Resto2, Resto3)),
L3 = [Z | Resto3].
115
J a verso a seguir possui um estilo muito mais transparente e legvel, alm de ser com certeza mais
eficiente, uma vez que tira partido da unificao dos argumentos correspondentes ao caso bsico na
prpria cabea da clusula:
merge([], L, L).
merge(L, [], L).
merge([X | R1], [Y | R2], [X | R3]) :X < Y, !, merge(R1, [Y | R2], R3).
merge(L1, [Y | R2], [Y | R3]) :merge(L1, R2, R3).
do que no programa inteiro, portanto, um bom princpio de correo de programas comear pelo
teste de pequenas unidades do programa e, quando estas forem consideradas confiveis, passar a testar
mdulos maiores at que o programa inteiro possa ser testado.
A correo de programas em Prolog facilitada por duas circunstncias: primeiro, Prolog uma linguagem interativa, de forma que qualquer parte do programa pode ser ativada diretamente por meio de
uma consulta apropriada; segundo, as implementaes Prolog normalmente oferecem ferramentas
especiais para "debugging". Como resultado desses dois recursos, a correo de programas em Prolog
pode, em geral, ser executada de forma bem mais eficiente do que a maioria das linguagens de programao.
A ferramenta bsica para a depurao de programas o processo de "tracing". Sua aplicao a um
objetivo significa que as informaes associadas satisfao desse objetivo iro sendo apresentadas
durante a execuo. Tais informaes incluem:
Informao de Entrada: O nome do predicado e os valores dos argumentos quando o objetivo
disparado;
Informao de Sada: No caso do objetivo ser bem sucedido, so apresentados os valores dos
argumentos que o satisfazem. Em caso contrrio, a indicao de falha no ponto em que esta
ocorreu;
Entre a entrada e a sada, pode-se obter a mesma informao de todos os sub-objetivos envolvidos, de
forma que podemos dispor do tracing da execuo de qualquer consulta ao programa, desde os nveis
mais elevados at que os fatos correspondentes sejam encontrados. Isso pode, em determinadas circunstncias, ocasionar um excesso de informao, assim, permitido ao usurio especificar um tracing seletivo. H dois mecanismos dedicados a essa seleo: primeiro, suprimir a informao de tracing alm de determinado nvel; segundo, executar o tracing apenas sobre algum subconjunto especfico de predicados e no sobre o programa inteiro. Tais ferramentas para a depurao de programas
so ativadas por meio de predicados pr-definidos que variam de uma implementao para outra. Um
conjunto tpico desses predicados o seguinte:
trace:
notrace:
spy(P):
O processo de tracing pode ser interrompido alm de uma certa profundidade por meio de comandos
especiais acionados durante a execuo. Dependendo da implementao pode haver ainda diversos
comandos de depurao disponveis, tais como retornar a um determinado ponto anterior da execuo.
Aps tal retorno podemos, por exemplo, repetir a execuo de forma mais detalhada.
12.5 EFICINCIA
H diversos aspectos de eficincia, incluindo os mais comuns: tempo de execuo e consumo de memria de um programa. Um outro aspecto, pouco considerado mas indubitavelmente de grande importncia o tempo consumido no desenvolvimento de um programa. A arquitetura dos computadores
convencionais no especialmente adequada para o estilo de execuo de programas adotado pelo
Prolog - ou seja, a satisfao de uma lista de objetivos. Portanto, as limitaes de espao e tempo a
117
que todas as linguagens de programao esto sujeitas, podem vir a ser sentidas antes pelos programas
Prolog.
Por outro lado, em muitas reas de aplicao, o uso do Prolog vai reduzir consideravelmente o tempo
de desenvolvimento, pois os programas Prolog so em geral mais fceis de escrever, entender e depurar do que os escritos em linguagens convencionais. Problemas que gravitam em torno do "domnio
Prolog" envolvem processamento simblico, no-numrico, sobre objetos estruturados e as relaes
entre eles. Em particular, o uso de Prolog tem sido especialmente bem sucedido em reas envolvendo
a soluo simblica de equaes, planejamento, bases de dados, solucionadores genricos, prototipao, implementao de linguagens de programao, simulao discreta e qualitativa, projeto arquitetnico, aprendizado de mquina, interpretao da linguagem natural, sistemas especialistas e diversas
outras reas da inteligncia artificial. Sob outro ngulo, matemtica numrica uma rea na qual os
programas Prolog no conseguem competir.
Com respeito a eficincia na execuo, um programa compilado sempre mais eficiente do que um
programa interpretado, portanto, se o sistema Prolog adotado possui um interpretador e um compilador, este ltimo deve ser usado preferencialmente ao primeiro quando a eficincia se tornar um ponto
crtico. Se um programa se apresenta ineficiente, muitas vezes isso pode ser radicalmente modificado
por meio de alteraes no seu prprio algoritmo, entretanto, para fazer isso, os aspectos procedimentais do programa devem ser considerados. Uma maneira simples de aumentar a eficincia de um programa encontrar uma ordenao mais adequada para as clusulas no interior dos procedimentos e
para os objetivos no interior das clusulas. Um outro mtodo, relativamente simples, a introduo de
cuts em posies apropriadas. Idias para aumentar a eficincia de um programa normalmente surgem
de um entendimento mais profundo do problema. Um algoritmo mais eficiente resulta de melhorias de
dois tipos:
Aumento na eficincia de busca, evitando backtracking desnecessrio e interrompendo a execu-
forma que as operaes sobre esses objetos possam ser implementadas de maneira mais eficiente.
Esses dois tipos de melhorias sero abordados em maior detalhe nos exemplos apresentados nas prximas sees.
12.5.1 O PROBLEMA DE COLORIR UM MAPA
O problema de colorir um mapa corresponde a atribuir, a cada pas em um determinado mapa, uma
certa cor, escolhida de um conjunto de quatro cores diferentes, de maneira que dois pases vizinhos
nunca sejam coloridos com a mesma cor. H um teorema que garante que isso sempre possvel de
ser feito. Vamos assumir que o mapa seja especificado pela relao:
viz(Pas, Vizinhos)
onde Vizinhos a lista de todos os pases que possuem alguma fronteira em comum com Pas. Assim,
o mapa da Amrica do Sul, com 13 pases, seria especificado em ordem alfabtica por:
viz(argentina, [bolvia,brasil,chile,paraguai,uruguai]).
viz(bolvia,
[argentina,brasil,chile,paraguai,peru]).
viz(brasil,
[argentina,bolvia,colmbia,guiana,
guiana_francesa,paraguai,suriname,
uruguai, venezuela]).
...
Uma possvel soluo para o problema das cores de cada pas seria representar a correspondncia
entre estes e suas cores por uma lista de pares do tipo:
Pas/Cor
118
que especifca uma cor para cada pas em um determinado mapa. Para o mapa proposto, os nomes dos
pases so dados antecipadamente e o problema ser encontrar a cor adequada para colorir cada um
deles. Assim, no caso da Amrica do Sul, o problema corresponde a encontrar uma instanciao adequada para as variveis C1, C2, C3, etc. na lista:
[argentina/C1, bolvia/C2, brasil/C3, ...]
Para isso define-se a relao cores/1, cujo nico argumento a lista acima e que ser verdadeira se a
lista satisfizer a restrio do colorido do mapa, com respeito relao viz/2 definida anteriormente.
Sejam as cores escolhidas azul, amarelo, vermelho e verde. A condio de que dois pases vizinhos
no podem ter a mesma cor pode ser formulada em Prolog por:
cores([]).
cores([Pa/Cor | Resto]) :cores(Resto),
membro(Cor, [azul, amarelo, vermelho, verde]),
not(membro(Pas1/Cor,Resto),vizinho(Pas,Pas1)).
vizinho(Pas, Pas1) :viz(Pas, Vizinhos), membro(Pas1, Vizinhos).
onde membro/2 a relao usual de ocorrncia em listas. O procedimento cores/1 funciona relativamente bem para mapas simples, com poucos pases, entretanto, para mapas complexos como o da
Amrica do Sul, sua eficincia deixar a desejar. Assumindo que o predicado pr-definido setof/3
esteja disponvel, uma tentativa de colorir a Amrica do Sul poderia ser a seguinte: Primeiro define-se
uma relao auxiliar:
pas(P) :- viz(P, _).
Ento uma consulta adequada para colorir a Amrica do Sul poderia ser formulada por:
?-setof(P/Cor, pas(P), Lista), cores(Lista).
O objetivo setof/3 ir primeiro construir uma lista de itens P/Cor, na qual as cores sero representadas
por variveis no-instanciadas. Depois o objetivo cores/1ir produzir a instanciao adequada. provvel, entretanto, que essa tentativa falhe devido sua ineficincia. Um estudo detalhado de como o
Prolog tenta satisfazer o objetivo cores/1 revela a fonte de tal ineficincia. Os pases em Lista so
organizados em ordem alfabtica, que no tem nada a ver com a sua disposio geogrfica. A ordem
em que as cores so atribudas aos pases corresponde ordem da Lista (comeando pelo final), o que
, no caso em questo, independente da relao viz/2. Assim, o processo de colorir os pases comea
em algum ponto do mapa, continua em um outro extremo, etc, movendo-se de forma mais ou menos
aleatria. Isso pode conduzir facilmente a uma situao na qual um pas que deva ser colorido encontre-se rodeado por outros pases j coloridos com todas as quatro cores disponveis, sendo ento necessrio acionar o mecanismo de backtracking, com elevado nus para a eficincia do programa.
Fica claro ento que a eficincia depende da ordem na qual os pases sero coloridos. A intuio sugere uma estratgia simples de ordenao que apresenta um desempenho muito superior ao mtodo aleatrio. Comea-se com algum pas que tenha muitos vizinhos. Depois so coloridos os seus vizinhos.
Depois os vizinhos dos vizinhos e assim por diante. Para a Amrica do Sul, ento, o Brasil (que faz
fronteira com nove pases, parece ser um bom candidato para iniciar o processo. Assim, quando a lista
de Pas/Cor for construda, o Brasil deve ser colocado no fim, com todos os demais pases o antecedendo. Dessa forma o algoritmo, que comea a processar a partir do ltimo elemento da lista iniciar
com o Brasil e continuar dali a processar os pases vizinhos como foi explicado anteriormente.
Essa nova ordenao aumenta muito a eficincia do programa em comparao com a ordenao alfabtica original, produzindo sem dificuldade os possveis coloridos do mapa da Amrica do Sul.
Pode-se construir manualmente uma lista apropriada dos pases da Amrica do Sul, mas no necessrio fazer isso. O procedimento fazLista/1, definido abaixo executar essa tarefa para ns. Ele inicia
a construo com algum pas especificado (Brasil, no nosso caso) e coleta os pases em uma lista denominada "Fechada". Cada pas , inicialmente colocado em outra lista, denominada "Aberta", antes
119
de ser transferido para Fechada. Toda vez que um pas for transferido de Aberta para Fechada, os seus
vizinhos sero colocados em Aberta.
fazLista(Lista) :coleta([brasil], [], Lista).
coleta([], Fechada, Fechada).
coleta([X | Aberta], Fechada, Lista) :membro(X, Fechada), !,
coleta(Aberta, Fechada, Lista).
coleta([X | Aberta], Fechada, Lista) :viz(X, Vizinhos),
conc(Vizinhos, Aberta, Aberta1),
coleta(Aberta1, [X | Fechada], Lista).
Essa forma de programar a concatenao de listas pode tornar-se bastante ineficiente quando a primeira lista muito longa, uma vez que esta deve ser inteiramente percorrida at que a lista vazia seja
encontrada. Para tornar a relao conc/3 verdadeiramente eficiente, deve-se pular diretamente para o
fim da primeira lista em um nico passo de computao. Isso somente possvel se soubermos localizar o fim de uma lista, o que no pode ser feito a partir da representao adotada at o momento.
necessrio portanto uma nova representao para listas. Uma soluo possvel representar cada lista
por meio de um par de listas. Por exemplo, a lista [a, b, c] pode ser representada por meio de duas
listas:
L1 = [a, b, c, d, e] e L2 = [d, e]
Esse par de listas, que denotaremos por L1-L2, representa a diferena entre L1 e L2. Isso, naturalmente, s vai funcionar se a lista L2 for um sufixo de L1. Note que a mesma lista pode ser representada por diversos pares-diferena. Por exemplo, a lista [a, b, c] pode ser representada por:
[a,
[a,
[a,
[a,
...
b,
b,
b,
b,
c]
c,
c,
c,
- []
d, e] - [d, e]
d | T] - [d | T]
| T] - T
A lista vazia representada por qualquer par L-L. Como o segundo membro do par indica o final da
lista, este passa a poder ser acessado diretamente. Isso pode ser usado para uma implementao muito
mais eficiente da concatenao de listas. O mtodo proposto ilustrado na figura 12.1 e a correspondente relao em Prolog que denominaremos concat/3 pode ser representada por um nico fato:
concat(A1-Z1, Z1-Z2, A1-Z2)
A1
Z1
L1
A2
Z2
L2
<------------------------------ L3 ------------------------------->
Na figura acima. L1 representada por A1-Z1, L2 por A2-Z2 e o resultado, L3, por A1-Z2, o que
verdadeiro quando Z1=A2. Vamos usar a relao concat/3 para concatenar as listas [a, b, c] (repre120
sentada pelo par [a, b, c, | T1] - T1) e [d, e] (representada pelo par [d, e | T2] - T2). A concatenao
obtida pela simples unificao do objetivo proposto na consulta com a clusula que define concat/3.
?-concat([a, b, c | T1] - T1, [d, e | T2] - T2, Lista.
T1 = [d, e | T2]
Lista = [a, b, c, d, e | T2] - T2
onde cada nmero, com exceo dos dois primeiros, a soma dos dois nmeros anteriores. Definiremos um predicado fib(N, F) para computar para um dado nmero N, o ensimo numero F da sequncia de Fibonacci. Contaremos os nmeros da sequncia iniciando com N=1. O programa a seguir trata
inicialmente os dois primeiros nmeros de Fibonacci como casos especiais e depois especifica a regra
geral para a gerao da seqncia.
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.
Esse programa tende a refazer partes da computao. Isso pode ser facilmente constatado se gerarmos
o tracing da execuo de uma consulta, por exemplo, ?-fib(6, F). A repetio desnecessria de computaes intermedirias pode ser facilmente evitada se o programa "lembrar" cada um dos nmeros de
Fibonacci gerados como resultados parciais. A idia utilizar o predicado pr-definido assert/1 e adicionar esses resultados parciais base de dados na forma de fatos. Esses fatos devem preceder todas
as outras clusulas sobre fib para prevenir o uso da regra geral nos casos em que o resultado j conhecido. O procedimento modificado fibo/2 difere de fib/2 apenas pela incluso de um objetivo adicional:
fibo(1, 1).
fibo(2, 1).
fibo(N, F) :N > 2,
N1 is N-1, fibo(N1, F1), N2 is N-2, fibo(N2, F2),
F is F1+F2,
asserta(fibo(N, F)).
Guardar os resultados intermedirios uma tcnica convencional para evitar computaes repetidas.
No caso dos nmeros de Fibonacci podemos evitar essa repetio por meio do uso de outro algoritmo,
diferente do proposto acima. Esse novo algoritmo produzir um programa mais difcil de entender,
porm de execuo mais eficiente. A idia bsica no definir o ensimo nmero de Fibonacci como
a simples soma de seus dois antecessores imediatos, deixando que chamadas recursivas completem o
processamento recuando at os dois primeiros nmeros de Fibonacci. Ao invs disso, podemos trabalhar "para frente" comeando com os dois nmeros iniciais e computando os nmeros na seqncia
natural, parando quando o ensimo nmero for encontrado. A maior parte do trabalho executada
pelo procedimento
geraFib(M, N, F1, F2, F)
121
Por exemplo, um lao para executar processamento de entrada e sada poderia assumir a forma seguinte:
loop :repeat,
read(X),
(X = fim, !; processa(X,Y), write(Y), nl, fail).
Tal procedimento ir ler um termo, process-lo, imprimir alguma sada e falhar, ocasionando por
backtracking a repetio destas operaes at que o termo lido seja "fim".
Considere agora um programa para imprimir todos os elementos de uma lista L. Uma soluo recursiva seria:
imprimeLista([]).
imprimeLista([X | Y]) :write(X), nl, imprimeLista(Y).
Esse padro de recurso comum em Prolog. Pode-se entretanto generaliz-lo definindo um predicado:
for(X, Y) :X, Y, fail.
for(X, Y).
de modo que para todas as solues de X, Y ser ativado. O lao produzido pelo predicado for/2 termina quando no houver mais solues para X. A impresso de todos os elementos de uma lista assumiria ento a forma abaixo:
imprimeLista(L) :for(membro(X, L), (write(X), nl)).
122
Um outro exemplo seria o problema de listar todos os nmeros de um a dez. Uma soluo recursiva
seria:
listaNmeros(N) :- N > 10, !.
listaNmeros(N) :write(N), nl, N1 is N+1, listaNmeros(N1).
?-listaNmeros(1).
1
2
...
10
Ao invs disso definiremos um predicado in/3 que, por backtracking, retorna com os valores de 1 a N:
in(I, I, H) :H >= I.
in(I, L, H) :N is L+1, in(I, N, H).
?-for(in(I, 1, 10), (write(I), nl)).
O predicado for/3 pode ser combinado de diversas formas diferentes. Por exemplo, para imprimir
tabelas de multiplicao:
tabMult :for(in(I, 1, 10),
(for(in(J, 1, 10),
(K is I*J, out(K))), nl)).
out(X) :write(X), write(' ').
Como uma aplicao do conceito acima, e ao mesmo tempo como um exerccio de utilizao de operadores, eis como possvel fazer programas iterativos em Prolog se parecerem com Pascal:
:-op(1110, xfy, do).
:-op(1109, fx, for).
:-op(1108, xfx, to).
:-op(1107, fx, begin).
:-op(700, xfx, ':=').
do1(begin
do1(begin
do1(begin
do1(X) :-
end) :- !.
X ; Y) :- !, do1(X), do1(begin Y).
X) :- !, do1(X).
X.
X := Y :- X is Y.
(for X := Y to Z do U) :- for(in(X, Y, Z), do1(U)).
writeln(X) :- write(X), nl.
A partir da definio acima, podemos escrever o seguinte programa para calcular e imprimir os quadrados dos nmeros de 1 a 10:
quadrados :for I := 1 to 10 do
begin
K := I*I;
writeln(K);
end.
Essa construo, apesar da aparncia, continua sendo um programa em Prolog. Deve-se entretanto
advertir o leitor para no tomar esse exemplo como uma tentativa sria de implementar um interpretador Pascal em Prolog. Por exemplo, difcil modelar estruturas aninhadas tais como begin-end, porque tais constantes so na verdade delimitadores, como os parnteses, e no operadores.
RESUMO
H diversos critrios para a avaliao de programas, entre outros:
Correo;
123
Eficincia;
Transparncia e Legibilidade;
Modificabilidade;
Robustez;
Documentao.
O princpio dos refinamentos sucessivos uma boa maneira de organizar o processo de desen-
volvimento de programas. Em Prolog essa tcnica pode ser tanto aplicada s relaes e algoritmos quanto s estruturas de dados;
refinamentos:
Recurso;
Generalizao;
Uso de Grficos.
de grande utilidade o emprego de convenes de estilo para reduzir o perigo de erros de pro-
res para a depurao de programas, dentre as quais o mecanismo de tracing uma das mais
teis;
1,
2,
3,
4,
a).
b).
p(c)).
a).
12.3 Defina a relao adiciona(Lista, Item, NovaLista) para adicionar Item ao final de Lista produzindo NovaLista, sendo que tanto Lista quanto NovaLista devem ser representadas por paresdiferena.
12.4 Defina a relao inverte(Lista, ListaInvertida) onde ambas as listas so representadas por paresdiferena.
12.5 Defina os operadores e as relaoes necessrias para representar as construes if-then-else e
while-do no estilo Pascal.
124
significando que "X maior que Y", independentemente do que significa "maior que". Se os itens da
lista so nmeros, ento a relao mq/2 ser talvez definida por:
mq(X, Y) :- X > Y.
Se os itens da lista so tomos, ento a relao mq pode corresponder, por exemplo, ordem do cdigo ASCII correspondente aos caracteres. Vamos considerar que
classifica(Lista, Sada)
denote uma relao onde Lista uma lista de itens e Sada uma lista dos mesmos itens classificados
em ordem crescente, de acordo com a relao mq/2. Desenvolveremos trs definies de tal relao
em Prolog, baseadas em diferentes idias sobre a classificao de listas. A primeira delas bastante
simples, chegando a ser ingnua: Para classificar uma lista Lista:
(1) Encontre dois elementos adjacentes, X e Y, nesta ordem em Lista, tais que mq(X, Y). Troque as
posies de X e Y, obtendo Lista1 e, depois, classifique Lista1;
(2) Se no houver nenhum par de elementos adjacentes, X e Y, nesta ordem em Lista, ento esta j
est classificada.
O propsito da troca das posies dos itens X e Y, que aparecem fora de ordem em Lista que, aps a
troca, a nova lista obtida est mais prxima de ser uma lista classificada.. Aps um determinado nmero de trocas de posio, a lista estar completamente ordenada. Esse princpio de classificao
denominado "bubble sort" (ou classificao "blha"). A relao correspondente, bubblesort/2, apresentada abaixo:
bubblesort(Lista, Sada) :troca(Lista, Lista1), !, bubblesort(Lista1, Sada).
bubblesort(Sada, Sada).
troca([X, Y | Resto], [Y, X | Resto]) :mq(X, Y).
troca([Z | Resto], [Z | Resto1]) :troca(Resto, Resto1).
Um outro algoritmo simples de classificao o sort por insero (insert sort), que se baseia na seguinte idia: Para classificar uma lista no vazia, L = [X | R]:
(1) Classifique o corpo R da lista L;
(2)
Insira a cabea, X, de L no corpo classificado em uma posio tal que a lista resultante esteja classificada.
O resultado a lista completamente classificada. Esse algoritmo representado em Prolog pela relao insertsort/2:
125
insertsort([], []).
insertsort([X | Resto], Sada) :insertsort(Resto, Resto1), insere(X, Resto1, Sada).
insere(X, [Y | Sada], [Y | Sada1]) :mq(X, Y), !, insere(X, Sada, Sada1).
insere(X, Sada, [X | Sada]).
[3, 7, 8, 1, 4, 7, 6]
todos =< 5
todos > 5
[3,1, 4]
[7, 8, 7, 6]
classifica
classifica
[1. 3, 4]
concatena
[6, 7, 7, 8]
adiciona X
[3, 7, 8, 1, 4, 7, 6]
Se a lista a ser classificada estiver vazia, ento o resultado da classificao tambm uma lista vazia.
Uma implementao do quicksort/2 em Prolog apresentada na Figura 13.2. Um detalhe particular
dessa implementao que o elemento X que retirado de L sempre a cabea da lista. O procedimento que divide L em Maior e Menor uma relao de quatro argumentos:
divide(X, L, Maior, Menor)
A complexidade temporal deste algoritmo depende de nossa sorte ao dividirmos a lista a ser classificada. Se a lista for dividida em duas outras com aproximadamente o mesmo tamanho, ento a complexidade temporal do procedimento ser proporcional a n.log(n), onde n o tamanho da lista a classificar. Se, ao contrrio, a diviso resultar em duas listas de tamanho muito desigual, ento a complexidade ser da ordem n2. Anlises mais acuradas mostram que, felizmente, o desempenho mdio do
algoritmo quicksort/2 se aproxima bem mais da primeira situao do que da segunda.
126
Para encontrar X em uma lista L, esse procedimento percorre a lista elemento por elemento at que X
seja encontrado ou o fim da lista seja atingido. Isso se torna especialmente ineficiente no caso de listas muito longas. Para a representao de conjuntos h diversas estruturas em rvore que possibilitam
uma implementao muito mais eficiente da relao de pertinncia. Vamos considerar neste ponto as
denominadas "rvores binrias". Uma rvore binria, ou vazia, ou constituda por trs argumentos:
(1) uma raiz,
(2) uma sub-rvore esquerda, e
(3) uma sub-rvore direita.
A raiz pode ser qualquer coisa, mas as sub-rvores devem necessariamente ser rvores. Na Figura
13.3, apresentado um exemplo. A rvore ali mostrada representa o conjunto {a, b, c, d}. Os elementos do conjunto so armazenados nos nodos da rvore e, normalmente, sub-rvores vazias no so
representadas. Por exemplo, o nodo "b" possui duas sub-rvores que so ambas vazias.
raiz
sub-rvore
esquerda
sub-rvore
direita
d
H diversas maneiras de se representar uma rvore binria atravs de um termo Prolog. Uma possibilidade simples tornar a raiz da rvore o functor principal do termo e as sub-rvores os seus argumentos. Assim a rvore da Figura 13.3 seria representada pelo termo:
a(b, c(d)).
127
Entre outras desvantagens, essa representao requer um novo functor para cada nodo da rvore. Isso
pode ocasionar problemas, se os nodos, por sua vez, forem objetos estruturados. Uma maneira melhor
e mais usual de representar rvores binrias o seguinte: Emprega-se um smbolo especial para representar a rvore vazia e um functor para representar rvores no-vazias a partir de seus trs componentes (a raiz e as duas sub-rvores). Assumiremos o seguinte:
A rvore vazia ser representada pelo tomo "nil", e
Ser empregado um functor t, de forma que a rvore que possui uma raiz R, uma sub-rvore es-
Vamos agora considerar a relao de pertinncia para conjuntos, que denominaremos pertence/2. O
objetivo pertence(X,T) verdadeiro se X um nodo da rvore T. A relao pertence/2 pode ser definida da seguinte maneira:
X pertence a uma rvore T se:
A raiz de T X, ou
X est na sub-rvore esquerda de T, ou
X est na sub-rvore direita de T.
Tais regras podem ser traduzidas diretamente para Prolog, da seguinte maneira:
pertence(X, t(_, X, _)) :- !.
pertence(X, t(E, _, _)) :pertence(X, E).
pertence(X, t(_, _, D)) :pertence(X, D).
Obviamente o objetivo pertence(X, nil) ir falhar para qualquer valor de X. Vamos investigar agora o
comportamento do predicado pertence/2. Considerando a rvore apresentada na Figura 13.3, temos:
?-pertence(X, T).
X=a; X=b; X=c; X=d;
no
onde os valores de X so obtidos por backtracking. Sob o ponto de vista da eficincia, entretanto, o
procedimento pertence/2 to ineficiente quanto o emprego do predicado membro/2. Um aumento
considervel de eficincia poder entretanto ser obtido se houver uma relao de ordem entre os elementos do conjunto. Ento os dados na rvore podem ser ordenados da esquerda para a direita de
acordo com essa relao de ordem. Dizemos que uma rvore no-vazia t(E, R, D) est ordenada da
esquerda para a direita se:
(1) Todos os nodos na sub-rvore E so menores do que X,
(2) Todos os nodos na sub-rvore D so maiores do que X, e
128
no
Essas regras so programadas em Prolog como o procedimento pertence/2, mostrado abaixo na Figura
13.5, onde a relao mq(X, Y) significa que X maior do que Y. Se os itens armazenados na rvore
so numricos, ento a relao simplesmente X > Y.
pertence(X, t(_,X,_)).
pertence(X, t(E, R, _)) :mq(R, X), pertence(X, E).
pertence(X, t(_, R, D) :mq(X, R), pertence(X, D).
O procedimento pertence/2 pode tambm ser empregado para construir um dicionrio binrio. Por
exemplo, a consulta abaixo ir construir um dicionrio binrio D que contm os elementos 5, 3 e 8:
?-pertence(5, D), pertence(3, D), pertence(8, D).
D=t(t(D1, 3, D2), 5, t(D3, 8, D4))
As variveis D1, D2, D3 e D4 so sub-rvores no especificadas, que podem conter qualquer coisa. O
dicionrio que ser construdo ir depender da ordem dos objetivos na consulta.
Um comentrio sobre a eficincia da pesquisa em dicionrios binrios interessante neste ponto. Em
geral a busca por um item em um dicionrio binrio bem mais eficiente do que em uma lista. Tal
eficincia devida ao seguinte: Vamos supor que n seja o nmero de itens em nosso conjunto de dados. Se o conjunto representado por uma lista. ento o tempo esperado de pesquisa proporcional
ao tamanho n da lista. Em mdia iremos pesquisar a lista at a metade para encontrar um determinado
item, se os valores tiverem uma distribuio normal. Agora, se o conjunto for representado por um
dicionrio binrio, o tempo de procura ser proporcional "altura" da rvore, representada pelo maior
caminho entre a raiz e uma folha da rvore, dependendo portanto de sua conformao.
Diz-se que uma rvore binria (aproximadamente) balanceada se, para cada nodo da rvore, as duas
129
SIGNIFICADO
X pertence a S
Inserir X em S produzindo S1
Remover X de S produzindo S1
A relao pertence/2 foi definida na seo anterior. Definiremos agora a relao insere/3. mais fcil
inserir novos dados no nvel mais "alto" de uma rvore, de modo que um novo item se torna uma "folha" da rvore em uma posio tal que a ordenao da rvore seja preservada. Representaremos esse
tipo de insero por:
insFolha(D, X, D1)
Vamos agora considerar a operao remover/3. fcil remover uma folha, mas a remoo de um nodo
mais complicada. A remoo de uma folha pode, na verdade, ser definida como o inverso da insero, isto :
remFolha(D1, X, D2) :insFolha(D2, X, D1).
130
D1
D2
D3
D4
Entretanto, se X um nodo interno, isso no vai funcionar, devido ao problema ilustrado na Figura
13.7: X tem duas sub-rvores, E e D. Aps a remoo de X ficamos com uma lacuna na rvore e E e D
ficam desconectadas do restante dela, sem possibilidade de se conectarem ao nodo pai de X, A, uma
vez que este pode somente acomodar uma delas.
remove X
A
------------------------>
Se uma das duas sub-rvores, E ou D, estiver vazia, ento a soluo simples: A sub-rvore no-vazia
conectada a A. Se ambas forem no-vazias, ento uma idia a seguinte: O nodo mais esquerda de
D, digamos Y, removido de sua posio e conduzido a ocupar a lacuna deixada por X. Aps esta
transferncia, a rvore resultante continuar ordenada. Naturalmente a mesma idia funciona simetricamente, com a transferncia do nodo mais direita de E. De acordo com essas consideraes, a operao de remover um item de um dicionrio binrio pode ser programada conforme mostrado na
Figura 13.8. A transferncia do nodo mais esquerda da sub-rvore direita realizada pela relao:
trans(T, Y, T1)
131
A dificuldade aqui a insero de X como raiz de D. Vamos formular essa operao como a relao:
insRaiz(D, X, D1)
onde X o item a ser inserido como raiz de D e D1 o dicionrio resultante, com X como raiz. A
figura 13.9 ilustra as relaes entre X, D e D1.
Y
ou
E1
E2
D1
D2
A relao que impe todas essas restries exatamente a relao procurada, insRaiz/3. Assim, se X
foi inserido em E como raiz, ento as sub-rvores resultantes so E1 e E2 que, em Prolog, devem satisfazer a:
insRaiz(E, X, t(E1, X, E2))
132
X, t(nil, X, nil)).
X, D), X, t(E, X, D)).
Y, D), X, t(E1, X, t(E2, Y, D))) :X), insRaiz(E, X, t(E1, X, E2)).
Y, D), X, t(t(E, Y, D1), X, D2)) :Y), insRaiz(D, X, t(D1, X, D2)).
A distncia de identao, H, que pode ser adequadamente escolhida, um parmetro adicional para a
identao de rvores. Pela introduo de H, precisamos de um procedimento, ap(T, H), para apresentar T identada H espaos a partir da margem esquerda. A relao entre os procedimentos apresenta/1 e
ap/2 a seguinte:
apresenta(T) :- ap(T, H).
A Figura 13.11 mostra o procedimento apresenta/1 codificado em Prolog. O princpio adotado para
obter esse formato de sada pode ser facilmente adaptado para a apresentao dos mais diversos tipos
de rvores.
apresenta(T) :- ap(T, 0).
ap(nil, _).
ap(t(E, R, D), H) :H2 is H+2,
ap(D,H2),
tab(H), write(R), nl,
ap(E,H2).
13.6 GRAFOS
13.6.1 REPRESENTAO
As estruturas em forma de grafos so empregadas em diversas aplicaes, tais como a representao
133
t
2
v
5
(a)
(b)
Figura 13.12: (a) Grafo. (b) Grafo direcionado com custos associados aos arcos
Os grafos podem ser representados em Prolog de diversas maneiras. Um mtodo representar cada
aresta ou arco separadamente, por meio de uma clusula. Por exemplo, os grafos da figura acima podem ser representados pelos seguintes conjuntos de clusulas:
conecta(a,
conecta(b,
conecta(c,
conecta(d,
b).
c).
d).
b).
arco(s,
arco(t,
arco(t,
arco(v,
arco(u,
t,
u,
v,
u,
t,
3).
5).
1).
2).
2).
Um outro mtodo representar o grafo completo, como um nico objeto. Um grafo pode ser ento
representado por um par de conjuntos: nodos e arcos. Vamos escolher o functor grafo/2 para combinar
esses conjuntos em um par, e o functor ar/2 para as arestas. Ento o grafo da Figura 13.12(a) pode ser
representado por:
G1 = grafo([a,b,c,d],[ar(a,b),ar(b,c),ar(b,d),ar(c,d)])
Para representar um grafo direcionado escolheremos os functores grd/2 para o grafo e a/3 para os
arcos. A representao do grafo direcionado apresentado na Figura 13.12(b) fica ento:
G2 = grd([s,t,u,v],[a(s,t,3),a(t,v,1),a(t,u,5),a(u,t,2), a(v,u,2)])
Se cada nodo estiver conectado a pelo menos um outro nodo - isto : no h nodos "soltos" - o grafo
denominado "conexo". Na representao de grafos conexos pode-se omitir a lista de nodos, uma vez
que esta fica implicitamente definida pela lista de arestas.
Ainda um outro mtodo para representar grafos em Prolog associar a cada nodo a lista de todos os
nodos que lhe so adjacentes. Nessa representao um grafo uma lista de pares constitudos por um
nodo e a sua correspondente lista de nodos adjacentes. Os grafos anteriormente exemplificados podem
ento ser representados por:
G1 = [a [b], b [a, c, d], c [b, d], d [b, c]]
G2 = [s [t/3], t [u/5, v/1], u [t/2], v [u/2]]
134
onde C um caminho acclico entre A e Z em G. O caminho C representado por uma lista de nodos.
Se G o grafo representado na Figura 13.12(a), ento podemos escrever, por exemplo:
caminho(a, d, G, [a, b, d])
caminho(a, d, G, [a, b, c, d])
Uma vez que o caminho no deve conter nenhum ciclo, cada nodo pode aparecer na lista no mximo
uma vez. Um mtodo para se encontrar um caminho entre dois nodos em um grafo o seguinte:
Para encontrar um caminho acclico C entre os nodos A e Z de um grafo G:
Se A = Z, ento C = [A], seno
Encontrar um caminho acclico C1, de algum nodo Y at o nodo Z e encontrar um caminho de A at Y, evitando os nodos em C1.
Essa formulao implica em outra relao: Encontre um caminho sob a restrio de evitar um determinado conjunto de nodos. Assim, definiremos um segundo procedimento:
caminho1(A, C1, G, C)
...
...
...
seu final.
A Figura 13.13 sugere uma definio recursiva de caminho1/4. O caso bsico surge quando o nodo
inicial de C1 (Y, na figura) coincide com o nodo inicial de C, que A. Se isso no ocorrer, ento deve
haver um nodo X tal que:
(1) Y adjacente a X,
(2) X no est em C1, e
(3) C satisfaz a caminho1(A, [X | C1], G, C)
O programa completo apresentado na Figura 13.14, abaixo, onde membro/2 a relao de ocorrncia para listas e a relao adjacente(X, Y, G) significa que h um arco conectando os nodos X e Y no
grafo G.
caminho(A, Z, G, C) :-
135
Pode-se associar custos aos caminhos em um grafo. O custo total de um caminho a soma dos custos
associados aos arcos que formam o caminho. Se no h custos associados aos arcos, ento pode-se
falar sobre a "extenso" do caminho, contando uma unidade para cada um dos arcos que o constituem.
As relaes caminho/4 e caminho1/4 podem ser modificadas de modo a manipular os custos, por meio
da introduo de um argumento adicional para cada caminho:
caminho(A, Z, G, C, Custo)
e
caminho1(A, C1, Custo1, G, C, Custo)
onde Custo o custo do caminho C e Custo1 o custo do caminho C1. A relao adjacente/5 tambm resultado da adio de um argumento extra - o custo de um arco - relao original adjacente/4.
A Figura 13.15 mostra um programa que computa caminhos e os seus custos, podendo ser utilizado
para encontrar um "caminho de custo mnimo" entre dois nodos de um grafo. Isso obtido por meio
dos objetivos:
?-caminho(n1, n2, G, CaminhoMin, CustoMin),
not(caminho(n1, n2, G, _, Custo), Custo < CustoMin).
caminho(A, Z, G, C, Custo) :caminho1(A, [Z], 0, G, C, Custo).
caminho1(A, [A | C1], Custo1, G, [A | C1], Custo1).
caminho1(A, [Y | C1], Custo1, G, C, Custo) :adjacente(X, Y, CustoXY, G),
not membro(X, C1),
Custo2 is Custo1+CustoXY,
caminho1(A, [X, Y | C1], Custo2, G, C, Custo).
De modo semelhante tambm possvel encontrar um "caminho de custo mximo" entre qualquer par
de nodos em um grafo G atravs da conjuno de objetivos abaixo:
?-caminho(_, _, G, CaminhoMax, CustoMax),
not(caminho(_, _, G, _, Custo), Custo > CustoMax).
Deve-se ressaltar que esse mtodo de encontrar caminhos de custo mnimo ou mximo extremamente ineficiente, uma vez que investiga todos os caminhos possveis de forma completamente no
seletiva, sendo totalmente inadequado para grandes grafos, devido sua elevada complexidade temporal.
136
onde cada termo da forma X-Y denota uma aresta entre os nodos X e Y. Pode-se escolher qualquer
nodo na lista para raiz da rvore. As rvores geradoras so de interesse, por exemplo, em problemas
de comunicao, porque fornecem, com o menor nmero de linhas de comunicao possvel, um caminho entre qualquer par de nodos. Definiremos um procedimento:
arvG(T, G)
onde T uma rvore geradora de G. Assumiremos para isso que G um grafo conexo. Podemos imaginar a construo algortmica de uma rvore geradora da seguinte maneira: Iniciamos com um conjunto vazio de arestas, ao qual gradualmente vamos adicionando arestas de G, tomando cuidado para
que nunca seja gerado um ciclo, at que mais nenhuma aresta de G possa ser adicionada ao conjunto,
porque isso determinaria a gerao de um ciclo. O conjunto de arestas resultante define uma das rvores geradoras de G. A condio de no-ciclo pode ser mantida por meio de uma regra simples: Uma
aresta pode ser adicionada ao conjunto somente se um de seus nodos j pertence rvore geradora em
formao e o outro ainda no pertence. Um programa que implementa essa idia mostrado na Figura
13.11. A relao fundamental ali desenvolve(T1, T, G), onde todos os trs argumentos so conjuntos
de arestas. G um grafo conexo. T1 e T so subconjuntos de G tais que ambos representam rvores. T
uma rvore geradora de G, obtida pela adio de zero ou mais arestas de G a T1. Pode-se dizer que
T1 origina o desenvolvimento de T.
interessante desenvolver tambm um programa para a construo de rvores geradoras de forma
completamente declarativa, pelo simples estabelecimento de relaes matemticas. Assumiremos que
tanto grafos conexos como rvores sejam representados por meio de listas de arestas como no programa da Figura 13.16. As definies necessrias so:
(1) T uma rvore geradora de G se:
T um subconjunto de G,
T uma rvore, e
T "cobre" G, isto , todo nodo de G est tambm em T.
(2) Um conjunto de arestas T uma rvore se:
T conexo, e
T no possui ciclos.
Usando a relao caminho/4, definida na seo anterior, tais definies podem ser estabelecidas em
Prolog conforme mostrado na figura 13.17. Deve-se notar, entretanto, que o programa ali definido
de pequeno interesse prtico devido a sua ineficincia.
137
RESUMO
No presente captulo estudou-se diferentes mtodos de classificao de listas, tecendo conside-
13.2 Escreva um programa para descrever a relao quicksort/2, empregando pares-diferena na representao de listas.
13.3 O programa quicksort/2, apresentado neste captulo, possui um desempenho sofrvel quanta a
lista a ser classificada j est classificada ou quase classificada. Analise porque isso ocorre e
proponha modificaes no algoritmo capazes de solucionar tal problema.
13.4 Um outro algoritmo de classificao de listas baseia-se na seguinte proposta: Para classificar uma
lista L:
(1) Divida L em duas listas, L1 e L2, com aproximadamente o mesmo tamanho,
(2) Classifique L1 e L2, obtendo S1 e S2, e
(3) Intercale S1 e S2, obtendo a lista L classificada.
Implemente este princpio de classificao e compare sua eficincia com a obtida no programa
quicksort/2.
13.5 Defina os predicados:
arvBin(Objeto) e dicBin(Objeto)
para computar a altura de uma rvore binria. Assuma que a altura de uma rvore vazia zero e
que a de uma rvore com um nico elemento 1.
13.7 Defina a relao
lineariza(rvore, Lista)
139
Deve-se observar que o objetivo no apenas obter a situao final desejada. O que se quer realmente
o "plano" que nos permite alcan-la. Para isso necessrio descobrir uma seqncia de operaes
que permita realizar a transformao proposta.
Podemos pensar neste problema como um caso de explorao entre alternativas. Na situao inicial,
existe apenas uma possibilidade: colocar o bloco C na mesa. Aps fazer isto, surgem trs alternativas
possveis:
Colocar o bloco A na mesa, ou
Colocar o bloco A sobre o bloco C, ou
Colocar o bloco C sobre o bloco A.
Como o exemplo ilustra, dois conceitos devem ser considerados nesse tipo de problema:
(1) As situaes do problema, e
(2) Os movimentos ou aes vlidas que transformam uma situao em outra.
As situaes e os movimentos possveis formam um grafo direcionado, denominado "espao de estados". Um espao de estados para o problema exemplificado apresentado na Figura 14.2. Os nodos
do grafo correspondem a situaes do problema e os arcos correspondem a transies legais entre os
140
estados. Encontrar um plano cuja execuo solucione o problema original equivalente a encontrar
um caminho entre a situao inicial dada (o nodo inicial) e alguma situao final especificada, tambm denominada "o nodo objetivo".
C
A
B
B
C
A
A
B C
C
B
A
A
B C
B
A C
B
C
A
C
A B
A B C
B
A C
C
A B
A
B
C
A
C
B
A Figura 14.3 apresenta um outro exemplo do mesmo tipo de problema: o "jogo do oito" e a sua representao reduzida ao problema de caminhamento em um grafo. O jogo do oito um clssico da
pesquisa em inteligncia artificial e consiste em oito peas deslizantes, numeradas de 1 a 8 e dispostas
em uma matriz 3x3 de nove casas. Uma das casas est sempre vazia e qualquer pea a ela adjacente
pode ser movida para essa casa. Podemos imaginar que a casa vazia pode "mover-se", trocando de
lugar com qualquer uma das peas adjacentes. A situao final algum arranjo especial das peas,
como pode ser visto na Figura 14.3.
1
3
4
4
2
fcil construir aplicaes grficas similares para outros quebra-cabeas populares que se enquadram
no mesmo tipo de problema como, por exemplo, o problema das torres de Hani, ou de como conduzir
a raposa, o ganso e o milho atravs do rio. Neste ltimo problema h um bote que somente pode conduzir o fazendeiro e algum outro objeto. O fazendeiro tem que proteger o ganso da raposa e o milho
do ganso. Muitos problemas prticos tambm se encaixam nesse mesmo paradigma, dentre os quais
141
talvez o mais importante seja o "problema do caixeiro-viajante", que modelo formal de diversas
aplicaes prticas. Este problema definido por um mapa com n cidades interligadas por diversas
rodovias. A idia encontrar a rota mais curta, partindo de alguma cidade inicial, visitando todas as
demais cidades e retornando ao ponto de partida. Nenhuma cidade, com exceo da inicial, pode aparecer duas vezes no trajeto. O problema pode ser facilmente solucionado atravs de uma adaptao
dos procedimentos de caminhamento em grafos estudados no captulo 13.
Vamos resumir os conceitos introduzidos nestes exemplos. O espao de estados de um dado problema
especifica "as regras do jogo". Os nodos no espao de estados correspondem a situaes possveis e
os arcos correspondem a movimentos vlidos ou "passos de soluo". Um problema particular pode
ser definido por:
Um espao de estados,
Um nodo inicial, e
Uma condio-objetivo, isto , a situao a ser atingida. Denomina-se nodos-objetivos aos no-
Podemos associar custos s aes vlidas de um espao de estados. Por exemplos, custos associados
movimentao, no problema de organizao de blocos, indicariam que alguns blocos so mais difceis
de mover do que outros. No problema do caixeiro-viajante, os movimentos correspondem a viagens
diretas entre duas cidades. Ali os custos dos movimentos podem corresponder a distncias entre as
cidades envolvidas.
Nos casos em que temos custos associados aos movimentos, estaremos normalmente interessados em
obter as solues de menor custo possvel. O custo total de uma soluo a soma de todos os custos
associados aos que compem o caminho entre o nodo inicial e o nodo objetivo. Mesmo que no haja
custos, iremos sempre nos deparar com um problema de otimizao: qual o caminho mais curto entre
esses dois pontos?
Antes de apresentar alguns programas que implementam algoritmos clssicos para a pesquisa em espaos de estados, vamos estudar como um espao de estados pode ser representado em Prolog. Representaremos um espao de estados pela relao
s(X, Y)
que verdadeira se h um movimento vlido no espao de estados de um nodo X a um nodo Y. Dizemos que o nodo Y um sucessor de X. Se h custos associados aos movimentos, ento adicionaremos
um terceiro argumento, representando o custo do movimento:
s(X, Y, Custo)
Essa relao pode ser representada explicitamente no programa por meio de um conjunto de fatos,
entretanto, para espaos de estado de maior complexidade, isso se torna impraticvel. Assim a relao
s/3 usualmente definida de maneira implcita, pelo estabelecimento de regras para computar os nodos sucessores de um determinado nodo.
Outra questo de importncia geral como representar as situaes do problema, isto , os nodos do
espao de estados. A representao deve ser compacta, mas por outro lado deve permitir uma execuo eficiente das operaes requeridas, particularmente a relao de sucesso, s/3.
Vamos considerar, por exemplo, o problema de organizao de blocos apresentado na Figura 14.1.
Estudaremos um caso mais geral, em que existe um nmero qualquer de blocos organizados em uma
ou mais pilhas. O nmero de pilhas ser limitado a um determinado mximo para tornar o problema
mais interessante. Isso pode corresponder tambm a uma restrio realstica, uma vez que a um rob
que manipule blocos somente pode ser oferecido um espao de trabalho limitado, sobre uma mesa.
142
Uma situao do problema pode ser representada por uma lista de pilhas. Cada pilha, por sua vez, ser
representada por uma lista de blocos, ordenada de forma que o bloco no topo da pilha a cabea da
lista. pilhas vazias sero representadas por listas vazias. A situao inicial do problema apresentado
na Figura 13.1 pode ser representada por:
[[c, a, b], [], []]
Uma situao objetivo qualquer arranjo com uma pilha ordenada de todos os blocos. H trs situaes objetivo possveis:
[[a, b, c], [], []]
[[], [a, b, c], []]
[[], [], [a, b, c]]
A relao s/3 pode ser programada de acordo com a seguinte regra: Uma situao s2 sucessora de
alguma situao s1, se h duas pilhas, P1 e P2 em s1 e bloco no topo de P1 pode ser movido para P2,
configurando a nova situao s2. Como todas as situaes so representadas por listas de pilhas, isso
pode ser escrito em Prolog da seguinte maneira:
s(Pilhas, [P1, [T1 | P2] | OutrasPilhas]) :remove([T1 | P1], Pilhas, Pilhas1),
remove(P2, Pilhas1, OutrasPilhas).
remove(X, [X | L], L).
remove(X, [Y | L], [Y | L1]) :remove(X, L, L1).
onde Incio o nodo inicial do espao de estados e Soluo um caminho entre Incio e qualquer
nodo objetivo. Para o problema da organizao de blocos proposto, a consulta correspondente seria:
?-resolve([[c, a, b], [], []], Soluo).
Como resultado de uma pesquisa bem sucedida, a varivel Soluo ser instanciada para uma lista de
arranjos de blocos representando um "plano" para transformar o estado inicial em um estado onde os
trs blocos estejam em uma pilha organizada segundo a lista [a, b, c].
14.2 PESQUISA EM PROFUNDIDADE
Dada uma formulao do espao de estados de um problema, h diversas abordagens para encontrar o
caminho da soluo. Duas estratgias bsicas so: a pesquisa em profundidade (depth-first search) e a
pesquisa em amplitude (breadth-first search). na presente seo ser estudada a pesquisa em profundidade, que pode ser formulada a partir de uma idia bastante simples:
Para encontrar uma linha de soluo, Sol, a partir de um determinado nodo, N, at algum nodo
objetivo:
Se N um nodo objetivo, ento Sol = [N], seno
Se h um nodo sucessor de N, N1, tal que existe um caminho, Sol1, de N1 at algum nodo
Pode-se representar essa formulao em Prolog por meio da seguinte relao resolve/2:
resolve(N, [N]) :objetivo(N).
resolve(N, [N | Sol1]) :s(N, N1), resolve(N1, Sol1).
143
Esse programa , na verdade, uma implementao da estratgia de pesquisa em profundidade. O mtodo denominado "em profundidade" devido ordem em que as alternativas so exploradas no espao de estados. Sempre que o algoritmo de pesquisa em profundidade tem oportunidade de continuar
sua pesquisa escolhendo entre diversos nodos alternativos, a deciso conduz ao n que se encontra em
maior profundidade, isto , ao mais distante possvel do nodo inicial. A Figura 14.4 ilustra a ordem na
qual os nodos so visitados, que corresponde ordem seguida pelo Prolog na soluo da consulta:
?-resolve(a, Sol).
A figura 14.4 representa um espao de estados onde "a" o nodo inicial e "f" e "g" so nodos objetivos. A ordem na qual a pesquisa realizada dada pelo nmero entre parnteses esquerda de cada
nodo. A pesquisa em profundidade a mais adequada ao estilo recursivo da linguagem Prolog, uma
vez que esta, na execuo de seus objetivos, explora as alternativas segundo esse mesmo princpio.
Essa tcnica simples, fcil de programar, e funciona bem na maioria dos casos. Entretanto, h vrias
situaes em que o procedimento resolve/2, que adota o mtodo de pesquisa em profundidade , pode
se mostrar ineficiente. Se isso vai acontecer ou no, depende do espao de estados. Para complicar o
procedimento resolve/2, suficiente uma leve modificao no problema apresentado na Figura 14.4:
adicionar um arco do nodo h ao nodo d, originando um ciclo, como mostrado na Figura 14.5. Nesse
caso a pesquisa em profundidade ir ocorrer da seguinte maneira: inicia em a e desce at h, seguindo o
ramo mais esquerda no grafo. Neste ponto, ao contrrio do que ocorre na Figura 14.4, h tem um
sucessor, que d. Por sua vez, d tem h como sucessor, resultando em um ciclo infinito:
a, b, d, h, d, h, d, h, ...
(1)
(2)
(3)
(4)
(5)
(6)
(8)
(9)
(7)
(10)
(11)
(12)
Na relao profundidade/3 , Nodo o estado a partir do qual o nodo objetivo deve ser encontrado.
Caminho um caminho (uma lista de nodos) entre o nodo inicial e Nodo, enquanto que Soluo
uma extenso de Caminho, passando por Nodo, at atingir um nodo objetivo. Essas idia so apresentadas na Figura 14.6.
Nodo
inicial
Caminho
N
Nodo
objetivo
Soluo
Para garantir uma programao simplificada, os caminhos sero representados em nossos programas
como listas em ordem inversa, isto , iniciando em um nodo objetivo (ou corrente, durante a execuo) e terminando no nodo inicial. O argumento "Caminho" pode ser utilizado para dois propsitos:
(1) Garantir que o algoritmo no ir considerar os sucessores de Nodo que j foram visitados (deteco de ciclos), e
(2)
Na Figura 14.7 apresentamos um programa que executa a pesquisa em profundidade em grafos com a
deteco de ciclos, conforme foi anteriormente comentado. Vamos considerar agora uma variao
desse programa. Dois argumentos que ali aparecem, N e Caminho, podem ser combinados em uma
lista [N | Caminho], assim, ao invs de se ter um "nodo candidato", N, para ser adicionado a um caminho que conduza ao objetivo desejado, temos um "caminho candidato", C = [N | Caminho], para ser
ampliado at alcanar o objetivo. A programao do predicado correspondente, profundidade1(C,
Soluo) deixada como um exerccio.
resolve(N, Soluo) :profundidade([], N, Soluo).
profundidade(Caminho, N, [N | Caminho]) :objetivo(N).
profundidade(Caminho, N, Soluo) :s(N, N1),
not membro(N1, Caminho),
profundidade([N | Caminho], N, Soluo).
145
onde a pesquisa no permitida alm de ProfMxima. Essa restrio pode ser programada decrementando o valor estabelecido para ProfMxima a cada chamada recursiva, no permitindo que esse limite
se torne negativo. O programa resultante mostrado na Figura 14.8.
profundidade2(Nodo, [Nodo], _) :objetivo(Nodo).
profundidade2(Nodo, [Nodo | Sol], ProfMxima) :ProfMxima > 0,
s(Nodo, Nodo1),
Max1 is ProfMxima -1,
profundidade2(Nodo1, Sol, Max1).
(1)
(2)
(4)
(8)
(3)
(5)
(9)
(10)
(6)
(11)
(7)
(12)
A estratgia de pesquisa em amplitude no to fcil de programar quanto a de pesquisa em profundidade. A razo dessa dificuldade que temos de manter um conjunto de nodos candidatos alternativos, e no apenas um nodo como na pesquisa em profundidade. Entretanto, mesmo esse conjunto de
nodos no suficiente se desejarmos extrair um caminho-soluo por meio desse processo. Assim, ao
invs de manter um conjunto de nodos candidatos, iremos manter um conjunto de caminhos candidatos. Isso representado pela relao:
amplitude(Caminhos, Soluo)
que verdadeira quando algum caminho pertencente ao conjunto de candidatos Caminhos pode ser
estendido at algum nodo objetivo. O argumento soluo representa tal caminho estendido.
14.3.1 REPRESENTAO DO CONJUNTO DE CAMINHOS CANDIDATOS
Vamos adotar inicialmente a seguinte representao para o conjunto de caminhos candidatos: O conjunto ser representado como uma lista de caminhos, onde cada caminho uma lista de nodos em
ordem inversa, isto , a cabea da lista ser o nodo mais recentemente visitado e o ltimo elemento da
lista ser o nodo inicial da pesquisa. O conjunto inicia como um conjunto unitrio de caminhos candidatos:
[ [NodoInicial] ]
Um esquema para definir o processo de pesquisa em amplitude pode ser formulado da seguinte manei146
ra:
Para executar a pesquisa em amplitude, dado um conjunto de caminhos candidatos:
Se o primeiro caminho contm um nodo objetivo como cabea da lista que o representa, en-
Por exemplo, para o espao de estados apresentado na Figura 14.9, onde f e j so nodos objetivo, o
processo se desenvolve da seguinte maneira:
(1) O conjunto de caminhos candidatos inicialmente contm apenas o nodo raiz:
[ [a] ]
(3) Remover o primeiro caminho candidato, [b, a] do conjunto e determinar suas extenses de um
s nodo:
[ [d,b,a], [e,b,a] ]
(4) Remover [c, a] e acrescentar suas extenses ao final do conjunto de caminhos candidatos produzindo:
[ [d,b,a], [e,b,a], [f,c,a], [g,c,a] ]
(5) Remover [d, b, a] e acrescentar sua nica extenso ao final do conjunto de caminhos candidatos:
[ [e,b,a], [f,c,a], [g,c,a], [h,d,b,a] ]
"
#
Um programa que executa esse processo apresentado na Figura 14.10. Ali, todas as extenses aos
conjuntos candidatos so geradas atravs do predicado pr-definido bagof/3. Um teste para prevenir a
gerao de ciclos tambm includo. Note que no caso em que nenhuma extenso possvel, o predicado bagof/3 falha, portanto fornecida uma chamada alternativa ao procedimento amplitude/2. Os
predicados membro/2 e conc/3 so respectivamente as relaes de ocorrncia de um item em uma lista
e a concatenao de listas, ambas j estudadas.
O problema desse programa a ineficincia da operao conc/3. Isso entretanto pode ser reparado se
representarmos listas por meio de pares-diferena conforme foi apresentado no captulo 12. O conjunto de caminhos candidatos seria ento representado como um par de listas: Caminhos e Z, e escrito
como
Caminhos-Z
Introduzindo essa representao no programa da Figura 14.10, este pode ser sistematicamente transformado no programa apresentado na Figura 14.11. A transformao (simples) deixada ao leitor a
ttulo de exerccio.
resolve(Incio, Soluo) :-
147
Na representao em rvore, esse mesmo conjunto de caminhos candidatos representado pelo termo:
t(a, [t(b, [f(d), f(e)]), t(c, [f(f), f(g)])])
Essa representao pode parecer complexa e ainda mais consumidora de memria do que a representao em listas, entretanto isso apenas a aparncia superficial, devido representao compacta que
o Prolog utiliza para listas. Na representao do conjunto candidato por meio de listas, o efeito da
pesquisa em profundidade era atingido pela movimentao dos caminhos expandidos para o fim do
conjunto candidato. No possvel usar o mesmo truque na representao em rvore, portanto nosso
novo programa ser algo mais complicado. A relao chave aqui ser:
expande(Caminho, Arv, Arv1, Sol, Soluo)
A Figura 14.12 ilustra a relao entre os argumentos da relao expande/5. Sempre que esta for ativa148
da, as variveis Caminho e Arv j devem estar instanciadas. Arv uma sub-rvore do espao de estados e representa o conjunto de caminhos candidatos a um objetivo nessa sub-rvore. Caminho o
caminho entre o nodo inicial e a raiz de Arv. A idia geral da relao expande/5 produzir Arv1 como
uma extenso de um nvel de Arv. Se, entretanto, durante a expanso de Arv, um nodo objetivo for
encontrado, expande/5 produzir o correspondente caminho soluo. Assim a relao expande/5 ir
produzir dois tipos de resultados. O tipo de resultado produzido ser indicado pelo valor da varivel
Sol, como se segue:
(1) Sol = sim,
Soluo = um caminho para solucionar o problema, e
Arv1 = no instanciada.
Resultados desse tipo somente sero produzidos quando houver um nodo objetivo em Arv (uma
"folha-objetivo").
(2) Sol = no,
Soluo = no instanciada,
Arv1 = Arv expandida de um nvel.
Aqui Arv1 no contm nenhum desvio bloqueado, (desvios que no podem ser expandidos porque no possuem sucessores)
a
Caminho
Arv
Arv1
Soluo
A Figura 14.13 apresenta um programa completo, baseado nas idias discutidas acima, empregando
representao em rvore para o conjunto de caminhos candidatos. Um procedimento auxiliar
expTodos/6, similar ao expande/5, que realiza a expanso de um nvel sobre um conjunto de rvores e
armazena todas as rvores expandidas resultantes, removendo todas as rvores bloqueadas. Alm disso esse procedimento produz, atravs de backtracking, todas as solues encontradas nessa lista de
rvores.
149
(2)
Em (1) representa-se um espao de estados na forma de grafo. Se "a" arbitrado o nodo inicial, ento
o grafo pode ser desdobrado na forma da rvore mostrada em (2), que contm todos os caminhos nocclicos possveis desenvolvidos a partir de "a". A tcnica de pesquisa em amplitude gera caminhos de
soluo, um aps outro, ordenados de acordo com o seu tamanho: os caminhos mais curtos aparecem
primeiro. Isso importante se a otimizao (no que toca ao comprimento do caminho deva ser consi150
derada. A tcnica de pesquisa em amplitude garantidamente produz o caminho mais curto primeiro, o
que no ocorre com a tcnica de pesquisa em profundidade. O programa dado na Figura 14.13, entretanto, no leva em conta os custos associados aos arcos do espao de estados. Se o custo mnimo de
um caminho de soluo o critrio para otimizao (e no o seu tamanho), ento a tcnica de pesquisa em amplitude no suficiente.
Outro problema tpico associado com a pesquisa de espaos de estado o da complexidade combinatria. Para os domnios de problemas no-triviais, o nmero de alternativas a ser explorado to
grande que o problema da complexidade freqentemente se torna crtico. fcil entender porque isso
acontece: se cada nodo no espao de estados tem n sucessores, ento o nmero de caminhos de comprimento c a partir do nodo inicial nc (assumindo a inexistncia de ciclos). Assim, o conjunto de
caminhos candidatos cresce exponencialmente com o seu tamanho, o que conduz ao que se denomina
exploso combinatria. As tcnicas de pesquisa em profundidade e em amplitude no possuem nenhum recurso contra essa complexidade, uma vez que todos os caminhos candidatos so tratados de
forma no-seletiva.
Um procedimento mais sofisticado para a pesquisa em espaos de estados complexos deveria empregar informaes especificamente relacionadas ao problema de decidir a maneira mais promissora de
agir em cada ponto da pesquisa. Isso teria o efeito de projetar o processo de pesquisa diretamente para
o objetivo procurado, evitando os caminhos improdutivos. Informao associada ao problema especfico que pode ento ser empregada para dirigir a pesquisa denominada heurstica. Os algoritmos que
utilizam heursticas so denominados heuristicamente guiados e executam um tipo de pesquisa chamada pesquisa heurstica, que ser introduzida no prximo captulo.
RESUMO
Um espao de estados um formalismo para a representao de problemas de planejamento.
Um espao de estados representado por meio de um grafo direcionado cujos nodos corres-
pondem a situaes do problema e os arcos a movimentos vlidos que transformam uma situao em outra. Um problema particular definido por um nodo inicial e um nodo objetivo. Uma
soluo do problema corresponde ento a um caminho no grafo. Assim, a soluo do problema
reduzida procura por um caminho em um grafo.
Problemas de otimizao podem ser modelados pela associao de custos aos arcos de um es-
pao de estados.
&
'
#(
'
#(
A pesquisa em profundidade mais fcil de programar, mas suscetvel presena de ciclos
A implementao da estratgia de pesquisa em amplitude mais complexa, uma vez que requer
a manuteno de um conjunto de caminhos candidatos. Isso pode ser mais facilmente representado por meio de uma lista de listas, entretanto, o mtodo mais eficiente emprega representao
em rvore.
em profundidade quanto a pesquisa em amplitude so ferramentas pobres no combate a tal dificuldade, onde a aplicao de tcnicas de pesquisa heurstica se faz necessria.
EXERCCIOS
profundidade1(CaminhoCandidato, Soluo)
com deteco de ciclos, para encontrar um caminho, Soluo, como uma extenso de CaminhoCandidato. Represente ambos os caminhos como listas de nodos em ordem inversa, de forma que
o nodo objetivo a cabea da lista Soluo.
14.2 Escreva um procedimento para pesquisa em profundidade combinando os mecanismos de deteco de ciclos e o de limitao da profundidade pesquisada.
14.3 Escreva um procedimento denominado
apresenta(Situao)
para representar um estado do mundo dos blocos. Situao deve ser representada por uma lista de
pilhas e cada pilha como uma lista de blocos. Por exemplo, o objetivo
mostra( [ [a], [e, d], [c, b] ]).
ir ocasionar a apresentao de
e
c
a
d
b
=======================
14.4 Como se pode usar os procedimento de pesquisa em amplitude estudados para permitir a pesquisa a partir de um conjunto de nodos iniciais, ao invs de um nico?
14.5 Como se pode usar os procedimentos de pesquisa em profundidade e amplitude estudados para
executar a pesquisa em direo inversa, isto , a partir dos nodos objetivos retroagir at atingir
um nodo inicial. (Dica: redefina a relao s/2). Em que situaes a pesquisa retroativa seria vantajosa em relao pesquisa progressiva?
14.6 Considere que h custos associados aos arcos de um espao de estados. Escreva um programa
(com deteco de ciclos que efetue a progresso em profundidade ou em amplitude, buscando
minimizar o custo total da pesquisa.
152
g(n)
n
h(n)
t
A funo g(n) representa a estimativa do custo de um caminho timo de s a n. h(n) representa a estimativa do custo de um caminho timo de n a t.
Quando um nodo n encontrado pelo processo de pesquisa, temos a seguinte situao: Um caminho
de s a n j foi encontrado e o seu custo pode ser computado como a soma dos custos dos arcos nesse
caminho. Esse caminho no necessariamente um caminho timo de s a n (pode haver um caminho
153
melhor de s a n ainda no encontrado pela pesquisa) mas o seu custo pode servir *como um valor
estimativo g(n) do custo mnimo de s a n. O outro termo, h(n) mais problemtico porque o espao
entre n e t ainda no foi explorado nesse ponto. Assim o valor de h(n) tipicamente uma perspectiva
heurstica real, baseada no conhecimento geral do algoritmo acerca do domnio do problema particular que esta sendo solucionado. Como h(n) depende muito fortemente do domnio do problema, no
h um mtodo universal para a sua construo. Exemplos concretos de como essa previso heurstica
pode ser estabelecida sero apresentados mais adiante. De momento vamos assumir que a funo h
dada e nos concentrar nos outros detalhes do programa de pesquisa heurstica.
Pode-se imaginar que a pesquisa heurstica funcione da seguinte maneira: o processo de pesquisa
consiste em diversos subprocessos competindo entre si, cada um dos quais explorando suas prprias
alternativas, isto , executando a pesquisa sobre os ramos de suas prprias sub-rvores. As subrvores, por sua vez, so constitudas de outras sub-rvores que so exploradas por subprocessos de
subprocessos e assim por diante.
Entre todos esses processos competitivos, somente um est ativo em cada instante: o que lida com a
alternativa mais promissora naquele momento, isto a alternativa cujo valor para a funo f o mais
baixo. Os processos restantes permanecem congelados at que o valor de f do processo em curso seja
modificado de maneira que uma outra alternativa se revele mais promissora. Ento a atividade dada
a essa alternativa. Pode-se imaginar esse mecanismo de ativao e desativao da seguinte maneira:
ao processo trabalhando sobre a alternativa mais promissora dado um crdito e o processo permanece ativo at que esse crdito tenha se esgotado. Durante o perodo em que est ativo, o processo continua a expanso da sua sub-rvore, informando uma soluo se algum nodo objetivo for encontrado. O
crdito para cada passo de execuo definido pela estimativa heurstica da alternativa competidora
mais prxima. Esse comportamento exemplificado na Figura 15.2, que se divide em duas partes
principais. Em (a) representado uma mapa rodovirio (sem qualquer pretenso de representao em
escala) onde as cidades so os nodos e os arcos representam estradas, rotuladas com as respectivas
distncias em alguma unidade qualquer. O objetivo atingir a cidade t partindo de s, no menor trajeto
rodovirio possvel. os valores entre colchetes representam a distncia em linha reta entre cada cidade
e a cidade objetivo t. Esses valores sero utilizados para computar a funo heurstica que prev a
distncia que resta a ser percorrida a partir de cada nodo, h(n). Em (b) representada a ordem na qual
o mapa explorado por meio da pesquisa heurstica. A funo estimativa heurstica considera a distncia at ento percorrida e a que resta percorrer estimada em funo da distncia em linha reta at
t, dada em (a) pelos valores entre colchetes. os valores entre parnteses em (b) indicam a troca de
atividade entre os caminhos alternativos, representando a ordem em que os nodos so expandidos e
no a ordem em que so gerados. Na estimativa do custo da distncia que resta a percorrer a partir de
uma cidade X at o objetivo t, usamos a distncia em linha reta denotada por dist(X, t), de modo que:
f(X) = g(X) + h(X) = g(X) + dist(X, t)
No exemplo dado podemos imaginar a pesquisa heurstica como constituda por dois processos, cada
um deles explorando uma das duas rotas alternativas: o processo 1 a rota a partir de a e o processo 2 a
rota a partir de e. Nos estgios iniciais, o processo 1 mais ativo porque os valores de f ao longo desse caminho so os mais baixos. No momento em que o processo 1 est em c e o processo 2 ainda no
saiu de e, a situao muda:
f(c) = g(c) + h(c) = 6 + 4 = 10
f(e) = g(e) + h(e) = 2 + 7 = 9
ento, como f(e) < f(c), o processo 2 ativado, deslocando a rota para f enquanto o processo 1 espera.
Aqui, entretanto, a situao mais uma vez se inverte, pois:
f(f) = 7 + 4 = 11
f(c) = 6 + 4 =10
f(c) < f(f)
154
(a)
s
2
2
c
[4]
[4]
[7]
[5]
2
b
[2]
[4]
2
[3]
(b)
f(a)=2+5=7
e
(1)
4+4=8
f
(2)
6+4=10
7+4 =11
(5)
g
(4)
9+3=12
f(e)=2+7=9
(3)
9+2=11
(6)
11+0=11
Portanto o processo 2 pra e o processo 1 novamente ativado. mas em seguida, no nodo d, temos
f(d) = 12 > 11. A ativao passa mais uma vez ao processo 2 que, a partir da, acaba por atingir o objetivo t. Vamos programar a pesquisa heurstica como um refinamento do programa de pesquisa em
amplitude estudado no captulo anterior. O conjunto de caminhos candidatos ser mais uma vez representado por uma rvore dada por meio de dois tipos de termos:
(1)
n(N, F/G) representa um nico nodo (uma folha). N um nodo no espao de estados. G
g(N), o custo do caminho percorrido desde o nodo inicial at N, e F f(N)=G+h(N), e
(2) t(N, F/G, Subs) representa uma rvore com sub-rvores no-vazias. N a raiz da rvore, Subs
uma lista de sub-rvores, G g(N) e F o valor atualizado da funo f, isto , o valor de f
para o sucessor mais promissor de N. A lista Subs ordenada de acordo com valores crescentes
de f para as sub-rvores que a compem.
A atualizao dos valores de f necessria para permitir ao programa reconhecer a sub-rvore mais
promissora a cada nvel da rvore de pesquisa, isto , a sub-rvore que contm o nodo mais promissor.
Essa modificao dos valores de f conduz , na verdade, a uma generalizao da definio da funo f.
Tal generalizao amplia o domnio de definio de f, de nodos para rvores. Para um nico nodo da
rvore (uma folha), n, temos a definio original:
f(n)= g(n) + h(n)
Para uma rvore T cuja raiz n e cujos sucessores de n so m1, m2, m3, etc, temos:
f(T) = min f(mi)
Um programa para executar pesquisa heurstica segundo as linhas apresentadas dado na Figura 15.3
155
e que corresponde expanso da sub-rvore corrente enquanto o seu valor de f for menor ou igual a
Limite. Os argumentos de expande/6 so:
P:
Arv:
Sol:
no
ou
nunca;
156
Soluo: Um caminho-soluo a partir do nodo inicial, passando por Arv1 at um nodo objetivo
= sim;
Soluo =
Arv1 =
(2) Sol
= no;
No instanciada;
Arv expandida de forma que seu valor para f excede o valor de Limite.
Soluo =
Arv1 =
(3) Sol
= nunca;
No instanciada;
No instanciada..
Soluo =
Arv1 =
O ltimo caso indica que Arv uma alternativa "morta" qual no deve ser dada nenhuma chance de
expanso posterior. Esse caso surge quando o valor de f para Arv menor ou igual ao valor de Limite
mas a rvore no pode ser expandida porque nenhum nodo nela possui sucessor, ou ento tal sucessor
iria originar um ciclo.
Algumas das clusulas sobre expande/6 merecem uma explicao mais detalhada: As clusulas que
lidam com o caso mais complexo, quando Arv possui sub-rvores, isto :
Arv = t(N, F/G, [T | Ts])
Em tais casos, primeiro a sub-rvore mais promissora, T expandida. essa expanso no dado o
limite Limite, mas possivelmente algum valor mais baixo, dependendo dos valores de f para as outras
sub-rvores competidoras, Ts. Isso assegura que a sub-rvore em expanso em um determinado momento sempre a mais promissora. Aps o melhor caminho candidato ter sido expandido, um procedimento auxiliar, continua/7 decide o que fazer a seguir. Isso depende do tipo de resultado produzido
pela ltima expanso. Se uma soluo foi encontrada, ento ela relatada ao usurio, seno o processo continua.
A clusula que lida com o caso
Arv = n(N, F/G)
gera os nodos sucessores de N, juntamente com os custos dos arcos entre N e esses sucessores. O
procedimento sucLista/3 organiza uma lista de sub-rvores a partir desses nodos sucessores, tambm
computando seus valores para as funes f e g. A rvore resultante ento expandida enquanto o valor de Limite permitir. Se, por outro lado, no h sucessores, ento o nodo abandonado para sempre,
pela instanciao de Sol = nunca. Outras relaes a considerar so:
s(N, M, C): M um nodo sucessor de N no espao de estados. C o custo do arco que liga N a
M.
h(N, H): H uma estimativa heurstica do custo do melhor caminho do nodo N a algum nodo
objetivo.
maior(M): M algum valor especificado pelo usurio, reconhecidamente maior do que qualquer
um caminho de custo mnimo), se tal soluo existe. O programa da Figura 15.3, que produz
por backtracking todas as solues possveis, pode ser considerado admissvel se a primeira
soluo encontrada for uma soluo tima. Considerando que para cada nodo n no espao de
estados, h*(n) denota o custo do caminho timo de n at um nodo objetivo. Um teorema sobre
a admissibilidade de A* diz que: Um algoritmo A* que utiliza uma funo heurstica h tal que
para todos os nodos n de um espao de estados, h(n) h*(n), admissvel.
Esse resultado possui um grande valor prtico. Mesmo sem conhecer o valor exato de h* podemos
encontrar um limite inferior de h* e empreg-lo como se fosse h em A*. Isso garantia suficiente de
que A* ir produzir uma soluo tima. H um limite inferior trivial: h(n) = 0, para todo n no espao
de estados. Isso, na verdade, garante a admissibilidade. A desvantagem de se ter h = 0 que isso no
possui qualquer potencial heurstico e no oferece nenhuma orientao para a pesquisa. A* usando h
= 0 se comporta de maneira similar pesquisa em amplitude. Na verdade se reduz pesquisa em amplitude no caso em que a funo de custo dos arcos c(n, n') possui o valor 1 para todos os arcos (n, n')
do espao de estados. A falta de potencial heurstico resulta em elevada complexidade, assim desejaramos ter valores para h que fossem limites inferiores de h* (para assegurar a admissibilidade), mas
que fossem to prximos quanto possvel de h* (para assegurar a eficincia). No caso ideal, se conhecemos o valor de h*, usamos esse prprio valor. Um algoritmo A* usando h* encontra a soluo tima diretamente, sem a necessidade de backtracking.
15.2 UMA APLICAO DA PESQUISA HEURSTICA
Para aplicar o programa da Figura 15.3 a algum problema particular, temos que adicionar as relaes
especficas do problema em questo. Tais relaes, alm de definir o problema, tambm transmitem,
na forma de funes heursticas, a informao heurstica necessria sua resoluo. Os predicados
especficos do problema so (1) s(Nodo, Nodo1, Custo), que verdadeiro se existe um arco de custo
Custo entre Nodo e Nodo1 no espao de estados, (2) objetivo(Nodo), que verdadeiro se Nodo um
nodo objetivo no espao de estados, e (3) h(Nodo, H), onde H uma estimativa heurstica do custo do
caminho mais barato de Nodo at um nodo objetivo.
Como um exemplo iremos retomar o processo do jogo do oito, apresentado no captulo anterior. As
relaes especficas do jogo do oito so apresentadas na figura 15.5. Um nodo no espao de estados
corresponde, nesse caso, a alguma configurao das peas do jogo. No programa isto representado
por meio de uma lista contendo as posies correntes das peas. Cada posio representada por um
par de coordenadas X/Y. A ordem das posies na lista a seguinte:
(1) Posio da casa vazia,
(2) Posio da pea 1,
(3) Posio da pea 2, etc...
A situao objetivo (ver Figura 15.4) definida pela clusula
objetivo( [ 2/2, 1/3, 2/3, 3/3, 3/2, 3/1, 2/1, 1/1, 1/2 ] ).
Uma relao auxiliar usada no programa d(S1, S2, D), onde D a distncia horizontal entre S1 e S2,
somada com a distncia vertical entre essas mesmas posies. Queremos minimizar o tamanho das
solues, logo definiremos o custo de todos os arcos no espao de estados como sendo igual a 1. No
programa da Figura 15.5 definimos, como posies iniciais, os estados apresentados a seguir.
158
1 3
2 1
1 2
6 4
(a)
(b)
(c)
objetivo
No programa apresentado na Figura 15.5, a funo heurstica, h, definida por meio da relao h(Pos,
H), onde Pos uma posio do jogo e H a combinao de dois fatores:
(1) disTot: a distncia total das oito peas em Pos s suas casas correspondentes na situao
objetivo. Por exemplo, na posio inicial apresentada na Figura 15.4, disTot = 4.
(2)
seq: o "escore de sequncia", que mede o grau de ordenao das peas na posio corrente
em relao ordem estabelecida na configurao objetivo. seq computado pela soma dos escores de todas as peas de acordo com as seguintes regras:
Essa funo heurstica funciona bem, no sentido em que dirige, de maneira muito eficiente, a pesquisa
para o objetivo estabelecido. Por exemplo, na soluo do jogo proposto pelas configuraes iniciais
apresentadas na Figura 15.4(a) e (b), nenhum nodo expandido alm dos que compem o caminho
mais curto para a soluo. Isso significa que as solues timas, nesses dois casos so encontradas
diretamente, sem necessidade de backtracking. Mesmo o problema mais difcil, proposto na Figura
15.4(c) solucionado quase que diretamente. Um problema com essa heurstica, entretanto, que ela
no garante que a soluo tima ser encontrada sempre antes de qualquer outra soluo mais longa.
A funo h no satisfaz a condio de admissibilidade: h h* para todos os nodos do espao de estados. Por exemplo, na posio inicial da Figura 15.4(a) temos:
h = 4 + 3*6 = 22
e
h* = 4
Por outro lado, a medida de distncia total admissvel, pois para todas as posies vale:
disTot h*
Essa relao pode ser facilmente demonstrada por meio do seguinte argumento: Se simplificassemos o
problema, permitindo s peas passar umas por cima das outras, ento cada pea poderia alcanar sua
casa-objetivo seguindo uma trajetria cuja distncia exatamente a soma da sua distncia horizontal
com a distncia vertical at esse objetivo. Ento a soluo tima seria exatamente do tamanho computado por disTot. No problema original, entretanto, h interao entre as peas, que se encontram
umas nos caminhos das outras. Isso evita que as peas possam ser movidas ao longo de suas trajetrias mais curtas, o que assegura que o tamanho da soluo tima encontrada ser sempre maior ou igual
a disTot.
159
_,
3/3,
3/1,
1/1,
1/3,
1)
0)
0)
0)
0)
:::::-
!.
!.
!.
!.
!.
escore(1/3, 2/3,
escore(3/3, 3/2,
escore(3/1, 2/1,
escore(1/1, 1/2,
escore(_, _, 2).
0)
0)
0)
0)
::::-
!.
!.
!.
!.
RESUMO
Informaes heursticas podem ser usadas para estimar a distncia entre um nodo e um objetivo
O princpio da pesquisa heurstica orienta o processo de pesquisa de forma que o nodo expan-
O algoritmo de pesquisa heurstica A*, que adota esse princpio, foi implementado na Figura
15.3;
Para usar o algoritmo A* na soluo de problemas concretos, um espao de estados e uma fun-
o heurstica devem ser definidos. Para problemas de grande complexidade a dificuldade reside em encontrar a funo heurstica apropriada;
160
O teorema da admissibilidade ajuda a estabelecer se A*, usando uma particular funo heursti-
EXERCCIOS
15.1 Proponha outras aplicaes para o programa de pesquisa heurstica apresentado no presente captulo e formalize sua representao em Prolog.
15.2 Para os problemas propostos no exerccio 15.1, determine a admissibilidade da funo heurstica
escolhida.
161
a
3
c
2
4
2
(rio)
1
5
h
i
3
2
3
162
a-z via f
a-z via g
a-f
f-z
a-g
g-z
P1
P2
(b)
P3
Q1
Q2
Q3
Figura 16.3: (a) Para resolver P, resolva P1 ou P2 ou P3. (b) Para resolver Q resolva Q1 e Q2 e Q3.
Em princpio um nodo pode possuir simultaneamente alguns sucessores conectados por meio de arcos
E e outros por meio de arcos OU. Assumiremos, entretanto, que cada nodo somente possui sucessores
de um nico tipo. Todo grafo E/OU pode ser representado dessa forma atravs da introduo de arcos
163
OU auxiliares, quando necessrio. Assim um nodo a partir do qual so emitidos somente arcos E so
denominados nodos E e os que emitem apenas arcos OU so chamados nodos OU.
Na representao atravs de espaos de estado, uma soluo para um problema era dada por um caminho nesse espao de estados. na representao E/OU, uma soluo tem necessariamente que incluir
todos os subproblemas decorrentes de um nodo E, de maneira que esta no representada mais por
um caminho e sim por uma rvore. Essa rvore-soluo, que denominaremos T definida da seguinte
maneira:
O problema original P a raiz da rvore T;
Se P um nodo OU, ento somente um nico dentre os seus sucessores, juntamente com a sua
Se P um nodo E, ento todos os seus sucessores, juntamente com suas sub-rvores soluo
esto em T.
A Figura 16.4 ilustra essa definio. Ali temos custos associados aos arcos, que nos permitem formular um critrio de otimizao. Podemos, por exemplo, definir o custo de uma rvore soluo como
sendo a soma dos custos de todos os seus arcos. Como normalmente estamos interessados em minimizar os custos, a rvore soluo apresentada em (c) dever ser a preferida.
(a)
f
6
g
3
2
h
(b)
(c)
b
1
d
c
1
2
e
f
6
1
g
2
h
No temos, entretanto, que basear nossa medida de otimizao exclusivamente no custo dos arcos.
Algumas vezes pode ser mais natural associar os custos com os nodos ao invs de com os arcos, ou
mesmo com arcos e nodos simultaneamente. Resumindo os conceitos relacionados com a representao de grafos E/OU:
164
blemas;
Os nodos em um grafo E/OU correspondem aos problemas. As ligaes entre os nodos corres-
Um nodo do qual partem ligaes OU um nodo OU. Para solucionar um nodo OU basta solu-
Um nodo do qual partem ligaes E um nodo E. Para solucionar um nodo E deve-se solucio-
Para um determinado grafo E/OU, um particular problema especificado atravs de duas coi-
sas:
Para nos beneficiarmos da representao E/OU, os nodos relacionados a uma condio E devem
Custos podem ser associados aos arcos ou aos nodos ou a ambos, para a formalizao de um
critrio de otimizao.
a-z
a-z via f
a-f
f-z
a-f via d
a-d
f-z via i
d-f
f-i
i-z
a-d via b
a-b
b-d
Para encontrar o caminho mais curto no problema proposto na Figura 16.1, um grafo E/OU, incluindo
uma funo de custo, pode ser definido da seguinte maneira:
Os nodos OU so da forma X-Z, significando: encontre o caminho de X at Z;
Os nodos E so da forma X-Z via Y, significando: encontre o caminho mais curto de X at Z,
Um nodo X-Z um nodo objetivo (um problema primitivo), se X e Z esto diretamente conec-
tados no mapa;
O custo de cada nodo objetivo X-Z dado pela distncia "rodoviria" entre as cidades X e Z;
O custo de todos os demais nodos (no terminais) zero.
?
a
Podemos enunciar o problema das Torres de Hani da seguinte maneira: H trs "estacas", 1, 2 e 3, e
trs "anis", a, b e c (sendo a o menor e c o maior). Inicialmente, todos os anis esto empilhados
ordenadamente na estaca 1. O problema transfer-los para a estaca 3, na mesma ordem original, movendo apenas um anel de cada vez e respeitando a restrio de que nenhum anel pode ser colocado
sobre outro menor do que ele. Este problema pode ser decomposto em trs subproblemas:
(1) Colocar o anel a na estaca 3;
(2) Colocar o anel b na estaca 3;
(3) Colocar o anel c na estaca 3.
Tais objetivos, entretanto, no so mutuamente independentes. Por exemplo, o anel a pode ser colocado imediatamente na estaca 3, entretanto isso impedir a soluo dos outros dois subproblemas (a
menos que se desmanche a soluo do primeiro), porque o enunciado original do problema probe a
colocao de qualquer anel sobre outro menor do que ele. Por outro lado h uma ordenao conveniente dos objetivos que permite a derivao de uma soluo. Essa ordenao deriva do seguinte raciocnio: O terceiro objetivo (anel c na estaca 3) o mais difcil, porque a movimentao do anel c est
sujeita a mais restries. Uma boa idia em casos como esse, que na maioria das vezes funciona,
tentar atingir primeiro o objetivo mais difcil. A lgica por trs deste princpio a seguinte: como os
outros objetivos so mais fceis (no sujeitos a tantas restries, possivelmente sero atingidos sem a
necessidade de desmanchar a soluo do mais difcil. A estratgia de soluo que resulta desse princpio para o problema em questo :
(1) Primeiro satisfazer o objetivo: anel c na estaca 3;
166
Tais deficincias sero removidas gradualmente. Inicialmente definiremos uma estratgia mais adequada para a pesquisa em profundidade em grafos E/OU. Para isso ser introduzida uma relao binria que ser representada pelo operador infixo "--->". Por exemplo, o nodo a, ligados ao seus dois
sucessores "OU" ser representado pela clusula:
a ---> ou:[b, c]
Os smbolos "--->" e ":" so ambos operadores infixos que podem ser definidos da seguinte maneira:
:- op(600, xfx, '--->').
e
:- op(500, xfx, ':').
de modo que o grafo E/OU da Figura 16.4 pode ser completamente especificado por meio das clusulas:
a
b
c
e
f
--->
--->
--->
--->
--->
ou:[b, c].
e:[d, e].
e:[f, g].
ou:[h].
ou:[h, i].
objetivo(d).
objetivo(g).
objetivo(h).
A correspondente pesquisa em profundidade para grafos E/OU pode ser definida a partir dos seguintes
princpios:
Para resolver um nodo N:
(1) Se N um nodo objetivo, ento j est solucionado de forma trivial;
(2) Se N possui sucessores OU, ento solucione um deles. (Tente um de cada vez at que uma soluo seja encontrada);
(3) Se N possui sucessores E, ento solucione todos eles. (Tente um de cada vez at que todos estejam solucionados);
(4) Se as regras acima no produzirem uma soluo, ento assuma que o problema no pode ser resolvido.
Um programa para executar tais regras pode ser o seguinte:
resolve(Nodo) :objetivo(Nodo).
resolve(Nodo) :Nodo ---> ou:Nodos,
membro(Nodo1, Nodos), resolve(Nodo1).
resolve(Nodo) :Nodo ---> e:Nodos, resolveTodos(Nodos).
resolveTodos([]).
resolveTodos([Nodo|Nodos]) :resolve(Nodo), resolveTodos(Nodos).
onde membro/2 a relao usual de ocorrncia em listas. O programa acima, no entanto, tem ainda as
seguintes desvantagens:
168
Pode-se entretanto modific-lo facilmente para produzir uma rvore soluo. Para isso modifica-se a
relao resolve/1 de modo que ela passe a ter dois argumentos:
resolve(Nodo, ArvSol).
As trs formas de uma rvore soluo correspondem s trs clusulas da relao resolve/1 original.
Assim, para modificar o programa suficiente adicionar uma rvore soluo como segundo argumento de resolve/1. Na Figura 16.7 apresentado o programa resultante acrescido de um procedimento adicional, mostra/2 para a apresentao de rvores soluo. Tal programa, entretanto, ainda
est sujeito a laos infinitos. Uma maneira simples de evit-los manter o acompanhamento da profundidade da pesquisa, impedindo o programa de ultrapassar um certo limite. isso obtido por meio
da introduo de um terceiro argumento na relao resolve/2:
resolve(Nodo, ArvSol, ProfMax)
Como anteriormente, Nodo representa um problema a ser solucionado e ArvSol uma soluo cuja
profundidade no ultrapassa ProfMax, que a profundidade mxima permitida de pesquisa no grafo.
No caso em que ProfMax=0, nenhuma expanso adicional permitida. Por outro lado, se ProfMax>0,
ento Nodo pode ser expandido e seus sucessores sero examinados at uma profundidade limitada
em ProfMax-1. Isso pode ser facilmente incorporado ao programa da Figura 16.7. Por exemplo, a
segunda clusula de resolve/2, acrescida do novo argumento fica:
resolve(Nodo, Nodo ---> Arv, ProfMax) :ProfMax > 0, Nodo ---> or:Nodos,
membro(Nodo1, Nodos),
P1 is ProfMax-1, resolve(Nodo1, Arv, P1).
Esse procedimento de pesquisa em rpofundidade limitada pode tambm ser utilizado para simular a
pesquisa em amplitude. A idia aqui executar a pesquisa em profundidade de forma repetitiva, cada
vez com um limite maior de profundidade, at que uma soluo seja encontrada. Isto , tentar o problema com ProfMax=0, depois 1, depois 2, etc. Um programa que implementa essa idia :
simulaAmpl(Nodo, ArvSol) :tentaProf(Nodo, ArvSol, 0).
tentaProf(Nod, ArvSol, Prof) :resolve(Nodo, ArvSol, Prof),
Prof1 is Prof+1,
tentaProf(Nodo, ArvSol, Prof1).
resolve(Nodo, Nodo) :objetivo(Nodo).
resolve(Nodo, Nodo ---> Arv) :Nodo ---> ou:Nodos,
169
A desvantagem desse programa que ele reprete a pesquisa nos nveis superiores do grafo de pesquisa
cada vez que a profundidade limite incrementada.
16.4 PESQUISA HEURSTICA EM GRAFOS E/OU
Os procedimentos de pesquisa em grafos E/Ou apresentados na seo anterior executam sua tarefa de
forma sistemtica e exaustiva sem empregar qualquer perspectiva heurstica. Para problemas complexos, tais procedimentos se apresentam ineficientes, devido complexidade combinatria do espao de
pesquisa. portanto necessrio empregar funes heursticas com o propsito de evitar as alternativas
que acabaro por se tornar improdutivas. As perspectivas heursticas que sero introduzidas na presente seo iro se basear em estimativas numricas relacionadas com a dificuldade dos problemas em
grafos E/OU. O programa que ser desenvolvido pode ser visto como uma generalizao do programa
de pesquisa heurstica em espaos de estados apresentado no captulo anterior.
Inicialmente deve-se introduzir um critrio de otimizao baseado nos custos dos arcos em um grafo
E/OU. Primeiro a representao de tais grafos ser estendida para incluir custos. Por exemplo, o grafo
E/OU da Figura 16.4 pode ser representado atravs das seguintes clusulas:
a
b
c
e
f
--->
--->
--->
--->
--->
ou:[b/1, c/3]
e:[d/1, e/1].
e:[f/2, g/1].
ou:[h/6].
ou:[h/2, i/3].
objetivo(d).
objetivo(g).
objetivo(h).
O custo da rvore soluo ser definido como sendo a soma dos custos de todos os arcos na rvore. O
objetivo ser uma rvore soluo de custo mnimo. Para ilustrao empregaremos mais uma vez a
Figura 16.4.
interessante definir o custo de um nodo em grafos E/OU como sendo o custo da rvore soluo tima para esse nodo. Assim definido, o custo do nodo passa a representar a dificuldade desse nodo.
Assumiremos agora que podemos estimar os custos dos nodos no grafo E/OU, atravs de alguma funo heurstica h, mesmo sem conhecer suas rvores soluo. Tais estimativas sero utilizadas para
orientar a pesquisa. O programa comear a pesquisa no nodo inicial e, atravs de expanses realizadas sobre os nodos j visitados, construir gradualmente uma rvore de pesquisa. Esse processo ir
construir uma rvore mesmo nos casos em que o grafo E/OU no seja uma rvore. Em tais casos o
grafo ir se desdobrar em uma rvore pela duplicao de algumas de suas partes.
170
O processo de pesquisa ir, a cada momento, selecionar para expanso a rvore candidata mais promissora ao desenvolvimento da rvore soluo. A questo agora : Como a funo h usada para
estimar o quanto promissora uma certa rvore candidata? Ou o quanto promissor um determinado
nodo candidato a raiz de uma rvore soluo?
Para um determinado nodo N na rvore de pesquisa, H(N) ir denotar a sua dificuldade estimada. para
um nodo folha da rvore de pesquisa corrente, H(N) = h(N). Por outro lado, para um nodo interior
dessa rvore, no necessrio empregar a funo h diretamente porque j se possui alguma informao adicional sobre ele, isto , j conhecemos os seus sucessores. Portanto, como ilustrado pela Figura 16.8, a dificuldade de um nodo OU interior pode ser dada aproximadamente por:
H(N) = min(custo(N, Ni)+H(Ni))
onde custo(N, Ni) o custo do arco entre N e Ni. A regra de minimizao se justifica pelo fato de que,
para solucionar N, deve-se solucionar um dos seus sucessores. J a dificuldade de um nodo E, N,
aproximada por:
H(N) = S(custo(N, Ni) + H(Ni))
Nodo OU
Nodo E
custo(N, N1)
N1
custo(N, N1)
N2
...
H(N) = min(custo(N,Ni)+H(Ni))
N1
N2
...
H(N) = (custo(N,Ni)+H(Ni))
Em nosso programa de pesquisa ser mais prtico, ao invs de valores de H utilizar uma outra medida,
F, definida em termos de H da seguinte maneira: Seja M um nodo antecessor de N na rvore de pesquisa e custo(M, N) o custo do arco que interliga M a N. Podemos definir:
F(N) = custo(M, N) + H(N)
De acordo com isso, se M um nodo antecessor de N e N1, N2, ... so nodos sucessores de N, ento:
F(N) = custo(M, N) + min F(Ni)
se N um nodo OU, e
F(N) = custo(M, N) + S F(Ni)
se N um nodo E.
O nodo inicial (representado por S) no possui antecessor, de modo que tem o seu custo (virtual) de
chegada definido como zero. Entretanto, se h for igual a zero para todos os nodos objetivos do grafo
E/OU e uma rvore soluo tima houver sido encontrada, ento F(S) tem o custo desta rvore soluo, isto , a soma dos custos de todos os seus arcos. Em qualquer estgio da pesquisa, cada sucessor
de um nodo OU representa uma sub-rvore soluo alternativa. O processo de pesquisa sempre ir
escolher continuar a explorao atravs do sucessor cujo valor de F mnimo. Esse processo pode ser
acompanhado, mais uma vez, a partir da Figura 16.4. Inicialmente a rvore de pesquisa o prprio
nodo a. Depois essa rvore se expande at que uma soluo seja encontrada. A Figura 16.9 mostra
alguns momentos dessa expanso. para simplificar assumiremos que h = 0 para todos os nodos. Os
nmeros associados aos nodos na figura so os valores de F para esses nodos (que naturalmente sero
alterados durante a pesquisa, medida em que novas informaes forem se acumulando).
171
A expanso da rvore inicial de pesquisa , (A), produz a rvore (B). O nodo a um nodo OU, de
modo que temos duas rvores soluo candidatas: b e c. Como (F(b) = 1) < (F(c) = 3), a alternativa b
ser escolhida para expanso. Agora, at onde a alternativa b pode ser expandida? A expanso da
rvore escolhida pode prosseguir at que:
(1) O valor de F para o nodo b se torne maior do que o nodo c, que disputa com b a possibilidade
de ser expandido, ou
(2) Se torne claro que uma rvore soluo foi encontrada.
Na Figura 16.9, o candidato b o primeiro a ser expandido, uma vez que F(b) 3 = F(c). Inicialmente
os sucessores de b, d e e so gerados (situao C) e o valor de F para o nodo b aumentado para 3.
Uma vez que isso no excede o valor limite, a rvore com raiz em b continua a ser expandida. O nodo
d reconhecido como um nodo soluo e ento o nodo e expandido, resultando na situao D. Neste
ponto, F(b) = 9 > 3, o que interrompe a expanso da alternativa b. Isso impede que o processo perceba
que h tambm um nodo objetivo e que uma rvore soluo j foi gerada. Ao invs disso a atividade
passa agora ao nodo c. O "crdito" para a expanso de F(c) agora 9, uma vez que nesse ponto F(b) =
9. Dentro desse limite a rvore candidata de raiz c expandida at que a situao E seja atingida.
Agora o processo identifica que uma rvore soluo (que inclui os objetivos g e h) foi encontrada, e o
processo encerrado. Deve-se notar que a soluo final a mais barata das duas possveis rvoressoluo, correspondendo apresentada na Figura 16.4 (c).
16.4.1 UM PROGRAMA DE PESQUISA HEURSTICA EM GRAFOS E/OU
Um programa que implementa as idias apresentadas na seo anterior dado na Figura 16.118. Antes
de comentar os detalhes do programa iremos considerar as convenes empregadas na representao
escolhida para a rvore de pesquisa.
8 Este programa gera uma nica soluo, que garantidamente a mais barata se a funo heurstica empregada gerar valores no maiores do
que os custos reais das rvores-soluo.
A rvore de pesquisa pode ser:
172
(B)
(A)
0
1
candidato 1
candidato 2
(C)
(D)
candidato 2
c
candidato 2
candidato 1
7
6
candidato 1
h
(E)
a
1
b
1
d
1
e
g
3
H diversos casos a analisar, como pode ser visto na Figura 16.10. As diferentes formas que a rvore
de pesquisa assume surgem em decorrncia das seguintes possibilidades de combinao entre o tamanho da rvore e o seu estado de soluo.
TAMANHO:
173
folha(N, F, C)
N
F = C + h(N )
F1
F2
T1
F = C + min F(Ni)
...
T2
F=C+
F(Ni)
F1
T1
F2
...
T2
folSol(N, F)
F=C
arvSol(N, F, T)
F1
F = C + F1
T1
F=C+
F(Ni)
F1
T1
F2
T2
...
O functor principal usado para representar a rvore de pesquisa indica uma combinao dessas possibilidades, podendo ser um dos seguintes:
folha/3 arv/4 folSol/2 arvSol/3
Alm disso, a representao abrange pelo menos algumas das informaes seguintes:
O nodo raiz da rvore;
O valor da funo F para a rvore;
O custo C do arco no grafo E/OU que chega at a raiz da rvore;
A lista das sub-rvores;
A relao entre as sub-rvores (E ou OU).
A lista das sub-rvores estar sempre ordenada segundo valores crescentespara a funo F. Uma subrvore pode inclusive j estar solucionada, sendo, nesse caso, acomodada no final da lista.
174
onde Nodo o nodo inicial da pesquisa. O programa produz uma rvore soluo arvSol (se esta existir) que deve corresponder a uma soluo tima para o problema. Se esta ser realmente a soluo
mais barata, isso vai depender da funo heurstica h adotada pelo algoritmo. H um teorema, semelhante ao teorema da admissibilidade estudado no captulo anterior, que se refere a essa dependncia.
Seja CUSTO(N) uma funo que denota o custo de uma rvore soluo mais barata para um nodo N.
Se, para cada nodo N no grafo E/OU, a estimativa heurstica h(N) CUSTO(N), ento a relao eou/2
garantidamente ir encontrar uma soluo tima. Se a funo h(N) no satisfaz a essa condio, ento
a soluo encontrada pode no ser uma soluo tima. Uma funo heurstica trivial que satisfaz a
condio de admissibilidade h = 0 para todos os nodos. A desvantagem dessa funo , naturalmente, a ausncia de potencial heurstico. A relao chave acionada por eou/2
expande(Arv, Limite, Arv1, Sol)
onde Arv e Limite so argumentos de entrada e Arv1 e Sol so argumentos de sada. Seu significado
o seguinte:
Arv uma rvore de pesquisa que deve ser expandida;
Limite o limite para o valor de F que deve ser respeitado na expanso de Arv;
Sol um indicador cujo valor indica um dos seguintes trs casos:
(1) Sol=sim: Arv pode ser expandida dentro de Limite de forma a abranger uma rvore soluo Arv1,
(2) Sol=no: Arv pode ser expandida at Arv1, de forma que o valor de F para Arv1 exceda
Limite e no seja encontrada nenhuma sub-rvore soluo, ou
(3) Sol=nunca: Arv insolvel;
Arv1 , dependendo dos casos acima, uma rvore soluo, uma extenso de Arv cujo valor de F
similar a expande/4. Assim como em expande/4, Limite o limite de expanso de uma rvore e Sol
um indicador do que ocorreu durante a expanso (sim, no ou nunca). O primeiro argumento, entretanto uma lista de rvores-E ou de rvores-OU:
Arvs = e:[T1, T2, ...] ou Arvs = ou:[T1, T2, ...]
relaciona diversos objetos manipulados por expLista/4. NovaArvs a rvore expandida obtida por
expLista/4, OutrasArvs so as rvores restantes e Sol1 o estado de soluo de NovaArv. Esse procedimento manipula diversos casos, dependendo de Sol1 e de se a lista de rvores do tipo OU ou E.
Por exemplo, a clusula:
combina(ou:_, Arv, sim, Arv, sim)
significa: No caso em que a lista do tipo OU e a rvore expandida foi solucionada e sua rvore soluo Arv, ento toda a lista foi solucionada e a sua soluo a prpria Arv. Para a apresentao de
rvores soluo pode-se definir um procedimento semelhante a mostra/2, apresentado na Figura 16.7.
A construo de tal procedimento deixada como um exerccio para o leitor.
16.4.2 UM EXEMPLO DE DEFINIO DE PROBLEMA
Vamos agora formular o problema de seleo de roteiros sob a forma de um grafo E/OU de modo que
a formulao obtida possa ser usada diretamente pelo procedimento eou/2, definido na Figura 16.11.
Assumiremos que o mapa rodovirio ser representado pela relao:
s(Cidade1, Cidade2, D)
significando que h uma ligao direta entre Cidade1 e Cidade2 a uma distncia D. Assumiremos
tambm a relao:
chave(Cidade1-Cidade2, Cidade3)
significando que, para encontrar um roteiro entre Cidade1 e Cidade2, devemos considerar somente os
caminhos que passam por Cidade3 (Cidade3 ponto de passagem obrigatrio entre Cidade1 e Cidade2). Por exemplo, no mapa da Figura 16.1, f e g so pontos de passagem obrigatria entre a e z:
chave(a-z, f) e chave(a-z, g)
(2) Se no h nenhum ponto-chave entre a e z, ento simplesmente encontre alguma cidade b, vizinha de a, tal que exista um roteiro entre b e z.
Temos ento dois tipos de problemas que sero representados por:
(1) a-z: Encontre um roteiro entre a e z;
(2) a-z via y: Encontre um roteiro entre a e z passando em y.
Aqui "via" um operador infixo com prioridade superior a "-" e inferior a "--->". O grafo E/OU correspondente pode agora ser implicitamente definido por:
:- op(550, xfx, via).
A-Z ---> ou:Lista :bagof((A-Z via Y)/0, chave(A-Z, Y), Lista), !.
A-Z ---> ou:Lista :bagof((Y-Z)/D, s(A, Y, D), Lista).
A-Z ---> e:[(A-Y)/0, (Y-Z)/0).
objetivo(A-A).
177
RESUMO
A representao atravs de grafos E/OU um formalismo adequado para a representao de
Custos de arcos e nodos podem ser introduzidos em um grafo E/OU na modelagem de proble-
A soluo de problemas representados por meio de grafos E/OU envolvem pesquisa nesse gra-
fo. A pesquisa em profundidade executada de maneira sistemtica e fcil de programar, entretanto, pode ser ineficiente em problemas complexos devido exploso combinatria;
Funes heursticas podem ser introduzidas para estimar a dificuldade dos problemas. O princ-
pio da pesquisa heurstica pode ser usado como orientao, entretanto, a implementao dessa
estratgia no to simples.
EXERCCIOS
16.1 Defina em Prolog um espao E/OU para o Problema das Torres de Hani. Use a definio encontrada com os procedimentos de pesquisa estudados no presente captulo.
16.2 Considere algum jogo simples para duas pessoas e escreva a sua representao E/OU. Use um
programa de pesquisa em profundidade em grafos E/OU para encontrar estratgias vitoriosas sob
a forma de rvores E/OU.
178
APNDICE A
FUNDAMENTOS TERICOS DA
PROGRAMAO EM LGICA
)
.
0
+ %
*+ %
!
,-%
!
,-%
%
2
A.1 PROGRAMAO EM LGICA DE PRIMEIRA ORDEM
A.1.1 PROGRAMAS EM LGICA
Um programa em lgica constitudo por sentenas que expressam o conhecimento relevante para o
problema que se pretende solucionar. A formulao de tal conhecimento emprega dois conceitos bsicos: a existncia de objetos discretos, que denominaremos indivduos, e a existncia de relaes entre
eles. Os indivduos, considerados no contexto de um problema particular, constituem o domnio do
problema. Por exemplo, se o problema solucionar uma equao algbrica, ento o domnio deve
incluir pelo menos os nmeros reais.
Para que possam ser representados por meio de um sistema simblico tal como a lgica, tanto os indivduos quanto as relaes devem receber nomes. A atribuio de nomes , entretanto, apenas uma
tarefa preliminar na criao de modelos simblicos para a representao de conhecimento. A tarefa
principal a construo de sentenas expressando as diversas propriedades lgicas das relaes nomeadas. O raciocnio sobre algum problema baseado no domnio representado obtido atravs da
manipulao de de tais sentenas por meio de inferncia lgica. Em um ambiente tpico de programao em lgica, o programador estabelece sentenas lgicas que, reunidas, formam um programa. O
computador ento executa as inferncias necessrias para a soluo dos problemas propostos.
A lgica de primeira ordem possui dois aspectos: sinttico e semntico. O aspecto sinttico diz respeito s frmulas bem-formadas (fbfs) admitidas pela gramtica de uma linguagem formal. O aspecto
semntico est relacionado com o significado atribudo aos smbolos presentes nas fbfs da teoria.
Apresenta-se a seguir os principais conceitos necessrios para a definio de linguagens lgicas de
primeira ordem.
DEFINIO A1: Teoria de Primeira Ordem
Uma Teoria de Primeira Ordem (TPO) consiste em uma linguagem de primeira ordem definida sobre
um alfabeto de primeira ordem, um conjunto de axiomas e um conjunto de regras de inferncia. A
linguagem de primeira ordem consiste nas fbfs da teoria. Os axiomas e regras de inferncia so utilzados para a derivao dos teoremas da teoria.
DEFINIO A2: Alfabeto de Primeira Ordem
Um Alfabeto de Primeira Ordem constitudo por sete classes de smbolos:
(i) Variveis Individuais,
(ii) Constantes Individuais,
(iii) Constantes Funcionais,
(iv) Constantes Predicativas,
(v) Conetivos,
(vi) Quantificadores, e
(vii) Smbolos de Pontuao.
179
As classes (v} a (vii) so as mesmas para todos os alfabetos, sendo denominadas smbolos lgicos. As
classes (i) a (iv) podem variar de alfabeto para alfabeto e so denominadas smbolos no-lgicos. Para
qualquer alfabeto de primeira ordem, somente as classes (ii) e (iii) podem ser vazias. Adotaremos aqui
as seguintes convenes para a notao dos smbolos do alfabeto: As variveis individuais sero denotadas por cadeias de smbolos iniciando com letras minsculas (a, b, ..., z). Os conetivos so: , ,
, , e . Os quantificadores so e . Os smbolos de pontuao so '(', ')' e ','. Adotaremos a seguinte hierarquia para a precedncia entre conetivos e quantificadores. Em ordem decrescente:
, ,
,
DEFINIO A3: Termo
Um termo definido recursivamente da seguinte maneira:
(i) Uma varivel individual um termo;
(ii) Uma constante individual um termo;
(iii) Se f uma funo n-ria e t1, t2, ..., tn so termos, ento f(t1,t2,...,tn) um termo.
DEFINIO A4: Frmula Bem-Formada (fbf)
Uma frmula bem-formada (fbf) definida indutivamente da seguinte maneira:
(i) Se p uma constante predicativa e t1, t2, ..., tn so termos, ento, p(t1, t2, ..., tn) uma
frmula bem formada (denominada frmula atmica ou simplesmente tomo);
(ii) Se f e g so frmulas bem formadas, ento (f), (f g), (f g), (f g) e (f g) so
frmulas bem formadas;
(iii) Se f uma frmula bem formada e X uma varivel, ento (Xf) e (Xf) so frmulas
bem formadas.
Adotou-se a conveno de escrever a implicao de modo reverso, isto (f g), devido a sua convenincia na representao da frma clausal, que ser descrita mais adiante. Tambm, por abuso da linguagem, de agora em diante se empregar indistintamente a palavra frmula para fazer referncia a
frmulas bem formadas.
DEFINIO A5: Linguagem de Primeira Ordem
Uma linguagem de primeira ordem sobre um alfabeto de primeira ordem o conjunto de todas as
frmulas bem formadas construdas a partir dos smbolos deste alfabeto.
A semntica informal dos conetivos e quantificadores a seguinte: representa a negao, a conjuno (e), a disjuno (ou), a implicao e a equivalncia. o quantificador existencial, tal
que 'X' significa 'existe um X', enquanto que o quantificador universal e 'X' significa 'para todo
X' ou 'qualquer que seja X'. Assim a semntica informal de X(p(X, g(X)) q(X) r(X) : 'para
todo X, se q(X) verdadeiro e r(X) falso, ento p(X,g(X)) verdadeiro'.
DEFINIO A6: Escopo de um Quantificador e Ocorrncia Ligada de uma Varivel em uma
Frmula
O escopo de X em Xf e de X em Xf f. Uma ocorrncia ligada de uma varivel em uma frmula uma ocorrncia que imediatamente segue o quantificador e qualquer ocorrncia dessa mesma
varivel no escopo desse quantificador. Qualquer outra ocorrncia de varivel dita ser livre.
DEFINIO A7: Frmula Fechada
Uma frmula dita ser fechada quando no contm nenhuma ocorrncia de variveis livres.
180
181
Lgica Matemtica
Clculo de Predicados de Primeira Ordem
Forma Clausal
Clusulas de Horn
ralmente a interpretao pretendida sempre ser um modelo. A partir de agora emprega-se os termos
constante, funo e predicado para designar respectivamente constantes individuais, constantes funcionais e constantes predicativas. A lgica de primeira ordem oferece mtodos para a deduo dos
teoremas presentes em alguma teoria. Estes podem ser caracterizados como sendo as frmulas que so
consequncia lgica dos axiomas da teoria, isto , que so verdadeiras em todas as interpretaes que
so modelos para cada um dos axiomas da teoria. Em particular, cada teorema deve ser verdadeiro na
interpretao pretendida da teoria. Os sistemas de programao em lgica que so objeto do presente
estudo adotam o Princpio da Resoluo como nica regra de inferncia. Suponha-se que se deseja
provar que a frmula
Y1 ... Yr (b1 ... bn)
uma consequncia lgica de um programa P. Com esse objetivo emprega-se o Princpio da Resoluo por meio de um sistema de refutao, isto , a negao da frmula a ser provada adicionada aos
axiomas e uma contradio deve ser derivada. Negando-se a frmula que se deseja provar obtem-se a
clusula objetivo:
b1, ..., bn
A partir dessa frmula objetivo e operando de forma top-down sobre os axiomas de P, o sistema deriva sucessivas clusulas objetivo. Se, em um determinado momento, for derivada a clusula vazia,
ento uma contradio foi obtida (a clusula vazia contraditria) e esse resultado assegura que
Y1...Yr (b1 ... bn) uma conseqncia lgica de P. De agora em diante se usar simplesmente
objetivo para designar clusulas objetivo.
Do ponto de vista da prova de teoremas, o nico interesse demonstrar a existncia da relao de
conseqncia lgica. Por outro lado, do ponto de vista da Programao em Lgica, o interesse se concentra muito mais sobre as ligaes que foram realizadas sobre as variveis Y1, ..., Yr, uma vez que
estas fornecem o resultado da execuo do programa. Segundo [Llo 84], a viso ideal de um sistema
de Programao em Lgica a de uma caixa preta para a computao de ligaes e o nico interesse
reside no seu comportamento de entrada e sada, isto , as operaes executadas internamente pelo
sistema deveriam ser transparentes para o programador. Infelizmente tal situao no ocorre, em maior ou menor grau nos sistemas Prolog atualmente disponveis, de forma que muitos programas Prolog
somente podem ser entendidos a partir de sua interpretao operacional, devido ao emprego de cuts e
outros mecanismos extra-lgicos.
DEFINIO A18: Interpretao
Uma interpretao de uma linguagem L de primeira ordem constituda por:
(i)
(ii)
(iii)
(iv)
g
V
F
V
F
f
F
F
V
V
fg
V
F
F
F
fg
V
V
V
F
fg
V
V
F
V
fg
V
F
F
V
PROPOSIO A.1
Seja S um conjunto de frmulas fechadas e f uma frmula fechada de uma linguagem L de primeira
ordem. Ento f conseqncia lgica de S se e somente se S {f} insatisfatvel.
Prova:
() Vamos supor que f seja conseqncia lgica de S. Se S {f} satisfatvel, ento existe uma
interpretao I da linguagem L tal que I modelo de S {f}. Por outro lado, se f conseqncia lgica de S, ento I tambm modelo de f, ou seja de {f, f}, o que no possvel.
Logo S {f} insatisfatvel.
() Inversamente, vamos supor que S {f} seja insatsfatvel e seja I uma interpretao da linguagem L. Suponhamos que I seja um modelo para S. Uma vez que S {f} insatisfatvel, I
no pode ser um modelo para f. Assim, I um modelo para f e portanto f conseqncia lgica de S.
Aplicando essas ltimas definies a programas em lgica, constata-se que quando se fornece um
objetivo G ao sistema com o programa P carregado, est-se pedindo ao sistema para provar que P
{G} insatisfatvel. Se G o objetivo b1, ..., bn com as variveis Y1, ..., Yr, ento a Proposio A.1
estabelece que provar que P {G} insatisfatvel equivale a provar que Y1 ... Yr (b1 ... bn)
conseqncia lgica de P. Assim o problema bsico a determinao da insatisfatibilidade de P
{G}, onde P um programa e G um objetivo. De acordo com a definio de insatisfatibilidade, isso
implica em mostrar que nenhuma interpretao de P {G} um modelo.
DEFINIO A28: Termo Bsico e tomo Bsico
Um termo bsico um termo que no contm variveis. Da mesma forma um tomo bsico um tomo que no contm variveis.
DEFINIO A29: Universo de Herbrand
Seja L uma linguagem de primeira ordem. O Universo de Herbrand, UL para L o conjunto de todos
os termos bsicos que podem ser obtidos a partir das constantes e funes presentes em L. No caso em
que L no possui constantes, introduz-se uma constante (por exemplo, "a") para a formao de termos
bsicos.
DEFINIO A30: Base de Herbrand
Seja L uma linguagem de primeira ordem. A Base de Herbrand, BL para L o conjunto de todos os
tomos bsicos que podem ser formados usando os predicados de L com os termos bsicos do correspondente Universo de Herbrand como argumentos.
DEFINIO A31: Interpretao de Herbrand
Seja L uma linguagem de primeira ordem. Uma interpretao sobre L uma Interpretao de Herbrand, se as seguintes condies forem satisfeitas:
(i) O domnio da interpretao o Universo de Herbrand, UL;
(ii) As constantes em L so atribudas a si prprias em UL;
(iii) Se f uma funo n-ria em L, ento a f atribudo o mapeamento de (UL)n em UL definido por (t1, ..., tn) f(t1, ..., tn).
Nenhuma restrio feita sobre a atribuio de predicados em L de forma que diferentes interpretaes de Herbrand surgem quando se emprega diferentes atribuies sobre eles. Uma vez que, para as
interpretaes de Herbrand, as atribuies de constantes e funes fixa, possvel identificar uma
interpretao de Herbrand como um subconjunto da Base de Herbrand. Para toda interpretao de
Hebrand, o correspondente subconjunto da Base de Herbrand o conjunto de todos os tomos bsicos
que so verdadeiros com respeito a essa interpretao. Inversamente, dado um subconjunto arbitrrio
da Base de Herbrand, h uma interpretao de Herbrand que a ele corresponde.
185
Segue da definio de umg que se e so ambos umg's de {E1, ..., En}, ento Ei variante de Ei.
A Proposio A.5 garante ento que Ei pode ser obtida de Ei por simples renomeao de variveis.
DEFINIO A41: Conjunto de Desacordo
Seja S um conjunto finito de expresses simples. O conjunto de desacordo de S definido da seguinte
maneira: Localizamos a posio do smbolo mais esquerda que no o mesmo para todas as expresses de S e extramos de cada uma delas a sub-expresso que inicia com tal smbolo. O conjunto de
todas as sub-expresses assim retiradas o conjunto de desacordo de S.
ALGORITMO DA UNIFICAO
(i) Faa k = 0 e k = ,
(ii) Se Sk nico, ento pare: k um umg de S,
seno encontre o conjunto de desacordo Dk de Sk;
(iii) Se existem V e t em Dk tais que V uma varivel que no ocorre em t,
ento faa k+1 = k{V/t}, incremente o valor de k e volte ao passo (ii),
seno pare: S no unificvel.
Na forma apresentada acima, o algoritmo da unificao no-determinstico, uma vez que podem ser
consideradas diversas escolhas para V no passo (iii), entretanto a aplicao de quaisquer dois umg's
produzidos pelo algoritmo ir conduzir a expresses que diferem entre si somente pelo nome das variveis envolvidas. Deve ficar claro tambm que o algoritmo sempre termina, uma vez que S contm um
conjunto finito de variveis e cada aplicao do passo (iii) elimina uma delas. Ainda devemos considerar que no passo (iii) uma verificao feita para garantir que V no ocorre em t. Tal verificao
denominada verificao de ocorrncia (occurs check).
TEOREMA A.1 (TEOREMA DA UNIFICAO)
(a) S um conjunto unificvel de de expresses simples se e somente se o Algoritmo da Unificao
termina, retornando um umg para S.
(b) S no um conjunto unificvel de de expresses simples se e somente se o Algoritmo da Unificao termina, retornando a resposta "no".
DEFINIO A42: Substituies Resposta
Seja P um programa e G um objetivo. Uma substituio resposta para P {G} uma substituio
para as variveis de G.
Entende-se que tal substituio no precisa necessriamente conter uma ligao para cada uma das
variveis em G. Em particular, se G no contm variveis, a nica substituio possvel a substituio identidade.
DEFINIO A43: Substituio Resposta Correta
Seja P um programa, G um objetivo A1, ..., Ak e uma substituio resposta para P {G}. Dizemos que uma substituio resposta correta para P {G} se ((A1 ... Ak)) conseqncia
lgica de P.
A partir da Proposio A.1 pode-se afirmar que uma substituio resposta correta se e somente se
P {((A1 ... Ak))} for insatisfatvel. Esta definio de substituio resposta correta captura o
sentido intuitivo de "resposta correta". Da mesma forma que fornece substituies respostas, um sistema de programao em lgica pode tambm retornar com a resposta "no". Dizemos que a resposta
"no" correta, se P {G} for satisfatvel.
188
3
3
4' (
M (g) =
| C(t0, ..., tn-1) (V(t0, g), ..., V(tn-1, g)) C'
onde V(t, g) = g(t) se t uma varivel individual e em f'(V(t0', ..., V(tm-1', g)) os ti so da forma f(t0', ..., tm-1').
(ii)
M (g) =
| A M (g) | A.
(iii)
M (g) =
| A B M (g) |= A e M (g) |= B.
(iv)
M (g) |= X A M (g{d\X}) |= A,
onde g{d\X} uma funo de atribuio idntica a g, exceto para a varivel X, qual atribudo o valor d.
189
As condies de verdade para os demais conetivos podem ser estabelecidas a partir das seguintes
equivalncias:
(v)
A B (A B)
(vi)
A B A B
(vii) A B (A B) (B A)
(viii) X A X A
DEFINIO A47: Frmula Universalmente Vlida
Uma fbf A dita ser universalmente vlida se e somente se, para todo frame M e para toda funo de
atribuio g, M (g) =
| A.
TEOREMA A.2: Completeza do Clculo de Predicados
Uma fbf do clculo de predicados de primeira ordem um teorema se e somente se universalmente
vlida.
190
BIBLIOGRAFIA
[AMB 87] AMBLE, T.: Logic Programming and Knowledge Engineering. Reading: AddisonWesley, 1987, 348p.
[AND 93]
ANDREWS, J.: Prolog Frequently Asked Questions. E-Text (Internet) by jamie@cs.sfu.ca. Stanford University, 1993.
[ARI 86]
[BOW 82] BOWEN, K.A.; KOWALSKI, R.A.: Amalgamating Language and Metalanguage in
Logic Programming. In: LOGIC PROGRAMMING. London: Academic Press, 1982.
366p. p.153-172.
[BOW 85] BOWEN, K.A.: Meta Level Programming and Knowledge Representation. New
Generation Compuiting, Tokyo, v.3 n.12, p.359-383, Oct. 1985.
[BOW 86] BOWEN, K.A.: Meta Level Techniques in Logic Programming. In: INTERNATIONAL CONFERENCE ON ARTIFICIAL INTELLIGENCE AND ITS APPLICATIONS, 1986, Singapore. Proceedings ... Amsterdam: North-Holland, 1986. p.262-271.
[BRA 86]
[BRO 86b] BRODIE, M.L.; JARKE, M.: On Integrating Logic Programming and Databases. In:
EXPERT DATABASE SYSTEMS. Menlo Park: Benjamin/Cummings, 1986. 701p.
p.191-208.
[CAR 88]
[CAS 87]
[CER 86]
CERRO, L.F.D.: MOLOG: A System that Extends PROLOG with Modal Logic.
New Generation Computing, Tokyo, v.4, n.1, p.35-50, 1986.
[CHA 82]
CHANDRA, A.K.; HAREL, D.: Horn Clauses and Fixpoint Query Hierarchy. In:
ACM SYMPOSIUM ON PRINCIPLES OF DATABASE SYSTEMS, March 1982, Los
Angeles. Proceedings ... New York: ACM, 1982. 304p. p.158-163.
[CLA 82]
CLARK, K.; TRNLUND, S-A.: Logic Programming. London: Academic Press, 1982.
[CLO 84]
[COE 80]
[DAH 83]
[DAT 83]
DATE, C.J.: An Introduction to Database Systems. 3rd. Edition. Reading: AddisonWesley, 1983. 513p.
[DOD 90]
DODD, T.: Prolog: A Logical Approach. New York: Oxford University Press, 1990.
556p.
[FIS 87]
FISCHLER, M.; FIRSCHEIN, O.: The Eye, The Brain and The Computer. Reading:
Addison-Wesley, 1987. 331p.
191
[FUR 84]
FURUKAWA, K. et al.: Mandala: A Logic Based Programming System. In: INRTERNATIONAL CONFERENCE ON FIFTH GENERATION COMPUTER SYSTEMS, 1984, Tokyo. Proceedings ... Amsterdam: North-Holland, 1984. 703p. p.613-622.
[GAL 78]
GALLAIRE, H.; MINKER, J.: Logic and Databases. New York: Plenum Press, 1978.
[GAL 83]
GALLAIRE, H.: Logic Databases vs, Deductive Databases. In LOGIC PROGRAMMING WORKSHOP '83, 1983, Albufeira, Portugal. Proceedings ... Amsterdam: NorthHolland, 1983.
[GAL 84]
GALLAIRE, H.; MINKER, J.; NICOLAS, J.-M.: Logic and Databases: A Deductive
Approach. Computing Surveys, New York, v.16, n.2, p.153-185, Jun. 1984.
[GD 31] GDEL, K.: ber Formal Unentscheidbare Satze der Principia Mathematica und
Verwandter System 1. Traduo em Ingles em: From Frege to Gdel: A Sourcebook in
Mathematical Logic. Harvard University Press, Cambridge, Mass.
[GRE 69]
GREEN C.: Theorem Proving by Resolution as a Basis for Question-Answering Systems. In: MACHINE INTELLIGENCE, 4. Edimburgh: Edimburgh University Press,
1969. p.183-205.
[HOF 79]
HOFSTADTER, D.: Gdel, Escher and Bach. New York: Basic Books, 1979.
[HOG 84] HOGGER, C.J.: Introduction to Logic Programming. London: Academic Press, 1984.
278p.
[ISR 83]
ISRAEL, D.; BERANEK, B.: The Role of Logic in Knowledge Representation. Computer, Los Alamitos, v.16, n.10, p.37-41, Oct. 1983.
[IWA 88]
[JAC 86]
[KAN 93]
KANTROWITZ, M.: Prolog Resource Guide. E-Text (Internet) by mkant+prologguide@cs.cmu.edu. Carnegie-Mellon University, 1993.
[KIT 84]
KITAKAMI, H.S.; MIYACHI, T.; FURUKAWA, K.: A Methodology for Implementation of a Knowledge Acquisition System. In: INTERNATIONAL SYMPOSIUM ON
LOGIC PROGRAMMING, Feb. 1984, Atlantic City. Proceedings ... New York: ACM,
1984.
[KOW 74] KOWALSKI, R.A.: Predicate Logic as a Programming Language. In: IFIP '74. Proceedings ... Amsterdam: North-Holland, 1974. p.569-574.
[KOW 75] KOWALSKI, R.A.: A Proof Procedure Using Conection Graphs. Journal of ACM,
New York, v.22, n.4, p.572-595, Apr. 1975.
[KOW 78] KOWALSKI, R.A.: Logic for Data Description. In: LOGIC AND DATABASES. New
York: Plenum Press, 1978.
[KOW 79a]
[KOW 79b]
KOWALSKI, R.A.: Logic for Problem Solving. New York: Elsevier, 1979. 287p.
[LID 84]
LI, D.: A Prolog Database System. Hertfordshire: Research Studies Press, 1984. 207p.
[LLO 84]
192
[MAE 88] MAES, P.: Issues in Computational Reflection. In: META LEVEL ARCHITECTURES AND REFLECTION. Amsterdam: North-Holland, 1988. 355p. p.21-36.
[MAT 89] MATTOS, N.M.: An Approach to Knowledge Basis Management. Kaiserslautern:
University of Kaiserslautern, 1989. PhD Thesis, Department of Computer Science. 255p.
[MCC 69] McCARTHY, J.; HAYES, P.J.: Some Philosophical Problems from the Standpoint of
Artificial Intelligence. In: MACHINE INTELLIGENCE, 4. Edimburgh: Edimburgh
University Press, 1969. p.463-502.
[MCC 77] McCARTHY, J.: Epistemological Problems of Artificial Intelligence. In: INTERNATIONAL JOINT CONFERENCE ON ARTIFICIAL INTELLIGENCE, 5., Aug. 1977,
Cambridge, Massachusetts. Proceedings ... New York: ACM, 1977
[MCC 80] McCARTHY, J.: Circunscription: A Form of Non-Monotonic Reasoning. Artificial
Intelligence, v.13, n.1, p.27-39, 1980.
[MIN 75]
[MIN 82]
MINSKI, M.: Why People Think Computers Can't? AI Magazine, v.3, n.1, p.2-8,
1982.
[MON 88] MONTEIRO, L..; PORTO A.: Contextual Logic Programming. Lisboa: Departamento
de Informtica, Universidade Nova de Lisboa, 1988.
[MOO 84] MOORE, R.C. A Formal Theory of Knowledge and Action. In: FORMAL THEORIES
OF THE COMMON SENSE WORLD. Norwood: Ablex, 1984. p.319-358.
[NEW 82] NEWELL, A.: The Knowledge Level. Artificial Intelligence v.18, n.1, p.87-127, 1982.
[NIL 80]
[PAL 89]
PALAZZO, L.A.M.: Rhesus: Um Modelo Experimental para Representao de Conhecimento. Porto Alegre: CPGCC da UFRGS, 1989. 115p.
[PAL 91]
[PAR 86]
PARKER Jr, D.S. et al.: Logic Programming and Databases. In: EXPERT DATABASE SYSTEMS. Menlo Park: Benjamin Cummings, 1986. 701p. p.35-48.
[PEN 83]
[PER 82]
[PER 88]
PERLIS, D.: Meta in Logic. In: META LEVEL ARCHITECTURES AND REFLECTION. Amsterdam: North-Holland, 1988. 355p. p.37-50.
[ROB 65]
[STE 86]
STERLING, L.; SHAPIRO, E.: The Art of Prolog. Cambridge: MIT Press, 1986. 427p.
[SAK 86]
[SHA 83]
INTELLIGENCE, 8., 1983, Karlsrue. Proceedings ... L:os Altos, Calif.: Distributed by
W. Kaufmann, 1983. p.529-532.
[SHA 93]
SHAPIRO, E.; WARREN, D.: The Fifth Generation Project: Personal Perspectives.
Communications of ACM, v.36, n.3, March 1993. p.48-101.
[STI 85]
[STE 86]
STERLING, L.; SHAPIRO, E.: The Art of Prolog. Cambridge: MIT Press, 1986.
[TAR 75]
[TUR 84]
TURNER, R.: Logics for Artificial Intelligence. West Sussex: Ellis Horwood, 1984,
121p.
194