Examples: X, Im4u, A_little_garden, _, _x, _22
Examples: a, dog, a_big_cat, 23, 'Hungry man', []
Example: date(monday, Month, 1994)
Arguments can in turn be variables, constants and structures.
| a + b | is the term | +(a,b) | if +/2 declared infix |
|---|---|---|---|
| - b | is the term | -(b) | if -/1 declared prefix |
| a < b | is the term | <(a,b) | if </2 declared infix |
| john father mary | is the term | father(john,mary) | if father/2 declared infix |
We assume that some such operator definitions are always preloaded.
meal(soup, beef, coffee). % <- A fact. meal(First, Second, Third) :- % <- A rule. appetizer(First), % main_dish(Second), % dessert(Third). %
Examples:
pet(spot). animal(tim). pet(X) :- animal(X), barks(X). animal(spot). pet(X) :- animal(X), meows(X). animal(hobbes).Predicate pet/1 has three clauses. Of those, one is a fact and two are rules. Predicate animal/1 has three clauses, all facts.
(i.e., a clause without a head).
A query represents a question to the program.
Example: ?- pet(X).
Example: the fact animal(spot).
can be read as "spot is an animal".
p :- p_1, ..., p_n p :- q_1, ..., q_m ...provide different alternatives (for p).
pet(X) :- animal(X), barks(X). pet(X) :- animal(X), meows(X).express two alternative ways for X to be a pet.
| ?- pet(spot). | ?- pet(X). | |
| Asks: Is spot a pet? | Asks: "Is there an X which is a pet?" |
A first program: the pets database.
:- module(_,_,[]).
pet(X) :-
animal(X),
barks(X).
pet(X) :-
animal(X),
meows(X).
animal(tim).
animal(spot).
animal(hobbes).
barks(spot).
meows(tim).
roars(hobbes).?- pet(spot).
?- pet(X).
Example: given the program above and the query ?- pet(X).
the system will try to find a "substitution" for X which makes pet(X) true.
Ciao X.Y-... ?- use_module(pets). yes ?- pet(spot). yes ?- pet(X). X = spot ? ; X = tim ? ; no ?-
p :- p_1, ..., p_n p :- q_1, ..., q_m ...it means: "to execute a call to p, call p and … and p, or, alternatively, q and … and q, or …"
| ≡ | ||||||
|---|---|---|---|---|---|---|
| f(X,g(t)) | f(m(h),g(M)) | X=m(h), M=t | f(m(h),g(t)) | f(m(h),g(t)) | ||
| dog | dog | ∅ | dog | dog | ||
| dog | cat | fail | ||||
| X | a | X=a | a | a | ||
| X | Y | X=Y | Y | Y | ||
| f(X,g(t)) | f(m(h),t(M)) | fail (1) | ||||
| X | f(X) | fail (2) |
| and | ||||
|---|---|---|---|---|
| f(X) | f(m(H)) | X=m(a), H=a | f(m(a)) | |
| " | " | X=m(g(b)), H=g(b) | f(m(g(b))) | |
| ... |
These are correct, but we want the solution that is minimal (that binds minimally the variables). In this case it is:
| and | ||||
|---|---|---|---|---|
| f(X) | f(m(H)) | X=m(H) | f(m(H)) |
Note that the result, f(m(H)), is "more general" than f(m(a)) or f(m(g(b))).
| depending on the form of equation : | do: |
| X=X | ignore |
| X=f(..., X, ...) | fail (occurs check) |
| X= | add X= to the solution and replace X by anywhere else |
| a=a | ignore |
| a=b | fail |
| a=f(...) | fail |
| g(...)=f(...) | fail |
| f(......)=f(......) () | fail |
| f(,...,)=f(,...,) | add to the system: =, …, = |
Try:
?- p(X,f(b)) = p(a,Y).
?- p(X,f(Y)) = p(a,g(b)).
?- p(X,X) = p(f(Z),f(W)).
?- p(X,f(Y)) = p(Z,X).
?- p(X,f(X)) = p(Z,Z).(Note that X=f(X) is an infinite term - really a constraint)
With this last case we can try also unify_with_occurs_check/1 (from the iso_misc library):
?- use_module(library(iso_misc)), unify_with_occurs_check( p(X,f(X)), p(Z,Z) ).Can you explain the difference between the two answers?
| p(X,f(b))p(a,Y) | Xa |
| Yf(b) |
| p(X,f(Y))p(a,g(b)) | Xa | |
| f(Y)g(b) | fail |
| p(X,X)p(f(Z),f(W)) | Xf(Z) | Xf(Z) | Xf(Z) | Xf(W) |
| Xf(W) | f(Z)f(W) | ZW | ZW |
| p(X,f(Y))p(Z,X) | XZ | X=Z | Xf(Y) |
| f(Y)X | f(Y)Z | Zf(Y) |
| p(X,f(X))p(Z,Z) | XZ | XZ | |
| f(X)Z | f(Z)Z | fail ("Occurs check") |
?- pet(X). ?- pet(X). X = spot ? ; X = tim ? ; X = tim ? ; X = spot ? ; no no ?- ?-

| Clause | |||
|---|---|---|---|
| pet(X) | pet(X) | C* | X=X |
| pet(X) | animal(X), barks(X) | C* | X=tim |
| pet(tim) | barks(tim) | ??? | failure |

