Prolog order of unification

2019-08-21 03:10发布

问题:

I am just starting to try and teach myself Prolog, using both Seven Languages in Seven Weeks and Learn Prolog Now! and I ran into something confusing. On day one of SLSW it presents the following knowledge base:

cat(lion).
cat(tiger).

dorothy(X,Y,Z):- X = lion, Y = tiger, Z = bear.
twin_cats(X,Y):- cat(X), cat(Y).

which when queried with twin_cats(X,Y). it produces all of the possibilities for two cats, including X = lion, Y = lion and X = tiger, Y = tiger. Messing around I was trying to add a goal to the twin_cats(X,Y) rule so that it would only report two, distinct cats. At first I tried

cat(lion).
cat(tiger).

dorothy(X,Y,Z):- X = lion, Y = tiger, Z = bear.
twin_cats(X,Y):- \+(X = Y), cat(X), cat(Y). 

To which Prolog responded no to twin_cats(X,Y). However, when i just switched the order of the goals to

cat(lion).
cat(tiger).

dorothy(X,Y,Z):- X = lion, Y = tiger, Z = bear.
twin_cats(X,Y):- cat(X), cat(Y), \+(X = Y).

Prolog now gives me what I was looking for, two answers X = lion, Y = tiger and X = tiger, Y = lion.

So this is my question, is there some inherent ordering in the way that Prolog unifies variables? It would seem there is because the order of the goals (even though they are all seperated by and) changes the outcome of the query. In the second, working KB, i think I know how it is unifying. First it unifies for a cat(X) then cat(Y) then checks if they are the same. If they are it moves onto the next combo, etc, until there are no combos left and report the two cases that passed all three goals. But for the first KB, when it checks if X = Y first, shouldn't this goal be true because the variables haven't been unified yet?

回答1:

So this is my question, is there some inherent ordering in the way that Prolog unifies variables?

Yes, prolog tries to unify in the order you wrote it (and fails if not able to):

clause:-
  <unifies first>,
  <unifies second>…

You especially can see that after trace. (this is in swi), the variable names are replaced by internal names:

[trace] ?- twin_cats(X,Y).

true.

   Call: (6) twin_cats(_G970, _G971) ? creep
   Call: (7) cat(_G970) ? creep
   Exit: (7) cat(lion) ? creep
   Call: (7) cat(_G971) ? creep
   Exit: (7) cat(lion) ? creep
   Call: (7) lion=lion ? creep
   Exit: (7) lion=lion ? creep
   Redo: (7) cat(_G971) ? creep
   Exit: (7) cat(tiger) ? creep
   Call: (7) lion=tiger ? creep
   Fail: (7) lion=tiger ? creep
   Redo: (6) twin_cats(lion, tiger) ? creep
   Exit: (6) twin_cats(lion, tiger) ? creep
X = lion,
Y = tiger ;   % first redo
   Redo: (7) cat(_G970) ? creep
   Exit: (7) cat(tiger) ? creep
   Call: (7) cat(_G971) ? creep
   Exit: (7) cat(lion) ? creep
   Call: (7) tiger=lion ? creep
   Fail: (7) tiger=lion ? creep
   Redo: (6) twin_cats(tiger, lion) ? creep
   Exit: (6) twin_cats(tiger, lion) ? creep
X = tiger,
Y = lion ;   % second redo
   Redo: (7) cat(_G971) ? creep
   Exit: (7) cat(tiger) ? creep
   Call: (7) tiger=tiger ? creep
   Exit: (7) tiger=tiger ? creep
   Fail: (6) twin_cats(_G970, _G971) ? 
false.

Furhermore, \+ doesn't unifiy. It also does not set any constraints on the variables how they may be unified later. it just calls what comes after and fails if it yields true.



标签: prolog