Presentation is loading. Please wait.

Presentation is loading. Please wait.

Prolog: Control, Negation and Cut

Similar presentations


Presentation on theme: "Prolog: Control, Negation and Cut"— Presentation transcript:

1 Prolog: Control, Negation and Cut
Backtracking The cut operator ! Negation-as-Failure not

2 Backtracking and Nondeterminism
member(X, [X|_]). % _ is an anonymous variable member(X, [_|T]) :- member(X, T). ?- member(fred, [john, fred, paul, fred]). yes Deterministic query ?- member(X, [john, fred, paul, fred]). X = john; X = fred; Nondeterministic query X = paul; X = fred; no

3 The Problem of Controlling Backtracking
color(cherry, red). color(banana, yellow). color(apple, red). color(apple, green). color(orange, orange). color(X, unknown). ?- color(banana, X). X = yellow ?- color(physalis, X). X = unknown ?- color(cherry, X). X = red; X = unknown; no

4 The CUT The cut is a built-in predicate written as !
The cut always succeeds When backtracking over a cut, the goal that caused the current procedure to be used fails Not used for its logical properties, but to control backtracking.

5 How CUT works Suppose goal H is called, and has two clauses:
H1 :- B1,… Bi, !, Bk,… Bm. H2 :- Bn,… Bp. If H1 matches goals B1...Bi are attempted and may backtrack among themselves If B1 fails, H2 will be attempted But as soon as ! is crossed, Prolog commits to the current choice. All other choices are discarded.

6 Commitment to the Clause
H1 :- B1,… Bi, !, Bk,… Bm. H2 :- Bn,… Bp. Goals Bk...Bm may backtrack amongst themselves, but If goal Bk fails, then the predicate fails and the subsequent clauses are not matched

7 How to Remember Cut Think of the ‘cut’ as a ‘fence’
When crossed it always succeeds, And asserts a commitment to the current solution. When a failure tries to cross the fence, the entire goal is failed. commit H1 :- B1, B2, ..., Bi, !, Bk, ..., Bm. fail calling goal

8 Uses of Cut: Deterministic Predicates
To define a deterministic (functional) predicate. Example: a deterministic version of member, which is more efficient for doing ‘member checking’ because it doesn't need to give multiple solutions: membercheck(X, [X|_]) :- !. membercheck(X, [_|L]) :- membercheck(X, L). ?- membercheck(fred, [joe, john, fred, paul]). yes. ?- membercheck(X, [a, b, c]). X = a; no.

9 Uses of Cut: Specify Exclusion Cases
To specify exclusion of cases by ‘committing’ to the current choice. Example: max(X, Y, Z) makes Z maximum of X and Y max(X, Y, X) :- X >= Y. max(X, Y, Y) :- X < Y. Note: Both clauses are logically correct statement Using cut can get rid of the second test: max(X, Y, X) :- X >= Y, !. max(X, Y, Y).

10 Max with Cut max(X, Y, X) :- X >= Y, !. max(X, Y, Y).
If max is called with X ≥ Y, the 1st clause succeeds, and because of ! the 2nd clause isn't called The advantage The test isn't made twice if X<Y The disadvantage Each rule isn't a logically correct statement about the predicate. To see why this is a problem, try ?- max(10, 0, 0).

11 Max with Cut It is better to use ! and both tests:
max(X, Y, X) :- X >= Y, !. max(X, Y, Y) :- X < Y. This backtracks correctly and avoids unnecessary tests Or, if order of clause may change: max(X, Y, X) :- X >= Y, ! max(X, Y, Y) :- X < Y, !.

12 A Larger Example Several versions of the disjoint partial map split:
split(list of integers, non-negatives, negatives). Without cut: split([], [], []). split([H|T], [H|Z], R) :- H >= 0, split(T, Z, R). split([H|T], R, [H|Z]) :- H < 0, split(T, R, Z). Good code Each clause stands on its own as a logical statement Inefficient because choice points are retained. Only the 1st solution is needed, but we don't ignore backtracked solutions

13 A Larger Example (cont.)
A version using cut. Most efficient, but not the best ‘defensively written’ code. The third clause does not stand on its own as a fact about the problem. As in normal programming languages, it needs to be read in context. This style is often seen in practice, but is discouraged. split([], [], []). split([H|T], [H|Z], R) :- H >= 0, !, split(T, Z, R). split([H|T], R, [H|Z]) :- split(T, R, Z). Minor modifications may have unintended effects E.g., adding more clauses Backtracked solutions are invalid

