Class 02
Class 02
- A Prolog program is composed of predicates defined by facts and rules, which are special cases of definite (or Horn) clauses, i.e. clauses with at most one positive literal (the head of the clause). - A predicate may have several alternative definitions (both facts and rules). - A fact has no negative literals and expresses positive knowledge that is definite (no disjunctions). Example: The knowledge that John is a child of Ann and Alex, and that Ann is a child of Bob, is expressed by two facts, namely child_of(john, ann). child_of(john, alex). child_of(ann, bob). - A rule has one or more negative literals (the body of the clause), and is used to infer predicates from other predicates. Example: A grand child is the child of the child, i.e. grand_child_of(X,Y) :child_of(X,Z), child_of(Z,Y). - The name rule is due to the fact that a definite clause, H B1 B2 ... Bk can be written in the form of an if expression H B1 B2 ... Bk, which is the syntax adopted in Prolog.
Foundations of Logic and Constraint Programming 1
Prolog by Example
child_of(john, ann). child_of(john, alex). child_of(ann,bob). grand_child_of(X,Y) :-
child_of(X,Z), child_of(Z,Y).
- All variables (starting with capital letters) are universally quantified. The rule can be read For any X, Y and Z, if X is a child of Z and Z is a child of Y, then X is a grand child of Y. - Rules can be rephrased such that variables appearing only in the body of a rule are existentially quantfied. In this case, X is a grand child of Y, if there is some Z, of which X is a child, and is a child of Y. - A program may be regarded as a knowledge base, combining explicit knowledge of facts as well as implicit knowledge by means of rules. This knowledge may be queried by negative rules (no head). Notice that due to the negation, variables are read existentially. - Examples: Are there any parents of Alex (any X of which John is a parent) ?
?- child_of(X,alex).
Are there any A and B such that A is a grand parent of B (or B is a grand child of A) ?
?- grand_child_of(A,B).
Foundations of Logic and Constraint Programming 2
Prolog by Example
child_of(john, ann). child_of(john, alex). child_of(bob, john). grand_child_of(X,Y) :-
child_of(X,Z), child_of(Z,Y).
- A Prolog engine is able to answer the queries (by implementing resolution on Horn clauses), finding all solutions and presenting them, one at a time. ?- child_of(X, alex). X = john; no. ?- grand_child_of(A,B). A = bob, B = ann ; A = bob, B = alex; no. - In practice, to program in Prolog, it is important to understand the options that Prolog engines assume, in applying resolution. - Informally, to search for all solutions, Prolog uses the query as an initial resolvent, and organises all the other resolvents in an execution tree. - Solutions, if any, are the success leafs of the tree (the other leafs are failed).
Prolog by Example
child_of(john, ann). child_of(john, alex). child_of(bob, john). grand_child_of(X,Y) :-
child_of(X,Z), child_of(Z,Y).
- For example, query ?- grand_child_of(A,B), expands into the execution tree grand_child_of(A,B)
X/A, Y/B
child_of(A,Z), child_of(Z,B)
A/john Z/ann A/john Z/alex A/bob Z/john
child_of(ann,B)
child_of(alex,B)
child_of(john,B)
B/ann B/alex
- Notice that Instances of the answers are obtained by composing the substitutions in a path to a successful leaf The tree is searched depth-first, left-to-right.
Foundations of Logic and Constraint Programming 4
ann
john
mary
vicky
peter
bob
alex
tess
alice
fred
- The basic relationship that must be recorded is parent-child (the one kept in the ID cards and databases), modeled by the binary predicate child_of/2. child_of(john,doug). child_of(john,carol). child_of(mary,carol). child_of(tess,mary). child_of(fred,tess). child_of(peter,john). child_of(bob,john). child_of(alex,john). child_of(peter,ann). child_of(bob,ann). child_of(alex,ann). child_of(alice, peter). child_of(alice,vicky).
vicky
peter
bob
alex
tess
alice
fred
- Once the basic relationships are established among the elements of the family, other family relationsships and predicates. For example, the relationship parent_of is the opposite of child_of:
parent_of(X,Y):- child_of(Y,X).
- The predicate mother is assigned to any female that is the parent of someone. mother_of(X,Y):- parent_of(X,Y), is_female(X). - A mother is the mother of someone. is_mother(X):- mother_of(X,_).
Foundations of Logic and Constraint Programming 6
X/john Y/doug
is_female(doug)
is_female(carol)
sibling_of(X,Y):child_of(X,Z), child_of(Y,Z), X \= Y.
vicky
sibling_of(X,Y)
alice
child_of(X,Z), child_of(Y,Z), X \= Y. X/john Z/doug child_of(Y,doug), john\= Y X/john Z/carol child_of(Y,carol), john\= Y Y/john john\= john Y/mary john\= mary
11
12
| ?- int(s(s(0))). yes | ?- int(X). X = 0 ? ; % X=0 X = s(0) ? ; % X=1 X = s(s(0)) ? ; % X=2 X = s(s(s(0))) ? % X=3 | ?- int(s(s(X))). X=0?; % X = s(0) ? ; - Notice in the last case, that the arguments of predicate int that are reported in the answers are usually denoted by 2, 3, ...
13
sum(0,M,M). sum(s(N),M,s(K)):sum(N,M,K).
The result X is then obtained by composition of substitutions X/s(K1) but K1/s(K2)) so X/s(s(K2)) but K2/s(s(s(0))) so X/s(s(s(s(s(0))))) i.e. X = 5
sum(s(0),s(s(s(0))),s(K1))
M2 / s(s(s(0))) N2 / 0 ; K1 / s(K2)
sum(s(s(s(0))),0,s(K2))
K2 / s(s(s(0)))
14
sum(s(s(0)),X,s(s(s(s(s(0))))))
M1 / X ; N1 / s(0) K1 / s(s(s(s(0))))
sum(s(0),X, s(s(s(s(0))))
M2 / X ; N2 / 0 K2 / s(s(s(0)))
sum(0,X,s(s(s(s(s(0)))))
X / s(s(s(0)))
15
sum(0,M,M). sum(s(N),M,s(K)):sum(N,M,K).
sum(N1,X,s(s(s(s(0)))))
X / s(s(s(s(0)))) N1 / 0 M2 / X ; N1 / s(N2) K2 / s(s(s(0)))
sum(N2,X,s(s(s(0))))
X / s(s(s(0))) N2 / 0
16
sum(M1,M1,s(s(s(0))))
M1 / s(N2)
sum(N4, s(s(s(s(N4)), 0)
17
sum(0,s(s(0)),s(s(s(0))))
sum(0,M,M). sum(s(N),M,s(K)):sum(N,M,K).
M2/s(s(0)) ; N1/s(N2), K1 / P2
- It is left as an exercise to check what types of calls do not terminate with the above definitions.
Foundations of Logic and Constraint Programming 18
sum(M,0,M). sum(s(N),M,s(K)):sum(N,M,K).
19
20
p_length(T1,s(0)))
L1/ [_|T2]
P_length([],0). P_length([_|T],s(N)):p_lenght(T,N).
p_length(T2,0)
T2/ []
a_length(L,2)
L/ [_|T1] , M / 2
a_length(T1,N1), 2 is N1+1
T1/ [_|T2] , M2 / N1
2 is 0+1
T2/[] , N2/0
2 is 1+1
- The correct answer is obtained as before, in the left part of the tree T2 = [] ; T1 = [_|T2] = [_]; L = [_|T1] = [_,_] - However, there is now a right part of the tree...
Foundations of Logic and Constraint Programming 22
N3 is 0+1,..., 2 is N1+1
N4 is 0+1,..., 2 is N1+1
N5 is 0+1,..., 2 is N1+1
23
cat([],L,L). cat([H|T],L,[H|R]):cat(T,L,R).
25
26
27
28
29
- The definition can be improved by head unification. Since the call to cat_diff/3 performs substitutions Lx = L2, Tx = [H|T] and T = T2, such substitutions can be made directly, avoiding the explicit call to cat_diff/3 rev_diff(L-T,L-T):- L == T. rev_diff([H|L1]-T1, L2-T):rev_diff (L1-T1, T1-[H|T]).
32
rev_diff(L-T,L-T):- L = T
- Hence, when this clause is tested to reverse a non-empty list, say rev_diff([1,2|Z]-Z, X-Y), the call should fail, since [1,2|Z]-Z does not encode an empty list (it encodes of course list [1,2]) and should not unify with L-L. - More precisely, it is not possible to unify L = [1,2|Z] and L = Z, because that would require Z = [1,2|Z], i.e. to substitute a variable, Z, by a term, [1,2|Z], where the variable occurs. - This occurs-check failure does not occur in most Prolog systems that do not implement it correctly (ending in an infinite loop X = f(X) = f(f(X)) = f(f(f(X))) = ...). - For this reason, instead of checking if the two variable L-T are the same through unification (i.e. with predicate =/2), the clause tests this case with predicate ==/2, that only succeeds if the terms are already the same at the time of call.
rev_diff(L-T,L-T):- L == T
33
34
35
36
Constraints 9 and 10 state facts about the house in the middle and the leftmost house, which are respectively the third and the firts member of list L. 9. Milk is drunk in the middle house. 10. The Norwegian lives in the first house on the left. L = [_,_,h(_,_,_,milk,_),_,_], % 9 L = [h(_,norwegian,_,_,_)| _], % 10
37
Other constraints refer to houses next to eachother, but not specifing which is to the left and which is to the right. Hence, the predicate next/3 may be defined specifying both alternatives next(A,B,L):- sublist([A,B],L). next(A,B,L):- sublist([B,A],L).
Now, constraints 11, 12 and 15 may be expressed declaratively. For example. 15. The Norwegian lives next to the blue house. next(h(_,norwegian,_,_,_),h(blue,_,_,_,_),L), % 15
38
zebra(L):L = [h(H1,N1,P1,D1,C1), h(H2,N2,P2,D2,C2), h(H3,N3,P3,D3,C3), h(H4,N4,P4,D4,C4), h(H5,N5,P5,D5,C5)], member(h(red,english,_,_,_),L), member(h(_,spanish,dog,_,_),L), member(h(green,_,_,coffee,_),L), member(h(_,ukrainian,_,tea,_),L), sublist([h(ivory,_,_,_,_),h(green,_,_,_,_)],L), member(h(_,_,snails,_,old_gold),L), member(h(yellow,_,_,_,kool),L), L = [_,_,h(_,_,_,milk,_),_,_], L = [h(_,norwegian,_,_,_)|_], next(h(_,_,_,_,chesterfields),h(_,_,fox,_,_),L), next(h(_,_,_,_,kool),h(_,_,horse,_,_),L), member(h(_,_,_,orange,lucky_strike),L), member(h(_,japanese,_,_,parliaments),L), next(h(_,norwegian,_,_,_),h(blue,_,_,_,_),L), member(h(_,_,zebra,_,_),L), member(h(_,_,_,water,_),L).
Foundations of Logic and Constraint Programming
% % % % % % % % % % % % % % % %
2 3 4 5 6 7 8 9 10 11 12 13 14 15 Q1 Q2
39