| ?- 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
| ?-
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
| ?-
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.
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
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.
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.
(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
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.
(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
| ?-