about summary refs log blame commit diff stats
path: root/js/games/nluqo.github.io/~bh/v2ch9/doctor.html
blob: b83fccbd359879d9701bc272dcac7d32602f161d (plain) (tree)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
1001
1002
1003
1004
1005
1006
1007
1008
1009
1010
1011
1012
1013
1014
1015
1016
1017
1018
1019
1020
1021
1022
1023
1024
1025
1026
1027
1028
1029
1030
1031
1032
1033
1034
1035
1036
1037
1038
1039
1040
1041
1042
1043
1044
1045
1046
1047
1048
1049
1050
1051
1052
1053
1054
1055
1056
1057
1058
1059
1060
1061
1062
1063
1064
1065
1066
1067
1068
1069
1070
1071
1072
1073
1074
1075
1076
1077
1078
1079
1080
1081
1082
1083
1084
1085
1086
1087
1088
1089
1090
1091
1092
1093
1094
1095
1096
1097
1098
1099
1100
1101
1102
1103
1104
1105
1106
1107
1108
1109
1110
1111
1112
1113
1114
1115
1116
1117
1118
1119
1120
1121
1122
1123
1124
1125
1126
1127
1128
1129
1130
1131
1132
1133
1134
1135
1136
1137
1138
1139
1140
1141
1142
1143
1144
1145
1146
1147
1148
1149
1150
1151
1152
1153
1154
1155
1156
1157
1158
1159
1160
1161
1162
1163
1164
1165
1166
1167
1168
1169
1170
1171
1172
1173
1174
1175
1176
1177
1178
1179
1180
1181
1182
1183
1184
1185
1186
1187
1188
1189
1190
1191
1192
1193
1194
1195
1196
1197
1198
1199
1200
1201
1202
1203
1204
1205
1206
1207
1208
1209
1210
1211
1212
1213
1214
1215
1216
1217
1218
1219
1220
1221
1222
1223
1224
1225
1226
1227
1228
1229
1230
1231
1232
1233
1234
1235
1236
1237
1238
1239
1240
1241
1242
1243
1244
1245
1246
1247
1248
1249
1250
1251
1252
1253
1254
1255
1256
1257
1258
1259
1260
1261
1262
1263
1264
1265
1266
1267
1268
1269
1270
1271
1272
1273
1274
1275
1276
1277
1278
1279
1280
1281
1282
1283
1284
1285
1286
1287
1288
1289
1290
1291
1292
1293
1294
1295
1296
1297
1298
1299
1300
1301
1302
1303
1304
1305
1306
1307
1308
1309
1310
1311
1312
1313
1314
1315
1316
1317
1318
1319
1320
1321
1322
1323
1324
1325
1326
1327
1328
1329
1330
1331
1332
1333
1334
1335
1336
1337
1338
1339
1340
1341
1342
1343
1344
1345
1346
1347
1348
1349
1350
1351
1352
1353
1354
1355
1356
1357
1358
1359
1360
1361
1362
1363
1364
1365
1366
1367
1368
1369
1370
1371
1372
1373
1374
1375
1376
1377
1378
1379
1380
1381
1382
1383
1384
1385
1386
1387
1388
1389
1390
1391
1392
1393
1394
1395
1396
1397
1398
1399
1400
1401
1402
1403
1404
1405
1406
1407
1408
1409
1410
1411
1412
1413
1414
1415
1416
1417
1418
1419
1420
1421
1422
1423
1424
1425
1426
1427
1428
1429
1430
1431
1432
1433
1434
1435
1436
1437
1438
1439
1440
1441
1442
1443
1444
1445
1446
1447
1448
1449
1450
1451
1452
1453
1454
1455
1456
1457
1458
1459
1460
1461
1462
1463
1464
1465
1466
1467
1468
1469
1470
1471
1472
1473
1474
1475
1476
1477
1478
1479
1480
1481
1482
1483
1484
1485
1486
1487
1488
1489
1490
1491
1492
1493
1494
1495
1496
1497
1498
1499
1500
1501
1502
1503
1504
1505
1506
1507
1508
1509
1510
1511
1512
1513
1514
1515
1516
1517
1518
1519
1520
1521
1522
1523
1524
1525
1526
1527
1528
1529
1530
1531
1532
1533
1534
1535
1536
1537
1538
1539
1540
1541
1542
1543
1544
1545
1546
1547
1548
1549
1550
1551
1552
1553
1554
1555
1556
1557
1558
1559
1560
1561
1562
1563
1564
1565
1566
1567
1568
1569
1570
1571
1572
1573
1574
1575
1576
1577
1578
1579
1580
1581
1582
1583
1584
1585
1586
1587
1588
1589
1590
1591
1592
1593
1594
1595
1596
1597
1598
1599
1600
1601
1602
1603
1604
1605
1606
1607
1608
1609
1610
1611
1612
1613
1614
1615
1616
1617
1618
1619
1620
1621
1622
1623
1624
1625
1626
1627
1628
1629
1630
1631
1632
1633
1634
1635
1636
1637
1638
1639
1640
1641








































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































                                                                                                                      
<HTML>
<HEAD>
<TITLE>Computer Science Logo Style vol 2 ch 9: Example: Doctor</TITLE>
</HEAD>
<BODY>
<CITE>Computer Science Logo Style</CITE> volume 2:
<CITE>Advanced Techniques</CITE> 2/e Copyright (C) 1997 MIT
<H1>Example: Doctor</H1>

<TABLE width="100%"><TR><TD>
<IMG SRC="../csls2.jpg" ALT="cover photo">
<TD><TABLE>
<TR><TD align="right"><CITE><A HREF="http://www.cs.berkeley.edu/~bh/">Brian
Harvey</A><BR>University of California, Berkeley</CITE>
<TR><TD align="right"><BR>
<TR><TD align="right"><A HREF="../pdf/v2ch09.pdf">Download PDF version</A>
<TR><TD align="right"><A HREF="../v2-toc2.html">Back to Table of Contents</A>
<TR><TD align="right"><A HREF="../v2ch8/v2ch8.html"><STRONG>BACK</STRONG></A>
chapter thread <A HREF="../v2ch10/v2ch10.html"><STRONG>NEXT</STRONG></A>
<TR><TD align="right"><A HREF="https://mitpress.mit.edu/books/computer-science-logo-style-second-edition-volume-2">MIT
Press web page for <CITE>Computer Science Logo Style</CITE></A>
</TABLE></TABLE>

<HR><P>Program file for this chapter: <A HREF="doctormatch.lg"><CODE>doctor</CODE></A>



<P><PRE>? <U>doctor</U>
Hello, I am the doctor.  What can i do for you?
Please end your remarks with an empty line.

<U>My spirits are low.</U>

Your spirits are low?

<U>I'm really depressed.</U>

I'm sorry to hear you are depressed.

<U>It's because nobody likes me.</U>

Really, nobody?

<U>That's right.</U>

I am not sure I understand you fully.

<U>I'm lonely.</U>

Is it because you are lonely that you came to me?

<U>Of course.</U>

Earlier you said your spirits are low.
</PRE>

<P>This is the beginning of a dialogue with a program called <CODE>doctor</CODE>.  The
program simulates the behavior of a psychotherapist.  People who
interact with this program are often fooled into thinking that the program
understands what they are saying, although in fact the program does not
understand at all.  Instead, the program recognizes certain key words in a
sentence, words like &quot;problem&quot; or &quot;nobody.&quot; For each of these words,
there is a list of <EM>rules;</EM> a rule consists of a pattern (of the sort
used by the pattern matcher of Chapter 7) and a set of possible
responses.  For example, associated with the word &quot;nobody&quot; is the pattern