| Clause | |||
|---|---|---|---|
| pet(X) | pet(X) | C* | X=X |
| pet(X) | animal(X), barks(X) | C* | X=spot |
| pet(spot) | barks(spot) | C |
?- pet(X). 1 1 Call: draft:pet(_15363) ? 2 2 Call: draft:animal(_15363) ? 2 2 Exit: draft:animal(tim) ? 3 2 Call: draft:barks(tim) ? 3 2 Fail: draft:barks(tim) ? 2 2 Redo: draft:animal(tim) ? 2 2 Exit: draft:animal(spot) ? 3 2 Call: draft:barks(spot) ? 3 2 Exit: draft:barks(spot) ? 1 1 Exit: draft:pet(spot) ? X = spot ? ; 1 1 Redo: draft:pet(spot) ? 2 2 Redo: draft:animal(spot) ? 2 2 Exit: draft:animal(hobbes) ? 3 2 Call: draft:barks(hobbes) ? 3 2 Fail: draft:barks(hobbes) ? 2 2 Call: draft:animal(_15363) ? 2 2 Exit: draft:animal(tim) ? 3 2 Call: draft:meows(tim) ? 3 2 Exit: draft:meows(tim) ? 1 1 Exit: draft:pet(tim) ? X = tim ?
p(X,Y):- q(X,Z), r(Z,Y). q(X,Z) :- ...
when the procedure call to q/2 finishes (Exit:), execution continues in p/2, just after the call to q/2, i.e., at the call to r/2 –the forward continuation.
p(X,Y):- q(X,Z), r(Z,Y). p(X,Y):- ...
q(X,Z) :- ...
if q/2 succeeds, it is as above, but if it fails, execution continues at (backtracks to) the previous alternative: the second clause of p/2 –the backward continuation.
:- module(_,_,[sr/bfall]).
:- module(_,_,[]).
Controlling of Search Size with literal order
(for Depth-First Search, i.e., backtracking)
:- module(_,_). p(4). p(5). q(1, a) :- lots_of_computing. q(2, b) :- lots_of_computing. q(4, c) :- lots_of_computing. q(4, d) :- lots_of_computing. lots_of_computing :- pause(1).To see the impact of the ordering of literals in the body of a clause:
%! \begin{focus}
% Consider:
a(X,Y) :-
p(X), % We execute p first,
q(X,Y). % then q
% versus:
b(X,Y) :-
q(X,Y), % We execute q first,
p(X). % then p
%! \end{focus}
:- use_module(library(odd),[undo/1]).
:- meta_predicate(walltime(goal)).
walltime(G) :-
statistics(walltime,_),
call(G),
undo(statistics(walltime,_)),
statistics(walltime,[_,T]),
write('Executed in '), NT is T/1000, write(NT), write(' seconds.').
walltime(_) :-
statistics(walltime,[_,T]),
write('Executed in '), NT is T/1000, write(NT), write(' seconds.').Compare the time running?- walltime(a(X,Y)).or
?- walltime(b(X,Y)).Run it in the debugger to see the steps!
Note that optimal order depends on the variable instantiation mode. E.g., for q(X,d), p(X), this order is better than p(X), q(X,d).
p(X), q(X,Y) is more efficient: execution of p/2 reduces the choices of q/2.
Controlling of Search Size with clause order
(for Depth-First Search, i.e., backtracking)
:- module(_,_). p(4). p(5). q(1, a) :- lots_of_computing. q(2, b) :- lots_of_computing. q(4, c) :- lots_of_computing. q(4, d) :- lots_of_computing. lots_of_computing :- pause(1).To see the impact of the ordering of clauses in a predicate:
%! \begin{focus}
% Consider calling q:
a(X,Y) :-
q(X,Y),
p(X).
% versus calling qr: a reordered version of q:
b(X,Y) :-
qr(X,Y),
p(X).
qr(4, d) :- lots_of_computing.
qr(4, c) :- lots_of_computing.
qr(2, b) :- lots_of_computing.
qr(1, a) :- lots_of_computing.
%! \end{focus}
:- use_module(library(odd),[undo/1]).
:- meta_predicate(walltime(goal)).
walltime(G) :-
statistics(walltime,_),
call(G),
undo(statistics(walltime,_)),
statistics(walltime,[_,T]),
write('Executed in '), NT is T/1000, write(NT), write(' seconds.').
walltime(_) :-
statistics(walltime,[_,T]),
write('Executed in '), NT is T/1000, write(NT), write(' seconds.').Compare the time running?- walltime(a(X,Y)).or
?- walltime(b(X,Y)).Run it in the debugger to see the steps!
Also, the order of solutions is different:
?- a(X,Y).we get:
X = 4, Y = c ? ; X = 4, Y = d ? ;and for:
?- b(X,Y).we get:
X = 4, Y = d ? ; X = 4, Y = c ? ;
3 The pruning operators (e.g., "cut"), which cut choices dynamically –see later.
Example: Consider query
?- animal(A), named(A,Name).
with:
animal(dog(tim)).named(dog(Name),Name).The call to animal(A) succeeds with A=dog(tim).
| Clause | |||
|---|---|---|---|
| pet(P) | pet(P) | C* | P=X |
| pet(X) | animal(X), barks(X) | C* | X=spot |
| pet(spot) | barks(spot) | C | |
| pet(spot) | — | — | — |
Answer: P=spot
Example: Consider query ?- pet(spot). vs. ?- pet(X).
:- module(_,_,[]).
pet(X) :-
animal(X),
barks(X).
pet(X) :-
animal(X),
meows(X).
animal(tim).
animal(spot).
animal(hobbes).
barks(spot).
meows(tim).
roars(hobbes).?- pet(spot).
?- pet(X).or in the Peano arithmetic example from the introduction:
?- plus( s(0), s(s(0)), Z). % Adds vs. ?- plus( s(0), Y, s(s(s(0)))). % Subtracts
:- module(_,_,[sr/bfall]). nat(0). nat(s(X)) :- nat(X). le(0,X) :- nat(X). le(s(X),s(Y)) :- le(X,Y). add(0,Y,Y) :- nat(Y). add(s(X),Y,s(Z)) :- add(X,Y,Z). mult(0,Y,0) :- nat(Y). mult(s(X),Y,Z) :- add(W,Y,Z), mult(X,Y,W). nat_square(X,Y) :- nat(X), nat(Y), mult(X,X,Y). output(X) :- nat(Y), le(Y,s(s(s(s(s(0)))))), nat_square(Y,X).
?- add( s(0), s(s(0)), Z).
?- add( s(0), Y, s(s(s(0)))).
:- module(_,_,[sr/bfall]).
father_of(john, peter).
father_of(john, mary).
father_of(peter, michael).
mother_of(mary, david).
mother_of(jill, john).
grandfather_of(L,M) :-
father_of(L,N),
father_of(N,M).
grandfather_of(X,Y) :-
father_of(X,Z),
mother_of(Z,Y). ?- father_of(john,peter).
?- father_of(john,david).
?- father_of(john,X).
?- grandfather_of(X,michael).
?- grandfather_of(X,Y).
?- grandfather_of(X,X).
A circuit topology example:
:- module(_,_,[sr/bfall]). resistor(power,n1). resistor(power,n2). transistor(n2,ground,n1). transistor(n3,n4,n2). transistor(n5,ground,n4).
inverter(Input,Output) :- transistor(Input,ground,Output), resistor(power,Output). nand_gate(Input1,Input2,Output) :- transistor(Input1,X,Output), transistor(Input2,ground,X), resistor(power,Output). and_gate(Input1,Input2,Output) :- nand_gate(Input1,Input2,X), inverter(X, Output).
?- inverter(In,Out).
?- and_gate(In1,In2,Out).
?- nand_gate(In1,In2,Out).
course(complog, wed, 18, 30, 20, 30, 'M.', 'Hermenegildo', new, 5102). course(ai, thu, 15, 00, 17, 00, 'J.', 'Smith', old, 3102). course(os, wed, 18, 30, 20, 30, 'L.', 'Hubbard', new, 6201). ...
course(complog, Time, Lecturer, Location) :- Time = t(wed,18:30,20:30), Lecturer = lect('M.','Hermenegildo'), Location = loc(new,5102).
Note: X=Y is equivalent to =(X,Y)
where predicate =/2 is defined as the fact =(X,X). – i.e., unification.
course(complog,t(wed,18:30,20:30),lect('M.','Hermenegildo'),loc(new,5102)).
course(complog,Time,Lecturer, Location) :- Time = t(wed,18:30,20:30), Lecturer = lect('M.','Hermenegildo'), Location = loc(new,5102).
main :- X=f(K,g(K)), Y=a, Z=g(L), W=h(b,L), % Heap memory at this point ---> p(X,Y), q(Y,Z), r(W).

