chomicki@topaz.UUCP (03/31/87)
I'm forwarding (with a permission from the originator) a message that has been circulating by E-mail for some time. It looks like an interesting challenge. %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% From: Francois-Michel Lang <lang%omega.PRC.Unisys.COM@cis.upenn.edu> Date: Tue, 17 Mar 87 17:05:54 EST A number of us at SDC have been experimenting with some Prolog control structures which we have so far been unable to implement cleanly--i.e., without using the database. Any ideas about how to hack either of these? 1. A predicate xor(G1,G2) which has the following behavior: First, goal G1 is called. If G1 succeeds, xor(G1,G2) succeeds, and can be re-satisfied on backtracking as often as G1 can be. When G1 can't be re-satisfied, xor fails. If G1 fails, G2 is called. If G2 succeeds, xor(G1,G2) succeeds, and can be re-satisfied as often as G2 can be. In other words, xor(G1,G2) produces -- all and only solutions of G1 if one exists, XOR -- all and only solutions of G2 if one exists The closest I've come to getting this one is my_xor(X,_) :- call(X), !, (true ; call(X)). my_xor(_,Y) :- call(Y). but the problem here is that backtracking into the first clause won't recognize the first solution to the goal X. 2. Given a predicate P :- A, B, C, D. Is it possible to rewrite P to obtain the following behavior: If the goal A initially succeeds, then if B doesn't succeed, backtrack into A. (This is quite normal.) However, if A initially succeeds, and B does too, then prevent backtracking into A. In other words, if a given solution of A allows B to be solved, then don't try to resatisfy A. But if a given solution of A does not allow B to be solved, then do try to resatisfy A. These structures may seem a bit odd, but we do have a good reason for wanting to use them! If either of you has any ideas about these structures, I'd love to hear about it, and so would the rest of us NL people! --Francois %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% Posted by: -- Jan Chomicki (chomicki@aramis.rutgers.edu) Phone: (201) 932-3999 Dept.of Computer Science, Rutgers University, New Brunswick, NJ 08903 Usenet: {...harvard,pyramid,seismo,nike}!rutgers!aramis!chomicki Arpanet: chomicki@aramis.rutgers.edu
debray@arizona.UUCP (04/02/87)
Regarding the probloems posed by chomicki@topaz.RUTGERS.EDU (Jan Chomicki): > 1. A predicate xor(G1,G2) which has the following behavior: > First, goal G1 is called. > If G1 succeeds, xor(G1,G2) succeeds, and can be re-satisfied > on backtracking as often as G1 can be. When G1 can't be > re-satisfied, xor fails. > If G1 fails, G2 is called. > If G2 succeeds, xor(G1,G2) succeeds, and can be re-satisfied > as often as G2 can be. > Assuming no side effects, the following works on most Prologs: xor(G1,G2) :- not(not(call(G1))) -> call(G1) ; call(G2). > 2. Given a predicate P :- A, B, C, D. > > Is it possible to rewrite P to obtain the following behavior: > If the goal A initially succeeds, then > if B doesn't succeed, backtrack into A. (This is quite normal.) > However, if A initially succeeds, and B does too, > then prevent backtracking into A. > Again, assuming no side effects, the following will work on most Prologs: P :- ( not(not( (A,B) )) -> (A -> B) ; (A, B) ), C, D. A comment in passing regarding these solutions and something the original poster said: > A number of us at SDC have been experimenting with some Prolog > control structures which we have so far been unable to implement > cleanly--i.e., without using the database. Apparently a lot of people have a tendency to resort to "assert" or "record" at the drop of a hat (I can see complaints that my solutions above compute the first solution twice: once in the satisfiability test not(not( ... )), once in the actual call). What's often ignored is that "assert" and "record" are *expensive*, and may end up being costlier than recomputing the solutions. I, personally, have found only two or three situations where I'd say "assert" was really necessary. I think a little more care in coding could in most cases result in code which, in avoiding asserts, would improve in speed, understandability _and_ portability. -- Saumya Debray CS Department, University of Arizona, Tucson internet: debray@arizona.edu uucp: {allegra, cmcl2, ihnp4} !arizona!debray
debray@arizona.UUCP (04/02/87)
I'd proposed the following solutions to Chomicki's problems: > Assuming no side effects, the following works on most Prologs: > > (1) xor(G1,G2) :- not(not(call(G1))) -> call(G1) ; call(G2). > > (2) P :- ( not(not( (A,B) )) -> (A -> B) ; (A, B) ), C, D. > The following simplifications should now be obvious: (1) xor(X,Y) :- not(call(X)) -> call(Y) ; call(X). (2) P :- ( not( (A -> B) ) -> (A, B) ; (A -> B) ), C, D. -- Saumya Debray CS Department, University of Arizona, Tucson internet: debray@arizona.edu uucp: {allegra, cmcl2, ihnp4} !arizona!debray
lee@mulga.UUCP (04/02/87)
In article <10588@topaz.RUTGERS.EDU> chomicki@topaz.RUTGERS.EDU (Jan Chomicki) writes: >From: Francois-Michel Lang <lang%omega.PRC.Unisys.COM@cis.upenn.edu> >Any ideas about how to hack either of these? > >1. A predicate xor(G1,G2) You need "soft cut" for this: xor(G1, _) :- call(G1), $soft_cut. % (this is what its called in MU-Prolog) xor(_, G2) :- call(G2). When soft cut is called, it removes the choice point of the parent call (xor in this case) but no others. It is pretty easy to implement this when you are writing a Prolog system, but difficult afterwards. It is not a standard construct and has no standard name. I'm not sure how many Prolog systems have it. It is useful for a version of "if" which can return multiple answers to the condition (see L. Naish, "Negation and Quantifiers in NU-Prolog", 3rd ICLP, 1986). For this reason, NU-Prolog and recent versions of MU-Prolog have soft cut implemented internally. I guess LM-Prolog does also, since it has a retrying version of "if". >2. Given a predicate > > P :- A, B, C, D. > > Is it possible to rewrite P to obtain the following behavior: > If the goal A initially succeeds, then > if B doesn't succeed, backtrack into A. (This is quite normal.) > However, if A initially succeeds, and B does too, > then prevent backtracking into A. P :- A, B, !, C, D. % if there are no more clauses, or P :- once (A, B), C, D. % if you Prolog system has once/1 Does what you said. It also prevents multiple solutions to B, which you may want (this was not said in the original posting). If you dont want to remove choice point from the execution of B, I cant think of how to do it in Prolog systems I know of (ancestor cut is not flexible enough). Lee Naish CSNET: lee@mulga.oz ARPA: munnari!lee@seismo UUCP: {seismo,mcvax,ukc,prlb2,ubc-vision}!munnari!lee {decvax,vax135,eagle,pesnta}!mulga!lee
wagner@iaoobelix.UUCP (04/02/87)
/***** iaoobelix:comp.lang.pro / topaz!chomicki / 5:28 pm Mar 31, 1987*/ > In other words, xor(G1,G2) produces > -- all and only solutions of G1 if one exists, XOR > -- all and only solutions of G2 if one exists ... > Jan Chomicki (chomicki@aramis.rutgers.edu) Phone: (201) 932-3999 > Dept.of Computer Science, Rutgers University, New Brunswick, NJ 08903 > Usenet: {...harvard,pyramid,seismo,nike}!rutgers!aramis!chomicki > Arpanet: chomicki@aramis.rutgers.edu The above `xor' predicate seems to be identical with something called `default(X,Y)' (if I remember it right) available in Prolog-II. `default(X,Y)' behaves like X if there is at least one solution for X, otherwise it behaves like Y. There are some other Prologs implementing this (e.g I saw one on the Macintosh). Juergen Wagner, USENET: ...seismo!unido!iaoobel!wagner ("Gandalf") or ...!pyramid!iaoobel!wagner Mail: Juergen Wagner Fraunhofer-Institut IAO Rosenbergstr. 28 D-7000 Stuttgart 1 Federal Republic of Germany Phone: + 49 711 6648-205
jo@epistemi.UUCP (04/02/87)
In article <10588@topaz.RUTGERS.EDU> chomicki@topaz.RUTGERS.EDU (Jan Chomicki) quotes Francois-Michel Lang <lang%omega.PRC.Unisys.COM@cis.upenn.edu>: > In other words, xor(G1,G2) produces > -- all and only solutions of G1 if one exists, XOR > -- all and only solutions of G2 if one exists > > The closest I've come to getting this one is > > my_xor(X,_) :- > call(X), !, > (true ; call(X)). > my_xor(_,Y) :- call(Y). > > but the problem here is that backtracking into the first clause > won't recognize the first solution to the goal X. > The problem is worse than that. Any instantiations which take place in the first call(X) will persist after the cut. These may be incompatible with other solutions of X. A better version might be: my_xor(P, _) :- \+( \+( P )), !, call(P). my_xor(_, P) :- call(P). In this case, any instantiations within the goal P will be undone by the ``double negation''. Alternatively one can copy the first goal, to ensure that no variable in P is instantiated as a result of determining that the goal has at least one solution; i.e. replace the first clause by my_xor(P, _) :- copy(P, NewP), call(NewP), !, call(P). where copy/2 could be copy(X, _) :- assert(foo(X)), fail. copy(_, Y) :- retract(foo(Y)). Incidentally, prolog II has a built in procedure called, I think, default/2, whose meaning is identical to my version of my_xor. The manual makes the mistake of claiming that ``default'' can't be implemented using the control structures of prolog. The example here shows that it can be done, although somewhat inefficiently. > >2. Given a predicate > > P :- A, B, C, D. > > Is it possible to rewrite P to obtain the following behavior: > If the goal A initially succeeds, then > if B doesn't succeed, backtrack into A. (This is quite normal.) > However, if A initially succeeds, and B does too, > then prevent backtracking into A. > > In other words, if a given solution of A allows B to be solved, > then don't try to resatisfy A. > But if a given solution of A does not allow B to be solved, > then do try to resatisfy A. The answer, due to Henk Zeevat (...!epistemi!henk), is very similar to the last problem. Basically we need to know that B has a solution, for a given solution to A, but we don't in the first instance care what the solution to B is. So: P :- A, \+( \+( B)), !, B, C, D. or equivalently P :- copy(B, NewB), A, NewB, !, B, C, D. Again, this suffers from a lack of efficiency, but seems to capture the behaviour that you want. Regards, Jo -- ---------------------------------------------------------------------- Jonathan Calder, University of Edinburgh, Centre for Cognitive Science, 2 Buccleuch Place, Edinburgh, EH8 9LW, Scotland (+44) 31 667 1011 x6257 UUCP: ...!ukc!cstvax!epistemi!jo ARPA: jo%epistemi.ed.ac.uk@ucl.cs JANET: jo@ed.epistemi.ac.uk Why don't we get together and form an institute
micha@ecrcvax.UUCP (04/02/87)
Both of the required predicates can be defined using one predicate succeeds(X) that succeeds if X does, without binding variables and leaving choice points (if X has some side effects, I doubt you can define it without assert). The xor(A, B) can be defined as xor(A, _) :- succeeds(A), !, A. xor(_, B) :- B. and the other one P :- A, succeeds(B), !, B, C, D. Not to forget the definition of succeeds/1: succeeds(X) :- not(not(X)). or alternatively succeeds(X) :- replace_vars(X, Y), Y, !. where replace_vars(X, Y) makes a copy of X with fresh variables. This has, of course the disadvantage that the predicate is called twice, first in succeeds(X) and then the proper call, the problem is similar to writing setof/3 in pure Prolog - you can make it but it is not very efficient. --Micha
schaefer@ogcvax.UUCP (04/03/87)
In article <ecrcvax.275> mcvax!unido!ecrcvax!micha (Micha Meier) writes: >[...] >Not to forget the definition of succeeds/1: > succeeds(X) :- not(not(X)). >or alternatively > succeeds(X) :- replace_vars(X, Y), Y, !. >where replace_vars(X, Y) makes a copy of X with fresh variables. >[...] >--Micha The replace_vars predicate can be implemented as: replace_vars(X,Y) :- copy1(X,Y,T), !. copy1(X,Y,Tbl) :- var(X), member((X,Y),Tbl). copy1(X,X,Tbl) :- atomic(X). copy1([H|T],[H1|T1],Tbl) :- copy1(H,H1,Tbl), copy1(T,T1,Tbl). copy1((F,S),(F1,S1),Tbl) :- copy1(F,F1,Tbl), copy1(S,S1,Tbl). copy1(X,Y,Tbl) :- not atomic(X), X =.. [F|R], copy1(R,R1,Tbl), Y =.. [F|R1]. member((X,Y),[(X1,Y)|R]) :- X == X1. member(E,[E1|R]) :- var(E1), E1 = E. member(E,[_|R]) :- member(E,R). -- Bart Schaefer Dept. of CS&E CSNET: schaefer@Oregon-Grad Oregon Graduate Center UUCP: {ihnp4,seismo,sun}!verdix \ 19600 NW Von Neumann Dr {hplabs,ucbvax,decvax}!tektronix !ogcvax!schaefer Beaverton, OR 97006
bd@zyx.UUCP (04/04/87)
In article <10588@topaz.RUTGERS.EDU> chomicki@topaz.RUTGERS.EDU (Jan Chomicki) writes: >From: Francois-Michel Lang <lang%omega.PRC.Unisys.COM@cis.upenn.edu> > >Any ideas about how to hack either of these? > >1. A predicate xor(G1,G2) which has the following behavior: > (...) A "real" exclusive-or should be equivalent to: G1, not(G2) ; not(G1), G2. The xor defined in the question is rather like: G1 ; \+ G1, G2. So one solution is: xor(G1,G2) :- G1 ; \+ G1, G2. This has the disadvantage that G1 is called twice. A more efficient solution is possible if (as Lee Naish pointed out) you have a retrying "if": xor(G1,G2) :- if(G1, true, G2). % if G1 then true else G2 >2. Given a predicate > > P :- A, B, C, D. > > Is it possible to rewrite P to obtain the following behavior: > (...) if a given solution of A allows B to be solved, > then don't try to resatisfy A. > But if a given solution of A does not allow B to be solved, > then do try to resatisfy A. Assuming that P has no more clauses, a possible solution might be: P :- A(X), B(X,_), !, B(X,Y), C, D. The variable X represents the solutions of A. Here too we have the disadvantage that B is called twice. We also have the requirement that the variables that represent the solutions of B for a given solution of A are easily identifiable. This may or may not be the case. -- Bjorn Danielsson, ZYX, +46 8 653205, ...mcvax!enea!zyx!bd