In this tutorial we simply want to practice using recursion. This is really important in Prolog, and we'll be using it a lot from now on, so you should try and work through all of the following...
In imperative languages like C/C++/Java we deal with situations which require iteration by means of constructs like while, do, for and so on. Prolog does not use these imperative-style constructs: instead, when we need to iterate, we use recursion. Recursion can take a little time to get used to, but it will be used in almost every non-trivial Prolog program from now on.
Basically recursion involves defining something in terms of itself. The key to ensuring that this makes sense is that you always define something in terms of a smaller copy of itself. Recursion is the algorithmic equivalent of "proof by induction" in maths.
When you do recursion you must have three things:
| gcd(x,y) = | x, when x=y gcd(x-y,y), when x>y gcd(x,y-x), when y>x |
A group of over-proud monks in a Hanoi monastery were assigned a task to perform: they had to move 100 discs from one peg to another with the help of a third peg. There are only two rules:
Thus, when we wish to transfer n discs we assume that we already know how to transfer n-1 discs.
To see that this works, let's code it in Prolog.
Since our knowledge of I/O is fairly narrow, we'll just write out the instructions for each move. Let's define a predicate that will write out one instruction:
% move(A,B) is true if we move the topmost disc from peg A to peg B
move(A,B) :-
nl, write('Move topmost disc from '),
write(A), write(' to '), write(B).
Now to actually do the main work, we'll define a recursive predicate which will have the form transfer(N,A,B,I) where:
Basically, transfer(N,A,B,I) will be satisfied if we can find an algorithm to transfer N discs from A to B using I
Thus we define:
% transfer(N,A,B,I) is true if we can transfer N discs from A to B
% using I as an intermediate peg.
% Base case - 1 disc
transfer(1,A,B,I) :- move(A,B).
% Recursive case - N discs
transfer(N,A,B,I) :-
M is N-1,
transfer(M,A,I,B), % Transfer topmost N-1 discs from A to I
move(A,B), % Move biggest disc from A to B
transfer(M,I,B,A). % Transfer remaining N-1 discs from I to B
Type this in (save it as hanoi.pl), and try the query:
transfer(3,peg1,peg2,inter).
A possible configuration of objects on the grid might be:
| | | | | |
| | | | | |
----+------[A]-----[B]------+------[C]------+----
| | | | | |
| | | | | |
| | | | | |
----+------[D]-----[E]-----[F]-----[G]------+----
| | | | | |
| | | | | |
| | | | | |
----+-------+------[H]------+-------+-------+----
| | | | | |
| | | | | |
| | | | | |
----+-------+------[I]------+-------+-------+----
| | | | | |
| | | | | |
Suggest an appropriate format for a Prolog knowledge base that will represent this. Rather than using absolute co-ordinates (remember - it's infinitely large in theory), describe the position of the objects relative to each other (after all, Prolog is a relational language...)
Think along the lines of the family tree example: make sure that you separate the facts which describe a given situation, from the rules which will work in any situation.
Now write some rules which will check the following (you might already have expressed some of these as facts):
Written by James Power