95.307 - Programming Paradigms 1999

11  Cut


What's in This Set of Notes ?


11.1 How to Stop Prolog from Succeeding?

member(X, [X|_]).
member(X, [_|Y]) :- member(X,Y).

?- member(a, [a, b, c, a, b, c, a]).

    succeeds 3 times

Question: How to stop Prolog from continuing to succeed?

 

Answer: Use CUT !

CUT commits the system to every choice it has made since it chose the rule.

Reason:

  1. faster execution, don't spend time satisfying goals that can't or don't need to be satisfied
  2. fewer backtrack points need to be considered, therefore less memory required.


Example

client('bob').
client('dwight').

book_overdue('bob', book100).
book_overdue('dwight', book101).

general_facility(X) :- basic_facility(X).
general_facility(X) :- additional_facility(X).

additional_facility(borrowing).
additional_facility(inter_library_loan).

basic_facility(references).
basic_facility(enquires).

facility(Person, Facility) :-
        book_overdue(Person, Book),
        !,                         <------------------------------- If a person has any overdue book, he has access to only basic facility
        basic_facility(Facility).
facility(Person, Facility) :- general_facility(Facility).
 

?- client(X), facility(X,Y).


If a client is found to have an overdue book, then only allow the client the basic facilities of the library. Don't bother going through all the clients overdue books and don't consider any other rule about facilities.


11.2 Common Uses of Cut

  1. Tell Prolog it has found right rule for a goal. e.g., if you get this far, you picked the right rule.
  2. Tell Prolog where we want it to fail without trying alternatives (use ! with fail). e.g., if you get here, you should stop trying to satisfy the goal.
  3. Tell Prolog to terminate generation of backtracking. e.g., if you get here you found the only solution to the problem, so there is no point in looking for alternatives.

Case 1 Confirmation of Choice of Rule

sum_one_to(X,Y). The sum of numbers from 1 to X is Y

?- sum_one_to(5,Y).
        Y = 15;
        NO

sum_one_to(1,1) :- !.
sum_one_to(N, Result) :-
        N1 is N - 1,
        sum_one_to(N1, Result1),
        Result is Result1 + N.

Better Solution (Handle 0 or negative numbers)

sum_one_to(N,1) :- N =< 1, !.
sum_one_to(N, Result) :-
        N1 is N - 1,
        sum_one_to(N1, Result1),
        Result is Result1 + N.

General Principle

Non Cut Version

sum_one_to(1,1).
sum_one_to(N, Result) :-
        not (N = 1),
        N1 is N - 1,
        sum_one_to(N1, Result1),
        Result is Result1 + N.

Better Non Cut Version

sum_one_to(N,1) :- N =< 1.
sum_one_to(N, Result) :-
        not (N = <1),
        N1 is N - 1,
        sum_one_to(N1, Result1),
        Result is Result1 + N.

Limitation with NOT

A :- B, C.
A :- not(B), D.


Case 2 Cut-Fail Combination

1) average_taxpayer(X) :- foreigner(X), fail.
2) average_taxpayer(X) :- ....
average_taxpayer(X) :- foreigner(X), !, fail.
average_taxpayer(X) :-
        spouse(X,Y),
        gross_income(Y, Income),
        Income < 5000.
gross_income(X,Y) :-
        receives_pension(X, P),
        P < 5000,
        !,
        fail.


We can implement NOT in terms of cut and fail

not(P) :- call(P), !, fail.
not(P).


Case 3 Terminating a "Generate and Test"

Model Consider a tic-tac-toe board with the following label scheme:

aline([1,2,3]).
aline([4,5,6]).
aline([7,8,9]).
aline([1,4,7]).
aline([2,5,8]).
aline([3,6,9]).
aline([3,5,7]).
aline([1,5,9]).

//Is there a forced move (a square) on the board (list of elements)
forced_move(Board, Sq) :-
        aline(Squares),                                               <- ----- Generate
        threatening(Squares, Board, Sq),                    <- ------Test
        !.                                                                   <--------Fail, no need to look further, found answer, must make move
 

threatening([X,Y,Z], B, X) :-
        empty(X,B),
        cross(Y,B),
        cross(Z,B).

threatening([X,Y,Z], B, Y) :-
        empty(Y,B),
        cross(X,B),
        cross(Z,B).

threatening([X,Y,Z], B, Z) :-
        empty(Z,B),
        cross(X,B),
        cross(Y,B).

empty(Square, Board) :-
        arg(Square, Board, Value),
        var(Value).

cross(Square, Board) :-
        arg(Square, Board, Value),
        nonvar(Value),
        Value = x.

nought(Square, Board) :-
        arg(Square, Board, Value),
        nonvar(Value),
        Value = 0.
 

Note:

    ?- arg(2, related(john, mother(jane)), X).
                X = mother(jane).

    ?- arg(2, [a, b, c], X)
             X = [b,c].

    var(X) succeeds if X has no binding
    novar(X) succeeds if X has a value

    novar(X) :- var(X), !, fail.
    novar(_).



11.3 Cut Problems

append([],X,X) :- !.
append([A|B], C, [A|D]) :- append (B,C,D).

?- append([a,b,c], [d,e], X).  works ok
?- append([a,b,c], X, Y).  works ok

But

?- append(X, Y, [a,b,c]).
produces the solution  X = [], Y = [a,b,c]. but because of the cut we can get no more solutions, even though they exist.
  • What happens with the following:
  • number_of_parents(adam, 0) :- !.
    number_of_parents(eve, 0) :- !.
    number_of_parents(X, 2).
    So
     
    ?- number_of_parents(eve, X). gives X = 0
    ?- number_of_parents(john, X). gives X = 2
    ?- number_of_parents(eve, 2). gives YES
  • Better solution
  • number_of_parents(adam, N) :- !, N = 0.
    number_of_parents(eve, N) :- !. N = 0.
    number_of_parents(X, 2).


    However,
     

    ?- number_of_parents(X,Y). give only one solution with X = adam, N = 0.


    Moral of the Story

    If you introduce cuts to obtain correct behavior when the goals are of one form (e.g., a special sequence of variables), there is no guarantee that anything sensible will happen if goals of another form start appearing.



    11.4 Other Colors of Cuts

    Green Cuts

    minimum(X,Y,Z) -> the minimum of X and Y is Z.

    minimum(X,Y,X) :- X < Y, !.
    minimum(X,Y,Y) :- X > Y, !.


    Red Cuts

    minimum(X,Y,Z) -> the minimum of X and Y is Z.

    minimum(X,Y,X) :- X < Y, !.
    minimum(X,Y,Y) .               The explicit conditions governing the use of the rule are omitted.

    Problem ?- minimum(2,5,5) succeeds

    A standard Prolog programming technique using red cuts is the omission of explicit conditions. Knowledge of the behavior of Prolog, the order it uses rules in a program, is relied on to omit conditions that could be inferred to be true. This is sometimes essential in practical Prolog programming, since explicit conditions, especially negative ones, are cumbersome to specify, and inefficient to run. But making such omissions is error prone.