:- module(_,_,[sr/bfall]).
resistor(r1,power,n1).
resistor(r2,power,n2).
transistor(t1,n2,ground,n1).
transistor(t2,n3,n4,n2).
transistor(t3,n5,ground,n4).
inverter(inv(T,R),Input,Output) :-
transistor(T,Input,ground,Output),
resistor(R,power,Output).
nand_gate(nand(T1,T2,R),Input1,Input2,Output) :-
transistor(T1,Input1,X,Output),
transistor(T2,Input2,ground,X),
resistor(R,power,Output).
and_gate(and(N,I),Input1,Input2,Output) :-
nand_gate(N,Input1,Input2,X),
inverter(I,X,Output). ?- inverter(Inv,I,O).
?- and_gate(G,In1,In2,Out).
?- resistor(R1,P1,P2).
?- transistor(T1,P1,P2,P3).
| Relational Database | Logic Programming | |
|---|---|---|
| Relation Name | → | Predicate symbol |
| Relation | → | Procedure consisting of ground facts |
| (facts without variables) | ||
| Tuple | → | Ground fact |
| Attribute | → | Argument of predicate |
| Name | Age | Sex |
|---|---|---|
| Brown | 20 | M |
| Jones | 21 | F |
| Smith | 36 | M |
| Name | Town | Years |
|---|---|---|
| Brown | London | 15 |
| Brown | York | 5 |
| Jones | Paris | 21 |
| Smith | Brussels | 15 |
| Smith | Santander | 5 |
Database programming
:- module(_,_,[]). % The argnames package allows giving names to arguments. :- use_package(argnames). :- argnames person( name, age, sex). % ====================== person( brown, 20, male). person( jones, 21, female). person( smith, 36, male). :- argnames person2( name, age, sex). % ====================== person2( cabeza, 33, male). person2( bueno, 39, male). person2( jones, 21, female). :- argnames lived_in( name, town, years). % =========================== lived_in( brown, london, 15). lived_in( brown, york, 5). lived_in( jones, paris, 21). lived_in( smith, brussels, 15). lived_in( smith, santander, 5).
city(C) :-
lived_in(_,C,_). ?- city(C).
city_(C) :-
lived_in${town => C}. ?- city_(C).
all_persons(Name,Age,Sex) :-
person(Name,Age,Sex).
all_persons(Name,Age,Sex) :-
person2(Name,Age,Sex). ?- all_persons(Name,Age,Sex).
difference(Name,Age,Sex) :-
person(Name,Age,Sex),
\+ person2(Name,Age,Sex).
difference(Name,Age,Sex) :-
person2(Name,Age,Sex),
\+ person(Name,Age,Sex). ?- difference(Name,Age,Sex).
person_X_lived_in(Name1,Age,Sex,Name2,Town,Years) :-
person(Name1,Age,Sex),
lived_in(Name2,Town,Years). ?- person_X_lived_in(Name1,Age,Sex,Name2,Town,Years).
underage_person(Name,Age,Sex) :-
person(Name,Age,Sex),
Age < 21. % We should use Peano here also of course. ?- underage_person(Name,Age,Sex).
person_lived_in(Name,Age,Sex,Town,Years) :-
person(Name,Age,Sex),
lived_in(Name,Town,Years). ?- person_lived_in(Name,Age,Sex,Town,Years).
person_joinName_person2(Name,Age,Sex) :-
person(Name,Age,Sex),
person2(Name,_Age2,_Sex2). ?- person_joinName_person2(Name,Age,Sex).
parent(X,Y) :- father(X,Y). parent(X,Y) :- mother(X,Y). ancestor(X,Y) :- parent(X,Y). ancestor(X,Y) :- parent(X,Z), parent(Z,Y). ancestor(X,Y) :- parent(X,Z), parent(Z,W), parent(W,Y). ancestor(X,Y) :- parent(X,Z), parent(Z,W), parent(W,K), parent(K,Y). ...
:- module(_,_,[]). parent(X,Y) :- father_of(X,Y). parent(X,Y) :- mother_of(X,Y). ancestor(X,Y) :- parent(X,Y). ancestor(X,Y) :- parent(X,Z), ancestor(Z,Y). father_of(john, peter). father_of(john, mary). father_of(peter, michael). mother_of(mary, david). mother_of(jill, john).
?- ancestor(john,michael).
?- ancestor(john,elisabeth).
?- ancestor(john,X).
?- ancestor(X,elisabeth).
?- ancestor(X,michael).
?- ancestor(X,Y).
?- X \= Y. yes % But X and Y may be unifiable, e.g.: ?- X=a, Y=a, X \= Y. no
:- module(_,_,[]). person_num(john, 0). person_num(peter, s(0)). person_num(mary, s(s(0))). person_num(michael, s(s(s(0)))). person_num(david, s(s(s(s(0))))). person_num(jill, s(s(s(s(s(0)))))).
diffnat(0,s(_)). % 0 differs from any s(...) diffnat(s(_),0). % any s(...) differs from 0 diffnat(s(X),s(Y)) :- diffnat(X,Y). % same depth: look further in
different_person(X,Y) :-
person_num(X,NX),
person_num(Y,NY),
diffnat(NX,NY). ?- diffnat(0,s(0)).
?- diffnat(s(0),s(s(0))).
?- diffnat(s(s(0)),s(s(0))).
?- diffnat(s(s(0)),X).
?- different_person(john,mary).
?- different_person(john,john).
?- different_person(john,X).
?- different_person(X,Y).
?- john \= mary.works OK, but:
?- X \= Y.produces 'no' (and does not enumerate), but X and Y could be the different! For example:
?- X = john, Y = mary, X \= Y.which does produce 'yes'.
father_of(john, peter). father_of(john, mary). father_of(peter, michael). mother_of(mary, david). mother_of(jill, john). parent(X,Y) :- father_of(X,Y). parent(X,Y) :- mother_of(X,Y). sibling(X,Y) :- parent(P,X), parent(P,Y), different_person(X,Y).
?- sibling(X,Y).
?- sibling(peter,peter).
Example: weekday:
:- module(_,_,[]).
weekday('Monday').
weekday('Tuesday').
weekday('Wednesday').
weekday('Thursday').
weekday('Friday').
weekday('Saturday').
weekday('Sunday'). day_of_month(1). day_of_month(2). % ... day_of_month(31).
date(date(W,D)) :- weekday(W), day_of_month(D).
?- date(date('Monday',31)). ?- date(date(a,31)).
?- date(D).
nat(0). nat(s(X)) :- nat(X).
integer( X) :- nat(X). integer(-X) :- nat(X).
% :- module(_,_,[sr/bfall]). :- module(_,_,[]). nat(0). nat(s(X)) :- nat(X).
less_or_equal(0,X) :- nat(X). less_or_equal(s(X),s(Y)) :- less_or_equal(X,Y).
?- less_or_equal(s(0),s(s(0))).
?- less_or_equal(X,0).
?- less_or_equal(X,s(0)).
?- less_or_equal(s(s(0)),Y).
plus(0,Y,Y) :- nat(Y). plus(s(X),Y,s(Z)) :- plus(X,Y,Z).
?- plus(s(s(0)),s(0),Z).
?- plus(s(s(0)),Y,s(0)).
?- plus(s(0),Y,s(s(s(0)))).
?- plus(X,Y,s(s(s(0)))).
plus(X,0,X) :- nat(X). plus(X,s(Y),s(Z)) :- plus(X,Y,Z).
We can simply write the specification:
:- module(_,_,[sr/bfall]).
mod(X,Y,Z) :-
less(Z, Y),
times(Y,_Q,W),
plus(W,Z,X).Try:?- op(100,fy,s).(defines s as a prefix operator to save us writing parenthesis)
?- mod(s s s s s s s s s s s s s 0, s s s 0, Z).
?- mod(s s s s s s s s s s s s s 0, Y, s 0).
mod2(X,Y,X) :-
less(X, Y).
mod2(X,Y,Z) :-
plus(X1,Y,X),
mod2(X1,Y,Z).?- mod2(s s s s s s s s s s s s s 0, s s s 0, Z).
?- mod2(s s s s s s s s s s s s s 0, Y, s 0).This second one is much more efficient than the first one for several query modes!
less(0,s(X)) :- nat(X). less(s(X),s(Y)) :- less(X,Y). plus(0,Y,Y) :- nat(Y). plus(s(X),Y,s(Z)) :- plus(X,Y,Z). times(0,Y,0) :- nat(Y). times(s(X),Y,Z) :- plus(W,Y,Z), times(X,Y,W). nat(0). nat(s(X)) :- nat(X).
fun ack 0 n = n + 1 | ack m 0 = ack (m - 1) 1 | ack m n = ack (m - 1) (ack m (n - 1))Using Peano arithmetic:
datatype nat = Zero | S of nat
fun ack 0 n = S n | ack (S m1) 0 = ack m1 (S 0) | ack (S m1) (S n1) = ack m1 (ack (S m1) n1)is represented as plain clauses as follows:
:- module(_,_,[rtchecks,modes]). ack(0,N,s(N)). ack(s(M),0,Val) :- ack(M,s(0),Val). ack(s(M),s(N),Val) :- ack(s(M),N,Val1), ack(M,Val1,Val).Try:
?- op(100,fy,s).(defines s as a prefix operator to save us writing parenthesis)
?- ack(s s 0, s s s 0, X).But of course now it also runs 'backwards'!
?- ack(s s 0, s s s 0, s s s s s s s s s 0).
?- ack(s s 0, Y, s s s s s s s s s 0).
?- ack(X, s s s 0, s s s s s s s s s 0).
% :- pred ack(+,+,-).If you place this assertion above the ack predicate, and reload, you will see an error if you try to call ack/3 in another mode. This is done by the rtchecks package above.
p(X,Y) :- q(X,Z), r(Z,Y).as
p(X,Y) :- r(~q(X),Y).or
p(X,~r(~q(X)).
p(X,Y) :- q(X,Z), r(Z,Y).as
p(X) := Y :- r(~q(X),Y).or
p(X) := ~r(~q(X)).
ack(s(M),s(N),Val) :- ack(s(M),N,Val1), ack(M,Val1,Val).can be rewritten (using also :- op(100,fy,s).) as:
ack(s M, s N) := ~ack(M, ~ack(s M, N) ).
:- fun_eval ack/2. ack(s M, s N) := ack(M, ack(s M, N) ).
:- fun_eval defined(true). :- fun_eval arith(true). add_one(X,X+1).
:- use_package(functional). :- module(_,_,[]). :- use_package(functional).
:- op(100,fy,s).
ack( 0, N) := s N. ack(s M, 0) := ack(M, s 0). ack(s M, s N) := ack(M, ack(s M, N) ).Try:
?- op(100,fy,s).
?- ack(s s 0, s s s 0, X).
?- ack(s s 0, s s s 0, s s s s s s s s s 0).
?- ack(s s 0, Y, s s s s s s s s s 0).
?- ack(X, s s s 0, s s s s s s s s s 0).
nat(0). nat(s(X)) :- nat(X).Using special := notation for the "return" (last) argument:
nat := 0. nat := s(X) :- nat(X).
nat := 0. nat := s(~nat).~ not needed with functional package if inside a function's own definition:
nat := 0. nat := s(nat).Using an :- op(100,fy,s). declaration to define s as a prefix operator:
nat := 0. nat := s nat.Using "|" (disjunction) we get a nice "type definition:"
:- module(_,_,[functional]). :- op(100,fy,s). nat := 0 | s nat.Try:
?- op(100,fy,s).
?- nat(s s 0).
?- nat(a).
?- nat(X).Which is exactly equivalent to:
nat(0). nat(s(X)) :- nat(X).exercise! ▶
| Formal object | "Cons pair" syntax | "Element" syntax |
|---|---|---|
| .(a,[]) | [a|[]] | [a] |
| .(a,.(b,[])) | [a|[b|[]]] | [a,b] |
| .(a,.(b,.(c,[]))) | [a|[b|[c|[]]]] | [a,b,c] |
| .(a,X) | [a|X] | [a|X] |
| .(a,.(b,X)) | [a|[b|X]] | [a,b|X] |
| [a,b] and [a|X] unify with {X = [b]} | [a] and [a|X] unify with {X = []} |
| [a] and [a,b|X] do not unify | [] and [X] do not unify |
list([]). list(.(X,Y)) :- list(Y).
:- module(_,_,[]).
list([]).
list([_|Y]) :-
list(Y).Try:?- list([a,b,c]).
?- list(a).
?- list(L).
:- use_package(functional). lst := [] | [_|lst].Try:
?- lst([a,b,c]).
?- lst(a).
?- lst(L).
| member(a,[a]). | member(b,[b]). | etc. | ⇒ member(X,[X]). |
| member(a,[a,c]). | member(b,[b,d]). | etc. | ⇒ member(X,[X,Y]). |
| member(a,[a,c,d]). | member(b,[b,d,l]). | etc. | ⇒ member(X,[X,Y,Z]). |
⇒ member(X,[XY]) :- list(Y).
| member(a,[c,a]), | member(b,[d,b]). | etc. | ⇒ member(X,[Y,X]). |
| member(a,[c,d,a]). | member(b,[s,t,b]). | etc. | ⇒ member(X,[Y,Z,X]). |
⇒ member(X,[YZ]) :- member(X,Z).
:- module(_,_,[]).
% :- use_package(sr/bfall).
% list_member(X,L): X is a member of list L
list_member(X,[X|L]) :-
list(L).
list_member(X,[_|T]) :-
list_member(X,T).
list([]).
list([_|T]) :-
list(T). ?- member(b,[a,b,c]).
?- member(X,[a,b,c]).
?- member(a,Y).
:- module(_,_,[]).
% :- use_package(sr/bfall).
list_length([],0).
list_length([_|T],s(N)) :-
list_length(T,N).Try:?- list_length([a, b, c], N).
?- list_length(L, s(s(s(0)))).
?- list_length(L, 3).
?- list_length(L, N).
sumlist([],0).
sumlist([H|T],S) :-
sumlist(T,ST),
add(ST,H,S).Try:?- sumlist([s(0),s(s(s(0))),s(s(0))],N).
?- sumlist(L,s(s(s(0)))).
?- sumlist(L,S).
natlist([]).
natlist([X|Y]) :-
nat(X),
natlist(Y).Or, using functional notation::- use_package(fsyntax). natlst := [] | [~nat|~natlst].
Auxiliary predicates:
list([]).
list([_|T]) :-
list(T).
add(0,X,X) :-
nat(X).
add(s(X),Y,s(Z)) :-
add(X,Y,Z).
nat(0).
nat(s(X)) :-
nat(X). ?- sumlist([s(0), s(s(0)), s(0)], R). \ sumlist([],0). % fails sumlist([H|T],S) :- % H=s(0), T=[s(s(0)), s(0)] sumlist(T,ST), % T=[s(s(0)), s(0)] | \ | sumlist([],0). % fails | sumlist([H1|T1],S1) :- % H1=s(s(0), T1=[s(0)] | sumlist(T1,ST1), % T1=[s(0)] | | \ | | sumlist([],0). % fails | | sumlist([H2|T2],S2) :- % H2=s(0), T2=[] | | sumlist(T2,ST2), % T2=[] | | | \ | | | sumlist([],0). % ST2=0 | | | / | | % H2=s(0), ST2=0 | | plus(H2,ST2,S2). % S2=s(0) | | / | % H1=s(s(0)), ST1=s(0) | plus(H1,ST1,S1). % S1=s(s(s(0))) | / % H=s(0), ST=s(s(s(0) plus(H,ST,S). % S=s(s(s(s(0)))) / % R=s(s(s(s(0))))
append([],Ys,Ys) :- list(Ys). append([X|Xs],Ys,[X|Zs]) :- append(Xs,Ys,Zs).
:- module(_,_,[]).
% `append(X,Y,Z)`: list `Z` is `X` and `Y` concatenated.
list_append([],L,L) :-
list(L).
list_append([X|Xs],Ys,[X|Zs]) :-
list_append(Xs,Ys,Zs).
list([]).
list([_|Y]) :-
list(Y). ?- list_append([a,b], [c], X).
?- list_append(X, [c], [a,b,c]).
?- list_append(X, Y, [a,b,c]).
:- module(_,_,[]). reverse_naive([],[]).
reverse_naive([X|Xs],Ys) :-
reverse_naive(Xs,Zs),
list_append(Zs,[X],Ys). ?- reverse_naive([a,b,c],X).
?- reverse_naive(X,[c,b,a]).
?- reverse_naive(X,Y).
reverse_acc(Xs,Ys) :-
reverse_(Xs,[],Ys).
reverse_([],Ys,Ys).
reverse_([X|Xs],Acc,Ys) :-
NewAcc=[X|Acc],
reverse_(Xs,NewAcc,Ys).?- reverse_acc([a,b,c],X).
?- reverse_acc(X,[c,b,a]).
?- reverse_acc(X,Y).
% append(X,Y,Z): list `Z` is `X` and `Y` concatenated.
list_append([],L,L) :-
list(L).
list_append([X|Xs],Ys,[X|Zs]) :-
list_append(Xs,Ys,Zs).
list([]).
list([_|Y]) :-
list(Y). :- module(_,_,[doccomments]).
% :- use_package(sr/bfall).
%! binary_tree(T): `T` is a binary tree.
binary_tree(void).
binary_tree(tree(_Element,Left,Right)) :-
binary_tree(Left),
binary_tree(Right). ?- tree_example(T), binary_tree(T).
?- tree_example(_T), binary_tree(_T).
?- binary_tree(tree(a, b, c)).
?- binary_tree(tree(a, [], [])).
?- binary_tree(T).
:- use_package(functional). %! binary_t(T): `T` is a binary tree. binary_t := void | tree(_Element, ~binary_t, ~binary_t).
%! tree_member(X,T): `X` is a node in binary tree `T`.
tree_member(X,tree(X,Left,Right)) :-
binary_tree(Left),
binary_tree(Right).
tree_member(X,tree(_,Left,_Right)) :-
tree_member(X,Left).
tree_member(X,tree(_,_Left,Right)) :-
tree_member(X,Right). ?- tree_example(_T), tree_member(b, _T).
?- tree_example(_T), tree_member(X, _T).
?- tree_example(_T), tree_member(e, _T).
?- tree_member(e,T).
?- tree_member(e,T), tree_member(a,T).
tree_mem_f(X) := tree( X, ~binary_tree, ~binary_tree)
| tree(_Y, ~tree_mem_f(X), _Right)
| tree(_Y, _Left, ~tree_mem_f(X)). ?- tree_example(_T), tree_mem_f(b, _T).
?- tree_example(_T), tree_mem_f(X, _T).
?- tree_example(_T), tree_mem_f(e, _T).
?- tree_member(e,T).
?- tree_member(e,T), tree_mem_f(a,T).
%! tree_example(T): `T` is a tree example.
tree_example( tree( a,
tree( b,
void,
void
),
tree( c,
tree( b,
void,
void
),
void
)
)). :- module(_,_,[doccomments]).
%! \title Binary tree example: preorder
%! pre_order(T,L): `L` is the list of elements of tree `T` in preorder.
pre_order(void, []).
pre_order(tree(X,Left,Right), Elements) :-
pre_order(Left, ElementsLeft),
pre_order(Right, ElementsRight),
list_append([X|ElementsLeft], ElementsRight, Elements). ?- tree_example(_T),pre_order(_T,List).
list_append([],L,L) :-
list(L).
list_append([X|Xs],Ys,[X|Zs]) :-
list_append(Xs,Ys,Zs).
tree_example( tree( a,
tree( b,
void,
void
),
tree( c,
tree( b,
void,
void
),
void
)
)). :- module(_,_,[doccomments,sr/bfall]). %! \title Polymorphic member %! \module We can write a 'polymorphic' member that works % with both lists and trees. %! lt_member(X,LT): X is a member of the list or tree LT. lt_member(X, [X|Y]) :- list(Y). lt_member(X, [_|T]) :- lt_member(X, T). lt_member(X, tree(X, L, R)) :- binary_tree(L), binary_tree(R). lt_member(X, tree(_Y, L, _R)) :- lt_member(X, L). lt_member(X, tree(_Y, _L, R)) :- lt_member(X, R).
?- lt_member(M,[1,2,3]).
?- tree_example(_T), lt_member(M,_T).However:
?- lt_member(M,T).We also get elements that are of mixed type –because that is what we specified!
%! lt_member_separate(X,LT): X is a member of the list or tree LT. lt_member_separate(X, Y) :- list_member(X, Y). lt_member_separate(X, Y) :- tree_member(X, Y).Try:
?- lt_member_separate(M,[1,2,3]).
?- tree_example(_T), lt_member_separate(M,_T).And:
?- lt_member_separate(M,T).Now we obtain separated types!
:- redefining(list/1).
list([]).
list([_|Y]) :-
list(Y).
list_member(X,[X|L]) :-
list(L).
list_member(X,[_|T]) :-
list_member(X,T).
tree_member(X,tree(X,Left,Right)) :-
binary_tree(Left),
binary_tree(Right).
tree_member(X,tree(_,Left,_Right)) :-
tree_member(X,Left).
tree_member(X,tree(_,_Left,Right)) :-
tree_member(X,Right).
binary_tree(void).
binary_tree(tree(_Element,Left,Right)) :-
binary_tree(Left),
binary_tree(Right).
tree_example( tree( a,
tree( b,
void,
void
),
tree( c,
tree( b,
void,
void
),
void
)
)). :- module(_,_,[sr/bfall]).
polynomial(X,X).
polynomial(Term,_X) :-
pconstant(Term).
polynomial(Term1+Term2,X) :-
polynomial(Term1,X),
polynomial(Term2,X).
polynomial(Term1-Term2,X) :-
polynomial(Term1,X),
polynomial(Term2,X).
polynomial(Term1*Term2,X) :-
polynomial(Term1,X),
polynomial(Term2,X).
polynomial(Term1/Term2,X) :-
polynomial(Term1,X),
pconstant(Term2).
polynomial(Term1^N,X) :-
polynomial(Term1,X),
nat_num(N).
pconstant(X) :-
nat_num(X).
pconstant(a).
pconstant(b).
pconstant(c).Try:?- polynomial( a * x ^ s(s(0)) + b, x).
?- polynomial(P, x).
?- polynomial( a * x ^ x + b, x).
:- use_package(fsyntax).
poly_f(X) := X
| ~pconst_f
| ~poly_f(X) + ~poly_f(X)
| ~poly_f(X) - ~poly_f(X)
| ~poly_f(X) * ~poly_f(X)
| ~poly_f(X) / ~pconst_f
| ~poly_f(X) ^ ~nat_num.
pconst_f := ~nat_num | a | b | c.Try:?- poly_f(x, a * x ^ s(s(0)) + b).
?- poly_f(x, P).
?- poly_f(x, a * x ^ x + b ).
nat_num(0).
nat_num(s(X)) :-
nat_num(X). :- module(_,_,[sr/bfall]). %! deriv(E,X,D): `D` is the derivative of expression `E` w.r.t. `X`. deriv(X, X, s(0) ). deriv(C, _X, 0 ) :- pconstant(C). deriv(U+V, X, DU+DV ) :- deriv(U,X,DU), deriv(V,X,DV). deriv(U-V, X, DU-DV ) :- deriv(U,X,DU), deriv(V,X,DV). deriv(U*V, X, DU*V+U*DV ) :- deriv(U,X,DU), deriv(V,X,DV). deriv(U/V, X, (DU*V-U*DV)/V^s(s(0))) :- deriv(U,X,DU), deriv(V,X,DV). deriv(U^s(N),X, s(N)*U^N*DU ) :- deriv(U,X,DU), nat_num(N). deriv(log(U),X, DU/U ) :- deriv(U,X,DU). pconstant(X) :- nat_num(X). pconstant(a). pconstant(b). pconstant(c).
?- deriv(s(s(s(0)))*x+s(s(0)),x,Y).
?- deriv(s(s(s(0)))*x+s(s(0)),x,0*x+s(s(s(0)))*s(0)+0).
?- deriv(E,x,0*x+s(s(s(0)))*s(0)+0).
:- use_package(fsyntax). derivf(X, X) := s(0). derivf(~pconstant, _) := 0. derivf(U+V, X) := ~derivf(U,X) + ~derivf(V,X). derivf(U-V, X) := ~derivf(U,X) - ~derivf(V,X). derivf(U*V, X) := ~derivf(U,X) * V + U * ~derivf(V,X). derivf(U/V, X) := ( ~derivf(U,X) * V - U * ~derivf(V,X) ) / V^s(s(0)). derivf(U^s(N), X) := s(N) * U^N * ~derivf(U,X) :- nat_num(N). derivf(log(U), X) := ~derivf(U,X) / U.
?- derivf(s(s(s(0)))*x+s(s(0)),x,Y).
?- derivf(s(s(s(0)))*x+s(s(0)),x,0*x+s(s(s(0)))*s(0)+0).
?- derivf(E,x,0*x+s(s(s(0)))*s(0)+0).
nat_num(0).
nat_num(s(X)) :-
nat_num(X). a ----> b f
| | |
v v v
c ----> d ----> e g
|
v
hedge_da(a,b). edge_da(a,c). edge_da(b,d). edge_da(c,d). edge_da(d,e). edge_da(d,h). edge_da(f,g).
path_da(X,Y) :-
edge_da(X,Y).
path_da(X,Y) :-
edge_da(X,Z),
path_da(Z,Y).
circuit_da :- path_da(A,A). ?- path_da(a,e).
yes
?- path_da(a,g).
noNodes that c is connected to:
?- path_da(c,X).All connected nodes:
?- path_da(X,Y).(We get duplicate solutions for different paths.)
graph_da([
edge(a,b),
edge(a,c),
edge(b,d),
edge(c,d),
edge(d,e),
edge(d,h),
edge(f,g) ]).
path_l(X,Y,G) :-
member(edge(X,Y),G).
path_l(X,Y,G) :-
member(edge(X,Z),G),
path_l(Z,Y,G). ?- graph_da(_G), path_l(a,e,_G).
yes
?- graph_da(_G), path_l(a,g,_G).
noNodes that c is connected to:
?- graph_da(_G), path_l(c,X,_G).All connected nodes:
?- graph_da(_G), path_l(X,Y,_G).(We get duplicate solutions for different paths.)
path_a(X,Y) :-
connected_a(X,Y).
path_a(X,Y) :-
connected_a(X,Z),
path_a(Z,Y).
connected_a(X,Y) :- edge_da(X,Y).
connected_a(Y,X) :- edge_da(X,Y).
circuit_a :- path_a(A,A). ?- path_a(a,e).
yesNodes that c is conn to - but duplicates (infinite):
?- path_a(c,X).Also duplicates (infinite):
?- path_a(X,Y).Loops:
?- path_a(a,g).
a <---- b f
| ^ |
v | v
c ----> d ----> e g
|
v
h graph([
edge(a,c),
edge(c,d),
edge(d,b),
edge(b,a),
edge(d,e),
edge(d,h),
edge(f,g) ]). path(X,Y,G) :-
member(edge(X,Y),G).
path(X,Y,G) :-
list_select(edge(X,Z),G,GRest),
path(Z,Y,GRest).
% `list_select(X,L,NL)`: selects an element `X` out of list `L`
% and `NL` is `L` without `X`.
list_select(X, [X|Xs], Xs).
list_select(X, [Y|Ys], [Y|Zs]) :-
list_select(X, Ys, Zs). ?- graph(_G), path(a,e,_G).
?- graph(_G), path(a,g,_G).
?- graph(_G), path(c,X,_G).
?- graph(_G), path(X,Y,_G).
where q0 is both the initial and the final state.
:- module(_,_,[sr/bfall]). accept(S) :- initial(Q), accept_from(S, Q). accept_from([],Q) :- final(Q). accept_from([C|Cs],Q) :- delta(Q, C, NewQ), accept_from(Cs,NewQ).
initial(q0). final(q0).and its transitions:
delta(q0, a, q1). delta(q1, b, q0). delta(q1, b, q1).
a
-------------
| | ___
___|__ ___v__/ \
| q0 | | q1 | | b
------ ------< |
^ | \__/
| |
-------------
b ?- accept([a,b,b]).
?- accept([a,b,b,a]).
?- accept([A, B, C, D]).
?- accept(X).
:- module(_,_,[sr/bfall]).
npda_accept(S) :-
npda_initial(Q),
npda_accept_from(S, Q, []).
npda_accept_from([], Q, []) :-
npda_final(Q).
npda_accept_from([C|Cs], Q, Stack) :-
npda_delta(Q, C, Stack, NewQ, NStack),
npda_accept_from(Cs, NewQ, NStack). npda_initial(q0). npda_final(q1).and its transitions:
npda_delta(q0, C, Stack, q0, [C|Stack]). npda_delta(q0, C, Stack, q1, [C|Stack]). npda_delta(q0,_C, Stack, q1, Stack ). npda_delta(q1, C, [C|Stack], q1, Stack ).
?- npda_accept([a,b,b,a]).
?- npda_accept([a,b,c,b,a]).
?- npda_accept([a,b,c,c,b,a]).
?- npda_accept([a,b,X,a]).
?- npda_accept([A, B, C, D]).
?- npda_accept(X).
npda_accept_alt(S) :-
npda_initial_alt(Q),
npda_accept_from_alt(S, Q, []).
npda_accept_from_alt([], Q, []) :-
npda_final_alt(Q).
npda_accept_from_alt([C|Cs], Q, Stack) :-
npda_delta_alt(Q, C, Stack, NewQ, NStack),
npda_accept_from_alt(Cs, NewQ, NStack). npda_initial_alt(q0). npda_final_alt(q1). npda_delta_alt(q0, C, Stack, q0, [C|Stack]) :- symbol(C). npda_delta_alt(q0, C, Stack, q1, [C|Stack]) :- symbol(C). npda_delta_alt(q0, C, Stack, q1, Stack ) :- symbol(C). npda_delta_alt(q1, C, [C|Stack], q1, Stack ) :- symbol(C). symbol(a). symbol(b). symbol(c).
?- npda_accept_alt([a,b,b,a]).
?- npda_accept_alt([a,b,c,b,a]).
?- npda_accept_alt([a,b,c,c,b,a]).
?- npda_accept_alt([a,b,X,a]).
?- npda_accept_alt([A, B, C, D]).
?- npda_accept_alt(X).
are represented by:
hanoi_moves( s(s(s(0))),
[ move(a,b), move(a,c), move(b,c), move(a,b),
move(c,a), move(c,b), move(a,b) ])
move N-1 disks to peg C using peg B, move the bottom disk to peg B, and then move the N-1 disks from peg C to peg B using peg A.
:- module(_,_,[]).
hanoi_moves(N,Moves) :-
hanoi(N,a,b,c,Moves).
hanoi(s(0),A,B,_,[move(A, B)]).
hanoi(s(N),A,B,C,Moves) :-
hanoi(N,A,C,B,Moves1),
hanoi(N,C,B,A,Moves2),
list_append(Moves1,[move(A, B)|Moves2],Moves).Try:?- hanoi_moves(s(s(s(0))),M).
?- hanoi_moves(s(s(s(0))),M), hanoi_moves(N,M).
?- hanoi_moves(N,M).
?- hanoi_test(NDisks).and hit Next for increasing numbers of disks. Execution time is exponential in the number of disks!
hanoi_test(D) :-
between(1,1000,D),
decimal_peano(D,N),
statistics(walltime, [_,_]),
hanoi_moves(N,M),
statistics(walltime, [_,T]),
length(M,L),
write(D), write(' disks = '),
write(L), write(' moves in '),
write(T), write(' mS').
:- use_module(engine(runtime_control),[statistics/2]).
:- use_module(library(write),[write/1]).
:- use_module(library(lists),[length/2]).
:- use_module(library(between)).
list_append([],L,L) :-
list(L).
list_append([X|Xs],Ys,[X|Zs]) :-
list_append(Xs,Ys,Zs).
list([]).
list([_|Y]) :-
list(Y).
decimal_peano(0,0).
decimal_peano(N,s(X)) :-
N>0,
NN is N-1,
decimal_peano(NN,X).