Search code examples
prologprolog-cut

Prolog: 'cut' in query vs. rules/facts


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.

Solution

  • 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.