Path: utzoo!utgpu!jarvis.csri.toronto.edu!mailrus!uwm.edu!gem.mps.ohio-state.edu!ginosko!uunet!munnari.oz.au!cs.mu.oz.au!ok From: ok@cs.mu.oz.au (Richard O'Keefe) Newsgroups: comp.lang.prolog Subject: Re: different Prologs? Message-ID: <2365@munnari.oz.au> Date: 10 Oct 89 02:02:18 GMT References: <111000002@uxa.cso.uiuc.edu> Sender: news@cs.mu.oz.au Lines: 124 In article <111000002@uxa.cso.uiuc.edu>, ddgg0881@uxa.cso.uiuc.edu writes: > I have two questions about Prolog implementations: > 1. I've discovered one implementation that gives 3 solutions to > member(a,[a,a,a]) even though there aren't 3 ways to instantiate > variables. Another implementation I've used gives only one solution. > Which way is standard? Are there arguments for doing things one way > or the other? There is precisely one "standard" way of defining member/2, modulo spelling of variable names and dot-vs-bracket syntax for lists: member(X, [X|_]). member(X, [_|L]) :- member(X, L). Prolog does *not* find solutions. Prolog finds **proofs**, and shows you (part of) the substitution it found for each proof. The goal ?- member(a, [a,a,a]). has three proofs. So that's the number of times Prolog should come back to you with an answer substitution. However, there is a fine point to do with the user interface. Many Prolog systems check the query that you type in, and if it hasn't got any variables, just say "yes" or "no". NU Prolog does this, except it says "true" or "false". Transcript: % np NU-Prolog 1.4 1?- member(a, [a,a,a]). true. However, if you put a variable in there to fool it, you will see that it is prepared to find the other proofs. Transcript: 2?- X=dummy, member(a, [a,a,a]). X = dummy ; X = dummy ; X = dummy ; fail. Another way of seeing that the three proofs are found is 3?- member(a, [a,a,a]), write(*), fail. ***fail. > 2. I find reading text files very painful in Prolog because you > always have to keep a lookahead character. So? You need a lookahead character for tokenising anyway, all you have to do is remember not to drop it on the floor. Just write down your DFSA and the transcription into Prolog is direct, but DIRECT. > This way of doing things doesn't feel like Prolog. Of course not, because it uses side effects. Depending on the size of the files you want to process, and the memory capacity of your machine, you might want to consider slurping the entire file into memory as a list of character codes, and parsing that using grammar rules. I'm quite serious about this: get_file(FileName, Chars) :- seeing(CurrentInput), see(FileName), get0(C), slurp(C, Cs), seen, see(CurrentInput), Chars = Cs. slurp(-1, []) :- !. slurp(C, [C|Cs]) :- get0(D), slurp(D, Cs). In SICStus Prolog 0.6 it takes about 6 seconds to read 30kbytes this way on a discless Sun-3/50. > One of the main selling points of Prolog > is the efficient way that it backtracks upon failure. Are there any > implementations of Prolog that allow 'get' or 'get0' to backtrack > and unread characters? Yes. Sort of. If you are reading from a file there is no problem. But what if you are reading from a source that can't back up? (For example, reading from a terminal --very likely-- or a UNIX command --I do that quite often-- or a named pipe --quite possible-- or a streaming tape --just joking.) Then the Prolog system would have to buffer an arbitrary amount of information just in case you decided to back up. Your Prolog system probably provides some kind of file positioning feature. Just for grins, here's how to hook it into grammar rules. file(NonTerminal, File) :- open(File, read, Stream), stream_position(Stream, Position), phrase(NonTerminal, @(Stream,Position), @(Stream,End)), stream_position(Stream, _, End), % high-water mark get0(-1). % is end-of-file get_char(Char, @(Stream,OldPos), @(Stream,NewPos)) :- stream_position(Stream, _, OldPos), % go to OldPos get0(Char), % pick up Char Char >= 0, % check not EOF stream_position(Stream, NewPos). % where are we now? Now your grammar rules, instead of saying [Char] will say get_char(Char) as a non-terminal. In NU Prolog, which provides the non-portable ftell/2 and fseek/3 constructs (they are fine in UNIX, but not so good in VMS or MVS or CMS), it's even simpler: stream_phrase(NonTerminal, Stream) :- open(File, read, Stream), ftell(Stream, Start), fseek(Stream, 0, end), ftell(Stream, Finish), call(NonTerminal, @(Stream,Start), @(Stream,Finish)). get_char(Char, @(Stream,OldPos), @(Stream,NewPos)) :- plus(1, OldPos, NewPos), fseek(Stream, OldPos, beginning), get0(Char), Char >= 0. call(Closure, X1, X2) :- Closure =.. L0, append(L0, [X1,X2], L2), Goal =.. L2, call(Goal).