Path: utzoo!utgpu!jarvis.csri.toronto.edu!mailrus!ames!lll-winken!uunet!mcvax!inesc!unl!uniq!px From: px@unl.fctunl.rccn.pt (Joaquim Baptista (pxQuim)) Newsgroups: comp.lang.prolog Subject: Re: retractall and backtracking Message-ID: Date: 9 Jun 89 19:41:19 GMT References: <575@hfserver.hfnet.bt.co.uk> Sender: px@unl.fctunl.rccn.pt Organization: Universidade Nova de Lisboa, Portugal Lines: 101 In-reply-to: nigel@hfserver.hfnet.bt.co.uk's message of 8 Jun 89 08:07:02 GMT Posting-Front-End: GNU Emacs 18.44.13 of Wed Jun 24 1987 on uniq (berkeley-unix) In article <575@hfserver.hfnet.bt.co.uk> nigel@hfserver.hfnet.bt.co.uk (Nigel Cliffe) writes: > > I have a problem with backtracking, and retracts that don't appear to > happen - here is a simplified example: > > If I have a database with repeated facts in it and want to a) get just the > unique entries, and b) retract the contents of the database as I get those > entries then one way that ought to work is this: > > :- dynamic foo/1. > foo(bar1). > foo(bar1). > foo(bar2). > > write_unique_foos :- > foo(X), > write(X),nl, > retractall(foo(X)), > fail. > > Now if the predicate "write_unique_foos" is called, then I get all three > values for foo written, not just the two unique ones as I would expect. > However if I trace the execution, and test the contents of the database at > various points from the debugger then "foo(bar1)" has disappeared after the > first call to "retractall". > > Can anyone explain why this happens. > > I know that I can get the result I want by using setof, and probably > hundreds of other methods as well, I'm just curious as to why things don't > behave as I would expect. > > Just incase this is system dependent, I'm using Quintus 2.4 on a Sun3 running > Sun OS 3.5. > This problem is a feature rather than a bug, and occurs because of the usual implementations for the prolog interpreters. When one calls one predicate, as you did in foo(X), a "frame" is pushed onto the stack. The actual formats of this frame differ, so I will have to simplify it a little. Anyway, this frame will have information about the first clause foo(bar1), and will know that, on failure, it should try the second foo(bar2). This "will know that" is just a pointer for the second clause. Now, when you retractall(foo(X)), both clauses will go away, but the pointer to the second stored in the frame will not. That is why your program, in backtracking, will still find the second clause. Note that if you had... foo(bar1) foo(bar2) foo(bar1) ...then your program probabily would run as you expected. Variations on this problem also arise with asserta and assertz, as in: /* this will write 1 and fail */ a(1). exec1:- a(X), write(X), asserta(a(X)), fail. /* this will loop forever, exhausting your heap space, writing 1211111111... */ b(1). b(2). exec2:- b(X), write(X), assertz(b(X)), fail. /* this will do one of the above, depending on your system. Tail recursion optimization will make it do as exec1; else it will do as exec2. */ c(1). exec3:- c(X), write(X), assertz(c(X)), fail. One way to solve your problem is not to depend on backtracking, as in: :- dynamic foo/1. /* I've never seen one of this, but I guess what it does. */ foo(bar1). foo(bar2). foo(bar3). write_unique_foos :- foo(X), write(X), nl, retractall(foo(X)), write_unique_foos. This works in all the interpreters and compilers I know of (more or less dinamic foo/1 :-). Hope this enlightened you. Feel free to write. -- -------- Joaquim Manuel Soares Baptista | BITNET/Internet: px@fctunl.rccn.pt Centro de Inteligencia Artificial | UUCP: px@unl.uucp Uninova | ARPA: px%fctunl.rccn.pt@mitvma.mit.edu Fac. de Ciencias e Tecnologia/UNL | PSI/VMS: PSI%(+2680)005010310::PX 2825 Monte de Caparica | Fax: (+351) (1) 295 4461 PORTUGAL | Sound: (+351) (1) 295 4464 ext. 1360