Adiel Mittmann
Implementação do chatterbot ELIZA na
linguagem multiparadigma Oz
Orientador:
Antônio Carlos Mariani
Membro da banca:
Bernd Heinrich Storb
Membro da bancadois:
Edla Maria Faust Ramos
Universidade Federal de Santa Catarina
Curso de Ciência da Computação
Florianópolis
2006
Agradecimentos
Meus mais sinceros agradecimentos a estas três pessoas:
� Mariani: por ser um verdadeiro orientador.
� Bernd: por apresentar-me a fascinante ELIZA.
� Edla: por mostrar-me qual era meu objetivo.
Agradeço a todos eles por sempre me indicar onde está o chão.
Sumário
Lista de Figuras
Resumo
1 Introdução p. 10
1.1 Objetivos . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . p. 15
1.2 Organização do texto . . . . . . . . . . . . . . . . . . . . . . . . . . . . p. 15
2 O modelo de programação de Oz p. 16
2.1 Memória . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . p. 16
2.2 Declaração de variáveis . . . . . . . . . . . . . . . . . . . . . . . . . . . p. 20
2.3 Uni�cação . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . p. 21
2.4 Nomes . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . p. 23
2.5 Procedimentos . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . p. 24
2.6 Controle de �uxo . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . p. 29
2.7 Células . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . p. 30
2.8 Espaços computacionais . . . . . . . . . . . . . . . . . . . . . . . . . . p. 31
3 Os paradigmas de Oz p. 32
3.1 Programação imperativa . . . . . . . . . . . . . . . . . . . . . . . . . . p. 32
3.2 Programação funcional . . . . . . . . . . . . . . . . . . . . . . . . . . . p. 34
3.3 Programação em lógica . . . . . . . . . . . . . . . . . . . . . . . . . . . p. 36
3.4 Programação orientada a objetos . . . . . . . . . . . . . . . . . . . . . p. 38
3.5 Programação por restrições . . . . . . . . . . . . . . . . . . . . . . . . . p. 39
4 O chatterbot ELIZA p. 43
4.1 A conversa de ELIZA . . . . . . . . . . . . . . . . . . . . . . . . . . . . p. 43
4.2 O funcionamento de ELIZA . . . . . . . . . . . . . . . . . . . . . . . . p. 44
4.2.1 Scripts de ELIZA . . . . . . . . . . . . . . . . . . . . . . . . . . p. 45
4.2.2 O ciclo básico de ELIZA . . . . . . . . . . . . . . . . . . . . . . p. 45
4.2.3 Substituições simples . . . . . . . . . . . . . . . . . . . . . . . . p. 46
4.2.4 Palavras-chave . . . . . . . . . . . . . . . . . . . . . . . . . . . p. 47
4.2.5 Regras de transformação . . . . . . . . . . . . . . . . . . . . . . p. 49
4.2.6 Padrões em regras de decomposição . . . . . . . . . . . . . . . . p. 51
4.2.7 Regras de decomposição especiais . . . . . . . . . . . . . . . . . p. 53
4.2.8 Regras de remontagem especiais . . . . . . . . . . . . . . . . . . p. 53
4.2.9 Memória . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . p. 54
4.2.10 Reação quando não há palavras-chave . . . . . . . . . . . . . . . p. 55
5 ELIZA em Oz p. 56
5.1 Divisão em módulos . . . . . . . . . . . . . . . . . . . . . . . . . . . . p. 56
5.2 Interface texto . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . p. 57
5.2.1 Classe Screen . . . . . . . . . . . . . . . . . . . . . . . . . . . . p. 59
5.2.2 Classe TextUserInterface . . . . . . . . . . . . . . . . . . . . p. 60
5.2.3 Procedimento Main . . . . . . . . . . . . . . . . . . . . . . . . . p. 61
5.3 Módulos de script . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . p. 61
5.3.1 Mensagem de boas-vindas . . . . . . . . . . . . . . . . . . . . . p. 61
5.3.2 Substituições simples . . . . . . . . . . . . . . . . . . . . . . . . p. 62
5.3.3 Grupos de palavras . . . . . . . . . . . . . . . . . . . . . . . . . p. 62
5.3.4 Palavras-chave . . . . . . . . . . . . . . . . . . . . . . . . . . . p. 63
5.3.5 Memória . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . p. 65
5.3.6 Comentários . . . . . . . . . . . . . . . . . . . . . . . . . . . . . p. 65
5.4 Ciclo básico de ELIZA . . . . . . . . . . . . . . . . . . . . . . . . . . . p. 66
5.5 O cérebro de ELIZA . . . . . . . . . . . . . . . . . . . . . . . . . . . . p. 67
5.5.1 Pré-processamento . . . . . . . . . . . . . . . . . . . . . . . . . p. 69
5.5.2 O procedimento MakeAnswer . . . . . . . . . . . . . . . . . . . . p. 69
5.5.3 Substituições simples . . . . . . . . . . . . . . . . . . . . . . . . p. 70
5.5.4 Busca e ordenação de palavras-chave . . . . . . . . . . . . . . . p. 71
5.5.5 Regras de decomposição . . . . . . . . . . . . . . . . . . . . . . p. 71
5.5.6 Regras de remontagem . . . . . . . . . . . . . . . . . . . . . . . p. 72
5.5.7 Regras de transformação . . . . . . . . . . . . . . . . . . . . . . p. 72
5.5.8 Atualização da memória . . . . . . . . . . . . . . . . . . . . . . p. 72
5.5.9 Ausência de palavras-chave . . . . . . . . . . . . . . . . . . . . p. 72
5.5.10 Pós-processamento . . . . . . . . . . . . . . . . . . . . . . . . . p. 73
5.6 O gerenciador de scripts . . . . . . . . . . . . . . . . . . . . . . . . . . p. 73
6 Considerações �nais p. 77
Referências p. 80
Apêndice A -- Doctor.oz p. 82
Apêndice B -- Eliza.oz p. 99
Apêndice C -- TextUserInterface.oz p. 116
Lista de Figuras
2 O modelo de programação de Oz
1 A memória do modelo de programação Oz . . . . . . . . . . . . . . . . p. 17
2 Exemplo de memória de restrições . . . . . . . . . . . . . . . . . . . . . p. 18
3 Compartilhamento da memória . . . . . . . . . . . . . . . . . . . . . . p. 19
4 Exemplo de registro . . . . . . . . . . . . . . . . . . . . . . . . . . . . . p. 20
5 Sintaxe de declaração de variáveis . . . . . . . . . . . . . . . . . . . . . p. 20
6 Exemplo de aninhamento de variáveis . . . . . . . . . . . . . . . . . . . p. 21
7 Sintaxe da uni�cação . . . . . . . . . . . . . . . . . . . . . . . . . . . . p. 22
8 Restrições básicas comunicadas pelo programa da Figura 7 . . . . . . . p. 23
9 Uni�cações que falham . . . . . . . . . . . . . . . . . . . . . . . . . . . p. 23
10 Sintaxe para criação de nomes . . . . . . . . . . . . . . . . . . . . . . . p. 24
11 Sintaxe para a de�nição de procimentos . . . . . . . . . . . . . . . . . . p. 25
12 Associação entre variáveis e procedimentos . . . . . . . . . . . . . . . . p. 25
13 Sintaxa da invocação de procedimentos . . . . . . . . . . . . . . . . . . p. 26
14 Procedimento criando novos procedimentos . . . . . . . . . . . . . . . . p. 26
15 Variáveis globais com escopo léxico . . . . . . . . . . . . . . . . . . . . p. 27
16 Conteúdo da memória após execução do programa da Figura 15 . . . . p. 28
17 Exemplo da construção case . . . . . . . . . . . . . . . . . . . . . . . . p. 29
18 Exemplo de criação de threads . . . . . . . . . . . . . . . . . . . . . . . p. 31
3 Os paradigmas de Oz
19 Atalhos para utilização de células . . . . . . . . . . . . . . . . . . . . . p. 33
20 Sintaxe dos loops em Oz . . . . . . . . . . . . . . . . . . . . . . . . . . p. 34
21 Ordenação no paradigma imperativo . . . . . . . . . . . . . . . . . . . p. 34
22 Procedimentos como funções . . . . . . . . . . . . . . . . . . . . . . . . p. 35
23 Declaração de funções . . . . . . . . . . . . . . . . . . . . . . . . . . . p. 36
24 Ordenação na programação funcional . . . . . . . . . . . . . . . . . . . p. 36
25 Append em Oz . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . p. 37
26 Uma classe que implementa um contador . . . . . . . . . . . . . . . . . p. 38
27 Exemplo de programação por restrições . . . . . . . . . . . . . . . . . . p. 40
28 Problema do envio de dinheiro em Oz . . . . . . . . . . . . . . . . . . . p. 41
29 Ordenação no paradigma de programação por restrições . . . . . . . . . p. 41
4 O chatterbot ELIZA
30 Uma conversa com ELIZA . . . . . . . . . . . . . . . . . . . . . . . . . p. 44
31 Substituições simples . . . . . . . . . . . . . . . . . . . . . . . . . . . . p. 46
32 Exemplo de aplicação de substituições simples . . . . . . . . . . . . . . p. 47
33 Identi�cação de palavras-chave . . . . . . . . . . . . . . . . . . . . . . . p. 48
34 Regras associadas a palavras-chave . . . . . . . . . . . . . . . . . . . . p. 49
35 Pilha de palavras-chave . . . . . . . . . . . . . . . . . . . . . . . . . . . p. 50
36 Transformação de frase do usuário . . . . . . . . . . . . . . . . . . . . . p. 51
37 Exemplo de padrão simples . . . . . . . . . . . . . . . . . . . . . . . . p. 52
38 Exemplo de grupos . . . . . . . . . . . . . . . . . . . . . . . . . . . . . p. 52
39 Exemplo de alternativas . . . . . . . . . . . . . . . . . . . . . . . . . . p. 52
40 Memória de ELIZA . . . . . . . . . . . . . . . . . . . . . . . . . . . . . p. 54
5 ELIZA em Oz
41 Interação entre usuário e chatterbot num sistema Unix. . . . . . . . . . p. 58
42 A classe Screen . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . p. 59
43 A classe TextUserInterface . . . . . . . . . . . . . . . . . . . . . . . p. 60
44 De�nição de mensagem de boas-vindas . . . . . . . . . . . . . . . . . . p. 62
45 Exemplo de substituições simples . . . . . . . . . . . . . . . . . . . . . p. 62
46 Exemplo de de�nição de grupos . . . . . . . . . . . . . . . . . . . . . . p. 62
47 Exemplo de de�nição de palavras-chave . . . . . . . . . . . . . . . . . . p. 63
48 Exemplo de de�nição para a memória de ELIZA . . . . . . . . . . . . . p. 65
49 Exemplo de comentários . . . . . . . . . . . . . . . . . . . . . . . . . . p. 66
50 A classe Eliza . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . p. 66
51 A classe Brain . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . p. 67
52 O método makeAnswer . . . . . . . . . . . . . . . . . . . . . . . . . . . p. 68
53 Sumário do método makeAnswer (Input Result) . . . . . . . . . . . . p. 68
54 Procedimentos locais de MakeAnswerFromPhrases (I) . . . . . . . . . . p. 70
55 Procedimentos locais de MakeAnswerFromPhrases (II) . . . . . . . . . . p. 71
56 Procedimentos locais de MakeAnswerFromPhrases (III) . . . . . . . . . p. 72
57 A classe Script . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . p. 74
Resumo
Linguagens multiparadigma são linguagens que disponibilizam ao programador váriosparadigmas de programação. Desta forma, o programador pode, para cada problema quetem de resolver, utilizar o paradigma de programação mais apropriado, sem ter de recorrera mais de uma linguagem de programação.
Este trabalho é um estudo exploratório sobre a utilização destas linguagens multipa-radigma. Para realização do estudo, o chatterbot ELIZA, um programa clássico e bastanteconhecido, foi implementado na linguagem Oz, uma linguagem multiparadigma.
Palavras-chave: linguagens multiparadigma, ELIZA, Oz.
10
1 Introdução
As linguagens de programação existem em grande número. No entanto, as linguagens
de programação não são todas radicalmente diferentes entre si. Se um programador
está habituado a utilizar uma determinada linguagem de programação, ele também se
sentirá confortável programando em algumas outras linguagens similares, ao passo de que
sentirá di�culdades ao tentar programar em linguagens que diferem muito daquela que ele
conhece. As linguagens similares àquela usual do programador geralmente o são porque
pertencem ao mesmo paradigma de programação.
Assim, um programador que sempre utilizou a linguagem Pascal para escrever seus
programas terá de enfrentar grandes obstáculos quando precisar escrever um programa
em Prolog, ainda que facilmente aprenderá a escrever programas em C. Isto ocorre porque
Pascal e C são representantes do mesmo paradigma, o chamado paradigma imperativo de
programação, enquanto Prolog é um exemplo de uma linguagem que segue o paradigma
de programação em lógica.
Mas por que existem diferentes paradigmas de programação? Todos os paradigmas
de programação têm o mesmo poder computacional, isto é, todo programa escrito em
uma linguagem de um dado paradigma pode ser escrito em uma segunda linguagem de
um outro paradigma. No entanto, as soluções para um problema especí�co podem ter
atributos muito diferentes quando implementadas em linguagens de paradigmas distintos.
As soluções podem variar, por exemplo, no que se refere a:
• Velocidade de implementação. Alguns problemas podem ser rapidamente implemen-
tados em certos paradigmas, enquanto a implementação em outros pode se tornar
demorada.
• Velocidade de execução. O programador geralmente tem de se preocupar com o
tempo que seu programa levará para resolver o problema quando o programa for
executado. Mesmo que um problema possa ser facilmente resolvido utilizando-se
uma linguagem que segue um determinado paradigma, o tempo que o programa
11
levará para executar pode ser simplesmente muito grande para que esta linguagem
seja adotada.
• Elegância. Um programa escrito em uma linguagem de um certo paradigma também
pode parecer deselegante. Um programa que resolva o mesmo problema mas escrito
em uma linguagem de outro paradigma pode apresentar-se com mais elegância.
A elegância de um programa é uma característica que só pode ser avaliada subjetivamente.
As outras duas características, apesar de serem mais objetivas, ainda assim apresentam
certos empecilhos ao serem avaliadas: como provar que o programa sendo avaliado é,
com relação a estas características, o melhor possível? Um programador pode escrever
um programa e, em seguida, um segundo programador ou ele mesmo pode escrever, na
mesma linguagem de programação, um outro programa que tome menos tempo de desen-
volvimento e que execute mais velozmente.
Apesar dos critérios subjetivos de avaliação dos programas escritos em linguagens de
diferentes paradigmas, sempre houve interesse na busca por novos modos de expressão
para as linguagens de programação. Um dos ramos da pesquisa sobre linguagens de
programação lida com as chamadas linguagens multiparadigma. O objetivo das linguagens
multiparadigma é permitir ao programador expressar-se da melhor maneira possível ao
resolver um problema especí�co. O programador pode, então, para cada problema que lhe
seja apresentado, escolher o paradigma que ele crê o levará mais facilmente a atingir uma
solução que possa ser escrita rapidamente, que execute velozmente e que seja elegante. E
poderá fazê-lo sem ter de trocar de linguagem de programação.
A linguagem Oz é uma destas linguagens multiparadigma. Em Oz o programador
pode, para cada problema, optar por um paradigma para resolvê-lo ou, o que é freqüente,
escrever um programa híbrido em que mais de um paradigma é empregado. Não raro um
determinado programa (ou trecho dele) di�cilmente pode ser identi�cado como perten-
cente a um único paradigma, sendo mais facilmente classi�cado como uma mescla de dois
ou mais paradigmas. Os principais paradigmas presentes em Oz são: programação impe-
rativa, programação funcional, programação em lógica, programação orientada a objetos
e programação por restrições.
Outros exemplos signi�cativos de linguagens que suportam mais de um paradigma de
programação são:
• Curry. Curry é �uma linguagem de programação universal que tem como objetivo
amalgamar os paradigmas de programação declarativos mais importantes, a saber,
12
programação funcional e programação em lógica.� [Antoy e Hanus] Ainda que Curry
suporte apenas dois paradigmas, estes paradigmas não são tradicionalmente reuni-
dos em uma única linguagem.
• Leda. Em Leda os principais paradigmas são: programação imperativa, programa-
ção orientada a objetos, programação funcional e programação em lógica [Budd 1994].
A linguagem Oz foi escolhida para este trabalho por estas razões:
• Oz provê ao programador muitos paradigmas. Poucas linguagens poderiam ser
comparadas a Oz no que se refere à quantidade de paradigmas.
• Oz tem várias publicações e documentação abundante, todas facilmente acessíveis
através da internet.
• Há um ambiente, chamado Mozart, que implementa um compilador para a lingua-
gem Oz. Este ambiente pode ser obtido através da internet.
Tanto as publicações e a documentação de Oz quanto o ambiente Mozart estão disponíveis
gratuitamente no site http://www.mozart-oz.org/.
Para explorar a linguagem Oz, este trabalho apresenta uma implementação de um
chatterbot. A palavra chatterbot é formada por duas palavras inglesas: chatter e bot. O
substantivo chatter signi�ca, entre outros, palavrório e tagarelice [Houaiss 2003]1 . Já
bot é uma variante de robot (robô) que tem um signi�cado especializado: �Um programa
que imita o comportamento de um humano, por exemplo fazendo consultas a mecanis-
mos de busca ou participando em uma sala de bate-papo ou em discussões em IRC.�
[The American Heritage Dictionary of the English Language 2000].
Um chatterbot é, portanto, um programa de computador que imita o comportamento
humano através de uma conversa trivial. Com esta de�nição concorda [Paradiso e L'Abbate 2001]:
O conceito de chatterbot é central para um sistema interativo e conver-sacional. Ele consiste de um sistema de software que tenta simular aconversação ou �palavrório� [chatter] de um ser humano, freqüentementeentretendo o usuário com algum tipo de �conversa leve� [smalltalk].
Também corrobora esta de�nição o que diz [Kalaiyarasi, Parthasarathi e Geetha 2003]:
1O verbete completo é: �chilro; charla, tagarelice; palavrório, palavreado oco; sons rápidos e inarticu-lados; trepidação, vibração; chio; ruído (esp. batida de dentes).� [Houaiss 2003]
13
Um robô para conversação é chamado de chatterbot. É um programadesenvolvido com o propósito de permitir ao usuário interagir com osistema. Um chatterbot é um software de inteligência arti�cial que simulaconversação humana e permite comunicação em linguagem natural entrehomem e máquina.
Uma característica importante que estas duas de�nições de chatterbot não mencionam
é que os humanos, ao conversarem com chatterbots, sempre o fazem digitando, nunca
utilizando voz. É o que a�rma [Laven 2005]:
Um chatterbot é um programa que tenta simular uma conversa digitada,com o objetivo de, ao menos temporariamente, enganar um humano,fazendo-o pensar que estava conversando com outra pessoa.
Pode-se dizer que as características que de�nem um chatterbot são, em linhas gerais,
as seguintes:
• Chatterbots são programas de computador que tentam se comunicar com seres hu-
manos utilizando linguagem natural. O chatterbot às vezes tem como objetivo nestas
comunicações simplesmente convencer o humano de que ele não fala com um bot,
mas com outro humano. É precisamente o que acontece em testes como o utilizado
no Prêmio Loebner [Loebner Prize]: os chatterbots devem convencer os juízes de que
eles conversam com pessoas e não com programas de computador.
• A conversa estabelecida por um chatterbot é tipicamente trivial, leve, sem complexi-
dade. Ainda que chatterbots podem conversar sobre tópicos especí�cos e ter muitas
informações acerca de um tema, a conversa em geral não tem profundidade.
• A comunicação entre humanos e chatterbots é feita sempre através de digitação.
Chatterbots atualmente não ouvem e nem tampouco falam2.
O primeiro chatterbot surgiu na década de 60 e chamava-se ELIZA [Weizenbaum 1966].
Apesar de seu autor em nenhum momento ter utilizado a palavra chatterbot, ELIZA é
o arquétipo de um chatterbot, ao ponto de [Bickmore 1999] dizer que �a maior parte
destes sistemas [os chatterbots] são descendentes diretos de ELIZA�. De acordo com
[Vrajitoru 2004], �o programa provou ser surpreendentemente e�ciente em manter a aten-
ção das pessoas durante a conversa e o sucesso do programa original in�uenciou o desen-
volvimento de muitos outros.�2Não ouvem e nem falam, mas por vezes �digitam�. Ao apresentar suas objeções ao Prêmio Loebner,
[Hutchens 1996] lamenta: �infelizmente tenho de simular velocidade de digitação, pausas para pensar,erros de digitação, etc. para ter uma chance de vencer.�
14
Outro chatterbot clássico é PARRY [Colby 1975]. PARRY simula o comportamento
de um paciente paranóico tão bem que, segundo [Mauldin 1994],
Colby [o criador de PARRY] submeteu-o a testes cegos com médicosque questionaram tanto o programa quanto três pacientes humanos di-agnosticados como paranóicos. Revisões das transcrições feitas tanto porpsiquiatras quanto por cientistas da computação mostraram que nenhumdos dois grupos conseguiu distinguir os pacientes humanos do computa-dor.
Um chatterbot contemporâneo que goza de muita popularidade é A.L.I.C.E. [ALICE Bot],
cujo comportamento pode ser programado através de uma linguagem própria. Esta lin-
guagem, batizada de AIML (Arti�cial Intelligence Markup Language ), é baseada no
padrão XML. Para aplicações desta linguagem, veja-se, por exemplo, [Neto et al. 2004] e
[Leonhardt 2003].
Ainda que a trivialidade da conversa dos chatterbots esteja na própria de�nição do
termo, eles nem por isso deixam de ter aplicações práticas. A educação é uma das áreas
em que os chatterbots têm sido utilizados. Segundo [Sganderla, Ferrari e Geyer 2003],
chatterbots representam um grande potencial como agentes pedagógicos,pois possuem autonomia e desenvoltura para direcionar o assunto doestudo de forma natural, sem prender-se a respostas �xas e programadaspara serem ativadas em determinados momentos, e talvez esta seja acaracterística que melhor os diferenciem dos agentes pedagógicos comuns.
O chatterbot ELIZA foi escolhido pelos seguintes motivos:
• Trata-se de um problema com uma complexidade adequada. O objetivo deste tra-
balho não é a implementação em si, mas a implementação utilizando a linguagem
Oz. Por este motivo não seria interessante a implementação de um programa muito
complexo, pois o foco do trabalho �caria deslocado; nem a implementação de um
programa muito simples, que seria incapaz de aproveitar a capacidade que Oz ofe-
rece.
• ELIZA possibilita a utilização de vários paradigmas.
• É um programa clássico. Desde sua concepção, em 1966 ([Weizenbaum 1966]),
ELIZA tem sido freqüente alvo de comentários e várias implementações foram es-
critas.
15
1.1 Objetivos
O objetivo geral deste trabalho é realizar um estudo exploratório sobre a utilização
de linguagens multiparadigma.
São objetivos especí�cos deste trabalho:
• Descrever o modelo de programação de Oz, que foi a linguagem multiparadigma
escolhida para este trabalho.
• Implementar a solução de um problema que favoreça o uso de diversos paradigmas.
Este problema é o chatterbot ELIZA.
1.2 Organização do texto
A linguagem Oz, por não ser convencional, mereceu dois capítulos deste trabalho. O
primeiro deles, o Capítulo 2, trata do modelo de programação de Oz. Este é o modelo
teórico que serve de base para construções mais complexas. O Capítulo 3 descreve as
construções de alto nível que dão suporte aos principais paradigmas de programação de
Oz.
O Capítulo 4 fala sobre o chatterbot ELIZA. Os principais conceitos de ELIZA são
abordados e os requisitos para uma implementação são especi�cados, tomando-se como
base sempre o artigo em que Weizenbaum descreveu ELIZA ([Weizenbaum 1966]).
O Capítulo 5 atinge o objetivo deste trabalho, que é a implementação de ELIZA na
linguagem Oz.
O trabalho termina com o Capítulo 6, em que as considerações �nais são apresentadas.
16
2 O modelo de programação de Oz
O modelo de programação de Oz constitui o cerne da linguagem Oz. Este é o modelo
teórico subjacente que fornece os meios para que possam ser expressas abstrações de mais
alto nível, como aquelas do Capítulo 3.
Este modelo de programação é composto por uma memória à qual estão ligados um
ou mais threads. Cada thread executa seqüencialmente sentenças da chamada linguagem
núcleo. A linguagem núcleo é uma linguagem simples, subconjunto da linguagem Oz inte-
gral, que de�ne apenas operações básicas. Todos programas da linguagem Oz podem ser
traduzidos para esta linguagem núcleo, isto é, a linguagem Oz completa tem exatamente
o mesmo poder computacional da linguagem núcleo.
Este capítulo começa por apresentar, na Seção 2.1, a memória do modelo de programa-
ção de Oz. As seções seguintes introduzem incrementalmente os elementos da linguagem
núcleo.
2.1 Memória
A memória do modelo de programação de Oz1 é o local onde toda informação acessível
aos programas é armazenada. Esta memória é análoga à memória RAM manipulada pelos
processadores dos computadores tradicionais, porém ela é mais abstrata do que as RAMs.
Enquanto a memória RAM é indexada por meio de endereços numéricos e armazena
valores inteiros, a memória do modelo de Oz armazena de maneira direta, por exemplo,
procedimentos e associações entre variáveis e valores2. E estes valores podem ser, além
de números inteiros, entidades bem mais complexas, que serão vistas a seu tempo.
A memória do modelo de Oz é compartimentada, isto é, possui divisões internas em
1Toda referência à memória do modelo de Oz, nos artigos em inglês, é feita utilizando a palavra store,e não memory.
2A memória RAM, obviamente, também é capaz de armazenar procedimentos e variáveis, mas o fazde maneira indireta, interpretando os valores inteiros que ela contém de diversas formas. Ao contrário daRAM, as entidades da memória de Oz não podem ter sua representação interna examinada.
17
que informações de diferentes categorias são armazenadas, conforme mostra a Figura 1.
Existem quatro divisões3:
• A divisão de restrições, também chamada de memória de restrições, que tem infor-
mações acerca de variáveis.
• A divisão de procedimentos, também chamada de memória de procedimentos, que
guarda os procedimentos de�nidos pelo programa.
• A divisão de células, também chamada de memória de células, que guarda referências
mutáveis a variáveis.
• A divisão de triggers, também chamada de memória de triggers, que é utilizada para
avaliação preguiçosa.
A primeira divisão, de restrições, será apresentada a seguir; as demais o serão no momento
oportuno4.
Figura 1 A memória do modelo de programação Oz
ρ: Y
Triggers(I, P )
MemóriaPro edimentosRestrições(X = Y ) ∧ (X = 1) ∧ (Z ∈ [3, 8]) ∧ (P = ξ) ∧ (C = ρ)
ξ: (pro edimento) CélulasA memória de restrições armazena, sob a forma de restrições, informações sobre as
variáveis do programa. Ao descrever a linguagem Basic Oz, que é sublinguagem de Oz,
Henz comenta ([Henz 1997]):
3O número de divisões e também o propósito de cada uma varia conforme a apresentação do modelo.Por exemplo: em [Roy et al. 2003], a memória é apresentada com quatro compartimentos, mas o deprocedimentos está mesclado com o da memória de restrições, e há um compartimento extra para osthreads; em [Smolka 1995], o compartimento de triggers é inexistente, pois estes ainda não haviam sidoadicionados à linguagem Oz à época em que o artigo foi escrito.
4Nas �guras, as divisões irrelevantes serão ocultadas.
18
Em um dado momento, a memória de restrições consiste de uma restriçãoque é uma conjunção �nita de restrições básicas. Restrições básicas têma forma:
• x = y, em que x e y são variáveis.
• x = c, em que x é uma variável e c é um valor.
A memória de restrições, portanto, armazena uma única restrição, que é formada
pela conjunção de diversas restrições básicas5. Uma única restrição é su�ciente para que
a memória guarde uma quantidade arbitrária de restrições básicas, pois �o conjunto de
restrições é fechado sob a conjunção� ([Smolka 1995]).
Quando a restrição armazenada na memória implica uma restrição básica qualquer,
dizemos que a memória con�rma esta restrição básica; por outro lado, quando a memória
implica a negação desta restrição básica, dizemos que a memória nega esta restrição básica.
Assim, a restrição da memória da Figura 2 con�rma a restrição básica X = 3 e também
Z = 3 (pois (X = Y)∧ (Y = Z)∧ (X = 3) → Z = 3 ), porém ela nega a restrição básica Y = 4
(pois (X = Y) ∧ (X = 3) → Y 6= 4 ). No entanto, ela nem con�rma nem nega a restrição
básica U = 5, pois não tem informação su�ciente para decidir se esta restrição básica é ou
não verdadeira.
Figura 2 Exemplo de memória de restrições
RestriçõesMemória(X = Y ) ∧ (X = 3) ∧ (Y = Z) ∧ (U = V )
Quando um programa adiciona restrições básicas à memória de restrições, dizemos
que ele comunica esta restrição básica à memória de restrições. Um programa só pode
comunicar restrições que sejam consistentes com a memória; ele não pode comunicar
restrições básicas que tornem falsa a conjunção de restrições que a memória armazena.
Por isso dizemos que a memória de restrições é monotônica, ou seja, �a restrição no
quadro-negro (memória) (...) torna-se monotonicamente mais forte ao longo do tempo.�
([Smolka, Henz e Würtz 1995]). Assim, um programa poderia comunicar a restrição bá-
sica U = 5 à memória da Figura 2, e a restrição daquela memória se tornaria mais forte. O
5A memória de restrições da linguagem Oz completa armazena outros tipos de restrições básicas,que estão ausentes na linguagem Basic Oz. É o caso, por exemplo, de restrições de conjuntos �nitos([Müller e Müller 1997]) e de domínios �nitos ([Schulte, Smolka e Würtz 1998], [Würtz e Müller 1996]).
19
programa não poderia, contudo, comunicar uma restrição como Z = 5, pois esta restrição
é inconsistente com o conteúdo da memória (já que a memória nega a restrição básica
Z = 5).
Todos os compartimentos da memória são compartilhados por vários threads, de
acordo com a Figura 3. Os threads, além de comunicarem restrições básicas à memória
de restrições, podem também sincronizar-se através dela. Várias primitivas da linguagem
núcleo, como será visto adiante, aguardam que a memória de restrições con�rme ou negue
alguma restrição básica para poderem prosseguir. Não há necessidade de os threads utili-
zarem mecanismos de acesso mutuamente exclusivo para acessar a memória; o modelo é
intrinsecamente concorrente.
Figura 3 Compartilhamento da memória
Restrições Pro edimentos Células TriggersMemória. . .Thread Thread Thread
Na linguagem núcleo de Oz, os nomes das variáveis sempre começam com letra maiús-
cula e todo identi�cador que começa com letra maiúscula é uma variável. Desta forma,
Var e Xs são nomes válidos para variáveis, mas var e xs não são.
As variáveis sobre as quais a memória de restrições tem informação podem, em um
dado momento, ter um dentre vários tipos. Os tipos que nos serão mais úteis são:
• Os números inteiros, representados utilizando a notação decimal tradicional (96,
1176).
• Os átomos, que são constantes equivalentes aos átomos de Prolog (veja, por exemplo,
[Sterling e Shapiro 1994]). Átomos sempre começam com letra minúscula (hidrogenio ,
oxigenio ).
• Os registros, que contém um rótulo e uma seqüencia de pares de características
e campos. Átomos são geralmente usados para o rótulo; para as características,
números e átomos são tipicamente usados. A sintaxe e também uma representação
20
alternativa em forma de árvore para os registros podem ser vistos na Figura 4. Nesta
�gura, vê-se um registro cujo rótulo é produto , que contém as características codigo
e origem . Associado à característica codigo está o inteiro 488; à característica
origem está um valor que é um registro, tendo, por sua vez, outras características
e campos.
• Os nomes, que tem uma seção dedicada mais à frente (Seção 2.4).
Figura 4 Exemplo de registro
488 odigo origem epnome idade89874000produto
produto ( odigo :488 origem : idade (nome :maravilha ep :89874000))maravilha2.2 Declaração de variáveis
Novas variáveis são introduzidas utilizando a construção local, conforme a sintaxe
da Figura 5. Nesta �gura, as variáveis U e V têm escopo que inicia com sua declaração e
termina com o end seguinte. Assim, qualquer sentença dentro deste escopo pode comuni-
car à memória restrições básicas acerca de U e V. Um número arbitrário de variáveis pode
ser declarado; neste caso foram apenas duas.
Figura 5 Sintaxe de declaração de variáveis% Declaração das variáveis U V.
local U V in
%
% Escopo de U e de V.
%
end
O Figura 6 mostra que as declarações de variáveis podem ser aninhadas. No caso
de aninhamento, valem as regras de escopo tradicionais: neste exemplo, a variável A é
declarada duas vezes, e a variável de nome A pode se referir a uma de duas variáveis,
21
dependendo do escopo. Fica claro, por conseguinte, que a memória de restrições não
utiliza diretamente os nomes das variáveis em suas restrições.
Figura 6 Exemplo de aninhamento de variáveislocal A in
local A in
% Qualquer restrição comunicada utilizando o nome de A
% neste ponto...
end
% ...não surtirá efeito na variável A deste escopo.
end
2.3 Uni�cação
De acordo com [Henz, Smolka e Würtz 1993], Oz herda da programação em lógica as
variáveis lógicas. Isto quer dizer que as variáveis de Oz se comportam da mesma forma que
as variáveis de Prolog. A respeito das variáveis lógicas de Prolog, [Sterling e Shapiro 1994]
diz:
Variáveis em programas em lógica se comportam de maneira diferente devariáveis em linguagens de programação convencionais. Elas se referema uma entidade não especi�cada porém única, em vez de um local dearmazenamento na memória.
O fato de elas se referirem a uma entidade única decorre diretamente da monotonici-
dade da memória de restrições. Quando Sterling fala em uma entidade �não especi�cada�,
ele salienta que, em Prolog, as variáveis inicialmente estão desamarradas. O mesmo acon-
tece em Oz: assim que uma variável é declarada, ela não está amarrada a valor algum.
As variáveis podem ser amarradas a valores através de um processo chamado de
uni�cação, também herdado de Prolog. Em Oz, a uni�cação é realizada utilizando-se o
operador �=�. Em linhas gerais, a uni�cação torna o que houver do lado esquerdo igual
ao que houver do lado direito, e no processo restrições básicas podem ser comunicadas à
memória de restrições.
A sintaxe da uni�cação na linguagem núcleo de Oz pode ser vista na Figura 7. Nesta
�gura, a primeira uni�cação comunica a restrição básica A = B à memória. Ainda que
nenhuma das variáveis tenham um valor de�nido, esta restrição pode ser adicionada à
memória e assim que A ou B forem amarrados a um valor, ambas variáveis estarão amar-
radas ao mesmo valor. É o que acontece em seguida, quando a variável A é uni�cada com
o valor 3. Neste momento, a memória de restrições con�rma tanto A = 3 quanto B = 3.
22
Figura 7 Sintaxe da uni�caçãolocal A in
local B in
local C in
% Unificação de variáveis
A=B
% Unificação de variável a valor
A=3
% Unificação de valores
reg (car1 :C car2 :B)=reg (car1 :2 car2 :3)
end
end
end
A terceira e última uni�cação da Figura 7 mostra um caso mais complexo. Nesta
uni�cação, dois registros são uni�cados. As seguintes restrições básicas são comunicadas
à memória de restrições em decorrência desta uni�cação:
• C = 2.
• B = 3.
Vemos que a uni�cação faz uma espécie casamento de padrões: poderia se dizer que
ela sobrepõe os dois lados da igualdade e comunica as restrições básicas que podem ser
deduzidas a partir desta sobreposição.
A Figura 8 mostra o conteúdo da memória de restrições enquanto o programa da
Figura 7 é executado. Ao início do programa, a memória de restrições está vazia. Em se-
guida, duas restrições básicas são comunicadas em duas uni�cações sucessivas. A terceira
uni�cação, mesmo comunicando duas restrições básicas, como visto acima, fortalece a me-
mória de restrições com apenas uma restrição, pois a segunda (B = 3) é redundante, isto
é, antes mesmo de a restrição ser comunicada à memória, ela já con�rmava a restrição6.
Não é um erro comunicar uma restrição básica que não fortaleça a memória.
Nem toda uni�cação é válida. Existem duas situações em que as uni�cações falham:
• Uma restrição básica inconsistente é comunicada. Como visto há pouco, a memória
de restrições não permite que restrições básicas inconsistentes sejam armazenadas.
6É claro que, por tratar-se de uma entidade abstrata, para todos os �ns podemos considerar que estasegunda restrição (B = 3) também foi adicionada. O ponto importante é que nenhuma informação nova
foi adicionada à memória com esta restrição.
23
Figura 8 Restrições básicas comunicadas pelo programa da Figura 7
A=BA=3reg ( ar1 :C ar2 :B)=reg ( ar1 :2 ar2 :3) (A = B) ∧ (A = 3) ∧ (C = 2)
(A = B) ∧ (A = 3)
A = B
Restrições% Iní io do programaA primeira uni�cação que falha da Figura 9 o faz por que a restrição A = 4 é
inconsistente7, já que neste momento a memória de restrições contém A = 3.
• Há uma falha no casamento entre os dois lados da igualdade. É o que acontece na
segunda uni�cação que falha da Figura 9. Lá vemos que dois registros estão sendo
uni�cados. Ambos têm o mesmo rótulo, o que é necessário para que a uni�cação
tenha êxito, mas as características são diferentes (o primeiro tem a característica
car1 , ausente no segundo; o segundo tem car2 , ausente no primeiro).
Figura 9 Uni�cações que falhamlocal A in
A=3
A=4 % Falha!
reg (car1 :A)=reg (car2 :A) % Falha!
end
Na uni�cação, o lado direito é tão passível de ter variáveis alteradas quanto o esquerdo.
A uni�cação A=3 e 3=A tem precisamente o mesmo signi�cado. Esta e também as demais
características mencionadas nesta seção deixam claro que o operador de uni�cação de Oz
(�=�) difere radicalmente do operador de atribuição das linguagens imperativas (o �=� de
C e Java, o �:=� de Pascal).
2.4 Nomes
Os nomes, em Oz, são �constantes que não podem ser forjadas e que não tem uma re-
presentação imprimível� ([Roy et al. 2003]). Como vemos, ao contrário de inteiros, nomes
7Aqui lembre-se a a�rmação de Sterling (citado acima) de que as variáveis lógicas não são locais dearmazenamento na memória: o valor de A não pode ser rede�nido.
24
não podem ser forjados. Isto signi�ca que não há como criar um programa que itere sobre
todas as possibilidades de nomes, pois os nomes �são entidades primitivas que não têm
estrutura� ([Schulte 2002]). Os nomes serão referenciados por letras gregas minúsculas.
Os nomes são utilizados em Oz para fazer a ligação entre variáveis e algumas enti-
dades complexas, como procedimentos (Seção 2.5) e células (Seção 2.7). Quando se lida
com estas entidades, os nomes são manipulados implicitamente conforme necessário � o
programador não lida diretamente com nomes quando precisa criar um simples procedi-
mento. Mas os nomes também podem ser criados explicitamente. A sintaxe para criação
de nomes está na Figura 10.
Figura 10 Sintaxe para criação de nomeslocal N in
{NewName N} % N é amarrado a um novo nome
end
A semântica de {NewName N}8 é a seguinte[Roy et al. 2003]:
1. Um novo nome ξ é criado.
2. A restrição básica N = ξ é comunicada à memória de restrições.
2.5 Procedimentos
Os procedimentos de Oz correspondem aos procedimentos das linguagens imperativas
e às funções das linguagens funcionais9. A sintaxe para de�nição de procedimentos na
linguagem núcleo de Oz é mostrada na Figura 11. Note que o número de argumentos
(aqui apenas 2) é variável, e pode ser zero.
A semântica do programa da Figura 11 é a seguinte ([Smolka 1995]):
1. Um novo nome ξ é criado, da mesma forma que a primitiva NewName cria um novo
nome.
2. A restrição básica P = ξ é comunicada à memória de restrições.
3. A associação entre o nome ξ e o procedimento sendo de�nido é escrita na memória
de procedimentos.
8Esta sentença ({NewName N}) segue a sintaxe de chamada de procedimentos de Oz. Procedimentossão vistos em Seção 2.5.
9Construções equivalentes aos predicados de Prolog são apresentados na Seção 3.3.
25
Figura 11 Sintaxe para a de�nição de procimentoslocal P in
% Procedimento que recebe dois argumentos, A1 e A2
proc {P A1 A2}
%
% Sentenças do procedimento
%
end
end
Esta associação indireta entre variáveis e procedimentos pode ser melhor compreendida
através da Figura 12, que mostra o estado da memória após a execução do programa da
Figura 11.
Figura 12 Associação entre variáveis e procedimentosMemória Pro edimentosRestriçõesP = ξ
Nome ξ Pro edimentoξ: (pro edimento)
Variavel P
Para invocar procedimentos, utiliza-se a sintaxe mostrada na Figura 13. O programa
desta �gura tem a seguinte semântica [Schulte 2002]:
1. O thread executando o programa é suspenso e aguarda que a memória con�rme ou
negue a restrição básica P = ξ. Ao nome ξ deve estar associado, através da memória
de procedimentos, um procedimento com o mesmo número de argumentos utilizados
na invocação (2 neste caso). Caso estas condições não sejam satisfeitas, o processo
falha.
2. Os parâmetros formais do procedimento são substituídos pelos valores utilizados
na invocação. Isto é equivalente a introduzir variáveis locais no procedimento e
uni�cá-las aos valores passados pela invocação.
3. O procedimento é executado.
26
Figura 13 Sintaxa da invocação de procedimentos% Chama o procedimento associado à variável P,
% passando dois argumentos inteiros
{P 3 4}
Várias características importantes dos procedimentos de Oz podem ser observadas
a partir das de�nições acima. Uma delas é a de que os procedimentos da linguagem
núcleo de Oz são de primeira classe. Segundo [Smolka 1995], uma linguagem dá suporte
a procedimentos de primeira classe se:
• Procedimentos podem criar novos procedimentos.
• Procedimentos podem ter variáveis globais com escopo léxico.
• Procedimentos são referenciados através de valores de primeira classe.
Vamos examinar como Oz manifesta cada um dos pontos expostos por Smolka.
A primeira condição para existência de procedimentos de primeira classe diz que a
linguagem deve permitir que novos procedimentos podem sejam criados. Isto não quer
dizer que qualquer linguagem que permita que o usuário crie procedimentos em seu código-
fonte satisfaz esta condição. Este �novos� de Smolka se refere a novos procedimentos
criados em tempo de execução. Desta forma, programas em linguagens como C++ e Java
não criam novos procedimentos; programas em linguagens como Smalltalk e Lisp criam.
Em Oz, toda de�nição de procedimento efetivamente cria um novo procedimento.
Figura 14 Procedimento criando novos procedimentoslocal P A B in
% Procedimento que recebe um único argumento, Q, ...
proc {P Q}
% Que é associado a um novo procedimento.
proc {Q X}
X=3
end
end
% Duas chamadas ao procedimento associado a P.
{P A} % P associará um procedimento a A
{P B} % P associará um outro procedimento a B
end
No programa da Figura 14, o procedimento da variável P associa a cada vez que
é invocado um novo procedimento ao seu parâmetro Q. Assim, na primeira chamada ao
procedimento de P, através de {P A}, a variável A será associada a um novo procedimento;
27
em seguida, quando o mesmo procedimento de P é invocado, através de {P B}, a variável
B será associada a um outro procedimento, também novo, e portanto diferente do que
fora associado a A. Trata-se, na verdade, de uma conseqüência natural da semântica de
de�nição de procedimentos em Oz: uma construção proc cria um novo nome, e cada nome
é único, diferente de todos os outros.
A segunda condição mencionada por Smolka para que haja procedimentos de primeira
classe é a possibilidade de procedimentos utilizarem variáveis globais com escopo léxico.
O �escopo léxico� a que Smolka se refere é o escopo dentro do qual uma variável está
disponível ao programa através de seu nome. E com o termo �variáveis globais�, Smolka
faz alusão a variáveis que não são locais a um determinado procedimento, e não a variáveis
acessíveis a todos os procedimentos.
Figura 15 Variáveis globais com escopo léxicolocal P A M in
proc {P Q}
local L in
% Início do escopo de L
{NewName L}
proc {Q X}
L=X
end
% Fim do escopo de L
end
end
{P A} % P associará a A um procedimento.
{A M} % O procedimento associado a A
% unifica M à variável L.
end
No programa da Figura 15, o corpo do procedimento associado a P declara uma
variável local a este procedimento, L. A esta variável é associado um novo nome, utilizando-
se a primitiva NewName. Em seguida, o parâmetro Q é associado a um novo procedimento.
Este último procedimento, de Q, faz referência a uma variável que não lhe é local (uma
variável global, no dizer de Smolka). O importante aqui é perceber que:
• A variável global L está �presa� dentro do procedimento associado a Q, já que este
procedimento está dentro do escopo de L e somente o procedimento de Q tem uma
referência a ela.
• Cada invocação do procedimento de P resultará em um procedimento com uma nova
variável L que está acessível somente dentro deste procedimento.
28
A situação �nal da memória, após a execução do programa da Figura 15, está na Figura 16.
Figura 16 Conteúdo da memória após execução do programa da Figura 15MemóriaRestriçõesP = ξ ∧ L = ρ ∧A = σ ∧M = LPro edimentosξ: (pro edimento); σ: (pro edimento)
O terceiro e último ponto de Smolka diz que procedimentos devem ser referenciados
através de valores de primeira classe10. Conforme tudo que já foi visto, �ca claro que Oz
satisfaz este último ponto: em Oz os procedimentos são referenciados através de nomes,
que são valores de primeira classe.
Ainda que linguagens imperativas modernas como Java e C++ não suportem proce-
dimentos de primeira classe, esta característica pode ser simulada utilizando o suporte de
programação orientado a objetos destas linguagens. Com efeito, pode-se mapear os três
requisitos de Smolka para estas linguagens da seguinte maneira:
• A criação de novos procedimentos corresponde à criação de um novo objeto. Neste
caso, a invocação do procedimento corresponde à invocação de um método qualquer
do objeto.
• As variáveis globais com escopo léxico correspondem aos atributos do objeto. Estes
atributos só estão acessíveis aos métodos do objeto.
• O uso de valores de primeira classe para referenciar procedimentos corresponde à
atribuição de um objeto a uma variável qualquer.
Sempre que o signi�cado não for prejudicado, passaremos a nos referir aos procedi-
mentos associados a variáveis através do próprio nome das variáveis. Assim, falaremos
10Um valor de primeira classe é um valor que tem tratamento idêntico àquele dispensado aos valoresmais básicos da linguagem. No caso de Oz, isto quer dizer que um procedimento está em pé de igualdadecom, por exemplo, números inteiros.
29
sobre o procedimento P quando nos referimos, na verdade, procedimento associado a P
através de um nome.
2.6 Controle de �uxo
A linguagem núcleo de Oz provê meios para que o �uxo do programa seja controlado
com base no valor de alguma variável. A construção para este �m é case, cuja sintaxe
está exempli�cada na Figura 17. Uma sentença case compara o valor de uma variável
com uma série de padrões, na ordem em que estes aparecem no programa. A cada um
destes padrões está associada uma seqüência de sentenças. O primeiro padrão que casar
com o valor da variável terá executada a seqüência de sentenças a que está associado.
Uma construção case pode, opcionalmente, especi�car uma seqüência de sentenças que
será executada se nenhum dos padrões casarem com o valor da variável.
Figura 17 Exemplo da construção case
% Fuso horário para cidades, com relação a Greenwich
proc {Fuso Cidade Horas}
case Cidade
of saoPaulo then Horas=~3
[] paris then Horas=1
[] atenas then Horas=2
else Horas=nil
end
end
Na Figura 17, um procedimento chamado Fuso é de�nido. Ele recebe dois argumentos,
Cidade e Horas. O argumento Cidade é comparado com uma série de átomos. Caso o
valor de Cidade seja, por exemplo, o átomo saoPaulo , o argumento Horas será uni�cado
com o número -3. Se nenhum casamento for bem sucedido, o argumento Horas é uni�cado
com o átomo nil .
As várias alternativas de�nidas em um case são separadas por []: trata-se de um
elemento léxico léxico da linguagem. A primeira alternativa é introduzida por of. A
palavra-chave else indica que o que segue são as sentenças a serem executadas caso
todos os casamentos falhem. Se todos os casamentos de uma sentença case falharem e
ela não especi�car um else, acontecerá um erro de execução.
30
2.7 Células
As variáveis do modelo de programação de Oz, como descrito na Seção 2.1, são va-
riáveis lógicas. Uma das conseqüências disto é que uma vez que um valor seja associado
à variável, esta associação jamais poderá ser desfeita. O modelo de Oz permite, contudo,
que programas armazenem informação mutável: para este �m existem as células.
Células nada mais são do que associações entre nomes e variáveis. A variável associada
ao nome pode variar com o tempo, ainda que o nome permanece sempre o mesmo. Assim,
uma célula que em um momento associe o nome ξ à variável A pode, no momento seguinte,
associar o nome ξ à variável B, sendo que A 6= B.
Existem duas primitivas de�nidas pela linguagem núcleo de Oz para a manipulação
de células. A primeira delas é {NewName Inicial
Celula}, que cria uma nova célula. Esta primitiva age da seguinte maneira:
1. Um novo nome ξ é criado.
2. Este nome é associado, através da memória de células, à variável Inicial, que é o
valor inicial para a célula.
3. A restrição Celula = ξ é comunicada à memória de restrições.
A segunda primitiva da linguagem núcleo de Oz para manipulação de células é {Exchange
Celula Velho Novo}, utilizada para ler e gravar em células. Ela tem a seguinte semân-
tica:
1. Aguarda que a memória de restrições con�rme uma restrição básica Celula = ξ,
em que ξ é um nome. Se esta restrição básica não for con�rmada, uma exceção é
lançada.
2. Procura-se a variável V que está associada ao nome ξ na memória de células. A
restrição básica V elho = V é comunicada à memória de restrições � com esta
restrição o programa consegue ler o valor de uma célula.
3. A associação do nome ξ é alterada: ele agora passa a estar associado à variável Novo.
O exemplo da Figura 18 ilustra a utilização de células. Este trecho de programa
procede da seguinte maneira:
31
Figura 18 Exemplo de criação de threadslocal
Contador
in
{NewCell 0 Contador}
local X in {Exchange Contador X 1} end
end
1. Ele introduz uma variável local, Contador.
2. A primitiva NewCell é invocada. Desta maneira, a memória de restrições agora
con�rma uma restrição básica Contador = ρ, em que ρ é um nome, e a memória de
células tem a associação de ρ a uma variável com valor 0.
3. Uma variável local X é introduzida. A primitiva Exchange é invocada e o valor velho
da célula é uni�cado com X; um novo valor, 1 é então associado ao nome ρ da célula.
A ação aqui descrita corresponde, efetivamente, a armazenar o valor 1 na célula e
desprezar o valor antigo lá armazenado.
2.8 Espaços computacionais
O modelo de programação de Oz introduz a noção de espaços computacionais. Um
espaço computacional agrupa memória e threads; isto signi�ca que toda sentença de um
programa de Oz é executada no contexto de um espaço computacional.
Os espaços computacionais são úteis na realização de buscas. Neste caso, os espaços
computacionais são clonados e cada clone explora uma alternativa da árvore de busca. Os
espaços computacionais, nestas buscas, formam uma hierarquia.
Existem várias primitivas da linguagem núcleo para manipular espaços computacio-
nais. No entanto, estas raramente são utilizadas diretamente, pois existem construções
de alto nível de abstração que são mais acessíveis ao programador � veja, por exemplo,
a Seção 3.3.
32
3 Os paradigmas de Oz
Este capítulo mostra, super�cialmente, como podem ser usados alguns dos princi-
pais paradigmas de programação suportados por Oz. Os paradigmas apresentados são:
programação imperativa, programação funcional, programação em lógica, programação
orientada a objetos e programação por restrições.
3.1 Programação imperativa
A característica mais marcante da programação imperativa é a presença de estado.
Segundo [Budd 1994], �o modelo imperativo é freqüentemente descrito usando-se a frase
`mudanças incrementais ao estado.' � Diz-se que as variáveis das linguagens imperativas
armazenam estado, pois as variáveis destas linguagens podem ter seus valores mudados
ao longo do tempo.
As variáveis de Oz, como visto em Seção 2.1, são variáveis lógicas e portanto incapazes
de armazenar estado. Oz, porém, provê suporte a estado, especialmente através das
células, vistas em Seção 2.7. Uma variável de Oz pode estar associada a uma célula, e
esta célula pode ter seu valor alterado ao longo do tempo. Há uma pequena indireção
quando se usa células, já que, para acessar o valor de uma célula, é preciso tipicamente
passar antes por uma variável que esteja associada àquela célula.
Duas primitivas foram introduzidas em Seção 2.7: {NewCell C V}, para criar uma
nova célula, e {Exchange C A B}, para trocar o valor de uma célula. Apesar de teori-
camente sólido, este modo de utilização de células � com uma mesma chamada para ler
e gravar em uma célula � é pouco prático e pouco elegante. A �gura Figura 19 mostra
dois procedimentos, P1 e P2, que são equivalentes. O segundo, porém, usa dois atalhos
de�nidos pela linguagem Oz, um para acessar o conteúdo de uma célula e outro para
alterá-lo.
O primeiro atalho utilizado pelo procedimento P2 torna mais fácil o acesso ao conteúdo
33
Figura 19 Atalhos para utilização de célulasproc {P1}
C X
in
% Cria célula com valor inicial 1
{NewCell C 1}
% Unifica X com o valor atual da célula
local
A
in
{Exchange C A X}
A=X
end
% Põe um novo valor, 2, na célula
{Exchange C _ 2}
end
proc {P2}
C X
in
% Cria célula com valor inicial 1
{NewCell C 1}
X=@C
% Põe um novo valor, 2, na célula
C:=2
end
das células. Este atalho consiste em antepor-se o símbolo �@� a uma variável. Esta
expressão, então, passa a denotar o conteúdo da célula associada à variável em questão.
No exemplo da Figura 19, o procedimento P2, na sentença X=@C, uni�ca X com o conteúdo
da célula associada à variável C.
O segundo atalho presente no procedimento P2 facilita a alteração do conteúdo de
uma célula. A sentença C:=2 põe o valor 2 na célula associada à variável C.
As operações realizadas por estes dois atalhos podem ser realizadas diretamente em
termos da linguagem núcleo, como mostra o procedimento P1 da Figura 19. O procedi-
mento P2, no entanto, é mais legível e mais sucinto.
Outra facilidade de Oz para a programação imperativa são os loops. Os loops de Oz
tem a sintaxe da Figura 20. As declarações que podem ser inseridas nos loops são várias.
Pode-se, por exemplo, iterar os itens de uma lista (for Item in Lista do) ou fazer um
loop do tipo while (for while :
Condicao do).
34
Figura 20 Sintaxe dos loops em Ozfor (Declarações ) do
% Sentenças
end
A Figura 21 é um programa adaptado de [Budd 1994]. O procedimento Sort recebe
um argumento, AList, que é uma célula contendo uma lista. Esta lista é ordenada pelo
procedimento.
Figura 21 Ordenação no paradigma imperativoproc {Sort AList}
Current={NewCell nil } P={NewCell nil }
X={NewCell nil } Temp={NewCell nil }
in
Current:=@AList
for while : @Current \= nil do
X:=@(@Current.value )
P:=@AList
for while : @P \= @Current do
if @X<(@(@P.value )) then
Temp:=@(@P.value )
(@P.value ):=@X
X:=@Temp
end
P:=@(@P.next )
end
(@Current.value ):=@X
Current:=@(@Current.next )
end
end
Neste pequeno programa pode-se perceber duas características fortes da programação
imperativa: a onipresença de estado, re�etida em Oz no uso constante dos operadores @
e :=, utilizados para acessar células; e também as estruturas de controle while e if.
3.2 Programação funcional
Os procedimentos apresentados na Seção 2.5 não são próprios para serem utilizados
na programação funcional, já que não são funções propriamente ditas. Para que haja
verdadeiras funções em Oz, algumas facilidades estão disponíveis.
A primeira destas facilidades permite que qualquer procedimento com mais de um
argumento seja tratado como uma função. Sempre que um destes procedimentos for invo-
35
cado, seu último argumento pode ser ocultado. Neste caso, a invocação do procedimento
passa a ser uma expressão que, portanto tem um valor. O valor desta expressão será o
valor uni�cado pelo procedimento ao seu último argumento.
Na Figura 22, há um procedimento, Soma, que recebe três argumentos: A, B e C. Este
procedimento soma os valores de A e B e uni�ca o resultado com C. Já o procedimento
Calculo faz três chamadas a este procedimento, todas elas equivalentes1. As chamadas
têm o seguinte signi�cado:
Figura 22 Procedimentos como funçõesproc {Soma A B C}
C=A+B
end
proc {Calculo R}
% As três linhas seguintes são equivalentes
{Soma 1 2 R}
R={Soma 1 2 $}
R={Soma 1 2}
end
• A primeira delas chama o procedimento da maneira mais simples. O resultado do
procedimento Soma é uni�cado com a variável R.
• A segunda invocação usa o símbolo �$� para transformar o que seria uma sentença
em uma expressão. Desta forma, a invocação de Soma começa a parecer a aplicação
de uma função, mas ainda há o incômodo de se utilizar o �$�.
• A terceira chamada oculta o terceiro argumento de Soma, resultando em uma ex-
pressão cujo valor é justamente aquele que é uni�cado pelo procedimento Soma com
este último parâmetro.
A linguagem Oz também de�ne um meio para que se possa de�nir as funções diretamente.
Para este �m, em Oz existe a palavra-chave fun. Esta palavra-chave introduz a de�nição
de uma função, e segue a mesma sintaxe de um procedimento normal. A diferença é que
toda função deve ter um valor de retorno, ou seja, toda função deve terminar com uma
expressão, jamais com uma instrução.
A Figura 23 mostra uma função, Soma, equivalente ao procedimento homônimo da
Figura 22. A de�nição de Soma na Figura 23, no entanto, é efetivamente uma função,
possuindo valor de retorno.
1O programa não tem problemas de execução.
36
Figura 23 Declaração de funçõesfun {Soma A B}
A+B
end
Um outro exemplo está na Figura 24, adaptado diretamente de [Budd 1994]. Nesta
�gura, a função Sort invoca a função padrão FoldR. O trabalho principal de ordenação
é realizado na função local Insertion. Esta função recebe um elemento X a ser inserido
em uma lista AList, já ordenada.
Figura 24 Ordenação na programação funcionalfun {Sort AList}
fun {Insertion X AList}
case AList
of nil then
X|nil[] H|T then
if X<H then
X|AListelse
H|{Insertion X T}
end
end
end
in
{FoldR AList Insertion nil }
end
3.3 Programação em lógica
A busca, na programação em lógica, está sempre implicitamente presente. Uma das
implicações disto é que o programador nunca precisa se preocupar em iniciar uma busca,
pois ela está sempre acontecendo. Outra implicação, porém, é a de que a busca age sempre
da mesma maneira: ela faz parte do próprio modelo de execução, e seu comportamento
não pode ser alterado.
Em Oz a busca pode ser controlada pelo programador. Isto é realizado através da
chamada busca encapsulada. Diz-se que a busca está encapsulada porque ela é programada
à parte do programa que descreve o problema em si. Assim, em um módulo o programador
pode escrever, declarativamente, um programa que descreve a árvore de busca de seu
problema, e separar este programa em um módulo. Em outro módulo, ele pode programa
37
a estratégia de busca (por exemplo, busca em profundidade) que será utilizada para
pesquisar a árvore descrita no outro módulo. Esta estratégia de busca pode ser inclusive
reutilizada mais tarde com outros módulos.
Na programação em lógica a busca é sempre busca em profundidade. Em Oz, vá-
rias buscas estão disponíveis na biblioteca padrão. Uma delas é Search.base .one ,
que procura por uma solução através de uma busca em profundidade. Outra busca é
Search.base .all , que também faz uma busca em profundidade, mas desta vez com o
objetivo de encontrar todas as soluções do problema. O programador muito raramente
precisa programar uma estratégia de busca para seu problema. Em geral, as buscas pré-
programadas do módulo Search são su�cientemente e�cientes para serem aplicadas à
maioria dos problemas.
Já que as estratégias de busca vêm pré-programadas, ao programador resta tipica-
mente apenas descrever seu problema. Sempre que for necessário uma rami�cação da
árvore de busca, o programador pode utilizar a construção choice, que cria um ponto
de escolha. A estratégia de busca, então, rami�cará a árvore tantas vezes quantas forem
as alternativas de�nidas pela construção choice. Como exemplo, veja o programa da
Figura 25. O procedimento Append ali de�nido concatena duas listas, Xs e Ys, em uma
terceira, Zs. No entanto, ele realiza esta tarefa de maneira não-determinística, já que cada
vez que é invocado ele cria um ponto de escolha com duas alternativas.
Figura 25 Append em Ozproc {Append Xs Ys Zs}
choice
Xs=nil Ys=Zs
[] Hx Tx Tz in Xs=Hx|Tx Zs=Hx|Tz {Append Tx Ys Tz}
end
end
fun {TodasCombinacoes Lista}
{Search.base .all
proc {$ Sol}
X Y
in
{Append X Y Lista}
Sol=X#Y
end}
end
É necessário que o procedimento Append da Figura 25 seja chamado no contexto
de uma busca, pois ele apenas de�ne como é a árvore de busca do problema, mas não
38
especi�ca nenhuma estratégia para a busca. O procedimento Busca, também da Figura 25,
faz justamente isto: chama o procedimento Append, mas no contexto de uma busca.
Este procedimento inicia uma busca para encontrar toda as combinações de listas que
concatenadas resultariam na lista Lista.
O procedimento TodasCombinacoes da Figura 25 invoca o procedimento Search.base .all ,
que inicia uma busca em profundidade. Como dito acima, os procedimentos que imple-
mentam uma estratégia de busca recebem um script. Aqui, o script é um procedimento
de�nido na própria chamada do procedimento Search.base .all . Este script delega a
descrição da árvore de busca para o procedimento Append.
O procedimento Search.base .all retorna uma lista com todas as soluções encontra-
das após uma busca em profundidade completa. Se o procedimento TodasCombinacoes
fosse chamado com o parâmetro [1 2], por exemplo, a busca de Search.base .all
encontraria as seguintes soluções: nil #[1 2], [1]#[2], [1 2]#nil . A concatenação
das duas listas de cada solução resulta na lista [1 2].
3.4 Programação orientada a objetos
O paradigma de programação orientada a objetos, em Oz, possui vários elementos
sintáticos que facilitam sua utilização. Para apresentar estes elementos sintáticos, o código
da Figura 26 será utilizado como exemplo.
Figura 26 Uma classe que implementa um contadorclass Contador
attr
valor
meth init ()
valor :=0
end
meth inc ()
valor :=@valor +1
end
meth atual ($)
@valor
end
end
O programa da Figura 26 de�ne uma classe chamada Contador. Esta classe tem um
único atributo, valor , que guarda o valor atual do contador. Possui três métodos:
39
• init : o construtor da classe. Inicializa o atributo valor para zero.
• inc : aumenta em uma unidade o valor do contador.
• atual : retorna o valor atual do contador.
Uma declaração de classe é introduzida pela palavra-chave class, e é seguida do nome
de uma variável. Esta variável conterá a de�nição da classe. Note-se, portanto, que as
classes em Oz são cidadãs de primeira classe. Os atributos de uma classe são de�nidos
através da palavra-chave attr.
Os métodos são de�nidos com a palavra-chave meth. Deve seguir esta palavra-chave
o nome do método, juntamente com os possíveis argumentos do método. Um método
funcional, isto é, um método que se comporta como uma função, deve ter como último
argumento o símbolo $ � este é o caso do método atual .
3.5 Programação por restrições
Como visto na Seção 2.1, a memória de restrições de Oz é uma conjunção de restrições
básicas. Estas restrições básicas, no entanto, não são su�cientes para que Oz tenha um
bom suporte ao paradigma de programação por restrições. De acordo com [Smolka 1995],
�restrições expressivas como x + y = z e x ∗ y = z não podem ser escritas na memória�;
isto acontece porque estas restrições expressivas não são restrições básicas.
Para resolver este problema, Oz introduz os propagadores. Os propagadores impõem
restrições mais expressivas que as restrições básicas. Eles aguardam que mais dados
sobre as variáveis estejam disponíveis para levar adiante suas restrições. Um exemplo é
ilustrativo neste ponto: veja-se o código da Figura 27. A função ali de�nida resolve a
equação X + Y = Z, sabendo-se que X = 2 e Z = 5. O programa segue os seguintes
passos:
• Como o problema trabalha com números inteiros, é imprescindível que, antes de
impor restrições sobre as variáveis, o programa informe o domínio das variáveis. Por
esta razão a primeira linha do corpo da função Calcula especi�ca que o domínio
das variáveis X, Y e Z é [0, 100].
• A linha seguinte cria um propagador para a restrição do problema, utilizando para
isto o operador =:. Este propagador ainda não tem dados su�cientes para resolver
40
Figura 27 Exemplo de programação por restriçõesfun {Calcula}
X Y Z
in
[X Y Z]:::0#100
X+Y=:Z
X=2
Z=5
Y
end
o problema � ele apenas conhece o domínio das variáveis envolvidas, mas ainda
existem muitas soluções possíveis para o problema.
• Em seguida, o procedimento restringe o valor de duas das três variáveis envolvidas no
problema: de�ne que X=:2 e que Z=:5. Neste momento, o propagador recém-criado
volta à ação e, tendo dados su�cientes, determina o valor da variável Y.
• O valor encontrado de Y, neste caso 3, é retornado pela função.
No exemplo anterior, não houve necessidade de busca para encontrar a solução do pro-
blema. Em muitos casos, no entanto, é necessária uma etapa �nal quando se utilizam
propagadores: a etapa de distribuição. Nesta etapa, inicia-se uma busca para encontrar
o valor de todas as variáveis do problema.
O exemplo da Figura 28 resolve um problema em que a busca é necessária. O problema
envolvido é o clássico problema intitulado �Send more money�. Este problema consiste
em encontrar-se valores distintos para cada um dos dígitos correspondentes às letras S,
E, N, D, M, O, R e Y, de maneira que a equação SEND + MORE = MONEY seja
respeitada. O procedimento SendMoreMoney procede da seguinte maneira:
• O primeiro passo dado pelo procedimento é a de�nição do domínio das variáveis
envolvidas. Como todas as variáveis do problema são dígitos, o domínio de todas é
[0, 9].
• Para que não haja mais de um dígito com o mesmo valor, é criado um propagador
através da chamada ao procedimento FD.distinct . Este propagador impõe a res-
trição não-básica de que todas as variáveis que lhe são passadas devem ser diferentes
entre si.
• Em seguida, mais duas restrições são impostas: para que S e M sejam diferentes de
zero.
41
Figura 28 Problema do envio de dinheiro em Ozproc {SendMoreMoney Sol}
S E N D M O R Y
in
[S E N D M O R Y] ::: 0#9
{FD.distinct [S E N D M O R Y]}
S \=: 0
M \=: 0
S * 1000 + E * 100 + N * 10 + D
+ M * 1000 + O * 100 + R * 10 + E
=: M * 10000 + O * 1000 + N * 100 + E * 10 + Y
{FD.distribute ff [S E N D M O R Y]}
Sol=[S E N D M O R Y]
end
• A principal restrição do problema é imposta: a equação do problema é de�nida
através de um propagador.
• Por �m, uma busca é iniciada através do procedimento FD.distribute . Esta busca
encontrará valores para todas as variáveis do problema que satisfaçam as restrições
até aqui impostas.
A Figura 29 mostra um programa para ordenação de uma lista de números utilizando-se o
suporte de programação por restrições de Oz2. Para encontrar a solução, o procedimento
Sort estabelece quais são as restrições da lista ordenada que se deseja encontrar. As duas
restrições necessárias para encontrar a lista ordenada são:
Figura 29 Ordenação no paradigma de programação por restriçõesproc {Sort AList SortedList}
proc {Ordered List}
case List
of F|(S|R) then F=<S {Ordered (S|R)}else skip
end
end
in
{FD.list {List.length AList} 0#1000 SortedList}
{Ordered SortedList}
for I in AList do {FD.exactly 1 SortedList I} end
{FD.distribute ff SortedList}
end
2Este programa tem duas limitações: os números da lista a ser ordenada devem estar no intervalo[0, 1000] e devem ser todos diferentes entre si.
42
• Em uma lista ordenada a1, a2 . . . an, é sempre verdade que ai < ai+1, para todo i < n
� trata-se, na verdade, de uma condição não apenas necessária mas também su�-
ciente para uma lista ordenada. Esta é a restrição imposta pelo sub-procedimento
Ordered.
• Os componentes �nais da lista ordenada não são números quaisquer; eles devem
ser elementos da lista de entrada. Por este motivo, para cada elemento da lista de
entrada é criado um propagador que impõe a restrição de que apenas um e apenas
um elemento da lista de saída é igual a este elemento da lista de entrada.
É importante notar o alto grau de declaratividade do programa da Figura 29. Nele são
de�nidas somente as características �nais da solução desejada, sem de�nir-se como uma
solução com estas características podem ser encontradas.
43
4 O chatterbot ELIZA
Neste capítulo o clássico chatterbot ELIZA, de 1966, é descrito. A Seção 4.1 mostra
como o ELIZA se sai em suas conversas. Por �m, a Seção 4.2 descreve como o ELIZA
funciona internamente.
4.1 A conversa de ELIZA
ELIZA foi o primeiro chatterbot a ser escrito, e causou certo impacto quando da sua
divulgação. Com efeito, se olharmos para a transcrição da Figura 30, ELIZA realmente
parece apresentar um comportamento humano. Esta conversa é a mais famosa de ELIZA,
pois foi publicada junto com o artigo de Weizenbaum ([Weizenbaum 1966]).
Na conversa da Figura 30, ELIZA tenta responder a grosso modo como fariam os
psicoterapeutas rogerianos [Weizenbaum 1966]. De acordo com [Weizenbaum 1966],
Este modo de conversação foi escolhido porque a entrevista psiquiátricaé um dos poucos exemplos de comunicação em linguagem natural diá-dica categorizada em que um dos participantes está livre para assumira posição de não conhecer quase nada acerca do mundo real. Se, porexemplo, se dissesse a um psiquiatra �Eu saí para um longo passeio debarco� e ele respondesse �Fale-me mais sobre barcos�, não se assumiriaque ele não sabe nada sobre barcos, mas que ele tinha algum propósitoem dirigir de tal maneira a conversa subseqüente.
Esta posição assumida pelo interlocutor de ELIZA, isto é, a de que o programa entende
o que ele diz, é considerada importante por [Mauldin 1994]:
O maior truque de ELIZA era usar perguntas para tirar uma conversaa partir do próprio usuário, com pouca ou nenhuma contribuição realdo programa. Isto funciona porque a maioria das pessoas gostam deconversar sobre si mesmas, e �cam felizes em acreditar que o programaestá ouvindo.
O efeito causado por ELIZA em alguns de seus companheiros de conversa foi tão forte
44
Figura 30 Uma conversa com ELIZAUsuário: Men are all alike.ELIZA: In what way?Usuário: They're always bugging us about something or other.ELIZA: Can you think of a speci�c example?Usuário: Well, my boyfriend mademe come here.ELIZA: Your boyfriend made you come here.Usuário: He says I'm depressed much of the time.ELIZA: I am sorry to hear you are depressed.Usuário: It's true. I am unhappy.ELIZA: Do you think coming here will help you not to be unhappy?Usuário: I need some help, that much seems certain.ELIZA: What would it mean to you if you got some help?Usuário: Perhaps I could learn to get along with my mother.ELIZA: Tell me more about your family.Usuário: My mother takes care of me.ELIZA: Who else in your family takes care of you?Usuário: My father.ELIZA: Your father.Usuário: You are like my father in some ways.ELIZA: What resemblance do you see?Usuário: You are not very aggressive but I think you don't me to notice that.ELIZA: What makes you think I am not very aggressive?Usuário: You don't argue with me.ELIZA: Why do you think I don't argue with you?Usuário: You are afraid of me.ELIZA: Does it please you to believe I am afraid of you?Usuário: My father is afraid of everybody.ELIZA: What else comes to mind when you think of your father?Usuário: Bullies.ELIZA: Does that have anything to do with the fact that your boyfriend made you comehere?
que, de acordo com Weizenbaum, foi difícil convencer algumas pessoas de que elas não
estavam efetivamente conversando com um humano [Weizenbaum 1966].
4.2 O funcionamento de ELIZA
Esta seção descreve como ELIZA funciona internamente, baseando-se no artigo origi-
nal de Weizenbaum ([Weizenbaum1966ELIZA]). Aqui, no entanto, não serão mencionados
detalhes relativos a uma implementação especí�ca, como fez Weizenbaum. Ao contrário,
o objetivo da seção é apresentar um modelo abstrato para ELIZA.
45
4.2.1 Scripts de ELIZA
O chatterbot ELIZA é dirigido por um script. Um script para ELIZA contém uma
série de estruturas que de�nem o comportamento do chatterbot. Weizenbaum já salientava
que o script utilizado por ELIZA não faz parte do programa, ou seja, é apenas um dado
manipulado por ele. [Weizenbaum 1966].
O artigo em que Weizenbaum descreve ELIZA contém um apêndice de�nindo um
script para o chatterbot que tenta parodiar o comportamento de um psiquiatra Rogeriano.
Este script foi amplamente divulgado e, freqüentemente, é tomado como parte inseparável
de ELIZA. Veja, por exemplo, o que diz [Leonhardt 2003]:
Dentre os chatterbots existentes, um dos mais antigos pode ser conside-rado o Eliza. Desenvolvido em 1966 pelo professor Joseph Weizenbaumno Massachussets Institute of Technology, seu objetivo é o de simularum psicanalista em uma conversa com seu paciente.
Na verdade, ELIZA não só não está restrito a apenas um comportamento, como
seu script pode estar em outros idiomas, e não apenas em inglês. Segundo Weizen-
baum, à época em que escreveu seu artigo existiam scripts em alemão, galês e inglês
[Weizenbaum 1966]. É claro que uma língua pouco �exionada como o inglês é mais propí-
cia para ELIZA, pois vários problemas de identi�cação e transformação de formas verbais
e nominais são evitados.
Nas subseções seguintes, sempre que trechos de scripts forem exempli�cados, estes
serão convenientemente retirados do script anexado ao artigo de Weizenbaum, que é o
mais conhecido.
4.2.2 O ciclo básico de ELIZA
O ciclo de funcionamento de ELIZA consiste em ler um conjunto de frases digitadas
pelo usuário e gerar uma resposta. Isto signi�ca que ELIZA só fala em resposta às frases
do usuário, ou seja, dá apenas uma resposta à entrada do usuário e não o interrompe
enquanto este digita a próxima entrada. A única exceção é o início da conversa, em que
uma mensagem especial, que todo script de ELIZA deve de�nir, é comunicada ao usuário.
A mensagem no script original de Weizenbaum é �How do you do. Please state your
problem.�
O texto do usuário pode conter quaisquer caracteres, inclusive pontuação normal1. Na
1Weizenbaum originalmente havia posto a restrição de que o ponto de interrogação não poderia ser
46
interface original de ELIZA, a entrada do usuário deveria ser terminada por dois retornos
do carro, mas isto só é aplicável a implementações de ELIZA em que o usuário interaja
com o chatterbot através de interfaces somente texto e, ainda assim, este certamente é um
detalhe especí�co de implementação.
A resposta do chatterbot é sempre em maiúsculas2. Ainda que Weizenbaum prova-
velmente não considerasse esta uma característica decisiva para ELIZA, ela pode tornar
certos aspectos da implementação mais simples (Capítulo 5).
Um dos objetivos do chatterbot de Weizenbaum era que o script pudesse ser editado.
Para isto, ELIZA disponibilizava uma espécie de comando, �EDIT�, que invocava um
editor externo, �ED�, para que o usuário pudesse modi�car o script [Weizenbaum 1966].
Não parece plausível, entretanto, que este seja um requisito para uma implementação de
ELIZA, já que qualquer editor da preferência do usuário pode ser utilizado.
4.2.3 Substituições simples
O processo básico de funcionamento de ELIZA consiste em ler um conjunto de frases
digitadas pelo usuário e gerar uma resposta baseando-se em um script. A unidade básica
de manipulação de ELIZA é a palavra (para todos efeitos, considera-se que o apóstrofo não
separa palavras; assim, �I'm� é uma palavra). O primeiro passo feito por ELIZA após a
leitura das frases do usuário é fazer uma pequena transformação através das substituições
simples.
Uma substituição simples é uma regra que substitui uma palavra por outra nas frases
digitadas pelo usuário, conforme mostra a Figura 31. Estas regras são parte do script
sendo utilizado por ELIZA.
Figura 31 Substituições simplesI youyou Ime you ant an'tdont don't
wont won'tAlguns objetivos podem ser identi�cados para as substituições simples:
usado, pois era interpretado de maneira especial pelo ambiente em que ELIZA era executado. Estarestrição não é levada em conta neste trabalho, por considerarmos-na irrelevante..
2As respostas serão apresentadas utilizando minúsculas para facilitar a leitura.
47
• Conforme exibe o lado esquerdo da Figura 31, as substituições simples são usadas
para fazer conversões gramaticais. Na �gura, há conversão entre pronomes de pri-
meira pessoa (I, me) e de segunda pessoa (you). Isto signi�ca que as frases digitadas
pelo usuário são verdadeiramente mutiladas durante a aplicação das substituições
simples, ou seja, elas freqüentemente perdem o sentido, servindo apenas para uso
interno pelo chatterbot. Considere o exemplo da Figura 32.
• No lado direito da Figura 31, vemos algumas substituições simples que nada fazem
além de corrigir o texto digitado pelo usuário. Se ELIZA falasse português, este
seria o tipo de substituição simples que corrigiria palavras como �vc� e �td� para
�você� e �tudo�, já que estas abreviações são freqüentemente usadas em bate-papos.
• Por �m, algumas substituições simples podem ser vistas como um meio de normali-
zar o texto digitado pelo usuário. Uma substituição simples que troque �computers�
por �computer� é útil porque assim somente o script só necessita tratar uma única
palavra (�computer�) em suas regras, ao invés de duas.
Figura 32 Exemplo de aplicação de substituições simples
omputeryouWell, I don't really like omputers. omputersIWell, you don't really like omputer.O exemplo da Figura 32 mostra uma entrada do usuário passando pelo processo
de aplicação das substituições simples. Aqui, ocorre a mudança de pronome de primeira
para segunda pessoa (de �I� para �you�) e também uma normalização (em que �computers�
torna-se �computer�).
4.2.4 Palavras-chave
Após a aplicação das substituições simples, o próximo passo consiste na identi�cação
das palavras-chave presentes nas frases do usuário. Estas nada mais são do que as palavras
que o script considera relevante3. A cada palavra-chave está associado um número de
3Por exemplo, para um script que tirasse dúvidas sobre a língua portuguesa, palavras como �vírgula�,�verbo�, �onomatopéia� seriam importantes; já para um outro cujo objetivo fosse guiar clientes em umaloja virtual de móveis, palavras como �cadeira�, �mesa�, �estante� seriam importantes.
48
precedência, que indica sua prioridade. Quanto maior for o número de precedência de
uma palavra-chave, maior é a sua importância.
A Figura 33 mostra a frase introduzida na subseção anterior com as substituições
simples já aplicadas e as palavras-chaves identi�cadas (estão em negrito). As palavras
sob as quais há o �-� não são palavras-chaves; as demais contém sob si seu número de
precedência.
Figura 33 Identi�cação de palavras-chave
5010--0you don't really like omputerWell-Frase:Frase:Na Figura 33, vemos que a sentença original do usuário foi segmentada em duas frases.
Isto se deve ao fato de que ELIZA trabalha sempre com apenas uma frase. Uma frase,
para ELIZA, é todo texto que não contém pontuação4. Assim, a vírgula contida na frase
original digitada pelo usuário de�niu a segmentação observada na �gura.
A frase que passará para os estágios seguintes do processamento é a primeira frase, da
esquerda para direita, que contenha pelo menos uma palavra-chave. No caso da Figura 33,
a primeira frase será descartada, pois não contém palavras-chave. A segunda frase será
utilizada nos passos seguintes, já que contém três palavras-chave.
As palavras-chave da frase sobrevivente são analisadas da esquerda para a direita e
ordenadas em uma pilha de acordo com o seguinte critério: quando uma palavra-chave é
encontrada, ela é posta no topo da pilha se seu número de precedência for maior do que
o maior número de precedência das palavras-chaves já contidas na pilha; caso contrário,
ela é posta no fundo da pilha. Weizenbaum observava que a pilha resultante não estará
monotonicamente ordenada, mas que, de qualquer maneira, estão numa ordem útil e
interessante [Weizenbaum 1966].
A pilha de palavras-chaves para a frase sobrevivente da Figura 33 conteria, portanto,
as palavras-chave nesta ordem: �computer�, �like� e �you�.
4A pontuação que de�nia segmentos na implementação de Weizenbaum era apenas o ponto e a vírgula[Weizenbaum 1966]. É difícil imaginar por que Weizenbaum não considerou também, por exemplo, oponto e vírgula. Neste trabalho, esta restrição é ignorada: qualquer caractere de pontuação segmenta otexto do usuário.
49
4.2.5 Regras de transformação
Uma vez identi�cada qual é a frase com que se trabalhará e também as palavras-chave
desta frase, as regras de transformação podem ser aplicadas. A cada palavra-chave o script
associa uma ou mais regras de transformação. Uma regra de transformação é composta
por dois elementos: uma regra de decomposição e uma ou mais regras de remontagem. Este
panorama pode ser visualizado na Figura 34. Esta �gura mostra parte das transformações
para a palavra-chave �you�.
Figura 34 Regras associadas a palavras-chave
(1) you are (2) (1) you feel (2)you
Do you believe it normal to (2)?Of what does feeling (2) reminds you?Do you often feel (2)?Do you enjoy feeling (2)?
How long have you been (2)?Do you enjoy being (2)?... ...
· · ·
De omposiçãoRemontagem
Palavra- have
Uma regra de decomposição de�ne como a frase sobrevivente do usuário (que neste
ponto já teve as substituições simples aplicadas) pode ser decomposta em fragmentos
menores. Esta decomposição é feita casando-se a frase do usuário com a regra de de-
composição. As regras de decomposição nada mais são do que padrões através dos quais
partes da frase do usuário são identi�cadas.
Quando a frase do usuário casa com o padrão da regra de decomposição, o resultado é
uma associação entre números e trechos da frase do usuário. Estes números são utilizados
50
mais tarde pelas regras de remontagem. O mecanismo mais simples que pode ser utilizado
para realizar esta associação é aquele que casa um número com uma seqüencia de qualquer
tamanho de palavras (inclusive nenhuma), que pode ser observado na Figura 34. Para
outros tipos de casamento, veja a Seção 4.2.6.
Se a frase original do usuário fosse �I am deadly ill�, por exemplo, esta seria transfor-
mada para �You are deadly ill� através das substituições simples. Nenhuma segmentação
aconteceria (não há pontuação). No script original de ELIZA, tanto �you� quanto �are�
são palavras-chave com número de precedência zero; neste caso, �you�, que está mais à
esquerda na frase do usuário, estará no topo da pilha de palavras-chave, como ilustra a
Figura 35. Assim sendo, as regras de decomposição associadas a esta palavra-chave seriam
tentadas uma a uma. A primeira é �(1) you are (2)� (veja a Figura 34), que casa com a
frase do usuário, fazendo �(1)� igual à cadeia vazia de palavras e �(2)� igual a �deadly ill�.
Figura 35 Pilha de palavras-chave Palavra- have: youPeso: 0Palavra- have: arePeso: 0PilhaYou are deadly ill.
Assim que uma das regras de decomposição da palavra-chave que está no topo da
pilha casa com a frase do usuário, uma das regras de remontagem associadas à regra de
decomposição é aplicada. A escolha da regra de decomposição é feita da seguinte maneira:
• Na primeira vez que a regra de decomposição é utilizada, a primeira regra de re-
montagem associada à regra de decomposição pelo script é escolhida.
• Nas vezes seguintes, as próximas regras de remontagem são escolhidas seqüencial-
mente.
• Quando todas as regras de remontagem tiverem sido utilizadas, recomeça-se da
primeira.
Em nosso exemplo, a primeira regra de remontagem é �How long have you been
(2)?� Os números nas regras de remontagem fazem referência ao texto casado com os
respectivos números na regra de decomposição. Aqui, portanto, ao aplicar-se esta regra
51
de remontagem, o �(2)� deve ser substituído pelo texto que foi previamente casado com o
�(2)� da regra de decomposição, a saber: �deadly ill�. Veja a Figura 36.
Figura 36 Transformação de frase do usuário deadly ill(1)(nada) You areDe omposiçãoRemontagem How long have you been deadly ill?(2) (2)Após a aplicação da regra de remontagem, temos a frase �nal que ELIZA comunicará
ao usuário. Seguindo o exemplo, a frase �nal seria �How long have you been deadly ill?�
4.2.6 Padrões em regras de decomposição
Como visto nas subseções anteriores, uma regra de decomposição é um padrão que é
casado com a frase do usuário. Esta subseção mostra quais são os mecanismos que podem
ser utilizados nestes padrões.
O primeiro mecanismo é aquele já visto anteriormente, em que um número é associ-
ado a uma seqüência qualquer de palavras (veja a Figura 37). De acordo com o artigo
de Weizenbaum, o script poderia especi�car a quantidade de palavras que deveriam ser
casadas, ou então optar por um número inde�nido de palavras (esta é a única opção que
este trabalho considera). Os motivos para ignorar esta funcionalidade são os seguintes:
• A utilidade deste mecanismo é limitada. Em geral, não se espera que em um de-
terminado local do padrão haja uma única palavra, mas sim um conjunto delas. Se
um substantivo é esperado, por exemplo, este sempre pode estar acompanhado de
um adjetivo; um verbo, de um advérbio; etc.
• Weizenbaum não tirou proveito do mecanismo nenhuma vez em seu script.
• Como acontece em outros casos, talvez esta seja uma facilidade da linguagem que
Weizenbaum utilizava, mas que acabou sendo subutilizada.
Outro mecanismo que pode ser utilizado nos padrões das regras de decomposição é
aquele que faz uso de grupos5 de palavras pré-de�nidos pelo script. Estes grupos permi-
tem que em um dado ponto da frase uma de várias palavras seja necessária para que o
5Weizenbaum os chama de tags.
52
Figura 37 Exemplo de padrão simplesPadrãoResultadoFraseTexto original (1) your (2)I love my hildren.you love your hildren(1) = you love(2) = hildrencasamento tenha êxito6. Conforme mostra a Figura 38, o script de Weizenbaum de�ne
um grupo chamado de family. Este grupo contém palavras como mother, father, wife,
etc. O mecanismo de grupos também associa um número ao texto que foi casado com o
grupo.
Figura 38 Exemplo de gruposPadrãoResultadoFraseTexto original (1) your (2) (3 [family℄) (4)I think my wife is �ne, but my mother doesn't.you think your wife is �ne(1) = you think(2) = (nada)(3) = wife(4) = is �neO terceiro e último mecanismo utilizado em padrões é idêntico em funcionalidade ao
mecanismo de grupos, porém a lista de palavras possíveis em um local da frase não precisa
ser pré-de�nida. Em vez disto esta lista de alternativas é inserida diretamente no padrão,
conforme mostra a Figura 39.
Figura 39 Exemplo de alternativasPadrãoResultadoFraseTexto original (1) you (2 {want,need}) (3)I need help.you need help(1) = (nada)(2) = need(3) = helpA diferença entre estes dois últimos mecanismos é que, utilizando-se grupos, a lista de
palavras é de�nida apenas uma vez e pode ser referenciada em várias partes do script, ao
6Weizenbaum a�rma que mais de um grupo poderia ser utilizado no mesmo local de um padrão.Provavelmente trata-se, novamente, de um recurso disponível no ambiente que Weizenbaum utilizava,mas que não teve todo seu potencial explorado � o script de Weizenbaum não utiliza nenhuma vez maisde um grupo em um único padrão.
53
passo que, ao inserir-se as alternativas diretamente no padrão, a lista de palavras precisa
ser de�nida cada vez que for utilizada.
Seja quais forem os mecanismos utilizados nos padrões, as regras de remontagem se
comportam da mesma maneiras, já que a elas apenas interessa que conjuntos de palavras
estejam associados aos números que elas utilizam.
4.2.7 Regras de decomposição especiais
Um tipo especial de regra de decomposição é aquela em que simplesmente se indica
que a busca por regras de decomposição deve ser reiniciada, mas utilizando-se uma outra
palavra-chave. Chamemos este tipo de regra de decomposição de redirecionadoras7. Se-
gundo [Weizenbaum 1966], estas regras de decomposição servem para manter um único
conjunto de regras de transformação para mais de uma palavra-chave. No script de Wei-
zenbaum, por exemplo, sempre que a busca por regras de decomposição é iniciada a partir
da palavra-chave �maybe�, a busca é redirecionada para a palavra �perhaps� através de
uma regra de decomposição redirecionadora. Neste caso em particular, uma substitui-
ção simples resolveria o problema: bastaria substituir na entrada do usuário todas as
ocorrências de uma palavra pela outra.
Um caso em que não se poderia utilizar as substituições simples no lugar de uma
decomposição redirecionadora é o da palavra-chave �why� do script de Weizenbaum. Exis-
tem duas regras de decomposição tradicionais para esta palavra, e somente a terceira é
redirecionadora. Portanto, a busca por decomposições só será redirecionada para outra
palavra-chave caso as duas primeiras regras de decomposição não casem com a frase do
usuário.
4.2.8 Regras de remontagem especiais
Da mesma maneira que as regras de decomposição redirecionadora, também as regras
de remontagem podem ser redirecionadoras8. As regras de remontagem redirecionadoras
aparecem, nos scripts, tipicamente por último. Neste caso elas são utilizadas somente
quando todas as outras regras já foram aplicadas em outros ciclos.
Um segundo tipo especial de regra de remontagem pode ser usado pelo script para
indicar que a palavra-chave sendo pesquisada deve ser abandonada, e que a busca deve
7Weizenbaum não utiliza nenhum termo especial para este tipo especial de regra de decomposição.8Novamente, Weizenbaum não utiliza nenhum termo especí�co para este conceito.
54
ser reiniciada partindo-se da próxima palavra-chave da pilha. A este tipo de regra de
remontagem chamemos de abandonadora9. O que deve ser feito quando não há mais
palavras-chave é o assunto da Seção 4.2.10.
4.2.9 Memória
Uma (e só uma) palavra-chave qualquer deve ser associada pelo script a um mecanismo
de memória, que, no dizer de Weizenbaum, �faz com que o sistema responda mais espe-
tacularmente.� [Weizenbaum 1966] Este mecanismo de memória especi�ca precisamente
quatro regras de decomposição (que não podem ser especiais), e a cada uma delas associa
apenas uma regra de remontagem (que também não pode ser especial). A palavra-chave
associada ao mecanismo de memória no script de Weizenbaum é �your�, conforme mostra
a Figura 40.
Figura 40 Memória de ELIZA
(1) your (2)(1) your (2)(1) your (2) But your (3).
Palavra- have: your Earlier you said your (2).Let's dis uss further why your (2).... ...Sempre que, após uma das frases do usuário ter sido escolhida para processamento, a
palavra-chave à qual está associado o mecanismo de memória estiver no topo da pilha de
palavras-chave, o mecanismo de memória é ativado. Neste momento, o chatterbot procede
da seguinte maneira:
1. Uma das regras de decomposição do mecanismo de memória é selecionada aleatori-
amente, e o padrão desta regra é casado com a frase do usuário.10
2. A regra de remontagem é aplicada.
3. O texto resultante é salvo em uma lista para ser utilizado mais tarde (veja Se-
ção 4.2.10).
9Mais uma vez, Weizenbaum não usa nenhum termo para este tipo especial de regra de remontagem.10Weizenbaum não leva em consideração o caso em que este casamento falha. Em seu script, as regras
de decomposição utilizadas no mecanismo de memória são feitas de tal maneira que o casamento nuncafalha (veja a Figura 40).
55
Este mecanismo de memória é o único meio através do qual ELIZA pode falar ao
usuário algo que não seja extraído diretamente da última entrada do usuário.
4.2.10 Reação quando não há palavras-chave
ELIZA pode �car sem palavras-chave por três motivos:
• Não foi localizada nenhuma palavra-chave no texto digitado pelo usuário.
• Uma regra de remontagem abandonadora entrou em ação, porém não havia mais
palavras-chave na pilha.
• Não foi possível casar nenhuma das regras de decomposição da palavra-chave do
topo da pilha com o texto do usuário.11
Seja qual for o motivo, ELIZA responde ou com algum comentário ou com algum texto
armazenado previamente pelo mecanismo de memória.
Weizenbaum não deixa nem um pouco claro quando o texto da memória deve ser
utilizado. A este respeito, diz que
quando um texto sem palavras-chave é encontrado mais tarde e um certomecanismo de contagem está em um estado particular [sic] e a pilha emquestão não está vazia, então o texto transformado é impresso comoresultado.
De qualquer forma, uma das saídas para ELIZA quando encontrar-se sem palavras-
chave é responder com uma das frases armazenadas pela mecanismo de memória.
A segunda saída para ELIZA é fazer um comentário livre de conteúdo [Weizenbaum 1966].
Para que isto seja possível, todo script deve de�nir uma série destes comentários que serão
utilizados por ELIZA no momento oportuno.
11Não é claro se neste caso a próxima palavra-chave da pilha deveria ser candidata a ter suas regrasde decomposição casadas com o texto do usuário. Parece que não é o caso. De qualquer forma, aspalavras-chave sempre podem ter uma regra de decomposição cujo padrão sempre case com o texto dousuário.
56
5 ELIZA em Oz
Este capítulo descreve uma implementação de ELIZA na linguagem Oz. Esta imple-
mentação segue as diretrizes estabelecidas no Capítulo 4 e procura utilizar os recursos de
Oz da melhor maneira possível.
A Seção 5.1 mostra a divisão da implementação em módulos e explica a função de cada
um deles. A Seção 5.2 apresenta a implementação de um módulo de interface somente
texto. A Seção 5.3 fala sobre os módulos de script. A Seção 5.4 mostra a classe que
gerencia o ciclo básico de ELIZA. A Seção 5.5, principal seção deste capítulo, mostra
como a classe Brain gera respostas para o usuário. Por �m, a Seção 5.6 descreve a classe
que gerencia um script.
Explicar o motivo/razão/azar utiliza-se nomes em inglês.
5.1 Divisão em módulos
A linguagem Oz fornece um meio de dividir um programa em módulos. Estes módulos,
em Oz, recebem o nome de functors. Cada um destes módulos pode de�nir uma interface
para que outros módulos interajam com ele. Os módulos de Oz podem ser carregados
dinamicamente, comportando-se como bibliotecas de vínculo dinâmico.
A implementação de ELIZA é constituída dos seguintes módulos:
• Módulo de interface com usuário. Este é o módulo responsável por interagir direta-
mente com o usuário do chatterbot, porém não tem a funcionalidade para responder
às perguntas do usuário.
• Módulo de script. O objetivo deste módulo é fornecer o script necessário para que
o chatterbot possa conversar.
• Módulo do cérebro. Este é o módulo capaz de gerar respostas para as frases do
usuário: é o módulo mais importante desta implementação.
57
O módulo de interface com usuário tem as seguintes atribuições:
• Permitir ao usuário selecionar o módulo de script a ser usado. Como a Seção 4.2.1
a�rma, o script é um dado do programa e mais de um script pode estar disponível
ao usuário num determinado momento. Cabe ao módulo de interface com usuário
possibilitar a escolha de um script.
• Ler as frases do usuário e mostrar as respostas do chatterbot. Esta é a principal
interação entre o usuário e a interface.
• Interagir com a classe Eliza para gerar as respostas a serem entregues ao usuário.
Esta interação está descrita na Seção 5.4.
A Seção 5.2 descreve a implementação de uma interface com o usuário em que a
interação é feita através de uma linha de comando. Esta interface, contudo, é apenas uma
possibilidade: poderia ser implementada uma interface grá�ca, uma interface Web, etc.
O módulo de script de�ne o script utilizado por ELIZA para gerar as respostas.
Este script, apesar de ser um functor de Oz, ou seja, apesar de poder conter programas
arbitrários de Oz, precisa de�nir apenas dados � não precisa de�nir nenhum código. Os
requisitos para um módulo de script são de�nidos na Seção 5.3.
O módulo central para a implementação é o módulo chamado Eliza. Este módulo
de�ne três classes:
• Eliza. Esta é a única classe visível aos outros módulos. É nela que acontece o ciclo
básico de Eliza. Esta classe é descrita na Seção 5.4.
• Brain. Este é o núcleo de toda implementação. É nesta classe que acontece o pro-
cessamento da entrada do usuário e a maior parte do processo descrito em Seção 4.2.
Esta classe é descrita na Seção 5.5.
• Script. Esta classe nada mais é do que uma interface para o script sendo utilizado
pelo chatterbot. É descrita na Seção 5.6.
5.2 Interface texto
O módulo TextUserInterface de�ne uma interface que utiliza somente texto para
comunicação com o usuário. O código-fonte deste módulo está disponível no Apêndice C.
Nesta interface, a interação acontece da seguinte maneira:
58
1. O usuário chama o programa através da linha de comando. Nesta linha de comando,
o usuário pode especi�car que módulo de script.deve ser carregado. Se não especi-
�car nenhum, o módulo de script padrão (o original de Weizenbaum) é carregado.
2. O programa imprime na tela uma mensagem de apresentação que explica ao usuário
como sair do programa e também a mensagem de boas-vindas especi�cada pelo
script.
3. Acontece, então, um ciclo em que é lida uma linha de texto digitada pelo usuário
e uma resposta é impressa pelo chatterbot. O aviso para que o usuário digite sua
entrada é �>�. O sinal �#� precede as respostas do chatterbot.
4. O ciclo é interrompido quando o usuário entra com o texto �quit�. O chatterbot,
então, é �nalizado.
Uma possível interação entre um usuário utilizando um ambiente Unix e o chatterbot pode
ser observada na Figura 41.
Figura 41 Interação entre usuário e chatterbot num sistema Unix.guest@localhost:~$ eliza
This is Eliza. Type "quit" when finished.
# HOW DO YOU DO. PLEASE STATE YOUR PROBLEM
> I am glad today.
# IS IT BECAUSE YOU ARE GLAD TODAY THAT YOU CAME TO ME
> Bye.
# I AM NOT SURE I UNDERSTAND YOU FULLY.
> quit
Para executar os passos de interação enumerados acima, o módulo da interface texto
de�ne:
• A classe Screen, que centraliza as funções de entrada e saída de texto. Objetos
desta classe podem ler textos digitados pelo usuário e imprimir na tela.
• A classe TextUserInterface. A função desta classe é servir de ponte para a comu-
nicação entre o cérebro de Eliza e a interface texto. Esta classe segue a especi�cação
dada em Seção 5.5.
• O procedimento Main. Este procedimento veri�ca os parâmetros da linha de co-
mando e inicia a interação entre o chatterbot e o usuário.
O resto desta seção discorre sobre estes três pontos.
59
5.2.1 Classe Screen
Figura 42 A classe Screen S reenstdinstdoutwriteLine (Line)write (String)readLine (Line)init ()
A classe Screen, cujo diagrama está na Figura 42, lê o texto do usuário utilizando a
entrada padrão1 e escreve as respostas na saída padrão2. Esta classe possui os seguintes
métodos:
• init (): o construtor desta classe, que inicializa os dois atributos, stdin e stdout ,
para objetos capazes de, respectivamente, ler da entrada padrão e escrever na saída
padrão. Este método de�ne uma classe local chamada TextFile, que descende de
duas outras, Open.file e Open.text , estas duas da biblioteca de Oz. Os objetos
de stdin e stdout são instâncias da classe TextFile.
• readLine (Line): lê uma linha digitada pelo usuário. O argumento Line é uni�cado
com a linha lida. Caso haja uma falha durante a leitura da linha, o valor false é
uni�cado com Line, informando a quem chama este método que a leitura falhou.
• write (String): escreve o texto String na saída padrão sem adicionar uma quebra
de linha. Este método é utilizado, por exemplo, para imprimir o aviso de que o
usuário deve entrar com seu texto3.
• writeLine (Line): escreve um texto seguido de uma quebra de linha.
No restante deste trabalho, sempre que se falar em �ler do teclado� ou �escrever na
1�Entrada padrão� é um termo tradicionalmente utilizado para identi�car o local de onde vem o textolido por um programa. Geralmente, trata-se do teclado do usuário, mas pode ser, por exemplo, umarquivo ou o resultado de um comando.
2�Saída padrão� é o termo que designa o destino do texto impresso por um programa, que geralmenteé a tela, mas pode ser também um arquivo ou pode servir de entrada para algum comando.
3Desta maneira o usuário pode digitar seu texto à direita do aviso, por exemplo �> Hello!�.
60
tela�, convencione-se de que está efetivamente se lendo da entrada padrão e escrevendo-se
na saída padrão, que em geral são o teclado e a tela, respectivamente.
5.2.2 Classe TextUserInterface
Figura 43 A classe TextUserInterfaceTextUserInterfa es reenreadUserInput (Input)init ()writeAnswer (Line)writeLine (Line)Uma instância da classe TextUserInterface, cujo diagrama é mostrado na Figura 43,
é passada pelo procedimento Main para um objeto da classe Eliza, que o utiliza para
se comunicar com o usuário (veja Seção 5.2.3, em seguida). Os seguintes métodos são
de�nidos para esta classe:
• init (): construtor da classe. Apenas cria uma nova instância da classe Screen e
põe o resultado no atributo screen .
• readUserInput (Input): lê uma linha de texto do usuário. Este método procede
da seguinte maneira: imprime um aviso ao usuário (�>�), lê uma linha de texto do
usuário e uni�ca esta linha lida com o argumento Input. Retorna false caso a
leitura falhe.
• writeLine (Line): chama diretamente o método writeLine do objeto do atributo
screen . O resultado é o argumento Line ser impresso na tela.
• writeAnswer (Line): escreve a resposta do chatterbot especi�cada através do ar-
gumento Line na tela. Antes de escrevê-la, porém, imprime o símbolo �#� para
indicar que o que segue é uma resposta de ELIZA. A impressão deste símbolo é o
que diferencia este método do anterior writeLine (Line).
61
5.2.3 Procedimento Main
O procedimento Main deste módulo recebe o controle do programa assim que ELIZA
é chamado pela linha de comando. Ele executa as seguintes etapas:
1. Veri�ca que módulo de script deve ser carregado. O usuário pode especi�car na linha
de comando o nome de um arquivo contendo um functor de Oz a ser carregado. Caso
o usuário não especi�que nenhum módulo, o módulo Doctor, que é o script original
de Weizenbaum4, é carregado.
2. Cria um objeto da classe Eliza (veja Seção 5.4), passando para seu construtor,
init (ScriptModule), o módulo que foi carregado no passo anterior.
3. Cria um objeto da classe TextUserInterface, de�nida na seção anterior, e chama o
método chat (UserInterface) do objeto criado no passo anterior, passando-lhe este
objeto recém-criado da classe TextUserInterface. O método chat (UserInterface)
utilizará este objeto para se comunicar com a interface, e só retornará quando a con-
versa com o usuário houver terminado.
5.3 Módulos de script
Os módulos de script, nesta implementação, são functors de Oz que fornecem através
de sua interface todos os dados necessários para o funcionamento de ELIZA. Este módulo
não contém código algum; contém apenas estruturas de dados. As subseções seguintes
descrevem uma a uma as estruturas de dados que devem ser exportadas pelo módulo para
que ele seja um módulo de script válido. Para exemplo de um módulo completo de script,
veja o código-fonte do Apêndice A.
5.3.1 Mensagem de boas-vindas
Um módulo de script deve exportar uma string contendo uma mensagem de boas-
vindas que será comunicada pelo chatterbot ao usuário antes mesmo de pedir-se-lhe que
digite algum texto. O fragmento da Figura 44 de�ne uma variável que pode ser exportada
para servir de mensagem de boas vindas.
4O script certamente foi traduzido para a sintaxe de Oz.
62
Figura 44 De�nição de mensagem de boas-vindasWelcome = "HOW DO YOU DO. PLEASE STATE YOUR PROBLEM"
5.3.2 Substituições simples
As substituições simples de ELIZA (veja Seção 4.2.3) devem ser exportadas por mó-
dulos de script como uma lista de registros cujos rótulos são o átomo subst . Cada um
dos registros deve conter duas características: 'from' e 'to' 5. A característica 'from'
especi�ca a palavra no texto original do usuário e a característica 'to' indica a pala-
vra que deve ser posta no lugar da original. A Figura 45 mostra uma possível lista de
substituições pronta para ser exportada.
Figura 45 Exemplo de substituições simplesSubstitutions =
[
subst ('from' : "DONT" to : "DON'T")
subst ('from' : "I'M" to : "YOU'RE")
]
5.3.3 Grupos de palavras
Os grupos de palavras (veja a Seção 4.2.6) devem ser de�nidos através de uma lista
cujos elementos são registros. Estes registros devem ter como rótulo o átomo group e
como características os átomos name e words (veja a Figura 46). A característica name
especi�ca o nome do grupo6, que pode ser utilizado mais tarde nas regras de decomposição.
A característica words é uma lista com todas as palavras que fazem parte daquele grupo.
Figura 46 Exemplo de de�nição de gruposGroups =
[
group (name : belief
words : ["FEEL" "THINK" "BELIEVE" "WISH"])
]
O trecho de programa da Figura 46 de�ne uma variável, Groups, pronta para ser
exportada como o conjunto de grupos de�nidos por um módulo de script. Aqui apenas
um grupo é de�nido, cujo nome é belief . A este grupo estão associadas as palavras
�FEEL�, �THINK�, �BELIEVE� e �WISH�.5As aspas simples aqui se devem ao fato de que tanto from quanto to são palavras reservadas da
linguagem Oz. As aspas simples permitem utilizar-se átomos com nomes quaisquer.6Ainda que os nomes dos grupos podem ser valores quaisquer, o uso de átomos é mais apropriado do
que, por exemplo, números ou strings.
63
5.3.4 Palavras-chave
As palavras-chave (veja a Seção 4.2.4) são a principal informação exportada por um
módulo de script, e também as estruturas mais complexas. As palavras-chave devem
ser exportadas por um módulo de script sob a forma de uma lista cujos elementos são
registros com o rótulo keyword (veja a Figura 47).
Figura 47 Exemplo de de�nição de palavras-chaveKEYWORDS=
[
keyword (text : "YOU"
rank : 0
rules : [rule (decomp : [0 "YOU"
oneof (num : 0 group : belief )
"YOU" 1]
reasm : [["DO YOU REALLY THINK SO"]
["BUT YOU ARE NOT SURE YOU" 1]])
rule (decomp : [0 "YOU" "WAS" 0]
reasm : [equals (keyword : "WAS")])])
keyword (text : "LIKE"
rank : 10
rules : [rule (decomp : [0
oneof (num : 0 alt : ["AM" "IS" "ARE"])
0 "LIKE" 0]
reasm : [equals (keyword : "DIT")])
rule (decomp : [0]
reasm : [newkey ])])
keyword (text : "HOW"
rank : 0
rules : [rule (equals : "WHAT")])
]
Cada um dos registros de rótulo keyword deve ter três características:
• text : especí�ca o �texto� da palavra-chave, isto é, a representação textual da
palavra-chave. Este texto é o que deve ser casado com a entrada do usuário.
• rank : especi�ca o número de precedência da palavra-chave. Este número pode ser
zero, mas precisa estar sempre presente7.
• rules : contém uma lista de regras de transformação, cada uma delas sendo um
registro com rótulo rule . Cada registro rule por sua vez tem duas características:
decomp , para a regra de decomposição, e reasm para as regras de remontagem.
7Nos scripts de Weizenbaum, este número podia estar oculto, subentendendo-se zero.
64
A regra de decomposição pode ser uma lista, especi�cando neste caso um padrão. Os
elementos desta lista devem ser um dos seguintes (veja Seção 4.2.6):
• Uma string. Esta string deverá casar exatamente com a entrada do usuário.
• Um número. Este número corresponde àqueles padrões simples em que um número
é associado a uma seqüência de palavras. Aqui, no entanto, o número zero tem
signi�cado especial: corresponde, como os demais, a uma seqüência de tamanho
qualquer de palavras, porém pode aparecer várias vezes na regra de decomposição
e não pode ser utilizado nas regras de remontagem.
• Um registro com rótulo group , utilizado para casamento com grupos. Este registro
deve ter duas características. Uma delas é num , que especi�ca o número ao qual
ao texto casado será associado. A segunda característica que deve estar presente é
name , que especi�ca o nome do grupo.
• Um registro com rótulo alt , utilizado para alternativas. Aqui também a caracte-
rística num é usada para indicar a que número deve ser associado o texto casado.
A característica words deve ser uma lista das possíveis palavras que podem �gurar
naquela posição da frase.
As regras de decomposição redirecionadoras (veja Seção 4.2.7) podem ser especi�cadas
de�nindo apenas a característica equals para o registro de rótulo rule . Neste caso,
equals deve estar associada a uma string especi�cando a palavra-chave a partir de onde
a busca deve ser reiniciada (veja Figura 47).
Já as regras de remontagem deve estar associadas à característica reasm do registro
de rótulo rule . A característica reasm especi�ca uma lista cujos elementos são as regras
de remontagem. Cada regra de remontagem deve ser de um dos seguintes tipos (veja a
Seção 4.2.5 e a Seção 4.2.8):
• Uma lista de palavras (strings) e números. Os números referenciam aqueles das
regras de decomposição. A concatenação destas strings com o texto associado aos
números resulta numa resposta apresentável ao usuário.
• Um registro com rótulo equals . Este indica que a busca por respostas deve reco-
meçar na palavra chave indicada pela característica keyword deste registro.
• O átomo newkey , indicando que a busca deve ser reiniciada com a palavra-chave do
topo da pilha.
65
• Um registro com rótulo pre . Este registro indica que um pré-processamento deve ser
feito utilizando-se a lista associada à característica reasm deste registro. Esta lista
pode conter palavras e números (os números aqui também fazem referências àqueles
das regras de decomposição). Após este pré-processamento, a busca deve recomeçar
a partir da palavra-chave indicada pela característica keyword deste mesmo registro.
5.3.5 Memória
Os módulos de script também devem exportar uma outra estrutura para o correto
funcionamento do mecanismo de memória de ELIZA (veja Seção 2.1). Esta estrutura,
exempli�cada na Figura 48, deve ser um registro cujo rótulo é o átomo memory . Este
registro deve ter duas características: keyword , especi�cando a que palavra-chave o me-
canismo de memória deve associar-se, e transfs , especi�cando a lista de transformações
possíveis.
Figura 48 Exemplo de de�nição para a memória de ELIZAMemory =
memory (keyword : "YOUR"
transfs :
[
transf (decomp : [0 "YOUR" 1]
reasm : ["LETS DISCUSS FURTHER WHY YOUR" 1])
transf (decomp : [0 "YOUR" 1]
reasm : ["EARLIER YOU SAID YOUR" 1])
transf (decomp : [0 "YOUR" 1]
reasm : ["BUT YOUR" 1])
transf (decomp : [0 "YOUR" 1]
reasm : ["DOES THAT HAVE ANYTHING TO DO WITH THE FACT THAT YOUR"
1])
])
Os elementos da lista da característica transfs devem ser registros com rótulo
transf . A característica decomp destes rótulos descreve uma regra de decomposição,
enquanto a característica reasm descreve uma regra de remontagem.
5.3.6 Comentários
O último item necessário aos módulos de script é uma lista de comentários livres de
contexto. Os elementos desta lista devem ser strings que indicam diretamente as frases
66
que servem de comentários � nenhuma decomposição ou transformação acontece com
estas strings.
Figura 49 Exemplo de comentáriosComments =
[
"I AM NOT SURE I UNDERSTAND YOU FULLY."
"PLEASE GO ON."
"WHAT DOES THAT SUGGEST TO YOU"
"DO YOU FEEL STRONGLY ABOUT DISCUSSING SUCH THINGS"
]
5.4 Ciclo básico de ELIZA
O ciclo básico de ELIZA, constituindo da leitura de frases do usuário e da geração de
respostas, é gerenciado pela classe Eliza, pertencente a módulo homônimo, cujo diagrama
pode ser visto na Figura 50.
Figura 50 A classe Eliza Elizabraininit (Module) hat (UserInterfa e)A classe Eliza tem apenas dois métodos. O primeiro método é init (Module), o
construtor da classe. Este construtor recebe como único argumento Module, que indica
ao objeto sendo construído qual dever ser o módulo de script utilizado. A única ação
tomada por este método é criar um objeto da classe Brain (veja Seção 5.5), passando
adiante o módulo de script que foi recebido � já que objetos da classe Eliza não interagem
diretamente com scripts.
O segundo método da classe Eliza é chat (UserInterface). Este é o método que
implementa o ciclo básico. A primeira ação deste método é comunicar ao usuário a
mensagem de boas-vindas, com auxílio do objeto UserInterface e do objeto armazenado
no atributo brain .
Após saudar o usuário, o método chat (UserInterface) chama o procedimento lo-
cal ChatLoop. Este procedimento solicita à interface com usuário (UserInterface) que
67
uma linha do usuário seja lida, chamando o método readUserInput (Input) do objeto
UserInterface. Caso a leitura falhe, o ciclo é encerrado imediatamente. Completando-
se com êxito a leitura, o procedimento solicita ao objeto da classe Brain armazenado no
atributo brain que gere uma resposta à entrada do usuário recém-lida. A resposta é co-
municada ao usuário através do método writeAnswer (Line) do objeto UserInterface.
Por �m, o ciclo é reiniciado chamando-se recursivamente o procedimento ChatLoop.
5.5 O cérebro de ELIZA
O cérebro de ELIZA é a classe Brain, cujo diagrama pode ser observado na Figura 51.
Esta classe de�ne três métodos:
• init (Module), um construtor. O argumento Module é o módulo de script que deve
ser utilizado pelo objeto sendo criado. Este módulo é usado pelo construtor para
criar um objeto da classe Script, que é então armazenado no atributo script . Este
construtor também inicializa o atributo memory com a lista vazia � este atributo
contém a lista de respostas do mecanismo de memória, inicialmente sem elementos.
• getWelcome (Answer): este método simplesmente repassa a requisição para o objeto
do atributo script .
• makeAnswer (Input Answer): este método é o coração do chatterbot ELIZA. Ele
recebe uma entrada do usuário em Input e, após gerar uma resposta, uni�ca-a com
Answer. Todo o restante desta seção tratará deste método.
Figura 51 A classe Brain Brains riptmemorymakeAnswer (Input Answer)getWel ome (Answer)init (Module)
Vários métodos das classes desta implementação de ELIZA têm procedimentos locais.
No entanto, nenhum deles tem procedimentos locais na quantidade e no nível de aninha-
mento que o método makeAnswer (Input Answer) tem. A Figura 52 mostra uma árvore
68
com os procedimentos locais deste método, ocultando a hierarquia sob o procedimento
local MakeAnswerFromPhrases. Esta hierarquia é apresentada mais tarde.
Figura 52 O método makeAnswer makeAnswerMakeAnswerFromPhrasesPrepro ess Postpro ess......O método makeAnswer (Input Answer) tem três etapas, que coincidem com os três
procedimentos locais. Estas etapas são as seguintes:
• Pré-processamento. Esta etapa prepara o texto digitado pelo usuário para ser utili-
zado pela etapa seguinte. É vista na Seção 5.5.1.
• Geração de respostas. Aqui a resposta para o usuário é gerada a partir da entrada
fornecida pela etapa anterior. Esta etapa é a mais complexa e está descrita ao longo
de várias seções.
• Pós-processamento. A resposta gerada pela etapa anterior é transformada para
que possa ser diretamente apresentada ao usuário. Esta etapa é discutida na Se-
ção 5.5.10.
Estas três etapas podem ser visualizadas na Figura 53. Aqui vê-se que o corpo propria-
mente dito do método makeAnswer (Input
Result) é composto de apenas uma linha, que chama os três procedimentos corres-
pondentes às etapas acima.
Figura 53 Sumário do método makeAnswer (Input Result)
meth makeAnswer (Input Answer)
%
% Definições dos procedimentos locais...
%
in
Answer={Postprocess {MakeAnswerFromPhrases {Preprocess Input}}}
end
69
5.5.1 Pré-processamento
A etapa de pré-processamento do método makeAnswer (Input Answer) da classe
Eliza é realizada pelo procedimento local {Preprocess String}, que contém outros
procedimentos locais. O argumento String que este procedimento recebe é a string que
o usuário digitou e que será pré-processada. O procedimento age aplicando uma série
de funções em seqüência, sendo que o valor de uma função é fornecido como entrada à
seguinte. As funções aplicadas pelo procedimento são as seguintes:
• {Uppercase String}. Retorna o parâmetro String com todas suas letras con-
vertidas para maiúsculas. Esta conversão é necessário pois ELIZA trabalha apenas
com letras maiúsculas.
Exemplo: "Hello,ttaretyoutthere?" → "HELLO,ttAREtYOUtTHERE?" (o es-
paço está representado pelo �t�).
• {NormalizeSpaces String}. Troca todas seqüências de um ou mais espaços por
apenas um espaço.
Exemplo: "HELLO,ttAREtYOUtTHERE?" → "HELLO,tAREtYOUtTHERE?".
• {SplitIntoPhrases String}. Quebra a string do argumento String em frases,
de acordo com a pontuação. Os espaços são eliminados conforme necessário. A
pontuação não faz parte das frases �nais.
Exemplo: "HELLO,tAREtYOUtTHERE?" → ["HELLO" "AREtYOUtTHERE"].
• {SplitIntoWords Phrases}. Quebra as frases da lista Phrases em palavras. Os
espaços desaparecem durante a conversão.
Exemplo: ["HELLO" "AREtYOUtTHERE?"]→ [["HELLO"] ["ARE" "YOU" "THERE"]].
Os procedimentos locais acima enumerados fazem uso do suporte à programação funcional
de Oz. Funções de ordem superior são utilizadas com freqüências; as células de Oz não são
utilizadas; a própria solução do problema é expressa como uma composição de funções.
5.5.2 O procedimento MakeAnswer
O procedimento MakeAnswer(Phrases Answer) recebe uma entrada, Phrases, que
é o resultado do pré-processamento do texto digitado pelo usuário e gera uma resposta,
uni�cando-a com Answer. Este procedimento comporta-se do seguinte modo:
70
• Como ELIZA trabalha com uma frase somente, tenta-se processar a primeira frase
contida na lista Phrases. Caso esta lista esteja vazia, o mecanismo de memória
é acionado. Se este não devolver uma resposta, um comentário livre de contexto
é gerado. A lista Phrases pode estar vazia por dois motivos: ou o usuário não
digitou nada ou todas as frases já foram processadas por chamadas anteriores a este
procedimento.
• A primeira frase de Phrases sofre o processo das substituições simples.
• Procura-se, então, por palavras-chave na frase. Em seguida, estas palavras-chave
são ordenadas seguindo o critério estabelecido por Weizenbaum.
• Caso nenhuma palavra-chave tenha sido encontrada na primeira frase de Phrases,
o procedimento é invocado recursivamente, mas como parâmetro recebe apenas as
frases restantes de Phrases.
• Utilizando-se o paradigma de programação em lógica, é iniciada uma busca por uma
resposta, baseando-se nas palavras-chave encontradas na primeira frase de Phrases.
Se a busca obtiver sucesso, o precidemento uni�ca a resposta encontrada com o
parâmetro Answer. Se a busca falhar, o procedimento é invocado recursivamente,
mas desta vez com o parâmetro Phrases igual à lista vazia.
5.5.3 Substituições simples
Figura 54 Procedimentos locais de MakeAnswerFromPhrases (I)
ApplySimpleSubstitutins SortKeywordsSubSimplify
MakeAnswerFromPhrasesGetKeywordsCompare
As aplicação das substituições são aplicadas a cada frase antes de esta ser vasculhada
em busca de palavras-chave. A função utilizado para aplicar as substituições simples é
{ApplySimpleSubstitutions(Phrase)}. O parâmetro Phrase que ela recebe é uma lista
71
de palavras que compõem a frase. Cada uma destas palavras será substituída por outra
ou não de acordo com o que o script especi�ca.
Se a função {ApplySimpleSubstitutions(Phrase)} fosse aplicada à frase ["ARE"
"YOU" "THERE"] e se o script sendo utilizado fosse o padrão, a resposta da função seria
["ARE" "I"
"THERE"].
A função {ApplySimpleSubstitutions(Phrase)} é um dos procedimentos locais do
função {MakeAnswerFromPhrases(Phrases)} (veja Figura 54), que por sua vez é proce-
dimento local do método makeAnswer (Input Answer) da classe Eliza.
5.5.4 Busca e ordenação de palavras-chave
A busca de palavras-chave é feita pela função {GetKeywords
Phrase}. A função retorna uma lista das palavras-chave que estão presentes na
frase, na ordem em que aparecem na frase.
Já a função {SortKeywords Keywords} recebe uma lista de palavras-chave e as
retorna ordenada de acordo com a posição na frase e o número de precedência das palavras-
chave.
5.5.5 Regras de decomposição
Figura 55 Procedimentos locais de MakeAnswerFromPhrases (II)
De ompose UpdateMemorySubMat h
MakeAnswerFromPhrasesAddToMemory
ApplyRulesReassembleMakeFromRules
O procedimento {Decompose Input Pattern Map tem a tarefa de confrontar uma
frase vinda do usuário (Input) com um padrão do script (Pattern) e, caso o casamento
seja feito com sucesso, adicionar associações de números a conjuntos de palavras ao dici-
onário indicado por Map.
72
O casamento do padrão com a entrada foi implementado fazendo uso do paradigma de
programação em lógica. Sempre que existe mais de uma possibilidade para o casamento
de padrões, um ponto de rami�cação da árvore de busca é criado utilizando-se a primitiva
choice de Oz.
5.5.6 Regras de remontagem
O procedimento {Reassemble Reasm Map} recebe uma regra de remontagem, Reasm,
que indica como a remontagem deve ser feita, e também um dicionário, Map, que contém
as associações entre números e seqüências de palavras que podem ser utilizadas pela regra
de remontagem.
5.5.7 Regras de transformação
O procedimento {ApplyRules Input Keywords Answer} recebe uma frase do usuá-
rio, já pré-processada, e também uma lista das palavras-chave daquela frase, em Keywords.
Utilizando seu procedimento local MakeFromPatterns, procura por uma resposta para o
usuário e, encontrando-a, uni�ca-a com Answer.
5.5.8 Atualização da memória
Sempre que uma frase possui palavras-chave, a palavra-chave que está no topo da lista
após a ordenação, juntamente com a entrada do usuário, são passados para o procedimento
{UpdateMemory Input Keyword}, que tenta adicionar uma nova frase à memória.
5.5.9 Ausência de palavras-chave
Figura 56 Procedimentos locais de MakeAnswerFromPhrases (III)MakeAnswerFromPhrasesMakeComment RetrieveFromMemoryQuando faltarem palavras-chave, o mecanismo de memória, através da função {RetrieveFromMemory},
é chamado. Caso este falhe, a função {MakeContextFreeComment} é invocada. Esta fun-
ção nunca deve falhar e retornará uma resposta ao usuário.
73
5.5.10 Pós-processamento
O pós-processamento é feito para que a resposta gerada esteja sob a forma de string,
que pode então ser passada à classe de interface com usuário.
5.6 O gerenciador de scripts
A classe Script do módulo Eliza, cujo diagrama está na Figura 57, existe para
prover uma interface amigável aos scripts de ELIZA. Os módulos de script exportam as
informações necessárias para sua utilização, mas estas informações não estão nem prontas
para serem usadas e tampouco podem ser acessadas facilmente. Algumas estruturas do
script também precisam ser atualizadas ao longo do tempo. Os três objetivos principais
desta classe são, pois:
• Deixar a informação do script pronta para ser usada. Em alguns casos faz-se neces-
sário o uso de células para armazenar informação que será atualizada. É o caso das
regras de remontagem, que só devem ser utilizadas uma segunda vez quando todas
as outras regras disponíveis já tiverem sido usadas. Por isso células são inseridas nas
regras de transformação, para que se saiba qual foi a última regra de remontagem
aplicada.
• Tornar mais fácil o acesso a certas informações. Por exemplo: a classe fornece um
método para convenientemente se obter a estrutura de uma palavra-chave baseando-
se em seu texto. Assim, o cérebro de Eliza não precisa preocupar-se em fazer
pesquisas nas estruturas do módulo de script; esta busca é feita pela classe Script.
• Fornecer um meio transparente para a atualização das estruturas do script que
necessitem ser modi�cadas. Estas �estruturas� são as regras de transformação e a
lista de comentários.
O construtor da classe Script, init (Module), recebe como único parâmetro o mó-
dulo de script ao qual o objeto proverá acesso. É neste construtor que as modi�cações
de algumas estruturas são feitas. As modi�cações feitas pelo construtor atingem apenas
duas estruturas exportadas pelo módulo de script: as palavras-chave e os comentários
livres de contexto.
As modi�cações feitas nestas duas estruturas visam a adicionar células para que se
saiba qual foi a última regra de remontagem aplicada. Para as palavras-chave utiliza-se
74
Figura 57 A classe Script S ript
getGroup (Name Group)getSubstitutions (Substs)getMemory (Memory)updateRule (Rule)
wel omegroupssubstsmemory ommentsupdateRulePro keywordsinit (Module)getWel ome (Answer)getComments (List)getKeyword (Word Keyword)a função {ProcessKeywords Keywords} que recebe a estrutura de palavras-chave expor-
tada pelo módulo de script e retorna uma nova estrutura com as células devidamente
adicionadas. Estas células são adicionadas a todo registro rule das palavras-chave. Es-
tes são os registros que contém, sob a característica reasm , as regras de remontagem. São
o local mais adequado, portanto, para armazenar a informação sobre qual foi a última
regra de remontagem aplicada.
O caso da estrutura de comentários é mais simples, pois trata-se de apenas um
conjunto de regras de remontagem. A adição da célula é feita diretamente no método
init (Module).
Algumas estruturas do módulo de script não sofrem quaisquer alterações e são retor-
nadas intocadas pelos métodos de acesso da classe Script. Os métodos que agem desta
maneira são os seguintes:
• getWelcome ($), que retorna a mensagem de boas-vindas do script.
• getMemory ($), que retorna a estrutura utilizada no mecanismo de memória do
chatterbot.
75
A classe Script possui alguns métodos que retornam parte das informações de um
determinado item exportado pelo módulo de script. O objetivo destes métodos é facilitar
o acesso à informação. Os seguintes métodos têm este objetivo:
• getKeyword (Word $): faz uma busca nas estruturas de palavra-chave para en-
contrar aquela que corresponde a palavra Word. Esta estrutura é então retornada.
Retorna false caso Word não seja uma palavra-chave.
• getGroup (GroupName $): procura pelo grupo de nome GroupName, e o retorna.
É um erro de script não existir um grupo a que se faz referência em uma regra de
decomposição.
• getSubstitution (Word $): retorna a palavra que deve ser posta no lugar de Word,
ou nil caso a palavra Word não deva ser substituída por nenhuma outra.
Os comentários, apesar de serem modi�cados pelo construtor da classe Script, não
precisam de nenhum meio de acesso especial: são retornados diretamente pelo método
getComments ($).
Como mencionado acima, as regras de transformação são atualizadas para que arma-
zene a informação de qual regra de remontagem foi utilizada por último. Esta informação
nada mais é do que um contador, que precisa ser incrementado toda vez que a regra de
transformação tiver uma de suas regras de remontagem aplicadas com êxito.
Incrementar este contador, no entanto, não é uma tarefa trivial. O problema surge
porque o objeto gerenciador de script está armazenado na memória do espaço computaci-
onal que está no topo da hierarquia. As regras de remontagem são aplicadas no contexto
de um espaço computacional subordinado e que, portanto, não pode escrever nas células
dos espaços superordinados.
A solução encontrada para o problema é a seguinte. O construtor da classe Script cria
um thread que permanecerá executando no contexto do espaço computacional do topo da
hierarquia. Quando algum espaço computacional subordinado necessitar atualizar alguma
regra de transformação, basta que ele se comunique com este thread, passando-lhe os dados
necessários para a atualização.
Para fazer com que este processo funcione, o construtor init (Module) cria uma
porta de comunicação e inicia um thread que aguarda dados serem recebidos através
desta porta de comunicação e, quando os recebe, baseia-se neles para atualizar a regra de
transformação em questão.
76
Para facilitar a comunicação com o thread que atualiza as regras de transformação,
uma função é criada e armazenada no atributo updateRuleProc . Esta função, quando
chamada, grava as informações recebidas na porta de comunicação e aguarda que o outro
thread atualize a regra de transformação especi�cada. O acesso a esta função é feito
fazendo-se uso do método updateRule (Rule).
Apesar de a solução para o problema da atualização de regras ser um pouco complexa,
para o utilizador da classe Script tudo está transparante: ele apenas ordena que uma
regra seja atualizada, sem preocupar-se como o objeto o fará.
77
6 Considerações �nais
Oz nasceu e permanece no meio acadêmico. Várias linguagens e modelos de progra-
mação, alguns inclusive multiparadigma, não deixaram os artigos para tornar-se realidade.
Não é o que acontece com Oz: há vários anos existe uma implementação da linguagem,
chamada Mozart. A implementação Mozart é totalmente funcional, robusta e conta com
uma biblioteca extensa.
Desde sua concepção, Oz passou por grandes mudanças e melhorias. O ponto positivo
das mudanças é que a linguagem não �cou estagnada; muito pelo contrário, esteve sempre
em desenvolvimento. O ponto negativo das mudanças é que as publicações mais antigas
sobre Oz tem utilidade um pouco reduzida, pois tratavam da linguagem em um estado
anterior de evolução.
Quanto ao chatterbot ELIZA, este é tecnicamente simples, com apenas alguns con-
ceitos auxiliares e sem empecilhos para a implementação. Mesmo com esta simplicidade,
ELIZA impressionou � e impressiona � pela conversa que consegue manter, especial-
mente quando o interlocutor parceiro de ELIZA não sabe que fala com uma máquina. O
interlocutor que sabe que está falando com um programa de computador não se demora
para encontrar falhas. A conversa apresentada por Weizenbaum em [Weizenbaum 1966],
reproduzida na Seção 4.1, mostra bem isso: a pessoa que conversa com ELIZA parece
despreocupada em desmascarar um computador.
ELIZA tem algumas limitações óbvias, por exemplo: o mecanismo de memória pode-
ria ser facilmente expandido; o comportamento repetitivo de ELIZA poderia ser menos
chamativo se um pouco de indeterminismo fosse incluído na geração das respostas. Estas
e outras limitações não se encontram mais nos chatterbots atuais, o que faz de ELIZA um
candidato muito fraco a ser utilizado em alguma aplicação prática. ELIZA constitui, no
entanto, um bom problema para demonstrar as capacidades de Oz.
Ao longo da implementação de ELIZA, a linguagem Oz revelou-se verdadeiramente
multiparadigma. O único dos paradigmas principais disponíveis em Oz que não está
78
presente na implementação foi o paradigma de programação por restrições. Os seguintes
paradigmas podem ser identi�cados na implementação de ELIZA:
• Programação imperativa. Ainda que as estruturas de iteração típicas do paradigma
imperativo, como os loops for e while , não foram usadas nenhuma vez, em algumas
partes há a presença do estado, tão característico deste paradigma. Há estado no
mecanismo de memória de ELIZA, que é alterado ao longo do tempo, e também nas
regras de transformação de ELIZA.
• Programação funcional. Este paradigma foi utilizado tanto na organização de pe-
quenos processos (composição de funções) quanto na elaboração interna das funções.
A parte de pré-processamento da entrada do usuário é um bom exemplo disso: o
corpo da função consiste numa cadeia de quatro funções, e cada uma delas utiliza,
para realizar sua tarefa, funções de ordem superior, recursão ou ambos.
• Programação em lógica. O paradigma de programação em lógica foi utilizado na
aplicação das regras de decomposição. Nesta parte da implementação, o casamento
de padrões é feito descrevendo-se as condições para que o casamento dê certo, mas
sem a preocupação de evitar falhas de uni�cação � o processo de backtracking cuida
delas.
• Programação orientada a objetos. Este paradigma foi usado para estruturar o pro-
grama num nível elevado de abstração. No baixo nível os três paradigmas mencio-
nados acima predominam amplamente, mas quando se trata de agrupar funcionali-
dades, os objetos são usados. O sistema como um todo é composto de cinco classes:
TextUserInterface, Screen, Eliza, Brain e Script.
A idéia por trás das linguagens multiparadigma é simples: permitir ao programador
escolher o paradigma de programação quando ele for resolver um problema, ao invés de
forçá-lo a sempre utilizar o mesmo paradigma para todo tipo de problema. Mas por que
as linguagens multiparadigma não estão sendo utilizadas em larga escala, se trazem para
o programador esta liberdade? Enumero aqui alguns fatores que contribuem para esta
falta de popularidade:
• Antes de surgirem as linguagens multiparadigma, foi necessário que surgissem os
paradigmas para que o �multi� �zesse algum sentido. Isto tem duas conseqüências:
79
� Enquanto os paradigmas de programação surgiam, eles foram muito pesquisa-
dos e linguagens representantes destes paradigmas foram criadas. Os paradig-
mas foram conquistando programadores ao longo deste tempo.
� Houve um tempo até que se pudesse pensar em linguagens multiparadigmas, ou
seja, as linguagens multiparadigmas são bem mais recentes que as tradicionais.
• O processo de aprender uma nova linguagem de programação é tipicamente difícil.
Este processo torna-se mais árduo quando a nova linguagem de programação per-
tence a um paradigma que o programador desconhecia. Mais penoso ainda quando
a nova linguagem o habilita a programar em vários paradigmas que ele desconhece.
• O paradigma de programação imperativo mesclado com a programação orientada a
objetos domina a indústria de software. A indústria utiliza várias linguagens, mas
poucos paradigmas.
Mas esta situação de segundo plano não enfraquece os méritos das linguagens mul-
tiparadigma. Estas linguagens realmente presenteiam o programador com o direito de
escolha. Um programador que opte por escrever sempre da maneira que ele julgue ser a
mais elegante encontra nas linguagens multiparadigma um meio para atingir seu objetivo.
A experiência de Oz mostra que um trecho de programa pode não estar escrito cla-
ramente em um determinado paradigma. Há casos em que qualquer classi�cação de um
código como sendo de um único paradigma será falha. Nesta situação, os paradigmas
estão verdadeiramente integrados � e talvez este seja um caminho para as linguagens
multiparadigma: uni�car os paradigmas de programação de tal forma que, ao progra-
mar, não se pense em um determinado paradigma, mas se utilize as características dos
paradigmas de maneira mais livre.
A implementação de ELIZA foi bene�ciada por ser escrita em uma linguagem �exível
como Oz. Cada parte de ELIZA pôde ser escrita de maneira que �casse sucinta. O
chatterbot executa velozmente, mesmo nas partes em que a programação em lógica foi
adotada. Por �m, cada problema resolvido na implementação incorporou a elegância dos
paradigmas em que foi escrito.
80Referências
[ALICE Bot]ALICE Bot. Disponível em: <www.alicebot.org>.
[Antoy e Hanus]ANTOY, S.; HANUS, M. Curry: A Tutorial Introduction. Disponível em:<http://www.informatik.uni-kiel.de/ curry/tutorial/tutorial.pdf>.
[Bickmore 1999]BICKMORE, T. W. Social intelligence in conversational computer agents.Prosseminar Conceptual Analysis of Thesis Area. Gesture and Narrative Language, 1999.
[Budd 1994]BUDD, T. A. Multiparadigm Programming in Leda. Boston, MA, USA:Addison-Wesley, 1994. ISBN 0201820803.
[Colby 1975]COLBY, K. A computer simulation of paranoid processes. Journal of Arti�-cial Intelligence, 1975.
[Henz 1997]HENZ, M. Objects in Oz. Tese (Doutorado) � Universität des Saarlandes,Fachbereich Informatik, Saarbrücken, Germany, jun. 1997.
[Henz, Smolka e Würtz 1993]HENZ, M.; SMOLKA, G.; WÜRTZ, J. Oz � a program-ming language for multi-agent systems. In: BAJCSY, R. (Ed.). Proceedings of the Thir-teenth International Joint Conference on Arti�cial Intelligence (IJCAI-93). Chambéry,France: Morgan Kaufmann publishers Inc.: San Mateo, CA, USA, 1993. p. 404�409.
[Houaiss 2003]HOUAISS, A. (Ed.). Dicionário inglês-português. [S.l.]: Record, 2003.
[Hutchens 1996]HUTCHENS, J. L. How to Pass The Turing Test by Cheating. [S.l.], abr.1996.
[Kalaiyarasi, Parthasarathi e Geetha 2003]KALAIYARASI, T.; PARTHASARATHI, R.;GEETHA, T. V. Poongkuzhali - an intelligent tamil chatterbot. In: SIXTH TAMILINTERNET 2003 CONFERENCE. [S.l.: s.n.], 2003.
[Laven 2005]LAVEN, S. The Simon Laven Page. 2005. Acessado em 13/08/2005. Dispo-nível em: <http://www.simonlaven.com/>.
[Leonhardt 2003]LEONHARDT, M. D. Elektra: Um chatterbot para uso em ambienteeducacional. In: II Ciclo de Palestras sobre Novas Tecnologias na Educação. [S.l.: s.n.],2003.
[Loebner Prize]LOEBNER Prize. Disponível em: <http://www.loebner.net/Prizef/loebnerprize.html>.
[Mauldin 1994]MAULDIN, M. Tinymuds, and the turing test: Entering the loebner prizecompetition. In: . [S.l.: s.n.], 1994.
[Müller e Müller 1997]MÜLLER, T.; MÜLLER, M. Finite set constraints in Oz. Technis-che Universität München, p. 104�115, 17�19 set. 1997.
[Neto et al. 2004]NETO, A. F. et al. Chatterbot em AIML para o Curso de Ciência daComputação. 2004.
81
[Paradiso e L'Abbate 2001]PARADISO, A.; L'ABBATE, M. A model for the generationand combination of emotional expressions. In: PELACHAUD; POGGI, I. (Ed.). Procee-dings of the Workshop on Multimodal Communication and Context in Embodied Agents.[S.l.: s.n.], 2001.
[Roy et al. 2003]ROY, P. V. et al. Logic programming in the context of multiparadigmprogramming: the Oz experience. Theory and Practice of Logic Programming, Cam-bridge University Press, Cambridge, v. 3, n. 6, p. 717�763, 2003.
[Schulte 2002]SCHULTE, C. Programming Constraint Services. Berlin, Germany:Springer-Verlag, 2002. (Lecture Notes in Arti�cial Intelligence, v. 2302). Disponível em:<http://link.springer.de/link/service/series/0558/tocs/t2302.htm>.
[Schulte, Smolka e Würtz 1998]SCHULTE, C.; SMOLKA, G.; WÜRTZ, J. Finite DomainConstraint Programming in Oz: A Tutorial. 1998. Disponível em: <http://www.mozart-oz.org/documentation/fdt/>.
[Sganderla, Ferrari e Geyer 2003]SGANDERLA, R. B.; FERRARI, D. N.; GEYER, C.F. R. Bonobot: Um chatterbot para interação com usuários em um sistema tutor inte-ligente. In: XIV Simpósio Brasileiro de Informática na Educação. [S.l.: s.n.], 2003.
[Smolka 1995]SMOLKA, G. The Oz programming model. In: LEEWEN, J. v. (Ed.).Computer Science Today: Recent Trends and Developments. Berlin: Springer-Verlag,1995, (Lecture Notes in Computer Science, v. 1000). p. 324�343. ISBN 3-540-60105-8.
[Smolka, Henz e Würtz 1995]SMOLKA, G.; HENZ, M.; WÜRTZ, J. Object-oriented con-current constraint programming in Oz. In: HENTENRYCK, P. van; SARASWAT, V.(Ed.). Principles and Practice of Constraint Programming. [S.l.]: The MIT Press, 1995.cap. 2, p. 29�48.
[Sterling e Shapiro 1994]STERLING, L.; SHAPIRO, E. The Art of Prolog: advanced pro-gramming techniques. Second. Cambridge: MIT Press, 1994. ISBN 0-262-19338-8.
[The American Heritage Dictionary of the English Language 2000]THE American Heri-tage Dictionary of the English Language. [S.l.]: Houghton Mi�in Company, 2000.
[Vrajitoru 2004]VRAJITORU, D. Evolutionary sentence building for chatterbots. In: TheIASTED International Conference on Arti�cial Intelligence and Applications. [S.l.: s.n.],2004.
[Weizenbaum 1966]WEIZENBAUM, J. ELIZA � A computer program for the study ofnatural language communication between man and machine. Communications of theACM, v. 9, n. 1, p. 36�44, jan. 1966.
[Würtz e Müller 1996]WÜRTZ, J.; MÜLLER, T. A Survey on Finite Domain Program-ming in Oz. 1996. Disponível em: <citeseer.ist.psu.edu/153317.html>.
82
APÊNDICE A -- Doctor.oz
functor
export
welcome : Welcome
groups : Groups
substs : Substitutions
memory : Memory
comments : Comments
keywords : Keywords
define
%
% The welcome message.
%
Welcome = "HOW DO YOU DO. PLEASE STATE YOUR PROBLEM"
%
% Simple substitutions
%
Substitutions =
[
subst ('from' : "DONT" to : "DON'T")
subst ('from' : "CANT" to : "CAN'T")
subst ('from' : "WONT" to : "WON'T")
subst ('from' : "DREAMED" to : "DREAMT")
subst ('from' : "DREAMS" to : "DREAM")
subst ('from' : "AM" to : "ARE")
83
subst ('from' : "YOUR" to : "MY")
subst ('from' : "I" to : "YOU")
subst ('from' : "YOU" to : "I")
subst ('from' : "MY" to : "YOUR")
subst ('from' : "WERE" to : "WAS")
subst ('from' : "ME" to : "YOU")
subst ('from' : "MYSELF" to : "YOURSELF")
subst ('from' : "YOURSELF" to : "MYSELF")
subst ('from' : "YOU'RE" to : "I'M")
subst ('from' : "I'M" to : "YOU'RE")
]
%
% Groups of words
%
Groups =
[
group (name : belief
words : ["FEEL" "THINK" "BELIEVE" "WISH"])
group (name : family
words : ["MOTHER" "MOM" "DAD" "FATHER" "SISTER" "BROTHER" "WIFE"
"CHILDREN"])
]
%
% Keywords
%
Keywords =
[
keyword (text : "SORRY"
84
rank : 0
rules : [rule (decomp : [0]
reasm : [["PLEASE DON'T APOLOGIZE"]
["APOLOGIES ARE NOT NECESSARY"]
["WHAT FEELINGS DO YOU HAVE WHEN YOU
APOLOGIZE"]
["I'VE TOLD YOU THAT APOLOGIES ARE NOT
REQUIRED"]])])
keyword (text : "REMEMBER"
rank : 5
rules : [rule (decomp : [0 "YOU" "REMEMBER" 1]
reasm : [["DO YOU OFTEN THINK OF" 1]
["DOES THINKING OF" 1 "BRING ANYTHING
ELSE TO MIND"]
["WHAT ELSE DO YOU REMEMBER"]
["WHY DO YOU REMEMBER" 1 "JUST NOW"]
["WHAT IN THE PRESENT SITUATION REMINDS
YOU OF" 1]
["WHAT IS THE CONNECTION BETWEEN ME AND"
1]])
rule (decomp : [0 "DO" "I" "REMEMBER" 1]
reasm : [["DID YOU THINK I WOULD FORGET" 1]
["WHY DO YOU THINK I SHOULD RECALL" 1
"NOW"]
["WHAT ABOUT" 1]
equals (keyword : "WHAT")
["YOU MENTIONED" 1]])
rule (decomp : [1]
reasm : [newkey ])])
keyword (text : "IF"
rank : 3
rules : [rule (decomp : [0 "IF" 1]
reasm : [["DO YOU THINK ITS LIKELY THAT" 1]
85
["DO YOU WISH THAT" 1]
["WHAT DO YOU THINK ABOUT" 1]
["REALLY, IF" 1]])])
keyword (text : "DREAMT"
rank : 4
rules : [rule (decomp : [0 "YOU" "DREAMT" 1]
reasm : [["REALLY," 1]
["HAVE YOU EVER FANTASIED" 1 "WHILE YOU
WERE AWAKE"]
["HAVE YOU DREAMT" 1 "BEFORE"]
equals (keyword : "DREAM")
newkey ])])
keyword (text : "DREAM"
rank : 3
rules : [rule (decomp : [0]
reasm : [["WHAT DOES THAT DREAM SUGGEST TO YOU"]
["DO YOU DREAM OFTEN"]
["WHAT PERSONS APPEAR IN YOUR DREAMS"]
["DON'T YOU BELIEVE THAT DREAM HAS
SOMETHING TO DO WITH YOUR PROBLEM"]
newkey ])])
keyword (text : "PERHAPS"
rank : 0
rules : [rule (decomp : [0]
reasm : [["YOU DON'T SEEM QUITE CERTAIN"]
["WHY THE UNCERTAIN TONE"]
["CAN'T YOU BE MORE POSITIVE"]
["YOU AREN'T SURE"]
["DON'T YOU KNOW"]])])
keyword (text : "NAME"
rank : 15
86
rules : [rule (decomp : [0]
reasm : [["I AM NOT INTERESTED IN NAMES"]
["I'VE TOLD YOU BEFORE, I DON'T CARE
ABOUT NAMES - PLEASE CONTINUE"]])])
keyword (text : "XFREMD"
rank : 0
rules : [rule (decomp : [0]
reasm : [["I AM SORRY, I SPEAK ONLY
ENGLISH"""]])])
keyword (text : "HELLO"
rank : 0
rules : [rule (decomp : [0]
reasm : [["HOW DO YOU DO. PLEASE STATE YOUR
PROBLEM"]])])
keyword (text : "COMPUTER"
rank : 50
rules : [rule (decomp : [0]
reasm : [["DO COMPUTERS WORRY YOU"]
["WHY DO YOU MENTION COMPUTERS"]
["WHAT DO YOU THINK MACHINES HAVE TO DO
WITH YOUR PROBLEM"]
["DON'T YOU THINK COMPUTERS CAN HELP
PEOPLE"]
["WHAT ABOUT MACHINES WORRIES YOU"]
["WHAT DO YOU THINK ABOUT MACHINES"]])])
keyword (text : "ARE"
rank : 0
rules : [rule (decomp : [0 "ARE" "YOU" 1]
reasm : [["DO YOU BELIEVE YOU ARE" 1]
["WOULD YOU WANT TO BE" 1]
["YOU WISH I WOULD TELL YOU YOU ARE" 1]
87
["WHAT WOULD IT MEAN IF YOU WERE" 1]
equals (keyword : "WHAT")])
rule (decomp : [0 "ARE" "I" 1]
reasm : [["WHY ARE YOU INTERESTED IN WHETHER I
AM" 1 "OR NOT"]
["WOULD YOU PREFER IF I WEREN'T" 1]
["PERHAPS I AM" 1 "IN YOUR FANTASIES"]
["DO YOU SOMETIMES THINK I AM" 1]
equals (keyword : "WHAT")])
rule (decomp : [0 "ARE" 1]
reasm : [["DID YOU THINK THEY MIGHT NOT BE" 1]
["WOULD YOU LIKE IT IF THEY WERE NOT" 1]
["WHAT IF THEY WERE NOT" 1]
["POSSIBLY THEY ARE" 1]])
% The rule below will never be used...
rule (decomp : [0]
reasm : [["WHY DO YOU SAY 'AM'"]
["I DON'T UNDERSTAND THAT"]])])
keyword (text : "MY"
rank : 0
rules : [rule (decomp : [0 "MY" 1]
reasm : [["WHY ARE YOU CONCERNED OVER MY" 1]
["WHAT ABOUT YOUR OWN" 1]
["ARE YOU WORRIED ABOUT SOMEONE ELSES"
1]
["REALLY, MY" 1]])])
keyword (text : "WAS"
rank : 2
rules : [rule (decomp : [0 "WAS" "YOU" 1]
reasm : [["WHAT IF YOU WERE" 1]
["DO YOU THINK YOU WERE" 1]
["WERE YOU" 1]
["WHAT WOULD IT MEAN IF WERE" 1]
88
["WHAT DOES '" 1 "' SUGGEST TO YOU"]
equals (keyword : "WHAT")])
rule (decomp : [0 "YOU" "WAS" 1]
reasm : [["WERE YOU REALLY"]
["WHY DO YOU TELL ME YOU WERE" 1 "NOW"]
["PERHAPS I ALREADY KNEW YOU WERE" 1]])
rule (decomp : [0 "WAS" "I" 1]
reasm : [["WOULD YOU LIKE TO BELIEVE I WAS" 1]
["WHAT SUGGESTS THAT I WAS" 1]
["WHAT DO YOU THINK"]
["PERHAPS I WAS" 1]
["WAHT IF I HAD BEEN" 1]])
rule (decomp : [0]
reasm : [newkey ])])
keyword (text : "YOU"
rank : 0
rules : [rule (decomp : [0 "YOU" alt (num : 0 words : ["WANT"
"NEED"]) 1]
reasm : [["WHAT WOULD IT MEAN TO YOU IF YOU GOT"
1]
["WHY DO YOU WANT" 1]
["SUPPOSE YOU GOT" 1 "SOON"]
["WHAT IF YOU NEVER GOT" 1]
["WHAT WOULD GETTING" 1 "MEAN TO YOU"]
["WHAT DOES WANTING" 1 "HAS TO DO WITH
THIS DISCUSSION"]])
rule (decomp : [0 "YOU" "ARE" 0 alt (num : 1 words :
["SAD" "UNHAPPY" "DEPRESSED" "SICK"]) 0]
reasm : [["I AM SORRY TO HEAR YOU ARE" 1]
["DO YOU THINK COMING HERE WILL HELP YOU
NOT TO BE" 1]
["I'M SURE ITS NOT PLEASANT TO BE" 1]
["CAN YOU EXPLAIN WHAT MADE YOU" 1]])
rule (decomp : [0 "YOU" "ARE" 0 alt (num : 1 words :
89
"HAPPY" "ELATED" "GLAD" "BETTER") 0]
reasm : [["HOW HAVE I HELPED YOU TO BE" 1]
["HAS YOUR TREATMENT MADE YOU" 1]
["WHAT MAKES YOU" 1 "JUST NOW"]
["CAN YOU EXPLAIN WHY YOU ARE SUDDENLY"
1]])
rule (decomp : [0 "YOU" "WAS" 0]
reasm : [equals (keyword : "WAS")])
rule (decomp : [0 "YOU" group (num : 0 name : belief )
"YOU" 1]
reasm : [["DO YOU REALLY THINK SO"]
["BUT YOU ARE NOT SURE YOU" 1]
["DO YOU REALLY DOUBT YOU" 1]])
rule (decomp : [0 "YOU" group (num : 0 name : belief ) 0
"I" 0]
reasm : [equals (keyword : "I")])
rule (decomp : [0 "YOU" "ARE" 1]
reasm : [["IS IT BECAUSE YOU ARE" 1 "THAT YOU
CAME TO ME"]
["HOW LONG HAVE YOU BEEN" 1]
["DO YOU BELIEVE IT NORMAL TO BE" 1]
["DO YOU ENJOY BEING" 1]])
rule (decomp : [0 "YOU" alt (num : 0 words : ["CAN'T"
"CANNOT"]) 1]
reasm : [["HOW DO YOU KNOW YOU CAN'T" 1]
["HAVE YOU TRIED"]
["PERHAPS YOU COULD" 1 "NOW"]
["DO YOU REALLY WANT TO BE ABLE TO" 1]])
rule (decomp : [0 "YOU" "DON'T" 1]
reasm : [["DON'T YOU REALLY" 1]
["WHY DON'T YOU" 1]
["DO YOU WISH TO BE ABLE TO" 1]
["DOES THAT TROUBLE YOU"]])
rule (decomp : [0 "YOU" "FEEL" 1]
reasm : [["TELL ME MORE ABOUT SUCH FEELINGS"]
90
["DO YOU OFTEN FEEL" 1]
["DO YOU ENJOY FEELING" 1]
["OF WHAT DOES FEELING" 1 "REMIND
YOU"]])
rule (decomp : [0 "YOU" 1 "I" 0]
reasm : [["PERHAPS IN YOUR FANTASY WE" 1 "EACH
OTHER"]
["DO YOU WISH TO" 1 "ME"]
["YOU SEEM TO NEED TO" 1 "ME"]
["DO YOU" 1 "ANYONE ELSE"]])
rule (decomp : [1]
reasm : [["YOU SAY" 1]
["CAN YOU ELABORATE ON THAT"]
["DO YOU SAY" 1 "FOR SOME SPECIAL
REASON"]
["THAT'S QUITE INTERESTING"]])])
keyword (text : "I"
rank : 0
rules : [rule (decomp : [0 "I" "REMIND" "YOU" "OF" 0]
reasm : [equals (keyword : "DIT")])
rule (decomp : [0 "I" "ARE" 1]
reasm : [["WHAT MAKES YOU THINK I AM" 1]
["DOES IT PLEASE YOU TO BELIEVE I AM" 1]
["DO YOU SOMETIMES WISH YOU WERE" 1]
["PERHAPS YOU WOULD LIKE TO BE" 1]])
rule (decomp : [0 "I" 1 "YOU"]
reasm : [["WHY DO YOU THINK I" 1 "YOU"]
["YOU LIKE TO THINK I" 1 "YOU - DON'T
YOU"]
["WHAT MAKES YOU THINK I" 1 "YOU"]
["REALLY, I" 1 "YOU"]
["DO YOU WISH TO BELIEVE I" 1 "YOU"]
["SUPPOSE I DID" 1 "YOU - WHAT WOULD
THAT MEAN"]
91
["DOES SOMEONE ELSE BELIEVE I" 1
"YOU"]])
rule (decomp : [0 "I" 1]
reasm : [["WE WERE DISCUSSING YOU - NOT ME"]
["OH, I" 1]
["YOU'RE NOT REALLY TALKING ABOUT ME -
ARE YOU"]
["WHAT ARE YOUR FEELINGS NOW"]])])
keyword (text : "YES"
rank : 0
rules : [rule (decomp : [0]
reasm : [["YOU SEEM QUITE POSITIVE"]
["YOU ARE SURE"]
["I SEE"]
["I UNDERSTAND"]])])
keyword (text : "NO"
rank : 0
rules : [rule (decomp : [0]
reasm : [["ARE YOU SAYING 'NO' JUST TO BE
NEGATIVE"]
["YOU ARE BEING A BIT NEGATIVE"]
["WHY NOT"]
["WHY 'NO'"]])])
keyword (text : "YOUR"
rank : 2
rules : [rule (decomp : [0 "YOUR" 0 group (num : 1 name : family )
2]
reasm : [["TELL ME MORE ABOUT YOUR FAMILY"]
["WHO ELSE IN YOUR FAMILY" 2]
["YOUR" 1]
["WHAT ELSE COMES TO MIND WHEN YOU THINK
OF YOUR" 1]])
92
rule (decomp : [0 "YOUR" 1]
reasm : [["YOUR" 1]
["WHY DO YOU SAY YOUR" 1]
["DOES THAT SUGGEST ANYTHING ELSE WHICH
BELONGS TO YOU"]
["IS IT IMPORTANT TO YOU THAT YOUR"
1]])])
keyword (text : "CAN"
rank : 0
rules : [rule (decomp : [0 "CAN" "I" 1]
reasm : [["YOU BELIEVE I" 1 "DON'T YOU"]
equals (keyword : "WHAT")
["YOU WANT ME TO BE ABLE TO" 1]
["PERHAPS YOU WOULD LIKE TO BE ABLE TO"
1 "YOURSELF"]])
rule (decomp : [0 "CAN" "YOU" 1]
reasm : [["WHETHER OR NOT YOU CAN" 1 "DEPENDS ON
YOU MORE THAN ON ME"]
["DO YOU WANT TO BE ABLE TO" 1]
["PERHAPS YOU DON'T WANT TO" 1]
equals (keyword : "WHAT")])])
keyword (text : "WHAT"
rank : 0
rules : [rule (decomp : [0]
reasm : [["WHY DO YOU ASK"]
["DOES THAT QUESTION INTEREST YOU"]
["WHAT IS IT YOU REALLY WANT TO KNOW"]
["ARE SUCH QUESTIONS MUCH ON YOUR MIND"]
["WHAT ANSWER WOULD PLEASE YOU MOST"]
["WHAT DO YOU THINK"]
["WHAT COMES TO YOUR MIND WHEN YOU ASK
THAT"]
["HAVE YOU ASKED SUCH QUESTION BEFORE"]
93
["HAVE YOU ASKED ANYONE ELSE"]])])
keyword (text : "BECAUSE"
rank : 0
rules : [rule (decomp : [0]
reasm : [["IS THAT THE REAL REASON"]
["DON'T ANY OTHER REASONS COME TO MIND"]
["DOES THAT REASON SEEM TO EXPLAIN
ANYTHING ELSE"]
["WHAT OTHER REASONS MIGHT THERE
BE"]])])
keyword (text : "WHY"
rank : 0
rules : [rule (decomp : [0 "WHY" "DON'T" "I" 1]
reasm : [["DO YOU BELIEVE I DON'T" 1]
["PERHAPS I WILL" 1 "IN GOOD TIME"]
["SHOULD YOU" 1 "YOURSELF"]
["YOU WANT ME TO" 1]
equals (keyword : "WHAT")])
rule (decomp : [0 "WHY" "CAN'T" "YOU" 1]
reasm : [["DO YOU THINK YOU SHOULD BE ABLE TO" 1]
["DO YOU WANT TO BE ABLE TO" 1]
["DO YOU BELIEVE THIS WILL HELP YOU TO"
1]
["HAVE YOU ANY IDEA WHY YOUU CAN'T" 1]
equals (keyword : "WHAT")])
rule (equals : "WHAT")])
keyword (text : "EVERYONE"
rank : 2
rules : [rule (decomp : [0 alt (num : 1 words : ["EVERYONE"
"EVERYBODY" "NOBODY" "NOONE"]) 0]
reasm : [["REALLY," 1]
["SURELY NOT" 1]
94
["CAN YOU THINK OF ANYONE IN
PARTICULAR"]
["WHO, FOR EXAMPLE"]
["YOU ARE THINKING OF A VERY SPECIAL
PERSON"]
["WHO, MAY I ASK"]
["SOMEONE SPECIAL, PERHAPS"]
["YOU HAVE A PARTICULAR REASON IN MIND,
DON'T YOU"]
["WHO DO YOU THINK YOU'RE TALKING
ABOUT"]])])
keyword (text : "ALWAYS"
rank : 1
rules : [rule (decomp : [0]
reasm : [["CAN YOU THINK OF A SPECIFIC EXAMPLE"]
["WHEN"]
["WHAT INCIDENT ARE YOU THINKING OF"]
["REALLY, ALWAYS"]])])
keyword (text : "LIKE"
rank : 10
rules : [rule (decomp : [0 alt (num : 0 words : ["AM" "IS"
"ARE"]) 0 "LIKE" 0]
reasm : [equals (keyword : "DIT")])
rule (decomp : [0]
reasm : [newkey ])])
keyword (text : "DIT"
rank : 0
rules : [rule (decomp : [0]
reasm : [["IN WHAT WAY"]
["WHAT RESEMBLANCE DO YOU SEE"]
["WHAT DOES THAT SIMILARITY SUGGEST TO
YOU"]
95
["WHAT OTHER CONNECTIONS DO YOU SEE"]
["WHAT DO YOU SUPPOSE THAT RESEMBLANCE
MEANS"]
["WHAT IS THE CONNECTION, DO YOU
SUPPOSE"]
["COULD THERE REALLY BE SOME
CONNECTION"]
["HOW"]])])
keyword (text : "HOW"
rank : 0
rules : [rule (equals : "WHAT")])
keyword (text : "WHEN"
rank : 0
rules : [rule (equals : "WHAT")])
keyword (text : "ALIKE"
rank : 10
rules : [rule (equals : "DIT")])
keyword (text : "SAME"
rank : 10
rules : [rule (equals : "DIT")])
keyword (text : "CERTAINLY"
rank : 0
rules : [rule (equals : "YES")])
keyword (text : "MAYBE"
rank : 0
rules : [rule (equals : "PERHAPS")])
keyword (text : "DEUTSCH"
rank : 0
96
rules : [rule (equals : "XFREMD")])
keyword (text : "FRANCAIS"
rank : 0
rules : [rule (equals : "XFREMD")])
keyword (text : "ITALIANO"
rank : 0
rules : [rule (equals : "XFREMD")])
keyword (text : "ESPANOL"
rank : 0
rules : [rule (equals : "XFREMD")])
keyword (text : "MACHINE"
rank : 50
rules : [rule (equals : "COMPUTER")])
keyword (text : "MACHINES"
rank : 50
rules : [rule (equals : "COMPUTER")])
keyword (text : "COMPUTERS"
rank : 50
rules : [rule (equals : "COMPUTER")])
keyword (text : "EVERYBODY"
rank : 2
rules : [rule (equals : "EVERYONE")])
keyword (text : "NOBODY"
rank : 2
rules : [rule (equals : "EVERYONE")])
keyword (text : "NOONE"
97
rank : 2
rules : [rule (equals : "EVERYONE")])
keyword (text : "I'M"
rank : 0
rules : [rule (decomp : [0 "I'M" 1]
reasm : pre (reasm : ["I" "ARE" 1] keyword :
"I"))])
keyword (text : "YOU'RE"
rank : 0
rules : [rule (decomp : [0 "YOU'RE" 1]
reasm : [pre (reasm : ["YOU" "ARE" 1] keyword :
"YOU")])])
]
%
% Memory
%
Memory = memory (keyword : "YOUR"
transfs :
[
transf (decomp : [0 "YOUR" 1]
reasm : ["LETS DISCUSS FURTHER WHY
YOUR" 1])
transf (decomp : [0 "YOUR" 1]
reasm : ["EARLIER YOU SAID YOUR" 1])
transf (decomp : [0 "YOUR" 1]
reasm : ["BUT YOUR" 1])
transf (decomp : [0 "YOUR" 1]
reasm : ["DOES THAT HAVE ANYTHING TO
DO WITH THE FACT THAT YOUR" 1])])
%
98
% Context-free comments
%
Comments =
[
"I AM NOT SURE I UNDERSTAND YOU FULLY."
"PLEASE GO ON."
"WHAT DOES THAT SUGGEST TO YOU"
"DO YOU FEEL STRONGLY ABOUT DISCUSSING SUCH THINGS"
]
end
99
APÊNDICE B -- Eliza.oz
functor
import
System
Search
OS
export
eliza : Eliza
define
class Eliza
attr
brain
meth init (Module)
@brain ={New Brain init (Module)}
end
meth chat (UserInterface)
proc {ChatLoop}
100
case {UserInterface readUserInput ($)}
of false then
skip
[] Input then
{UserInterface writeAnswer ({@brain makeAnswer (Input $)})}
{ChatLoop}
end
end
in
{UserInterface writeAnswer ({@brain getWelcome ($)})}
{ChatLoop}
end
end
class Brain
attr
script
memory
meth init (Module)
@script ={New Script init (Module)}
@memory =nil
skip
end
101
meth getWelcome ($)
{@script getWelcome ($)}
end
meth makeAnswer (Input Answer)
fun {Preprocess UserInput}
fun {Uppercase String}
{Map String Char.toUpper }
end
fun {NormalizeSpaces String}
{FoldL
String
fun {$ String Ch}
if String == nil then
[Ch]
else
if {And {Char.isSpace Ch} {Char.isSpace {List.last
String}}} then
String
else
{Append String [Ch]}
end
end
end
nil }
102
end
fun {SplitIntoPhrases String}
Phrase
Rest
in
{List.takeDropWhile
{List.takeDropWhile
String
fun {$ C} {Not {Char.isAlpha C}} end
_
$}
fun {$ C} {Or {Not {Char.isPunct C}} (C == &')} end
Phrase
Rest}
if Phrase \= nil then
Phrase|{SplitIntoPhrases Rest}
else
nil
end
end
fun {SplitIntoWords Phrases}
{Map Phrases fun {$ L} {String.tokens L & } end}
end
in
103
{SplitIntoWords {SplitIntoPhrases {NormalizeSpaces {Uppercase
UserInput}}}}
end
fun {MakeAnswerFromPhrases Phrases}
fun {ApplySimpleSubstitutions Phrase}
fun {Substitute Word}
NewWord={@script getSubstitution (Word $)}
in
if NewWord \= nil then
NewWord
else
Word
end
end
in
{Map Phrase Substitute}
end
fun {GetKeywords Phrase}
case Phrase
of nil then nil
[] X|Xs then
Y
in
Y={@script getKeyword (X $)}
104
if Y==false then
{GetKeywords Xs}
else
Y|{GetKeywords Xs}
end
end
end
fun {SortKeywords Keywords}
fun {Compare Stack Keyword}
case Stack
of S|_ then
if Keyword.rank > S.rank then
{Append [Keyword] Stack}
else
{Append Stack [Keyword]}
end
[] nil then
[Keyword]
end
end
in
{FoldL Keywords Compare nil }
end
proc {Decompose Input Pattern Map}
105
proc {SubMatch Input Result Rest}
Ir R Rr
in
choice
R|Rr=Result R|Ir=Input {SubMatch Ir Rr Rest}
[] Result=nil Rest=Input
end
end
P Pr I Ir Q N Alt Group
in
choice
P|Pr=Pattern
choice
P=0 {Decompose {SubMatch Input _ $} Pr Map}
[] {IsNumber P}=true P>0=true {Dictionary.put Map P Q}
{Decompose {SubMatch Input Q $} Pr Map}
[] P=alt (num : N words : Alt)
I|Ir=Input {Member I Alt}=true {Dictionary.put Map N
[I]} {Decompose Ir Pr Map}
[] P=group (num : N name : Group)
I|Ir=Input {Member I {@script getGroup (Group
$)}}=true {Dictionary.put Map N [I]} {Decompose Ir Pr Map}
[] P|Ir=Input {Decompose Ir Pr Map}
106
end
[] Pattern=nil Input=nil
end
end
fun {Reassemble Phrase Substs}
case Phrase
of nil then nil
[] P|Pr then
case {IsNumber P}
of true then {Append {Dictionary.get Substs P}
{Reassemble Pr Substs $}}
[] false then P|{Reassemble Pr Substs $}
end
end
end
proc {ApplyRules Input SortedKeywords Answer}
proc {MakeFromPatterns Input Patterns Answer}
Pr P S Phrase
in
P|Pr=Patternscase P
of rule (equals : K next : _) then
Answer=equals (K)
[] rule (decomp : Q next : C reasm : A) then
choice
S={Dictionary.new }
{Decompose Input Q S}
107
Phrase={List.nth A @C}
case Phrase
of pre (reasm : Reasm keyword : Keyword) then
Answer=pre (phrase : {Reassemble Reasm S $}
keyword : Keyword)
[] newkey () then Answer=newkey ()
[] equals (keyword : K) then Answer=equals (K)
else Answer=answer ({Reassemble Phrase S $})
{@script updateRule (P)}
end
[] {MakeFromPatterns Input Pr Answer}
end
end
end
P Pr Patterns T
proc {PrintKeywords Ks}
case Ks
of K|O then {System.printError K.text #"\n"}
{PrintKeywords O}
else skip
end
end
in
P|Pr=SortedKeywordsP.rules =Patterns
T={MakeFromPatterns Input Patterns}
case T
of newkey () then
108
{ApplyRules Input Pr Answer}
[] pre (phrase : Phrase keyword : Keyword) then
{ApplyRules Phrase {@script getKeyword (Keyword $)}|PrAnswer}
[] equals (K) then
{ApplyRules Input {@script getKeyword (K $)}|Pr Answer}
[] answer (A) then
Answer=A
end
end
proc {MakeContextFreeComment Answer}
Comments Next
in
comments (reasm : Comments next : Next)={@script
getComments ($)}
Answer=[{List.nth Comments @Next}]
{@script updateRule ({@script getComments ($)})}
end
proc {UpdateMemory Input Keyword}
proc {AddToMemory Answer}
memory :={Append @memory [Answer]}
end
proc {DoMatch S}
S={Dictionary.new }
{Decompose Input Trans.decomp S}
end
109
N Trans
in
if Keyword.text == {@script getMemory ($)}.keyword then
N=(({OS.rand } mod {List.length {@script
getMemory ($)}.transfs }) + 1)
Trans={List.nth {@script getMemory ($)}.transfs N}
case {Search.base .one proc {$ S} S={DoMatch} end}
of nil then skip
[] [Dic] then
{AddToMemory {Reassemble Trans.reasm Dic}}
end
end
end
fun {RetrieveFromMemory}
case @memory
of M|Mr then
memory :=Mr
M
[] nil then nil
end
end
in
case Phrases
of P|Pr then
Simple
in
Simple={ApplySimpleSubstitutions P}
case {SortKeywords {GetKeywords Simple}}
of nil then
110
{MakeAnswerFromPhrases Pr}
[] Keywords then
{UpdateMemory Simple {List.nth Keywords 1}}
case {Search.base .one fun{$} {ApplyRules Simple
Keywords} end}
of nil then
{MakeAnswerFromPhrases nil }
[] [Answer] then
Answer
end
end
[] nil then
case {RetrieveFromMemory}
of nil then
{MakeContextFreeComment}
[] Answer then
Answer
end
end
end
fun {PostProcess Answer}
case Answer
of L|Lr then {Append L {Append " " {PostProcess Lr}}}
[] nil then nil
end
end
in
Answer={PostProcess {MakeAnswerFromPhrases {Preprocess Input}}}
111
end
end
class Script
attr
welcome
groups
substs
memory
comments
keywords
updateRuleProc
meth init (Module)
fun {StartRuleUpdater}
UpdaterPort RuleList
Wait
proc {RuleUpdater RuleList}
case RuleList
of R|Rr then
X Y
in
if @(R.next )=={Length R.reasm } then
(R.next ):=1
else
(R.next ):=@(R.next )+1
end
Y=@Wait
Wait:=X
112
Y=nil |X{RuleUpdater Rr}
[] nil then skip
end
end
in
UpdaterPort={NewPort RuleList}
thread {RuleUpdater RuleList} end
Wait={NewCell _}
proc {$ Rule}
{Port.send UpdaterPort Rule}
case @Wait
of nil then skip
else skip
end
end
end
fun {ProcessKeywords Keywords}
fun {ProcessRules Rules}
case Rules
of R|Rr then
{Adjoin R rule (next : {NewCell 1})}|{ProcessRules Rr}
[] nil then
nil
end
113
end
in
case Keywords
of K|Kr then
keyword (text : K.text
rank : K.rank
rules : {ProcessRules K.rules })|{ProcessKeywordsKr}
[] nil then
nil
end
end
proc {ProcessGroups Groups Map}
case Groups
of S|Sr then
{Dictionary.put Map S.name S.words }
{ProcessGroups Sr Map}
[] nil then skip
end
end
in
@updateRuleProc ={StartRuleUpdater}
@welcome =Module.welcome
@substs =Module.substs
114
@memory =Module.memory
@comments =comments (reasm : Module.comments next : {NewCell 1})
@keywords ={ProcessKeywords Module.keywords }
@groups ={Dictionary.new }
{ProcessGroups Module.groups @groups }
end
meth getWelcome ($)
@welcome
end
meth getComments ($)
@comments
end
meth getKeyword (Word $)
fun {GetKeywordFromList Word List}
case List
of nil then false
[] X|Xs then
if X.text == Word then X
else {GetKeywordFromList Word Xs}
end
end
end
in
{GetKeywordFromList Word @keywords }
end
meth getGroup (GroupName $)
{Dictionary.get @groups GroupName}
end
115
meth getSubstitution (Word $)
Find={List.dropWhile @substs fun {$ S} S.'from' \= Word end}
in
case Find
of F|_ then
F.'to'
else
nil
end
end
meth getMemory ($)
@memory
end
meth updateRule (Rule)
{@updateRuleProc Rule}
end
end
end
116
APÊNDICE C -- TextUserInterface.oz
functor
import
Open
System
Application
Module
Eliza
Doctor
define
class Screen
attr
stdin
stdout
meth init ()
class TextFile
from Open.file Open.text
end
in
stdin :={New TextFile init (name :stdin )}
stdout :={New TextFile init (name :stdout )}
end
117
meth readLine ($)
fun {RemoveTrailingNewline Line}
case Line
of L|Lr then
case L
of 13 then nil
[] 10 then nil
else L|{RemoveTrailingNewline Lr}
end
else Line
end
end
Line
in
Line={@stdin getS ($)}
case Line
of false then false
else
{RemoveTrailingNewline Line}
end
end
meth write (String)
{@stdout write (vs :String)}
end
meth writeLine (String)
{self write (String)}
{self write ([13 10])}
end
end
class TextUserInterface
118
attr
screen
meth init ()
screen :={New Screen init ()}
end
meth readUserInput ($)
proc {PrintPrompt}
{@screen write ("> ")}
end
in
{PrintPrompt}
case {@screen readLine ($)}
of false then false
[] "quit" then
false
[] Line then
Line
end
end
meth writeAnswer (Answer)
{@screen write ("# ")}
{@screen writeLine (Answer)}
119
end
meth writeLine (Line)
{@screen writeLine (Line)}
end
end
proc {Main}
ArgSpec=record (help (rightmost char : &h default : false)
data (single char : &d type : string default : false))
UsageString=
"usage: eliza OPTION...\n"#
"-h, --help Shows usage info\n"#
"-d, --data FILE Use FILE as rule database\n"
proc {ShowUsageAndQuit ExitCode}
{System.printError UsageString}
{Application.exit ExitCode}
end
Args DataModule ElizaChatterbot UserInterface
in
try
Args={Application.getCmdArgs ArgSpec}
catch _ then
{ShowUsageAndQuit 1}
end
120
if Args.1 \= nil then {ShowUsageAndQuit 1} end
if Args.help then {ShowUsageAndQuit 0} end
try
if Args.data \= false then
[DataModule]={Module.link [Args.data ]}
else
DataModule=Doctor
end
ElizaChatterbot={New Eliza.eliza init (DataModule)}
UserInterface={New TextUserInterface init ()}
catch _ then
{System.printError "Error loading module '"#Args.data #"'.\n"}
{Application.exit 2}
end
{UserInterface writeLine ("This is Eliza. Type \"quit\" when
finished.")}
{ElizaChatterbot chat (UserInterface)}
{Application.exit 0}