🚀 go-pugleaf

RetroBBS NetNews Server

Inspired by RockSolid Light RIP Retro Guy

Article View: comp.lang.prolog
Article #3209

Re: back_retract

#3209
From: Paul Tarau
Date: Tue, 05 Apr 1988 16:13
194 lines
7286 bytes
A number of recent articles by Bart Demoen, Richard O'Keefe,
Sanjay Manchanda and Paul Voda re-opened the problem of the
"assert" family of primitives. I agree that we can do better without. 
However, as much as we restrict us to a unique path of the Prolog
proof-tree, it is possible to manipulate their "soft" counterparts 
safely and to implement them efficiently. I introduce these soft
counterparts in the context of an abstract data-type, namely a 
"soft-database". They may be useful to do explicit hypothetical 
resonning in Prolog.

Soft databases are sets of Prolog clauses having their 
modifications undone on backtracking exactly as if they 
were an ordinary Prolog data structure.
Soft databases contain chunks of executable Prolog code and/or
global "data".

Each one-argument operation on a soft-database is mapped in
a three-argument meta-logical operation using the following
transformation:

 operation(SoftClause) ---> operation(SoftClause,OldDb,NewDb) 

We can implement them simply by using the DCG preprocessor,
who can handle the last two arguments in a very nice way.

The basic idea is to meta-evaluate our program by a source-to-source 
transformation of most of the operations on the soft database, using 
a DCG driven term-expansion. On the other hand we must insure that 
predicates which do not use meta-evaluation have not to pay for. 

In the source file, clauses which use the soft database 
operations are represented by the functor "-->" as in DCG rules. 
This means that two additional arguments are appended at the end of 
each predicate-term (say Db1 and Db2), to simulate the state of the
database before and after the call. However, if the predicate-term
is a variable, (say X), this gives "phrase(X,Db1,Db2)" and X will 
be only expanded at run-time, when it becomes instantiated.

The "-->" functor may be interpreted declaratively
as a grammar based  specification of the problem, or 
procedurally as a description of soft data-base transformations.

The meta-circular evaluation mechanism works by chaining successive
states of the soft database. 

"Assume" and "assume_last" work like asserta and assertz,
"forget" works like retract, but all their effect is backtrackable.

Previously assumed code in the database can be activated by using "wake-up". 
The "{}" escape mechanism provides a call to Prolog without term-expansion. 
The predicates "demo" and "wake_up" implement the meta-circular evaluation
mechanism.

This mechanism works as if it were a conventional meta-interpreter but
it is far more efficient, as only a limited amount of interpretation 
is done (when compiled code explicitly uses wake-up).

In this case, the soft database is searched by "assumed" for a
matching clause, then "demo" is called and the body of the current
clause is traversed recursively, until a callable compiled predicate-term
is reached. 
Then control is transferred to compiled code, which works as fast
as it can.

If one of the predicates which interact with the database
is called, it effectively uses the 2 hidden arguments
corresponding to the state of the database.

Suppose, for example that in the source code "assume(C)" is called.
The DCG expansion done at compile time insures that
"assume(C,Db1,Db2)" will execute, and if "Db1" is the state of
the database before, then "Db2" will be the state of the database
after assuming C.

/*------------------------ cut here -----------------------------------*/

% main predicate
go :- make_empty_database(_,Db),readloop(Db,_).

% definition of soft database operations
make_empty_database(_,Db):-new(Db).	% the old database may be lost

assume(C)-->add_front(C).		% like 'asserta'

assume_last(C)-->add_end(C).		% like 'assertz'

forget(C)-->remove(C). % like retract, potentially non-monotonic operation

% retrieve a soft clause as data
assumed(C,D,D):-find(C,D).	% like 'clause', but does not copy

% activate a soft-clause as code
wake_up(H) -->			% works like 'call'
  assumed(T),{copy_term(T,C)},  
  ( {C = (H :- B)} -> demo(B)   % match a clause with a body
  ; {C = H}			% match a fact
  ).