14 A Larger Example (cont. 2)
A version using cut which is also ‘safe’. The only inefficiency is that the goal H < 0 will be executed unnecessarily whenever H < 0 split([], [], []). split([H|T], [H|Z], R) :- H >= 0, !, split(T, Z, R). split([H|T], R, [H|Z]) :- H < 0, split(T, R, Z). Recommended for practical use. Hidden problem: the third clause does not capture the idea that H < 0 is a committal. Here committal is the default because H < 0 is in the last clause. Some new compilers detect that H < 0 redundant.

15 A Larger Example (cont. 3)
A version with unnecessary cuts split([], [], []) :- !. split([H|T], [H|Z], R) :- H >= 0, !, split(T, Z, R). split([H|T], R, [H|Z]) :- H < 0, !, split(T, R, Z). First cut is unnecessary because anything matching first clause will not match anything else anyway Most Prolog compilers can detect this Why is the third cut unnecessary? Because H < 0 is in the last clause. Whether or not H < 0 fails, there are no choices left for the caller of split. However, the above will work for any order of clauses.

16 Mapping with Cut Remember squint? Maps integers to their squares, map anything else to itself: squint([], []). squint([X|T], [Y|L]) :- integer(X), !, Y is X * X, squint(T, L). squint([X|T], [X|L]) :- squint(T, L).

17 More Mapping with Cut Extract the even numbers...
evens([], []). evens([X|T], [X|L]) :- 0 is X mod 2, !, evens(T, L). evens([X|T], L) :- evens(T, L). Extract the unique members... uniques([], []). uniques([X|T], L) :- member(X, T), !, uniques(T,L). uniques([X|T], [X|L]) :- uniques(T,L).

18 Negation-as-Failure Using cut together with the built-in predicate fail defines a kind of negation. Examples: Mary likes any animals except reptiles: likes(mary,X) :- reptile(X), !, fail. likes(mary,X) :- animal(X). A utility predicate meaning something like “not equals”: different(X, X) :- !, fail. different(_, _).

19 Negation-as-Failure: not
We can use the idea of “cut fail” to define the predicate not, which takes a term as an argument not “calls” the term, evaluating as if it was a goal: not(G) fails if G succeeds not(G) succeeds if G does not succeed. In Prolog, not(G) :- call(G), !, fail. not(_). call is a built-in predicate.

20 Negation-as-Failure: not (cont.)
Most Prolog systems have a built-in predicate not. SICStus Prolog calls it \+. not does not correspond to logical negation, because it is based on the success/failure of goals. It can, however, be useful likes(mary, X) :- not(reptile(X)). different(X, Y) :- not(X = Y).

21 Misleading Negation-as-Failure
A student who missed some Prolog lectures was commissioned to write a Police Database system in Prolog The database held the names of members of the public, marked by whether they are innocent or guilty of some offence Suppose the database contains the following: innocent(peter_pan). innocent(X) :- occupation(X, nun). innocent(winnie_the_pooh). innocent(julie_andrews) guilty(X) :- occupation(X, thief). guilty(joe_bloggs). guilty(rolf_harris). Consider the following dialogue: ?- innocent(st_francis). no.

22 Problem – No may not mean False
This can't be right – we know that St. Francis is innocent Why does this happen? Prolog produces no, because st_francis is not in the database The user will believe it because the computer says so and the database is hidden from the user How to solve this?

23 not Makes Things Worse Using not doesn't help
guilty(X) :- not(innocent(X)). This makes matters even worse ?- guilty(st_francis). yes It is one thing to show that st_francis cannot be demonstrated to be innocent, but it is very bad to incorrectly show that he is guilty

24 Negation-as-Failure can be Non-Logical
More subtle than the innocent/guilty problem, not can lead to some extremely obscure programming errors. An example using a restaurant database good_standard(goedels). good_standard(hilberts). expensive(goedels). reasonable(R) :- not(expensive(R)). Consider query: ?- good_standard(X), reasonable(X). X = hilberts But let's ask the logically equivalent question: ?- reasonable(X), good_standard(X). no.

25 Why Different Answers? Why different answers for logically equivalent queries? ?- good_standard(X), reasonable(X). ?- reasonable(X), good_standard(X). In the 1st query, X is always instantiated when reasonable(X) is executed In the 2nd query, X is not instantiated when reasonable(X) is executed The semantics of reasonable(X) differ depending on whether its argument is instantiated!

26 Bad Programming Practice
It is bad to write programs that destroy the correspondence between the logical and procedural meaning of a program without any good reason Negation-as-failure does not correspond to logical negation, and so requires special care

27 How to fix it? One way is to specify that
Negation of a non-ground formula is undefined A formula is ground if is has no unbound variables Some Prolog systems issue a run-time exception when a non-ground goal is negated


Download ppt "Prolog: Control, Negation and Cut"

Similar presentations


Ads by Google