Introduction to Prolog
by Paul Brna and Tamsin
Treasure-Jones at the University of Leeds
http://cbl.leeds.ac.uk/~tamsin/prologtutorial. Also check out
Paul Brna's on-line book at
http://cblslca.leeds.ac.uk/~paul/prologbook.)Problem --> Algorithm --> Assembly Code --> Machine Code |------------->--------------| Assembler |-------------------->---------------------| C compiler |------------------------>-----------------------------| Prolog Interpreter
sunny.
?- sunny.
john_is_cold. /* john is cold */
raining. /* it is raining */
john_Forgot_His_Raincoat. /* john forgot his raincoat */
fred_lost_his_car_keys. /* fred lost is car keys */
peter_footballer. /* peter plays football */
?- john_Forgot_His_Raincoat.
yes
?- raining.
yes
?- foggy.
no
relation(<argument1>,<argument2>,....,<argumentN> ).
likes(john,mary).
eats(fred,oranges). /* "Fred eats oranges" */
eats(fred,t_bone_steaks). /* "Fred eats T-bone steaks" */
eats(tony,apples). /* "Tony eats apples" */
eats(john,apples). /* "John eats apples" */
eats(john,grapefruit). /* "John eats grapefruit" */
?- eats(fred,oranges). /* does this match anything in the database? */
yes /* yes, matches the first clause in the database */
?- eats(john,apples). /* do we have a fact that says john eats apples? */
yes /* yes we do, clause 4 of our eats database */
?- eats(mike,apples). /* how about this query, does mike eat apples */
no /* not according to the above database. */
?- eats(fred,apples). /* does fred eat apples */
no /* again no, we don't know whether fred eats apples */
age(john,32). /* John is 32 years old */
age(agnes,41). /* Agnes is 41 */
age(george,72). /* George is 72 */
age(ian,2). /* Ian is 2 */
age(thomas,25). /* Thomas is 25 */
?- age(ian,2). /* is Ian 2 years old? */
yes /* yes, matches against the fourth clause of age */
?- agnes(41). /* for some relation agnes are they 41 */
no /* No. In the database above we only know about the relation */
/* age, not about the relation agnes, so the query fails */
?- age(ian,two) /* is Ian two years old? */
no /* No. two and 2 are not the same and therefore don't match */
eats(fred,mangoes).
?- eats(fred,what).
X /* a capital letter */
VaRiAbLe /* a word - it be made up or either case of letters */
My_name /* we can link words together via '_' (underscore) */
?- eats(fred,What).
What=mangoes
yes
loves(john,mary).
loves(fred,hobbies).
?- loves(john,Who). /* Who does john love? */
Who=mary /* yes , Who gets bound to mary */
yes /* and the query succeeds*/
?- loves(arnold,Who) /* does arnold love anybody */
no /* no, arnold doesn't match john or fred */
?- loves(fred,Who). /* Who does fred love */
Who = hobbies /* Note the to Prolog Who is just the name of a variable, it */
yes /* semantic connotations are not picked up, hence Who unifies */
/* with hobbies */
tape(1,van_morrison,astral_weeks,madam_george).
tape(2,beatles,sgt_pepper,a_day_in_the_life).
tape(3,beatles,abbey_road,something).
tape(4,rolling_stones,sticky_fingers,brown_sugar).
tape(5,eagles,hotel_california,new_kid_in_town).
?- tape(5,Artist,Album,Fave_Song). /* what are the contents of tape 5 */
Artist=eagles
Album=hotel_california
Fave_Song=new_kid_in_town
yes
?- tape(4,rolling_stones,sticky_fingers,Song). /* find just song */
Song=brown_sugar /* which you like best from the album */
yes
mortal(X) :-
human(X).
?- mortal(P).
P = socrates
yes
fun(X) :- /* an item is fun if */
red(X), /* the item is red */
car(X). /* and it is a car */
fun(X) :- /* or an item is fun if */
blue(X), /* the item is blue */
bike(X). /* and it is a bike */
fun(ice_cream). /* and ice cream is also fun. */
?- fun(harley_davidson). /* to which Prolog will reply */
yes /* to show the program succeeded */
?- fun(What).
What=vw_beatle
yes
eats(fred,pears).
eats(fred,t_bone_steak).
eats(fred,apples).
?- eats(fred,FoodItem).
FoodItem = pears
FoodItem = t_bone_steak
FoodItem = apples
?- hold_party(Who).
fun(X) :- /* something is either fun because its .... */
red(X), /* red */
car(X). /* and a car */
fun(X) :- /* or its fun if its.... */
blue(X), /* blue */
bike(X). /* and a bike */
/* database of red items */
red(apple_1).
red(block_1).
red(car_27).
/* database of cars */
car(desoto_48).
car(edsel_57).
?- fun(What).
/* clause 1 */
fun(X) :-
red(X),
car(X).
/* clause 2 */
fun(X) :-
blue(X),
bike(X).
/* database of red items */
red(apple_1). first choice
red(block_1).
red(car_27).
/* database of cars */
car(desoto_48).
car(edsel_57).
red(apple_1).
red(block_1). second choice
red(car_27).
red(apple_1).
red(block_1).
red(car_27). third choice
car(desoto_48).
car(edsel_57).
/* database of blue items */
blue(flower_3).
blue(glass_9).
blue(honda_81).
bike(iris_8).
bike(my_bike).
bike(honda_81).
blue(flower_3).
blue(glass_9).
blue(honda_81).
?- fun(What).
What=honda_81
yes
[first,second,third] = [A|B]
where A = first and B=[second,third]
[a,b,c,d,e,f,g]
[apple,pear,bananas,breadfruit]
[ ] /* this is a special list, it is called the empty list because it contains nothing */
[a,b,c] unifies with [Head|Tail] resulting in Head=a and Tail=[b,c]
[a] unifies with [H|T] resulting in H=a and T=[]
[a,b,c] unifies with [a|T] resulting in T=[b,c]
[a,b,c] doesn't unify with [b|T]
[] doesn't unify with [H|T]
[] unifies with []. Two empty lists always match
p([H|T], H, T).
?- p([a,b,c], X, Y).
X=a
Y=[b,c]
yes
?- p([a], X, Y).
X=a
Y=[]
yes
?- p([], X, Y).
no
(Taken from material written by James Power)
Prolog differs from imperative languages (like C) in that it concentrates on dealing with facts and rules, rather than sequences of instructions. However, for effificency, it can sometimes be desirable to add explicit control information to programs - this is the purpose of the cut.
grade(N,first) :- N>=70. grade(N,two_1) :- N<70, N>=63. grade(N,two_2) :- N<63, N>=55. grade(N,third) :- N<55, N>=50. grade(N,pass) :- N<50, N>=40. grade(N,fail) :- N<40.While this will work it is a little inefficient. The query grade(75,G) will answer G=first as expected but, once this has been satisfied, Prolog will go back to look for any other solutions. In order to do this it will process all of the other options, failing during the body of the rule in each case.
If we were implementing this in C, we might try using a "switch" statement as follows:
// This code is a little artifical for the purpose of comparison int fir(int n) { return n>=70; } int fir(int n) { return n<70 && n>=63; } // ... fill in the rest ... int fai(int n) { return n<40; } switch(n) { case(fir(n)): cout << "1st"; break; case(tw1(n)): cout << "2.1"; break; case(tw2(n)): cout << "2.2"; break; case(thi(n)): cout << "3rd"; break; case(pas(n)): cout << "Pass"; break; case(fai(n)): cout << "Fail"; }Here we explicitly indicate that after one result has been accepted, we need not look at any of the others at all - this is the purpose of the "break" statement in each branch.
We can do something similar in Prolog to improve efficiency. Basically, we want to tell Prolog that once it has satisfied one version of the predicate, it need look at no other. Prolog's equivalent of the break statement here is the cut, written "!".
To elimiate useless backtracking from the above, (and taking advantage of Prolog's order of execution) we can rephrase the program as:
grade(N,first) :- N>=70, ! . grade(N,two_1) :- N>=63, ! . grade(N,two_2) :- N>=55, ! . grade(N,third) :- N>=50, ! . grade(N,pass) :- N>=40, ! . grade(N,fail) :- N<40.The cut predicate has the effect of telling Prolog not to pass back through this point when it is looking for alternative solutions. Thus, the ``!'' acts as a marker, back beyond which Prolog will not go. When it passes this point all choices that is has made so far are ``set''; ie. they are treated as though they were the only possible choices.
Note that the cut always appears where a predicate can appear (never, for example, as arguments to a predicate). It is treated at this level just like any other predicate, and it alwayssucceeds.
In summary, the effect of the cut is as follows:
holiday(friday,may1). weather(friday,fair). weather(saturday,fair). weather(sunday,fair). weekend(saturday). weekend(sunday). % We go for picnics on good weekends and May 1st picnic(Day) :- weather(Day,fair), weekend(Day). picnic(Day) :- holiday(Day,may1).Pose the query:
picnic(When).You should get three answers; make sure you understand where they come from! Note that in order to get this answer, Prolog had to work through exactly one unsuccessful instantiation of When with "friday", before getting it right the second time.
picnic(Day) :- weather(Day,fair), !, weekend(Day). picnic(Day) :- holiday(Day,may1).Now when we pose the query: Picnic(When) Prolog will try to satisfy the sub-goal:
weather(When,fair), !, weekend(When).The first rule for weather is:
weather(friday,fair),so the new sub-goal becomes:
....., !, weekend(friday).Prolog passes the cut, and goes on to try to satisfy
weekend(friday)which fails. Previously, it would have backtracked to the last choice point, and gone on with processing
weather(saturday,fair)But now the presence of the cut stops it going back, so it is trapped between the cut and the end of the (failed) predicate.
The answer now is simply:
No.