0

Doing exercise 10.4 on learnprolognow, could someone explain to me or help me visualize why for ?- p(X),p(Y) we get:
X=1,Y=1; X=1,Y=2; X=2, Y=1; X=2, Y=1. And not just X=1, Y=1; X=1, Y=2.

I think I'm misunderstanding how the cut happens, when it's in the ruleset instead of the query - because I think I can visualize it for ?- p(X),!,p(Y)., where it actually behaves as I thought the last one would...

Edit: From the website

% database
p(1).
p(2):-!.
p(3).

% Queries
p(X). % returns: X=1; X=2.
p(X),p(Y). % returns: X=1,Y=1; X=1, Y=1; X=2, Y=2. (?)
p(X),!,p(Y). % returns X=1, Y=1; X=1, Y=2.
false
  • 10,264
  • 13
  • 101
  • 209
user452306
  • 139
  • 4
  • 9
  • Read this: https://en.wikibooks.org/wiki/Prolog/Cuts_and_Negation – damianodamiano Jan 05 '18 at 15:43
  • Thanks, I already had a look at it, but I'm missing why for `p(X),p(Y)` it backtracks to change X's value ? – user452306 Jan 05 '18 at 16:08
  • 2
    `p(X), p(Y)` backtracks to `p(X)` because that's simply how Prolog works. The cut in between, `p(X), !, p(Y)` prevents backtracking to `p(X)`. In other words, once `p(X), p(Y)` succeeds, Prolog will backtrack and try to find another `Y` that also makes it succeed. Once those options are exhausted, it backtracks further to find another `X` to enable `p(X)` to succeed and then moves forward to `p(Y)` again (starting fresh from the `p/1` facts). – lurker Jan 05 '18 at 16:32
  • So the cut in the rules, doesn't prevent backtracking of the query? – user452306 Jan 05 '18 at 16:33
  • 1
    Yes, it does. Do you see `X = 3` or `Y = 3` in any of your solutions? You don't because of the `p(2) :- !.`. Prolog never gets to checking `p(3).`. – lurker Jan 05 '18 at 19:07

1 Answers1

1

To understand this problem you can imagine a tree with X in as first level and Y as second level (prolog uses sld resolution that can be well described with a tree). Consider this problem:

p(1).
p(2):-!.
p(3).

sol(X,Y):-
    p(X),
    p(Y).

I've added the predicate solve/2 to make it more clear. Run the query:

?- solve(X,Y).

First of all you have to choose the value for X. Prolog uses depth first search from the top to the bottom, from left to right. So it evaluates p(x): p(1) succeed (because is the first clause, if you write p(2) above p(1), p(2) will succeed) and so X = 1. Then evaluates p(Y): p(1) succeed and so you have the first solution:

X = Y, Y = 1.

If you click on more, then prolog does a backtrack (you can imagine this as a step above on the tree) and tries another value for p(Y). In this case p(2) succeed, the predicate is true and you get:

X = 1, Y = 2.

Now, if you click on more, due to the fact there is a cut (!) in the body of p(2) (a general rule in prolog has the form head :- body), prolog will not go more in depth and p(3) is ignored. So there's no more solution to p(Y). So there is another backtracking and this time, for p(X), p(2) succeed and X = 2 and for p(Y), p(1) succeed and you get:

X = 2, Y = 1.

If you click on more, you get:

X = Y, Y = 2.

Now, due to the fact there is a cut after p(2), there are no more solutions available for both X and Y (! cuts everything below p(2)).

If you remove the cut you get all the possible solutions:

X = Y, Y = 1
X = 1,
Y = 2
X = 1,
Y = 3
X = 2,
Y = 1
X = Y, Y = 2
X = 2,
Y = 3
X = 3,
Y = 1
X = 3,
Y = 2
X = Y, Y = 3

Keep in mind that the order of the clauses is important. If you write

p(2).
p(1):-!.
p(3).

You get

X = Y, Y = 2
X = 2,
Y = 1
X = 1,
Y = 2
X = Y, Y = 1

You can check this behaviour using the tracer. In SWI or SWISH you can write ?- trace, solve(X,Y).

If you have a situation like this:

p(1).
p(2).
p(3).

sol(X,Y):-
    p(X),
    !,
    p(Y).

prolog will tests all the possible values for Y and only one value for X because the cut stops the exploration of the tree (ideally you have 3 branches for X (1,2,3) and 3 for Y (1,2,3), ! cuts 2 and 3 from X) and you get:

X = Y, Y = 1
X = 1,
Y = 2
X = 1,
Y = 3

Sorry for the long post, hope to be clear.

damianodamiano
  • 2,528
  • 2
  • 13
  • 21
  • Thank you, but why does this: _So there is another backtracking and this time, for p(X),(...)_ happen? Shouldn't the cut stop all backtracking? Or does it only stop backtracking until _a certain level_ ? – user452306 Jan 05 '18 at 17:09
  • 1
    The cut, yes, stops the backtracking. In this case you test `p(1)` then `p(2)` for both `X` and `Y`. In this case `!` stops the backtracking only for the predicate `p/1`, not all the backtracking. – damianodamiano Jan 05 '18 at 17:49
  • 1
    That makes sense! Thank you very much :) – user452306 Jan 05 '18 at 18:04