Path: utzoo!utgpu!news-server.csri.toronto.edu!rutgers!tut.cis.ohio-state.edu!pacific.mps.ohio-state.edu!zaphod.mps.ohio-state.edu!samsung!munnari.oz.au!bruce!goanna!ok From: ok@goanna.cs.rmit.oz.au (Richard A. O'Keefe) Newsgroups: comp.lang.prolog Subject: Re: Novice RETRACT question Message-ID: <3493@goanna.cs.rmit.oz.au> Date: 31 Jul 90 07:01:36 GMT References: <1939@engage.enet.dec.com> Organization: Comp Sci, RMIT, Melbourne, Australia Lines: 97 In article <1939@engage.enet.dec.com>, ssmith@ncdel.enet.dec.com (Sheldon E. Smith) writes: > What I'd *really* like is a book of Prolog > algorithms, but I'll defer *that* to another article. (Hmm. What would you pay for it? What do you want to be in it?) > In any event, I have facts of the form > object(Time,X,Y) > where X and Y are locations, and Time is the time-stamp when the object > was observed. That is, given the facts > object(1,12,34). > object(2,12,34). > object(3,12,34). > I'd like to retract "object(1,12,34)" and "object(2,12,34)". This is where data base references come in extremely handy, if your Prolog system supports them. Suppose you want to retract everything before a particular Time: retract_objects_before(Time) :- forall(( clause(object(T,_,_), _, Ref), T < Time ), ( erase(Ref) )). If your Prolog lacks the forall/2 structure, you might do retract_objects_before(Time) :- \+ ( clause(object(T,_,_), _, Ref), T < Time, \+ erase(Ref) ). which comes to the same thing. (The jargon for this kind of structure is a ``failure-driven loop''.) Something marginally cleaner employs an auxiliary predicate: erase_refs([]). erase_refs([Ref|Refs]) :- erase(Ref), erase_refs(Refs). which you only have to write once, and then uses a call to setof/3: retract_objects_before(Time) :- setof(Ref, X^Y^B^( clause(object(T,X,Y), B, Ref), T < Time ), Refs), erase_refs(Refs). Another approach might be to write an erase_all/2 looking rather like findall/3 but erasing things instead of returning them. (Algol 60 fans: do you recognise Jensen's device?) erase_all(Ref, Generator) :- forall(Generator, erase(Ref)). and then call erase_all(Ref, /* where */ ( clause(object(T,_,_), _, Ref), T < Time )) > I'd have thought that something like > reduce_objects :- object(I,X,Y),object(J,X,Y), > I < J, > retract(object(I,X,Y)). > What am I doing wrong? Not a lot, actually, except for ugly layout. The main problem is that retract/1 removes *ONE* clause each time it succeeds; here you want to remove ALL the old clauses. Your code could be reshaped a bit to retract_old_objects :- object(J, X, Y), % for each timestamped object object(I, X, Y), % for each object at that same place I < J, % if I is older than J retract(object(I,X,Y), % delete it fail % and look for some more ; true. % succeed when we've run out. I'm a wee bit unhappy about this. Surely an object ought to have an identity of its own, other than its location at some time. How do you say that the object which is at (27,33) at time 98 is the same object as the object which is at (28,32) at time 99? A rule of thumb for checking that your program makes sense is that whenever you have a call to retract/1 - either it is in a loop - or you have identified a UNIQUE clause you want to remove -- Science is all about asking the right questions. | ok@goanna.cs.rmit.oz.au I'm afraid you just asked one of the wrong ones. | (quote from Playfair)