Lab 3
Lab 3
Recursion
1. Recursive Definition
Predicates can be defined recursively. A predicate is recursively defined if one or
more rules in its definition refer to itself.
1.1. Example 1
Consider the following knowledge base:
is_digesting(X,Y) :- just_ate(X,Y).
is_digesting(X,Y) :-
just_ate(X,Z),
is_digesting(Z,Y).
just_ate(mosquito,blood(john)).
just_ate(frog,mosquito).
just_ate(stork,frog).
The procedural meaning (how the recursive definition is used) is: for any query
asking whether X has just eaten Y, Prolog first checks whether the base clause
1
applies. If it does not apply, the recursive clause gives Prolog another strategy
for determining whether X is digesting Y: it can try to find some Z such that X
has just eaten Z, and Z is digesting Y. That is, this rule lets Prolog break the task
apart into two subtasks. Doing so will eventually lead to simple problems which
can be solved by simply looking up the answers in the knowledge base.
?- is_digesting(stork,mosquito).
then Prolog, first, tries to make use of the first rule listed concerning
is_digesting; that is, the base rule. This tells it that X is digesting Y if X just
ate Y, By unifying X with stork and Y with mosquito it obtains the following
goal:
just_ate(stork,mosquito).
But the knowledge base doesn’t contain the information, so this attempt fails. So
Prolog next tries to make use of the second rule. By unifying X with stork and
Y with mosquito it obtains the following goals:
just_ate(stork,Z),
is_digesting(Z,mosquito).
Now, Prolog needs to find a value for Z such that follows the above two facts.
And there is such a value for Z, namely frog. It is immediate that
just_ate(stork,frog).
will succeed, for this fact is listed in the knowledge base. Now it has to deduce
is_digesting(frog,mosquito).
To deduce so, it again tries to use the first clause of is_digesting/2
This reduces this goal to deducing
just_ate(frog,mosquito).
and this is a fact listed in the knowledge base.
Note that it is crucial to have a base clause/escape clause otherwise Prolog runs
into an infinite loop.
1.2. Example 2
Suppose we have a knowledge base recording facts about the child
relation:
2
child(martha,charlotte).
child(charlotte,caroline).
child(caroline,laura).
child(laura,rose).
Suppose we wished to define the descendant relation; that is, the relation of
being a child of, or a child of a child of, or a child of a child of a child of, or....
We could add the following two non-recursive rules to the knowledge base:
descend(X,Y) :- child(X,Y).
descend(X,Y) :- child(X,Z), child(Z,Y).
These definitions work up to a point, but they are clearly extremely limited.
The two rules are inadequate. For example, if we pose the queries
?- descend(martha,laura).
or
?- descend(charlotte,rose).
we get the answer ‘No!’, which is not what we want. We could add the following
rules:
descend(X,Y) :- child(X,Z_1), child(Z_1,Z_2), child(Z_2,Y).
descend(X,Y) :- child(X,Z_1),
child(Z_1,Z_2),
child(Z_2,Z_3),
child(Z_3,Y).
But this approach is clumsy and hard to read.
The following recursive rule, however, fixes everything exactly the way we want:
descend(X,Y) :- child(X,Y).
descend(X,Y) :- child(X,Z), descend(Z,Y).
3
descend(martha,lau
child(martha,_G490
_G490 =
charlotte
child(charlotte,_G4
_G494 =
caroline
It is obvious from this example that no matter how many generations of children
we add, we will always be able to work out the descendant relation. That is, the
recursive definition is both general and compact: it contains all the information in
the previous rules, and much more besides.
1.3. Example 3
Now we will see examples of building structures through recursion.
4
numeral(0).
numeral(succ(X)) :- numeral(X).
1.4. Example 4
As a final example, let’s see whether we can use the representation of numerals
that we introduced in the previous section for doing simple arithmetic. Let’s try to
define addition. That is, we want to define a predicate add/3 which when given
two numerals as the first and second argument returns the result of adding them
up as its third argument. E.g. Following queries should give results as follows:
add(succ(succ(0)),succ(succ(0)),succ(succ(succ(succ(0))))).
yes
5
?- add(succ(succ(0)),succ(0),Y).
Y = succ(succ(succ(0)))
Call: (6)
add(succ(succ(succ(0))), succ(succ(0)), R)
Call: (7)
add(succ(succ(0)), succ(succ(0)), _G648)
Call: (8)
add(succ(0), succ(succ(0)), _G650)
Call: (9)
add(0, succ(succ(0)), _G652)
Exit: (9)
add(0, succ(succ(0)), succ(succ(0)))
Exit: (8)
add(succ(0), succ(succ(0)), succ(succ(succ(0))))
Exit: (7)
add(succ(succ(0)), succ(succ(0)),
succ(succ(succ(succ(0)))))
Exit: (6) add(succ(succ(succ(0))), succ(succ(0)),
6
succ(succ(succ(succ(succ(0))))))
Underlying logic programming is a simple (and seductive) vision: the task of the
programmer is simply to describe problems. The programmer should write down
(in the language of logic) a declarative specification (that is: a knowledge base),
which describes the situation of interest. The programmer shouldn’t have to tell
the computer what to do. To get information, he or she simply asks the
questions. It’s up to the logic programming system to figure out how to get the
answer.
7
In short, the two definitions of descend/2 have the same declarative meaning
but different procedural meanings: from a purely logical perspective they are
identical, but they behave very differently.
Two more variants for the definition of the predicate are as follows:
descend(X,Y) :- child(X,Y).
descend(X,Y) :- descend(Z,Y), child(X,Z).
and
descend(X,Y) :- child(X,Z), descend(Z,Y).
descend(X,Y) :- child(X,Y).
Thus the declarative and procedural meanings of a Prolog program can differ,
when writing Prolog programs you need to bear both aspects in mind. Try
drawing the search trees of all the variants.
3. Exercises
1. Do you know these wooden Russian dolls, where smaller ones are
contained in bigger ones? Here is a schematic picture of such dolls.
8
contained in which other doll. E.g. the query in(katarina,natasha)
should evaluate to true, while in(olga, katarina) should fail.
9
Hint: What happens when you ask the query descend(rose,martha)?
10
byPlane(frankfurt,bangkok).
byPlane(frankfurt,singapore).
byPlane(paris,losAngeles).
byPlane(bangkok,auckland).
byPlane(losAngeles,auckland).
Write a predicate travel/2 which determines whether it is possible to
travel from one place to another by ‘chaining together’ car, train, and plane
journeys. For example, your program should answer ‘yes’ to the query
travel(valmont,raglan).
11