Tuesday, September 27, 2011

The Basics prolog programming


Chapter 1
The Basics


Prolog (programming in logic) is one of the most widely used programming languages
in artificial 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, such as artificial intelligence, natural
language processing, databases, . . . , but pretty useless in others, such as graphics or
numerical algorithms.
By following this course, you will learn how to use Prolog as a programming language to solve practical problems in computer science and artificial intelligence. You will also learn how the Prolog interpreter actually works. The latter will include an introduction to the logical foundations of the Prolog language.
These notes cover the most important Prolog concepts you 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.
• I. Bratko.  Prolog Programming for Artificial Intelligence.          3rd edition, Addison-
Wesley Publishers, 2001.
• F. W. Clocksin and C. S. Mellish. Programming in Prolog. 5th edition, SpringerVerlag, 2003.
• L. Sterling and E. Shapiro. The Art of Prolog. 2nd edition, MIT Press, 1994.

1.1    Getting Started: An Example
In the introduction it has been said that Prolog is a declarative (or descriptive) language. Programming in Prolog means describing the world. Using such programs means asking Prolog questions about the previously described world. The simplest way of describing the world is by stating facts, like this one:

1








2                                                                                                     Chapter 1.  The Basics


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. Heres 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 previously been communicated to the Prolog system. Now, is a monkey bigger than an elephant?
?- bigger(monkey, elephant).
No
No, its 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 find 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 defined in our program.  The correct interpretation of the negative answer Prolog has given 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 way of doing this would be to add the remaining facts, like e.g. bigger(elephant, monkey), to our program.  For our little example this would mean adding another 5 facts.  Clearly too much work and probably not too clever anyway.
The  far  better  solution  would  be  to  define  a  new  relation,  which  we  will  call
is_bigger, as the transitive closure (don
t worry if you dont know what that means)








Ulle Endriss.  And Introduction to Prolog Programming                                                    3

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.
You can think of the the bigger-facts as data someone has collected by browsing through the local zoo and comparing pairs of animals. The implementation of is_bigger, on the other hand, could have been provided by a knowledge engineer who may not know anything at all about animals, but understands the general concept of something being bigger than something else and thereby has the ability to formulate general rules regarding this relation.  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 find 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 thats 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 finally succeeds.  How this goal execution as well as term matching and variable instantiation really work will be examined in more detail in Section 1.3.
Of course, we can do slightly more exiting stuff 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 makes us








4                                                                                                     Chapter 1.  The Basics

happy already, we can press Return now and thats it.  In case we want to find 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 final 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 first impression of Prolog programming. 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 differ from one system to the other, but it should not be difficult to find out by consulting the user manual of your system.

1.2     Prolog Syntax
This section describes the most basic features of the Prolog programming language.

1.2.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.  And Introduction to Prolog Programming                                                    5


This is also a Prolog atom.
Finally, strings made up solely of special characters like + - * = < > : & (check the
manual of your Prolog system for the exact set of these characters) are also atoms.
Examples:
+, ::, <------>, ***
Numbers.  All Prolog implementations have an integer type:  a sequence of digits,
optionally preceded by a - (minus).  Some also support floats.  Check the manual for
details.
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.

Compound terms.  Compound terms are made up of a functor (a Prolog atom) and a number of arguments (Prolog terms, i.e. atoms, numbers, variables, or other compound terms) enclosed in parentheses and separated by commas. The following are some examples for compound terms:
is_bigger(horse, X), f(g(X, _), 7),My Functor(dog)
Its important not to put any blank characters between the functor and the opening parentheses, or Prolog wont understand what youre trying to say.  In other places, however, spaces can be very helpful for making programs more readable.
The sets of compound terms and atoms together form the set of Prolog predicates. A term that doesnt contain any variables is called a ground term.

1.2.2     Clauses, Programs and Queries
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.
Facts.  A fact is a predicate followed by a dot. Examples:
         bigger(whale, _).
