Location via proxy:   [ UP ]  
[Report a bug]   [Manage cookies]                
0% found this document useful (0 votes)
43 views

Logic Programming 1 - Overview

This document provides an overview of the Prolog programming language. It introduces Prolog's ability to represent relations between objects and use logic rules and queries to reason about these relations. As an example, it defines family relationships between people and uses Prolog to answer questions about these relationships, such as determining grandparents or whether two people share a common parent. The document also demonstrates how rules can extend the example program by defining new relations in terms of existing ones.

Uploaded by

saleem
Copyright
© © All Rights Reserved
Available Formats
Download as DOCX, PDF, TXT or read online on Scribd
0% found this document useful (0 votes)
43 views

Logic Programming 1 - Overview

This document provides an overview of the Prolog programming language. It introduces Prolog's ability to represent relations between objects and use logic rules and queries to reason about these relations. As an example, it defines family relationships between people and uses Prolog to answer questions about these relationships, such as determining grandparents or whether two people share a common parent. The document also demonstrates how rules can extend the example program by defining new relations in terms of existing ones.

Uploaded by

saleem
Copyright
© © All Rights Reserved
Available Formats
Download as DOCX, PDF, TXT or read online on Scribd
You are on page 1/ 13

Programming in Prolog

(An Overview)

Prepared By

Dr Augustine S. Nsang
An Overview of Prolog

Prolog is a programming language for symbolic, non-numeric computation. It is specially well


suited for solving problems that involve objects and relations between the objects. As a matter of
fact, the term ‘Prolog’ stands for ‘PROgramming in LOGic’.

Fig 1 below shows an example: a family relation. The fact that Tom is a parent of Bob
can be written in Prolog as:

parent(tom, bob).

pam tom

bob liz

ann
pat

jim

Fig 1: A family tree

Here, we choose parent as the name of a relation: tom and bob are its arguments. For
reasons, that will become clear later, we write names like tom with an initial lowercase letter.
The whole family of Fig 1 is defined by the following Prolog program:
parent(pam, bob).
parent(tom, bob).
parent(tom, liz).
parent(bob, ann).
parent(bob, pat).
parent(pat, jim).

The program consists of six clauses. Each of these clauses declares one fact about the parent
relation.
When this program has been communicated to the Prolog system, Prolog can be
posed some questions about the parent relation. For example: Is Bob a parent to Pat? This
question can be communicated to the Prolog system by typing into the terminal:
?- parent(bob, pat).
Having found this as an asserted fact in the program, Prolog will answer:
yes
A further query can be:
?- parent(liz, pat).
Prolog answers
no
because the program does not mention anything about Liz being a parent of Pat. It also
answers ‘no’ to the question:
?- parent(tom, ben).
because the program has not even heard of the name Ben.
More interesting questions can also be asked. For example: Who is Liz’s parent?
?- parent(X, liz).
Prolog’s answer will not just be ‘yes’ or ‘no’ this time. Prolog will tell us what is the
(yet unknown) value of X such that the above statement is true. So the answer is:
X = tom
The question: Who are Bob’s children? can be communicated to Prolog as
?- parent(bob, X).
This time, there is more than just one possible answer. Prolog first answers with one
solution:
X = ann
We may now want to see other solutions. We can that say to Prolog (in many Prolog
implementations) by typing a semicolon, and Prolog will find other answers:
X = pat
If we request more solutions again, Prolog will answer ‘no’ because all solutions have
been exhausted.
Our program can be asked an even broader question: Who is the parent of whom?
Another formulation of this question is:
Find X and Y such that X is a parent if Y.
This is express in Prolog by:
?- parent(X, Y).
Prolog now finds all the parent child pairs, one after another. The solution will be
displayed one at a time as long as we tell Prolog we want more solutions, until all the
solutions have been found. The answers are output as:

X = pam
Y = bob;

X = tom
Y = bob;

X = tom
Y = liz;

We can stop the stream of solutions by typing, for example, a period instead of a
semicolon (this depends on the implementation of Prolog)

parent
Y grandparent

parent

Figure 2: The grandparent relation expressed as a composition of two parent relations

