prolog

What is the difference between once and cut in prolog?


once/1 can be defined as

once(Goal) :- Goal, !.

I am trying to understand the difference between those two examples that appeared in the discussion in SWI Prolog's docs:

Note that this is absolutely not the same as using "cut": [...]

Once-ify the first clause:

foo(A,B) :- once(member(A-B,[a-1,b-2,c-3,a-4])).
foo(A,B) :- member(A-B,[d-5,e-6,a-7,f-8]).

Then:

?- foo(a,B).
B = 1 ;
B = 7 ;
false.

But if you cut at the end of the first clause:

foo(A,B) :- member(A-B,[a-1,b-2,c-3,a-4]),!.
foo(A,B) :- member(A-B,[d-5,e-6,a-7,f-8]).

You get:

?- foo(a,B).
B = 1.

I'm not following what's the scope of !. I would assume that those two are the same, i.e. the cut propagates out of once, but this does not seem to be the case. Could someone explain how cut works in the once example?


Solution

  • Cut does not propagate out of the once/1. In fact, the once/1 definition:

    once(Goal) :-
        call(Goal), !.
    

    is exactly like that so that the !/0 doesn't get out.

    The cut in this once/1 definition works like this: whenever the call(Goal) succeeds for the first time (it might have failed 0, 1, or more times before that!), then the cut is reached, all pending choice points created by you Goal are discarded, and you succeed "once" and for all.