PROLOG clauses can be interpreted on two levels:
Prolog predicates can be used in many ways depending on which arguments
are given and which are variable.
For example, consider the predicate
append(A,B,AB)
which is true if
AB
is the list resulting from appending lists A
and B
together.
append([], B, B). append([A1 | MoreAs], B, [A1 | X]) :- append(MoreAs, B, X).
This can be used in eight different ways.
| ?- append([a, b], [1, [e]], [a, b, 1, [e]]). yes
| ?- append([a, b], [1, [e]], X). X = [a,b,1,[e]]
| ?- append([a, b], X, [a, b, 1, [e]]). X = [1,[e]]
| ?- append(X, [1, [e]], [a, b, 1, [e]]). X = [a,b]
| ?- append(X, Y, [a, b, 1, [e]]). X = [] Y = [a,b,1,[e]] ? ; X = [a] Y = [b,1,[e]] ? ; X = [a,b] Y = [1,[e]] ? ; X = [a,b,1] Y = [[e]] ? ; X = [a,b,1,[e]] Y = [] ? ; no
| ?- append([a, b], X, Y). Y = [a,b|X]
| ?- append(X, [1,[e]], Y). X = [] Y = [1,[e]] ? ; X = [_A] Y = [_A,1,[e]] ? ; X = [_A,_B] Y = [_A,_B,1,[e]]
| ?- append(X, Y, Z). X = [], Z = Y ? ; X = [_A], Z = [_A|Y] ? ; X = [_A,_B], Z = [_A,_B|Y] ? ; X = [_A,_B,_C], Z = [_A,_B,_C|Y]
zero
denote 0.
succ(N)
denote $N + 1$.
sum(M, zero, M). sum(M, succ(N), succ(S)) :- sum(M, N, S). | ?- sum(succ(succ(zero)), succ(succ(succ(zero))), N). N = succ(succ(succ(succ(succ(zero))))) product(M, zero, zero). product(M, succ(N), P) :- sum(P1, M, P), product(M, N, P1). | ?- product(succ(succ(zero)), succ(succ(succ(zero))), N). N = succ(succ(succ(succ(succ(succ(zero)))))) ? ;
Neat, multipurpose, very inefficient.
sum(A, 7, 14)
: Solve A+7=14, i.e., subtract.
product(A, 2, 4)
: Solve A*2=4, i.e., divide.
sum(A, B, 4)
: Solve A+B = 4. Can generate all possibilities.
product(A, B, 4)
: Solve A*B = 4. Can generate all possibilities.
A built-in expression evaluator used with the is
predicate.
sqr(X, Z) :- Z is X * X. | ?- sqr(3, Y). Y = 9Limitation: All variables on RHS of
is
must have values.
?- sqr(Z, 49). {INSTANTIATION ERROR: _36 is _34*_34 - arg 2}
!
is a special PROLOG facility
called the cut.
Cuts may be inserted anywhere within a clause to prevent backtracking to previous subgoals, e.g,
p(X) :- b(X), c(X), !, d(X), e(X).Suppose that this clause has been invoked with a goal matching
p(X)
and the subgoals b(X)
and c(X)
have been satisfied.
On encountering the cut:
d(X)
and e(X)
.
d(X)
and e(X)
do not succeed and backtracking
returns to the cut, then the backtracking process will immediately
terminate and the goal which matched p(X)
fails.
In essence, the cut freezes the decisions made in satisfying the parent goal:
b(X)
and c(X)
are first satisfied.
b(X)
or c(X)
and no attempt to use any other clause to satisfy p(X)
.
The cut can be used to mean "succeed with this; there aren't any other alternatives." For example, the member function:
member(X,[X|Y]). member(X,[Y|Z]) :- member(X, Z).Consider the goal
X is 4 + 1, member(X, [5, 9, 24, 17, 5, 2]), X < 4.
member(5, [5, 9, 24, ..., 5, 2])
succeeds, but then
5 < 4
fails.
member(5, [9, 24, ..., 5, 2])
,
working down the list.
member(5, [5, 2])
which
succeeds again, but 5 < 4
still fails.
member(5, [])
, which fails
because there is no appropriate clause.
The repeated backtracking can be avoided with the cut:
member(X,[X|Y]) :- !. member(X,[Y|Z]) :- member(X, Z).
PROLOG works under the Closed World Assumption:
This can cause problems if the fact and rule database is incomplete, especially when trying to define negative rules.
can_marry(X, Y) :- X \== Y, nonsibling(X, Y), noncousin(X, Y). nonsibling(X, Y) :- X == Y. nonsibling(X, Y) :- mother(M1, X), mother(M2, Y), M1 \== M2. nonsibling(X, Y) :- father(F1, X), father(F2, Y), F1 \== F2.
Now consider the queries:
| ?- sibling(albert,alice). no | ?- nonsibling(albert, alice). noThe problem is that our database doesn't have information about the parents of
albert
and alice
, so
PROLOG can't prove that they are siblings and it can't
that they aren't.
fail
and not
How can we force nonsibling(X, Y)
to be the logical opposite of sibling(X, Y)
,
i.e., so that it succeeds when sibling
fails and
vice versa?
The built-in fail
predicate can be used to force
failure. Thus we can use the cut-fail combination
as follows.
nonsibling(X, Y) :- sibling(X, Y), !, fail. nonsibling(X, Y).In a call to
nonsibling
:
sibling
succeeds, the cut prevents
backtracking and fail
forces nonsibling
to fail.
sibling
fails, then the next clause is taken
which allows nonsibling
to succeed immediately.
Cut-fail combinations cannot be read as normal logical statements; their semantics depends fundamentally on the PROLOG's execution order.
The cut-fail combination is so common that PROLOG provides
a special not
meta-predicate as a shorthand.
nonsibling(X, Y) :- not(sibling(X, Y)).Keep in mind, however, that
not
is not logical
negation, but means "failure to prove."