% Experimenting with the use of the ``cut'' predicate % (after Sterling and Shapiro ``The Art of Prolog'') % GREEN CUTS % ---------- % minimum(+Number1, +Number2, ?Minimum). % Succeeds if Minimum is the minimum value of Number1 and Number2. minimum(X, Y, X):- X =< Y. minimum(X, Y, Y):- X > Y. /* The potential search tree for the goal minimum(1,2,X) is: minimum(1, 2, X) / \ / \ minimum(1, 2, 1) minimum(1, 2, 2) / \ / \ / \ 1 =< 2 1 > 2 */ % gc_minimum(+Number1, +Number2, ?Minimum). % Identical to minimum/3 but with a cut in the first clause. gc_minimum(X, Y, X):- X =< Y, !. gc_minimum(X, Y, Y):- X > Y. /* The potential search tree for the goal minimum(1,2,X) is: minimum(1, 2, X) / \ / *<--- This branch is pruned from the tree / \ once the ! has been reached. minimum(1, 2, 1) minimum(1, 2, 2) / \ / \ / \ 1 =< 2, ! 1 > 2 / / ! */ % Another "green cut" example: % delete(+List, +Item, ?Result). % Removes all instances of Item from List, returning the Result. delete([H|T], H, Result):- !, delete(T, H, Result). delete([H|T], X, [H|Rest]):- \+ H = X, !, delete(T, X, Rest). delete([], _, []). % RED CUTS % -------- % rc_minimum(+Number1, +Number2, ?Minimum). % Like to gc_minimum/3 but with the check in the second clause % left out, because the programmer thinks it isn't needed (given % that Y should be the chosen minimum if X isn't). rc_minimum(X, Y, X):- X =< Y, !. rc_minimum(_, Y, Y). /* The potential search tree for the goal minimum(1,2,X) is: minimum(1, 2, X) / \ / *<--- This branch is pruned from the tree / \ once the ! has been reached. minimum(1, 2, 1) minimum(1, 2, 2) / / / 1 =< 2, ! / / ! **BUT** There is a major flaw in this reasoning, as shown by calling the goal minimum(1, 2, 2), which will (erroneously) succeed. i.e. The intended meaning of minimum has been lost. */ % Another "red cut" example: % rc_delete(+List, +Item, ?Result). % Like delete/3, but omits the non-unification check in the second % clause. This makes the meaning of the code more obscure. rc_delete([H|T], H, Result):- !, rc_delete(T, H, Result). rc_delete([H|T], X, [H|Rest]):- !, rc_delete(T, X, Rest). rc_delete([], _, []). % USING CUTS TO SPECIFY DEFAULTS % ------------------------------ pension(X, invalid_pension):- invalid(X), !. pension(X, old_age_pension):- over_65(X), paid_up(X), !. pension(X, supplementary_benefit):- over_65(X), !. pension(_, nothing). % This is the default if none of the above succeed. invalid(fred). over_65(fred). over_65(joe). over_65(jim). paid_up(fred). paid_up(joe). /* **BUT** This doesn't work in certain cases. For example: the query pension(fred,nothing) would succeed. */ entitlement(X, Y):- possible_pension(X, Y). entitlement(X, nothing):- \+ possible_pension(X, _). possible_pension(X, invalid_pension):- invalid(X). possible_pension(X, old_age_pension):- over_65(X), paid_up(X). possible_pension(X, supplementary_benefit):- over_65(X). /* This is a bit better, and the query entitlement(fred, nothing) will now succeed. */ % CUT AND FAIL % ------------ % not(+X). % Succeeds if X fails. not(X):- X, !, fail. not(_).