122

Implementação do chatterbot ELIZA na linguagem ... · Pascal e C são representantes do mesmo paradigma, o chamado paradigma imperativo de programação, enquanto Prolog é um exemplo

Embed Size (px)

Citation preview

Adiel Mittmann

Implementação do chatterbot ELIZA na

linguagem multiparadigma Oz

Florianópolis

2006

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}

121

end

in

{Main}

end