52 349 Logic Programming
Chapter 8 - Using System Procedures
Lists - The Basic Prolog Element
Throughout this chapter, we use the notation of C-Prolog (as on the SUN's)
- other systems may differ somewhat!
The basics we met in the initial practicals
- invoke Prolog by the Unix command
- pose questions in the obvious way
?- spouse(Who, paul).
- seek other instantiations through the semi-colon
- escape to Unix as a side effect of
When preparing files for use in Prolog, remember the syntactic definition of a term
- which means the last Prolog clause must be followed by at least one space or blank line.
- incorporate the contents of an ASCII file as a side effect of
% Which adds all the clauses therein.
% Which replaces all the previously
% existing clauses for every added functor.
- recall the shorthand forms
?- [file_1, file_2, ..., file_n].
% consult(file_1), consult(file_2), ..., consult(file_n).
?- [- file_1, - file_2, ..., - file_n].
reconsult(file_1), reconsult(file_2), ..., reconsult(file_n).
- the shorthand forms can, of course, be mixed
- "consult" will add extra clauses to a procedure,
"reconsult" will replace clauses
... never consult the same file twice!
- note how these accommodate the re-use of code
... so build many small files!
- "consult" and "reconsult" can not undertake Unix shell expansions, for example of
- useful in this context is
?- system("Unix command string").
which gives temporary escape to Unix.
We can add files of rules - how about a single rule?
assert(clause) always succeeds
- but as a side effect adds to the database the fact or rule called "clause"
- note the syntax:
- extra bracket pair for some systems
- no period after clause
- In the example that follows, the new lines and spaces (although fully correct) are not necessary but are included to emphasise the syntax.
asserta/1, assertz/1 are procedurally-driven variants, adding procedures respectively at the start and the end of the list of rules.
The opposite of the assert family is retract/1.
Between them, "assert" and "retract" allow the database to change as the program is run ...
... but not, of course, the associated, consulted files!
To obtain a summary of the database, use
but note how numbers (reflecting a memory map) replace the variable names.
clause/2 works similarly for a single procedure, by matching the head and instantiating the body.
Hint: printing to a file (see §8.3 below) the output of "listing" can be useful for future runs!
A related effect
- showing the operation of a program
- as it steps through the computational tree
is obtained from trace/0.
- trace steps through the operation of a program a line at a time,
but only for the immediately following goal
- for maximum effect, you need to use "spy points"
- see the goals spy/0 and no_spy/0 in the standard textbooks such as Bratko.
Prolog manipulates sequential files of text
- which can be accessed in two ways
- character by character
- term by term
The notion is that of streams of data
- only one of which can be active
- the "current input (or output) stream"
By default, the usual rules hold
- input from the keyboard
- output to the screen
- in each case, the default pseudo-file being called "user"
Files are opened as a side effect:
and eventually closed as another side-effect:
see/1 and tell/1 both
- reinitialise the relevant file
seen/0 and told/0 both also
- make "user" the current (relevant) stream
Thus, assuming write/1 and nl/0 both defined below, consider the following program fragments:
) As on left
) As on left
For file_2 (which remains, potentially, open for writing) the output is
But for file_1 (now closed) the output is
Character handling is primitive - working through decimal ASCII values.
Output is straight-forward
put(X) where X is an ASCII value
Input is more complex
get0(X) [get zero] instantiates X to the ASCII code of the next character waiting to be taken from the input stream
get(X) is a more subtle variant, of similar behaviour except that non-printable characters (including, in particular, spaces) are skipped to instantiate X to the ASCII code of the next printable character
| % sets X, 0 < X < 128
|% skips to X, 32 < X < 128
If we wish, we can convert between literals and ASCII using the built in
?- name(Atom, List).
(true if List is a list of the ASCII codes for the successive characters of Atom), which is useful in term manipulation.
To input terms
- each of which must end with a period and non-printing character
- which instantiates X, if it can, to the next term on the input stream
- or, if at the end of the relevant file, instantiates X to the atom end_of_file
(typically the value 26, the ASCII code for <CONTROL>-d).
- read/1 will fail if X is already instantiated! See §8.3.3 below.
To output terms, we have
- write/1 is clever ...
... it can handle terms of any complexity
- though the result may be a nasty tangle of letters and brackets!
There are at least two other output elements
provides 'Number' blanks
provides a new line
Let's look at a simple example, using 'read' and 'write' to improve on the crude-
N_square is N*N.
% 'is' instantiates N_square to N^2.
Which gives a response like:
?- square(2, X).
X = 4.
?- square (3, X).
X = 9
If we split the process into two, one to read, and one to respond:
% Arbitrary atom to stop with.
N_square is N*N,
then the response is much neater than it was for the first version:
There is a catch!
- "read" and "write" are ordinary goals
- what happens if they fail?
- or, more likely, have to resatisfied?
For example, read(X)
- will cause X if free to be instantiated to t, say, the next term of input
- but if X is not free, read fails
... and t has been consumed and is gone for ever.
Input/Output activity is essentially deterministic
- and thus has no place, strictly, in logic [or functional] programming!
- Sometimes we need to compare terms - are they equal?
- But humans overlay the phrase "equality"
- whereas Prolog disambiguates.
- Note that normally in this context we depart from conventional Prolog syntax and in effect use "operators" (see also §9.1)
- which are simply standard functors redefined to be used in infix form
- but Prolog evaluation remains the same:
we undertake pattern matching to determine the truth (or otherwise) of some assertion.
Simplest equality test is
X = Y
- true if X and Y can be matched
For numerical evaluation,
X is Expression
- as in the square programs of §8.3.2 above
- true if X matches the numerical value of Expression
Related to this is
Exp_1 =:= Exp_2
- true if the numerical values of Exp_1 and Exp_2 are the same
- with converse
Exp_1 =\= Exp_2
When we want to test the literal equality of two terms, we use
Term_1 == Term_2
- with converse
Term_1 == Term_2 is true when both terms have the same structure and have corresponding arguments
?- f(a, b) == f(a, b).
?- f(a, b) == f(a, X).
?- X = b, f(a, b) == f(a, X).
X = b
Somewhat related to this are the triple of
functor(Term, F, N).
- true if F is Term's principal functor and it has arity N
arg(Number, Term, A)).
- true if the Number-th argument of Term is A
- see §8.3.1 above.
Moving even further towards procedural considerations, sometimes we need a guard
- for example,
X is Y + Z
requires X to be free, Y and Z numerical
Prolog offers the range of guards you might expect:
|true if X is an integer or if X is an atom
|true if X is atomic or if X is an instantiated variable
|true if X is an uninstantiated variable
and, depending on the particular version of Prolog, may have others!