Written by Zhiwei Wang, 1996
Students taking class in the Department of Computer Science, University of Regina are permitted to reproduce this manual for the purpose of studying.

INPUTS AND OUTPUTS

7.17.4.1. WRITE AND READ

The built-in predicate 'write' is used for displaying outputs on the screen. For example:

| ?- write('Hello, world').

output on the screen will be:

Hello, world

The built-in predicate 'read' is used for reading inputs from the keyboard. The input term must be followed by a dot and a return key. The syntax for read is:

read(X).

If X is an uninstantiated variable, that means no value has been assigned to it, then X will be instantiated to the argument entered. If X is instantiated, then the goal will succeed only when the input matches the value of X. This can be used to check for a particular user response. For example:

| ?- read(yes).

will succeed only if the user enters 'yes'.

To cause the start of a new line, we can use 'nl', which is a built-in predicate having no arguments. To insert N spaces in the output, use:

tab(N).

The following is a procedure called 'greetings' which asks the user to input a name and then gives greetings.

greetings :-
    nl,
    write('Input your name, please'),
    tab(3),
    read(Name),
    nl,nl,
    write('Hello '),
    write(Name).

The following is the output when this procedure is called:

| ?- greetings.

Input your name, please zhiwei.
Hello zhiwei
yes

| ?-

7.17.4.2. READING AND WRITING CHARACTERS

A character is written on the screen with the predicate put. The syntax is:

put(C).

where C is the ASCII code of the character to be output. For example:

| ?- put(72), put(105).

will output

Hi

on the screen.

The predicate get is used for reading printable characters. The syntax is:

get(C).

This will instantiate the variable C to the ASCII code of the first printable character. For example:

| ?- get(C).
|: a.

C = 97 ? Action (";" for more choices, otherwise <return>): ;

no

| ?-
| ?- get(Character).
|: 1.

Character = 49 ? Action (";" for more choices, otherwise <return>):[return]

yes

| ?-

7.17.4.3. FILE INPUT AND OUTPUT

At any time during the execution of a Prolog program, only two files are 'active': the current input stream and the current output stream.

 By default, they are set to the keyboard and the monitor respectively.

The current input stream can be changed to a file by:

see(Filename)

To switch back up the terminal, use:

see(user)

Similarly, The current output stream can be changed to a file by:

tell(Filename)

To switch back up the terminal, use:

tell(user)

The goal

seen

closes the current input file. The goal

told

closes the current output file.

7.17.5. BUILT-IN PREDICATES

Built-in predicates are predicates whose definitions are provided in advance by the Prolog system. The predicates introduced in section 7.17.4 (read, write, etc.) are examples. Here is a list of some more:

var(X) succeeds if X is uninstantiated
nonvar(X) succeeds if X is instantiated
atom(X) succeeds if X is an atom
integer(X) succeeds if X stands for an integer
atomic(X) succeeds if X is either an integer or an atom
real(X) succeeds if X is an real number
string(X) succeeds if X is a string
listing(X) Produces a list of all the predicates with the same name as X. X must be an atom
listing lists all the user defined clauses in the database
assert(X) X is added to the database as a new clause
asserta(X) has the same effect as assert except that X is placed before any clause with the same name in the database
retract(X) searches the database for a clause matching X and deletes it. Succeeds if such a matching is found.
retractall(X) removes all clauses that can be matched with X from the database
=.. this is an infix operator. The syntax is
X =.. L
not(X) not(X) succeed if X fails and vice versa

7.17.6. PROGRAMS AND PROCEDURES

7.17.6.1. PROCEDURES

A Prolog program consists of clauses. Each clause has a head which is about some predicate. Sometimes it is convenient to consider the collection of clauses about the same predicate as a whole. Such a collection is called a procedure.

Let us look at an example:

qualified :-
    experienced.

qualified :-
    having_degree,
    good_academic_records.

experienced :-
    working_year(Y),
    Y > 5.

good_academic_records :-
    courses_average(A),
    A > 70.

having_degree.
working_year(2).
courses_average(75).

The above program defines several relations- 'qualified', 'experienced', etc. The procedure 'qualified' consists of two clauses, the procedure 'experienced' consists of only one clause, etc.

7.17.6.2. DECLARATIVE MEANING AND PROCEDURAL MEANING

Prolog programs may be understood in two ways---declaratively and procedurally. The declarative meaning of a program determines what a program does. The procedural meaning determines how to do it. A Prolog clause consists of a head and a body :

head :- body.

The declarative meaning of a clause is that 'head' is true if 'body' is true. The procedural meaning is that to solve 'head', first solve 'body'.

In the example in the preceding section, the procedure 'qualified' consists of two clauses:

qualified :-
    experienced.

qualified :-
    having_degree,
    good_academic_records.

The declarative meaning of this procedure is:

One is qualified if one is experienced or one has a degree and has a good academic record.

The procedural meaning is:

To find out if one is qualified, first find out if one is experienced. If this fails, find out if one has a degree and then find out if one has a good academic records.

7.17.6.3. PROGRAM EXECUTION AND BACKTRACKING

When we query a Prolog program, the Prolog system will try to answer the question according to the following steps:

(1) Put all the goals in the question into a goal list.

(2) If the goal list is empty, which means that the goals in the question are satisfied, then stop, else search the program in a top-down manner until a clause whose head matches the first goal in the goal list.

(3) If such a clause is found, then put the body of the clause at the beginning of the goal list and go to step (2).

(4) If such a clause can not be found, then go back to the last successfully matched goal and search for another clause whose head matches the goal and go to (3). This process is called backtracking. This time, the search begins from the clause that previously matched the goal. If there was no previously matched goal, which means that the goals in the query can not be satisfied, then stop.

Let us look at the example given in section 7.17.6.1:

qualified :- % clause 1.
    experienced.

qualified :- % clause 2.
    having_degree,
    good_academic_records.

experienced :- % clause 3.
    working_year(Y),
    Y > 5.

good_academic_records :- % clause 4.
    courses_average(A),
    A > 70.

having_degree. % clause 5.
working_year(2). % clause 6.
courses_average(75). % clause 7.

(1) When we asked the question:

| ?- qualified.

'qualified' becomes the first item in the goal list. Prolog searches the program in a top-down manner and find clause 1:

qualified :- % clause 1.

experienced.

The body of clause 'experienced' is put at the beginning of the goal list.

(2). Now 'experienced' becomes the goal to be satisfied. Prolog searches the program in a top-down manner again and find clause 3:

experienced :- % clause 3.
    working_year(Y),
    Y > 5.

(3). There are two goals in the body of clause 3. They are now put in the goal list with the goal 'working_year(Y)' at the very beginning followed by the goal 'Y > 5'. Prolog looks at the updated goal list and tries to satisfy 'working_year(Y)'. It finds the fact:

working_year(2). % clause 6.

match the goal. The variable Y is instantiated to 2. The body of a fact is empty, so no new goal is added to the goal list.

(4). Prolog now tries to satisfy the next goal on the list:

Y > 5

Since Y is instantiated to 2, Prolog can not satisfy this goal. Prolog backtracks the last goal that was matched, i.e. the goal 'working_year(Y)', and tries to match this goal in another way. While backtracking, the variable Y is uninstantiated.

(5). To satisfy the goal 'working_year(Y)', Prolog does not search from the beginning, but continues from the clause where this goal was matched last. In our case, it is clause 6. Since there is no other clauses whose head matches the goal 'working_year(Y)', the matching fails.

(6). Prolog backtracks again to the last goal that was successfully matched, i.e. the goal 'experienced'. It tries to find an alternative clause to match the goal. The searching continues. No other clause with 'experience' as the head is found, so Prolog backtracks to the goal 'qualified'.

(7). Prolog finds clause 2:

qualified :- % clause 2.
    having_degree,
    good_academic_records.

(8). Similarly, Prolog put the goals in the body of clause 2 in the goal list. The goals to be satisfied are now 'having_degree' and 'good_academic_records'. The goal 'having_degree' is satisfied by matching it with clause 5:

having_degree. % clause 5.

(9). The goal 'good_academic_records' is matched with the head of clause 4:

good_academic_records :- % clause 4.
    courses_average(A),
    A > 70.
 
(10). The goals in the body of clause 4 are the two goals to be satisfied now. The first goal is 'courses_average(A)' which is satisfied by matching it with clause 7:

courses_average(75). % clause 7.

The variable A is instantiated to 75.

(11). The next goal to be satisfied is 'A > 70'. Since A is instantiated to 75, this goal succeeds.

(12). By now, there is no more goal to be satisfied in the goal list, the searching stops. The goal 'qualified' in the original question is satisfied and Prolog answers:

yes

7.17.6.4. CONTROL BACKTRACKING WITH 'CUT'