life_is_beautiful.
The intuitive meaning of a fact is that we define a certain instance of a relation as being
true.








6                                                                                                     Chapter 1.  The Basics


Rules.  A rule consists of a head (a predicate) and a body.       (a sequence of predicates
separated by commas).  Head and body are separated by the sign :- and, like every Prolog expression, a rule has to be terminated by a dot. Examples:
is_smaller(X, Y) :- is_bigger(Y, X).
aunt(Aunt, Child) :-
sister(Aunt, Parent),
parent(Parent, Child).
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 rules body are true.

Programs.  A Prolog program is a sequence of clauses.

Queries.  After compilation a Prolog program is run by submitting queries to the interpreter.  A query has the same structure as the body of a rule, i.e. it is a sequence of predicates separated by commas and terminated by a dot.  They can be entered at the Prolog prompt, which in most implementations looks something like this: ?-. When writing about queries we often include the ?-. Examples:
?- is_bigger(elephant, donkey).
?- small(X), green(X), slimy(X).
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.

1.2.3     Some Built-in Predicates
What we have seen so far is already enough to write simple programs by defining 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; all of them should be explained in the user manual of your Prolog system.
Built-ins can be used in a similar way as user-defined predicates.  The important difference 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 effectively mean changing their definition.

Equality.  Maybe the most important built-in predicate is = (equality).   Instead of
writing expressions such as =(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.3.








Ulle Endriss.  And Introduction to Prolog Programming                                                    7

Guaranteed success and certain failure.  Sometimes it can be useful to have predicates that are known to either fail or succeed in any case. The predicates fail and true serve exactly this purpose.

Consulting  program  files.  Program  files  can  be  compiled  using  the  predicate
consult/1.1 The argument has to be a Prolog atom denoting the particular pro-
gram file. For example, to compile the file 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.

Output.  If besides Prologs 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

?- X = elephant, write(X), nl. elephant
X = elephant
Yes
In the second example first the variable X is bound to the atom elephant and then the
value of  X, i.e. elephant, is written on the screen using the write/1 predicate.  After
skipping to a new line Prolog reports the variable binding(s), i.e. X = elephant.

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


1 The /1 is used to indicate that this predicate takes one argument.








8                                                                                                     Chapter 1.  The Basics

?- 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.
Help.  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:
?- help(atom).
atom(+Term)
Succeeds if Term is bound to an atom.

1.3     Answering Queries
We have mentioned the issue of term matching before in these notes.  This concept is
crucial to the way Prolog replies to queries, so we present it before describing what
actually happens when a query is processed (or more generally speaking: when a goal is
executed).

1.3.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 fixed
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 corresponding query to which Prolog would react by listing the appropriate variable instantiations:
?- is_bigger(X, dog) = is_bigger(elephant, dog). X = elephant
Yes
The following is an example for a query that doesnt succeed, because X cannot match with 1 and 2 at the same time.
?- p(X, 2, 2) = p(1, Y, X).
No








Ulle Endriss.  And Introduction to Prolog Programming                                                    9

If, however, instead of X we use the anonymous variable _, matching is possible, because
every occurrence of _ represents a distinct variable.  During matching Y is instantiated
with 2:
?- p(_, 2, 2) = p(1, Y, _).
Y = 2
Yes
Another example for matching:
?- f(a, g(X, Y)) = f(X, Z), Z = g(W, h(X)).
X = a
Y = h(a)
Z = g(a, h(a))
W = a
Yes
So far so good. But what happens, if matching is possible even though no specific variable
instantiation has to be enforced (like in all previous examples)? Consider the following
query:
?- X = my_functor(Y).
X = my_functor(_G177)
Y = _G177
Yes
In this example matching succeeds, because X could be a compound term with the functor
my_functor and a non-specified single argument. Y could be any valid Prolog term, but
it has to be the same term as the argument inside X. In Prolog
s output this is denoted
through the use of the variable _G177. This variable has been generated by Prolog during
execution time. Its particular name, _G177 in this case, may be different every time the
query is submitted.

1.3.2     Goal Execution
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 satisfied. 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








10                                                                                                   Chapter 1.  The Basics

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 first possible fact or rule-head it can find.
If the principal functor of a goal is a built-in predicate the associated action is executed whilst the goal is being satisfied. For example, as far as goal execution is concerned the predicate
write(Hello World!)
will simply succeed, but at the same time it will also print the words Hello World! on the screen.
As mentioned before the built-in predicate true will always succeed (without any further side-effects), whereas fail will always fail.
Sometimes there is more than one way of satisfying the current goal. Prolog chooses the first possibility (as determined by the order of clauses in a program), but the fact that there are alternatives is recorded.  If at some point Prolog fails to prove a certain subgoal, the system can go back and try an alternative way of executing the previous goal. This process is known as backtracking.
We shall exemplify the process of goal execution by means of the following famous argument:
All men are mortal.
Socrates is a man.
Hence, Socrates is mortal.
In Prolog terms, the first statement represents a rule: X is mortal, if X is a man (for all
X). The second one constitutes a fact: Socrates is a man.  This can be implemented in Prolog as follows:
mortal(X) :- man(X). man(socrates).
Note that X is a variable, whereas socrates is an atom. The conclusion of the argument, "Socrates is mortal", can be expressed through the predicate mortal(socrates). After having consulted the above program we can submit this predicate to Prolog as a query, which will cause the following reaction:
?- mortal(socrates).
Yes
Prolog agrees with our own logical reasoning. Which is nice. But how did it come to its conclusion? Lets follow the goal execution step by step.

(1) The query mortal(socrates) is made the initial goal.








Ulle Endriss.  And Introduction to Prolog Programming                                                  11

(2) Scanning  through  the  clauses  of  our  program,   Prolog  tries  to  match
      
mortal(socrates) with the first possible fact or head of rule. It finds mortal(X),
      
the head of the first (and only) rule. When matching the two terms the instantia-
       tion X = socrates needs to be made.
(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).
(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.

1.4    A Matter of Style
One of the major advantages of Prolog is that it allows for writing very short and compact programs solving not only comparatively difficult problems, but also being readable and (again: comparatively) easy to understand.
Of course, this can only work, if the programmer (you!) pays some attention to his or her programming style. As with every programming language, comments do help. In Prolog comments are enclosed between the two signs /* and */, like this:
/* This is a comment.           */
Comments that only run over a single line can also be started with the percentage sign
%. This is usually used within a clause.
aunt(X, Z) :-
sister(X, Y),            % A comment on this subgoal.
parent(Y, Z).
Besides the use of comments a good layout can improve the readability of your programs significantly. The following are some basic rules most people seem to agree on:
(1) Separate clauses by one or more blank lines.
(2) Write only one predicate per line and use indentation:

blond(X) :-
father(Father, X),
blond(Father),
mother(Mother, X),
blond(Mother).








12                                                                                                   Chapter 1.  The Basics


(Very short clauses may also be written in a single line.)
(3) Insert a space after every comma inside a compound term:
       born(mary, yorkshire,
01/01/1980)
(4) Write short clauses with bodies consisting of only a few goals.  If necessary, split
      
into shorter sub-clauses.
(5) Choose meaningful names for your variables and atoms.

1.5     Exercises
Exercise 1.1.  Try to answer the following questions first "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 whats 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).








Ulle Endriss.  And Introduction to Prolog Programming                                                  13


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).
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, define 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) cousin

You may want to use the operator \=, which is the opposite of =.  A goal like X          \= 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 dont 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.








14                                                                                                   Chapter 1.  The Basics

Exercise 1.4.  Most people will probably find all of this rather daunting at first. 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 stuff on matching and goal execution are particularly
important.

0 comments: