| Copyright © Bill Wilson, 1998-2008 | Contact Info |
This version of the Prolog dictionary assumes the syntax of SWI Prolog. Some examples assume a Unix command interpreter, as in Linux and MacOS X/Terminal.
You should use The Prolog Dictionary to clarify or revise concepts that you have already met. The Prolog Dictionary is not a suitable way to begin to learn about Prolog. That said, this dictionary is designed to be used by beginner and intermediate Prolog programmers. Further information on Prolog can be found in the SWI Prolog documentation linked above.
Other related dictionaries:
The AI Dictionary
- URL: http://www.cse.unsw.edu.au/~billw/aidict.html
The Machine Learning Dictionary
- URL: http://www.cse.unsw.edu.au/~billw/mldict.html
The NLP Dictionary (Natural Language Processing)
- URL: http://www.cse.unsw.edu.au/~billw/nlpdict.html
Other places to find out about artificial intelligence include the AAAI (American Association for Artificial Intelligence) AI Overview page or AI Reference Shelf
The URL of this Prolog Dictionary is
http://www.cse.unsw.edu.au/~billw/prologdict.html
This dictionary was limited to the Prolog concepts covered in COMP9414 Artificial Intelligence at the University of New South Wales, Sydney. It is currently (2007-8) being expanded to also cover the Prolog concepts in COMP9814 Extended Artificial Intelligence. Either way, it does not currently cover certain concepts underlying Prolog (resolution, etc.) More details on Prolog can be found in the COMP9414 lecture notes and COMP9814 lecture notes . The basic Prolog concepts - that is, the ones covered/used in COMP9414 - are marked with green dots (•) in the index below, and also have headword of the article about them in green.
There are no entries (yet) for the letters J, K, X, Y, and Z.
append
append(?List1, ?List2, ?List1_then_List2)
succeeds if List1 followed by List2 =
List1_then_List2. Examples:
?- append([a, b], [c, d, e], Result). Result = [a, b, c, d, e]. true. ?- append([a, b], SecondBit, [a, b, c, d, e]). SecondBit = [c, d, e] true. ?- append(FirstBit, [c, d, e], [a, b, c, d, e]). FirstBit = [a, b] true.
A not uncommon beginner's mistake is to use
append([Head], Tail, List)
to build a list
instead of something like List = [Head | Tail].
This will work, but is like using a bulldozer to dig a
hole to plant out a seedling.
Note: there is also an unrelated version of append
that takes a single parameter. This is described under
files, and is referred to as
append/1 - i.e. 1 argument,
while the append in this article is referred
to as append/3 - 3 arguments.
arg
?- arg(2, buys(john, beer), Arg). Arg = beer
It is also possible to put arguments into terms using
arg, should this prove useful:
?- X = likes(mary, Y), arg(2, X, pizza). X = likes(mary, pizza) Y = pizza
arg,
term.
| Operator | Meaning | Example |
|---|---|---|
| + | addition | 2 is 1 + 1. |
| – | subtraction | 1 is 2 – 1. |
| – | unary minus | Try the query X is 1, Y is - X. |
| * | multiplication | 4 is 2 * 2. |
| / | division | 2 is 6 / 3. |
| // | integer division | 1 is 7 // 4. |
| mod | integer remainder | 3 is 7 mod 4. |
| ** | exponentiation | 1.21 is 1.1 ** 2. |
Except in the context of an arithmetic comparison operator, arithmetic
expressions need to be explicitly evaluated in Prolog, using
the is built-in predicate.
Mostly one uses these operators in a goal more like this:
X = Y + 2where
Y is a variable already bound to a numeric value,
or perhaps a arithmetic comparison goal like this:
X > Y * ZHere's another example: a rule to tell if a (whole) number is odd:
odd(Num) :- 1 is Num mod 2.Another way to do this one is to use
=:=:
odd(Num) :- Num mod 2 =:= 1.If you wanted to be more cautious, you could first check that
Num is in fact a whole number:
odd(Num) :- integer(Num), 1 is Num mod 2.
As usual in digital computations, with fractional numbers, it is
necessary to be careful with
approximation issues. Thus the final example in the table above,
1.21 is 1.1 ** 2. actually fails when typed into Prolog
as a query. This is because 1.1 ** 2, as represented in computers,
actually comes out to be something like 1.210000000001. See the section
on comparison operators for a solution to
this issue.
See also
comparison operators,
built-in functions.
likes, as in
likes(jane, pizza), is 2, as it takes two
arguments, jane and pizza.
Every fact and rule in a Prolog program, and every
built-in predicate has an
arity. Often this is referred to in descriptions of
these facts and rules, by appending
Arity Example Could Mean … 0 debugging.a bit like #define DEBUGGING in C1 happy(fido).Fido is happy. 2 likes(jane, pizza). 3 gave(mary, john, book).Mary gave John a book. /
and the arity to the name of the rule or fact.
For example, the built-in predicate
member/2 has arity 2.
A fact or rule can have more than one arity. For example, you might
want to have two versions of make terms:
% make(X, Y, Z): X makes Y for Z make(fred, chest_of_drawers, paul). make(ken, folding_bicycle, graham). % make(X, Y): X makes Y make(steve, fortune). make(kevin, mistake).What we have here are
makes/3 and makes/2.
assert, asserta, assertz
asserta ensures that the added fact/rule is
added before any other facts or rules with
the same functor), while assertz
adds the fact after any other rules or facts with the same functor.
When more than one rule/fact with the same functor is present
in the database, they are tried in the order that they appear in
the database, hence the need for asserta and assertz.
You would use asserta in the common case where the new fact is
supposed to save the effort involved in checking the fact using
rules and other facts. You might use assertz, for example, if
you were trying to construct a queue data structure (where items
are added to the end of the queue.
Examples:
?- assert(rich(mary)). true. ?- rich(mary). true. ?- assert((happy(X) :- rich(X), healthy(X))). X = _G180 ?- assert(healthy(mary)). true. ?- happy(X). X = mary
Facts/rules that are loaded from a file cannot be mixed with
facts/rules that are created using
assert/asserta/assertz
without take the special step of declaring the procedure in question
to be dynamic. For example, if you have a
rule to say, compute N!, with header
factorial(+N, -Result),
i.e with functor factorial and
arity 2, and you also want to
use asserta to add pre-computed values of some
factorials to the Prolog database (see also
memoisation), then you need to
declare factorial to be dynamic, by including
the following with your loaded rules for factorial:
:- dynamic factorial/2.
Programs with asserted facts and rules can be extra
hard to debug.
See also
retract,
retractall,
dynamic.
likes,
john, and pizza, in
likes(john, pizza). Atoms
of this type must start with a lower case letter.
They can include digits (after the initial lower-case letter)
and the underscore character (_).
<--->,
..., ===>. When using atoms of this
type, some care is needed to avoid using strings of special characters
with a predefined meaning, like the neck symbol
:-, the cut symbol
!, and various arithmetic
and comparison operators.
The available special characters for constructing this class of
atom are:
+,
-,
*,
/,
<,
>,
=,
:,
.,
&,
_, and
~.
Numbers, in Prolog, are not considered to be atoms.
atom is also the name of a built-in
predicate that tests whether its
single argument is an atom.
?- atom(pizza). true. ?- atom(likes(mary, pizza)). fail. ?- atom(<-->). true. ?- atom(235). fail.
atom_chars
can convert an atom into the list of
its constituent letters, or vice-versa. A fairly broad
concept of atom is used: this predicate will glue together
(or split up) any reasonable characters you give it.
A possible list would be to put together a list of
letters read, one character at a time, to make a word
- that is, an atom whose name is the word.
Examples:
?- atom_chars(pizza, List). List = [p, i, z, z, a] ?- atom_chars(Atom, [b, e, e, r]). Atom = beer ?- atom_chars(2007, List). List = ['2', '0', '0', '7'] ?- atom_chars(Atom, ['[', '3', ' ', ',', '4', ']']). Atom = '[3 ,4]'
See also atom_codes.
atom_codes
can convert an atom into the list of
the numeric codes used internally to represent the characters
in the atom, or vice-versa. Examples:
?- atom_codes(pizza, List). List = [112, 105, 122, 122, 97] ?- atom_codes(Atom, [98, 101, 101, 114]). Atom = beer
See also atom_chars.
Example: here is the definition of the built-in Prolog predicate
member:
member(X, [X | Rest]). % X is a member if its the first element
member(X, [Y | Rest]) :-
member(X, Rest). % otherwise, check if X is in the Rest
You may not think of member as a backtracking predicate, but
backtracking is built into Prolog, so in suitable circumstances,
member will backtrack:
?- member(X, [a, b, c]). X = a ; X = b ; X = c ; fail.Here
member backtracks to find every possible solution to
the query given to it. Consider also:
?- member(X, [a, a, a]). X = a ; X = a ; X = a ; fail.Here
member backtracks even though it keeps on finding the
same answer. What about
?- member(a, [a, a, a]). true ; true ; true ; fail.This is because prolog has three ways of proving that
a
is a member of [a, a, a].
The term backtracking also applies to seeking several sets of variable bindings to satisfy a single goal.
In some circumstances, it may be desirable to inhibit backtracking, as when only a single solution is required. The Prolog cut goal allows this.
Tracing the execution of a piece of Prolog
code that backtracks can be a good way to figure out what
happens during backtracking. If you wanted to experiment with
backtracking by tracing member,
you could achieve this by copying the code for member,
given above, changing the name from member to say
mem in the three places where it appears, and then
tracing your mem procedure.
bagof
bagof(+Template, +Goal, -Bag)
is used to collect a list Bag of all the
items Template that satisfy some goal Goal.
Example: assume
likes(mary, pizza). likes(marco, pizza). likes(Human, pizza) :- italian(Human). italian(marco).Then
?- bagof(Person, likes(Person, pizza), Bag). Person = _G180 Bag = [mary, marco, marco]
Notice that Bag contains the item marco
twice, because there are two ways to prove that marco
likes pizza - the fact and via the rule.
bagof fails if Goal has no solutions.
See also
setof, and, for differences between
bagof and findall, see
findall.
Suppose that the Prolog database contains just the single fact
likes(mary, pizza). and that there is a query:
?- likes(mary, What).
Prolog will search the (tiny) database, find that the query can
be satisfies if What = pizza, do it will bind
What to pizza and report success:
?- likes(mary, What). What = pizza ; fail.
Now suppose that there is a rule and facts in Prolog's database:
teaches(Teacher, Student):-
lectures(Teacher, Subject), studies(Student, Subject).
lectures(codd, databases).
studies(fiona, databases).
studies(fred, databases).
and that the user issues the query:
?- teaches(codd, Who).
The Prolog interpreter first matches the head of the rule with
the query, and so binds Teacher to codd.
It then finds a fact that indicates a subject
that Codd lectures - namely lectures(codd, databases).
At this point, the variable Subject is bound to
databases (Subject = databases).
In other words, Subject, perhaps temporarily,
has the value databases. Then Prolog tries to satisfy
the second goal studies(Student, Subject) with
Subject = databases,
i.e. it tries to satisfy studies(Student, databases).
When it finds the solution, studies(fiona, databases)
it will bind Subject to fiona,
and report the solution:
?- teaches(codd, Who). Who = fiona
Notice that the binding Subject = databases was made
in solving the query, but it is not reported, as it is not
explicitly part of the query.
Then, if the user types ";", Prolog will
backtrack and undo the binding Student = fiona
and look for another value for Subject that satisfies
studies(Student, databases), and find
as Student = fred.
However, while binding (and unbinding) is involved in this step,
it is properly treated under backtracking.
:-.
It has the form of a comma-separated list of goals, each of which is a functor, possibly followed by a
comma-separated list of arguments, in parentheses. E.g. in the rule
sister_of(X,Y) :- female(Y), X \== Y, same_parents(X,Y).
the three goals female(Y), X \== Y, same_parents(X,Y) form the body.
abs,
atan,
ceiling,
cos,
exp,
float,
floor,
log,
round,
sign,
sin,
sqrt,
tan,
truncate
| Function | Meaning |
|---|---|
abs(Exp) | absolute value of Exp : i.e. Exp if Exp ≥ 0, –Exp if Exp < 0 |
atan(Exp) | arctangent (inverse tangent) of Exp : result is in radians |
cos(Exp) | cosine of the Exp : Exp is in radians |
exp(Exp) | eExp : e is 2.71828182845… |
log(Exp) | natural logarithm of Exp : i.e. logarithm to the base* e |
sin(Exp) | sine of the Exp : Exp is in radians |
sqrt(Exp) | square root of the Exp |
tan(Exp) | tangent of the Exp: Exp is in radians |
sign(Exp) | sign (+1 or –1) of the Exp: sign(–3) = –1 = sign(–3.7) |
float(Exp) | float of the Exp: float(22) = 22.0 -
see also float the predicate |
floor(Exp) | largest integer ≤ Exp: floor(1.66) = 1 |
truncate(Exp) | remove fractional part of Exp: truncate(–1.5) = –1, truncate(1.5) = 1 |
round(Exp) | round Exp to nearest integer: round(1.6) = 2, round(1.3) = 1 |
ceiling(Exp) | smallest integer ≥ Exp: ceiling(1.3) = 2 |
These functions should be used in a context where they will actually
be evaluated, such as following is
or as part of an arithmetic comparison
operator like =:= or >.
Example:
?- X is sqrt(2). X = 1.41421Compare this with the following, where sqrt(2) is not evaluated, because
= does not evaluate its arguments.
?- X = sqrt(2). X = sqrt(2)
These mathematical functions may correspond to arity 2 built-in predicates: for example, one can do this:
?- sqrt(2, X). X = 1.41421Some versions of SWI Prolog (e.g. 5.6.47) implement many of these arity 2 predicates, but not e.g.
exp/2.
call
call is a built-in meta-predicate that allows its single
argument to be called/invoked as a goal. For
example, a program might create a goal (perhaps using
=..) on the fly, and then,
presumably later in the program, need to test the goal.
Here are queries that perform these roles - in a real
program, both the assert and the call
would be built in to Prolog procedures written by the programmer.
?- assert(likes(mary, pizza)). ?- call(likes(Person, pizza)). Person = mary ?- Goal =.. [likes, mary, What], call(Goal). Goal = likes(mary, pizza) What = pizza
."). A clause
may be a fact, like:likes(mary, pizza). food(pizza).
or a rule, like:
eats(Person, Thing) :- likes(Person, Thing), food(Thing).
A clause may also be a query to the Prolog interpreter, as in:
?- eats(mary, pizza).
A group of clauses about the same relation is termed a procedure.
%. Comments are ignored by the Prolog
interpreter; their purpose is to help a human reader understand
the Prolog code. Examples:
% This is a full line comment. % The next line contains a part-line comment. member(Item, [Item|Rest]). % member succeeds if Item is 1st object in list.
As in other programming languages, comments should be used freely to explain the high-level significance of sections of code, to explain tricky sections of code, etc. Comments should not echo what the code already clearly says. Comments like that actually get in the way of understanding, for example because they are likely to make the reader ignore the comments. Where it is possible to make code understandable by using a meaningful functor name or variable name, this is preferable to a comment.
It is good practice to begin each Prolog procedure with a comment describing what it does, e.g.
% member(Item, List) - succeeds if the item is a member of the list
If the list needs to have some special properties, e.g. if it must be a list of numbers, or must be instantiated (that is, have a value) at the time the procedure is called, then this header comment should say so:
% member(Item, List) - succeeds if the item is a member of the list; % List must be instantiated at the time member is called. Item need not % be instantiated.
It can be a good idea for the header comments to indicate examples of the kind of data on which the procedure is intended to operate, and what the result should be, if this can be done reasonably briefly. E.g.
% Examples of use: % ?- member(b, [a, b, c]). % true. % % ?- member(X, [a, b, c]). % X = a ; % X = b ; % X = c ; % fail.
Each file of Prolog code should begin with a (file) header comment, indicating who wrote the code, when, and what it is (overall) intended to do. This would be enough in a short Prolog assignment. In a "industrial strength" Prolog system, there would be details on the origins of algorithms used, and a revision history.
A good source of examples of Prolog commenting is example code made available in class, such as this one. Note however that this one is rather more heavily commented than usual, for instructional purposes. Note also that code examples presented on screen may be under-commented, because of the difficulty of fitting the comments and the code on the screen, and because the oral presentation accompanying the on-screen material replaces the comments to some extent.
Don't make your lines of comments (or code) too long - long lines can be hard to read, and really long lines may be folded, turning your neat formatting into a dog's breakfast. Stick to a maximum line length of 70 or 80 characters. Cute lines and/or boxes constructed of comment characters have the side effect of preventing the reader from seeing as much of your code at one time. The reader may then have to page up and down to figure out what your code does. They will not like you for this.
The use of cuts should normally be commented to explain why the cut is necessary.
It is a bad idea to comment (almost) every line, partly because of the clutter, and the distraction that this causes, and partly because most such comments are pointless duplications of the code (and/or comment things obvious except to a novice Prolog programmer):
Example of bad commenting (every line commented):
factorial(0, 1). % Factorial of 0 is 1.
factorial(N, FactN) :-
N > 0, % N is positive
Nminus1 is N - 1, % Calculate N minus 1
factorial(Nminus1, FactNminus1), % recursion
FactN is N * FactNminus1. % N! = N * (N - 1)!
Of these comments, only the first and last are even faintly justifiable,
and even these are probably too obvious, as, particularly for the last
comment, the variable names tell you everything you need to know. It looks
pretty, with all the %-signs neatly lined up, but it forces
the reader to check through all the unnecessary comments in case there
is anything important there. This code needs a header code, and probably
nothing else, though a beginning Prolog programmer might be justified
in pointing out that the line N > 0, is there to make
sure that the rule is only used in the case where N is
positive. (If you're not sure why this is so important, try leaving out
N > 0, and test the function on say N = 2.)
So the comment would be "% only use rule if N > 0.
How to write even worse comments!
It's easy - just write comments that are actually wrong.
![]()
Review your comments in a cool moment, and make sure that
what they say is true.
The + - ? convention for arguments to procedures
A convention often used to make the role of arguments to
a procedure clearer is to tag them with +,
–, and ?, as follows:
| Tag | Meaning |
|---|---|
+ | argument is expected to be instantiated (i.e. have a value rather than be a variable) when the procedure is called as a goal. |
– | argument is expected to be a variable to be bound when the procedure is called as a goal. |
? | argument may be either instantiated, or be a variable, when procedure is called as a goal. |
Example:
% factorial(+N, -FactorialN). %% supply a value for N, and FactorialN will be computed. % member(?Item, ?List). %% Item and List may either be instantiated, or a variable %% (but in fact at least one of them must be instantiated!)It's worth checking out, by the way, what happens if
List
is not instantiated …
?- member(a, List). List = [a|_G231] ; List = [_G230, a|_G234] ; List = [_G230, _G233, a|_G237]and so on … Prolog works its way through all list structures that contain
a.
Spelling, grammar, etc.
It's no bad thing if all of these comments are spelled correctly and are grammatically phrased. (Sometimes it is appropriate to abbreviate, so perhaps your comments don't all need to be full sentences.) It makes life harder for folks trying to read your code, if they have to wade through poor spelling and grammar. Remember that the people reading your code will include people you will want to understand your comments easily - helpers, markers, colleagues, your boss, even yourself in a year's time when you've forgotten how you got the code to work … So get into good commenting habits from the start.
See also indentation and white space.
| Comparison | Definition | Evaluates? |
|---|---|---|
X = Y | succeds if X and Y unify (match) in the Prolog sense | No |
X \= Y | succeeds if X and Y do not unify; i.e. if not (X = Y) | No |
T1 == T2 | succeeds if terms T1 and T2 are identical; e.g. names of variables have to be the same | No |
T1 \== T2 | succeeds if terms T1 and T2 are not identical | No |
E1 =:= E2 | succeeds if values of expressions E1 and E2 are equal | Yes |
E1 =\= E2 | succeeds if values of expressions E1 and E2 are not equal | Yes |
E1 < E2 | succeeds if numeric value of expression E1 is < numeric value of E2 | Yes |
E1 =< E2 | succeeds if numeric value of expression E1 is ≤ numeric value of E2 | Yes |
E1 > E2 | succeeds if numeric value of expression E1 is > numeric value of E2 | Yes |
E1 >= E2 | succeeds if numeric value of expression E1 is ≤ numeric value of E2 | Yes |
T1 @< T2 | succeeds if T1 is alphabetically < T2 | No |
T1 @=< T2 | succeeds if T1 is alphabetically ≤ T2 | No |
T1 @> T2 | succeeds if T1 is alphabetically > T2 | No |
T1 @>= T2 | succeeds if T1 is alphabetically ≥ T2 | No |
See also is. is is not
a comparison operator, but is frequently confused with = by
novice Prolog programmers. Briefly, you use X is Exp
to evaluate an arithmetic expression, like Y + 2,
that contains an arithmetic operator, like +, and
bind the resulting value to the variable X to the left of the
the operator is.
As an example of @< and its relatives,
?- likes(mary, pizza) @< likes(mary, plums). true.This succeeds because
likes and mary are
the same in both terms, and pizza alphabetically
precedes plums.
Comparison of fractional numbers: When comparing two
fractional numbers, problems can arise from the approximate
nature of representations of fractional numbers in computers.
Sometimes it will work as expected, and sometimes not.
For example, the query 1.21 =:= 1.1 * 1.1. fails
with SWI Prolog on the computer where this article was written.
You can and should work around this problem by, instead of
testing fractional numbers for equality, doing something
like the following:
?- abs(1.21 - 1.1 * 1.1) < 0.000001. true.
abs signifies "absolute value":
abs(X) = X if X >= 0 and
abs(X) = -X if X =< 0.abs(X - Y) < Tiny, where
Tiny is bound to small number (or is a small number, as in
the example).
How small to make Tiny above depends on the particular
case - you might need to use a smaller number if X and
Y need to be very close together to make your
algorithm work correctly.
consult
?- consult('myprogram.pl').
This loads the contents of the Prolog program in the file
myprogram.pl into the running Prolog's database.
Note that in SWI Prolog, at least, before loading the new facts
and rules into the database, Prolog first removes all facts
and rules that relate to procedures in myprogram.pl.
So if myprogram.pl contains, for example, the
fact likes(jane, pizza) then all facts and rules
about likes that have two arguments will be removed
from the Prolog database before the new facts in
myprogram.pl are loaded up. This is convenient
when you are using consult to re-load a program
after editing it (e.g. in another window, with the Prolog
interpreter left running), but could be a little surprising
if you were trying to load extra facts from a file.
It is possible to consult more than one file at a time, by replacing the single file name with a list of files:
?- consult(['file1.pl', 'file2.pl', 'file3.pl']). % file1.pl compiled 0.00 sec, 524 bytes % file2.pl compiled 0.01 sec, 528 bytes % file3.pl compiled 0.00 sec, 524 bytes true.It is also possible to abbreviate a consult call, simply typing the list of (one or more) files as a goal for Prolog:
?- ['file1.pl', 'file2.pl', 'file3.pl'].In some Prolog implementations, consulting
user causes the
Prolog interpreter to read facts and rules from the user's
terminal:
?- [user]. |: likes(jane, pizza). |: bad_dog(Dog) :- |: bites(Dog, Person), |: is_human(Person), |: is_dog(Dog). |: <control-D> % user://1 compiled 0.00 sec, 1,156 bytes true. ?-
However, you might wish to get input/read from somewhere else
during the execution of your Prolog program - for example,
you might want to read from a file held on the computer on
which the program is running, or on some local file server.
To change the current input stream, you use the Prolog
built-in extra-logical predicate see. If Prolog
executes the goal see('input.dat'), then input will subsequently
come from the file input.dat, in the current working directory
of the workstation*
that is running Prolog. If the specified file cannot be found in the
current working directory, an error may be reported,
as in this interaction with SWI Prolog:
?- see('nosuchfile.dat').
ERROR: see/1: source_sink `nosuchfile.dat' does not exist (No such file or directory)
Other errors are possible - you may not have the right to read
the file in question, for example.
If the file does exist and is readable, then subsequent read operations
get their data from the file. The parameter to see can be
just the file name, as illustrated above, or it could be
a path to the file that is wanted: e.g.
see('/Users/billw/prologstuff/input.dat')
Prolog will continue to issue prompts for more queries while
you are "seeing" a file, but any explicit read operations
access the file.
The built-in extra-logical predicate seen (with no argument)
allows you to revert to reading data from the keyboard.
Example (assuming info.dat starts with the line
hungry(jack).):
?- see('info.dat'), read(X).
X = hungry(jack)
?- seen, read(Y).
|: full(jack).
Y = full(jack)
See also current output stream, input, output, files.
However, you might wish to write to somewhere else during the execution of your Prolog program - for example, you might want to write to a file held on the computer on which the program is running, or on some local file server.
To change the current output stream, you use one of the Prolog
built-in extra-logical predicates tell and
append/11.
If Prolog executes the goal tell('output.dat'), then output
will subsequently go to the file output.dat,
in the current working directory of the
workstation2
that is running Prolog.
If the specified file cannot be found in the
current working directory, it will be created. If the file does exist, it will
be overwritten. If you use append/1, subsequent
write operations will add material to the end of the file,
instead of overwriting the file. If you do not have permission
to write files in the current directory, you will see an error
message:
?- tell('/usr/bin/ztrash').
ERROR: tell/1: No permission to open source_sink `/usr/bin/ztrash' (Permission denied)
This means that you do not have permission to write files in
the directory /usr/bin. Another possible error is
that you might not have permission to write to the particular
file, if it already exists.
If the file is able to be written, then subsequent write operations
send their data to the file. The parameter to tell can be
a path to the file that is wanted, as in the example above, or it
could be just the file name, e.g.
tell('output.dat').
Prolog will continue to issue prompts for more queries and
print bindings while you are "telling" or "appending" a file,
but any explicit write operations access the file.
The built-in extra-logical predicate told (with no argument)
allows you to revert to writing data to the original window.
Example:
?- tell('info.dat'), write(thirsty(jack)), nl.
true.
?- told, write(drunk(jack)).
drunk(jack)
true.
?- halt.
% cat info.dat # - # is Unix comment char, cat lists info.dat
thirsty(jack)
%
See also current input stream, input, output, files.
append/1 is not related to
append/3, which in turn
has nothing to do with output streams.
!, which always succeeds, but cannot be backtracked past. It is used to prevent unwanted
backtracking, for example, to prevent extra solutions being found by
Prolog.Example: Suppose we have the following facts:
teaches(dr_fred, history). |
studies(alice, english). |
Then consider the following queries and their outputs:
?- teaches(dr_fred, Course), studies(Student, Course). Course = english Student = alice ; Course = english Student = angus ; Course = drama Student = amelia ; fail.
Backtracking is not inhibited here. Course is
initially bound to history, but there are no students
of history, so the second goals fails, backtracking
occurs, Course is re-bound to english, the
second goal is tried and two solutions found (alice
and angus), then backtracking occurs again, and
Course is bound to drama, and a final
Student, amelia, is found.
?- teaches(dr_fred, Course), !, studies(Student, Course). fail.
This time Course is initially bound to history,
then the cut goal is executed, and then studies goal
is tried and fails (because nobody studies
history). Because of the cut, we cannot
backtrack to the teaches goal to find
another binding for Course, so the whole query
fails.
?- teaches(dr_fred, Course), studies(Student, Course), !. Course = english Student = alice ; fail.
Here the teaches goal is tried as usual, and
Course is bound to history, again as
usual. Next the studies goal is tried and fails,
so we don't get to the cut at the end of the query at this point,
and backtracking can occur. Thus the teaches goal is
re-tried, and Course is bound to english.
Then the studies goal is tried again, and succeeds, with
Student = alice. After that, the cut goal is
tried and of course succeeds, so no further backtracking is possible
and only one solution is thus found.
?- !, teaches(dr_fred, Course), studies(Student, Course). Course = english Student = alice ; Course = english Student = angus ; Course = drama Student = amelia ; fail. ?-
In this final example, the same solutions are found as if no cut was present, because it is never necessary to backtrack past the cut to find the next solution, so backtracking is never inhibited.
Cuts in Rules
In practice, the cut is used in rules rather than in multi-goal
queries, and some particular idioms apply in such cases.
For example, consider the following code for
max(X, Y, Max), which is supposed to bind Max to
the larger of X and Y (which are assumed to be numbers).
max(X, Y, X) :- X > Y, !. max(X, Y, Y).This is a way of saying: "if the first rule succeeds, use it and don't try the second rule. (Otherwise, use the second rule.) We could instead have written:
max(X, Y, X) :- X > Y. max(X, Y, Y) :- X =< Y.in which case both rules will normally be tried (unless backtracking is prevented by a cut in some other part of the code). This is slightly less efficient if
X
is in fact greater than Y (unnecessary backtracking
occurs) but easier for people to understand, though regular
Prolog programmers rapidly get to recognise this type of
idiom. The extra computation in the case of max is
trivial, but in cases where the second rule involves a long computation,
there might be a strong argument for using the cut on efficiency
grounds.
(1) and (2) are often the
hard part. Once you have observed or detected an error,
tracing the code in question can help
find the problem. Sometimes inserting calls to
write
into parts of the code where you think that the problem
might be can help to localise the error.
Ultimately, reading your code carefully with be part of the task. So it would be a good idea to write the code carefully in the first place, in order to make it easy to understand. See also error and warning messages and commenting and white space.
dynamic
assert/asserta/assertz",
or maybe remove facts/rules using
retract/retractall.
To do this, you must declare the procedure to be dynamic.
You can declare a procedure to be dynamic by including
in your code (normally adjacent to the facts/rules for that
procedure) a suitable dynamic directive.
Example - suppose you have a procedure called likes,
with arity 2, and you have a "starter set"
of facts/rules in your Prolog program, but you want to infer extra
facts about likes during execution, and add them
to the data base so that they don't need to be recomputed each
time they are used. [You would normally only do this - add the
new facts to the database - if the extra facts were slow to
compute.] You need to declare likes (with arity 2)
to be dynamic. You do this as follows:
:- dynamic likes/2.
[By the way, notice that this leaves open the possibility that a
different version of likes (with arity 3, say) might
not be dynamic.]
See also memoisation.
happy(fred). likes(mary, pizza).
as opposed to this rule:
happy(Person) :-
healthy(Person),
enough_money(Person),
has_friends(Person).
Bodiless clauses with variables, like …
member(Item, [Item | RestOfList]).
… behave like rules in that they provide a general
way of knowing that, for any Item, that
item is a member of a list of which it is
the first item (see also member).
The clause above is likely to generate a SWI Prolog warning
message like this:
Singleton variables: [RestOfList]
to tell you that the variable RestOfList is
only mentioned once in the code (so may be a spelling
mistake, for example). You can suppress the warning by
writing, instead:
member(Item, [Item | _]).using a don't-care variable, although this may be more difficult for a human to follow, particularly a beginnner at Prolog.
fail
See also
true,
repeat,
input schema.
findall
findall(+Template, +Goal, -List)
is used to collect a list List of all the
items Template that satisfy some goal Goal.
Example: assume
likes(mary, pizza). likes(marco, pizza). likes(Human, pizza) :- italian(Human). italian(marco).Then
?- findall(Person, likes(Person, pizza), Bag). Person = _G180 List = [mary, marco, marco]
findall succeeds and binds List
to the empty list, if Goal has no solutions.
This can be convenient if you don't want your goal to
fail just because the collection of solutions is empty.
(In other cases, you would want the goal to fail
if there are no solutions.)
Another difference between
bagof
and findall is the extent of backtracking done
before binding the third parameter (List).
For example, assume:
believes(john, likes(mary, pizza)). believes(frank, likes(mary, fish)). believes(john, likes(mary, apples)).
Then bagof and findall exhibit the following behaviour:
?- bagof(likes(mary, X), believes(_, likes(mary, X)), Bag). X = _G188 Bag = [likes(mary, fish)] ; X = _G188 Bag = [likes(mary, pizza), likes(mary, apples)] ; fail. ?- findall(likes(mary, X), believes(_, likes(mary, X)), Bag). X = _G181 Bag = [likes(mary, pizza), likes(mary, fish), likes(mary, apples)] ; fail.
You can see that bagof is collecting frank's
beliefs about what mary likes,
binding Bag, then backtracking and collecting john's
beliefs and rebinding Bag,
while findall all finds everybody's
beliefs and binds them all to Bag, just once.
See also
setof.
tell, telling, told, see, seeing, seen, append/1
For example, we may wish to write out a table of results that have been
computed for us by our Prolog program. One can use built-in predicates like
write, nl, putc,
tab and others to write out the table, but by default
it will appear on the computer screen. To direct this output to a file,
we use the tell built-in predicate. Suppose that we wish
to write the table to a file called "mytable.data". By executing the
(pseudo-)goal tell("mytable.data"), we tell Prolog that
the new current output stream is to be the file "mytable.data".
Subsequent writes will go to this file. When one wishes to stop
writing to the file and resume writing on the screen, one uses the
built-in predicate told (with no arguments). Also, the
query ?- telling(X). binds X to the name of the
current output file. If the current output stream is not a file, then
X will be bound to something that indicates that the current
output stream is the screen - for example, in Unix, X may
be bound to the atom stdout (standard output, which is
normally the screen). Example:
?- tell('mytable.data'), write('***** Table of results *****'), nl, told.
% the file mytable.data should now contain a single line of text as above
The built-in Prolog predicate append/1 is like tell,
except that it arranges for subsequent write operations to add
data to the end of the specified file, rather than overwriting
the file with the first subsequent write operation.
If myothertable.data initially contains, say, a single
line, This is the first line, then append/1
works as follows:
?- append('myotherfile.dat'), write('Here is another line'), nl.
true.
?- halt.
% cat myotherfile.dat # - # is Unix comment char, cat lists file contents
This is the first line
Here is another line
%
The situation for reading from a file is analogous to writing
(except that there is no analogue for append/1). One can use
built-in predicates like read,
getc and others to read, by default from the keyboard.
By executing the
(pseudo-)goal see('mydata.text'), we tell Prolog that
the new current input stream is to be the file mydata.text.
Subsequent reads will come from this file. When one wishes to stop
reading from the file and resume reading from the keyboard, one uses the
built-in predicate seen (with no arguments). Also, the
query ?- seeing(X). binds X to the name of the
current input file. If the current input stream is not a file, then
X will be bound to something that indicates that the current
output stream is the screen - for example, in Unix, X may
be bound to the atom stdin (standard input, which is
normally the keyboard). Example:
?- see('mydata.text'), read(X), seen, write(X), nl.
% the first Prolog term in the file mydata.text should now appear
% on the screen, having been read from the file with read(X), and then
% written to the screen with write(X) and nl.
What happens if you try to read from a file and there is nothing (left)
to read, either because the file is empty, or you have previously read
everything there was to read in this file? In this case, Prolog binds
the variable that was the argument to read to the special
atom end_of_file. Knowing this means that you can test
after a read to make sure that you did not hit end of file.
Example:
?- see('empty.dat'), read(Term).
Term = end_of_file
See also
current input stream,
current output stream,
input,
output.
append/3 is unrelated to append/1.
functor
likes(mary, pizza), likes is the functor. In a more
complex structure, like
persondata(name(smith, john), date(28, feb, 1963))
persondata -
There is also a built-in predicate called functor, used
to extract the functor and arity of a structure.
There is also a built-in predicate functor with
three arguments: functor(Term, Functor, Arity),
which succeeds if Term is a term
with functor Functor and arity
Arity. Examples:
?- functor(likes(mary, pizza), Functor, Arity). Functor = likes Arity = 2 ?- functor(likes(X, Y), Functor, Arity). X = _G180 Y = _G181 Functor = likes Arity = 2 ?- functor(likes, Functor, Arity). Functor = likes Arity = 0 ?- functor(X, likes, 2). X = likes(_G232, _G233)
?- lectures(john, Subject), studies(Student, Subject).
there are two goals,
lectures(john, Subject)
and studies(Student, Subject).
A goal is something that Prolog tries to satisfy by finding
values of the variables (in this case Student
and Subject) that make the goal succeed.
These value(s) are then said to be bound to
the variable(s).
If Prolog is unable to do this, the goal fails (and Prolog
will print "fail" in response to the query).
If all the goals in a query succeed, Prolog prints the bindings
necessary to make the query succeed. (If you then, in SWI Prolog,
type a semicolon (;) Prolog will
backtrack and look for another set of bindings that will
satisfy the goals in the query.
Sometimes it is not necessary to bind variables in order
to satisfy a goal. For example, there is no variable to
bind, when the goal is
likes(mary, pizza) and the Prolog database
already contains likes(mary, pizza).
In this case, Prolog will print "true"
in response to the query, rather than printing bindings.
Goals occur in rules as well as in queries. In
happy(Dog) :-
is_dog(Dog),
go_for_walk(Dog).
is_dog(Dog) and go_for_walk(Dog) are the
two goals that form the body of the rule.
halt
halt:
% prolog --- Welcome message from Prolog interpreter --- ?- halt. %
The "%" signs in this example represent the operating system
command interpreter (aka "shell") prompt, not a Prolog
comment.
:-. It normally has the form of a functor
(i.e. a relation symbol, followed by a comma-separated
list of parameters, in parentheses. E.g. in the rule
sister_of(X,Y) :- female(Y), X \== Y, same_parents(X,Y).
sister_of(X,Y) is the head.
->
… -> … ; … functions as
an if … then … else … facility.
Example:
min(A, B, Min) :- A < B -> Min = A ; Min = B.
This version of min (which, like the one
below, assumes that
A and B are numbers) says
"if A < B then unify Min with
A otherwise unify Min with
B". Possibly it is easier to understand a
two-rule version of min:
min(A, B, A) :- A <= B. min(A, B, B) :- B < A.
That is, the minimum of A and B is A
if A <= B; the minimum
is B if B < A.
However, the -> definition of min does illustrate
the operation of ->.
The code illustration has a rather procedural-programming
feel to it that may comfort beginning users of Prolog. Possibly
it should be avoided by them just for this reason! At least,
they should avoid using it when the only reason for using it is the
procedural feel. If use of -> massively reduced the
length of the code, thereby simplifying it, that might be an
argument for using it.
The detailed semantics of … -> … ; … is relatively
complex. The following description is quoted from the SWI Prolog
help text on ->:
The->/2construct commits to the choices made at its left-hand side, destroying choice-points created inside the clause (by;/2), or by goals called by this clause. Unlike!/0, the choice-point of the predicate as a whole (due to multiple clauses) is not destroyed. The combination;/2and->/2acts as if defined by:If -> Then ; _Else :- If, !, Then. If -> _Then ; Else :- !, Else. If -> Then :- If, !, Then.Please note that(If -> Then)acts as(If -> Then ; fail), making the construct fail if the condition fails. This unusual semantics is part of the ISO and all de-facto Prolog standards.
listlength([], 0).
listlength([Head | Tail], Length) :-
listlength(Tail, TailLength),
Length is TailLength + 1.
The rule is to align heads of rules against the left margin,
and to indent body clauses, normally all by the same amount.
Sometimes further indentation is needed, for example if the
code involves a complex term that won't
fit on one line. In this case, you would indent the term to
exhibit its structure.
…
book(
title('The Strange Case of Dr Jekyll and Mr Hyde and Other Tales of Terror'),
author(given_names(['Robert', 'Louis']),
surname('Stevenson')
),
publisher('Penguin Books')
), …
Sometimes the indentation rules can be bent: for example, it is not unusual to put on a single line, a rule with a body that contains just a single (short) clause.
happy(Person) :- rich(Person).
Comments on indented code should also be indented, if they can't be fitted on the same line as the code.
% sum_even_elements(+ListOfNumbers, -Sum): compute Sum of even items in ListOfNumbers
sum_even_elements([], 0). % base case
sum_even_elements([FirstNum | Rest], Sum) :-
% check whether FirstNum is even
0 is FirstNum mod 2,
% if we get here it was even, so process rest of list and add FirstNum to result
sum_even_elements(Rest, SumForRest),
Sum is FirstNum + SumForRest.
This example is more heavily commented than necessary, in order to demonstrate the
commenting convention.
See also comments and white space.
member, we write
something like member(Item, [a, b, c]) - first the
predicate name, member, then "(", then the first
argument, Item, then a comma, then the second
argument, [a, b, c], then ")".
However, with built-in predicates like
=, < and > that are
usually written between their arguments, as in First
< Max, we write, in Prolog as in mathematics, in
infix notation. Another infix built-in "predicate" is
is, which is used in
evaluating arithmetic expressions.
It is possible in Prolog to define your own infix (and postfix)
operators, and modify the syntactic handling of prefix operators
- see op.
read, end_of_file,
get, get_byte, getc,
flush_output
read(X) which reads the next term
in the current input stream,
which means the window on your workstation
unless you have done something slightly fancy with
files, and unifies
it with the variable X.
end_of_file: if there is nothing to read in the
current input stream, read(X) causes X
to be bound to the special
symbol end_of_file. Unless you are absolutely sure
you have some other way of knowing when there will be no more
input data to read, you should check to make sure that the term
that you have read is not end_of_file. Ways that you
might be (almost) absolutely sure: the first term read might be
a number indicating how many further terms are to be read.
get_byte(C) which reads a single character from
the current input stream, and binds the equivalent integer
code, in the range 0 to 255. If there is no further character
to read, C is bound to –1.
get(C) is like get_byte(C), except
that it reads the first non-blank character, if any,
from the current input stream.
flush_output is actually an output goal,
which ensures that any output which has been requested but
not yet performed, is performed right now. Output might not
be completed until there is enough to make it worthwhile,
or until an operation like flush_output forces it.
Example 1: Assume that input is coming from the user's workstation
window and that procedure read_a_char is defined as:
read_a_char(C) :-
write('Type: '), flush_output,
get_byte(C).
Then you can do the following - note that 43 is the character
code for the character +.
?- read_a_char(Ch). Type: + Ch = 43
See also atom_codes,
for conversion of a string of numeric character
codes to an atom composed of the characters represented by those
character codes. For example, 102, 105, 100, and 111 are the
numeric codes for the letters f, i, d, and o. atom_codes
can be used as follows:
?- atom_codes(A, [102, 105, 100, 111]). A = fido
Example 2: Assume that there is a file called inputdata,
which contains on its first line the term
likes(mary, pizza).
with a full stop at the end of the term.
?- see('inputdata'), read(Term), seen.
Term = likes(mary, pizza)
NB: No full stop at the end of Term's binding.
dountilstop :- repeat, read(X), (X = stop, ! ; process(X), fail ).
In this case, the code reads data, a term at a time, from the current input stream. To make that input stream a file called, say, inputdata, you would invoke the code as follows:
?- see('inputdata'), dountilstop, seen.
The call to fail is there to force backtracking.
The call to repeat is there to force the code
to endlessly repeat - that is, until something terminates
the backtracking. This "something" is the cut
after X = stop - if the term read is the
atom stop, the goal
X = stop succeeds, the cut is executed, and
the rule terminates.
To use the schema, replace the read operation with the
one you want (might be getc or ratom
or …), replace the call to process with a call to a
procedure that does whatever you want to do to the
data, and you're in business. Actually - you'll probably
find a few other adjustments are needed, but at least you're
on the right track.
See also
read,
repeat,
see, seen,
; (or), and
fail.
is, evaluation
is built-in predicate is used in Prolog to
force the evaluation of arithmetic expressions. If you just
write something like X = 2 + 4, the result is
to bind X to the unevaluated term
2 + 4, not to 6. Example:
?- X = 2 + 4.
X = 2+4
If instead you write X is 2 + 4, Prolog arranges
for the second argument, the arithmetic expression 2 + 4,
to be evaluated (giving the result 6) before binding
the result to X.
?- X is 2 + 4.
X = 6
It is only and always the second argument that is evaluated. This
can lead to some strange-looking bits of code, by mathematical
standards. For example, mod is the remainder-after-division
operator, so in Prolog, to test whether a number N
is even, we write 0 is N mod 2, rather than the
usual mathematical ordering: N mod 2 = 0.
What is the difference between N = 1 and
N is 1? In final effect, nothing. However, with
N is 1, Prolog is being asked to do an extra step
to work out the value of 1 (which, not surprisingly, is 1).
Arguably, it is better to use N = 1, since this does
not call for an unnecessary evaluation.
The message is: use is when, and only when, you
need to evaluate an arithmetic expression.
Actually, you don't need to use is to
evaluate arithmetic expressions that are arguments to the arithmetic
comparison operators
>, >=, < =<, =:= (equal), and =\= (not equal),
all of which automatically evaluate their arguments.
A common mistake, for people used to procedural programming
languages like C, is to try to change the binding of a variable
by using an goal like N is N + 1. This goal will
never succeed, as it requires N to have the same value
as N + 1, which is impossible. If you find yourself
wanting to do something like this, you could look at the code
of factorial for inspiration.
[1, 2, 3]
is a list. The empty list is written [].
Frequently it is convenient
to refer to a list by giving the first item, and a list consisting of
the rest of the items. In this case, one writes the list as
[First | Rest].
We have expressed this here using variables, but this need not be so,
for example, we could write [1, 2, 3] as:
[1 | [2, 3]]
[1 | Rest], where Rest is bound to
[2, 3]
[First | [2, 3]], where First is bound to
1
[First | Rest], where First is bound to
1, and Rest is bound to [2, 3]
[1, 2 | [3]]
[1, 2, 3 | []]
Lists can also be expressed using a normal term syntax, using the
built-in predicate name . - that is, a full
stop or period. In this case, the empty list atom ([])
must be used to terminate the list. However, this approach is
more cumbersome, and in practice people use the
[1, 2, 3]- style syntax. Example:
?- X = .(1, .(2, .(3, []))). X = [1, 2, 3]
listing
listing/0) and with one argument
(listing/1). With no arguments, it causes the
contents of the Prolog database to be printed out. With one
argument - the name of a procedure - it prints out just the
rules and/or facts relating to that procedure. Examples,
assuming you have started Prolog with a program file that
contains the facts and ruleshappy(ann). happy(tom). happy(X) :- rich(X). rich(fred).
?- listing.
happy(ann).
happy(tom).
happy(A) :-
rich(A).
rich(fred).
true.
?- listing(happy).
happy(ann).
happy(tom).
happy(A) :-
rich(A).
true.
In the case of listing/0, Prolog may also print out
some internal house-keeping information.
member built-in predicate
?- member(a, [b, a, c]). true. ?- member(X, [b, a, c]). X = b ; X = a ; X = c ; fail. ?- member(happy(fido), [angry(rex), happy(fido)]). true. ?- member(a, [b, [a], c]). fail. ?- member([a], [b, [a], c]). true. ?- member(a, Y). Y = [a|_G310] ; Y = [_G309, a|_G313] ; Y = [_G309, _G312, a|_G316] ; Y = [_G309, _G312, _G315, a|_G319] ;… and so on for as long as you press "
;". The
response to this last query says that a is a member of an
(uninstantiated) list Y if a is the first member
or the second member or the third member or the fourth member or …_G309, _G310, _G313,
etc. represent bits of the list Y that have no information
about.
See also backtracking.
asserta), that has been inferred
during the execution of your program, in the Prolog database,
to avoid possibly having to infer the same fact again, later
in the execution of the program.
Whether this is worth doing depends on the nature of the problem. If facts are regularly re-inferred (possibly as a consequence of a recursive solution), then memoisation could be a very good idea. Some algorithms perform double or multiple recursion (that is, they "recurse" on more than one variable), and both recursive branches can end up re-calculating the same thing, leading in some cases to exponential growth in the number of sub-cases as the size of the problem grows. The classic example is the recursive computation of the n-th Fibonacci number, which is (recursively) defined* by:
f0 = f1 = 1, and,
for n > 1, fn = fn-2 + fn-1.
* Note that there is also a non-recursive expression for f>n.
As can be seen (or rather, extrapolated) from the diagram, the number of calls grows exponentially, and the Fibonacci numbers rapidly become infeasible to calculate by the naive recursive program:
fib_naive(0, 1).
fib_naive(1, 1).
fib_naive(N, Result) :-
N > 1,
Nminus2 is N - 2,
Nminus1 is N - 1,
fib_naive(Nminus1, FibNminus2),
fib_naive(Nminus1, FibNminus1),
Result is FibNminus1 + FibNminus2.
However, by memoising, the computation becomes perfectly feasible
for much larger values of n:
fib_memo(0, 1).
fib_memo(1, 1).
fib_memo(N, Result) :-
N > 1,
Nminus2 is N - 2,
Nminus1 is N - 1,
fib_memo(Nminus2, Fib2Nminus2),
fib_memo(Nminus1, Fib2Nminus1),
Result is Fib2Nminus1 + Fib2Nminus2,
asserta((fib_memo(N, Result) :- !)).
Note the use of asserta to
store the memoised value at the start of the collection of
facts for fib_memo, so that it will be checked
before trying the rule. Note also the cut
(!) at the end of the assserta-ed rule,
which stops the algorithm from backtracking and trying the rule
after it has found a solution using the memoised value(s).
More?
true. or fail.. However, in some
versions of Prolog, including recent versions of SWI-Prolog (e.g.
version 5.6.47) another possible response can occur: More?.
This means (1) true. and (2) that there are other alternatives
for Prolog to explore, that might allow it to conclude true.
in a different way. I.e. Prolog may be able to prove that your query
is true in more than one way. For example, suppose that
likes(jane, pizza) is in your Prolog program as a fact,
but can also be inferred using a rule and some other facts.
?- likes(jane, pizza). More?
The More? is a prompt to
allow you to find out if this is so - i.e. that the query can be proven
in more than one way. Type a semicolon if you want
Prolog to go ahead and look for a different proof. Type return if you
are happy with a single proof.
If you want to suppress this behaviour, you can insert
cuts to prevent the backtracking that is finding
the extra (possible) proofs, or you can, in SWI-Prolog, use this
query:
set_prolog_flag(prompt_alternatives_on, groundness).
or alternatively, put the following directive in your Prolog source code:
:- set_prolog_flag(prompt_alternatives_on, groundness).
You may also be able to avoid responses of More? in some
cases by re-ordering relevant clauses in your program.
Here's some example code to demonstrate More?
happy(fido).
happy(rex).
happy(Dog) :- is_dog(Dog), walkies(Dog).
happy(Dog) :- is_dog(Dog), chase_car(Dog).
is_dog(fido).
is_dog(rex).
walkies(fido).
walkies(rex).
chase_car(fido).
There are three ways to prove fido is
happy: from the fact, and
from each of the two rules. So:
Prolog Dialogue Commentary
?- happy(fido).
More? ;
More? ;
true.
?- happy(rex).
More ;
More ;
fail.
happy(fido) proven using fact
happy(fido) proven using "walkies" rule
happy(fido) proven using "chase_car" rule
happy(rex) proven using fact
happy(rex) proven using "walkies" rule
happy(rex) not provable using "chase_car" rule
:-, used in a Prolog rule to separate the head from the body. Usually read as if. Thus
a :- b, c.
a (is true) if b and c (are
true).
\+
A less obvious problem is that, depending again on the rules and facts known to the Prolog interpreter, it may take a very long time to determine that the proposition cannot be proven. In certain cases, it might "take" infinite time.
Because of the problems of negation-as-failure, negation
in Prolog is represented in modern Prolog interpreters using
the symbol \+, which is supposed to be a mnemonic
for not provable with the \ standing for
not and the + for provable.
In practice, current Prolog interpreters tend to support the
older operator not as well, as it is present in
lots of older Prolog code, which would break if not
were not available.
Examples:
?- \+ (2 = 4). true. ?- not(2 = 4). true.
Arithmetic comparison operators in Prolog each come equipped with a negation which does not have a "negation as failure" problem, because it is always possible to determine, for example, if two numbers are equal, though there may be approximation issues if the comparison is between fractional (floating-point) numbers. So it is probably best to use the arithmetic comparison operators if numeric quantities are being compared. Thus, a better way to do the comparisons shown above would be:
?- 2 =\= 4. true.
number(–)
number is also the name of a built-in predicate which tests
its single argument and succeeds if that argument is a number. Examples:
?- number(2.1). true. ?- number(-8). true. ?- X = 7, number(X). X = 7. ?- number(1+1). fail.The last query fails because 1+1 is an unevaluated expression, not a number. See is, for more on evaluation.
once
once
takes a single argument, which must be a "callable term" - one
that makes sense as a goal - e.g. happy(X) makes sense as a goal,
but 23 does not -
and calls the term in such a way as to produces just one solution.
It is defined as:
once(P) :- P, !.
See also
!,
call,
backtracking.
op, infix, prefix, and postfix operators, precedence in Prolog
:- op(+Precedence, +Type, :Name).
The Prolog built-in predicate op serves to
define the Type and Precedence of infix and postfix, and
prefix operators in Prolog terms.
Prolog terms normal begin with the functor
(e.g. likes, in likes(mary, pizza))
but exceptions exist - for example, arithmetic expressions are
written in the usual infix way (i.e. as X + 1,
rather than +(X, 1)), and negation can be written
without parentheses, as a prefix operator: not P.
The table below lists the predefined infix operators in
SWI-Prolog. You may wish to add infix operators of your own.
For example, you might wish to define an infix and.
This can be done as follows:
:- op(700, xfy, and).
This declares and to be an operator with
precedence 700 and type xfy.
Note two things: (1) the fact that these things are
referred two as operators does not mean that an
operation is performed when they are encountered. This
is not usually the case; and (2) the declaration of an
operator only affects the external appearance of the
operator in your program - internally, the standard
representation is used - for example X + 1
really is internally represented by Prolog as
+(X, 1).
Precedence is an integer between 0 and 1200.
Precedence 0 removes the declaration.
Type is one of:
xf,
yf,
xfx,
xfy,
yfx,
yfy,
fy
or
fx.
The f indicates the position of the functor, while
x and y indicate the position of the
arguments. Thus xfx, xfy, and yfx
all indicate an infix operator.
y should be interpreted as "in this position
a term with precedence equal to or less than the precedence of the functor
should occur". For x the precedence of the argument must
be strictly less than that of the functor.
The precedence of a term is 0, unless its principal
functor is an operator, in which case the precedence is the precedence
of this operator. A term enclosed in brackets (…) has precedence 0.
To see how this works, consider the arithmetic expression
a - b - c. In normal maths, this is interpreted as
(a - b) - c, rather than a - (b - c).
To achieve this, the binary infix operator - must
be declared as type yfx so that the first argument
has precedence over the second. Then, internally,
a - b - c will be represented as
-(-(a, b), c) rather than
-(a, -(b, c)).
| Prec. | Type | Operator |
|---|---|---|
| 1200 | xfx | -->, :- |
| 1200 | fx | :-, ?- |
| 1150 | fx | dynamic, discontiguous, initialization, module_transparent, multifile, thread_local, volatile |
| 1100 | xfy | ;, | |
| 1050 | xfy | ->, op*-> |
| 1000 | xfy | , |
| 954 | xfy | \ |
| 900 | fy | \+ |
| 900 | fx | ~ |
| 700 | xfx | <, =, =.., =@=, =:=, =<, ==, =\=, >, >=, @<, @=<, @>, @>=, \=, \==, is |
| 600 | xfy | : |
| 500 | yfx | +, -, /\, \/, xor |
| 500 | fx | +, -, ?, \ |
| 400 | yfx | *, /, //, rdiv, <<, >>, mod, rem |
| 200 | xfx | ** |
| 200 | xfy | ^ |
Note that all operators can be redefined by the user, most commonly accidentally and with disastrous results.
;
happy(X) :- rich(X), famous(X).
says that X is happy if X is rich
and X is famous. What about logical-or, ∨, also called
disjunction? If we want to say that X is happy
if X is rich or if X
is famous, we have two possibilities:
; operator (pronounced "or").
Option 1:
happy1(X) :- rich(X). happy1(X) :- famous(X).Option 2:
happy2(X) :- rich(X) ; famous(X).
Sometimes it is necessary, or at least advisable,
to wrap parentheses ( ) around a
disjunction in order to make clear what how the
conjunctions and disjunctions interact. If you
leave out the parentheses, Prolog has its own
rules about the precedence
of logical-and and logical-or, which might or might
not correspond to what you intend. If you use
parentheses, the things inside the parentheses are done
first, so everything is crystal clear.
Option 3:
happy3(X) :- attractive(X), ( rich(X) ; famous(X) ).This says
Xishappy3ifXis attractive, andXis either rich or famous:
happy3(X) ⇐ attractive(X) ∧ (rich(X) ∨ famous(X)).*
write(X) which writes the term
X to the current output stream (which means the window on your workstation
unless you have done something fancy).
print(X, …) which writes a variable number of arguments to
the current output stream. If an argument is a string (like
'Hello world!\n') then the string is printed without quotes, and any
'\n' and '\t' are interpreted as newline and tab,
respectively.
A newline is printed at the end of the printing operation.
prin(X, …) is like print except that it does not
append a newline to the end of the output.
tab(N) prints N spaces to output -
N should be a number.
nl starts a new line.
put(C) prints a single character to the current output
stream. C might be bound to a number in the range 0 to 255, in which
case the number is treated as a character code, and the equivalent
character is printed, or it might be bound to a character, e.g.
C = 'a', in which case obviously the character is
printed.
Here, two clauses together define the condition for someone to be a parent - together they form a procedure.is_a_parent(Parent) :- father_of(Parent, _Child). is_a_parent(Parent) :- mother_of(Parent, _Child).
See also underscore for the reason for
putting an underscore (_) character at the front of the
variable _Child.
?- prompt, separated by commas,
and terminated with a full stop (.). For example,?- lectures(john, Subject), studies(Student, Subject).
If the query fails, Prolog types "fail.". If it succeeds,
Prolog either types the list of variable
bindings it had to assume in order to make
the query succeed, or, if no variable bindings were necessary, it
types "true.".
If several variable bindings allow the query to succeed, then
normally (i.e. in the absence of cuts) all the
bindings will be typed out, one after the other, with the Prolog
user typing a ; to let the Prolog system know that they
are ready to see the next set of bindings.
member procedure:The second clause is clearly recursive -member(Item, [Item|Rest]).
member(Item, [_|Rest]) :- member(Item, Rest).
member
occurs both in the head and the body. A recursive procedure can work
because of two things:
member) in the body
is in some way simpler than that in the head, so that Prolog is being
asked to solve a simpler problem. (In member, the list that
is the argument to member in the body is shorter by one than
that in the head.)
member, the first clause is the trivial branch - it says
that member succeeds if Item is the first member
of the list that is the second argument.)
A recursive data structure is one where the structures
include substructures whose principle functor
is the same as that of the whole structure. For example, the tree
structure:
tree(tree(empty, fred, empty), john, tree(empty, mary, empty))
is recursive. Recursive data structures require recursive procedures to process
them.
It can help, in understanding recursion, to separate
the different depths of recursive invocation
of Prolog rules by drawing boxes around the parts that correspond
to a particular invocation, and giving separate (but systematic)
names to the variables in each invocation. So if the rule involves
a variable X, then in the first invocation, we refer
to X as X1, while in the second (recursive)
invocation, we refer to the new X as X2,
and so on.
Let's try this out with the recursive procedure sumlist:
% sumlist(NumList, SumOfList) - binds SumOfList to the sum % of the list NumList of numbers. % Rule 1: sumlist([], 0). % Rule 2: sumlist([First | Rest], Sum) :- sumlist(Rest, SumOfRest), % Goal 2.1 Sum is First + SumOfRest. % Goal 2.2and the query
?- sumlist([5, 2, 1], Answer).
sumlist[5, 2, 1], Answer).Choose Rule: only rule 2 matches, with First1 = 5; Rest1 = [2, 1]; Sum1 = AnswerGoal 2.1: sumlist(Rest1, SumOfRest1),
Sum1 is First1 + SumOfRest1.
|
Try doing a Prolog trace of the same query and procedure, and compare the results.
See also mutual recursion.
is_dog(fido). is_dog(rex). is_dog(rover).
A binary relation is a set of pairs all of which are related in the same way. Example:
eats(fido, biscuits). eats(rex, chocolate). eats(rover, cheese). eats(lassie, bone).
Similarly for ternary relations (triples) and so on.
In the examples above, is_dog and eats are
the functors for the relations. Prolog stores
information in the form of relations, or more specifically
relational instances, like those above, and also in the form of
rules.
It is also possible in Prolog to have nullary relations (a relation with no arguments), though they are perhaps not often used. Example:
program_untested.
This could be used as a goal in a query:
?- program_untested. true.
Possibly a better way to do this would be to use a unary relation:
program_status(untested).
However, the point is that nullary relations are there in Prolog, should you find a use for them.
See also the the next entry for more detail.
In Prolog, instead, by default we use prefix notation:
smaller(mouse, rabbit). See op
if you want to define an infix operator in a Prolog program.
This view of a relation is sometimes called the extensional view - you can lay out the full "extent" of the relation, at least in the case of a relation over a finite number of items, like our "smaller" example. The alternative view is called intensional, where we focus on the meaning of the relation. In Prolog, this corresponds to a rule expressing the relation, such as:
smaller(X, Y) :-
volume(X, XVolume),
volume(Y, YVolume),
XVolume < YVolume.
This, of course, only defines a meaning or intension for smaller
relative to the unspecified meaning of the relation volume(_,_),
and the meaning of <, which is defined by the Prolog
implementation.
When we come to non-binary relations, such as unary ones
(like is_a_dog(fido)) or ternary ones (like
gives(john, mary, book) or
between(rock, john, hard_place)) the infix
relation doesn't make any sense, so prefix notation is
normal. (Postfix notation - fido is_a_dog
can work for unary relations.)
While we often don't think of it this way, a function is a kind of relation. If you are thinking about the function f(x) = x2, then the essential thing is that for each value of x, there is a value of f(x) (namely x2). In fact, there is a unique value of f(x). So a function (of a single variable) can be viewed as a binary relation such that for every relevant first component x, there is exactly one pair in the relation that has that first component1: the pair is (x, f(x)). In the case of f(x) = x2, the pairs are (x, x2) for every applicable value of x. We need to specify what the applicable values of x are - the domain of the function. If the domain is {1, 2, 3}, then the pairs are {(1,1), (2,4), (3,9)}. If the domain is all natural numbers, then we can't write out the extension of the function in full, but we can use a set expression such as {(n, n×n) | n ∈ N} where N signifies the set of all natural numbers.
In Prolog, despite the fact that it uses a
relational notation (except for arithmetic expressions), functions
are common, but expressed using the relational notation that depends
on the definition/convention in the previous paragraph. In our Prolog
code defining smaller(X, Y), above, the relation called
volume is in fact a function written relationally. We
are saying that for every relevant object X there
is a value XVolume that is the volume of X.
This is a function-type relationship.
The notion of domain applies to relations as well as functions: a more detailed definition of a binary relation on a set A says that such a relation is a subset of the set A×A of all pairs of elements of A. A is the domain of the relation. A binary relation between sets A and B is a subset of A×B. And so on for ternary relations and beyond. A unary relation on A is just a subset of A. Thus is_a_dog is a subset of the set of all Animals. Or a subset of the set of all Things, depending on just how broadly you want to consider the concept of being a dog.
However, in Prolog, domains of relations are not explicitly addressed.2 The notion of domain corresponds exactly to that of type in typed programming languages like, C, C++, Modula-2, Pascal, etc. Prolog has no built-in way of confining the definition of a relation (whether extensionally or by a rule) to a particular domain. If you want to, you can build type-checking into your rules, e.g. by giving an definition of a type as a unary relation. For example, if you wanted to define the type of all popes, you could enumerate them all, though the list would be long:
Then the goalpope(peter). … pope(john_paul_ii). pope(benedict_xvi).
pope(X) would check if X was a pope.
Another example: when defining the relation sister in
Prolog, you would usually write something like:
sister(X, Y) :-
female(X), female(Y),
mother(X, Mother), mother(Y, Mother),
father(X, Father), father(Y, Father),
X \== Y.
female(X) and female(Y) are type-checks
- without them, you are defining sibling, not
sister. In practice, you can't enumerate all females, unlike
all popes, but in many cases you would be able to enumerate all the relevant
ones.
In some cases, you might be able to write rules to do type-checking.
Prolog includes some built-in predicates for type-checking:
number(X) succeeds if X is a number,
while integer(X) succeeds only if X is a
an integral (whole) number. Here is a rule to check if a number
is divisible by 3.
div_by_3(X) :-
X mod 3 =:= 0.
There is no special syntax for creating functions in Prolog (though there are special arrangements for built-in mathematical functions). To create a function in Prolog, you have to use the relation syntax and create a "relation that