% meta-circular interpreter
demo(C) --> 
  (  {C=(P,Q)}    -> (demo(P),demo(Q))
  ;  {C=(I->T;E)} -> (demo(I) -> demo(T) ; demo(E))
  ;  {C=(P;Q)}    -> (demo(P);demo(Q))
  ;  C
  ).

% hack for 'demo' to handle {} when evaluating itself
{C} --> {call(C)}.

getdb(Db,Db,Db). % gets the database as a term

setdb(Db,_,Db). % sets the database - the previous is lost

% failure driven printing of the assumed clauses
showdb -->
     {write('Soft database:'),nl},getdb(Db),{numbervars(Db,0,_)},
     assumed(C),{write(C),write('.'),nl,fail}
  ;  {true}. 

% simple read-eval loop
readloop -->
  {nl,write('<READ LOOP>'),nl,write('| ?- '),read(X)},
  ( X ->  {write('Yes.')}
  ; {write('No.')}
  ),{nl},readloop.

% difference list based implementation of
% soft database manipulation tools

% makes a new difference list
new(H:H).

% adds to the front of a difference list
add_front(X,H:T,[X|H]:T).

% adds to the end of a difference list
add_end(X,H:[X|T],H:T).

% finds an element of the difference list
find(X,H:T):- H\==T,
  ( H=[X|_]
  ; H=[_|H1],H1\==T,
    find(X,H1:T)
  ).

% removes an element of the difference list
remove(X,L1:T,R1:T):- L1\==T,
  ( L1=[X|R1]
  ; L1=[Y|L2],L2\==T,R1=[Y|R2],
    remove(X,L2:T,R2:T)
  ).

% As an example, let us consider the following solution
% to the N-queens problem:

queens(N) --> 
    make_free_positions(N),place(N),print_solution,{fail}
  ; {true}.

% makes a pool of free positions
make_free_positions(P) --> 
    {P>0} -> assume(free(P)),{P1 is P-1},make_free_positions(P1)
  ; {true} .

% try to place the queen Q
place(Q) --> 
    {Q>0} -> forget(free(P)),{S is Q+P, D is Q-P},% generate
             not_under_attack(S,D),		  % test
             {NewQ is Q-1},place(NewQ)
  ; {true}.

% tests if on diagonals S and D the queen is not under attack
% and assimilates the proposed position as the
% intersection of two diagonals
not_under_attack(S,D) -->
    assumed(on_diagonals(S1,D1)),
    { S=S1
    ; D=D1
    } -> {fail}
  ; assume(on_diagonals(S,D)). 

% calculates the position of each queen and prints it
print_solution -->
    assumed(on_diagonals(S,D)),{P is (S-D)//2,write(P),fail}
  ; {nl}.

The program was tested in Quintus Prolog 2.0, on a Sun 3.
To use it, type "go" and then something like "queens(8)" or
"assume(phone(mike,X)),assume(phone(mary,X)),showdb".

If soft databases will be of some interest on the net I will post some
less trivial applications to games and automated induction.

Paul Tarau
Technical Details
Message-ID: <510@mannix.iros1.UUCP>
Path: arc1-ca.pugleaf.net!utzoo!mnetor!uunet!husc6!bloom-beacon!mcgill-vision!iros1!ouareau!tarau
References: <1194@kulcs.kulcs.uucp> <777@cresswell.quintus.UUCP>

Show Full Headers
Complete Article Headers
Path: arc1-ca.pugleaf.net!utzoo!mnetor!uunet!husc6!bloom-beacon!mcgill-vision!iros1!ouareau!tarau
From: tarau@ouareau.iro.umontreal.ca (Paul Tarau)
Newsgroups: comp.lang.prolog
Subject: Re: back_retract
Message-ID: <510@mannix.iros1.UUCP>
Date: 5 Apr 88 16:13:32 GMT
References: <1194@kulcs.kulcs.uucp> <777@cresswell.quintus.UUCP>
Sender: news@iros1.UUCP
Reply-To: tarau@iros1.UUCP (Paul Tarau)
Organization: Universite de Montreal
Lines: 194
Keywords: backtrackable assert and retract, DCG term expansion
Summary: Soft databases