Endriss Prolog
Endriss Prolog
Ulle Endriss
1 The Basics 5
1.1 Introduction . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 5
1.2 Getting Started: An Example . . . . . . . . . . . . . . . . . . . . . . . 5
1.3 Prolog Syntax . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 8
1.3.1 Terms . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 8
1.3.2 Clauses, Programs and Queries . . . . . . . . . . . . . . . . . . 9
1.3.3 Some Built-in Predicates . . . . . . . . . . . . . . . . . . . . . 10
1.4 Answering Queries . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 12
1.4.1 Matching . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 12
1.4.2 Goal Execution . . . . . . . . . . . . . . . . . . . . . . . . . . . 13
1.5 A Matter of Style . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 15
1.6 Exercises . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 16
2 List Manipulation 19
2.1 Notation . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 19
2.2 Head and Tail . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 19
2.3 Some Built-in Predicates for List Manipulation . . . . . . . . . . . . . 21
2.4 Exercises . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 23
3 Arithmetic Expressions 25
3.1 The is-Operator for Arithmetic Evaluation . . . . . . . . . . . . . . . 25
3.2 Predened Arithmetic Functions and Relations . . . . . . . . . . . . . 26
3.3 Exercises . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 27
4 Operators 31
4.1 Precedence and Associativity . . . . . . . . . . . . . . . . . . . . . . . 31
4.2 Declaring Operators Using the op-Predicate . . . . . . . . . . . . . . . 34
4.3 Exercises . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 35
5 Backtracking, Cuts and Negation 39
5.1 Backtracking and Cuts . . . . . . . . . . . . . . . . . . . . . . . . . . . 39
5.1.1 Backtracking Revisited . . . . . . . . . . . . . . . . . . . . . . . 39
5.1.2 Problems with Backtracking . . . . . . . . . . . . . . . . . . . . 40
5.1.3 Introducing Cuts . . . . . . . . . . . . . . . . . . . . . . . . . . 41
3
4 Contents
The Basics
1.1 Introduction
Prolog (pro gramming in log ic) is one of the most widely used programming languages
in articial intelligence research. As opposed to imperative languages such as C or Java
(which also happens to be object-oriented) it is a declarative programming language.
That means, when implementing the solution to a problem, instead of specifying how
to achieve a certain goal in a certain situation, we specify what the situation (rules and
facts ) and the goal (query ) are and let the Prolog interpreter derive the solution for us.
Prolog is very useful in some problem areas, like articial intelligence, natural language
processing, databases, . . . , but pretty useless in others, like graphics or numerical
algorithms.
By following this course, rstly you will learn how to use Prolog as a programming
language to solve certain problems in computer science and articial intelligence, and
secondly you will learn how the Prolog interpreter actually works. The latter will also
include an introduction to the logical foundations of the Prolog language.
These notes cover the most important Prolog concepts you'll need to know about,
but it is certainly worthwhile to also have a look at the literature. The following three
are well known titles, but you may also consult any other textbook on Prolog.
[1] I. Bratko. Prolog Programming for Articial Intelligence. 2nd edition, Addison-
Wesley Publishing Company, 1990.
[2] F. W. Clocksin and C. S. Mellish. Programming in Prolog. 4th edition, Springer-
Verlag, 1994.
[3] L. Sterling and E. Shapiro. The Art of Prolog. MIT Press, 1986.
5
6 Chapter 1. The Basics
means asking Prolog questions about the previously described world. The simplest
way of describing the world is by stating facts, like this one:
bigger( elephant, horse).
This states, quite intuitively, the fact that an elephant is bigger than a horse.
(Whether the world described by a Prolog program has anything to do with our real
world is of course entirely up to the programmer.) Let's add a few more facts to our
little program:
bigger( elephant, horse).
bigger( horse, donkey).
bigger( donkey, dog).
bigger( donkey, monkey).
This is a syntactically correct program, and after having compiled it we can ask
the Prolog system questions (or queries in proper Prolog-jargon) about it. Here's an
example:
?- bigger( donkey, dog).
Yes
The query bigger( donkey, dog) (i.e. the question \Is a donkey bigger than a
dog?") succeeds, because the fact bigger( donkey, dog) has been communicated to
the Prolog system before. Now, is a monkey bigger than an elephant?
?- bigger( monkey, elephant).
No
No, it's not. We get exactly the answer we expected: the corresponding query,
namely bigger( monkey, elephant) fails. But what happens when we ask the other
way round?
?- bigger( elephant, monkey).
No
According to this elephants are not bigger than monkeys. This is clearly wrong
as far as our real world is concerned, but if you check our little program again, you
will nd that it says nothing about the relationship between elephants and monkeys.
Still, we know that if elephants are bigger than horses, which in turn are bigger than
donkeys, which in turn are bigger than monkeys, then elephants also have to be bigger
than monkeys. In mathematical terms: the bigger-relation is transitive. But this
has also not been dened in our program. The correct interpretation of the negative
answer Prolog gave is the following: from the information communicated to the system
it cannot be proved that an elephant is bigger than a monkey.
If, however, we would like to get a positive reply for a query like bigger(
elephant, monkey), we have to provide a more accurate description of the world. One
Ulle Endriss. An Introduction to Prolog Programming 7
way of doing this would be to add the remaining facts, like e.g. bigger( elephant,
monkey). For our little example this would mean adding another 5 facts. Clearly too
much work and probably not too intelligible anyway.
The far better solution would be to dene a new relation, which we will call
is bigger, as the transitive closure of bigger. Animal X is bigger than animal Y
either if this has been stated as a fact or if there is an animal Z for which it has
been stated as a fact that animal X is bigger than animal Z and it can be shown that
animal Z is bigger than animal Y. In Prolog such statements are called rules and are
implemented like this:
is_bigger( X, Y) :- bigger( X, Y).
is_bigger( X, Y) :- bigger( X, Z), is_bigger( Z, Y).
In these rules :- means something like `if' and the comma between the two terms
bigger( X, Z) and is bigger( Z, Y) stands for `and'. X, Y, and Z are variables,
which in Prolog is indicated by using capital letters.
If from now on we use is bigger instead of bigger in our queries, the program
will work as intended:
?- is_bigger( elephant, monkey).
Yes
Prolog still cannot nd the fact bigger( elephant, monkey) in its database, so
it tries to use the second rule instead. This is done by matching the query with
the head of the rule, which is is bigger( X, Y). When doing so the two variables
get instantiated: X = elephant and Y = monkey. The rule says, that in order to
prove the goal is bigger( X, Y) (with the variable instantiations that's equivalent
to is bigger( elephant, monkey)) Prolog has to prove the two subgoals bigger( X,
Z) and is bigger( Z, Y) { again with the same variable instantiations. This process
is repeated recursively until the facts that make up the chain between elephant and
monkey are found and the query nally succeeds. How this goal execution as well as
term matching and variable instantiation really work will be examined in more detail
in Section 1.4.
Of course, we can do slightly more exiting stu than just asking yes/no-questions.
Suppose we want to know, what animals are bigger than a donkey? The corresponding
query would be:
?- is_bigger( X, donkey).
Again, X is a variable. We could also have chosen any other name for it as long as
it starts with a capital letter. The Prolog interpreter replies as follows:
?- is_bigger( X, donkey).
X = horse
Horses are bigger than donkeys. The query has succeeded, but in order to allow
it to succeed Prolog had to instantiate the variable X with the value horse. If this
8 Chapter 1. The Basics
makes us happy already, we can press Return now and that's it. In case we want to
nd out if there are more animals that are bigger than the donkey, we can press the
semicolon key, which will cause Prolog to search for alternative solutions to our query.
If we do this once, we get the next solution X = elephant: elephants are also bigger
than donkeys. Pressing semicolon again will return a No, because there are no more
solutions:
?- is_bigger( X, donkey).
X = horse ;
X = elephant ;
No
There are many more ways of querying the Prolog system about the contents of its
database. As a nal example we ask whether there is an animal X that is both smaller
than a donkey and bigger than a monkey:
?- is_bigger( donkey, X), is_bigger( X, monkey).
No
The (correct) answer is No. Even though the two single queries is bigger(
donkey, X) and is bigger( X, monkey) would both succeed when submitted on
their own, their conjunction (represented by the comma) does not.
This section has been intended to give you a rst impression of Prolog program-
ming. The next section provides a more systematic overview of the basic syntax.
There are a number of Prolog interpreters around. How to start a Prolog session may
slightly dier from one system to the other. Details about this will be given during
the course.
1.3.1 Terms
The central data structure in Prolog is that of a term. There are terms of four kinds:
atoms, numbers, variables, and compound terms. Atoms and numbers are sometimes
grouped together and called atomic terms.
Atoms. Atoms are usually strings made up of lower- and uppercase letters, digits,
and the underscore, starting with a lowercase letter. The following are all valid Prolog
atoms:
elephant, b, abcXYZ, x_123, another_pint_for_me_please
On top of that also any series of arbitrary characters enclosed in single quotes
denotes an atom.
Ulle Endriss. An Introduction to Prolog Programming 9
Variables. Variables are strings of letters, digits, and the underscore, starting with
a capital letter or an underscore. Examples:
X, Elephant, _4711, X_1_2, MyVariable, _
The last one of the above examples (the single underscore) constitutes a special case.
It is called the anonymous variable and is used when the value of a variable is of no
particular interest. Multiple occurrences of the anonymous variable in one expression
are assumed to be distinct, i.e. their values don't necessarily have to be the same.
More on this later.
In the introductory example we have already seen how Prolog programs are made up
of facts and rules. Facts and rules are also called clauses.
The intuitive meaning of a rule is that the goal expressed by its head is true, if we
(or rather the Prolog system) can show, that all of the expressions (subgoals) in the
rule's body are true.
Intuitively, when submitting a query like the last example, we ask Prolog whether
all its predicates are provably true, or in other words whether there is an X such that
small( X), green( X), and slimy( X) are all true.
What we have seen so far is already enough to write simple programs by dening
predicates in terms of facts and rules, but Prolog also provides a range of useful built-
in predicates. Some of them will be introduced in this section; most of them are
explained in the manual.
Built-ins can be used in a similar way as user-dened predicates. The important
dierence between the two is, that a built-in predicate is not allowed to appear as the
principal functor in a fact or the head of a rule. This must be so, because using them
in such a position would eectively mean changing their denition.
Maybe the most important built-in predicate is = (equality). Instead of =( X,
Y) we usually write more conveniently X = Y. Such a goal succeeds, if the terms X and
Y can be matched. This will be made more precise in Section 1.4.
Sometimes it can be useful to have predicates that are known to either fail or
succeed in any case. The predicates true and fail serve exactly this purpose.
Ulle Endriss. An Introduction to Prolog Programming 11
Program les can be compiled using the predicate consult/1.1 The argument
has to be a Prolog atom denoting the particular program le. For example, to compile
the le big-animals.pl submit the following query to Prolog:
?- consult( 'big-animals.pl').
If the compilation is successful, Prolog will reply with Yes. Otherwise a list of
errors is displayed.
If besides Prolog's replies to queries you wish your program to have further output
you can use the write/1 predicate. The argument can be any valid Prolog term. In
the case of a variable its value will be printed out. Execution of the predicate nl/0
causes the system to skip a line. Here are two examples:
?- write( 'Hello World!'), nl.
Hello World!
Yes
Checking the type of a Prolog term. There are a number of built-in predicates
available that can be used to check the type of a given Prolog term. Here are some
examples:
?- atom(elephant).
Yes
?- atom(Elephant).
No
?- X = f(mouse), compound(X).
X = f(mouse)
Yes
The last query succeeds, because the variable X is bound to the compound term
f(mouse) at the time the subgoal compound(X) is being executed.
Most Prolog systems also provide a help function in the shape of a predicate,
usually called help/1. Applied to a term (like the name of a built-in predicate) the
system will display a short description, if available. Example:
1
The /1 is used to indicate that this predicate takes one argument.
12 Chapter 1. The Basics
?- help(atom).
atom(+Term)
Succeeds if Term is bound to an atom.
1.4.1 Matching
Two terms are said to match if they are either identical or if they can be made identical
by means of variable instantiation. Instantiating a variable means assigning it a xed
value. Two free variables also match, because they could be instantiated with the
same ground term.
It is important to note that the same variable has to be instantiated with the same
value throughout an expression. The only exception to this rule is the anonymous
variable , which is considered to be unique whenever it occurs.
We give some examples. The terms is bigger( X, dog) and is bigger(
elephant, dog) match, because the variable X can be instantiated with the atom
elephant. We could test this in the Prolog interpreter by submitting the correspond-
ing query to which Prolog would react by listing the appropriate variable instantia-
tions:
?- is_bigger( X, dog) = is_bigger( elephant, dog).
X = elephant
Yes
The following is an example for a query that doesn't succeed, because X cannot
match with 1 and 2 at the same time.
?- p( X, 2, 2) = p( 1, Y, X).
No
Submitting a query means asking Prolog to try to prove that the statement(s) implied
by the query can be made true provided the right variable instantiations are made.
The search for such a proof is usually referred to as goal execution. Each predicate in
the query constitutes a (sub)goal, which Prolog tries to satisfy one after the other. If
variables are shared between several subgoals their instantiations have to be the same
throughout the entire expression.
If a goal matches with the head of a rule, the respective variable instantiations
are made inside the rule's body, which then becomes the new goal to be satised.
If the body consists of several predicates the goal is again split into subgoals to be
executed in turn. In other words, the head of a rule is considered provably true, if the
conjunction of all its body-predicates are provably true.
If a goal matches with a fact in our program the proof for that goal is complete
and the variable instantiations made during matching are communicated back to the
surface.
Note that the order in which facts and rules appear in our program is important
here. Prolog will always try to match its current goal with the rst possible fact or
rule-head it can nd.
If the principal functor of a goal is a built-in predicate the associated action is
executed whilst the goal is satised. For example, as far as goal execution is concerned
the predicate
14 Chapter 1. The Basics
3. The variable instantiation is extended to the body of the rule, i.e. man( X)
becomes man( socrates).
4. The newly instantiated body becomes our new goal: man( socrates).
Ulle Endriss. An Introduction to Prolog Programming 15
5. Prolog executes the new goal by again trying to match it with a rule-head or
a fact. Obviously, the goal man( socrates) matches the fact man( socrates),
because they are identical. This means the current goal succeeds.
6. This again means that also the initial goal succeeds.
4. Write short clauses with bodies only consisting of a few goals. If necessary, split
into shorter sub-clauses.
5. Choose meaningful names for your variables and atoms.
16 Chapter 1. The Basics
1.6 Exercises
Exercise 1.1. Try to answer the following questions rst \by hand" and then verify
your answers using a Prolog interpreter.
(a) Which of the following are valid Prolog atoms?
f, loves(john,mary), Mary, _c1, 'Hello', this_is_it
(b) Which of the following are valid names for Prolog variables?
a, A, Paul, 'Hello', a_123, _, _abc, x2
(c) What would a Prolog interpreter reply given the following query?
?- f( a, b) = f( X, Y).
(d) Would the following query succeed?
?- loves( mary, john) = loves( John, Mary).
Why?
(e) Assume a program consisting only of the fact
a( B, B).
has been consulted by Prolog. How will the system react to the following query?
?- a( 1, X), a( X, Y), a( Y, Z), a( Z, 100).
Why?
Exercise 1.2. Read the section on matching again and try to understand what's
happening when you submit the following queries to Prolog.
(a) ?- myFunctor( 1, 2) = X, X = myFunctor( Y, Y).
(b) ?- f( a, _, c, d) = f( a, X, Y, _).
(c) ?- write( 'One '), X = write( 'Two ').
Exercise 1.3. Draw the family tree corresponding to the following Prolog program:
female( mary).
female( sandra).
female( juliet).
female( lisa).
male( peter).
male( paul).
male( dick).
Ulle Endriss. An Introduction to Prolog Programming 17
male( bob).
male( harry).
parent( bob, lisa).
parent( bob, paul).
parent( bob, mary).
parent( juliet, lisa).
parent( juliet, paul).
parent( juliet, mary).
parent( peter, harry).
parent( lisa, harry).
parent( mary, dick).
parent( mary, sandra).
After having copied the given program, dene new predicates (in terms of rules
using male/1, female/1 and parent/2) for the following family relations:
(a) father
(b) sister
(c) grandmother
(d) uncle
(e) cousin
For item (d) forget about the case where people became uncles through marriage,
i.e. your program doesn't have to nd Peter as Sandra's uncle, etc. You may want to
use the operator n=, which is the opposite of =. A goal like X n= Y succeeds, if the
two terms X and Y cannot be matched.
Example: X is the brother of Y, if they have a parent Z in common and if X is
male and if X and Y don't represent the same person. In Prolog this can be expressed
through the following rule:
brother( X, Y) :-
parent( Z, X),
parent( Z, Y),
male( X),
X \= Y.
Exercise 1.4. Most people will probably nd all this rather daunting at rst. Read
the chapter again in a few weeks time when you will have gained some programming
experience in Prolog and enjoy the feeling of enlightenment. The part on the syntax
of the Prolog language and the stu on matching and goal execution are particularly
important.
18 Chapter 1. The Basics
Chapter 2
List Manipulation
This chapter introduces a special notation for lists, one of the most useful data struc-
tures in Prolog, and provides some examples how to work with them.
2.1 Notation
Lists are contained in square brackets with the elements being separated by commas.
Here's an example:
[elephant, horse, donkey, dog]
This is the list of the four atoms elephant, horse, donkey, and dog. Elements
of lists could be any valid Prolog terms, i.e. atoms, numbers, variables, or compound
terms. This includes also other lists. The empty list is written as []. The following is
another example for a (slightly more complex) list:
[elephant, [], X, parent( X, tom), [a, b, c], f( 22)]
19
20 Chapter 2. List Manipulation
is then constructed by appending this sub-list to the list represented by the sequence
of elements before the bar. If there is exactly one element before the bar it is the
head and the term after the bar is the list's tail. In the next example 1 is the head
of the list and [2,3,4,5] is the tail, which has been computed by Prolog simply by
matching the list of numbers with the head/tail-pattern.
?- [1, 2, 3, 4, 5] = [Head | Tail].
Head = 1
Tail = [2, 3, 4, 5]
Yes
Note that Head and Tail are just names for variables. We could have used X and
Y or whatever instead with the same result. Note also that the tail of a list (more
generally speaking: the thing after |) is always a list itself. Possibly the empty list,
but denitely a list. The head, however, is an element of a list. It could be a list as
well, but not necessarily (as you can see from the previous example { 1 is not a list).
The same applies for all other elements listed before the bar in a list.
This notation also allows us to retrieve the, say, second element of a given list. In
the following example we use the anonymous variable for the head and also for the list
after the bar, because we are only interested in the second element.
?- [quod, licet, jovi, non, licet, bovi] = [_, X | _].
X = licet
Yes
The head/tail-pattern can be used to implement predicates over lists in a very
compact and elegant way. We exemplify this by presenting an implementation of a
predicate that can be used to concatenate two lists.1 We call it concat lists/3.
When called with the rst two elements being instantiated to lists the third argument
should be matched with the concatenation of those two lists, in other words we would
like to get the following behaviour:
?- concat_lists( [1, 2, 3], [d, e, f, g], X).
X = [1, 2, 3, d, e, f, g]
Yes
The general approach to such a problem is a recursive one. We start with a base
case and then write a clause to reduce a complex problem to a simpler one until the
base case is reached. For our particular problem a suitable base case would be when
one of the two input-lists (for example the rst one) is the empty list. In that case
the result (the third argument) is simply identical with the second list. This can be
expressed through the following fact:
concat_lists( [], List, List).
1
Note that most Prolog systems already provide such a predicate, usually called append/3 (see
Section 2.3).
Ulle Endriss. An Introduction to Prolog Programming 21
In all other cases (i.e. in all cases where a query with concat lists as the main
functor doesn't match with this fact) the rst list has at least one element. Hence, it
can be written as a head/tail-pattern: [Elem | List1]. If the second list is associated
with the variable List2, then we know that the head of the result should be Elem and
the tail should be the concatenation of List1 and List2. Note how this simplies our
initial problem: We take away the head of the rst list and try to concatenate it with
the (unchanged) second list. If we repeat this process recursively, we will eventually
end up with an empty rst list, which is exactly the base case that can be handled by
the previously implemented fact. Turning this simplication algorithm into a Prolog
rule is straightforward:
concat_lists( [Elem | List1], List2, [Elem | List3]) :-
concat_lists( List1, List2, List3).
And that's it! The concat lists/3 can now be used for concatenating two given
lists as specied. But it is actually much more
exible than that. If we call it
with variables in the rst two arguments and instantiate the third one with a list,
concat lists/3 can be used to decompose that list. If you use the semicolon key to
get all alternative solutions to your query, Prolog will print out all possibilities how
the given list could be obtained from concatenating two lists.
?- concat_lists( X, Y, [a, b, c, d]).
X = []
Y = [a, b, c, d] ;
X = [a]
Y = [b, c, d] ;
X = [a, b]
Y = [c, d] ;
X = [a, b, c]
Y = [d] ;
X = [a, b, c, d]
Y = [] ;
No
Recall that the No at the end means that there are no further alternative solutions.
length/2: The second argument is matched with the length of the list in the rst
argument. Example:
?- length( [elephant, [], [1, 2, 3]], Length).
Length = 3
Yes
The names of those variables will be dierent every time you call this query,
because they are generated by Prolog during execution time.
member/2: The goal member( Elem, List) will succeed, if the term Elem can be
matched with one of the members of the list List. Example:
?- member( dog, [elephant, horse, donkey, dog, monkey]).
Yes
append/3: Concatenate two lists. This built-in works exactly like the predicate
concat lists/3 presented in Section 2.2.
last/2: This predicate succeeds, if its rst argument matches the last element of the
list given as the second argument of last/2.
reverse/2: This predicate can be used to reverse the order of elements in a list. The
rst argument has to be a (fully instantiated) list and the second one will be
matched with the reversed list. Example:
?- reverse( [1, 2, 3, 4, 5], X).
X = [5, 4, 3, 2, 1]
Yes
select/3: Given a list in the rst argument and an element of that list in the second,
this predicate will match the third argument with the remainder of that list.
Example:
?- select( [mouse, bird, jellyfish, zebra], bird, X).
X = [mouse, jellyfish, zebra]
Yes
Ulle Endriss. An Introduction to Prolog Programming 23
2.4 Exercises
Exercise 2.1. Write a Prolog predicate analyse list/1 that takes a list as its
argument and prints out the list's head and tail on the screen. If the given list is
empty the predicate should put out an according message. If the argument term isn't
a list the predicate should just fail. Examples:
?- analyse_list( [dog, cat, horse, cow]).
This is the head of your list: dog
This is the tail of your list: [cat, horse, cow]
Yes
?- analyse_list( []).
This is an empty list.
Yes
?- analyse_list( sigmund_freud).
No
Exercise 2.2. Write a Prolog predicate membership/2 that works like the built-in
predicate member/2 (without using member/2).
Hint: This exercise, like many others, can and should be solved using a recursive
approach and the head/tail-pattern of lists.
Exercise 2.4. Write a Prolog predicate reverse list/2 that works like the built-in
predicate reverse/2 (without using reverse/2). Example:
?- reverse_list( [tiger, lion, elephant, monkey], List).
List = [monkey, elephant, lion, tiger]
Yes
Exercise 2.6. The objective of this exercise is to implement a predicate for returning
the last element of a list in two dierent ways.
(a) Write a predicate last1/2 that works like the built-in predicate last/2 using a
recursion and the head/tail-pattern of lists.
(b) Dene a similar predicate last2/2 solely in terms of append/3, without using
a recursion.
Exercise 2.8. Prolog lists without duplicates can be interpreted as sets. Write a
program that given such a list computes the corresponding power set. Recall that the
power set of a set S is the set of all subsets of S . This includes the empty set as well
as the set S itself.
Dene a predicate power/2 such that, if the rst argument is instantiated with a
list, the corresponding power set (i.e. a list of lists) is returned in the second position.
Example:
?- power( [a, b, c], P).
P = [[a, b, c], [a, b], [a, c], [a], [b, c], [b], [c], []]
Yes
Note: The order of the sub-lists in your result doesn't matter.
Chapter 3
Arithmetic Expressions
When trying to use numbers in Prolog programs you might have encountered some
unexpected behaviour of the system already. The rst part of this section claries this
phenomenon. After that an overview of the arithmetic operators available in Prolog
is given.
25
26 Chapter 3. Arithmetic Expressions
?- 3 + 5 is 8.
No
This is because is only causes the argument to its right to be evaluated and then
tries to match the result with the left-hand argument. The arithmetic evaluation of 8
yields again 8, which doesn't match the (non-evaluated) Prolog term 3 + 5.
To summarise, the is-operator is dened as follows: It takes two arguments, of
which the second has to be a valid arithmetic expression with all variables instantiated.
The rst argument has to be either a number or a variable representing a number. A
call succeeds, if the result of the arithmetic evaluation of the second argument matches
with the rst one (or in case of the rst one being a number, if they are identical).
Note that (in SWI-Prolog) the result of the arithmetic calculation will be an integer
whenever possible. That means, for example, that the goal 1.0 is 0.5 + 0.5 would
not succeed, because 0.5 + 0.5 evaluates to the integer 1, not the
oat 1.0. In
general, it is better to use the operator =:= (which will be introduced in Section 3.2)
instead whenever the left argument has been instantiated to a number already.
Relations. Arithmetic relations are used to compare two evaluated arithmetic ex-
pressions. The goal X > Y, for example, will succeed, if expression X evaluates to a
greater number than expression Y. Note that the is-operator is not needed here. The
arguments are evaluated whenever an arithmetic relation is used.
Besides > the operators < (lower), =< (lower or equal), >= (greater or equal), =n=
(non-equal), and =:= (arithmetically equal) are available. The dierentiation of =:=
and = is crucial. The former compares two evaluated arithmetic expressions, whereas
the later performs logical pattern matching.
?- 2 ** 3 =:= 3 + 5.
Yes
?- 2 ** 3 = 3 + 5.
No
Note that unlike is arithmetic equality =:= also works, if one of its arguments
evaluates to an integer and the other one to the corresponding
oat.
3.3 Exercises
Exercise 3.1. Write a Prolog predicate distance/3 to calculate the distance be-
tween two points in the 2-dimensional plane. Points are given as pairs of coordinates.
Examples:
Exercise 3.2. Write a Prolog program to print out a square of N N given characters
on the screen. Call your predicate square/2. The rst argument should be a (positive)
integer, the second argument the character (any Prolog term) to be printed. Example:
Exercise 3.3. Write a Prolog predicate fibonacci/2 to compute the nth Fibonacci
number. The Fibonacci sequence is dened as follows:
F0 = 1
F1 = 1
Fn = Fn 1 + Fn 2 for n 2
Examples:
?- fibonacci( 1, X).
X = 1
Yes
?- fibonacci( 2, X).
X = 2
Yes
?- fibonacci( 5, X).
X = 8
Yes
Exercise 3.4. Write a Prolog predicate element at/3 that given a list and a natural
number n will return the nth element of that list. Examples:
?- element_at( [tiger, dog, teddy_bear, horse, cow], 3, X).
X = teddy_bear
Yes
Exercise 3.5. Write a Prolog predicate mean/2 to compute the arithmetic mean of
a given list of numbers. Example:
?- mean( [1, 2, 3, 4], X).
X = 2.5
Yes
Exercise 3.6. Write a predicate range/3 to generate all integers between a given
lower and an upper bound. The lower bound should be given as the rst argument, the
upper bound as the second. The result should be a list of integers, which is returned
in the third argument position. If the upper bound specied is lower than the given
lower bound the empty list should be returned. Examples:
Ulle Endriss. An Introduction to Prolog Programming 29
?- range( 7, 4, X).
X = []
Yes
Operators
In the chapter on arithmetics we have already seen some operators. Several of the
predicates associated with arithmetic operations are also predened operators. This
chapter deals with how to dene your own operators, which can then be used instead
of normal predicates.
31
32 Chapter 4. Operators
?- X is 10 - 5 - 2.
X = 3
Yes
Here are some more examples. Note that - is dened twice; once as subtraction
(inx) and once as negative sign (prex).1
As you can see there aren't just arithmetic operators, but also stu like = and even
:- are declared as operators. From the very last example you can see that :- can also
be a prex operator. You will see an example for this in the next section.
Table 4.1 provides an overview of possible associativity patterns. Note that it is
not possible to nest non-associative operators. For example, is is dened as an xfx-
operator, which means a term like X is Y is 7 would cause a syntax error. This
makes sense, because that term certainly doesn't (make sense).
1
When generating these examples I always pressed ; to get all alternatives. This is why at the end
4.3 Exercises
Exercise 4.1. Consider the following operator denitions:
:- op( 100, yfx, plink),
op( 200, xfy, plonk).
(a) Have another look at Section 4.1 to nd out what this actually means.
(b) Copy the operator denitions into a program le and compile it. Then run the
following queries and try to understand what's happening.
(i) ?- tiger plink dog plink fish = X plink Y.
(ii) ?- cow plonk elephant plink bird = X plink Y.
36 Chapter 4. Operators
(a) Indicate the structure of this term using parentheses and name its principal
functor:
claudia has a car
(b) What would Prolog reply when presented with the following query?
?- the lion has hunger = Who has What.
(c) Explain why the following query would cause a syntax error:
?- X = she has whatever has style.
- Disjunction: or
- Implication: implies
Think about what precedences and associativity patterns are appropriate. In par-
ticular, your declarations should re
ect the precedence hierarchy of the connectives
as they are dened in propositional logic. Dene all binary logical operators as being
left-associative. Your denitions should allow for double negation without parentheses
(see examples).
Hint: You can easily test whether your operator declarations work as intended. Recall
that Prolog when printing out answers omits all redundant parentheses. That means
when you ask Prolog to match a variable with a formula, whose structure you have
indicated using parentheses, they should all disappear in the output. Parentheses that
are necessary, however, will be shown. Examples:
?- Formula = a implies ((b and c) and d).
Formula = a implies b and c and d
Yes
?- ThirdFormula = (a or b) and c.
ThirdFormula = (a or b)and c
Yes
Exercise 4.4. Write a Prolog predicate cnf/1 to test whether a given formula is in
conjunctive normal form (using the operators you dened for the previous exercise).
Examples:
?- cnf( (a or neg b) and (b or c) and (neg d or neg e)).
Yes
?- cnf( a).
Yes
38 Chapter 4. Operators
In this chapter you will learn a bit more on how Prolog resolves queries. It will also
introduce a control mechanism (cuts) that allows for more eÆcient implementations.
Furthermore, some extensions to the syntax of Prolog programs will be discussed.
Besides conjunction (remember, a comma separating two subgoals in a rule-body rep-
resents a conjunction) we shall introduce negation and disjunction.
In Chapter 1 of these lecture notes the term backtracking has been mentioned already.
During proof search Prolog keeps track of choicepoints, i.e. situations where there is
more than one possible match. Whenever the chosen path ultimately turns out to be
a failure (or if the user asks for alternative solutions), the system can jump back to
the last choicepoint and try the next alternative. This is a crucial feature of Prolog
and facilitates the concise implementation of many problem solutions.
Let's look at an example. We want to write a predicate to compute all possible
permutations of a given list. The following implementation uses the built-in predicate
select/3, which takes a list as its rst argument and matches the second one with
an element from that list. The variable in the third argument position will then be
matched with the rest of the list after having removed the chosen element.
Here's a very simple recursive denition of the predicate permutation/2:
permutation( [], []).
The simplest case is that of an empty list. There's just one possible permutation,
the empty list itself. If the input list has got elements the subgoal select( List,
39
40 Chapter 5. Backtracking, Cuts and Negation
Element, Rest) will succeed and bind the variable Element to an element of the
input list. It makes that element the head of the output list and recursively calls
permutation/2 again with the rest of the input list. The rst answer to a query will
simply reproduce the input list, because Element will always be assigned to the value
of the head of List. If further alternatives are requested, however, backtracking into
the select-subgoal takes place, i.e. each time Element is instantiated with another
element of List. This will generate all possible orders of selecting elements from
the input list, in other words, this will generate all permutations of the input list.
Example:
?- permutation( [1, 2, 3], X).
X = [1, 2, 3] ;
X = [1, 3, 2] ;
X = [2, 1, 3] ;
X = [2, 3, 1] ;
X = [3, 1, 2] ;
X = [3, 2, 1] ;
No
We have also seen other examples for exploiting the backtracking feature before,
like e.g. in Section 2.2. There we used backtracking into concat lists/3 (which is
the same as the built-in predicate append/3) to nd all possible decompositions of a
given list.
There are cases, however, were backtracking is not desirable. Consider, for example,
the following denition of the predicate remove duplicates/2 to remove duplicate
elements from a given list.
remove_duplicates( [], []).
List = [b, c, a] ;
List = [b, b, c, a] ;
List = [a, b, c, a] ;
List = [a, b, b, c, a] ;
No
To solve this problem we need a way of telling Prolog that, even when the user (or
another predicate calling remove duplicates/2) requests further solutions, there are
no such alternatives and the goal should fail.
Now, whenever the head of a list is a member of it's tail the rst subgoal of the
rst rule, i.e. member( Head, Tail), will succeed. Then the next subgoal, !, will also
succeed. Without that cut it would be possible to backtrack, that is to match the
original goal with the head of the second rule to search for alternative solutions. But
once Prolog went pass the cut, this isn't possible anymore: alternative matchings for
the parent goal1 will not be tried.
Using this new version of remove duplicates/2 we get the desired behaviour.
When asking for alternative solutions by pressing ; we immediately get the right
answer, namely No.
Now we are ready for a more precise denition of cuts in Prolog: Whenever a cut
is encountered in a rule's body, all choices made between the time that rule's head has
been matched with the parent goal and the time the cut is passed are nal, i.e. any
choicepoints are being discarded.
Let's exemplify this with a little story. Suppose a young prince wants to get mar-
ried. In the old days he'd simply have saddled his best horse to ride down to the
valleys of, say, Essex and nd himself the sort of young, beautiful, and intelligent
girl he's after. But, obviously, times have changed, life in general is becoming much
more complex these days, and most importantly, our prince is rather busy defend-
ing monarchy against communism/anarchy/democracy (pick your favourite form of
government). Fortunately, his royal board of advisors consists of some of the nest
psychologists and Prolog programmers this rotten world has on oer. They form an
executive committee to devise a Prolog program to automatise the prince's quest for
a bride. The task is to simulate as closely as possible the prince's decision if he'd go
out and look for her himself. From the expert psychologists we gather the following
information:
The prince is primarily looking for a beautiful girl. But, to be eligible for the job
of a prince's wife, she'd also have to be intelligent.
1
With parent goal we mean the goal that caused the matching of the rule's head.
Ulle Endriss. An Introduction to Prolog Programming 43
The prince is young and very romantic. Therefore, he will fall in love with the
rst beautiful girl he comes across, love her for ever, and never ever consider
any other woman as a wife anymore. Even if he can't marry that girl.
The MI5 provides the committee with a database of women of the appropriate age.
The entries are ordered according to the order the prince would have met them on his
ride through the country. Written as a list of Prolog facts it looks something like this:
beautiful( claudia).
beautiful( sharon).
beautiful( denise).
...
intelligent( margaret).
intelligent( sharon).
...
After some intensive thinking the Prolog sub-committee comes up with the follow-
ing ingenious rule:
bride( Girl) :-
beautiful( Girl), !,
intelligent( Girl).
Let's leave the cut in the second line unconsidered for the moment. Then a query
of the form
?- bride( X).
will succeed, if there is a girl X for which both the facts beautiful( X) and
intelligent( X) can be found in the database. Therefore, the rst requirement
identied by the psychologists will be fullled. The variable X would then be instan-
tiated with the girl's name.
In order to incorporate the second condition the Prolog experts had to add the
cut. If the subgoal beautiful( Girl) succeeds, i.e. if a fact of the form beautiful(
X) can be found (and it will be the rst such fact), then that choice will be nal, even
if the subgoal intelligent( X) for the same X should fail.
Given the above database this is rather tragic for our prince. The rst beautiful girl
he'd meet would be Claudia, and he'd fall in love with her immediately and forever.
In Prolog this corresponds to the subgoal beautiful( Girl) being successful with
the variable instantiation Girl = claudia. And it stays like this forever, because
after having executed the cut, that choice cannot be changed anymore. As it happens,
Claudia isn't the most amazingly intelligent young person that you might wish her to
be, which means they cannot get married. In Prolog again, this means that the subgoal
intelligent( Girl) with the variable Girl being bound to the value claudia will
not succeed, because there is no such fact in the program. That means the entire
query will fail. Even though there is a name of a girl in the database, who is both
beautiful and intelligent (Sharon), the prince's quest for marriage is bound to fail:
44 Chapter 5. Backtracking, Cuts and Negation
?- bride( X).
No
Cuts are very useful to \guide" the Prolog interpreter towards a solution. But this
doesn't come for free. By introducing cuts, we give up some of the (nice) declarative
character of Prolog and move towards a more procedural system. This can sometimes
lead to unexpected results.
To illustrate this, let's implement a predicate add/3 to insert an element into a
list, if that element isn't already a member of the list. The element to be inserted
should be given as the rst argument, the list as the second one. The variable given
in the third argument position should be matched with the result. Examples:
The important bit here is that there are no wrong alternative solutions. The
following Prolog program does the job:
If the element to be inserted can be found in the list already, the output list should
be identical with the input list. As this is the only correct solution, we prevent Prolog
from backtracking by using a cut. Otherwise, i.e. if the element is not already in the
list, we use the head/tail-pattern to construct the output list.
This is an example for a program where cuts can be problematic. When used as
specied, namely with a variable in the third argument position, add/3 works ne.
If, however, we put an instantiated list in the third argument, Prolog's reply can be
dierent from what you might expect. Example:
Compare this with the denition of the add/3-predicate from above and try to
understand what's happening here. And be careful with those cuts!
Ulle Endriss. An Introduction to Prolog Programming 45
In order to give a positive answer to a query Prolog has to construct a proof to show
that the set of facts and rules of a program implies that query. Therefore, precisely
speaking, answering Yes to a query means not only that the query is true, but that it
is provably true. Consequently a No doesn't mean the query is necessarily false, just
not provably true : Prolog failed to derive a proof.
This attitude of negating everything that is not explicitly in the program (or can
be concluded from the information provided by the program) is often referred to as
the closed world assumption. That is, we think of our Prolog program as a little world
of its own, assuming nothing outside that world does exist (is true).
In everyday reasoning we usually don't make this sort of assumption. Just because
the duckbill might not appear in even a very big book on animals, we cannot infer
that it isn't an animal. In Prolog, on the other hand, when we have a list of facts like
animal( elephant).
animal( tiger).
animal( lion).
...
and animal( duckbill) does not appear in that list (and there are no rules with
animal/1 in the head), then Prolog would react to a query asking whether the duckbill
was an animal as follows:
?- animal( duckbill).
No
The closed world assumption might seem a little narrow-minded at rst sight, but
you will appreciate that it is the only admissible interpretation of a Prolog reply as
Prolog clauses only give suÆcient, not necessary conditions for a predicate to hold.
Note, however, that if you have completely specied a certain problem, i.e. when you
can be sure that for every case where there is a positive solution Prolog has all the
data to be able to construct the respective proof, then the notions of not provable and
false coincide. A No then really does mean no.
A goal of the form n+ Goal succeeds, if the goal Goal fails and vice versa. In other
words, n+ Goal succeeds, i Prolog fails to derive a proof for Goal (i.e. i Goal is not
provably true).
Such semantics of the negation operator are known as negation as failure. Prolog's
negation is dened as the failure to provide a proof. In real life again this is usually not
the right notion (though it has been adopted by judicature: `innocent unless proven
guilty'). And in real life mathematics of course this also not the way to do it, as shown
by Godel, but that's another story . . .
Let's look at an example for the use of the n+-operator. Assume we have a list of
Prolog facts with pairs of people who are married to each other:
Then we can dene a predicate single/1 that succeeds if the argument given can
neither be found as the rst nor as the second argument in any of the married/2-facts.
We can use the anonymous variable for the other argument of married/2 because its
value would be irrelevant:
single( Person) :-
\+ married( Person, _),
\+ married( _, Person).
Example queries:
?- single( mary).
No
?- single( claudia).
Yes
Again, we have to read the answer to the last query as `Claudia is assumed to be
single, because she cannot shown to be married'. We are only allowed to shorten this
interpretation to `Claudia is single', if we can be sure that the list of married/2-facts
is exhaustive, i.e. if we accept the closed world assumption for this example.
Now consider the following query and Prolog's response:
?- single( X).
No
This means, that not for all people X Prolog can show they are single.
Ulle Endriss. An Introduction to Prolog Programming 47
Where to use n+. We have mentioned already that the n+-operator can be applied
to any valid Prolog goal. Recall what this means. Goals are either (sub)goals of a
query or subgoals of a rule-body. Facts and rule-heads aren't goals. Hence, it is not
possible to negate a fact or the head of a rule. This perfectly coincides with what has
been said about the closed world assumption and the notion of negation as failure: it
is not possible to explicitly declare a predicate as being false.
5.3 Disjunction
The comma in between two subgoals of a query or a rule-body denotes a conjunction.
The entire goal succeeds i both the rst and the second subgoal succeed.
We already know one way of expressing a disjunction. If there are two rules with
the same heads in a program then this represents a disjunction, because during the goal
execution process Prolog could chose either of the two rule bodies when the current
goal matches the common rule-head. Of course, it will always try the rst such rule
rst, and only execute the second one, if there has been a failure or if the user has
asked for alternative solutions.
In most cases this form of disjunction is the one that should be used, but sometimes
it can be useful to have a more compact notation corresponding to the comma for
conjunction. In such cases you can use ; (semicolon) to separate two subgoals. As an
example, consider the following denition of parent/2:
parent( X, Y) :-
father( X, Y).
parent( X, Y) :-
mother( X, Y).
This means, `X can be shown to be the parent of Y, if X can be shown to be the
father of Y or if X can be shown to be the mother of Y'. The same denition can also
be given more compactly:
parent( X, Y) :-
father( X, Y);
mother( X, Y).
Note that the precedence of ; (semicolon) is higher than that of , (comma).
Therefore, when implementing a disjunction inside a conjunction you have to structure
your rule-body using parentheses.
The semicolon should only be used in exceptional cases. As it can easily be mixed
up with the comma it makes programs less readable.
(see for example the exercises at the end of the chapter on operators). Using those
operators, we want to be able to type a Prolog term corresponding to the logic formula
in question (with the propositional variables being replaced by a combination of truth
values) into the system and get back the truth value for that row of the table.
In order to compute the truth table for A ^ B we would have to execute the
following four queries:
?- true and true.
Yes
In the examples we have used the Prolog atoms true and false. The former is
actually a built-in predicate with exactly the meaning we require, so that's ne. And
our false should behave just as the built-in fail; the only dierence is the name. So
the Prolog rule for false is quite simple:
false :- fail.
Next are conjunction and disjunction. They obviously correspond to the Prolog
operators , (comma) and ; (semicolon), respectively.
and( A, B) :- A, B.
or( A, B) :- A; B.
Ulle Endriss. An Introduction to Prolog Programming 49
Our own negation operator neg again is just another name for the built-in n+-
Operator:
neg( A) :- \+ A.
Dening implication is a bit more tricky. One way would be to exploit the classical
equivalence of A)B :A_B and dene implies in terms of or and neg. A somewhat
nicer solution (this admittedly depends on one's sense of aesthetics) however would
be to use a cut. Like this:
implies( A, B) :- A, !, B.
implies( _, _).
How does that work? Suppose A is false. Then the rst rule will fail, Prolog will
jump to the second one and succeed whatever B may be. This is exactly what we
want: an implication evaluates to > whenever its antecedent evaluates to ?. In case
A succeeds, the cut in the rst rule will be passed and the overall goal will succeed if
and only if B does. Again, precisely what we want in classical logic.
5.5 Exercises
Exercise 5.1. Type the following queries into a Prolog interpreter and explain what
happens.
(a) ?- (Result = a ; Result = b), !, Result = b.
(b) ?- member( X, [a, b, c]), !, X = b.
result( _, []).
(a) After having consulted this program, what would Prolog reply when presented
with the following query? Try answering this question rst without actually
typing the program in, but verify your solution later using the Prolog system.
?- result( [a, b, c, d, e, f, g], X).
50 Chapter 5. Backtracking, Cuts and Negation
(b) Brie
y describe what the program does and how it does what it does, when the
rst argument of the result/2-predicate is instantiated with a list and a variable
is given in the second argument position, i.e. as in item (a). Your explanations
should include answers to the following questions:
{ What case(s) is/are covered by the Prolog fact?
{ What eect has the cut in the rst line of the program?
{ Why has the anonymous variable been used?
Exercise 5.3. Implement Euclid's algorithm to compute the greatest common divi-
sor (GCD) of two non-negative integers. This predicate should be called gcd/3 and
given two non-negative integers in the rst two argument positions should match the
variable in the third position with the GCD of the two given numbers. Examples:
?- gcd( 57, 27, X).
X = 3
Yes
Exercise 5.5. Write a Prolog predicate divisors/2 to compute the list of all divisors
for a given natural number. Example:
?- divisors( 30, X).
X = [1, 2, 3, 5, 6, 10, 15, 30]
Yes
Ulle Endriss. An Introduction to Prolog Programming 51
Make sure your program doesn't give any wrong alternative solutions and doesn't
fall into an innite loop when the user presses the semicolon key.
Exercise 5.6. Check some of your old Prolog programs to see whether they produce
wrong alternative solutions or even fall into a loop when the user presses ; (semicolon).
Fix any problems you encounter using cuts (one will often be enough).
52 Chapter 5. Backtracking, Cuts and Negation
Chapter 6
From using terms like `predicate', `true', `proof', etc. when speaking about Prolog
programs and the way goals are executed when a Prolog system attempts to answer
a query it should have become clear already that there is a very strong connection
between logic and Prolog. Not only is Prolog the programming language which is
probably best suited for implementing applications that involve logical reasoning, but
Prolog's query resolution process itself is actually based on a logical deduction system.
This part of the notes is intended to give you a rst impression of the logics behind
Prolog.
We start by showing how (simple) Prolog programs can be translated into sets of
rst-order logic formulas. These formulas all have a particular form; they are known
as Horn formulas. Then we shall brie
y introduce resolution, a proof system for Horn
formulas, which forms the basis of every Prolog interpreter.
53
54 Chapter 6. Logic Foundations of Prolog
clause constitutes a conjunction. Prolog queries can be seen as Prolog rules with an
empty head. This empty head is translated as ?. Why this is so will become clear
later. When translating a clause, for every variable X appearing in the clause we have
to put 8x in front of the resulting formula. The universal quantication implicitly
inherent in Prolog programs has to be made explicit when writing logic formulas.
Before summarising the translation process more formally we give an example.
Consider the following little program consisting of two facts and two rules:
bigger( elephant, horse).
bigger( horse, donkey).
is_bigger( X, Y) :- bigger( X, Y).
is_bigger( X, Y) :- bigger( X, Z), is_bigger( Z, Y).
( )
bigger horse; donkey ;
Hence, every formula we get when translating a Prolog program into rst-order
formulas can be transformed into a universally quantied disjunction of literals with
at most one positive literal. Such formulas are called Horn formulas .1 (Sometimes
the term Horn formula is also used to refer to conjunctions of disjunctions of literals
with at most one positive literal.)
As A)? is logically equivalent to :A, by translating queries as implications with
? in the consequent we are basically putting the negation of the goal in a query
into the set of formulas. Answering a query in Prolog means showing that the set
corresponding to the associated program together with the translation of that query is
1
A Prolog fact is simply translated into an atomic formula, i.e. a positive literal. Therefore, formulas
logically inconsistent. Recall that this is equivalent to showing that the goal logically
follows from the set representing the program:
P )? ` ?
;A i P ` A
In principle, such a proof could be accomplished using any formal proof system,
like e.g. the goal-directed calculus, but usually the resolution method is chosen, which
is particularly suited for Horn formulas.
We will not present the resolution method in its entirety here, but the basic idea is
very simple. This proof system has just one rule, which is exemplied in the following
argument (all formulas involved need to be Horn formulas):
: 1_: 2_ 1
A A B
: 1 _: 2B B
: 1 _: 2 _: 2
A A B
hold.
In the example, the rst formula corresponds to a Prolog rule of the following
structure:
b1 :- a1, a2.
The second formula corresponds to a query:
?- b1, b2.
The result of applying the resolution rule then corresponds to the following new
query:
?- a1, a2, b2.
And this is exactly what we would expect. When executing the goal b1, b2 Prolog
starts with looking for a fact or a rule head matching the rst subgoal b1. When the
right rule has been found the current subgoal is replaced with the rule body, in this
case a1, a2. The new goal to execute therefore is a1, a2, b2.
In Prolog this process is repeated until there are no more subgoals left in the query.
In resolution this corresponds to deriving an `empty disjunction', in other words ?.
When using variables in Prolog we have to move from propositional to rst-order
logic. The resolution rule for rst-order logic is basically the same as the one for
propositional logic. The dierence is, that it is not enough anymore just to look for
complementary literals (B1 and :B1 in the previous example) that can be found in
the set of Horn formulas, but now we also have to consider pairs of literals that can
be made complementary by means of unication. Unication in logic corresponds
to matching in Prolog. The variable instantiations put out by Prolog for successful
queries correspond to the unications made during a resolution proof.
Ulle Endriss. An Introduction to Prolog Programming 57
This short presentation has only touched the very surface of what is commonly
referred to as the theory of logic programming. The `real thing' goes much deeper and
has been the object of intensive research for many years all over the world. More details
can be found in books on automated theorem proving (in particular resolution), more
theoretically oriented books on logic programming in general and Prolog in particular,
and various scientic journals on logic programming and alike.
Even though, historically, Prolog is based on the resolution method, the goal execu-
tion process in Prolog can also be interpreted as goal-directed deduction in rst-order
predicate logic.2 The data formulas in a goal-directed proof may represent a list of
clauses and facts, and a goal formula would then correspond to a Prolog query.
6.3 Exercises
Exercise 6.1. Translate the following Prolog program into a set of rst-order logic
formulas.
parent( peter, sharon).
parent( peter, lucy).
male( peter).
female( lucy).
female( sharon).
father( X, Y) :-
parent( X, Y),
male( X).
sister( X, Y) :-
parent( Z, X),
parent( Z, Y),
female( X).
Exercise 6.2. It has been mentioned that the goal execution process in Prolog may
also be explained in terms of the goal-directed deduction method. (By the way, this
also means, that a Prolog interpreter could be based on a goal-directed theorem prover
implemented in a low-level language such as Java or C++.)
Recall the mortal Socrates example from the introductory chapter (page 14) and
what has been said there about Prolog's way of deriving a solution to a query. Trans-
late that program and the query into rst-order logic and give the corresponding
goal-directed proof. Then, sit back and appreciate what you have learned.
2
For an introduction to goal-directed theorem proving see Dov Gabbay, Elementary Logics: A
Procedural Perspective, Prentice Hall Europe, 1998.
58 Chapter 6. Logic Foundations of Prolog
Appendix A
Recursive Programming
Recursion has been mentioned over and over again in these notes. It is not just
a Prolog phenomenon, but one of the most basic and most important concepts in
computer science (and mathematics) in general.
Some people tend to nd the idea of recursive programming diÆcult to grasp at
rst. If that's you, maybe you'll nd the following helpful.
concluded that the statement also holds for n + 1. This proves the statement for all
natural numbers n.
Let's look at an example. You might recall the formula for calculating the sum of
the rst n natural numbers. Before one can use such a formula, it has to be shown
that it is indeed correct.
Claim:
X= n
i
( + 1)
n n
(induction hypothesis)
i =1 2
n =1:
X1
Proof by complete induction:
i =1=
1(1 + 1) p (base case)
=1
i
2
n ; n +1 :
X+1 = X + (
n
i
n
=
n n ( + 1)
+n+1 =
(n + 1)(n + 2) p
2 2
59
60 Appendix A. Recursive Programming
dened as the product of all natural numbers from 1 to n. Here's a more formal,
recursive denition:
1! = 1 (base case)
n! = (n 1)! n for n > 1 (recursion rule)
To compute the actual value of, say, 5! we have to pass through the second part
of that denition 4 times until we get to the base case and are able to calculate the
overall result. That's a recursion!
Take an example, say the query factorial( 5, X), and go through the goal ex-
ecution process step by step, just as Prolog would { and just as you would, if you
wanted to compute the value of 5! systematically by yourself.
Another Example. The following predicate can be used to compute the length of
a list (it does the same as the built-in predicate length/2):
len( [], 0). % base case
Understanding it. Make an eort to really understand at least one recursive pred-
icate denition, like e.g. concat lists/3 (see Section 2.2) or len/2 completely. Draw
the Prolog goal execution tree and do whatever else it takes.
The recursion principle itself is very simple and applicable to many problems.
Despite the simplicity of the principle the actual execution tree of a recursive program
might become rather complicated.
After you got to the stage where you are theoretically capable of understanding it
fully, it is usually enough to look at the problems more abstractly: \I know I dened
the right base case and I know I dened a proper recursion rule, which is calling the
same predicate again with a simplied argument. Hence, it will work. This is so,
because I understand the recursion principle, I believe in it, and I am able to apply
it. Now and forever."
A.4 Debugging
In SWI-Prolog it is possible to debug your Prolog programs. This might help you to
understand how queries are resolved (it might however just be really confusing).
62 Appendix A. Recursive Programming
Use spy/1 to put a spypoint on a predicate (typed into the interpreter as a query,
after compilation). Example:
?- spy( len).
Spy point on len/2
Yes
[debug] ?-
For more information on how to use the Prolog debugger check your reference
manual. Here's an example for the len/2-predicate dened before.
[debug] ?- len( [dog, fish, tiger], X).
* Call: ( 8) len([dog, fish, tiger], _G397) ? leap
* Call: ( 9) len([fish, tiger], _L170) ? leap
* Call: ( 10) len([tiger], _L183) ? leap
* Call: ( 11) len([], _L196) ? leap
* Exit: ( 11) len([], 0) ? leap
* Exit: ( 10) len([tiger], 1) ? leap
* Exit: ( 9) len([fish, tiger], 2) ? leap
* Exit: ( 8) len([dog, fish, tiger], 3) ? leap
X = 3
Yes
Index
=/2, 10 true/0, 10
n=/2, 17 write/1, 11
n+-Operator, 45
clause, 9
anonymous variable, 9 closed world assumption, 45
append/3, 22 comments, 15
arithmetic evaluation, 25 compilation, 11, 35
associativity, 32 complete induction, 59
associativity patterns, 32, 33 compound/1, 11
atom, 8, 53 compound term, 9
atom/1, 11 concatenation of lists, 20
conjunction (,), 47
backtracking, 14, 39 consult/1, 11
problems with, 40 current op/2, 32
bar notation, 19 cut, 41
body of a rule, 10 problems with, 44
built-in predicate
=/2, 10 debugging, 61
n=/2, 17 disjunction (;), 47
n+/1, 45 empty list, 19
append/3, 22
atom/1, 11 fact, 9, 55
compound/1, 11 fail/0, 10
consult/1, 11 functor, 9, 53
current op/2, 32
fail/0, 10 goal execution, 13, 56
help/1, 11 goal-directed deduction, 57
is/2, 25 ground term, 9
last/2, 22 head of a list, 19
length/2, 22 head of a rule, 10
member/2, 22 head/tail-pattern, 20
nl/0, 11 help/1, 11
op/3, 34 Horn formula, 55
reverse/2, 22
select/3, 22 induction, 59
spy/1, 62 inx operator, 32
63
64 Index
is-Operator, 25 true/0, 10
last/2, 22 variable, 9, 53
length/2, 22 anonymous, 9
list, 19
empty, 19 write/1, 11
list notation
bar, 19
head/tail-pattern, 20
internal, 19
matching, 12, 25
operators, 35
member/2, 22
negation as failure, 46
nl/0, 11
op/3, 34
operator
inx, 32
postx, 32
prex, 32
operator denition, 34
postx operator, 32
precedence, 31
predicate, 9, 53
prex operator, 32
program, 10
query, 10, 55
recursive programming, 60
resolution, 56
reverse/2, 22
rule, 10, 55
select/3, 22
spy/1, 62
tail of a list, 19
term, 8, 53
compound, 9
ground, 9
translation, 53