Chapter 7
Input and Output in Prolog
Prof. N. G. J. Dias
Senior Professor
Department of Computer Systems Engineering,
Faculty of Computing and Technology,
University of Kelaniya,
Kelaniya
7.1 Introduction
• Every computing application needs input data which can be processed within the
system to get the required output.
• In this chapter we will investigate some build-in facilities for reading data from
computer files and for outputting data to files.
16 May 2025 NGJDias@FCT-UoK 2
7.2 Communication with files
• We will first consider the question of directing input and output to files, and then
how data can be input and output in different forms.
• The following figure shows the general situation in which a Prolog program
communicates with several files.
User
terminal
User User
Input File 1 File 3 Output
Streams File 2 File 4 Streams
16 May 2025 NGJDias@FCT-UoK 3
• The program can, in principle, read data from several input files also called input
streams and output data to several output files, also called output streams.
• Data coming from the user’s terminal is treated as just another input stream.
• Data output to the terminal is analogously treated as another output stream.
• Both of these ’pseudo-files’ are referred by the name user.
• The names of the files can be chosen by the programmer according to rule for
naming files in the computer system used.
• At any time, during the execution of a Prolog program, only two files are active:
one for input and one for output.
• The two files are called the current input stream and current output stream
respectively.
• At the beginning of execution these two streams corresponds to the user terminal
(keyboard and display).
16 May 2025 NGJDias@FCT-UoK 4
• The build-in predicates,
see, seeing and seen
are used to communicate with input files.
• The current input stream can be changed to another file, filename, by the goal
see(filename).
• Such a goal succeeds (unless there is something wrong with file name) and
causes, as side effect, that input to be switched from the previous input stream to
filename.
• The goal
seeing (File)
causes the variable File to be unified with the (name of the) current input file and
the goal
seen
closes the current input file.
16 May 2025 NGJDias@FCT-UoK 5
• The build in predicates
tell, telling and told
are used to communicate with output files.
• The current output stream can be changed by a goal of the form
tell(filename).
• The goal
telling(File).
causes the variable File to be unified with the (name of file) current output
stream and the goal
told
closes the current output file.
16 May 2025 NGJDias@FCT-UoK 6
• So a typical example of using the see predicate is the following sequence of goals,
which reads something from file1 and then switches back to the terminal:
…
see( file1),
read_from_file(Information),
see( user),
…
• A sequence of goals to output some information to file3, and then redirect
succeeding output back to the terminal, is:
…
tell(file3),
write_on_file(Information),
tell(user),
…
16 May 2025 NGJDias@FCT-UoK 7
• We will assume here that files can only be processed sequentially although many
Prolog implementations also handle files with random access.
• In sequential files, each request to read something from an input file will cause
reading at the current position in the current input stream.
• After the reading, the current position will be, of course, moved to the next unread
item.
So the next request for reading will start reading at this new current position.
• If a request for reading is made at the end of a file, then the information returned
by such a request is the atom end_of_file.
• Once some information has been read, it is not possible to reread it again.
• Writing is similar; each request to output information will append this information
at the end of the current output stream.
• It is not possible to move backward and to overwrite part of the file.
16 May 2025 NGJDias@FCT-UoK 8
• Sequential files are read and written in two ways:
(a) as a sequence of characters
In this case a single character is considered as the basic element of the file.
(b) as a sequence of terms
In this case a bigger unit of information, i.e., a Prolog term is considered as the
basic building block of the file.
16 May 2025 NGJDias@FCT-UoK 9
7.3 Processing Files of Terms
7.3.1 Read and Write
• The built-in predicate ‘read’ is used for reading terms from the current input
stream.
• The goal
read(X)
will cause the next term, T, to be read, and this term will be matched with X.
• If X is a variable then, as a result, X will become instantiated to T.
• If matching does not succeed then the goal read(X) fails.
• The predicate ‘read’ is deterministic, so in the case of failure there will be no
backtracking to input another term.
• Each term in the input file must be followed by a full stop and a space or carriage-
return.
16 May 2025 NGJDias@FCT-UoK 10
• If read(X) is executed when the end of the input has been reached, then X will
become instantiated to the atom end-of-file.
Example
The following terms can be read into the variable X:
23 % An integer
23.56 % A real number
[2, 4, 5, 6] % A list of numbers
tom % A string (atom)
[tom, pat, ann] % A list of names
[[1, 2, 3], [tom, pat, ann]] % A list of lists
16 May 2025 NGJDias@FCT-UoK 11
• The build-in predicate ‘write’ is used for outputting terms to the current output
stream.
• The goal
write( X).
will output the term ‘ M’ to the current output file, if X is a variable which has been
instantiated to M.
• There are additional build-in predicates ‘tab’ and ‘nl’ for formatting the output.
• The goal
tab(N)
causes N spaces (blanks) to be output, if N instantiated to an integer.
• The predicate ‘nl’ (which has no arguments) causes of start of a new line at output.
16 May 2025 NGJDias@FCT-UoK 12
7.3.2 Examples
• The following examples will illustrate the use of the predicates defined in section
7.3.1.
Example 1
Consider the following procedure for compute the cube of a number.
cube(N,C) :- C is N*N*N.
Suppose we want to use this for calculating cube of a sequence of numbers, we
could do this by a sequence of questions:
|?_cube(2,X).
X=8
|?_cube(5,Y).
Y=125
|?_cube(12,Z).
Z=1728
For each number, we had to type in the corresponding goal.
16 May 2025 NGJDias@FCT-UoK 13
Example 2
Let us now modify the above program so that the ‘cube’ procedure will read the
data itself. Now the program will keep reading data and outputting their cubes until
the atom ‘stop’ is read.
cube:-read(X),process(X).
process(stop):-!.
process(N):-C is N*N*N,
write(C),
cube.
According to this program, to execute ‘cube’, first read X and then process it; if
X=stop then everything has been done, otherwise write the cube of X and
recursively call the ‘cube’ procedure to process further data.
16 May 2025 NGJDias@FCT-UoK 14
A table of cubes of numbers can be produced using this new procedure as follows:
|?_cube.
2.
8
5.
125
12.
1728
stop.
yes
The number 2, 5 and 12 were typed in by the user on the terminal; the other
numbers were output by the program. Note that each number entered by the user
had to be followed by a full stop, which signals the end of a term.
16 May 2025 NGJDias@FCT-UoK 15
Example 3
We can modified our procedure in Example 2 interactively as follows:
cube:-write(‘Next item, please’),
read(X),
process(X).
process(stop):- !.
process(X):-C is X*X*X,
write(‘Cube of ’),write(X),write(‘is’),write(C),nl,
cube.
16 May 2025 NGJDias@FCT-UoK 16
A conversation with this new version of cube would then be, for example, as
follows:
|?_cube.
Next item , please: 5.
Cube of 5 is 125
Next item , please:12.
Cube of 12 is 1728
Next item ,please: stop.
Yes.
Note:
Depending on the implementation, an extra request (like ttypflush, say) after
writing the prompt might be necessary in order to force the prompt to actually
appear on the screen before reading.
16 May 2025 NGJDias@FCT-UoK 17
7.3.3 Displaying Lists
1. In order to write out a list of terms, including quoted comments (which are Prolog
terms), we define a predicate,
writeln(List)
which writes the terms in List to the output stream without any separating
commas or spaces, and then moves to the next line.
writeln([X|L]):-write(X), writeln(L).
writeln([]):-nl.
For example, the goal
|?_writeln([’The next number is ’, X]).
will, if X is instantiated to 100, give the output
The next number is 100.
16 May 2025 NGJDias@FCT-UoK 18
2. The following procedure writelist(L) outputs a list L so that each element of L is
written on a separate line.
writelist([]).
writelist([X|L]):-write(X),nl,writelist(L).
For example the goal
|?_writelist([a,b,c,d]).
will give the output
a
b
c
d.
16 May 2025 NGJDias@FCT-UoK 19
3. If we have a list of lists, one natural output form is to write the elements of each
list in one line. To do this, we define the procedure, writelist2(L) as follows:
writelist2([]).
writelist2([L|LL]):-doline(L),nl,writelist2(LL).
doline([]).
doline([X|L]):-write(X),tab(1),doline(L).
An example of its use is
|?_writelist2([[a,b,c], [d,e,f], [g,h,i]]).
a b c
d e f
g h i
16 May 2025 NGJDias@FCT-UoK 20
4. A list of integer numbers can be sometimes conveniently shown as a bar graph.
The following procedure, bars, will display a list in this form, assuming that the
number in the list are between 0 and 80.
bars([]).
bars([N|L]):-stars(N),nl,bars(L).
stars(N) :- N>0, write(*), N1 is N-1, stars(N1).
stars(N) :- N=<0.
Now the goal,
|?_bars([3,4,6,2]).
will give
***
****
******
**
16 May 2025 NGJDias@FCT-UoK 21
7.3.4 Processing a File
• A typical sequence of goals to process a whole file, F, would look something like
this:
..., see(F ), processfile, see(user),...
Here ‘processfile’ is a procedure to read and process each term in F, one after
another, until the end of the file is encountered.
• A typical schema for ‘processfile’ is :
processfile :-
read( Term),
process(Term).
process( end-of-file) :- !. % All done
process(Term) :-
treat(Term), % Process current item
processfile. % Process rest of file
16 May 2025 NGJDias@FCT-UoK 22
• Here ‘treat(Term)’ represents whatever is to be done with each term.
• An example would be a procedure to display on the terminal each term together
with its consecutive number.
Let us call this procedure showfile. It has to have an additional argument to count
the terms read:
showfile( N) :- read( Term), show( Term, N).
show( end_of_file, _) :- !. % All done
show(Term, N) :-
write( N), tab(2), write(Term),
N1 is N+1,
showfile(N1).
16 May 2025 NGJDias@FCT-UoK 23
7.4 Processing File of Characters
• A character is written on the current output stream with the goal
put(C).
where C is the ASCII code (a number between 0 and 127) of the character to be
output.
For example, the question
|?_put(65),put(66),put(67).
would cause the following output:
ABC
where 65 is the ASCII code of ‘A’, 66 of ‘B’, 67 of ’C’.
16 May 2025 NGJDias@FCT-UoK 24
• A simple character can be read from the current input stream by the goal
get0(C).
This causes the current character to be read from the input stream and the
variable C becomes instantiated to the ASCII code of this character.
• A variation of the predicate ‘get0’ is ‘get’, which is used for reading non-blank
characters.
So the goal,
get(C).
will cause the skipping over all non-printable characters (blanks in particular) from
current input position in the input stream up to the first printable character. This
character is then also read and C is instantiated to its ASCII code.
16 May 2025 NGJDias@FCT-UoK 25
Summary
• Input and output (other than that associated with querying the program) is done
using built-in procedures. This chapter introduced a simple and practical repertoire
of such procedures that can be found in many Prolog implementations.
• This repertoire assumes that files are sequential. There is the current input stream
and the current output stream.
The computer keyboard and display are treated as files called ‘user’.
• Switching between streams is done by
see(File) - File becomes the current output stream
tell(File) - File becomes the current output stream
seen - close the current input stream
told - close the current output stream
16 May 2025 NGJDias@FCT-UoK 26
• Files are read and written in two ways: as sequences of characters or as sequences
of terms.
• Built-in procedures for reading and writing characters and terms are:
read(Term) - input next term
write(Term) - output Term
put(CharCode) - output character with the given ASCII code
get0(CharCode) - input next character
get(CharCode) - input next ‘printable’ character
• Two procedures help formatting:
nl - output new line
tab(N) - output N blanks.
16 May 2025 NGJDias@FCT-UoK 27