Path: utzoo!attcan!uunet!husc6!mailrus!uwmcsd1!ig!agate!ucbvax!POLYA.STANFORD.EDU!restivo From: restivo@POLYA.STANFORD.EDU (Chuck Restivo) Newsgroups: comp.lang.prolog Subject: PROLOG DIGEST V6 #28 Message-ID: <8807041642.AA11243@polya.Stanford.EDU> Date: 4 Jul 88 16:42:22 GMT Sender: daemon@ucbvax.BERKELEY.EDU Organization: The Internet Lines: 1266 Date: Thursday, 5 May 1988 0:1:43-PST From: Chuck Restivo (The Moderator) Reply-to: PROLOG@POLYA.STANFORD.EDU> US-Mail: P.O. Box 4584, Stanford CA 94305 Subject: PROLOG Digest V6 #28 To: PROLOG@POLYA.STANFORD.EDU PROLOG Digest Friday, 1 Apr 1988 Volume 6 : Issue 28 Today's Topics: Query - Parallel Compilers, Implementation - end_of_file & Strings & Eliza ---------------------------------------------------------------------------------------------------------------------------- Date: 23 Mar 88 14:26:19 GMT From: mcvax!dutrun!dutesta!ignacio@uunet.uu.net (Ignacio GARCIA ALVES) Subject: Parallel prolog compilers and interpreters We are interested in PARALLEL PROLOG COMPILERS AND INTERPRETERS, like for example PARLOG and CONCURRENT PROLOG. But now the problem: WHERE CAN WE FIND THEM? So we would like to hear the answers to the following questions: - where can we order them? - what are the prices? - who has already such a compiler or interpreter to hear their experience? - what are the hardware and software requirements? We do have the following hardware: - VAX, Berkeley Unix 4.1 - RT, AIX - PC All information is welcome either by post or email! POST: Ignacio GARCIA ALVES & Mark KORSLOOT Delft University of Technology Faculty of Electrical Engineering Section Computer Architecture Mekelweg 4 2628 CD DELFT THE NETHERLANDS EMAIL: !mcvax!dutrun!dutesta!ignacio.uucp or !mcvax!dutrun!dutesta!mark.uucp ----------------------------- Date: 24 Mar 88 03:06:23 GMT From: quintus!ok@unix.sri.com (Richard A. O'Keefe) Subject: behavior of read/get0 at end_of_file I just grepped through the UNIX [UNIX is a trademark of AT&T] manuals, and all I could find was the function feof(Stream). None of the UNIX utilities I am familiar with uses "eof" to signify end of file. Franz Lisp does something interesting: (ratom [Port [Eof]]) (read [Port [Eof]]) (readc [Port [Eof]]) return the Eof argument (which defaults to nil) when you read the end of the file, so you can get whatever takes your fancy. > so i think we could maybe abandon the end_of_file notation of Quintus (sorry > for you Richard, a compatibility switch could very easily turn it back anyway), But it ***ISN'T*** a Quintus notation! This is the notation used by DEC-10 Prolog EMAS Prolog C Prolog Quintus Prolog Stony Brook Prolog ALS Prolog Expert Systems International Prolog-2 AAIS Prolog (in "Edinburgh" mode only) and doubtless many others. end_of_file IS the "de facto" standard. Poterie's suggestions are good ones, but in order to overthrow the de facto standard, they would have to be MUCH MUCH better, and they aren't. > but it is not an important point as the aim would be to discipline one's > programming style by systematically using the test form: > eof(Term) > and never ever explicit the EOF term itself. Portability is great. Beware. While Quintus Prolog offers the library predicate is_endfile(?EndMarker) there are other Prolog systems, such as AAIS Prolog, where there is a predicate with a similar name which takes a Stream argument: is_eof(+Stream) in AAIS Prolog means "is it the case that Stream is positioned at its end?". Yes, portability is great, but would it not be more just to reward those people (such as SICS, Saumya Debray, ALS, and others) who have tried to provide it, by standardising their solution? > As a side effect, close/1 is > not strictly necessary anymore as the following sequence does the job: > eof(EOF), put(EOF) Um, what about INPUT streams? And there is another reason for wanting close/1: it will close a stream which is not the current output stream. ------------------------------ Date: 26 Mar 88 05:25:23 GMT From: quintus!ok@unix.sri.com (Richard A. O'Keefe) Subject: End-of-file handling In article <2403@zyx.UUCP>, bd@zyx.UUCP (Bjorn Danielsson) writes: > I can't understand why there should be a "strait-jacket solution" to this > kind of problem. Why not put in enough flexibility to allow for the most > obvious cases? In Z-Prolog, (which was never designed to be Edinburgh > compatible) the "read" predicate can handle end-of-file in three different > ways, depending on an optional argument: > (1) signal an error, > (2) fail, > (3) return a value supplied by the programmer. > Oh, __HOW__ I wish you were were handling the I/O part of BSI substandard! That's EXACTLY the right sort of attitude for a standard designer. I think that there are sound technical reasons for regarding fail-at-end as a straight-out blunder, and I have used PL/I and Fortran enough to convince me that end-of-file is NOT an error and shouldn't be treated like one. (Reading *past* end-of-file is another matter, which is one of the things I have against fail-at-end.) But a more powerful operation, which is not perceptibly harder to implement, which will let me synthesise the operation I want, and let BIM synthesize the operation they want, that's exactly the right thing to do in the standard. Oddly enough, I had intended to mention Algol 68. Files in Algol 68 are rather interesting. One of the things you can do is say FILE example file; ... logical file ended OF example file := (FILE file parameter) BOOL: ( # return FALSE to get default action (e.g. error report) # # return TRUE to say "corrected, please try again" # # or jump out # ); Interlisp does the same sort of thing: (WHENCLOSE file 'EOF (FUNCTION (LAMBDA (file) (* action *)) )) So even if we decide to feed this goose instead of killing it, there are at least three options: 1. specify in each call what action to take on end of file read(Stream, Term, EofAction) -- This affects 'read' only. 2. specify per stream what action to take on end of file eof_action(Stream, EofAction) -- This could affect any form of input. 3. allow both. One of the constraints which is of interest to Quintus is that whatever the end of file action is, it has to make sense if the stream was being used by C or Lisp code. The nice thing about sticking with -1 as the character end-of-file mark and adopting option 3 is that it does this. Note though that as I mentioned in my previous message, just because the host operating system indicates an end-of-file condition doesn't mean that it isn't possible to read any more from the file. (UNIX fans will know about 'tail -f' and why it is useful...) The standard should, I said in '84, include a predicate for testing whether the stream is really ended. If the stream is not really ended, it should be possible to resume reading. ------------------------------ Date: 22 Mar 88 16:55:39 GMT From: mcvax!unido!ecrcvax!bruno@uunet.uu.net (Bruno Poterie) Subject: behavior of read/get0 at end_of_file I do not think that having read/1 returning an atom on EOF is a bad thing. If you take as an example certain UN*X tools, they read their input from a file (typically stdin) until finding a line composed of a single dot. So it is perfectly legal to submit a file which contains a dot-line in the middle if you want only the first part to be feeded to the tool. Same thing for Prolog, if you have a huge file full of various facts but want only say the first 100 to be used as input in your test program, then simply add the EOF mark before line 101. I would then prefer to have it as a directive: ... :- eof. so that it is not a bare fact but actually a command to the consulting tool that it have to EOF this input source. It is then coherent with other directives like: ... :- [file3,file4]. ... which actually order the consulting tool to insert at this point the content of the named files. I believe that the notation "eof" is quite standard in the UN*X system and already in some Prolog, including as a test predicate for this very term: ... read(Term), eof(Term) -> ... so i think we could maybe abandon the end_of_file notation of Quintus (sorry for you Richard, a compatibility switch could very easily turn it back anyway), but it is not an important point as the aim would be to discipline one's programming style by systematically using the test form: eof(Term) and never ever explicit the EOF term itself. Portability is great. Now for the get0/1 [or get_char/1/2]: having it returning an otherwise impossible value, say an atom as suggested by Micha, is ok if the returned thing is an integer representing the ascii code [or ebcdic] of the character. using the same term as the one returned by read/1, and consequently the same test predicate as only mechanism to check for EOF, would greatly improve the compactness and consistency of the i/o system. As a side effect, close/1 is not strictly necessary anymore as the following sequence does the job: eof(EOF), put(EOF) Because obviously put/1 must handle the same term in the same way (I am afraid that outputing CHAR modulo 256 would not work in this case). I nevertheless believe that EOF == -1 is a clearer convention, returning an object of the same type but out of the normal range of normal input, and is already the UN*X convention. It would not force put/1 to accept it as EOF character, as it would be outputed as: -1 modulo 256 (or 512) == 255. Passing -1 to UN*X putchar() does not generate an EOF! Ok, enough delirium tremens for today. My main point is: the character i/o should provide a very low level facility, with no hypothesis about the use which could be made of them. Using read(Term) and eof(Term) provides an uniform, simple, elegant and portable mean of performing i/o at Prolog term level. Using get0/1 implies you are interested in the real bits contained in your input support, so you want to control it at a low level. Returning the -1 value is portable and low-level, because independant of ascii or any other character set. Alternatively, returning eof and using the same eof(Char) test predicate would be again low-level, portable, and free of any supposed semantic. More important, most of prolog input loops may be adapted with this scheme at low cost. Failing at EOF, however, would mean full rewriting of those applications and system libraries. ------------------------------ Date: 22 Mar 88 10:29:39 GMT From: mcvax!prlb2!kulcs!bimandre@uunet.uu.net (Andre Marien) Subject: end_of_file treatment in prolog Sorry for being inaccurate: we should have taken the trouble of explaining what BIMprolog's predicate eof/0 does and it is different from what Richard's at_eof/0 does: eof/0 succeeds only after a read/1 or get0/1 has failed because end of file was encountered so, let me change buggy_read into: non_buggy_read(Term) :- bim_read(Term), ! . non_buggy_read(end_of_file) :- eof . actually, eof/0 is written using a predicate in_status/1 which unifies its argument with an integer indicating the 'status' of the input stream > end-failure approach, despite having asked Bart Demoen at the Prolog > Benchmarking Workshop for enlightenment on this point. Again, this Perhaps you asked Andre' Marien, but not Bart Demoen: he was not at the Prolog Benchmarking Workshop Now about the transformation of s1: a -> s2. s1: b -> s1. s1: $ -> accept. to a prolog procedure; 1. the textbook uses an explicit end of file marker, so it is clear that the behavior of Cprolog's get0/1 suites better in this transformation; still, I would rather represent a state by a predicate with arity 0 and let it get its character itself: s1 :- get0(X) , (X = a , s2 ; X = b , s1 ; X = -1 , true ) . this of course with get0/1 behaving like in Cprolog 2. Now, do it with BIMprolog's get0/1, I will call it BIMget0 so that you will always know which behavior to expect s1 :- BIMget0(X) , (X = a , s2 ; X = b , s1 ) . s1 :- eof . this doesn't look very nice, but a. I would expect that in a parser, in most states eof indicates an error, so that error recovery must be done, which is very easy (in prolog) by backtracking to the appropriate level (state) in the parser; so in most states, there would be no clause like s1 :- eof. then, on eof, s1 fails as it does on input characters different from a or b see Pascal : an endpoint is the endmarker, and occurrence of eof during tokenizing means an error see Prolog : every clause must have an endpoint; eof could only occur after a clause b. prolog (at least BIMprolog) is used for other things than writing tokenizers - actually, tokenizers should not be written at all: they should be generated - and in an administrative application written in prolog, using BIMread or BIMget, the programmer can feel at ease: after a successful BIMread or BIMget, he has got data; it is in a way impossible for him to forget testing for eof, because otherwise the execution of the program would never have gone so far 3. A more general approach can be used, using a state transition table. The previous program can be derived from this by partial evaluation. In the triangle puzzle problem discussion, R.O'K argued that this approach is more general. step_state(accept) :- ! . step_state(From) :- BIMget0(X), !, From : X => New, step_state(New) . step_state(From) :- BIMeof, From : '$' => New, step_state(New) . s1: a => s2. s1: b => s1. s1: '$' => accept. If eof is to be treated as an error, remove the last clause of step_state/1. Eof is handled in one place, in Cprolog one should add : transit(si,-1,error) . for all states, or write : state(From) :- get0(X), noteof(X), !, transit(From,X,New), state(New) . noteof(-1) :- !, fail . noteof(_) . To sum up: there is an appropriate level of testing for eof: BIMread and BIMget (and BSIread and BSIget for that matter, to answer one of Richard's questions and worries) allow you to do this testing of eof at these levels only, while read and get0 of Cprolog force you to test it after every input operation ------------------------------ Date: 23 Mar 88 09:54:10 GMT From: quintus!ok@unix.sri.com (Richard A. O'Keefe) Subject: behavior of read/get0 at end_of_file In article <518@ecrcvax.UUCP>, micha@ecrcvax.UUCP (Micha Meier) writes: > By the way, get0/1 does *not* exist in BSI, it uses get_char/1 instead, > and its argument is a character, i.e. a string of length 1. > This means that the type 'character' is inferred from > the type 'string' (and not the other way round like in C). > Does anybody out there know what advantages this can bring? > It is independent on the character <-> integer encoding, > but this only because explicit conversion predicates have > to be called all the time. I find it extremely odd to call a string of length one a character. It's like calling a list of integers which contains one element an integer. Do we call an array with one element a scalar? I haven't commented on the BSI's get_char/1 before because for once they have given a new operation a new name. There are two problems with it. A minor problem is that the result being a string, they can't represent end of file with an additional character, so the fail-at-end approach is hard to avoid. (Not impossible.) There is an efficiency problem: something which returns an integer or a character constant can just return a single tagged item, but something which returns a string either has to construct a new string every time, or else cache the strings somehow. For example, Interlisp has a function which returns you the next character in the current input stream, represented as an atom with one character in its name. (Well, almost: characters `0`..`9` are represented by integers 0..9.) This was quite attractive on a DEC-20, where you could just compute a table of 128 atoms once and for all. It wasn't too bad on VAXen either, where the table had to have 256 elements. But it because rather more clumsy on the D machines, which have a 16-bit character set. (Can you say "Kanji"? I knew you could.) So the alternatives I can see at the moment are o construct a new string every time. o precompute 2^16 strings. o cache 2^8 strings, and construct a new string every time for Kanji and other non-Latin alphabets. o not support Kanji or other non-Latin alphabets at all. (Can you say "Cyrillic"? How about "Devanagari"? You may need the assistance of a good dictionary; I used to mispronounce "Devanagari", and probably still do.) I wrote that > >For example, the arcs > > s1: a -> s2. > > s1: b -> s1. > > s1: $ -> accept. > >would be coded like this: > > s1(0'a) :- get0(Next), s2(Next). > > s1(0'b) :- get0(Next), s1(Next). > > s1(- 1) :- true. Meier says that > In his tutorial to the SLP '87 Richard has taken another > representation of a finite automaton which is more appropriate: > s1 :- > get0(Char), > s1(Char). > > s1(0'a) :- > s2. > s1(0'b) :- > s1. > s1(-1) :- > accept. There wasn't time to go into this in detail in the tutorial, but it should be obvious that the first approach is more general: in particular it can handle transitions where (perhaps because of context) no input is consumed, and it can handle lookahead. > Such representation can > be more easily converted to the BSI's variant of get: > s1 :- > % do the corresponding action > ( get0(Char) -> s1(Char) > ; > accept > ). This doesn't generalise as well as the end-marker version. Here is the kind of thing one is constantly doing: rest_identifier(Char, [Char|Chars], After) :- is_csymf(Char), !, get0(Next), rest_identifier(Next, Chars, After). rest_identifier(After, [], After). See how this code can treat the end marker just like any other character: because it doesn't pass the is_csymf/1 test (copied from Harbison & Steele, by the way) we'll pick the second clause, and there is no special case needed for an identifier which happens to be at the end of a stream. The fail-at-end approach forces us not only to do something special with the get0/1 in rest_identifier/3, but in everything that calls it. (In the Prolog tokeniser, there are two such callers.) The point is that if-then-elses such as Meier suggests start appearing all over the place like maggots in a corpse if you adopt the fail-at-end approach, to the point of obscuring the underlying automaton. > I must say, none of the two seems to me satisfactory. Richard's > version is not portable due to the -1 as eof character. If the standard were to rule that -1 was the end of file character, it would be precisely as portable as anything else in the standard! In strict point of fact, the Prolog-in-Prolog tokeniser was written in DEC-10 Prolog for DEC-10 Prolog, and used 26 as the end of file character, and 31 as the end of line character. It took 5 minutes with an editor to adapt it to Quintus Prolog. I wish C programs written for UNIX took this little effort to port! > for a Prolog system it is better to have get0/1 return > some *portable* eof (e.g the atom end_of_file, for get0/1 > there can be no confusion with source items) instead of > some integer. It is important that the end-of-file marker, whatever it is, should be the same kind of thing, in some sense, as the normal values, so that classification tests such as is_lower/1, is_digit/1, and so on will just fail quietly for the end-of-file marker, not report errors. Since end of file is rare, we would like to test the other cases first. Pop-2 on the Dec-10 returned integers almost all the time, except that at the end of a stream you got an end-of-file object which belonged to another data type (there was only one element of that data type, and it printed as ^Z). This was in practice a major nuisance, because before you could do anything other than an equality test with the result, you had to check whether it was the end of file mark. I have been giving out copies of the Prolog-in-Prolog tokeniser to show how easy it is to program character input with the Edinburgh Prolog approach. If someone would give me a tokeniser for BSI Prolog written entirely in BSI Prolog using the fail-at-end approach, and if that tokeniser were about as readable as the Prolog-in-Prolog one, that would go a long way towards convincing me that fail-at-end was a good idea. > BSI objects that if [read/1] returns e.g. the atom end_of_file > then any occurrence of this atom in the source file > could not be distinguished from a real end of file. That's not a bug, it's a feature! I'm serious about that. At Edinburgh, I had the problem that if someone asked me for help with Prolog, they might be using one of four different operating systems, where the end of file key might be ^Z or ^D or ^Y or something else which I have been glad to forget. No problem. I could always type end_of_file. to a Prolog listener, and it would go away. Oh, this was so nice! In fact, on my SUN right now I have function key F5 bound to "end_of_file.\n" so that I can get out of Prolog without running the risk of typing too many of them and logging out. Another thing it is useful for is leaving test data in a source file. One can do end_of_file. and include the test cases in the program or not just by moving the end_of_file around. Ah, you'll say, but that's what nested comments are for! Well no, they don't work. That's right, "#| ... |#" is NOT a reliable way of commenting code out in Common Lisp, and "/* ... */" is NOT a reliable way of commenting code out in PopLog. But end_of_file, in Edinburgh Prolog, IS a reliable way of commenting out the rest of the file. > In this case, a remedy would be the introduction of Prolog needs a remedy for end_of_file like Elizabeth Schwarzkopf needs a remedy for her voice. Before taking end_of_file away from me, the BSI committee should supply me with a portable way of exiting a break level and a reliable method of leaving test cases in a file without having them always read. ------------------------------ Date: 24 Mar 88 08:58:35 GMT From: quintus!ok@unix.sri.com (Richard A. O'Keefe) Subject: behavior of read/get0 at end_of_file Just to continue with the get0 topic: The fail-at-end approach rests on an assumption which deserves to be made explicit, because it is false. What is the assumption? That receiving the end-of-file indication from an operating system indicates that there is nothing further to read in that stream. This is false? Yes. Let's ignore 4.2BSD sockets, V.3 Streams, non-blocking I/O, VMS concatenated files, and other esoterica which one doesn't expect BSI Prolog to cope with. Let's just consider terminals. In Tops-10 (home of DEC-10 Prolog): end-of-file from the terminal is a software convention (^Z). You can just keep reading from the terminal after that, and in fact that's exactly what DEC-10 Prolog does. In UNIX (original home of C Prolog): end-of-file from the terminal is a software convention (EOF character typed after an empty line). You can just keep reading from the terminal after that, and in fact that's exactly what C Prolog does. In VM/CMS, using SAS Lattice C end-of-file from the terminal is a software convention (some magic string, which defaults to "EOF", but it is trivially easy for a program to change it -- use afreopen()). I believe that you can keep reading from the terminal after that, but I haven't tried it myself. On a Macintosh, using ALS Prolog end-of-file from a window is a software convention (you click on "End of File" in the "Edit" menu). All windows and streams remain live after that, and you can just keep reading, and that's what ALS Prolog does. On a Xerox Lisp Machine, using Xerox Quintus Prolog end-of-file from a TEXEC window is a software convention. All windows and streams remain live after that, and you can just keep reading, and that's what XQP does. [The sample of Prologs is not of interest here; my point is that there are several *operating systems* with this characteristic. ] So the rule actually followed in Edinburgh-compatible Prologs is that - the sequence of characters codes returned by get0/1 is the sequence of characters delivered by the source - with the end-of-file marker inserted every time the host indicated the end-of-file condition - Prolog receives through get0/1 as many characters and as many end-of-file markers as there are; any attempt to read past the end of this union stream is an error. Not a failure, an error. It happens that when you are reading from disc files, most operating systems will indicate the end of file condition once. Are terminals the only kind of file for which multiple end-of-file conditions are plausible? No. The convention for tapes is that a single tape-mark (usually reported as an end-of-file condition) is merely a separator; a tape as such is terminated by a double tape-mark. Thus a Prolog program copying one tape to another (this is a reason why we might want put(-1) NOT to close a file; if it does anything special on a tape it should be to write a tape-mark) might want to keep reading after seeing an end-marker. ------------------------------ Date: 24 Mar 88 15:29:47 GMT From: mcvax!ukc!dcl-cs!simon@uunet.uu.net (Simon Brooke) Subject: Strings In article <776@cresswell.quintus.UUCP> ok@quintus.UUCP (Richard A. O'Keefe) writes: >Xerox Lisp uses lists for bignums to this very day. Yes, true. But please don't assume this means it is efficient. For example, I recently benchmarked a Xerox 1186 running Interlisp (Koto) against the new Acorn Archimedes running Arthur Norman's Cambridge Lisp. Generally the Arch was a bit faster, reflecting the simpler lisp and faster processor. But running (fact 1000) it was 321 (three hundred and twenty one) times faster - and this must reflect grotesque inefficiency in Xerox' bignum code. ------------------------------ Date: 24 Mar 88 18:35:05 GMT From: eagle!icdoc!doc.ic.ac.uk!cdsm@ucbvax.Berkeley.EDU (Chris Moss) Subject: Strings I haven't contributed to the string discussion because I don't believe I know all the answers. But I do have strong reservations about the current Edinburgh implementations. Let me air them: 1. They don't get printed properly by "write". I don't like seeing "Prolog" come out as [80,114,111,108,111,103]. OK, I can define a "writestring", but not use it within a bigger structure without pain. Now here's an odd thing: there is no writestring routine in the Quintus library that I can discover! Not in the built-ins or the (extensive) library! What does that tell us about the use of strings? It suggests to me that people actually always use atoms for this purpose, and somewhere in their code is an implicit definition: writestring(String) :- name(Atom,String),write(Atom). Note that in most systems this involves an entry in the symbol table (reasonably slow) and a limit of 256 (or maybe 512) characters. MuProlog DOES print out "Prolog" as a string, wherever it occurs. Presumably it has to scan the list first. It uses list format whenever there is a integer outside the range 32-127 in the list (it also gets tabs right tho I don't understand why it finishes at 127 not 126). That's a pretty good heuristic, tho I can imagine it going wrong on occasions. e.g. if I read in a clause over several lines the string will contain newlines, so my DCG debugging will still look horrid! 2. I don't like having ASCII values in my programs. With a certain amount of discipline and inefficiency one can get round that, by saying, for instance: lowercase(X) :- "a"=[A], "z"=[Z], X>=A, X=, cdsm@ivax.doc.ic.ac.uk (Chris Moss) writes: > Richard's certainty that the > Prolog world begins and ends with Edinburgh is not shared in France! I have no such certainty. I think the Prolog world includes Israel, Japan, Australia, Portugal, the USA, Hungary, China[*], and all sorts of places. The plain fact of the matter is that there are two sorts of Prolog systems: ones whose authors have tried to provide some sort of compatibility with other Prologs, and ones whose authors have let their imaginations rip. What the compatible Prologs are compatible with is not Prolog-II or Waterloo Prolog, but Clocksin&Mellish. [*] I am credibly informed that the PRC uses an unauthorised translation of Clocksin & Mellish as the main Prolog text, so presumably the French Prologs are of small concern to them. ------------------------------ Date: 20 Mar 88 20:47:39 GMT From: lagache@violet.Berkeley.EDU (Edouard Lagache) Subject: An Eliza-like problem solving assistant (400+ lines). The following program is the mockup of the Eliza-like problem solving assistant that was demonstrated at the March 10 meeting of the PROLOG Forum. The program is the result of 3 late night work, and NO claims of correctness or efficiency are expressed on implied. On the contrary, suggestions for expansion or improvement are most welcome. As stated earlier, it is hoped to turn this program into a group project, so we hope that there is plenty of room for expansion! There are some calls to my imfamous PROLOG libraries. The 'window' and 'set_attribute' calls will only work on a Texas Instruments PC, so I leave it to you to omit, or port these calls to your system. There is one call to the predicate 'nthelem', I have enclosed a copy of the predicate at the end of the file. There may be other calls that I have forgotten, if so let me know and I will provide the needed code. Hack to your hearts delight! -- Edouard Lagache /* File: 'consulta.pro' */ /******************************************************************************/ /* */ /* An A.I. Consultant for PROLOG Programmers */ /* */ /* This program is intended to serve as a "intelligent consultant" */ /* for PROLOG programs to turn to when encountering some impasse in a */ /* programming project. The program is based on the "Eliza" program, but */ /* it designed to provide comments that might foster the user to "solve" */ /* his/her own problem. */ /* */ /* A collaborative project of the PROLOG Forum. */ /* Release - 1.00, March - 1988, Copyright(C) 1988, The PROLOG Forum */ /* */ /* Credits: The concept of an "Eliza" type program to help users with */ /* problem solving was first proposed (as far as we know) by */ /* Professor Charles Woodson of the School of Education at */ /* U.C. Berkeley. He also built a small system in LISP for the */ /* purposes of demonstrating the concept to members of his LISP */ /* programming course. */ /* */ /******************************************************************************/ /*>> 'gethelp' is the routine that the user can call to get access to the <<*/ /*>> system. E. Lagache, Ver-1.0, 3/88 <<*/ gethelp :- tell('_WINDOWS'), make_window(0,15,14,64), set_attribute(magenta), clear_screen, set_attribute(white), prtstr("Please describe your problem, type 'quit.' to resume work"), nl, converse, close_window, tell(user). /*>> 'converse' is the main "looping" routine. It takes a list of words <<*/ /*>> and searches for keywords. Then it generates a response based on <<*/ /*>> keywords. The responses are randomized to make the system more <<*/ /*>> "humanlike". E.L., Ver-1.0, 3/88 <<*/ converse :- set_attribute(white), prtstr("<>-> "), set_attribute('yellow'), read_sentence(Request), nl, remove_duplicates(Request,Request1), make_reply(Request1,Reply), set_attribute(cyan), print_reply(Reply), nl, continue(Request1). /* continue makes the recursive call to 'converse' if the user doesn't want */ /* quit */ continue(['quit','.']) :- set_attribute(white), pause. continue(_) :- converse. /* 'pause' uses the interval function to simply carry out some time consuming */ /* task to make a delay between the exit prompt, and the closing of the */ /* window. */ pause :- interval(1,X,10), fail. pause. /* 'print_reply' simply prints out the list of items recursively */ print_reply([]). print_reply([Item|Rest]) :- print(Item), put(32), print_reply(Rest). /*>>------------------------------------------------------------------------<<*/ /*>> 'make_reply' generates a response to a keyword in the users input text <<*/ /*>> it keeps a list of keywords and appropriate responses, and chooses one <<*/ /*>> randomly to provide a "natural" appearance. <<*/ /*>> E. Lagache, Ver - 1.0, 3/88 <<*/ /* Exit comment */ make_reply(['quit','.'],['Thank','you','I','hope','these','comments','were', 'useful.'] ). /* Main test, select some set of keywords, test if they match, if so succeed */ /* otherwise fail and look at another possibility. */ make_reply(Sentence,Reply) :- word_class(Keywords,Responses), intersection(Keywords,Sentence,List), List \== [], !, length(Responses,Top), irandom(1,Top,Number), nthelem(Responses,Number,Reply). /* if no keywords are found, then use default responses */ make_reply(_,Reply) :- default_responses(Responses), length(Responses,Top), irandom(1,Top,Number), nthelem(Responses,Number,Reply). /* 'remove_duplicates' returns a list that contains only one of each item. */ /* This is necessary for the 'intersection' predicate, and makes the search */ /* less time consuming. E.L. 3/88 */ remove_duplicates([],[]). /* First case, don't copy duplicate items */ remove_duplicates([Item|Rest],NewRest) :- member(Item,Rest), !, remove_duplicates(Rest,NewRest). /* If not duplicated, then copy */ remove_duplicates([Item|Rest],[Item|NewRest]):- remove_duplicates(Rest,NewRest). /* 'intersection' return true if one of the keywords was found among the */ /* words typed in. From C&M page 154 */ intersection([],X,[]). intersection([X|R],Y,[X|Z]) :- member(X,Y), !, intersection(R,Y,Z). intersection([X|R],Y,Z) :- intersection(R,Y,Z). /*>> 'irandom' returns a integer in the range specified by the first two <<*/ /*>> arguments to the predicate, E. Lagache, Ver-1.0, 3/88 <<*/ /*>> Values for computation taken from the first edition of "Oh Pascal" <<*/ /*>> By Clancy and Cooper (page-227). <<*/ irandom(Lower,Upper,Number) :- get_seed(Seed), Start is (25173*Seed + 13849) mod 65536, Number is (Start mod (Upper - Lower)) + Lower. /* 'get_seed' will in general be an implementation specific predicate. */ /* For ADA PROLOG, 'get_seed' will return the number of logical inferences */ /* so far made, using the ADA specific 'licount' predicate */ get_seed(Seed) :- licount(Seed). /*>>---------------------------------------------------------------------<<*/ /*>> 'read_sentence' is a routine to take user input and make a list of <<*/ /*>> atoms out of it. It is slightly modified from Clocksin & Mellish <<*/ /*>> page 104. <<*/ read_sentence([W|Wa]) :- get0(C), readword(C,W,C1), restsent(W,C1,Wa). /* Given a word and the character after it, read in the rest of the sentence */ restsent(W,_,[]) :- lastword(W),!. restsent(W,C,[W1|Wa]) :- readword(C,W1,C1), restsent(W1,C1,Wa). /* Read in a single word, given initial character, and remembering what */ /* character came after that word. */ readword(C,W,C1) :- single_character(C), !, name(W,[C]), get0(C1). readword(C,W,C2) :- in_word(C,NewC), !, get0(C1), restword(C1,Cs,C2), name(W,[NewC|Cs]). readword(C,W,C2) :- get0(C1), readword(C1,W,C2). /* continue process of stringing characters of the same word together */ restword(C,[NewC|Cs],C2) :- in_word(C,NewC), !, get0(C1), restword(C1,Cs,C2). restword(C,[],C). /* These characters form words on their own */ single_character(44). /* , */ single_character(59). /* ; */ single_character(58). /* : */ single_character(63). /* ? */ single_character(33). /* ! */ single_character(46). /* . */ /* These characters can appear within a word */ /* the second in_word clause converts letters to lower case */ in_word(C,C) :- C>96, C<123. /* 'a' to 'z' */ in_word(C,L) :- C>64, C<91, L is C+32. /* 'a' to 'z' */ in_word(C,C) :- C>47, C<58. /* '0' to '9' */ in_word(39,39). /* '\'' */ in_word(45,45). /* '-' */ /* These words terminate a sentence */ lastword('.'). lastword('!'). lastword('?'). /*>>-----------------------------------------------------------------------<<*/ /*>> 'word_class' and 'default_responses' contain the text that will be <<*/ /*>> presented to the user. 'word_class' also contains the list of <<*/ /*>> keywords, that will be used to decide if this type of response is <<*/ /*>> appropriate. <<*/ /* --------------------- Programming specific keywords -------------------- */ /* Words related to failure to get the file to consult properly */ word_class([consult,consulting,compile,compiling,load,loads,loading], [ ['Do',you,know,at,what,location,the,problem,'is?'], ['Could',the,problem,be,at,a,different,place,from,where,the, error,message,is,'reported?' ], ['What',sort,of,diagnostic,message,did,the,system,'give?'], ['What',sort,of,problems,could,have,caused,this,'situation?'], ['Is',there,another,way,you,could,arrange,your,file,that,might, make,it,easier,to,find,the,'problem?'], ] ). /* Problems with incorrect output */ word_class([output,print,prints,printing,write,writes,writing,printout], [ ['What',sort,of,output,were,you,'expecting?'], ['What',kind,of,output,did,you,actually,get,'(if','anything)?'], ['How',could,you,modify,your,program,to,get,more,information, about,this,'i/o','problem?' ], ['Can',you,see,where,in,the,program,the,bug,must,be,'located?'], ['How',could,you,further,localize,the,source,of,the,incorrect, 'output?' ] ] ). /* Input problems */ word_class([input,read,reads,reading], [ ['How',do,you,that,the,problem,is,caused,by,incorrect,input, 'routines?' ], ['How',could,you,modify,your,program,to,get,more,information, about,this,'i/o','problem?' ], ['How',could,you,further,localize,the,source,of,the,incorrect, input,'behavior?' ], ['How',could,you,test,these,input,routines,in,'isolation?'], ] ). /* flow of control problems */ word_class([infinite,loop,recursion,stepping,trace,tracing], [ ['Where',is,the,last,place,that,you,know,your,program,was,running, 'correctly?' ], ['How',do,you,know,that,the,program,is,not,behaving,as,it, 'should?' ], ['Which',predicates,could,have,caused,this,'phenomenon?'], ['Is',there,any,way,that,you,could,isolate,the,predicates,that, are,at,'fault?' ], ] ). /* Error word */ word_class([error,warning,failure,diagnostics], [ ['Can',you,isolate,the,error,to,one,'place?'], ['Can',you,find,a,reasonable,interpretation,for,these, diagnostics ], ['Have',you,encountered,similar,sorts,of,messages,in,the, 'past?' ], ['What',sort,of,information,would,allow,you,to,deal,with,this, 'message?' ], ] ). /* Logical errors */ word_class([infer,inference,deduce,deduction,entails,entailment], [ ['What',should,have,the,system,'deduced?'], ['What',was,the,last,inference,that,you,know,was,'correct?'], ['How',do,you,know,that,the,inference,was,in,fact,incorrect, '(given',the,systems,actual,'configuration)?' ], ['What',sort,of,database,condition,could,have,caused,the, observed,'deductions?' ] ] ). /* Bug talk */ word_class([bug,malfunction,wrong,incorrect,incomplete,unexpected], [ ['How',do,you,know,you,have,a,'bug?'], ['Where',is,the,last,place,that,you,know,your,program,was, functioning,'correctly?' ], ['What',program,behavior,where,you,'expecting?'], ['What',indicates,that,something,is,'wrong?'], ['What',occurred,that,was,not,'expected?'], ['What',information,would,you,need,to,isolate,the,'bug?'], ['what',would,you,need,to,know,to,locate,the,'malfunction?'], ['What',tests,could,you,perform,to,get,at,the,'problem?'], ] ). /* ----------------- Problem solving keywords ------------------------- */ /* Alternatives */ word_class([option,options,alternatives,possible,possibilities], [ ['What',alternatives,have,you,already,'tried?'], ['Are',there,other,possibilities,that,you,might,'consider?'], ['Which',options,have,you,already,'tried?'], ['Might','I',suggest,that,you,take,a,new,look,at,your, 'alternatives.' ], ] ). /* Reflection, on previous work */ word_class([tried,attempted,thought], [ ['What',have,you,done,in,the,past,in,such,'situations?'], ['Tell',me,which,approaches,you,have,already,'tried?'], ['What',have,you,attempted,'previously?'], ['What',have,you,done,here,that,you,might,want,to,do,differently, in,the,'future?' ] ] ). /* Strategic problem solving, evaluating plans */ word_class([try,attempt,do,complete,investigate,think,thinking], [ ['How',will,trying,this,help,you,out,of,your,'impasse?'], ['What',can,you,learn,from,doing,'this?'], ['Are',you,sure,that,there,is,not,a,more,productive,investigation, that,you,could,'make?' ], ['Are',there,better,ways,to,get,the,information,you,need,to,solve, this,'problem?' ], ] ). /* -------------------------- Human Psychology words ----------------------- */ /* stuck words */ word_class([stuck,stopped,impasse], [ ['Perhaps',you,should,reflect,back,on,the,various,'possibilities.' ], ['There',must,be,alternatives,that,you,have,not,'considered.'], ['I',suggest,that,you,step,back,from,the,problem,and,review, what,you,have,'tried.' ], ] ). /* "down" words */ word_class([depress,depressing,depressed,frustrating,frustrated,mad,annoy, annoyed,annoying ], [ ['It',is,not,a,good,idea,to,get,worked,up,about,a,'program.'], ['One',should,not,take,this,too,'seriously,',after,all,it,is, only,a,'program.' ], ['If',this,task,is,getting,to,you,perhaps,you,should,take,a,break, and,come,back,to,it,'later.' ], ['Perhaps',you,have,been,working,too,long,on,this,one,'problem,', maybe,you,should,take,a,'break.' ], ['Do',you,tell,me,that,this,program,is,getting,to,'you!'], ['Well',nobody,said,that,programming,was,'easy.'], ['Do',not,get,worked,up,over,'this,','after,all,',no,program,that, is,interesting,is,easy,to,'write.' ] ] ). /* "Bad" words (should this sort of thing be censored!?!) */ word_class([fuck,fucking,shit,shitty,damn,screw,screwed,hell], [ ['Well','I',hope,that,relieves,your,'aggressions;',now,can, we,get,back,to,'work?' ], ['I',am,glad,you,know,'profanity;',unfortunately,this,system, only,understands,'PROLOG!' ], ['Yes,','yes,',profanity,is,the,language,that,all,programmers, know,'best!' ], ['If',you,can,not,say,anything,more,'constructive,',perhaps, you,should,take,a,break,and,come,back,to,this,'problem.' ], ['Sorry',that,language,is,not,in,my,'vocabulary!'], ] ). /* Help words */ word_class([help,assistance,explain,explanation], [ ['In',what,area,do,you,need,some,'assistance?'], ['Can',you,be,more,specific,about,what,sort,of,help,you,'need?'], ['Exactly',what,sort,of,information,do,you,'seek?'], ['In',what,way,may,'I',be,of,'assistance?'], ['What',kind,of,advice,do,you,'need?'], ['Exactly',in,what,area,do,you,need,an,'explanation?'], ['Please',tell,me,background,about,your,'problem?'], ['Could',you,further,elaborate,on,the,type,of,problem,you,are, 'having? ' ] ] ). default_responses([ ['Please','continue.'], ['Could','you','say','more','about','your','problem.'], ['I','would','like','to','hear','some','more','specifics.'], ['Could','you','please','elaborate','on','one','aspect', 'of','your','problem.' ], ['Is',there,anything,else,that,you,know,about,this, 'problem?' ], ['I',do,not,quite,understand,the,'situation;',could,you, elaborate,it,for,'me?' ] ] ). /* * * * * * * * * * * * * * * * * * * * * * * */ /* 'nthelem' returns the element in the */ /* 'Index' place of the list. */ /* */ /* E. Lagache, Vers. - 1.00, Dec - 86 */ /* * * * * * * * * * * * * * * * * * * * * * * */ nthelem([],_,error):- !, fail. /* fail if list is smaller than index */ nthelem([Item|_],1,Item):- !. /* return item */ nthelem([X|Rest],NewIndex,Item):- Index is NewIndex-1,nthelem(Rest,Index,Item). /* end file consulta.pro */ ------------------------------ Date: 24 Mar 88 02:37:00 GMT From: quintus!ok@unix.sri.com (Richard A. O'Keefe) Subject: An Eliza-like problem solving assistant (400+ lines). In article <7845@agate.BERKELEY.EDU>, lagache@violet.berkeley.edu (Edouard Lagache) distributed an "Eliza-like problem solving assistant". (1) In the tables at the end of the program, the pattern word_class([...], [ [...], ... [...], *--------------------^ ]). occurs quite often. While C allows extra commas in initialisers, many Prolog systems will be unhappy about that extra comma. (2) In order to test whether any of a given list of Keywords occurs in a given Sentence, the program does intersection(Keywords, Sentence, List), List \== [], This is correct, but there isn't a lot of point in constructing List. A more efficient approach (effiency is not a concern in this program) would be to call intersect(Keywords, Sentence) where for example intersect(Set1, Set2) :- member(X, Set1), member(X, Set2), !. (3) I thought I'd try the program. This is what I got: Please describe your problem. Type 'quit.' to resume work. e> My nematodes have been eaten by the peripatus. Could you say more about your problem. e> Yes. Could you please elaborate on one aspect of your problem. e> Well, the peripatus crawled out of its box last night, and it got into e> another box on the same bench, where we keep nematodes, and well, it e> ate the lot of them. Is there anything else that you know about this problem? e> I suppose there are two problems, really. One problem is how do I e> stop this sort of thing in the future, and the other is where do I e> get some more nematodes right now. I want your help with the second e> part of the problem. What can you learn from doing this? e> From doing WHAT? Could you say more about your problem. e> I just told you everything there was to say. Could you say more about your problem. e> You're getting a bit repetitious, aren't you? I do not quite understand the situation; could you elaborate it for me? e> Gods below, what is there to understand? I need some nematodes, I e> had some nematodes, but Hodgkins' wretched peripatus ate them. Is there anything else that you know about this problem? e> Oh forget it. Could you say more about your problem. e> quit. In case you were wondering, the e> lines do make sense. (4) Part of the reason for the frustrating and unhelpful nature of the conversation shown is that the program is not Eliza-like. One of the things that Eliza would do is pick up phrases of yours and throw them back at you. For example, after my third input, an Eliza-like program might say Tell me more about the box. ^^^^^^^----- copied from input or after the last input but two, Why did Hodgkins' wretched peripatus eat them? ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ transformed input ------------------------------ End of PROLOG Digest ********************