<P><PRE>[# !a:in [everyone everybody nobody noone] #]
</PRE>

<P>Corresponding to that pattern is a list of responses:

<P><PRE>[[Really, :a?]
 [Surely not :a.]
 [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 person in mind, don't you?]
 [Who do you think you're talking about?]
 [I suspect you're exaggerating a little.]]
</PRE>

<P>The program has so many possible responses so that each time you
use the word &quot;nobody&quot; (or &quot;everybody,&quot; etc.) you get a different
answer.  Even though many of the answers have the same meaning, the variety
in the wording helps to convince you that it's a real person at the other
end of the conversation.

<P>Many versions of this program have been written, but the first was written
in 1966 by Joseph Weizenbaum, a professor of computer science at
MIT.  He called his program Eliza after Eliza Doolittle, a
character in the play <EM>Pygmalion</EM> by George Bernard Shaw.  Eliza
started life poor and uneducated, with rough speech, but in the play she is
taught to speak better.  The program, too, can be taught rules to help it
speak better.

<BLOCKQUOTE><P>
Because conversations must be about something, that is, because they must
take place within some context, the program was constructed in a two-tier
arrangement, the first tier consisting of the language analyzer and the
second of a script.  The script is a set of rules rather like those that
might be given to an actor who is to use them to improvise around a certain
theme.  Thus Eliza could be given a script to enable it to maintain a
conversation about cooking eggs or about managing a bank checking account,
and so on.  Each specific script thus enabled Eliza to play a specific
conversational role.

<P>
For my first experiment, I gave Eliza a script designed to permit it to play
(I should really say parody) the role of a Rogerian psychotherapist engaged
in an initial interview with a patient.  The Rogerian psychotherapist is
relatively easy to imitate because much of his technique consists of drawing
his patient out by reflecting the patient's statements back to him...
<P>
[Joseph Weizenbaum, <EM>Computer Power and Human Reason</EM> (Freeman, 1976),
page 3.]
</BLOCKQUOTE>

<P>
It has long been a popular Logo programming project to implement a small
subset of <CODE>doctor</CODE>'s conversational ability through a program that embodies
directly a few of the rules in Weizenbaum's Doctor script for Eliza.  (See,
for example, Harold Abelson, <EM>Apple Logo</EM> (BYTE Publications, 1982),
page 158.)  Home computers now have enough memory to permit the
implementation in Logo of Weizenbaum's original two-tier approach.  The
program presented here is not a line-for-line translation of Eliza into
Logo, but does embody the same fundamental strategy (namely pattern
matching) as the original.  The script is a close adaptation of Weizenbaum's
version, via a Lisp version by Jon L. White.

<P>You should try conversing with <CODE>doctor</CODE> yourself a few times.  Whenever you
get a response that seems linguistically bizarre, make a note of it.  Later,
as I'm talking about the way the program works, you can ask yourself how you
would modify the script to eliminate the bad responses you've noted.

<P><H2>Eliza and Artificial Intelligence</H2>

<P>When Eliza was first unveiled, many people considered it a major advance in
the pursuit of <EM>artificial intelligence:</EM> the search for ways
to make computers as intelligent as people.  Especially to a person
unfamiliar with computer programming, a conversation with the Doctor can
seem very real indeed.  But as Weizenbaum himself points out, the underlying
techniques used in the program do not involve any real understanding of the
conversation.  The program &quot;cheats.&quot;

<P>Today there are language-understanding programs that use techniques much
more sophisticated than the simple pattern matching of Eliza.  The authors
of some such programs maintain that they really do understand what they are
saying and hearing, in a sense in which Eliza does not.  Other people,
including Weizenbaum, suggest that even these state-of-the-art artificial
intelligence programs are merely cheating in more complex ways.  What would
it mean for a program &quot;really&quot; to understand a conversation?  This is a
deep question that would take too long to explore here.  (I'll come back to
it in the discussion of artificial intelligence in the third volume of this
series.)  If you're interested, you should begin by reading Weizenbaum's
book, <EM>Computer Power and Human Reason</EM>, from which I quoted a passage
earlier.  He argues not only that computers <EM>cannot</EM> do certain
things, but also that people <EM>should not</EM> use computers for certain
purposes even if it were possible.  In particular, he is horrified at the
suggestion, made even by some psychiatrists, that programs like Eliza should
be used to provide therapy to actual patients.

<P><H2>Eliza's Linguistic Strategy</H2>

<P>The program allows the user to enter one or more sentences, typing several
lines if necessary.  The first step in processing the user's remarks is to
string the several lines into one long list.  This list is a &quot;sentence&quot; in
the Logo technical sense; that is, it's a list of words with no sublist
structure.  It may, however, contain one or more English sentences.  The
generation of a response involves several steps:

<P>
<OL><LI> Find the punctuation in the input and use it to extract a single
sentence to answer.
<LI> Find keywords in the sentence for which the script includes rules.
<LI> Apply word-for-word translations as specified by the script; these
are primarily to convert first person (&quot;I&quot;) to second person (&quot;you&quot;) and
vice versa.
<LI> Pick the highest priority keyword.
<LI> Find a rule for that keyword whose pattern matches the input
sentence.
<LI> Choose a response according to that rule.
</OL><P>

<P>These steps are not really quite sequential; for example, the
first two are done in parallel because, if there is more than one sentence,
the program chooses a sentence that contains one or more keywords.

<P>I have omitted from this list several possible complications.  One important
one is that there is a second kind of rule (besides the response rules)
called a <EM>memory</EM> rule.  These rules do not generate immediate
responses to what the user types.  Instead, they generate sentences that are
appropriate for later use, like &quot;Earlier you said your spirits are low&quot; in
the sample dialogue earlier.  That response was not generated from the
user's comment just before it, but was instead added to memory at the time
of the user's first remark.  Because the user's comment &quot;Of course&quot;
contained no keywords, the program chose to use a response from memory at
that time.

<P>Other complications have to do with the nature of the rules associated with
a keyword.  Instead of patterns and responses, a keyword can just have
<EM>another</EM> keyword as its rules, meaning that the rules for that keyword
should be used for this one also.  Alternatively, another keyword can be
used in place of a response for a particular pattern, so the alternate
keyword is used only if that pattern is matched.  Another alternative to
a response is the word &quot;newkey,&quot; which means that the program should
abandon this keyword and try another one in the same sentence.  Yet another
alternative is an instruction to rearrange the sentence and try matching
patterns again with the new version.  As you read the procedures shown
below, try not to get caught up in these complications the first time
through.  Just forget about the <CODE>if</CODE> instructions that check for these
special cases and concentrate on the usual situation.

<P>I'll explain how each part of the linguistic strategy is carried out by
the procedures in the project.  Here is a diagram of the
subprocedure/superprocedure relationships.  The top-level <CODE>doctor</CODE> does
some initialization and then invokes <CODE>loop</CODE>, which does the real work.

<P><CENTER><IMG SRC="doctorflow.gif" ALT="figure: doctorflow"></CENTER>

<P><PRE>to doctor
local [text sentence stuff a b c rules keywords memory]
make &quot;memory []
print [Hello, I am the doctor. What can I do for you?]
print [Please end your remarks with an empty line.]
print []
loop
end

to loop
make &quot;text tokenize getstuff []
make &quot;sentence getsentence :text
analyze :sentence :keywords
print []
loop
end
</PRE>

<P><CODE>Loop</CODE> uses <CODE>getstuff</CODE> to read several lines of the user's
typing into a long list.  This list is processed by several procedures in
turn.

<P><PRE>to getstuff :stuff
localmake &quot;line readlist
if emptyp :line [output :stuff]
output getstuff sentence :stuff :line
end
</PRE>

<P>The first step in my numbered list is to find the punctuation and use it to
extract a single sentence from the list.  The first part, finding the
punctuation, is the job of <CODE>tokenize</CODE> and its subprocedure <CODE>
tokenword</CODE>.  (These procedures get their name from the computer scientist's
term <EM>token</EM>, which means the smallest possible meaningful string of
characters.  In the BASIC compiler of Chapter 6, the procedure called
<CODE>reader</CODE> separates a line of text into tokens.)

<P><PRE>to tokenize :text
output map.se [tokenword ? &quot;] :text
end

to tokenword :word :out
if emptyp :word [output :out]
if memberp first :word [, &quot; ] [output tokenword butfirst :word :out]
if memberp first :word [. ? ! |;|] [output sentence :out &quot;.]
output tokenword butfirst :word word :out first :word
end
</PRE>

<P>The program's understanding of punctuation is
very simple.  Some punctuation, like a quotation mark, is just ignored
completely.  Periods, question marks, and semicolons are all treated as
having the same meaning, namely, they mark the end of a sentence.  <CODE>
tokenword</CODE> turns a word like <CODE>why?</CODE> into the two-word list <CODE>[why .]</CODE>
so that the period as a separate word serves as the separator between
sentences in the long list.

<P>Extracting a single sentence is done at the same time as steps 2 and 3,
finding keywords and applying translations.  All of these are done by
<CODE>getsentence</CODE>, which uses <CODE>checkpriority</CODE> and <CODE>translate</CODE> as
subprocedures for tasks 2 and 3 respectively.  (The version of Doctor
in the first edition worked with all capital letters.  In this new version,
I've added a procedure <CODE>decapitalize</CODE> that turns the first letter of
each sentence to lower case, and when the program prints a
response it uses the inverse procedure <CODE>capitalize</CODE> to capitalize the
first word.  This is necessary because the first word of the user's sentence
might end up in the middle of the program's response, and vice versa.)

<P><PRE>to getsentence :text
make &quot;keywords []
output getsentence1 decapitalize :text []
end

to getsentence1 :text :out
if emptyp :text [output :out]
if equalp first :text &quot;. ~
   [ifelse emptyp :keywords
           [output getsentence1 decapitalize butfirst :text []]
           [output :out]]
checkpriority first :text
output getsentence1 butfirst :text sentence :out translate first :text
end

to decapitalize :text
if emptyp :text [output []]
output fput lowercase first :text butfirst :text
end

to checkpriority :word
localmake &quot;priority gprop :word &quot;priority
if emptyp :priority [stop]
if emptyp :keywords [make &quot;keywords ( list :word ) stop]
ifelse :priority &gt; ( gprop first :keywords &quot;priority ) ~
       [make &quot;keywords fput :word :keywords] ~
       [make &quot;keywords lput :word :keywords]
end

to translate :word
localmake &quot;new gprop :word &quot;translation
output ifelse emptyp :new [:word] [:new]
end
</PRE>

<P>At each sentence separator (a word containing only a period), <CODE>
getsentence1</CODE> checks whether any keywords have been found yet.  If so, the
sentence before the separator is the one the program uses.  If not, <CODE>
getsentence1</CODE> goes on to examine the next sentence in the list.  If the last
sentence ends without any keywords found, that last sentence is chosen
anyway.

<P>

<P>Both <CODE>checkpriority</CODE> and <CODE>translate</CODE> work through the use of
property lists that are associated with words.  The script part of
<CODE>doctor</CODE> (Weizenbaum's second tier) consists of these property lists.
For example, here's part of the script setup:

<P><PRE>pprop &quot;my &quot;priority 2
pprop &quot;my &quot;translation &quot;your
</PRE>

<P>

<P>The first of these means that <CODE>my</CODE> is a keyword, with priority
2.  In the Doctor script, most keywords have priority 0.  <CODE>My</CODE> is a
little more important than most words, but not as important as <CODE>dreamed</CODE>
(priority 4) or <CODE>computer</CODE> (priority 50)!  <CODE>Checkpriority</CODE> arranges
the list of keywords so that the word with highest priority is first in the
list.  The <CODE>translation</CODE> property means that the word <CODE>my</CODE> is
changed to <CODE>your</CODE> in generating responses.  For example, at the
beginning of the sample dialogue above, the sentence &quot;My spirits are low&quot;
is echoed as &quot;Your spirits are low?&quot;  (The keyword list created by
<CODE>checkpriority</CODE> has <EM>untranslated</EM> keywords.  That's why the
patterns associated with the keyword <CODE>you</CODE>, for example, all contain
the word <CODE>I</CODE> instead; the patterns deal with the translated version.)

<P>Step 4, finding the highest priority keyword, is simply a matter of choosing
the <CODE>first</CODE> keyword in the list because of the way <CODE>checkpriority</CODE>
has done its job.  This selection is made by <CODE>analyze</CODE>, which then
invokes <CODE>checkrules</CODE> as a subprocedure.  (<CODE>Analyze</CODE> also recognizes
the special situation in which one keyword refers to the rules of another.)

<P><PRE>to analyze :sentence :keywords
local [rules keyword]
if emptyp :keywords [norules stop]
make &quot;keyword first :keywords
make &quot;rules gprop :keyword &quot;rules
if wordp first :rules ~
   [make &quot;keyword first :rules make &quot;rules gprop :keyword &quot;rules]
checkrules :keyword :rules
end
</PRE>

<P><CODE>Checkrules</CODE> handles step 5, finding an applicable rule for the chosen
keyword.  That is, <CODE>checkrules</CODE> invokes <CODE>match</CODE> to match the
selected sentence against each pattern associated with the given keyword.
(The program assumes that the script is written so that there will always be
at least one matching pattern.  Most keywords have <CODE>[#]</CODE> as the pattern
in the last rule.)  When <CODE>checkrules</CODE> finds a matching pattern, it
invokes <CODE>dorule</CODE> to examine the corresponding list of responses.  (One
complication in understanding these procedures is that the input to <CODE>
dorule</CODE> is <EM>the name of a property</EM> whose value is the list of
responses.  I'll get back to discussing why it's done this way later; for
now, all that's really important is that <CODE>dorule</CODE> chooses one of the
responses and uses it as the input to <CODE>reconstruct</CODE>.)

<P><PRE>to checkrules :keyword :rules
if not match first :rules :sentence ~
   [checkrules :keyword butfirst butfirst :rules stop]
dorule first butfirst :rules
end

to dorule :rule
localmake &quot;print first gprop :keyword :rule
pprop :keyword :rule lput :print butfirst gprop :keyword :rule
if equalp :print &quot;newkey [analyze :sentence butfirst :keywords stop]
if wordp :print [checkrules :print gprop :print &quot;rules stop]
if equalp first :print &quot;pre ~
   [analyze reconstruct first butfirst :print
            butfirst butfirst :print
    stop]
print capitalize reconstruct :print
memory :keyword :sentence
end
</PRE>

<P>The usual task of <CODE>dorule</CODE> is to carry out step 6, the generation of a
response.  For example, when the user typed

<P><PRE>My spirits are low.
</PRE>

<P>the highest priority keyword found was <CODE>my</CODE>.  There are three
patterns associated with this keyword:

<P><PRE>[# your # !a:familyp #b]
[# your &amp;stuff]
[#]
</PRE>

<P>(Again, the pattern is matched against the sentence <EM>after</EM>
translation, so the patterns contain the word <CODE>your</CODE> even though the
actual keyword is <CODE>my</CODE>.)  The first of these patterns does not match the
sentence.  (<CODE>Familyp</CODE> is a predicate that's true for words like <CODE>
mother</CODE> and <CODE>brother</CODE>.)  The second pattern, however, does match the
sentence.  <CODE>Match</CODE> gives the variable <CODE>stuff</CODE> the list

<P><PRE>[spirits are low]
</PRE>

<P>as its value.  (The period that ended what the user typed was
removed by <CODE>tokenize</CODE>.)

<P>Associated with that second pattern is this list of responses:

<P><PRE>[[Your :stuff?]
 [Why do you say your :stuff?]
 [Does that suggest anything else which belongs to you?]
 [Is it important to you that your :stuff?]]
</PRE>

<P><CODE>Dorule</CODE> chooses the first of these, and invokes <CODE>
reconstruct</CODE> to substitute the actual value of the variable into the
response.  By the way, although I've used Logo's notation of colon to mean
&quot;the value of the variable,&quot; <CODE>reconstruct</CODE> isn't exactly like the Logo
interpreter.  For one thing, it recognizes punctuation marks; it knows that
this response refers to a variable named <CODE>stuff</CODE>, not <CODE>stuff?</CODE>.

<P><PRE>to reconstruct :sentence
if emptyp :sentence [output []]
if not equalp &quot;: first first :sentence ~
   [output fput first :sentence reconstruct butfirst :sentence]
output sentence reword first :sentence reconstruct butfirst :sentence
end

to reword :word
if memberp last :word [. ? ,] ~
   [output addpunct reword butlast :word last :word]
output thing butfirst :word
end

to addpunct :stuff :char
if wordp :stuff [output word :stuff :char]
if emptyp :stuff [output :char]
output sentence butlast :stuff word last :stuff :char
end
</PRE>

<P><H2>Stimulus-Response Psychology</H2>

<P>Historically, there have been two ways of looking at the purpose of
artificial intelligence research.  One way is to see it as research into
what computers can do, and into the meaning of intelligence in general,
without any special reference to how <EM>people</EM> think.  Researchers who
take this approach are willing to use any technique that will solve a
problem, even if it's perfectly obvious that people don't think that way.
The second approach is to see artificial intelligence as a way to shed light
on human intelligence.  In this approach, the idea is to use the computer as
a <EM>model</EM> for the human mind.  Researchers who follow this path try to
write their programs to mimic human behavior and, they hope, even the inner
mechanisms of human brains.  Recently there has been a tendency for
researchers to declare themselves as wholly in one camp or the other.
People who want to solve problems even if by non-human methods are part of
the &quot;knowledge engineering&quot; field; the programs they develop are called
&quot;expert systems.&quot; People who want to use the computer to help build
theories of human intelligence are in the field of
&quot;cognitive science.&quot;

<P>Weizenbaum's work on Eliza is an early example of the former approach.  He
was emphatically <EM>not</EM> claiming that his program worked the way people
work.  Indeed, one of the purposes of the program was to demonstrate how
realistic the <EM>behavior</EM> of a computer program can be, even when we
are quite sure that the underlying <EM>mechanism</EM> is completely
unrealistic.

<P>Nevertheless, Eliza could be taken as a computer model of a certain theory
of human psychology.  It may not be obvious what it means for a computer
program to model a theory about people, so it may be worthwhile to examine
Eliza from this point of view.  One theory about how people think is called
<EM>behaviorism,</EM> or the <EM>stimulus-response</EM> theory.
According to this theory, a person's mind is a &quot;black box,&quot; and we can't
know what's inside.  What we <EM>can</EM> know, however, is how a person
reacts to different situations and events.  Whatever situation presents
itself to you is called a <EM>stimulus</EM>.  A stimulus can be something very
abrupt like an electric shock, or it can be something more subtle like a
particular sentence spoken by a particular person.  When you are presented
with a given stimulus, you produce a certain <EM>response</EM>: you say
something back, or you jump, or you fall asleep.  People learn to associate
certain responses with certain stimuli.  People can be trained to change the
response associated with a stimulus by using <EM>conditioning</EM>
techniques.  If you are rewarded for producing a certain response, you'll
produce it more often.

<P>The behaviorist theory was very influential several years ago, although
hardly anyone believes it any more.  What would it mean to write a computer
model for this theory?  Well, the model would have two main parts: one that
recognizes stimuli, and one that produces a response for a given stimulus.
In Eliza, the first part is the pattern matcher.  I haven't spoken much
about that part of the program in this description because I discussed it at
length in the last project.  But in fact <CODE>match</CODE> and its subprocedures
are a substantial part of the complete <CODE>doctor</CODE> program.  The second
part, the one that produces responses, is <CODE>dorule</CODE> and <CODE>reconstruct</CODE>.

<P>Eliza does not represent a very sophisticated form of stimulus-response
theory because it leaves out the idea of <EM>learning</EM>.  In Eliza, the
responses are all provided in advance, as part of the script.  People can
develop new responses over time, and behaviorist theory has a lot to say
about exactly what the rules are that govern such learning.  (That's what
education is, to a behaviorist: learning new responses to stimuli.)  Since
the script for Eliza is stored in lists, and those lists can be manipulated
by the program, it would be possible to modify the rules of the program so
that it can learn new rules while it's running.  For example, if the user
types something like &quot;What are you talking about?&quot; then the program could
decide that its previous response was inappropriate.  It would learn to
avoid that response next time.  You might like to think about how to design
such an extension to the project as presented here.

<P>Researchers who, unlike Weizenbaum, are deliberately trying to model
theories of human psychology generally use a more complicated program
structure.  For example, experiments measuring the reaction time of human
beings in different situations seem to indicate that people have a
short-term memory and a long-term memory.  The former may hold the telephone
number you're dialing right now, for instance, while the latter holds all
the telephone numbers of all your friends.  Short-term memory is faster than
long-term, but much smaller; you can only remember a few things at a time in
it.  Computers do not inherently have these two kinds of memory; they're
really good at remembering many things <EM>and</EM> finding them quickly.
But cognitive scientists write programs that deliberately limit the
computer's ability to remember things quickly, trying to model the inner
structure of the brain in this way.

<P><H2>Property Lists</H2>

<P>The response rules, memory rules, translations, and priorities that make up
the script are all stored in the form of property lists.  Each
keyword has a property list.  For example, the property list for the word
<CODE>my</CODE> looks like this:

<P>

<P><PRE>[priority 2
 translation your
 rules [[# your # !a:familyp #b] g1 [# your &amp;stuff] g2 [#] g3]

 g1 [[Tell me more about your family.]
     [Who else in your family :b?]
     [Your :a?]
     [What else comes to mind when you think of your :a?]]
 g2 [[Your :stuff?]
     [Why do you say your :stuff?]
     [Does that suggest anything else which belongs to you?]
     [Is it important to you that your :stuff?]]
 g3 [newkey]
 memr [[# your &amp;stuff] g4]
 g4 [[Earlier you said your :stuff.]
     [But your :stuff.]
     [Does that have anything to do with your statement about :stuff?]]]
</PRE>

<P>Remember that a property list contains pairs of members; the odd
numbered members are names of properties, and the even numbered members are
the values of those properties.  The word <CODE>my</CODE> has properties named
<CODE>priority</CODE>, <CODE>translation</CODE>, <CODE>rules</CODE>, <CODE>g1</CODE>, <CODE>g2</CODE>, <CODE>
g3</CODE>, <CODE>memr</CODE>, and <CODE>g4</CODE>.  The <CODE>priority</CODE> and <CODE>translation</CODE>
properties are straightforward.  The <CODE>rules</CODE> and <CODE>memr</CODE> properties
have as their values lists in which the odd numbered members are patterns
and the even numbered members are names of other properties.  These other
properties contain the lists of responses.  The names of these secondary
properties are arbitrary and are generated by the program.

<P>To create these property lists, I used <CODE>pprop</CODE> directly for some of the
properties, but wrote setup procedures to help with the more complicated
parts.  Here are the instructions that contribute to this property list.

<P><PRE>pprop &quot;my &quot;priority 2
pprop &quot;my &quot;translation &quot;your
addrule &quot;my [# your # !a:familyp #b]
  [[Tell me more about your family.]
   [Who else in your family :b?]
   [Your :a?]
   [What else comes to mind when you think of your :a?]]
addrule &quot;my [# your &amp;stuff]
  [[Your :stuff?]
   [Why do you say your :stuff?]
   [Does that suggest anything else which belongs to you?]
   [Is it important to you that your :stuff?]]
addrule &quot;my [#] [newkey]
addmemr &quot;my [# your &amp;stuff]
  [[Earlier you said your :stuff.]
   [But your :stuff.]
   [Does that have anything to do with your statement about :stuff?]]
</PRE>

<P>In general, the order in which properties are added to the list
doesn't matter.  However, the order of the <CODE>addrule</CODE> instructions <EM>
does</EM> matter, because the rule that's added first is the one that
<CODE>checkrules</CODE> tries first.  It's important, therefore, that the rules go
from most specific pattern to least specific pattern.  In this case, the
first pattern checks for a remark about a member of the user's family; the
second checks for a remark about some other object or characteristic
belonging to the user; and the third is a catch-all pattern just in case the
other two fail.

<P>

<P><H2>Generated Symbols</H2>




<P>The procedures <CODE>addrule</CODE> and <CODE>addmemr</CODE> are very similar, since the
<CODE>rules</CODE> and <CODE>memr</CODE> properties are similar in format.

<P><PRE>to addrule :word :pattern :results
localmake &quot;propname gensym
pprop :word &quot;rules (sentence gprop :word &quot;rules list :pattern :propname)
pprop :word :propname :results
end

to addmemr :word :pattern :results
localmake &quot;propname gensym
pprop :word &quot;memr (sentence gprop :word &quot;memr list :pattern :propname)
pprop :word :propname :results
end
</PRE>

<P>Each of these procedures uses a local variable <CODE>propname</CODE> to
contain the name of the response property, a &quot;generated symbol&quot; or <EM>
gensym.</EM>  These are the words like <CODE>g3</CODE> in the example above.  Each
procedure carries out two <CODE>pprop</CODE> instructions.  The first appends a new
pattern and a new gensym to the previous value of the <CODE>rules</CODE> or <CODE>
memr</CODE> property; the second creates a new property with the gensym as its
name and the response (or memory) list as its value.  <CODE>Gensym</CODE> is
a Berkeley Logo library procedure.

<P><H2>Modification of List Structure</H2>



<P>Why are generated symbols needed in this program at all?  In the Lisp
version of Doctor, property lists are still used, but the entire collection
of rules is one big list, the value of the property <CODE>rules</CODE>.  It's as if
the Logo property list looked like this:

<P>

<P><PRE>[priority 2
 translation your
 rules
   [[# your # !a:familyp #b]
      [[Tell me more about your family.]
       [Who else in your family :b?]
       [Your :a?]
       [What else comes to mind when you think of your :a?]]
    [# your &amp;stuff]
      [[Your :stuff?]
       [Why do you say your :stuff?]
       [Does that suggest anything else which belongs to you?]
       [Is it important to you that your :stuff?]]
    [#]
      [newkey]]
 memr
   [[# your &amp;stuff]
      [[Earlier you said your :stuff.]
       [But your :stuff.]
       [Does that have anything to do with your statement
        about :stuff?]]]
]
</PRE>

<P>I chose not to use one big list of rules in the Logo version.  In Lisp
(and in Berkeley Logo, but not in the versions of Logo I had available
when writing the first edition), it's possible to change
one of the members of a list without recopying the rest of the list.
Without that capability, it's better to divide the rules into separate,
smaller lists, so that only a little recopying is needed to change one.

<P>Each pattern has several responses associated with it.  When the program
matches a particular pattern, it does not choose a response at random.
Instead, it rotates through the list of responses in order.  That is, it
uses the first response first, then the second, and so on until the end of
the list; if another response is needed, it starts over at the beginning of
the list.  This strict rotation is sometimes important because some of the
responses say things like &quot;I already told you that...&quot;

<P>The way the program keeps track of the rotation of the responses for a given
rule is that it actually changes the response list so that what used to be
the first response is moved to the end.  Thus, <CODE>dorule</CODE> contains the
instructions

<P><PRE>localmake &quot;print first gprop :keyword :rule
pprop :keyword :rule lput :print butfirst gprop :keyword :rule
</PRE>

<P>The first of these instructions extracts the first response from
the list of responses.  The second one replaces the list of responses with a
new list, in which the old first response is <CODE>lput</CODE> behind the remaining
ones.

<P>

<P>What if the rules were one big list?  To see what would be required, let's
look at a smaller list, in which it will be easier to follow what needs to
be changed.  Suppose some word's <CODE>rules</CODE> property had as its value this
list:

<P><PRE>[1 [A B C] 2 [D E F] 3 [G H I]]
</PRE>

<P>In this example, the numbers represent patterns, while the letters
represent responses.  Now suppose that the program finds a match for pattern
number <CODE>2</CODE>.  It should then issue the response <CODE>D</CODE>.  Then it
should rotate the three responses associated with pattern <CODE>2</CODE> so that
the new <CODE>rules</CODE> property is

<P><PRE>[1 [A B C] 2 [E F D] 3 [G H I]]
</PRE>

<P>The only way to do this in most versions of Logo is to construct a
new copy of the entire list.  Here is a way you could write such a program:

<P><PRE>to rotate :keyword :pattern
pprop :keyword &quot;rules (rotate1 :pattern gprop :keyword &quot;rules)
end

to rotate1 :pattern :rules
if emptyp :rules [output []]
if equalp :pattern first :rules
   [output sentence (list :pattern rotate2 first butfirst :rules)
                    (butfirst butfirst :rules)]
output sentence (list first :rules first butfirst :rules) ~
                (rotate1 :pattern butfirst butfirst :rules)
end

to rotate2 :list
output lput first :list butfirst :list
end
</PRE>

<P>You'd use this program with an instruction like

<P><PRE>rotate &quot;word 2
</PRE>

<P>where <CODE>2</CODE> represents the pattern in the example above.

<P>The trouble with this approach is that it's slow.  It does a lot of <CODE>
list</CODE> and <CODE>sentence</CODE> operations to reconstruct the modified list.
More importantly, the <EM>entire</EM> list must be copied, even though
only one rule is to be modified.

<P>
In Lisp, and in Berkeley Logo, there are primitive commands that can be used
to change the contents of a list without recopying the unchanged parts.  In
Berkeley Logo they are called <CODE>.setfirst</CODE> and <CODE>.setbutfirst</CODE>; using
them, we could write <CODE>rotate</CODE> this way:

<P><PRE>to rotate :keyword :pattern
rotate1 :pattern (gprop :keyword &quot;rules)
end

to rotate1 :pattern :rules
if emptyp :rules [stop]
ifelse equalp :pattern first :rules ~
       [.setfirst (butfirst :rules) (rotate2 first butfirst :rules)]
       [rotate1 :pattern butfirst butfirst :rules]
end
</PRE>

<P>(I'll leave <CODE>rotate2</CODE> the same as in the earlier version, for
now.)  This is a tricky sort of procedure.  Here's a trace of how it might
be used:

<P><PRE>rotate &quot;word 2
  rotate1 2 [1 [A B C] 2 [D E F] 3 [G H I]]
    rotate1 2 [2 [D E F] 3 [G H I]]
</PRE>

<P>In the lower-level invocation of <CODE>rotate1</CODE>, the <CODE>equalp</CODE>
test outputs <CODE>true</CODE>, so the <CODE>ifelse</CODE> instruction evaluates its second
input.  This is equivalent to the instruction

<P><PRE>.setfirst [[D E F] 3 [G H I]] (rotate2 [D E F])
</PRE>

<P>or

<P><PRE>.setfirst [[D E F] 3 [G H I]] [E F D]
</PRE>

<P>To understand what this means, you must realize that the primitive operation
<CODE>butfirst</CODE> does not make a <EM>copy</EM> of the butfirst of its input.
Instead, the list output by <CODE>butfirst</CODE> is actually part of the list that
is its input--they share the same cells in the computer's memory.
Therefore, to change something in the <CODE>butfirst</CODE> also changes the larger
list.  The <CODE>setfirst</CODE> instruction ends up changing the <CODE>rules</CODE>
property of the word <CODE>word</CODE> even though there is no explicit <CODE>
pprop</CODE> to change the property.

<P>If you're not a Lisp programmer, this probably seems like magic.  It
certainly violates some of the rules you've learned about the evaluation of
Logo instructions.  For example, if you actually typed the instruction

<P><PRE>.setfirst [[D E F] 3 [G H I]] [E F D]
</PRE>

<P>explicitly at top level, it would <EM>not</EM> change the property
list of <CODE>word</CODE>, because the list that <CODE>setfirst</CODE> modifies would <EM>
not</EM> be part of that property list, even though it has the same members.
It's only because <CODE>setfirst</CODE>'s input is derived from that property list
by a series of <CODE>butfirst</CODE> operations that they share the same memory.

<P>

<P>Do you find this confusing?  The original designers of Logo chose not to include
<CODE>.setfirst</CODE> in the language because it <EM>is</EM> hard to understand, and
because it can produce some very strange results if you're not careful with
it.  For example, consider these instructions:

<P><PRE>make &quot;c [x y]
.setfirst (butfirst :c) (:c)
</PRE>

<P>This <CODE>.setfirst</CODE> instruction will produce a <EM>
circular list</EM>, one that contains itself as a member.  If you try
to print <CODE>:c</CODE>, you'll see something like

<P><PRE>[x [x [x [x [x [x [x [x [x [x [x [x [x [x [x ...
</PRE>

<P>going on forever.

<P>Once we have these list modification tools, even the implicit recopying
done by <CODE>lput</CODE> can be avoided.  Here's a more efficient version of
<CODE>rotate1</CODE>, but it's really tricky to understand and it isn't a
technique that I recommend:

<P><PRE>
to rotate1 :pattern :rules
if emptyp :rules [stop]
ifelse equalp :pattern first :rules ~
       [rotate2 butfirst :rules]
       [rotate1 :pattern butfirst butfirst :rules]
end

to rotate2 :rulelist
localmake &quot;firstresponse first :rulelist
localmake &quot;restresponses butfirst :firstresponse
.setfirst :rulelist :restresponses
.setbutfirst :firstresponse []
while [not emptyp butfirst :restresponses] ~
      [make &quot;restresponses butfirst :restresponses]
.setbutfirst :restresponses :firstresponse
end
</PRE>

<P>In <CODE>rotate2</CODE>, the <CODE>.setfirst</CODE> instruction removes
the first response from the head of the list of responses; the two
<CODE>.setbutfirst</CODE> instructions &quot;splice&quot; that first response back
into the list at the end, following what used to be the last
response.

<P>
Leaving <CODE>.setfirst</CODE> out of Logo was a controversial decision.  Some
people take the position that, as a &quot;language for learners,&quot; Logo should
not include mechanisms for which we can't provide an easy-to-follow
metaphor; it's counterproductive for the language to encourage you to think
in terms of what's where in memory.  Other people refer to this idea
scornfully as &quot;protecting the user from himself,&quot; arguing that if a
mechanism is useful it should be provided even though it's error-prone.

<P>

<P>In any case, since Logo didn't have <CODE>.setfirst</CODE> and I didn't want the
<CODE>doctor</CODE> program to be slowed down by having to recopy the <CODE>rules</CODE>
property all the time, I decided to make each response list a separate
property, so that each response list can be modified independently of the
others.  That's the reason for the gensym property names: so that <CODE>
dorule</CODE> can rotate the responses for a particular pattern without disturbing
the responses for other patterns.  I could have changed this in the Berkeley
Logo version, but it didn't seem worthwhile; using names for the rules is a
little inelegant but doesn't hurt the program's efficiency.

<P><H2>Linguistic Structure</H2>

<P>Because it treats a sentence as simply a string of words, Eliza is limited
in its linguistic sophistication.  For example, the Doctor script has this
pattern associated with the keyword <CODE>I</CODE>:

<P><PRE>[# you are # !stuff:in [sad unhappy depressed sick] #]
</PRE>

<P>(Remember that the pattern is matched against the input sentence
after translation, so the words <CODE>you are</CODE> in the pattern really match a
sentence containing the words <CODE>I am</CODE>.)  The purpose of the pattern is to
match a sentence like

<P><PRE>I think I am really depressed because Susan doesn't like me.
</PRE>

<P>The response of the program might be

<P><PRE>I'm sorry to hear you are depressed.
</PRE>

<P>The <CODE>#</CODE> between <CODE>are</CODE> and <CODE>!stuff</CODE> in the pattern is meant to
catch adverbs like the word <CODE>really</CODE> in the example I just gave.  But it
could also &quot;absorb&quot; some more structurally important parts of a sentence:

<P><PRE>I am sure that I would be depressed if she left me.
</PRE>

<P>This sentence matches the pattern, but it doesn't really fit the
intent of the pattern.  The person who types this sentence is not saying &quot;I
am depressed&quot; at all.

<P>The trouble is that the string of words &quot;sure that I would be&quot; is not
equivalent to an adverb.  In fact, these words do not form a phrase at all.
The program is making a grammatical error by connecting the word <CODE>
depressed</CODE> with the word <CODE>am</CODE> as a predicate adjective.  To avoid such
errors, it's not good enough to have more and more detailed patterns to
match.  You can't anticipate every possible string of words by that
technique.  Instead, the program would have to impose a tree structure on
the sentence, sort of like what you did in diagraming sentences in
elementary school.  The true structure of this sentence is something like

<P><PRE>[[subject I] [predicate [[verb am] [nominative [[adjective sure]
   [adverb [clause [[connective that] [subject I] [verb [would be] ...
</PRE>

<P>and so on.  (Actually, I've just made up this structure to
illustrate the idea, and it's not very realistic.  I've tried too hard to
preserve the order of the words in the original sentence.  A more practical
structure would probably center on the verb in each clause, and have
subordinate slots for the subject, object, and so on.  A connective like
&quot;that&quot; might just be thrown away completely; the purpose served by such
words in spoken text would instead be filled by the very sublist
organization itself.)  People have in fact written several computer programs
that transform English sentences into a structured representation.  It's very
hard to do a perfect job, though, because of problems like homonyms: the
word &quot;like&quot; can be a verb (I like ice cream) or a preposition (I want to
be like my big brother).

<P><H2>Further Explorations</H2>

<P>There are three main directions in which you can explore the territory that
this project begins.  First, you can try to refine the existing Doctor
script, so that it does a better job within the same general framework.
Whenever you get an inappropriate response from <CODE>doctor</CODE>, see if you can
think of a new rule that would solve that case without messing up other
sentences.

<P>A second possibility would be to write an entirely new Eliza script, so that
instead of being a doctor the program can carry on some different sort of
conversation.  How about taking orders in a fast food restaurant?  Answering
questions from some data base about presidents or baseball players?

<P>The third direction would be to abandon Eliza and look into some of the
other approaches to understanding and generating English sentences that have
been developed.

<P>
<TABLE width="100%"><TR><TD><A HREF="../v2-toc2.html">(back to Table of Contents)</A>
<TD align="right"><A HREF="../v2ch8/v2ch8.html"><STRONG>BACK</STRONG></A>
chapter thread <A HREF="../v2ch10/v2ch10.html"><STRONG>NEXT</STRONG></A>
</TABLE>

<P><H2>Program Listing</H2>

<P>The procedures from the pattern matcher of Chapter 7 are included in
this program, but they are not listed again here.

<P><PRE>
to doctor
local [text sentence stuff a b c rules keywords memory]
make "memory []
print [Hello, I am the doctor. What can I do for you?]
print [Please end your remarks with an empty line.]
print []
loop
end

;; Controlling the conversation

to loop
make "text tokenize getstuff []
make "sentence getsentence :text
analyze :sentence :keywords
print []
loop
end

;; Reading and preparing the input

to getstuff :stuff
localmake "line readlist
if emptyp :line [output :stuff]
output getstuff sentence :stuff :line
end

to tokenize :text
output map.se [tokenword ? "] :text
end

to tokenword :word :out
if emptyp :word [output :out]
if memberp first :word [, " ] [output tokenword butfirst :word :out]
if memberp first :word [. ? ! |;|] [output sentence :out ".]
output tokenword butfirst :word word :out first :word
end

to getsentence :text
make "keywords []
output getsentence1 decapitalize :text []
end

to getsentence1 :text :out
if emptyp :text [output :out]
if equalp first :text ". ~
   [ifelse emptyp :keywords ~
           [output getsentence1 decapitalize butfirst :text []] [output :out]]
checkpriority first :text
output getsentence1 butfirst :text sentence :out translate first :text
end

to decapitalize :text
if emptyp :text [output []]
output fput lowercase first :text butfirst :text
end

to checkpriority :word
localmake "priority gprop :word "priority
if emptyp :priority [stop]
if emptyp :keywords [make "keywords ( list :word ) stop]
ifelse :priority > ( gprop first :keywords "priority ) ~
       [make "keywords fput :word :keywords] ~
       [make "keywords lput :word :keywords]
end

to translate :word
localmake "new gprop :word "translation
output ifelse emptyp :new [:word] [:new]
end

;; Choosing the rule and replying

to analyze :sentence :keywords
local [rules keyword]
if emptyp :keywords [norules stop]
make "keyword first :keywords
make "rules gprop :keyword "rules
if wordp first :rules ~
   [make "keyword first :rules make "rules gprop :keyword "rules]
checkrules :keyword :rules
end

to checkrules :keyword :rules
if not match first :rules :sentence ~
   [checkrules :keyword butfirst butfirst :rules stop]
dorule first butfirst :rules
end

to dorule :rule
localmake "print first gprop :keyword :rule
pprop :keyword :rule lput :print butfirst gprop :keyword :rule
if equalp :print "newkey [analyze :sentence butfirst :keywords stop]
if wordp :print [checkrules :print gprop :print "rules stop]
if equalp first :print "pre ~
   [analyze reconstruct first butfirst :print butfirst butfirst :print stop]
print capitalize reconstruct :print
memory :keyword :sentence
end

to reconstruct :sentence
if emptyp :sentence [output []]
if not equalp ": first first :sentence ~
   [output fput first :sentence reconstruct butfirst :sentence]
output sentence reword first :sentence reconstruct butfirst :sentence
end

to reword :word
if memberp last :word [. ? ,] [output addpunct reword butlast :word last :word]
output thing butfirst :word
end

to addpunct :stuff :char
if wordp :stuff [output word :stuff :char]
if emptyp :stuff [output :char]
output sentence butlast :stuff word last :stuff :char
end

to capitalize :text
if emptyp :text [output []]
output fput (word uppercase first first :text butfirst first :text) butfirst :text
end

to memory :keyword :sentence
local [rules rule name]
make "rules gprop :keyword "memr
if emptyp :rules [stop]
if not match first :rules :sentence [stop]
make "name last :rules
make "rules gprop :keyword :name
make "rule first :rules
pprop :keyword :name lput :rule butfirst :rules
make "memory fput reconstruct :sentence :memory
end

to norules
ifelse :memflag [usememory] [lastresort]
make "memflag not :memflag
end

to lastresort
print first :lastresort
make "lastresort lput first :lastresort butfirst :lastresort
end

to usememory
if emptyp :memory [lastresort stop]
print capitalize first :memory
make "memory butfirst :memory
end

;; Predicates for patterns

to beliefp :word
output not emptyp gprop :word "belief
end

to familyp :word
output not emptyp gprop :word "family
end

;; Procedures for adding to the script

to addrule :word :pattern :results
localmake "propname gensym
pprop :word "rules (sentence gprop :word "rules list :pattern :propname)
pprop :word :propname :results
end

to addmemr :word :pattern :results
localmake "propname gensym
pprop :word "memr (sentence gprop :word "memr list :pattern :propname)
pprop :word :propname :results
end

;; data

make "gensym.number 80

make "lastresort [[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?]]

make "memflag "false

pprop "alike "priority 10
pprop "alike "rules [dit]

pprop "always "priority 1
pprop "always "rules [[#] g69]
pprop "always "g69 [[Can you think of a specific example?] [When?]
                    [What incident are you thinking of?]
                    [Really, always?] [What if this never happened?]]

pprop "am "priority 0
pprop "am "translation "are
pprop "am "rules [[# are you #stuff] g18 [#] g19]
pprop "am "g18 [[Do you believe you are :stuff?] [Would you want to be :stuff?]
                [You wish I would tell you you are :stuff.]
                [What would it mean if you were :stuff?] how]
pprop "am "g19 [[Why do you say "am"?] [I don't understand that.]]

pprop "are "priority 0
pprop "are "rules [[#a there are #b you #c] g20 [# there are &stuff] g21
                   [# are I #stuff] g22 [are #] g23 [# are #stuff] g24]
pprop "are "g20 [[pre [:a there are :b] are]]
pprop "are "g21 [[What makes you think there are :stuff?]
                 [Do you usually consider :stuff?]
                 [Do you wish there were :stuff?]]
pprop "are "g22 [[Why are you interested in whether I am :stuff or not?]
                 [Would you prefer if I weren't :stuff?]
                 [Perhaps I am :stuff in your fantasies.]
                 [Do you sometimes think I am :stuff?] how]
pprop "are "g23 [how]
pprop "are "g24 [[Did you think they might not be :stuff?]
                 [Would you like it if they were not :stuff?]
                 [What if they were not :stuff?] [Possibly they are :stuff.]]

pprop "ask "priority 0
pprop "ask "rules [[# you ask #] g77 [# you ! asking #] g78 [# I #] g79 [#] g80]
pprop "ask "g77 [how]
pprop "ask "g78 [how]
pprop "ask "g79 [you]
pprop "ask "g80 [newkey]

pprop "because "priority 0
pprop "because "rules [[#] g64]
pprop "because "g64 [[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?]
                     [You're not concealing anything from me, are you?]]

pprop "believe "belief "true

pprop "bet "belief "true

pprop "brother "family "true

pprop "can "priority 0
pprop "can "rules [[# can I #stuff] g58 [# can you #stuff] g59 [#] g60]
pprop "can "g58 [[You believe I can :stuff, don't you?] how
                 [You want me to be able to :stuff.]
                 [Perhaps you would like to be able to :stuff yourself.]]
pprop "can "g59 [[Whether or not you can :stuff depends more on you than on me.]
                 [Do you want to be able to :stuff?]
                 [Perhaps you don't want to :stuff.] how]
pprop "can "g60 [how newkey]

pprop "cant "translation "can't

pprop "certainly "priority 0
pprop "certainly "rules [yes]

pprop "children "family "true

pprop "computer "priority 50
pprop "computer "rules [[#] g17]
pprop "computer "g17 [[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?]]

pprop "computers "priority 50
pprop "computers "rules [computer]

pprop "dad "translation "father
pprop "dad "family "true

pprop "daddy "translation "father
pprop "daddy "family "true

pprop "deutsch "priority 0
pprop "deutsch "rules [[#] g15]
pprop "deutsch "g15 [[I'm sorry, I speak only English.]]

pprop "dit "rules [[#] g72]
pprop "dit "g72 [[In what way?] [What resemblance do you see?]
                 [What does that similarity suggest to you?]
                 [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]

pprop "dont "translation "don't

pprop "dream "priority 3
pprop "dream "rules [[#] g9]
pprop "dream "g9 [[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?]
                  [Do you ever wish you could flee from reality?] newkey]

pprop "dreamed "priority 4
pprop "dreamed "rules [[# you dreamed #stuff] g7 [#] g8]
pprop "dreamed "g7 [[Really :stuff?]
                    [Have you ever fantasied :stuff while you were awake?]
                    [Have you dreamed :stuff before?] dream newkey]
pprop "dreamed "g8 [dream newkey]

pprop "dreams "translation "dream
pprop "dreams "priority 3
pprop "dreams "rules [dream]

pprop "dreamt "translation "dreamed
pprop "dreamt "priority 4
pprop "dreamt "rules [dreamed]

pprop "espanol "priority 0
pprop "espanol "rules [deutsch]

pprop "everybody "priority 2
pprop "everybody "rules [everyone]

pprop "everyone "priority 2
pprop "everyone "rules [[# !a:in [everyone everybody nobody noone] #] g68]
pprop "everyone "g68 [[Really, :a?] [Surely not :a.]
                      [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 person in mind, don't you?]
                      [Who do you think you're talking about?]
                      [I suspect you're exaggerating a little.]]

pprop "father "family "true

pprop "feel "belief "true

pprop "francais "priority 0
pprop "francais "rules [deutsch]

pprop "hello "priority 0
pprop "hello "rules [[#] g16]
pprop "hello "g16 [[How do you do. Please state your problem.]]

pprop "how "priority 0
pprop "how "rules [[#] g63]
pprop "how "g63 [[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 questions before?]
                 [Have you asked anyone else?]]

pprop "husband "family "true

pprop "i "priority 0
pprop "i "translation "you
pprop "i "rules [[# you !:in [want need] #stuff] g32
                 [# you are # !stuff:in [sad unhappy depressed sick] #] g33
                 [# you are # !stuff:in [happy elated glad better] #] g34
                 [# you was #] g35 [# you !:beliefp you #stuff] g36
                 [# you # !:beliefp # i #] g37 [# you are #stuff] g38
                 [# you !:in [can't cannot] #stuff] g39
                 [# you don't #stuff] g40 [# you feel #stuff] g41
                 [# you #stuff i #] g42 [#stuff] g43]
pprop "i "g32 [[What would it mean to you if you got :stuff?]
               [Why Do you want :stuff?] [Suppose you got :stuff soon.]
               [What if you never got :stuff?]
               [What would getting :stuff mean to you?] [You really want :stuff.]
               [I suspect you really don't want :stuff.]]
pprop "i "g33 [[I'm sorry to hear you are :stuff.]
               [Do you think coming here will help you not to be :stuff?]
               [I'm sure it's not pleasant to be :stuff.]
               [Can you explain what made you :stuff?] [Please go on.]]
pprop "i "g34 [[How have I helped you to be :stuff?]
               [Has your treatment made you :stuff?]
               [What makes you :stuff just now?]
               [Can you explain why you are suddenly :stuff?]
               [Are you sure?] [What do you mean by :stuff?]]
pprop "i "g35 [was]
pprop "i "g36 [[Do you really think so?] [But you are not sure you :stuff.]
               [Do you really doubt you :stuff?]]
pprop "i "g37 [you]
pprop "i "g38 [[Is it because you are :stuff that you came to me?]
               [How long have you been :stuff?]
               [Do you believe it normal to be :stuff?]
               [Do you enjoy being :stuff?]]
pprop "i "g39 [[How do you know you can't :stuff?] [Have you tried?]
               [Perhaps you could :stuff now.]
               [Do you really want to be able to :stuff?]]
pprop "i "g40 [[Don't you really :stuff?] [Why don't you :stuff?]
               [Do you wish to be able to :stuff?] [Does that trouble you?]]

pprop "i "g41 [[Tell me more about such feelings.] [Do you often feel :stuff?]
               [Do you enjoy feeling :stuff?]
               [Of what does feeling :stuff remind you?]]
pprop "i "g42 [[Perhaps in your fantasy we :stuff each other.]
               [Do you wish to :stuff me?] [You seem to need to :stuff me.]
               [Do you :stuff anyone else?]]
pprop "i "g43 [[You say :stuff.] [Can you elaborate on that?]
               [Do you say :stuff for some special reason?]
               [That's quite interesting.]]

pprop "i'm "priority 0
pprop "i'm "translation "you're
pprop "i'm "rules [[# you're #stuff] g31]
pprop "i'm "g31 [[pre [you are :stuff] I]]

pprop "if "priority 3
pprop "if "rules [[#a if #b had #c] g5 [# if #stuff] g6]
pprop "if "g5 [[pre [:a if :b might have :c] if]]
pprop "if "g6 [[Do you think it's likely that :stuff?] [Do you wish that :stuff?]
               [What do you think about :stuff?]]

pprop "is "priority 0
pprop "is "rules [[&a is &b] g61 [#] g62]
pprop "is "g61 [[Suppose :a were not :b.] [Perhaps :a really is :b.]
                [Tell me more about :a.]]
pprop "is "g62 [newkey]

pprop "italiano "priority 0
pprop "italiano "rules [deutsch]

pprop "like "priority 10
pprop "like "rules [[# !:in [am is are was] # like #] g70 [#] g71]
pprop "like "g70 [dit]
pprop "like "g71 [newkey]

pprop "machine "priority 50
pprop "machine "rules [computer]

pprop "machines "priority 50
pprop "machines "rules [computer]

pprop "maybe "priority 0
pprop "maybe "rules [perhaps]

pprop "me "translation "you

pprop "mom "translation "mother
pprop "mom "family "true

pprop "mommy "translation "mother
pprop "mommy "family "true

pprop "mother "family "true

pprop "my "priority 2
pprop "my "translation "your
pprop "my "rules [[# your # !a:familyp #b] g55 [# your &stuff] g56 [#] g57]
pprop "my "g55 [[Tell me more about your family.] [Who else in your family :b?]
                [Your :a?] [What else comes to mind when you think of your :a?]]
pprop "my "g56 [[Your :stuff?] [Why do you say your :stuff?]
                [Does that suggest anything else which belongs to you?]
                [Is it important to you that your :stuff?]]
pprop "my "g57 [newkey]
pprop "my "memr [[# your &stuff] g12]
pprop "my "g12 [[Earlier you said your :stuff.] [But your :stuff.]
                [Does that have anything to do with your statement about :stuff?]]

pprop "myself "translation "yourself

pprop "name "priority 15
pprop "name "rules [[#] g14]
pprop "name "g14 [[I am not interested in names.]
                  [I've told you before I don't care about names\;
                   please continue.]]

pprop "no "priority 0
pprop "no "rules [[no] g53 [#] g54]
pprop "no "g53 [xxyyzz [pre [x no] no]]
pprop "no "g54 [[Are you saying "no" just to be negative?]
                [You are being a bit negative.] [Why not?] [Why "no"?] newkey]

pprop "nobody "priority 2
pprop "nobody "rules [everyone]

pprop "noone "priority 2
pprop "noone "rules [everyone]

pprop "perhaps "priority 0
pprop "perhaps "rules [[#] g13]
pprop "perhaps "g13 [[You don't seem quite certain.] [Why the uncertain tone?]
                     [Can't you be more positive?] [You aren't sure.]
                     [Don't you know?]]

pprop "problem "priority 5
pprop "problem "rules [[#a !b:in [is are] your !c:in [problem problems] #] g73
                       [# your !a:in [problem problems] !b:in [is are] #c] g74
                       [#] g75]
pprop "problem "g73 [[:a :b your :c.] [Are you sure :a :b your :c?]
                     [Perhaps :a :b not your real :c.]
                     [You think you have problems?]
                     [Do you often think about :a?]]
pprop "problem "g74 [[Your :a :b :c?] [Are you sure your :a :b :c?]
                     [Perhaps your real :a :b not :c.]
                     [You think you have problems?]]
pprop "problem "g75 [[Please continue, this may be interesting.]
                     [Have you any other problems you wish to discuss?]
                     [Perhaps you'd rather change the subject.]
                     [You seem a bit uneasy.] newkey]
pprop "problem "memr [[#stuff is your problem #] g76]
pprop "problem "g76 [[Earlier you mentioned :stuff.]
                     [Let's talk further about :stuff.]
                     [Tell me more about :stuff.]
                     [You haven't mentioned :stuff for a while.]]

pprop "problems "priority 5
pprop "problems "rules [problem]

pprop "remember "priority 5
pprop "remember "rules [[# you remember #stuff] g2
                        [# do I remember #stuff] g3 [#] g4]
pprop "remember "g2 [[Do you often think of :stuff?]
                     [Does thinking of :stuff bring anything else to mind?]
                     [What else do you remember?]
                     [Why do you remember :stuff just now?]
                     [What in the present situation reminds you of :stuff?]]
pprop "remember "g3 [[Did you think I would forget :stuff?]
                     [Why do you think I should recall :stuff now?]
                     [What about :stuff?] what [You mentioned :stuff.]]
pprop "remember "g4 [newkey]

pprop "same "priority 10
pprop "same "rules [dit]

pprop "sister "family "true

pprop "sorry "priority 0
pprop "sorry "rules [[#] g1]
pprop "sorry "g1 [[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.]]

pprop "svenska "priority 0
pprop "svenska "rules [deutsch]

pprop "think "belief "true

pprop "was "priority 2
pprop "was "rules [[# was you #stuff] g26 [# you was #stuff] g27
                   [# was I #stuff] g28 [#] g29]
pprop "was "g26 [[What if you were :stuff?] [Do you think you were :stuff?]
                 [Were you :stuff?] [What would it mean if you were :stuff?]
                 [What does " :stuff " suggest to you?] how]
pprop "was "g27 [[Were you really?] [Why do you tell me you were :stuff now?]
                 [Perhaps I already knew you were :stuff.]]
pprop "was "g28 [[Would you like to believe I was :stuff?]
                 [What suggests that I was :stuff?] [What do you think?]
                 [Perhaps I was :stuff.] [What if I had been :stuff?]]
pprop "was "g29 [newkey]

pprop "we "translation "you
pprop "we "priority 0
pprop "we "rules [I]

pprop "were "priority 0
pprop "were "translation "was
pprop "were "rules [was]

pprop "what "priority 0
pprop "what "rules [[!:in [what where] #] g10 [# !a:in [what where] #b] g11]
pprop "what "g10 [how]
pprop "what "g11 [[Tell me about :a :b.] [:a :b?]
                  [Do you want me to tell you :a :b?]
                  [Really.] [I see.] newkey]

pprop "where "priority 0
pprop "where "rules [how]

pprop "why "priority 0
pprop "why "rules [[# why don't I #stuff] g65
                   [# why can't you #stuff] g66 [#] g67]
pprop "why "g65 [[Do you believe I don't :stuff?]
                 [Perhaps I will :stuff in good time.]
                 [Should you :stuff yourself?] [You want me to :stuff?] how]
pprop "why "g66 [[Do you think you should be able to :stuff?]
                 [Do you want to be able to :stuff?]
                 [Do you believe this will help you to :stuff?]
                 [Have you any idea why you can't :stuff?] how]
pprop "why "g67 [[Why indeed?] [Why "why"?] [Why not?] how newkey]

pprop "wife "family "true

pprop "wish "belief "true

pprop "wont "translation "won't

pprop "xxyyzz "priority 0
pprop "xxyyzz "rules [[#] g50]
pprop "xxyyzz "g50 [[You're being somewhat short with me.]
                    [You don't seem very talkative today.]
                    [Perhaps you'd rather talk about something else.]
                    [Are you using monosyllables for some reason?] newkey]

pprop "yes "priority 0
pprop "yes "rules [[yes] g51 [#] g52]
pprop "yes "g51 [xxyyzz [pre [x yes] yes]]
pprop "yes "g52 [[You seem quite positive.] [You are sure.] [I see.]
                 [I understand.] newkey]

pprop "you "priority 0
pprop "you "translation "I
pprop "you "rules [[# I remind you of #] g44 [# I are # you #] g45
                   [# I # are #stuff] g46 [# I #stuff you] g47
                   [# I &stuff] g48 [#] g49]
pprop "you "g44 [dit]
pprop "you "g45 [newkey]
pprop "you "g46 [[What makes you think I am :stuff?]
                 [Does it please you to believe I am :stuff?]
                 [Perhaps you would like to be :stuff.]
                 [Do you sometimes wish you were :stuff?]]
pprop "you "g47 [[Why do you think I :stuff you?]
                 [You like to think I :stuff you, don't you?]
                 [What makes you think I :stuff you?] [Really, I :stuff you?]
                 [Do you wish to believe I :stuff you?]
                 [Suppose I did :stuff you. what would that mean?]
                 [Does someone else believe I :stuff you?]]
pprop "you "g48 [[We were discussing you, not me.] [Oh, I :stuff?]
                 [Is this really relevant to your problem?] [Perhaps I do :stuff.]
                 [Are you glad to know I :stuff?] [Do you :stuff?]
                 [What are your feelings now?]]
pprop "you "g49 [newkey]

pprop "you're "priority 0
pprop "you're "translation "I'm
pprop "you're "rules [[# I'm #stuff] g30]
pprop "you're "g30 [[pre [I are :stuff] you]]

pprop "your "priority 0
pprop "your "translation "my
pprop "your "rules [[# my #stuff] g25]
pprop "your "g25 [[Why are you concerned over my :stuff?]
                  [What about your own :stuff?]
                  [Are you worried about someone else's :stuff?]
                  [Really, my :stuff?]]

pprop "yourself "translation "myself
</PRE>

<P><A HREF="../v2-toc2.html">(back to Table of Contents)</A>
<P><A HREF="../v2ch8/v2ch8.html"><STRONG>BACK</STRONG></A>
chapter thread <A HREF="../v2ch10/v2ch10.html"><STRONG>NEXT</STRONG></A>

<P>
<ADDRESS>
<A HREF="../index.html">Brian Harvey</A>, 
<CODE>bh@cs.berkeley.edu</CODE>
</ADDRESS>
</BODY>
</HTML>