User's Guide to Emas Prolog
KEY PROLOG
This View tree provides an online version of the Emas Prolog User's Guide.
The sections are arranged exactly as in the hardcopy User's Guide which
is available from the AI Department at Edinburgh (see section 6).
The list below gives the top level structure of the manual. To go through
the manual page by page just keep hitting return. Examples of other useful
View commands are:
View: 2.3 - to go to section 2.3
View: q - to leave the view system (quit)
Using the EMAS Prolog system
KEY PROLOG
1.1 Preface
1.2 Using Prolog - Overview
1.3 Access to EMAS Prolog
1.4 Reading-in Programs
1.5 Using files, and calling EMAS commands from within Prolog
1.6 Directives: Questions and Commands
1.7 Syntax Errors
1.8 Saving A Program
1.9 Restoring A Saved Program
1.10 Program Execution And Interruption
1.11 Nested executions - break and abort
1.12 Exiting From The Interpreter
1.13 Debugging facilities
1.14 Prolog Syntax
1.14.1 Terms
1.14.2 Operators
1.15 Using a Terminal without Lower-Case
Preface
This manual describes the EMAS 2900 version of Prolog.
Prolog is a simple but powerful programming language originally developed at
the University of Marseilles, as a practical tool for programming in logic.
From a user's point of view the major attraction of the language is ease of
programming. Clear, readable, concise programs can be written quickly with few
errors. Prolog is especially suitable for high level symbolic programming
tasks and has been applied in many areas of Artificial Intelligence research.
The EMAS Prolog system was written by Luis Damas of the Dept. of Computer
Science, Edinburgh. The system consists of a Prolog interpreter and a wide
range of evaluable predicates (system provided procedures). Its design was
based on the (Edinburgh) DEC10 Prolog system and the system is closely
compatible with DEC10 Prolog and thus is also reasonably close to UNIX Prolog
and RT11 Prolog.
Queries, suggestions, bug reports, and so forth should be sent to the EMAS
Prolog maintainers by using the Prolog evaluable predicate:
| ?- gripe.
You will be prompted for text which will then be mailed to the appropriate
person. Emas Prolog is currently being maintained by Lawrence Byrd and Peter
Ross in the Artifificial Intelligence Department. This facility is also
available as an EMAS command, once you have the directory CONLIB.PROLOG in
your searchlist - which you need to run Prolog anyway:
Command: gripe
If you would prefer to use the EMAS MAIL program then queries etc. can be
mailed to "Prolog".
Note, however, that this manual is not intended as an introduction to the
Prolog language and how to use it. For this purpose you should study:
Programming in Prolog
William Clocksin & Chris Mellish
Springer Verlag 1981
This manual assumes that you are familiar with the principles of the Prolog
language, its purpose being to explain how the EMAS Prolog system is used, and
to describe all the evaluable predicates that the system makes available to the
user.
Using Prolog - Overview
The Emas Prolog system offers the user an interactive programming environment
with tools for incrementally building programs, debugging programs by following
their executions, and modifying parts of programs without having to start again
from scratch.
The text of a Prolog program is normally created in a number of EMAS files
using one of the standard text editors. The Prolog interpreter can then be
instructed to read-in programs from these EMAS files; this is called
"consulting" the file. The text editor can be called directly from within
Prolog, and it can be arranged for the file to be consulted or re-consulted
when you leave the editor and return to Prolog. Re-consulting means that
definitions for procedures in the file will replace any old definitions for
these procedures. Using the editor in this way makes it easy to incrementally
develop, debug and then correct Prolog programs without ever leaving the Prolog
system. It is recommended that you make use of a number of different files
when writing programs. Since you will be editing and consulting/re-consulting
individual files it is useful to use files to group together related
procedures; keeping collections of procedures that do different things in
different files. Thus a Prolog program will consist of a number of files, each
file containing a number of related procedures.
When your programs start to grow to a fair size, it is also a good idea to have
one file which just contains commands to the interpreter to consult all the
other files which form your program. You will then be able to consult your
entire program by just consulting this single file. More about how to do this
later, though.
Access to EMAS Prolog
In order to use the interpreter it is necessary for you get access to all the
bits of the system by adding the Prolog library to your search list with the
following EMAS command:
Command: OPTION SEARCHDIR=CONLIB.PROLOG
I have assumed the EMAS "no brackets" convention here; see the EMAS
documentation for details. This will only need to be done once, as EMAS will
remember it, If you are a student then this initial setting up operation will
probably have already been done for you, and you will not have to worry about
it (try the following and see).
Since Prolog makes syntactic use of the difference between upper and lower case
it is important that you have your terminal set up so that it accepts lower
case in the normal way. This means, for a start, that you must be using an
upper and lower case terminal - and not, for example, an upper case only
teletype. It is possible to use Prolog using upper case only (see section
1.15) but it is unnecessarily painful. We shall assume both upper and lower
case throughout this manual. Since EMAS does not always consider the use of
lower case to be the normal state of affairs, you may have to tell EMAS that
that is what you want before using Prolog. This can be done with the EMAS
command:
Command: SETMODE LOWER
More usefully, you could havethis action occur automatically whenever you use
EMAS. See the EMAS documentation, or ask someone who knows, for more details.
If you are a student you may find that all this is being done for you, so that
you don't have to bother about it.
To run Prolog type the EMAS command:
Command: prolog
Prolog will then output a banner and prompt you for directives as follows:
Emas Prolog version n
| ?-
There will be a pause between the first line and the prompt while the system
loads itself. It is possible to type ahead during this period if you get
impatient.
When you run Prolog you can tell it to set your terminal properly (in case you
havn't done that yet), and you can also tell it to start by restoring a
save-state that you made earlier. Save-states will be explained fully later.
Command: PROLOG /L (Set lower case)
Command: PROLOG /V (Set video and lower case)
Command: prolog prog (Restore "prog")
Command: PROLOG PROG/L (Restore "prog", set lower case)
Reading-in Programs
A program is made up of a sequence of clauses, possibly interspersed with
directives to the interpreter. The clauses of a procedure do not have to be
immediately consecutive, but remember that their relative order may be
important.
To input a program from a file file, give the directive:
| ?- [file].
which will instruct the interpreter to read-in (or consult) the program. The
file specification file must be a Prolog atom. It may be any EMAS file name,
note that if this file name contains characters which are not normally allowed
in an atom then it is necessary to surround the whole file specification with
single quotes (since quoted atoms can include any character); e.g.
| ?- ['ecmi25.greeks'].
The specified file is then read in. Clauses in the file are stored in the
database ready to be executed, while any directives are obeyed as they are
encountered. When the end of the file is found, the interpreter displays on
the terminal the time spent for read-in and the number of bytes occupied by the
program.
In general, this directive can be any list of filenames, such as:
| ?- [myprogram,extras,testbits].
In this case all three files would be consulted. If a filename is preceded by a
minus sign, as in:
| ?- [-testbits,-moreideas].
then that file is reconsulted. The difference between consulting and
reconsulting is important, and works as follows: if a file is consulted then
all the clauses in the file are simply added to Prolog's database. If you
consult the same file twice then you will get two copies of all the clauses.
However, if a file is reconsulted then the clauses for all the procedures in
the file will replace any existing clauses for those procedures, i.e any such
previously existing clauses in the database get thrown away. reconsult is
useful for telling Prolog about corrections in your program (see the 'redo'
predicate in section 1.5).
Clauses may also be typed in directly at the terminal. To enter clauses at the
terminal, you must give the directive:
| ?- [user].
The interpreter is now in a state where it expects input of clauses or
directives. To return to interpreter top level, type ^Y (Control Y). This is
equivalent to an end of file for the ersatz file 'user'.
However, this is only recommended if the clauses will not be needed
permanently, and are few in number. For significant bits of program you should
use an editor to produce an Emas file containing the text of the program. See
the next section for some useful Prolog predicates that help you to do this.
Using files, and calling EMAS commands from within Prolog
More usefully the following directives allow the editing of EMAS files, and
other operations, to be performed from inside Prolog:
| ?- edit File.
You are placed inside ECCE, editing the file File, and when ECCE is exited
(using "%c") you are returned to Prolog.
| ?- redo File.
This is like edit except that after returning from ECCE the file is
reconsulted, which means that definitions of procedures in the file replace any
existing definitions. This predicate allows you to correct parts of your
program incrementally.
| ?- edit.
| ?- redo.
Without arguments, these predicates perform their operations on the last file
you edited with an edit File or redo File.
The following predicates allow you to find out what files you have, and to type
them out onto the terminal:
| ?- files.
This shows you the names of all your files using the Emas FILES command.
| ?- dir.
This is currently a version of FILES, but it is eventually intended to provide
more information about each file.
| ?- ty File.
The file File is typed out onto the terminal. This is probably quicker than
editing it with ECCE if you just want a brief look at its contents.
In general, it is possible to call any Emas command using the following
evaluable predicates:
emas(Command)
emas(Command,Arguments)
Both Command and Arguments must be Prolog atoms, examples of how to use these
would be:
emas(users)
emas(list,'foo,.lp15')
Note that Arguments is the whole argument string for the command and that if it
contains characters like "," then it will have to be quoted as in the example,
otherwise it would not be syntactically recognised as a Prolog atom. Since
multiple arguments to Emas commands must be separated by commas, you will have
to quote this atom in most cases.
All Prolog predicates described earlier in this section, such as edit, redo
etc., are evaluable predicates written in Prolog which use emas(_) and
emas(_,_). It is very easy for you to write similar conveniences for youself
using these facilities.
Directives: Questions and Commands
When Prolog is at top-level (signified by an initial prompt of "| ?- ", with
continuation lines prompted with "| " - ie indented out from the left
margin) it reads in terms and treats them as directives to the interpreter to
try and satisfy some goals. These directives are called questions. Remember
that Prolog terms must terminate with a full-stop ("."), and that therefore
Prolog will not execute anything for you until you have typed the full-stop
(and then <return>) at the end of the directive.
Suppose list membership has been defined by:
member(X,[X|_]).
member(X,[_|L]) :- member(X,L).
Note the use of anonymous variables written "_".
If the goal(s) specified in a question can be satisfied, and if there are no
variables as in this example:
| ?- member(b,[a,b,c]).
then the system answers
yes
and execution of the question terminates.
If variables are included in the question, then the final value of each
variable is displayed (except for anonymous variables). Thus the question
| ?- member(X,[a,b,c]).
would be answered by
X = a
redo?
At this point the interpreter is waiting for you to indicate whether or not
that solution is sufficient, or whether you want to backtrack to see if there
are any further solutions. Simply typing <return> terminates the question,
while typing "y" followed by <return> causes the system to backtrack looking
for alternative solutions. If no further solutions can be found it outputs
no
The outcome of some questions is shown below, where a number preceded by "_" is
a system-generated name for a variable.
| ?- member(X,[tom,dick,harry]).
X = tom
redo? y
X = dick
redo? y
X = harry
redo? y
no
| ?- member(X,[a,b,f(Y,c)]),member(X,[f(b,Z),d]).
Y = b,
X = f(b,c),
Z = c
redo ? % Just <return> typed here
yes
| ?- member(X,[f(_),g]).
X = f(_1728)
redo?
yes
| ?-
When Prolog reads terms from file (or from the terminal following a call to
[user]), it treats them all as program clauses. In order to get the interpreter
to execute directives from a file they must be preceded by '?-', for questions,
or ':-', for commands.
Commands are like questions except that they do not cause results or answers to
be printed out. They always start with the symbol ":-". At top level this is
simply written after the prompted "?-" which is then effectively overridden.
Any required output must be programmed explicitly; e.g. the command:
:- member(3,[1,2,3]), write(ok).
directs the system to check whether 3 belongs to the list [1,2,3], and to
output "ok" if so. Execution of a command terminates when all the goals in the
command have been successfully executed. Other alternative solutions are not
sought (one may imagine an implicit "cut" at the end of the command). If no
solution can be found, the system gives:
?
as a warning.
The principle use for commands (as opposed to questions) is to allow files to
contain directives which call various procedures, but for which you don't want
to have the answers printed out and the "redo?" question asked. In such cases
you only want to call the procedures for effect, ie you don't want terminal
interaction in the middle of consulting the file. A useful example would be
the use of a directive in a file which consults a whole list of other files,
e.g.
:-([ bits, bobs, mainpart, testcases, data, junk ]).
(NB note that the extra parentheses, with the :- immediately next to them, are
currently essential due to a problem with prefix operators (like :-) and lists.
They are not required for commands that do not contain lists. This restriction
will eventually be removed.)
If this directive was contained in the file 'program' then typing the the
following at top-level would be a quick way of loading your entire program:
| ?- [program].
When simply interacting with the top-level of the Prolog interpreter this
distinction between questions and commands is not normally very important. At
top-level you should just type questions normally. In a file, if you wish to
execute some goals then you should use a command. I.e. To execute a directive
in a file it must be preceded by ":-", otherwise it will be treated as a
clause.
Syntax Errors
Syntax errors are detected during reading. Each clause, directive or in
general any term read-in by the built-in procedure read that fails to comply to
syntax requirements is displayed on the terminal as soon as it is read. A mark
indicates the point in the string of symbols where the parser has failed to
continue analysis. e.g.
member(X,X:L).
gives:
***SYNTAX ERROR***
member(X,X
***HERE***
: L).
if ':' has not been declared as an infix operator.
If the syntax error occured in a file being consulted then you should edit the
file and reconsult it (try using redo). Syntax errors do not disrupt the
consulting/reconsulting of a file in any way except that the term with the
syntax error will be ignored (it couldn't be read after all). All the other
clauses in the file will have been read-in properly. If the syntax error
occurs at top-level then you should just retype the question. Given that
Prolog has a very simple syntax it is usually quite straight forward to see
what the problems is (look for missing brackets in particular). See the
section 1.14 for details of the syntax for Prolog terms. You should also read
the "Programming in Prolog" book mentioned earlier if you are confused and want
further clarification and examples.
Saving A Program
Once a program has been read, the interpreter will have available all the
information necessary for its execution. This information is called a program
state.
The state of a program may be saved on disk for future execution. To save a
program into a file file, perform the command:
?- save(file).
Save can be called at top-level, from within a break-level, or from anywhere
within a program.
Restoring A Saved Program
Once a program has been saved into a file file, the interpreter can be restored
to this saved state by invoking the Prolog system as follows when it is
initially run:
Command: prolog file
After execution of this Emas command, the interpreter will be in EXACTLY the
same state as existed immediately prior to the call to save. That is to say
execution will start at the goal immediately following the call to save, just
as if save had returned successfully. If you saved the state at top-level then
you will be back at top-level, but if you explicitly called save from within
your program then the execution of your program will continue.
Save states can only be restored when Prolog is initially run from Emas command
level. There is currently no way of restoring a saved state from inside Prolog.
Note that when a new version of the Prolog system is installed, all program
files saved with the old version become obsolete. You are thus advised to rely
on source files for your programs and not on some gigantic save state.
Program Execution And Interruption
Execution of a program is started by giving the interpreter a directive which
contains a call to one of the program's procedures.
Only when execution of one directive is complete does the interpreter become
ready for another directive. However, one may interrupt the normal execution
of a directive by hitting the interrupt key on your terminal (normally marked
ESC). In response to the prompt
Int:
you can type either "a" or "d" (or "A" or "D"). The "a" response will force
Prolog to abort back to top level, whereas "d" will switch on debugging and
continue the execution. If you are having trouble and not getting any reply
from Prolog (perhaps your program is in a loop), then typing ESC followed by
"a" (and then <return>) should get you back to Prolog top-level.
Nested executions - break and abort
The Prolog system provides a way to suspend the execution of your program and
to enter a new incarnation of the top-level where you can issue directives to
solve goals etc. This is achieved using the evaluable predicate:
break
The message:
[ Break (level 1) ]
will then be displayed. This signals the start of a break-level and except for
the effect of aborts (see below), it is as if the interpreter was at top-level.
If break is called within a break-level, then another recursive break-level is
started (and the message will say (level 2) etc). Break-levels may be
arbitrarily nested.
A ^Y (Control Y) character, signifying end-of-file from the terminal, will
close the break-level and resume the execution which was suspended, starting at
the procedure call where the suspension took place.
To abort the current execution, i.e. to force an immediate failure of the
directive currently being executed at the interpreter's top-level, call the
evaluable predicate abort, either from the program or by executing the
directive:
| ?- abort.
within a break. In this case no ^Y is needed to close the break, because ALL
break levels are discarded and the system returns right back to top-level. The
"Int:a" interrupt (described above) can also be used to force an abort.
Exiting From The Interpreter
To exit from the Prolog interpreter and return to Emas command level you should
give the directive:
| ?- halt.
This can be issued either at top-level, or within a break-level, or indeed -
halt could be called from within your program. The evaluable predicates:
quit
stop
are equivalent to halt.
If your program is still executing then you should interrupt it and abort
(typing ESC and then "a") to return to top-level so that you can call halt.
If you type ^Y to the top-level then this end-of-file from the terminal causes
the system to exit back to EMAS (ie call halt), in a similar way that ^Y at a
break-level causes the system to exit that break-level and return to the
previous one.
Debugging facilities
The debugging facilities in the current version of Emas Prolog are still under
development. Currently the predicates described in Section 2.11 are all
available but their behaviour is somewhat unsatisfactory. When the enhanced
facilities become available this section will be replaced by a supplement which
will provide a proper description of the debugging facilities.
Prolog Syntax
This section gives an overview of Prolog's syntax.
Terms
The data objects of the language are called terms. A term is either a
constant, a variable or a compound term.
The constants include integers such as
0 1 999 512
Integers prefixed with a minus sign are currently read as full terms (not
negative integers). I.e.
-77 -909 -1
are read as the terms:
-(77) -(909) -(1)
However these are valid integer expressions and will work as expected with the
arithmetic predicates, such as is or < etc.
Constants also include atoms such as
a void = := 'Algol-68' []
The symbol for an atom can be any sequence of characters, written in single
quotes if there is possibility of confusion with other symbols (such as
variables, integers). As in conventional programming languages, constants are
definite elementary objects, and correspond to proper nouns in natural
language.
Variables are distinguished by an initial capital letter or by the initial
character "_", e.g.
X Value A A1 _3 _RESULT
If a variable is only referred to once, it does not need to be named and may be
written as an "anonymous" variable, indicated by the underline character "_".
A variable should be thought of as standing for some definite but unidentified
object. This is analogous to the use of a pronoun in natural language. Note
that a variable is not simply a writeable storage location as in most
programming languages; rather it is a local name for some data object, cf. the
variable of pure LISP and identity declarations in Algol68.
The structured data objects of the language are the compound terms. A compound
term comprises a functor (called the principal functor of the term) and a
sequence of one or more terms called arguments. A functor is characterised by
its name, which is an atom, and its arity or number of arguments. For example
the compound term whose functor is named 'point' of arity 3, with arguments X,
Y and Z, is written
point(X,Y,Z)
Note that an atom is considered to be a functor of arity 0.
Functors are generally analogous to common nouns in natural language. One may
think of a functor as a record type and the arguments of a compound term as the
fields of a record. Compound terms are usefully pictured as trees. For
example, the term
s(np(john),vp(v(likes),np(mary)))
would be pictured as the structure
s
/ \
np vp
| / \
john v np
| |
likes mary
Sometimes it is convenient to write certain functors as operators - 2-ary
functors may be declared as infix operators and 1-ary functors as prefix or
postfix operators. Thus it is possible to write, e.g.
X+Y (P;Q) X<Y +X P;
as optional alternatives to
+(X,Y) ;(P,Q) <(X,Y) +(X) ;(P)
The use of operators is described fully in Section 1.14.2 below.
Lists form an important class of data structures in Prolog. They are
essentially the same as the lists of LISP: a list either is the atom
[]
representing the empty list, or is a compound term with functor '.' and two
arguments which are respectively the head and tail of the list. Thus a list of
the first three natural numbers is the structure
.
/ \
1 .
/ \
2 .
/ \
3 []
which could be written, using the standard syntax, as
.(1,.(2,.(3,[])))
but which is normally written, in a special list notation, as
[1,2,3]
The special list notation in the case when the tail of a list is a variable is
exemplified by
[X|L] [a,b|L]
representing
. .
/ \ / \
X L a .
/ \
b L
respectively.
Note that this list syntax is only syntactic sugar for terms of the form
'.'(_,_) and does not provide any additional facilities that were not available
in Prolog.
For convenience, a further notational variant is allowed for lists of integers
which correspond to ASCII character codes. Lists written in this notation are
called strings. E.g.
"Prolog"
which represents exactly the same list as
[80,114,111,108,111,103]
Operators
Operators in Prolog are simply a notational convenience. For example, the
expression
2 + 1
could also be written +(2,1). It should be noticed that this expression
represents the data structure
+
/ \
2 1
and not the number 3. The addition would only be performed if the structure
was passed as an argument to an appropriate procedure (such as is - see 2.3).
The Prolog syntax caters for operators of three main kinds - infix, prefix and
postfix. An infix operator appears between its two arguments, while a prefix
operator precedes its single argument and a postfix operator is written after
its single argument.
Each operator has a precedence, which is a number from 1 to 1200. The
precedence is used to disambiguate expressions where the structure of the term
denoted is not made explicit through the use of brackets. The general rule is
that it is the operator with the HIGHEST precedence that is the principal
functor. Thus if '+' has a higher precedence than '/', then
a+b/c a+(b/c)
are equivalent and denote the term "+(a,/(b,c))". Note that the infix form of
the term "/(+(a,b),c)" must be written with explicit brackets, i.e.
(a+b)/c
If there are two operators in the subexpression having the same highest
precedence, the ambiguity must be resolved from the types of the operators.
The possible types for an infix operator are
xfx xfy yfx
With an operator of type 'xfx', it is a requirement that both of the two
subexpressions which are the arguments of the operator must be of LOWER
precedence than the operator itself, i.e. their principal functors must be of
lower precedence, unless the subexpression is explicitly bracketed (which gives
it zero precedence). With an operator of type 'xfy', only the first or
left-hand subexpression must be of lower precedence; the second can be of the
SAME precedence as the main operator; and vice versa for an operator of type
'yfx'.
For example, if the operators '+' and '-' both have type 'yfx' and are of the
same precedence, then the expression
a-b+c
is valid, and means
(a-b)+c i.e. +(-(a,b),c)
Note that the expression would be invalid if the operators had type 'xfx', and
would mean
a-(b+c) i.e. -(a,+(b,c))
if the types were both 'xfy'.
The possible types for a prefix operator are
fx fy
and for a postfix operator they are
xf yf
The meaning of the types should be clear by analogy with those for infix
operators. As an example, if 'not' were declared as a prefix operator of type
'fy', then
not not P
would be a permissible way to write "not(not(P))". If the type were 'fx', the
preceding expression would not be legal, although
not P
would still be a permissible form for "not(P)".
In Emas Prolog, a functor named name is declared as an operator of type type
and precedence precedence by calling the evaluable predicate op:
| ?- op(precedence,type,name).
The argument name can also be a list of names of operators of the same type and
precedence.
It is possible to have more than one operator of the same name, so long as they
are of different kinds, i.e. infix, prefix or postfix. An operator of any
kind may be redefined by a new declaration of the same kind. This applies
equally to operators which are provided as standard in Emas Prolog, namely:
:- op( 1200, xfx, [ :-, --> ]).
:- op( 1200, fx, [ :-, ?- ]).
:- op( 1100, xfy, [ ; ]).
:- op( 1050, xfy, [ -> ]).
:- op( 1000, xfy, [ ',' ]). /* See note below */
:- op( 900, fy, [ not, \+, spy, nospy ]).
:- op( 700, xfx, [ =, is, =.., ==, \==, @<, @>, @=<, @>=,
=:=, =\=, <, >, =<, >= ]).
:- op( 500, yfx, [ +, -, /\, \/ ]).
:- op( 500, fx, [ +, - ]).
:- op( 400, yfx, [ *, /, <<, >> ]).
:- op( 300, xfx, [ mod ]).
:- op( 200, xfy, [ ^ ]).
Operator declarations are most usefuly placed in directives at the top of your
Program files. In this case the directive should be a command as shown above.
Another common method of organisation is to have one file just containing
commands to declare all the necessary operators. This file is then always
consulted first.
Note that a comma written literally as a punctuation character can be used as
though it were an infix operator of precedence 1000 and type 'xfy', i.e.
X,Y ','(X,Y)
represent the same compound term. But note that a comma written as a quoted
atom is NOT a standard operator.
Note also that the arguments of a compound term written in standard syntax must
be expressions of precedence BELOW 1000. Thus it is necessary to bracket the
expression "P:-Q" in
assert((P:-Q))
Note carefully the following syntax restrictions, which serve to remove
potential ambiguity associated with prefix operators.
1. In a term written in standard syntax, the principal functor and its
following "(" must NOT be separated by any intervening spaces,
newlines etc. Thus
point (X,Y,Z)
is invalid syntax.
2. If the argument of a prefix operator starts with a "(", this "("
must be separated from the operator by at least one space or other
non-printable character. Thus
:-(p;q),r.
(where ':-' is the prefix operator) is invalid syntax, and must be
written as e.g.
:- (p;q),r.
3. If a prefix operator is written without an argument, as an ordinary
atom, the atom is treated as an expression of the same precedence as
the prefix operator, and must therefore be bracketed where
necessary. Thus the brackets are necessary in
X = (?-)
Using a Terminal without Lower-Case
The standard syntax of Prolog assumes that a full ASCII character set is
available. With this "full character set" or 'LC' convention, variables are
(normally) distinguished by an initial capital letter, while atoms and other
functors must start with a lower-case letter (unless enclosed in single
quotes).
When lower-case is not available, the "no lower-case" or 'NOLC' convention has
to be adopted. With this convention, variables must be distinguished by an
initial underline character "_", and the names of atoms and other functors,
which now have to be written in upper-case, are implicitly translated into
lower-case (unless enclosed in single quotes). For example:
_VALUE2
is a variable, while
VALUE2
is 'NOLC' convention notation for the atom which is identical to:
value2
written in the 'LC' convention.
The default convention is 'LC'. To switch to the "no lower-case" convention,
call the built-in procedure 'NOLC', e.g. by the directive:
| ?- 'NOLC'.
To switch back to the "full character set" convention, call the built-in
procedure 'LC', e.g. by:
| ?- 'LC'.
Note that the names of these two procedures consist of upper-case letters (so
that they can be referred to on all devices), and therefore the names must
ALWAYS be enclosed in single quotes.
It is recommended that the 'NOLC' convention only be used in emergencies, since
the standard syntax is far easier to use and is also easier for other people to
read.
Built-in Procedures
2.1 Input / Output
2.1.1 Reading in Programs
2.1.2 File Handling
2.1.3 Input and Output of Terms
2.1.4 Character Input/Output
2.2 Editing files and calling Emas Commands
2.3 Arithmetic
2.4 Convenience
2.5 Extra Control
2.6 Meta-Logical
2.7 Modification of the Program
2.8 Information about the State of the Program
2.9 Collecting together solutions
2.10 Internal Database
2.11 Debugging
2.12 Environmental
2.13 Pre-Processing
Built-in procedures are also referred to as evaluable predicates.
This section describes all the built-in predicates available in EMAS Prolog.
These predicates are provided in advance by the system and they cannot be
redefined by the user. If you try to add clauses for a built-in predicate then
this will cause an error, and the built-in predicates will be unaffected. The
EMAS Prolog system provides a fairly wide range of built-in predicates to
perform the following tasks:
Input/Output
Reading-in programs
Opening and closing files
Reading and writing Prolog terms
Getting and putting characters
Editing files and calling Emas commands
Arithmetic
Affecting the flow of the execution
Classifying and operating on Prolog terms (meta-logical facilities)
Manipulating the Prolog program database
Manipulating the additional indexed database
Debugging facilities
Environmental facilities
The following descriptions of the built-in predicates will follow the above
categorisation of their tasks. In Appendix II there is a complete list of the
built-in predicates.
Input / Output
2.1.1 Reading in Programs
2.1.2 File Handling
2.1.3 Input and Output of Terms
2.1.4 Character Input/Output
A total of ten I/O streams may be open at any one time for input and output.
This includes a stream that is always available for input and output to the
user's terminal. A stream to a file F is opened for input by the first see(F)
executed. F then becomes the current input stream. Similarly, a stream to
file H is opened for output by the first tell(H) executed. H then becomes the
current output stream. Subsequent calls to see(F) or to tell(H) make F or H
the current input or output stream, respectively. Any input or output is
always to the current stream.
When no input or output stream has been specified, the standard ersatz file
'user', denoting the user's terminal, is utilised for both. When the terminal
is waiting for input on a new line, a prompt will be displayed as follows:
"| " - top-level continuation line.
"| " - during consult(user).
"|: " - default for read from user program.
When the current input (or output) stream is closed, the user's terminal
becomes the current input (or output) stream.
No file except the ersatz file 'user' can be simultaneously open for input and
output.
A file is referred to by its name, written as an atom, e.g.
myfile
'F123'
'DATA#LST'
'ecmi25.greeks'
All I/O errors normally cause an abort, except for the effect of the evaluable
predicate nofileerrors decribed below.
End of file is signalled on the user's terminal by issuing a ^Y (Control and Y)
character. Any more input requests for a file whose end has been reached
causes an error failure.
Reading in Programs
consult(F) Instructs the interpreter to read-in the program which is in file
F. When a directive is read it is immediately executed. When a
clause is read it is put after any clauses already read by the
interpreter for that procedure.
reconsult(F)
Like consult except that any procedure defined in the "reconsulted"
file erases any clauses for that procedure already present in the
interpreter. reconsult makes it possible to amend a program
without having to restart from scratch and consult all the files
which make up the program.
[File|Files]
This is a shorthand way of consulting or reconsulting a list of
files. A file name may optionally be preceded by the operator '-'
to indicate that the file should be "reconsulted" rather than
"consulted". Thus
| ?- [file1,-file2,file3].
is merely a shorthand for
| ?- consult(file1), reconsult(file2), consult(file3).
File Handling
see(F) File F becomes the current input stream.
seeing(F) F is unified with the name of the current input file.
seen Closes current input stream.
tell(F) File F becomes the current output stream.
telling(F) F is unified with the name of the current output file.
told Closes the current output stream.
close(F) File F, currently open for input or output, is closed.
fileerrors Undoes the effect of nofileerrors.
nofileerrors
After a call to this predicate, the I/O error conditions "incorrect
file name ...", "can't see file ...", "can't tell file ..." and
"end of file ..." cause a call to fail instead of the default
action, which is to type an error message and then call abort.
exists(F) Succeeds if the file F exists.
rename(F,N) If file F is currently open, it is closed and renamed to N. If N
is '[]', the file is deleted.
Input and Output of Terms
read(X) The next term, delimited by a full stop (i.e. a "." followed by a
carriage-return or a space), is read from the current input stream
and unified with X. The syntax of the term must accord with current
operator declarations. If a call read(X) causes the end of the
current input stream to be reached, X is unified with the term
'end_of_file'. Further calls to read for the same stream will then
cause an error failure.
write(X) The term X is written to the current output stream according to
current operator declarations.
display(X) The term X is displayed on the terminal in standard parenthesised
prefix notation.
writeq(Term)
Similar to write(Term), but the names of atoms and functors are
quoted where necessary to make the result acceptable as input to
read.
print(Term) Print Term onto the current output. This predicate provides a
handle for user defined pretty printing. If Term is a variable
then it is written, using write(Term). If Term is non-variable
then a call is made to the user defined procedure portray(Term).
If this succeeds then it is assumed that Term has been output.
Otherwise print is equivalent to write.
Character Input/Output
nl A new line is started on the current output stream.
get0(N) N is the ASCII code of the next character from the current input
stream.
get(N) N is the ASCII code of the next non-blank printable character from
the current input stream.
skip(N) Skips to just past the next ASCII character code N from the current
input stream. N may be an integer expression.
put(N) ASCII character code N is output to the current output stream. N
may be an integer expression.
tab(N) N spaces are output to the current output stream. N may be an
integer expression.
Editing files and calling Emas Commands
The following conveniences are provided to assist program development. They
allow files to be created, consulted, corrected and reconsulted without leaving
the Prolog system
edit(File) Edit the file File using the ECCE editor. When ECCE is exited
(using "%c") control returns to Prolog.
edit Edit the last file edited.
redo(File) Edit the file File and then reconsult File when the editor returns
to Prolog.
redo Redo the last file edited (with edit or redo).
ty(File) Type out the file File on the terminal.
ty Type out the last file edited (with edit or redo) on the terminal.
files List all your files, using the Emas FILES command.
dir List all your files using the Emas DIR command, available in
CONLIB.PROLOG . This is currently just a version of FILES but
will eventually provide more information about each file listed.
gripe Prompt for text which will be mailed to the maintainers of the Emas
prolog system. Use this to complain about things, ask questions
etc.
emas(C) Call the Emas command C. C should be an atom.
emas(C,A) Call the Emas command C with the argument string A. Both C and A
should be atoms.
Arithmetic
Arithmetic is performed by built-in procedures which take as arguments integer
expressions and evaluate them. An integer expression is a term built from
evaluable functors, integers and variables. At the time of evaluation, each
variable in an integer expression must be bound to an integer or to an integer
expression.
Each evaluable functor stands for an arithmetic operation. The evaluable
functors are as follows, where X and Y are integer expressions.
X+Y integer addition
X-Y integer subtraction
X*Y integer multiplication
X/Y integer division
X mod Y X modulo Y
-X unary minus
X/\Y bitwise conjunction
X\/Y bitwise disjunction
X<<Y bitwise left shift of X by Y places
X>>Y bitwise right shift of X by Y places
[X] (a list of just one element) evaluates to X if X is an
integer. Since a quoted string is just a list of integers,
this allows a quoted character to be used in place of its
ASCII code; e.g. "A" behaves within arithmetic expressions
as the integer 65.
The arithmetic built-in procedures are as follows, where X and Y stand for
arithmetic expressions, and Z for some term. Note that this means that is only
evaluates one of its arguments as an integer expression (i.e. X), whereas all
the comparison predicates evaluate both their arguments.
Z is X Integer expression X is evaluated and the result, is unified with
Z. Fails if X is not an integer expression.
X =:= Y The values of X and Y are equal.
X =\= Y The values of X and Y are not equal.
X < Y The value of X is less than the value of Y.
X > Y The value of X is greater than the value of Y.
X =< Y The value of X is less than or equal to the value of Y.
X >= Y The value of X is greater than or equal to the value of Y.
Convenience
P , Q P and Q.
P ; Q P or Q.
true Always succeeds.
X = Y Defined as if by the clause " Z=Z. "; i.e. X and Y are unified.
Extra Control
! Cut (discard) all choice points made since the parent goal started
execution.
not P If the goal P has a solution, fail, otherwise succeed. It is
defined as if by
not(P) :- P, !, fail.
not(_).
\+ P Identical to not (this is provided for DEC10 Prolog compatibility).
P -> Q ; R Analogous to
"if P then Q else R"
i.e. defined as if by
P -> Q; R :- P, !, Q.
P-> Q; R :- R.
P -> Q When occurring other than as one of the alternatives of a
disjunction, is equivalent to
P -> Q; fail.
repeat Generates an infinite sequence of backtracking choices. It behaves
as if defined by the clauses:
repeat.
repeat :- repeat.
fail Always fails.
Meta-Logical
var(X) Tests whether X is currently instantiated to a variable.
nonvar(X) Tests whether X is currently instantiated to a non-variable term.
atom(X) Checks that X is currently instantiated to an atom (i.e. a
non-variable term of arity 0, other than an integer).
integer(X) Checks that X is currently instantiated to an integer.
atomic(X) Checks that X is currently instantiated to an atom or integer.
X == Y Tests if the terms currently instantiating X and Y are literally
identical (in particular, variables in equivalent positions in the
two terms must be identical).
X \== Y Tests if the terms currently instantiating X and Y are not
literally identical.
functor(T,F,N)
The principal functor of term T has name F and arity N, where F is
either an atom or, provided N is 0, an integer. Initially, either
T must be instantiated to a non-variable, or F and N must be
instantiated to, respectively, either an atom and a non-negative
integer or an integer and 0. If these conditions are not satisfied,
an error message is given. In the case where T is initially
instantiated to a variable, the result of the call is to
instantiate T to the most general term having the principal functor
indicated.
arg(I,T,X) Initially, I must be instantiated to a positive integer and T to a
compound term. The result of the call is to unify X with the Ith
argument of term T. (The arguments are numbered from 1 upwards.)
If the initial conditions are not satisfied or I is out of range,
the call merely fails.
X =.. Y Y is a list whose head is the atom corresponding to the principal
functor of X and whose tail is the argument list of that functor in
X. E.g.
product(0,N,N-1) =.. [product,0,N,N-1]
N-1 =.. [-,N,1]
product =.. [product]
If X is instantiated to a variable, then Y must be instantiated
either to a list of determinate length whose head is an atom, or to
a list of length 1 whose head is an integer.
name(X,L) If X is an atom or integer then L is a list of the ASCII codes of
the characters comprising the name of X. E.g.
name(product,[112,114,111,100,117,99,116])
i.e. name(product,"product")
name(1976,[49,57,55,54])
name(hello,[104,101,108,108,111])
name([],"[]")
If X is instantiated to a variable, L must be instantiated to a
list of ASCII character codes. E.g.
| ?- name(X,[104,101,108,108,111])).
X = hello
| ?- name(X,"hello").
X = hello
call(X) If X is instantiated to a term which would be acceptable as body of
a clause, the goal call(X) is executed exactly as if that term
appeared textually in place of the call(X). In particular, any cut
("!") occurring in X is interpreted as if it occurred in the body
of the clause containing call(X), unless that clause is a compiled
clause, in which case only the alternatives in the execution of X
are cut. If X is not instantiated as described above, an error
message is printed and call fails.
X (where X is a variable) Exactly the same as call(X).
Modification of the Program
The predicates defined in this section allow modification of the program as it
is actually running. Clauses can be added to the program ("asserted") or
removed from the program ("retracted"). Some of the predicates make use of an
implementation-defined identifier which uniquely identifies every clause in the
interpreted program. This identifier makes it possible to access clauses
directly, instead of requiring a search through the program every time.
However such faciities are intended for more complex use of the database and
are not required (and undoubtably should be avoided) by novice users.
assert(C) The current instance of C is interpreted as a clause and is added
to the current interpreted program (with new private variables
replacing any uninstantiated variables). The position of the new
clause within the procedure concerned is implementation-defined. C
must be instantiated to a non-variable.
assert(Clause,Ref)
Equivalent to assert(_) where Ref is the implementation-defined
identifier of the clause asserted.
asserta(C) Like assert(_), except that the new clause becomes the first clause
for the procedure concerned.
asserta(Clause,Ref)
Equivalent to asserta(_) where Ref is the implementation-defined
identifier of the clause asserted.
assertz(C) Like assert(_), except that the new clause becomes the last clause
for the procedure concerned.
assertz(Clause,Ref)
Equivalent to assertz(_) where Ref is the implementation-defined
identifier of the clause asserted.
clause(P,Q) P must be bound to a non-variable term, and the current interpreted
program is searched for a clause whose head matches P. The head
and body of those clauses are unified with P and Q respectively.
If one of the clauses is a unit clause, Q will be unified with
'true'.
clause(Head,Body,R)
Equivalent to clause(_) where Ref is the implementation-defined
term which uniquely identifies the clause concerned. If Ref is not
given at the time of the call, Head must be instantiated to a
non-variable term. Thus this predicate can have two different modes
of use, depending on whether the identifier of the clause is known
or unknown.
retract(C) The first clause in the current interpreted program that matches C
is erased. C must be initially instantiated to a non-variable.
The predicate may be used in a non-determinate fashion, i.e. it
will successively retract clauses matching the argument through
backtracking.
abolish(N,A)
Completely remove all clauses for the procedure with name N (which
should be an atom), and arity A (which should be an integer).
The space occupied retracted or abolished clauses will be recovered
when instances of the clause are no longer in use.
See also erase (Section 2.10) which allows a clause to be directly
erased via its implementation-defined identifier (note however that
this is a lower level facility that is not recommended for novice
users).
Information about the State of the Program
listing Lists in the current output stream all the clauses in the current
interpreted program.
listing(A) If A is just an atom, then the interpreted procedures for all
predicates of that name are listed as for listing/0. The argument
A may also be a predicate specification of the form Name/Arity in
which case only the clauses for the specified predicate are listed.
Finally, it is possible for A to be a list of predicate
specifications of either type, e.g.
| ?- listing([concatenate/3, reverse, go/0]).
current_atom(Atom)
Generates (through backtracking) all currently known atoms, and
returns each one as Atom.
current_functor(Name,Functor)
Generates (through backtracking) all currently known functors, and
for each one returns its name and most general term as Name and
Functor respectively. If Name is given, only functors with that
name are generated.
current_predicate(Name,Functor)
Similar to current_functor, but it only generates functors
corresponding to predicates for which there currently exists an
interpreted procedure.
Collecting together solutions
When there are many solutions to a problem, and when all those solutions are
required to be collected together, this can be achieved by repeatedly
backtracking and gradually building up a list of the solutions. The following
evaluable predicate is provided to automate this process. Note, however, that
this is the simple version of this predicate and the implementation does not
match the sophistication of the equivalent in DEC10 Prolog (ie the logical
semantics are incorrect) - if this does not mean much to you then don't worry
about it.
bagof(X,P,Bag)
Bag is a list of all X's such that P. Ie all the instantiations of
X produced by backtracking through all possible solutions of P are
gathered into the list Bag. Since this list may contain duplicate
elements it is known, technically, as a bag; as opposed to a set,
say, which would not allow duplicate elements.
Internal Database
This section describes predicates for manipulating an internal indexed database
that is kept separate from the normal program database. They are intended for
more sophisticated database applications and are not really necessary for
novice users. For normal tasks you should be able to program quite
satisfactorily just using assert and retract.
recorded(Key,Term,Ref)
The internal database is searched for terms recorded under the key
Key. These terms are successively unified with Term in the order
they occur in the database. At the same time, Ref is unified with
an implementation-defined identifier uniquely identifying the
recorded item. The key must be given, and may be an atom, integer
or complex term. If it is a complex term, only the principal
functor is significant.
recorda(Key,Term,Ref)
The term Term is recorded in the internal database as the first
item for the key Key, where Ref is its implementation-defined
identifier. The key must be given, and only its principal functor
is significant.
recordz(Key,Term,Ref)
The term Term is recorded in the internal database as the last item
for the key Key, where Ref is its implementation-defined
identifier. The key must be given, and only its principal functor
is significant.
erase(Ref) The recorded item or interpreted clause whose
implementation-defined identifier is Ref is effectively erased from
the internal database or interpreted program.
instance(Ref,Term)
A (most general) instance of the recorded term whose
implementation-defined identifier is Ref is unified with Term. Ref
must be instantiated to a legal identifier.
Debugging
The current debugging package is only preliminary and is currently being
enhanced. The appearance of the debugging aids is thus likely to change;
however, the predicates described here will not change - rather they will
gradually be made more effective.
debug Debug mode is switched on. Information will now be retained for
debugging purposes and executions will require more space.
nodebug Debug mode is switched off. Information is no longer retained for
debugging.
trace Debug mode is switched on, and an immediate CREEP decision is made,
so that tracing will start with the very next port through which
control passes. Since this is a once-off decision, a call to trace
is necessary whenever tracing is required right from the start of
an execution. (The assumed decision is otherwise LEAP).
spy Spec Spy-points will be placed on all the procedures given by Spec. All
control flow through the ports of these procedures will henceforth
be traced. If debug mode was previously off, then it will be
switched on. Spec can either be a predicate specification of the
form Name/Arity or Name, or a list of such specifications. When
the Name is given without the Arity this refers to all predicates
of that name which currently have definitions. If there are none,
then nothing will be done. Spy-points can be placed on particular
undefined procedures only by using the full form, Name/Arity.
nospy Spec Spy-points are removed from all the procedures given by Spec (as
for spy).
debugging Outputs information concerning the status of the debugging package.
This will show whether debug mode is on, and if it is -
1. what spy-points have been set
2. what mode of leashing is in force.
Environmental
'NOLC' Establishes the "no lower-case" convention described in Section
1.15.
'LC' Establishes the "full character set" convention described in
Section 1.15. It is the default setting.
op(priority,type,name)
Treat name name as an operator of the stated type and priority
(refer to Section 1.14.2). name may also be a list of names in
which case all are to be treated as operators of the stated type
and priority.
break Causes the current execution to be interrupted at the next
interpreted procedure call. Then the message "[ Break (level 1) ]"
is displayed. The interpreter is then ready to accept input as
though it was at top level. If another call of break is
encountered, it moves up to level 2, and so on. To close the break
and resume the execution which was suspended, type ^Y. Execution
will be resumed at the procedure call where it had been suspended.
Alternatively, the suspended execution can be aborted by calling
the evaluable predicate abort. Refer to Section 1.11.
abort Aborts the current execution taking you back to top-level. Refer
to Section 1.11.
save(F) The system saves the current state of the system into file F.
Refer to Section 1.8.
prompt(Old,New)
The sequence of characters (prompt) which indicates that the system
is waiting for user input is represented as an atom, and matched to
Old; the atom bound to New specifies the new prompt. In particular,
the goal
prompt(X,X)
matches the current prompt to X, without changing it. Note that
this only affects the prompt issued for read's in the user's
program; it will not change the propmts used by the system at
top-level etc.
Pre-Processing
expand_term(T1,T2)
When a program is read in, some of the terms read are transformed
before being stored as clauses. If T1 is a term that can be
transformed, T2 is the result. Otherwise T2 is just T1 unchanged.
The only transformation currently available translates grammar
rules into clauses. Note that this means that grammar rules are
automatically accepted, and read-in properly, by consult and
reconsult.
Programming Examples
Some simple examples of Prolog programming are given below. To clearly
differentiate the examples themselves, they are marked with a vertical bar in
the left margin.
Simple List Processing
The goal concatenate(L1,L2,L3) is true if list L3 consists of the elements of
list L1 concatenated with the elements of list L2. The goal member(X,L) is true
if X is one of the elements of list L. The goal reverse(L1,L2) is true if list
L2 consists of the elements of list L1 in reverse order.
| concatenate([X|L1],L2,[X|L3]) :- concatenate(L1,L2,L3).
| concatenate([],L,L).
|
| member(X,[X|L]).
| member(X,[_|L]) :- member(X,L).
|
| reverse(L,L1) :- reverse_concatenate(L,[],L1).
|
| reverse_concatenate([X|L1],L2,L3) :-
| reverse_concatenate(L1,[X|L2],L3).
| reverse_concatenate([],L,L).
A Small Database
The goal descendant(X,Y) is true if Y is a descendant of X.
| descendant(X,Y) :- offspring(X,Y).
| descendant(X,Z) :- offspring(X,Y), descendant(Y,Z).
|
| offspring(abraham,ishmael).
| offspring(abraham,isaac).
| offspring(isaac,esau).
| offspring(isaac,jacob).
If for example the question
?- descendant(abraham,X).
is executed, Prolog's backtracking results in different descendants of Abraham
being returned as successive instances of the variable X, i.e.
X = ishmael
X = isaac
X = esau
X = jacob
Quick-Sort
The goal qsort(L,[],R) is true if list R is a sorted version of list L. More
generally, qsort(L,R0,R) is true if list R consists of the members of list L
sorted into order, followed by the members of list R0. The algorithm used is a
variant of Hoare's "Quick Sort".
|
| qsort([X|L],R0,R) :-
| partition(L,X,L1,L2),
| qsort(L2,R0,R1),
| qsort(L1,[X|R1],R).
| qsort([],R,R).
|
| partition([X|L],Y,[X|L1],L2) :- X =< Y, !,
| partition(L,Y,L1,L2).
| partition([X|L],Y,L1,[X|L2]) :- X > Y, !,
| partition(L,Y,L1,L2).
| partition([],_,[],[]).
Differentiation
The goal d(E1,X,E2) is true if expression E2 is a possible form for the
derivative of expression E1 with respect to X.
| :-op(300,xfy,^).
|
| d(U+V,X,DU+DV) :-!, d(U,X,DU), d(V,X,DV).
| d(U-V,X,DU-DV) :-!, d(U,X,DU), d(V,X,DV).
| d(U*V,X,DU*V+U*DV) :-!, d(U,X,DU), d(V,X,DV).
| d(U^N,X,N*U^N1*DU) :-!, integer(N), N1 is N-1, d(U,X,DU).
| d(-U,X,-DU) :-!, d(U,X,DU).
| d(exp(U),X,exp(U)*DU) :-!, d(U,X,DU).
| d(log(U),X,DU/U) :-!, d(U,X,DU).
| d(X,X,1) :-!.
| d(C,X,0) :- atomic(C), C \== X, !.
Use Of Meta-Predicates
This example illustrates the use of the meta-predicates var and =... The
procedure call variables(Term,L,[]) instantiates variable L to a list of all
the variable occurrences in the term Term. e.g. variables(d(U*V,X,DU*V+U*DV),
[U,V,X,DU,V,U,DV], []).
| variables(X,[X|L],L) :- var(X),!.
| variables(T,L0,L) :- T =.. [F|A], variables1(A,L0,L).
|
| variables1([T|A],L0,L) :- variables(T,L0,L1), variables1(A,L1,L).
| variables1([],L,L).
Prolog In Prolog
This example shows how simple it is to write a Prolog interpreter in Prolog,
and illustrates the use of a variable goal. In this mini-interpreter, goals
and clauses are represented as ordinary Prolog data structures (i.e. terms).
Terms representing clauses are specified using the unary predicate my_clause,
e.g.
my_clause( (grandparent(X,Z):-parent(X,Y),parent(Y,Z)) ).
A unit clause will be represented by a term such as
my_clause( (parent(john,mary) :- true) ).
The mini-interpreter consists of four clauses:
| execute(true) :-!.
| execute((P,Q)) :- !, execute(P), execute(Q).
| execute(P) :- my_clause((P:-Q)), execute(Q).
| execute(P) :- P.
The last clause enables the mini-interpreter to cope with calls to ordinary
Prolog predicates, e.g. evaluable predicates.
Translating English Sentences Into Logic Formulae
The following example of a definite clause grammar defines in a formal way the
traditional mapping of simple English sentences into formulae of classical
logic. By way of illustration, if the sentence
Every man that lives loves a woman.
is parsed by satisfying the goal
| ?- sentence(P,[every,man,that,loves,a,woman],[]).
then P will get instantiated to
all(X):(man(X)&lives(X) => exists(Y):(woman(Y)&loves(X,Y)))
where ':', '&' and '=' are infix operators defined by
:-op(900,xfx,=>).
:-op(800,xfy,&).
:-op(300,xfx,:).
The grammar follows:
| sentence(P) --> noun_phrase(X,P1,P), verb_phrase(X,P1).
|
| noun_phrase(X,P1,P) -->
| determiner(X,P2,P1,P), noun(X,P3), rel_clause(X,P3,P2).
| noun_phrase(X,P,P) --> name(X).
|
| verb_phrase(X,P) --> trans_verb(X,Y,P1), noun_phrase(Y,P1,P).
| verb_phrase(X,P) --> intrans_verb(X,P).
|
| rel_clause(X,P1,P1&P2) --> [that], verb_phrase(X,P2).
| rel_clause(_,P,P) --> [].
|
| determiner(X,P1,P2, all(X):(P1=>P2) ) --> [every].
| determiner(X,P1,P2, exists(X):(P1&P2) ) --> [a].
|
| noun(X, man(X) ) --> [man].
| noun(X, woman(X) ) --> [woman].
|
| name(john) --> [john].
|
| trans_verb(X,Y, loves(X,Y) ) --> [loves].
| intrans_verb(X, lives(X) ) --> [lives].
Summary of the Evaluable Predicates
abolish(F,N) Abolish the interpreted procedure named F arity N.
abort Abort execution of the current directive.
arg(N,T,A) The Nth argument of term T is A.
assert(C) Assert clause C.
assert(C,R) Assert clause C, reference R.
asserta(C) Assert C as first clause.
asserta(C,R) Assert C as first clause, reference R.
assertz(C) Assert C as last clause.
assertz(C,R) Assert C as last clause, reference R.
atom(T) Term T is an atom.
atomic(T) Term T is an atom or integer.
bagof(X,P,B) The bag of instances of X such that P is provable is B.
break Break at the next interpreted procedure call.
call(P) Execute the interpreted procedure call P.
clause(P,Q) There is an interpreted clause, head P, body Q.
clause(P,Q,R) There is an interpreted clause, head P, body Q, ref R.
close(F) Close file F.
consult(F) Extend the interpreted program with clauses from file F.
current_atom(A) One of the currently defined atoms is A.
current_functor(A,T) A current functor is named A, m.g. term T.
current_predicate(A,P) A current predicate is named A, m.g. goal P.
debug Switch on debugging.
debugging Output debugging status information.
dir List users files (file directory).
display(T) Display term T on the terminal.
edit(F) Edit the file F.
edit Edit the last file edited.
emas(C) Call the Emas command C.
emas(C,A) Call the Emas command C with argument A.
erase(R) Erase the clause or record, reference R.
expand_term(T,X) Term T is a shorthand which expands to term X.
exists(F) The file F exists.
fail Backtrack immediately.
fileerrors Enable reporting of file errors.
files List the users files.
functor(T,F,N) The principal functor of term T has name F, arity N.
get(C) The next non-blank character input is C.
get0(C) The next character input is C.
gripe Mail a complaint/query about the Prolog system.
halt Halt Prolog, exit to the monitor.
instance(R,T) A m.g. instance of the record reference R is T.
integer(T) Term T is an integer.
Y is X Y is the value of integer expression X.
leash(M) Set leashing mode to M.
listing List the current interpreted program.
listing(P) List the interpreted procedure(s) specified by P.
name(A,L) The name of atom or integer A is string L.
nl Output a new line.
nodebug Switch off debugging.
nofileerrors Disable reporting of file errors.
nonvar(T) Term T is a non-variable.
nospy P Remove spy-points from the procedure(s) specified by P.
not P Goal P is not provable.
op(P,T,A) Make atom A an operator of type T precedence P.
print(T) Portray or else write the term T.
prompt(A,B) Change the prompt from A to B.
put(C) The next character output is C.
read(T) Read term T.
reconsult(F) Update the interpreted program with procedures from file F.
recorda(K,T,R) Make term T the first record under key K, reference R.
recorded(K,T,R) Term T is recorded under key K, reference R.
recordz(K,T,R) Make term T the last record under key K, reference R.
redo(F) Edit the file F and then reconsult it.
redo Redo the last file edited.
rename(F,G) Rename file F to G.
repeat Succeed repeatedly.
retract(C) Erase the first interpreted clause of form C.
save(F) Save the current state of Prolog in file F.
see(F) Make file F the current input stream.
seeing(F) The current input stream is named F.
seen Close the current input stream.
skip(C) Skip input characters until after character C.
spy P Set spy-points on the procedure(s) specified by P.
tab(N) Output N spaces.
tell(F) Make file F the current output stream.
telling(F) The current output stream is named F.
told Close the current output stream.
trace Switch on debugging and start tracing immediately.
true Succeed.
ty(F) Type the file F on the terminal.
ty type the last file edited.
var(T) Term T is a variable.
write(T) Write the term T.
writeq(T) Write the term T, quoting names where necessary.
'LC' The following Prolog text uses lower case.
'NOLC' The following Prolog text uses upper case only.
! Cut any choices taken in the current procedure.
\+ P Goal P is not provable.
X<Y As integer values, X is less than Y.
X=<Y As integer values, X is less than or equal to Y.
X>Y As integer values, X is greater than Y.
X>=Y As integer values, X is greater than or equal to Y.
X=Y Terms X and Y are equal (i.e. unified).
T=..L The functor and arguments of term T comprise the list L.
X==Y Terms X and Y are strictly identical.
X\==Y Terms X and Y are not strictly identical.
[F|R] Perform the (re)consult(s) specified by [F|R].
Prolog Utilities
There are a number of files available which contain various useful Prolog
programs. Unfortunately there is not currently any adaquate documentation
for these. However many of them are very short - they include obvious things
like append(L1,L2,L3) for appending lists together. So, if you are interested,
you could look at the source code to see what they all do. There is one
piece of documentation for a formatted write utility, and also a file with
a list of all the procedures defined in the source files. The whole set of
utilities are already loaded if you run Prolog using the command:
Command: util
The files to look at are:
CONLIB.PROLOGUTIL PD file with all the source code
CONLIB.PROLOGUTIL_EMASUTIL Loads all the other files (used to build 'util').
CONLIB.PROLOGUHELP_UTIL Lists all the procedures, by file and alphabetically.
CONLIB.PROLOGUHELP_WRITEF Documentation for the 'writef' procedure.
Obtaining this manual
This manual is available in hard copy form as follows:
from: Margaret Pithie
Deptartment of Artificial Intelligence
Forrest Hill
Edinburgh
as: "User's Guide to Emas Prolog" Edited by Lawrence Byrd.
DAI Occasional Paper 26
(price 2 pounds)