Prolog provides a somewhat controversial mechanism to control backtracking. If we insert an exclamation mark ! in the set of premises, backtracking will not cross the exclamation mark.
For instance, if we rewrote path above as
path(X,Y) :- edge(X,Z),!, path(Z,Y).
then for each X, it would try path(Z,Y) only once, for the first edge of the form edge(X,Z). If there is no path using this value of Z it will not backtrack to another edge involving X--the overall search just fails.
Cut is useful for optimizing Prolog code to avoid unnecessary backtracking. Suppose we want to program the ternary predicate ifthenelse(B,X,Y) that evaluates X if B is true (that is, if the goal B succeeds) and evaluates Y otherwise. A first attempt would be to write.
ifthenelse(B,X,Y) :- B,X. ifthenelse(B,X,Y) :- not(B),Y.
We have to look at Prolog backtracking a little more to explain this code. If the first rule fails for a goal, Prolog will try alternative rules before backtracking along the first rule. Thus, if we omit the not(B) in the second rule, we have a problem. If B succeeds and X fails, Prolog backtracking would directly try to satisfy Y. By placing a guard not(B) on the second clause, we ensure that this does not happen. However, evaluating this guard involves an unnecessary computation because, if we reach this stage, we have already evaluated B so we already ``know'' the result not(B).
A second source of inefficiency in this definition lies in the first rule. Suppose that B succeeds and X fails. We should then conclude that the ifthenelse(B,X,Y) fails. However, if there are multiple ways of satisfying B, Prolog backtracking will retry X for all of these options before abandoning the original goal!
We can fix both these inefficiencies by introducing a cut in the first rule.
ifthenelse(B,X,Y) :- B,!,X. ifthenelse(B,X,Y) :- Y.
The cut has two effects. First, as we mentioned earlier, it prevents backtracking to B. Second, it discards all future alternative rules for this goal. Thus, even if X fails, the cut ensures that Y is not tried. On the other hand, if B fails, which is before the cut, normal backtracking takes Prolog to the second rule and tries Y without wastefully computing not(B) as in the previous version.
In more complex context, it is quite difficult to predict the portion of the search tree that is pruned by such a cut, so this feature is to be used with caution.