Prolog will automatically backtrack if a goal is not satisfied. This is nice. However, sometimes this backtracking is unnecessary, and unnecessary backtracking may cause inefficiency. To prevent this, the facility 'cut' can be used. We will illustrate this by an example. The sign of a number is defined as -1 for negative number, 0 for zero, and 1 for positive number. Written in Prolog:

sign(X, -1) :- X < 0.
sign(X, 0) :- X = 0.
sign(X, 1) :- X > 0.

Suppose we pose a query as below:

| ?- sign(-3, Y), Y > 0.

When execute the first goal, Y will be instantiated to -1, so the second goal becomes:

-1 > 0

which fails, and so does the whole goal list. Since the goal in not satisfied, Prolog will backtrack. It will try the second and third clauses, but in vain, because the three clauses deal with three mutually exclusive conditions. If a goal succeeds in match one of these clauses, it is bound to fail for the rest. To avoid this futile backtracking we have to tell Prolog explicitly. This is done by using the facility 'cut' which is written as '!'. The new version of the program is as below:

sign(X, -1) :- X < 0, !.
sign(X, 0) :- X = 0, !.
sign(X, 1) :- X > 0.

The symbol '!' will prevent backtracking at the points that it appears in the program . If we ask the same question:

| ?- sign(-3, Y), Y > 0.

Prolog will match the goal 'sign(-3, Y)' with the first clause and instantiate Y to -1, the second goal becomes '-1 > 0' and will fail. Prolog will try to backtrack. But this time the backtracking will not beyond the point marked '!', so the second and third clauses will not be searched. The new version with 'cut' is in general more efficient than the original one.

7.17.6.5. RECURSIVE PROCEDURES

Recursion is one of the fundamental principles in Prolog programming. The principle of recursive programming is to split the problem into two cases:

(1) boundary cases, which is the simplest form and the solution is given directly;

(2) general cases, where the procedure itself is called but in a simpler form.

Let us look at some examples.

Suppose we want to add a new rule in Example 2 of section 7.17.1. The new rule defines a relation called 'descendant'. The non-recursive version of the definition involves infinite sentences:

If X is Y's child then X is Y's descendant;

if X is Z1's child and Z1 is Y's child then X is Y's descendant;

if X is Z1's child and Z1 is Z2's child and Z2 is Y's child then X is Y's descendant;

 ...

Expressed in Prolog, there will be a sequence of infinite clauses:

descendant(X, Y) :-
    child(X, Y).

descendant(X, Y) :-
    child(X, Z1),
    child(Z1, Y).

descendant(X, Y) :-
    child(X, Z1),
    child(Z1, Z2),
    child(Z2, Y).

...

Using recursion, the definition can be completed in two sentences. The boundary case is:

if X is Y's child, then X is Y's descendant.

The general case is:

if X is Z's child and Z is Y's descendant, then X is Y's descendant.

Written in Prolog:

descendant(X, Y) :- % boundary case
    child(X, Y).

descendant(X, Y) :- % general case
    child(X, Z),
    descendant(Z, Y).

Note that in the second clause, we use the predicate 'descendant' to define 'descendant' itself.

Here is another example. The factorial of n is defined by:

n! = n*(n-1)*(n-2)*...*2*1

The recursive definition is

0! = 1
 n! = n*((n-1)!)

Written in Prolog it is:

factorial(0, 1). % boundary case
factorial(N, F) :- % general case
    N > 0,
    N1 is N-1,
    factorial(N1, F1),
    F is N * F1.

The following are some sample queries and their answers from Prolog:

| ?- factorial(4, F).

F = 24 ?

yes

| ?- factorial(X, 4).

{INSTANTIATION ERROR: _22>0 - arg 1}

| ?- factorial(X, F).

F = 1,

X = 0 ? ;

{INSTANTIATION ERROR: _22>0 - arg 1}

| ?- factorial(X, F).

F = 1,
X = 0 ?

yes

| ?-

7.16.7. REFERENCES

Bratko, I., Prolog-Programming for Artificial Intelligence, Addison-Wesley Inc, 1990
Clocksin, W. F. and Mellish, C. S., Programming in Prolog, Springer-Verlog Berlin Heidelberg, New York, 1981
Crookes, D., Introduction to Programming in Prolog, Prentice Hall, New York, 1988
Kononenko, I. and Lavrac, N., Prolog Through Examples, Sigma Press, Wilmslow, England, 1988
McDonald, C. and Yazdani, M., Prolog Programming: A Tutorial Introduction, Blackwell Scientific Publications, Oxford, 1990