Our example program can be asked still more complicated questions like:
Who is the grandparent of Jim?
As our program does not directly know the grandparent relation, this query has to be broken
down into two steps, as illustrated by Fig 2.

1 Who is the parent of Jim? Assume that this is some Y.


2 Who is the parent of Y? Assume that this is some X.
Such a composed query is written in Prolog as a sequence of two simple ones:
?- parent(Y, jim), parent(X, Y).
The answer will be:
X = bob
X = pat
One composed query can be read: Find X and Y that satisfy the following two requirements:
parent(Y, jim) and parent(X,Y)
Note that we can change the order of the requirements (in the query) and the logical meaning
will remain the same.
In a similar way, we can ask:
Who are Tom’s grandchildren?
?- parent(tom, X), parent(X, Y).
Prolog’s answers are:
X = bob
Y = ann;

X = bob
X = pat

Yet another question could be: Do Ann and Pat have a common parent? This can expressed
again in two steps:

1 Who is a parent, X, of Ann?


2 Is (the same) X a parent of Pat?
The corresponding question to Prolog is then:
?- parent(X, ann), parent(X, pat).
The answer is:
X = bob
Our example program has helped to illustrate some important points:
 It is easy to define a relation, such as the parent relation, by stating the n-tuples of
objects that satisfy the relation
 The user can easily query the Prolog system about relations defined in the program
 A Prolog program consists of a number of clauses. Each clause terminates with a
fullstop.
 The arguments of relations can (among other things) be: concrete objects, or
constants (such are called atoms). Others of the second kind are called variables.
 Questions to the system consist of one or more goals. A sequence of goals, such as:
?- parent(Y,ann), parent(X, pat).
means a conjuction of the goals:
X is a parent of Ann and
X is a parent of Pat
The word ‘goals’ is used because Prolog accepts questions as goals that are to be
satisfied.
 An answer to a question to a question can be either positive or negative, depending
on whether the corresponding goal can be satisfied or not. In the case of a positive
answer, we say the corresponding goal was satisfiable and that the goal succeeded.
Otherwise the goal was unsatisfiable and it failed.
 If several answers satisfy the question, then Prolog will find as many of them as
desired by the user.

Extending the example program by rules

Our example program can easily be extended in many ways. Let us first add information
on the sex of the people that occur in the parent relation. This can be done by simply
adding the following facts:

female(pam).
male(tom)
male(bob)
female(liz)
female(pat)
female(ann)
male(jim).

The relations introduced here are male and female. These relations are unary (or one
place) relations. A binary relation (like parent) defines a relation between pairs of
objects; on the other hand, unary relations can be used to define simple yes/no properties
of objects. The first unary clause can be read: ‘Pam is female’. We could convey the
same information in two unary relations with one binary relation, sex, instead. An
alternative piece of program would then be:

sex(pam, feminine).
sex(tom, masculine).
sex(bob, masculine).
sex(liz, feminine)
.
.
.

We could define offspring in a similar way as the parent relation; that is, by simply
providing a list of simple facts about the offspring relation, each fact mentioning one pair of
people such that one is an offspring of the other. For example:
offspring(liz, tom).
However, the offspring relation can be defined much more elegantly by making use of the
parent relation i.e. by making use of the fact that it is the inverse of parent. This alternative
way can be based on the following logical statement:
For All X and Y, Y is an offspring of X if X is a parent of Y.
This formalism is already close to the formalism of Prolog. The corresponding Prolog clause
which has the same meaning is:
offspring(Y, X) :- parent(X, Y).
Prolog clauses such as the above are called rules. There is a very important difference
between facts and rules. A fact like:
parent(tom, liz).
specifies something that is always, unconditionally, true. On the other hand, rules specify
things that may be true if some condition is satisfied. Therefore, we say that rules have:
 a condition part (the right hand side of the rule) and
 a conclusion part (the left hand side of the rule).
The conclusion part is also called the head of the clause and the condition part is the body of
the clause. For example:
offspring(Y, X) :- parent(X, Y).

head body

How are rules actually used by Prolog? We will illustrate it by the following example.
Let us ask our program whether Liz is an offspring of Tom:
?- offspring(liz, tom).
There is no fact about offsprings in our program; therefore, the only way to consider this
question is to apply the rule about offsprings. The rule is general in the sense that it is
applicable to any objects X and Y; therefore, it can be applied to such particular objects as liz
and tom. To apply the rule to liz and tom, Y has to be substituted with liz, and X with tom.
We say that the variables X and Y become instantiated to:
X = tom and Y = liz.
After the instantiation, we have obtained a special case of our general rule. The special case
is:
offspring(liz, tom) :- parent(tom, liz).
The condition part has become:
parent(tom, liz).
Now, Prolog tries to find out whether the condition part is true. So the initial goal:
offspring(liz, tom)
has been replaced with the sub-goal
parent(tom, liz).
This new goal happens to be trivial, as it can be found as a fact in our program. This means
that the conclusion part of the rule is also true, and Prolog will answer the question with yes.
Let us now add more family relations to our example program. The specification of
the mother relation can be written in Prolog as:
mother(X, Y) :- parent(X, Y), female(X).
following the logical statement: “For All X and Y, X is the mother of Y if X is a parent of Y
and X is female”.
A comma between two conditions indicates the conjunction of the conditions,
meaning that both conditions have to be true.
Fig 3 below illustrates the sister relation, defined by:
“For any X and Y, X is a sister of Y if
i) both X and Y have the same parent, and
ii) X is female”

X Y

Fig 3: Defining the sister relation

The graph in Fig 3 can be translated into Prolog as:


sister(X, Y) :- parent(Z, X), parent(Z, Y), female(X).
We can now ask:
sister(X, pat).
Prolog will find two answers, one of which may come as a surprise:
X = ann;
X = pat
So Pat is a sister to herself?! This is probably not what we had in mind when defining the
sister relation. However, according to our rule about sisters Prolog’s answer is perfectly
logical. Our rule about sisters does not mention that X and Y must not be the same, if X is to
be a sister of Y. As this is not required, Prolog (rightfully) assumes that X and Y can be the
same, and will, as a consequence, find that any female who has a parent is a sister to herself.
To correct our rule about sisters, we add that X and Y must be different. We will see
later how this can be done in several ways, but for the moment we will assume that a relation
different is already known to Prolog, and that different(X, Y) is satisfied if and only if X and
Y are not equal. An improved rule for the sister relation can then be:
sister(X, Y) :- parent(Z, X), parent(Z, Y), female(X), different(X,Y).
Some important points in this section are:
 Prolog programs can be extended by simply adding new clauses.
 Prolog clauses are of three types: facts, rules and questions.
 Facts declare things that are always, unconditionally, true.
 Rules declare things that are true depending on a given condition.
 By means of questions the user can ask the program what things are true.
 Prolog clauses consist of the head and the body. The body is a list of goals separated
by commas. Commas are understood as conjunctions.
 Facts are clauses that have the empty body. Questions only have the body. Rules have
the head and the (non-empty) body.
 In the course of computation a variable can be substituted by another object. We say
that the variable becomes instantiated.

A recursive rule definition


Let us add one more relation to our family program: the predecessor relation. This relation
will be defined in terms of the parent relation. The whole definition can be expressed with
two rules. The first rule will define the direct (immediate) predecessors and the second rule
will define the indirect predecessors. We say that some X is an indirect predecessor of some
Z if there is a parentship chain of people between X and Z. In our example program of Fig 1,
Tom is a direct predecessor of Liz, and Tom is an indirect predecessor of Pat.
The first rule is simple and can be formulated as:
“For All X and Z, X is a predecessor of Z if X is a parent of Z”. This is
straightforwardly translated in Prolog as:
predecessor(X, Z) :- parent(X, Z).
The second rule, on the other hand, is more complicated because the chain of parents may
present some problems. Attempts to define indirect predecessors may include the following:
 “For All X and Z, X is a predecessor of Z if X is a parent of Y and Y is a parent of Z”
 “For All X and Z, X is a predecessor of Z if X is a parent of Y1, Y1 is a parent of Y2,
and Y1 is a parent of Z” etc
A program to define the predecessor relation using the above statements will be quite
lengthy and, more importantly, it only works to some extent. It would only discover
predecessors to a certain depth in a family tree because the length of the chain of people
between the predecessors and the successors would be limited according to the length of our
predecessor clauses.
There is, however, an elegant and correct formulation of the predecessor relation. It
will be correct in the sense that it will work for predecessors at any depth. The key idea is to
define the predecessor relation in terms of itself:
“For All X and Z, X is a predecessor of Z if there is a Y such that:
i) X is a parent of Y, and
ii) Y is a predecessor of Z”
A Prolog clause with the above meaning is:
predecessor(X, Z) :- parent(X, Y), predecessor(Y, Z).
We have thus constructed a complete program for the predecessor relation which consists of
two rules: one for direct predecessors and one for indirect predecessors. Both rules are
rewritten together:

predecessor(X, Z) :- parent(X, Z).


predecessor(X, Z) :- parent(X, Y), predecessor(Y, Z).

The key to the formulation was the use of predecessor itself in its definition. Such definitions
are, in general, referred to recursive definitions. Recursive programming is, in fact, one of
the fundamental principles of programming in Prolog. It is not possible to solve tasks of any
significant complexity in Prolog without the use of recursion.
Now, let’s put together all the pieces of our family program, which was extended
gradually by adding new facts and rules. The final form of the program is shown below (in
Fig 4). Looking at Fig 4, two further points are in order here: the first will introduce the term
procedure, the second will be about comments in programs.

parent(pam, bob). % Pam is a parent of Bob


parent(tom, bob).
parent(tom, liz).
parent(bob, ann).
parent(bob, pat).
parent(pat, jim).
female(pam). % Pam is female
male(tom) % Tom is male
male(bob)
female(liz)
female(pat)
female(ann)
male(jim).
offspring(Y, X) :- % Y is an offspring of X if
parent(X, Y). % X is a parent of Y
mother(Y, X) :- % X is the mother of Y if
parent(X, Y), % X is a parent of Y, and
female(X). % X is female
grandparent(X, Z) :- % X is a grandparent of Z if
parent(X, Y), % X is a parent of Y, and
parent(Y, Z). % Y is a parent of Z.
sister(X, Y) :- % X is a sister of Y if
parent(Z, X), % X and Y have the
parent(Z, Y), % same parent,
female(X), % X is female, and
different(X,Y). % X and Y are different
predecessor(X, Z) :- % Rule pr1: X is a predecessor of Z
parent(X, Z).
predecessor(X, Z) :- % Rule pr2: X is a predecessor of Z
parent(X, Y),
predecessor(Y, Z).

Fig 4: The family program

The program in Fig 4 defines several relations: parent, male, female, predecessor etc. The
predecessor relation, for example, is defined by two clauses. We say that these two clauses
are about the predecessor relation. Sometimes, it is convenient to consider the whole set of
clauses about the same relation. Such a set of clauses is called a procedure.
In Fig 4, the rules about the predecessor relation have been distinguished by the
names ‘pr1’ and ‘pr2’, added as comments to the program. These names will be used later as
references to these rules. Like in Pascal (or any other programming language) comments are,
in general, ignored by the Prolog system. They only serve as further clarification to the
person who reads the program. They are distinguished in Prolog from the rest of the program
by being enclosed in special brackets ‘/*’ and ‘*/’. Thus comments in Prolog look like this:
/* This is a comment */
Another method, more practical for short comments, uses the percent character ‘%’.
Everything between ‘%’ and the end of the line is interpreted as a comment.
% This is also a comment

How Prolog answers Questions:


