We have created a representation of the network and we have seen some queries that might be used to find routes between two points. When the query involved finding a route via one or more intermediate nodes, it became somewhat tedious having to specify all the intermediate nodes in a series of goals in the query. In practice, it may not be feasible to specify all the intermediate nodes because the network may be so very large that we cannot reasonably list them all or because we may not know what the intermediate nodes are. For instance, if I wish to make a rail journey from Sunderland to Birmingham University station, I don't know the route and how many changes I would have to make on the way, but I could search the British Rail timetable and try to find one.
Once we start to write our knowledge about how to find a route, we are starting to write the rule part of a Prolog program.
Looking back to the queries we tried above, what do we know about finding a route between one point and another? It seems to me that there are two main things (which are true of this network, of motorway driving or train journeys):
The first case is easily represented. Suppose we want to travel from node a to node 1. We know that a fact (path(a, 1).) exists that represents this. We have to write a rule that uses this fact. The rule must be absolutely general, which is to say that it applies to all path/2 facts in the Prolog database.
This is the point at which we introduce the syntax of the Prolog rule.
A rule has two parts: the head and the body and they are joined by the ' :- ' symbol (which can be thought to mean "if" or "is implied by").
The head can either be an atom (eg start) or a complex object (eg mammal(Animal)).
The body consists of one or more goals, like the goals we have been using in the previous examples of queries.
The rule for finding a route between two points would be:
route(Start, End) :-
path(Start, End).
We can read this as "it is true that there is a route from Start to End if it is true that there is a path from Start to End".
Notice that the variables have been given meaningful names. We could have written:
route(X, Y) :-
path(X, Y).
and obtain the same solutions. When we came to review the program in a few months time (eg when we find a bug in its working) or when someone else comes to look at the program (eg the workshop demonstrator), it would not be very obvious what the variables where intended to stand for. Always use meaningful variable names.
The second case is more involved. It says that to find the route between two points that are not directly linked, there must be an intermediate node from which there is a route to the final node. (If you want to think of it in terms of a process: find a node which is directly linked to the starting node, then find a route from the new node to the end node.)
We start by having (in this example) exactly the same rule head as with the first case:
route(Start, End) :-
...
From here we have to write two parts:
path(Start, Via),
Don't forget the comma which (as we saw in the queries with more than one
goal in the previous modules) represents "and".
route(Via, End).
(Not forgetting the final full-stop.)
Putting the two parts together we have:
route(Start, End) :-
path(Start, End).
route(Start, End) :-
path(Start, Via),
route(Via, End).
In Prolog, groups of rules and/or facts that have the same functor and arity are known as a procedure. This is the procedure called route/2. To those used to languages like Pascal, C, C++ and others, the definition above will have one outstanding peculiarity. Most computer languages will not allow you to have two different definitions with the same name. For instance, Pascal would not allow you to have two procedures with the same name and number of arguments. Prolog is not fussy, indeed it tends not to have fixed, authoritarian rules but prefers as much freedom as it can get away with - perhaps to the point of anarchy.
One final point: we shall adopt the convention of labelling the individual clauses by their position in the procedure. We will introduce each label by the symbol used for a single line comment: '%'. This can either be used at the start of a line, or as the last item in a line. For instance, the following are correct:
% 1
route(Start, End) :-
path(Start, End).
and
route(Start, End) :- % 1
path(Start, End).
But the following would not work as we wanted:
route(Start, End) :-
% 1 path(Start, End).
because Prolog would assume that you wished everything after the '%' sign until you press the "return" key to be a comment (and thus ignored).[+] In this case, Prolog would stop and give you an error message somewhere because you have said there would be a body to this rule (by typing ":-") but haven't included a full-stop at the end.
So the complete procedure is:
% 1
route(Start, End) :-
path(Start, End).
% 2
route(Start, End) :-
path(Start, Via),
route(Via, End).
These Pages are maintained by
Dr Peter Hancox
Last updated October 1998