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.
*Strictly speaking, input.dat
will be expected to be in the current working directory of the command interpreter that started Prolog. The command interpreter will be running on the workstation/computer, and sending output to a window on that workstation or computer.
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/1
*. 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 workstation* 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 either you do not have permission to write files in the directory /usr/bin
, or if the file ztrash
already exists in this directory, that you do not have permission to write to that file.
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.
# Strictly speaking, output.dat
will be expected to be in the current working directory of the command interpreter that started Prolog. The command interpreter will be running on the workstation/computer, and sending output to a window on that workstation or computer.
!
, 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 ;
false.
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).
false.
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 ;
false.
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 ;
false.
?-
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.
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, !.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, Y).
max(X, Y, X) :- 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
max(X, Y, Y) :- X =< Y.
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 will 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 suitabledynamic
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.