Before you can start: * Read the lab instructions * Add module file (prog/sicstus/3.11.2 or higher) * Modify your .emacs (see course page) Using the system: * Separate code (file.pl) and query (*prolog*) buffers: all facts and rules in file.pl * Open or create filename.pl to enter prolog mode (after modifying .emacs and restarting emacs) * Save file and press C-c C-b to “consult” buffer and create the query window * Quit command: “halt.” Strict deadline 20 Dec. for labs (if you miss it, you have to wait for the next re-examination) Prolog syntax: Constants: lower case first letter (a, anna, aX) Variables: upper case first letter (X, Xs) Special “ignored” variables: _ as first letter A fact: mother(anna, bob). %”anna is mother of bob” created(god, _). %”’god created X’ is true % for any value of X” A rule: grandparent(X,Z) :parent(X,Y), parent(Y,Z). A query (“goal”): grandparent(X,julia). (written in *prolog* buffer) Usually written: :- grandparent(X, julia). ...in the material to distinguish from facts. Further extensions (lists, functors, etc) later Ex 1: Basic usage Contents of .pl file: mother(anna,bertil). father(adam,bertil). father(arne,beata). mother(beata,christina). father(bertil,cecilia). parent(X,Y) :- mother(X,Y). parent(X,Y) :- father(X,Y). grandfather(X,Y) :father(X,Z), parent(Z,Y). Queries (goals): | ?- mother(anna,bertil). yes | ?- parent(X,bertil). X = anna ? ; X = adam ? ; no | ?- grandfather(X,Y). X = adam, Y = cecilia ? ; X = arne, Y = christina ? ; no Example 2: More usage Contents of .pl file: and(0,0,0). and(0,1,0). and(1,0,0). and(1,1,1). or(0,0,0). or(0,1,1). or(1,0,1). or(1,1,1). not(0,1). not(1,0). circuit(X1,X2,X3,X4,Out) :and(X1, X2, Y1), not(X3, Y2), or(Y2,X4,Y3), not(Y3,Y4), or(Y1,Y4,Out). Example queries: ?- circuit(0,1,0,1,Out). Out = 0 ? yes ?- circuit(A,B,C,D,1). A B C D = = = = 0, 0, 1, 0 ? ; A B C D = = = = 0, 1, 1, 0 ? yes (“Fill in the blanks” computation) Prolog computation order Prolog works by constructing an SLD-derivation: 1. Select the first atom of the goal. 2. Select the first untried rule or fact for the same predicate as this atom. If none remain, the current line of derivation has failed; backtrack to a higher level. 3. Unify the atom and the head of the rule/fact. If this fails, return to 2. 4. Apply the mgu to the body of the rule. Replace the selected atom in the goal with the result (keep the rest of the goal). Note: A fact has an empty body. 5. If the new goal is empty, we have a successful refutation (ie, an answer). Otherwise, start again from 1 with the new goal (recursively). Debug output (activate with command “trace.”) pauses derivation and prints a message each time: * Step 1 is reached * Step 2 fails * Step 4 succeeds The unification is invisible. (Deactivate tracing with “notrace.”) Ex 3: Lists [X,2,c] is a list with three elements [] is the empty list [X|Xs] is (cons X Xs) from Lisp Note: [1,2,3] is syntactic sugar [1|[2|[3|[]]]] is the “real” form Contents of .pl file: %member(X,L): Does the list L contain X? member(X,[X|_]). member(X,[_|L]) :- member(X,L). Queries: | ?- member(X,[1,2,3]). X = 1 ? ; X = 2 ? ; X = 3 ? ; no | ?- member(a,L). L = [a|_A] ? ; L = [_A,a|_B] ? yes -- note: list with *at least* one element (_A is a list) -- L has at least two elements (_A is an item, _B is a list) Ex 4: Some arithmetics X = Y+Z: Unifies X with +(Y,Z) without performing any calculation (eg. ‘6=5+1’ fails). X is E: Calculate expression E, then unify. ‘X is 5+1’ results in X=6; ‘X is Y+Z’ requires that Y and Z are numbers (or expressions containing numbers, eg. Y=2*3) E1<E2, E1=<E2, E1>=E2, E1>E2: Compare expressions E1=:=E2: Expressions E1 and E2 have the same value “2*3 = 3*2” false, “2*3 =:= 3*2” true E1=\=E2: Expressions E1 and E2 have different values Code: fac(0,1). fac(N,F) :N>0, M is N-1, fac(M,G), F is G*N. Queries: | ?- fac(3,F). F = 6 ? yes | ?- fac(N,6). {INSTANTIATION ERROR: _51>0 - arg 1} Ex 5: Recursion: Order matters Code: (Same ’parent’ relation as in ex 1) ancestor1(X,Y) :- parent(X,Y). ancestor1(X,Y) :parent(X,Z), ancestor1(Z,Y). ancestor2(X,Y) :- parent(X,Y). ancestor2(X,Y) :ancestor2(X,Z), parent(Z,Y). Queries: | ?- ancestor1(X,cecilia). X = bertil ? ; X = anna ? ; X = adam ? ; no | ?- ancestor2(X,cecilia). X = bertil ? ; X = anna ? ; X = adam ? ; (Infinite loop!) Ex 6: Useful builtins length(L,N): L is a list with length N setof(X,p(X),Set) bagof(X,p(X),Bag) Set of all results to query p(X) “Bag” of all results to query p(X) | ?- setof(X,member(X,[b,a,c,a]),Set). Set = [a,b,c] ? -- sorted, unique members yes | ?- bagof(X,member(X,[b,a,c,a]),Bag). Bag = [b,a,c,a] ? -- all answers in returned order yes Negation \+p(X,Y): p(X,Y) has no solutions | ?- parent(anna,adam). no | ?- \+ parent(anna,X). no | ?- \+ parent(X,X). true ? yes \+(X=Y) is written “X\=Y”: “X,Y can’t be equal” (Negation is covered in more detail in Ulf’s lectures!) Ex 7: data types Code: birthday(anna, date(feb,20)). birthday(arne, date(jun,15)). birthday(beata, date(feb,10)). birthday(bertil, date(oct,20)). Queries: | ?- birthday(anna,X). X = date(feb,20) ? yes | ?- birthday(X,date(feb,Y)). X = anna, Y = 20 ? ; X = beata, Y = 10 ? ; no | ?- birthday(X, Date), birthday(Y, Date), X\=Y. no Also nested: tree(tree(1,2), 3) Fully handled by unification Ex 8: More data type usage Define the tree type: tree(N) :number(N). tree(tree(L,R)) :tree(L), tree(R). Searching procedure: treecontains(X,X). treecontains(tree(L,_R), X) :treecontains(L,X). treecontains(tree(_L,R), X) :treecontains(R,X). Possibly unexpected query effects: | ?- treecontains(tree(tree(1,2),3),X). X = tree(tree(1,2),3) ? ; X = tree(1,2) ?; X = 1? | ?- treecontains(T,1). T = 1 ? ; T = tree(1,_A) ? | ?- tree(T). (infinite loop: number(X) doesn’t generate numbers) Hints and reminders * There are no “return values”! Using the result of a predicate is done by reusing variables: do_something(Input, Output) :find_thing(Input, Thing), process(Thing, Output). * The effect of a predicate in the body of a rule is exactly that the variables of the predicate are instantiated. (Sometimes they are “instantiated” into another variable, or a data type containing variables). * ‘X=Y’ unifies; ‘X is Y+1’ evaluates Y+1. * Make the head of a rule as specific as possible (‘fac(0,1)’ rather than ‘fac(N,X) :- N=0, X=1’) * Each fact and rule should be literally correct, taken as a statement by itself