This section gives an informal explanation of how Prolog answers questions.
A question in Prolog is always a sequence of one or more goals. To answer a
question, Prolog tries to satisfy all the goals. What does it mean to satisfy a goal? To satisfy a
goal means to demonstrate that the goal is true, assuming that the relations in the program are
true. In other words, to satisfy a goal means to demonstrate that the goal logically follows
from the facts and rules in the program. If the question contains variables, Prolog has to find
what are particular objects (in the place of variables) for which the goals are satisfied. The
particular instantiation of variables to these objects is displayed to the user. If Prolog cannot
demonstrate for some instantiation of variables that the goals logically follow from the
program, then Prolog’s answer to the question will be ‘no’.
An appropriate view of the interpretation of a Prolog program in mathematical terms
is then as follows: Prolog accepts facts and rules as a set of axioms, and the user’s question
as a conjectured theorem; and then tries to prove this theorem – that is, demonstrate that it
can be logically derived from the axioms.
We will illustrate this by a classical example. Let the axioms be:
All men are fallible.
Socrates is a man.
A theorem that logically follows from these two axioms is:
Socrates is fallible.
The first axiom above can be rewritten as: “For all X, X is a man if X is fallible”.
Accordingly, the example can be translated into Prolog as follows:
fallible(X) :- man(X).
man(socrates).
?- fallible(socrates).
yes
A more complicated example from the family program of Fig 4 is:
?- predecessor(tom, pat).

We know that parent(bob, pat) is a fact. Using this fact and rule pr1 we can conclude that we
can conclude predecessor(bob, pat). This is a derived fact; it cannot be found explicitly in
our program, but it can be derived from the facts and rules in the program. An inference step,
such as this, can be written in a more compact form as:
parent(bob, pat)  predecessor(bob, pat).
This can be read: “From parent(bob, pat) it follows predecessor(bob,pat), by rule pr1”.
Further, we know that parent(tom, bob) is a fact. Using this fact, and the derived fact
predecessor(bob,pat), we can conclude predecessor(tom, pat), by rule pr2. We have thus
shown that our goal statement predecessor(tom, pat) is true. This whole inference process of
two steps can be written as:
parent(bob, pat)  predecessor(bob, pat).
parent(tom, bob) and predecessor(bob, pat)  predecessor(tom, pat).
We have thus shown what can be a sequence of steps that satisfy a goal – that is, makes it
clear that the goal is true. Let us call this a proof sequence. We have not, however, shown
how the Prolog system finds such a proof sequence.
Prolog finds the proof sequence in the reverse order to that we have used. Instead of
starting with simple facts in the program, Prolog starts with the goals and, using rules,
substitutes the current goal with the new goals until new goals happen to be simple facts.
Given the question:
?- predecessor(tom, pat).
Prolog will try to satisfy this goal. In order to do so, it will try to find a clause in the program
from which the above goal could immediately follow. Obviously, the only clauses relevant to
this end are rules pr1 and pr2. These are the rules about the predecessor relation. We say that
the heads of these rules match the goal.
The two clauses, pr1 and pr2, represent two alternative ways for Prolog to proceed.
Prolog first tries that clause which appears first in the program:
predecessor(X, Z) :- parent(X, Z).
Since the goal is predecessor(tom, pat), the variables in the rule must be instantiated
as follows:
X = tom; Z = pat
The original goal predecessor(tom, pat) is then replaced by a new goal:
parent(tom, pat).
There is no clause in the program whose head matches the goal parent(tom, pat). Therefore
this goals fails. Now, Prolog “backtracks” to the original goal in order to try an alternative
way to derive the top goal predecessor(tom, pat). The rule pr2 is thus tried:
predecessor(X, Z) :- parent(X, Y), predecessor(Y, Z).
As before, the variables X and Z become instantiated as:
X = tom; Z = pat
But Y is not instantiated yet. The top goal predecessor(tom, pat) is replaced by two
goals:
parent(tom, Y) and predecessor(Y, pat).
Being now faced with two goals, Prolog tries to satisfy them in the order that they are
written. The first one is easy as it matches one of the facts in the program. The matching
forces Y to become instantiated to bob. Thus the first goal has been satisfied, and the
remaining goal has become:
predecessor(bob, pat).
To satisfy this goal, the rule pr1 is used again. Note that this (second) application of the same
rule has nothing to do with its previous application. Therefore, Prolog uses a new set of
variables in the rule each time the rule is applied. To indicate this we shall rename the
variables in rule pr1 for this application follows:
predecessor(X’, Z’) :- parent(X’, Z’).
The head has to match our current goal predecessor(bob, pat). Therefore,
X’ = bob, Z’ = pat.
The current goal is then replaced by:
parent(bob, pat).
This goal is immediately satisfied because it appears in the program as a fact. This completes
the execution trace!

You might also like