[Previous] [Up] [Next]
Go backward to Introduction
Go up to Input, Output and Control Features
Go forward to Control Functions

Input/Output

Input and Output

INPUT and OUTPUT are two very common terms when talking about using computers. What we mean by INPUT is the information given to a program (be it an operating system, an application such as a word processing program, or a program you write yourself). OUTPUT concerns what the computer produces as a result of running the program. Slightly confusingly, computer users often refer to the input and output channels as "input" and "output" as well.

Typically, you give input to a program from the terminal (what you type on the keyboard) or from a file. The former is often referred to as STANDARD INPUT (in the input channel sense). For instance, when you type commands to Unix or to SICStus at the top level prompt you use the standard input. When you ask the Prolog compiler to consult a file, your input channel is a file.

Output from programs is typically sent to the terminal (displayed on the screen) but you can also send output to a file, or to another command. This is what you do in Unix when you use the > command to redirect the output of a command to a file, or the pipe | command to send the output of a command into another command (more for instance).

So far, when you have run your Prolog programs, you have only used the terminal for input and output. But there are ways of telling Prolog to take input from a file, or to send the output to one. This is useful for instance when you want to test a program, or if it generates a set of data that you want to analyse later.

Term and Character I/O

We can read input and write output in chunks of two sizes: TERMS and CHARACTERS. A TERM is a term in the ordinary Prolog sense, except that for input purposes it is typically delimited by a full stop. A character is an ASCII character. The predicates used for these purposes are illustrated in the table below.


Input Output
Term read/1 write/1 Character get0/1 put/1 get/1
These predicates are a bit different from ordinary ones. Ordinary Prolog predicates are declarative: they never do anything, they only verify that terms are compatible. Whatever effect they have is restricted to their own arguments. The input/output predicates, however, are not declarative--you call them, and they actually do something, and then vanish. All you are left with is the result of calling them.

In fact, most programming languages work this way. You can think about them as a vending machine. You put money in (the call) and you get a packet of cigarettes back, but you have lost the money. Prolog, on the other hand, normally behaves like a ticket stamping machine. You put the ticket in and you get it back with something written on it.

What happens when we use write/1 and read/1? The former is relatively easy to understand: the result simply appears on the terminal:

| ?- write(hello).
hello
yes
| ?- write('hello world').
hello world
yes
| ?- X=hello, write(X).
hello
X = hello ? 
yes
This example shows how different Prolog is! In every other programming language, the first program you write is the "Hello World"--program. Here, it only appears well into the course.

If write/1 is to print a longer string with blanks in it, the string must be quoted as in the second example. The same goes if the string contains anything that Prolog can interpret as an operator. If you want to write a string which is a combination of known text and instantiated variables, you have to separate the calls to write:

| ?- X = 2, write('The value is '), write(X), write('.').
The value is 2.
X = 2 ? 
yes
If write's argument is uninstantiated, it will just write the meaningless identifier for the variable concerned--something like _32547 or _1.

With read/1, Prolog goes somewhere else (by default to the terminal, in which case the |: prompt appears (also by default) to tell the user where to type the term) to look for something that ends in a full stop. Whatever it finds is unified with the variable you give it:

| ?- read(X).
|: 'hello ana'.
'hello ana'.
X = 'hello ana' ? 
yes
As with write, if you want to input a term with spaces from the terminal, you must quote it. You can use the value of what Prolog reads in (X in the example above) in a later goal, e.g.
 | ?- read(X),name(X,L).
|: hello.
hello.
L = [104,101,108,108,111],
X = hello ? 
yes
put and get0 are the same as write and read, except that they write or read single ASCII characters. Their argument is a list of a single ASCII number. get0 reads everything, while get binds its argument to the next printing character, skipping over spaces, newlines and control characters found in the input stream.

Another procedure useful in producing output is nl/0. This throws a new line, and always succeeds exactly once. The misnamed tab/1, where the single argument is an integer. tab(3) prints 3 spaces.

Examples

Writing Out A Message

The following DIRECTIVE at the beginning of a program would write out the program introduction at the beginning of this section:

:- write('This is MEGAPARSE (c) Vi Otter, an amazing natural language'),
   nl,
   write('parser which caters for every flavour of syntactic religion.'),
   nl,
   write('Use the query '),
   nl,
   write('| ?- parse(<sentence>).'),
   nl,
   write('where <sentence> is a sentence enclosed by single quotes, e.g.'),
   nl,
   write('| ?- parse('The otter chases the cat').'),
   nl.
(A directive is like a query--it has no head, which means that the head always matches (so to speak) and hence that the subgoals are executed.)

A Graphical Representation of Sentence Structure

We can use also use the input/output commands to produce a more (though not very) graphical representation of the structure of a sentence. Starting with the bracketed output of the grammar, we could write a procedure drawtree/1 to produce the following:

| ?- drawtree(s(np(det(every),n(dog)),vp(verb(likes),np(det(a),n(bone)))).
 
s 
   np 
      det 
         every 
      n 
         dog 
   vp 
         verb 
            likes 
         np 
            det 
               a 
            n 
               bone
 
yes
That can be achieved with the following code:
drawtree(Struct):- 
        drawtree(Struct,0).
 
drawtree(Struct,N):-
        Struct =.. [Mother|Daughters],
        write_out(Mother,N),
        NewN is N + 4,
        drawtrees(Daughters,NewN).
 
drawtrees([],_N):- !.
drawtrees([FirstDaughter|MoreDaughters],N):-
        drawtree(FirstDaughter,N),
        drawtrees(MoreDaughters,N).
 
write_out(Symbol,N):-
        nl, tab(N), write(Symbol).
This code is available as ~avk/Temp/drawtree.pl. As an exercise, you can modify it to display the tree with a different type of alignment (you can use the output of a Prolog program as input to a program which displays trees using "real" graphics too--this is what you would do if you used Prolog in a serious programming project).

File I/O

By default, input is taken from the keyboard and output is sent to the screen. These constitute a virtual file called user (you can read more about this in the manual). We can also handle other files using predefined commands to

These predicates are shown in the table below.

Input File Output File
Determine current seeing/1 telling/1 Change to new see/1 tell/1 Close current seen/0 told/0
  If you want to save the output of a program to a file, you can use tell/1 and told/0. Say that you want to analyse the data generated by the file mary.pl, and save the output to mary.dat. Then you can use the following (the command which generates the data is called run):
| ?- consult([mary]).
{consulting /homedir/mary/Prolog/mary.pl...}
{/homedir/mary/Prolog/mary.pl consulted, 150 msec 13776 bytes}
yes
| ?- tell('mary.txt'), run, told.
yes

<a.von.klopp@bangor.ac.uk>

[Previous] [Up] [Next]