Prolog
History
- roots in predicate calculus and theorem proving
- Alain Colmeraur (Marseilles) invented Prolog in the early 70's
- David Warren - University of Edinburgh - implemented compiler
for DEC PDP-10
The existence of a de facto language standard and widely available,
efficient implementation was very important for language acceptance
(compare with functional languages).
Fifth Generation project in Japan - 1984 - adopted logic programming
currently Quintus is a commercial Prolog vendor
Lots of offshoots of Prolog:
- constraint logic programming: CLP(R), CHIP,
Prolog III, Trilogy, HCLP, etc.
- concurrent logic programming: FCP, Strand, etc etc.
- concurrent constraint programming
Running Prolog
To run prolog on lynx:
- teletype version: prolog
- X terminal version: qui
(the license doesn't seem to let it run on wolf or grizzly)
Try turning on debugging to get a better feel for the operation of Prolog.
You can also use the clpr system (a constraint logic programming language),
which offers most of the features of Prolog plus constraints.
Program Structure
A Prolog program is a collection of facts and rules (like axioms). A query
is like a theorm to be proved.
two modes: enter assertions; make queries
Suppose we have the following Prolog program in a file likes.pl :
likes(fred,beer).
likes(fred,cheap_cigars).
likes(fred,monday_night_football).
likes(sue,jogging).
likes(sue,yogurt).
likes(sue,bicycling).
likes(sue,noam_chomsky).
likes(mary,jogging).
likes(mary,yogurt).
likes(mary,bicycling).
likes(mary,george_bush).
politically_correct(X) :-
likes(X,yogurt),
likes(X,noam_chomsky).
low_life(X) :-
likes(X,cheap_cigars).
File these in by saying
| ?- consult(likes).
Make queries:
| ?- likes(fred,beer).
yes.
| ?- likes(fred,X).
X = beer
| ?- likes(fred,X).
X = beer ;
X = cheap_cigars ;
X = monday_night_football ;
no
| ?- likes(Y,jogging).
Y = sue ;
Y = mary ;
no
| ?- politically_correct(A).
A = sue ;
no
Key concepts in Prolog
- logic variables
(scope rules: variables locally scoped within a fact, rule, or query)
- unification (two-way pattern matching)
- depth-first search; backtracking
- dual declarative and procedural reading of Prolog program
Prolog data types
- variables -- begin with capital letter
X, Y, Fred, A_very_long_variable_name
anonymous variable: _
- integers, reals
- atoms -- begin with lower-case letter
x, y, fred, a_very_long_atom_name
- lists
[] -- the empty list
[10]
[10,11,12]
[ [john,paul,george,ringo], dylan]
- structures
point(10,30)
line(point(10,30),point(99,100))
lists could be represented using structures:
cons(4,cons(5,cons(6,nil)))
point, line, cons are unevaluated function symbols
vertical bar -- item after that is the remainder of the list
A = [4,5,6], B=[3|A]. /* then B =[3,4,5,6] */
A = [4,5,6], B=[3,A]. /* then B =[3,[4,5,6]] */
Some sample list manipulation programs
append([],Ys,Ys).
append([X|Xs],Ys,[X|Zs]) :- append(Xs,Ys,Zs).
| ?- append([1,2],[3,4,5],Q).
Q = [1,2,3,4,5]
| ?- append([1,2],M,[1,2,3,4,5,6]).
M = [3,4,5,6]
| ?- append(A,B,[1,2,3]).
A = [],
B = [1,2,3] ;
A = [1],
B = [2,3] ;
A = [1,2],
B = [3] ;
A = [1,2,3],
B = [] ;
no
| ?-
| ?- append(A,B,C).
A = [],
B = C = _3278 ;
A = [_3443],
B = _3278,
C = [_3443|B] ;
A = [_3443,_3447],
B = _3278,
C = [_3443,_3447|B] ;
A = [_3443,_3447,_3451],
B = _3278,
C = [_3443,_3447,_3451|B] ;
A = [_3443,_3447,_3451,_3455],
B = _3278,
C = [_3443,_3447,_3451,_3455|B] ;
| ?-
member(X,[X|Xs]).
member(X,[Y|Ys]) :- member(X,Ys).
| ?- member(3,[1,2,3,4]).
yes
| ?- member(X,[1,2,3,4]).
X = 1 ;
X = 2 ;
X = 3 ;
X = 4 ;
no
| ?- member(1,X).
X = [1|_3502] ;
X = [_3501,1|_3504] ;
X = [_3501,_3503,1|_3506] ;
X = [_3501,_3503,_3505,1|_3508] ;
X = [_3501,_3503,_3505,_3507,1|_3510] ;
X = [_3501,_3503,_3505,_3507,_3509,1|_3512]
sublist(S,Z) :- append(A,S,X), append(X,Y,Z).
/* clauses to find all permutations of a list */
permute([],[]).
permute([H|T],L) :-
permute(T,U),
insert(H,U,L).
/* insert an element X somewhere in list L */
insert(X,L,[X|L]).
insert(X,[H|T],[H|U]) :-
insert(X,T,U).
/* inefficient sort */
sort(L,S) :- permute(L,S), sorted(S).
sorted([]).
sorted([X]).
sorted([A,B|R]) :-
A=
Prolog warts
- arithmetic
- cut
- negation
- assert, retract
- evaluable predicates
Arithmetic
X is 3+4, Y is X*X.
(compare with X = 3+4.)
factorial(0,1).
factorial(N,F) :-
N>0,
N1 is N-1,
factorial(N1,F1),
F is N*F1.
Cut
Cut prunes the search tree
examples:
compile(Source,Code) :- Parse(Source,Tree), !, encode(Tree,Code).
member(X,[X|Xs]) :- !.
member(X,[Y|Ys]) :- member(X,Ys).
| ?- member(1,[1,2,3,1]). /* will succeed twice */
but: member(X,[1,2,3]) will only give one answer: X=1.
Negation in Prolog
not(X) :- call(X), !, fail.
not(X).
| ?- not(3=4).
yes
| ?- not(3=3).
no
| ?- not(X=3), X=4.
no
| ?- X=4, not(X=3).
yes
Other Features
assert(X) -- asserts a clause in the database
retract(X) -- retracts a clause from the database
evaluable predicates:
read(X).
write(Y).
meta-logical predicates:
var(X)
nonvar(X)
Some Prolog programming techniques
- use of backtracking to replace simple loops
- meta-interpreters
- incomplete data structures
Simple Prolog Meta-Interpreter for Pure Prolog
goal(true).
goal(A,B) :- goal(A), goal(B).
goal(A) :- clause(A,B), goal(B).
Difference Lists (example of incomplete data structures)
represent a list S as the difference of two lists X and Y
define a new infix operator \ ...
write the difference list as X\Y
these all represent the list [1,2,3]:
[1,2,3]\[]
[1,2,3,4,5]\[4,5]
[1,2,3,4,5,6,7,8]\[4,5,6,7,8]
[1,2,3|M]\M
Notice that the first 3 examples are all substitution instances of the last
one!
cheap_append( X\Y , Y\Z , X\Z).
example: ?- cheap_append( [1,2,3|M]\M , [8,9,10|N]\N , A\P )
succeeds with A = [1,2,3,8,9,10]\N , P = N
?- cheap_append( [1,2,3|M]\M , [8,9,10|N]\N , A\[] )
succeeds with A = [1,2,3,8,9,10]
Example of using difference lists:
/* Naive flatten: /*
flatten([],[]).
flatten([X|Xs],Y) :-
flatten(X,XF),
flatten(Xs,XsF),
append(XF,XsF,Y).
flatten(X,[X]).
----------------------------------------
/* Flatten using difference lists: /*
/* declare "\" as a new infix operator */
:- op(400,xfx,\).
flatten(S,F) :-
flatten_dl(S,F\[]).
flatten_dl([],X\X).
flatten_dl([X|Xs],Y\Z) :-
flatten_dl(X,Y\T),
flatten_dl(Xs,T\Z).
flatten_dl(X,[X|Z]\Z).
By using difference lists, we can write an efficient flatten routine
(comparable in efficiency to one in an imperative language), while still
retaining a declarative programming style.