Prolog Programming for AI - Chapter 6 Notes and Exercises

Summary

  • Input and output is done using built-in procedures.

  • Files are sequential. There are two types of streams:

    • There is the current input stream and the current output stream.
    • The user terminal is treated as a file called user.
  • Switching between streams is done by:

    • see(File) File becomes the current input stream
    • tell(File) File becomes the current output stream
    • seen close the current input stream
    • told close the current output stream
  • Files are read and written in two ways:

    • as sequences of characters
    • 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
  • The procedure name(Atom, CodeList) decomposes and constructs atoms.

    • CodeList is the list of ASCII codes of the characters in Atom.

Communication with files

At any time during the execution of a Prolog program, only two files are ‘active’: one for input and one for output. These two files are called the current input stream and the current output stream.

At the beginning of execution these two streams correspond to the user’s terminal, and then the current input stream can be changed to another file. The current output stream can also be changed to another file.

Example

1
2
3
4
5
...
see(file1),
read_from_file(Information),
see(user),
...
1
2
3
4
5
...
tell(file3),
write_on_file(Information),
tell(user),
...

Processing files of terms

Read and write

  • read(X)
    • used for reading terms from the current input stream
    • if read(X) is executed when the end of the current input file has been reached then X will become instantiated to the atom end_of_file
  • write(X)
    • will output term X on the current output file
  • tab(X)
    • inserts spaces into the output stream
  • nl

    • causes the start of a new line at output
  • examples

1
2
cube(N, C) :-
C is N * N * N.
1
2
3
4
5
6
7
8
9
10
cube :-
read(X),
process(X).

process(stop) :-
!.
process(N) :-
C is N * N * N,
write(C),
cube.
1
2
3
4
5
6
7
8
9
10
11
cube :-
write('Next item, please:'),
read(X),
process(X).

process(stop) :- !.
process(N) :-
C is N * N * N,
write('Cube of'), write(N), write('is'),
write(C), nl,
cube.

Displaying lists

1
2
3
4
5
6
% this procedure will print each element of L on a separate line
writelist([]).
writelist([X|L]) :-
write(X),
nl,
writelist(L).
1
2
3
4
5
6
7
8
9
10
11
12
% this procedure will write the elements of each list in one line
writelist([]).
writelist([L | LL]) :-
doline(L),
nl,
writelist(LL).

doline([]).
doline([X|L]) :-
write(X),
tab(1),
doline(L).
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
% this procedure will produce a bar graph, assuming that the numbers in the list are between 0 and 80.

% ?- bars([3, 4, 6, 5]).
% ***
% ****
% ******
% *****

bars([N | L]) :-
stars(N),
nl,
bars(L).

stars(N) :-
N > 0,
write('*'),
N1 is N - 1,
stars(N1).
stars(N) :-
N =< 0.

Formatting terms

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
writefamily(family(Husband, Wife, Children)) :-
nl, write(parents), nl, nl,
writeperson(Husband), nl,
writeperson(Wife), nl,
write(children), nl, nl,
writepersonlist(Children).

writeperson(person(Firstname, Secname, date(D, M, Y), Work)) :-
tab(4), write(Firstname),
tab(1), write(Secname),
write(', born'),
write(D), tab(1),
write(M), tab(1),
write(Y), write(','),
writework(Work).

writepersonlist([]).
writepersonlist([X | L]) :-
writeperson(X),
nl,
writepersonlist(L).

writework(unemployed) :-
write(unemployed).

writework(works(Comp, Sal)) :-
write('works '), write(Comp),
write(', salary '), write(Sal).

?- writefamily(family(person(tom, fox, date(7,may, 1950), works(bbc, 15200)), person(ann, fox, date(9, may, 195l), unemployed), [person(pat, fox, date(S, may, 1973), unemployed), person(jim, fox, date(S, may, 1973), unemployed)])
parents

tom fox, born 7 may 1950,worksbbc,salary15200
ann fox, born 9 may 1951,unemployed

children

pat fox, born 5 may 1973,unemployed
jim fox, born 5 may 1973,unemployed

Processing a file of terms

A typical sequence of goals to process a whole file, F, would look something like this:

1
2
3
4
5
...,
see(F),
processfile,
see(user),
...

A typical schema for processfile is:

1
2
3
4
5
6
7
8
processfile :-
read(Term),
process(Term).

process(end_of_files) :- !.
process(Term) :-
treat(Term),
processfile.

Here treat(Term) represents whatever is to be done with each term.

Exercises

  1. Let f be a file of terms. Define a procedure findterm(Term) that displays on the terminal the first term in f that matches Term
  2. Let f be a file of terms. Write a procedure findallterms(Term) that displays on the terminal all the terms in f that match Term. Make sure that Term is not instantiated in the process (which could prevent its with terms that occur later in the file).

Manipulating characters

Exercise

  1. Generalize the squeeze procedure to handle commas as well. All blanks imediately preceding a comma are to be removed, and we want to have one blank after each comma.

Constructing and decomposing atoms

Exercise

Reading programs: consult, reconsult

We tell Prolog to read a program from a file F with the goal:

1
?- consult(F).
  • The effect will be that all clauses in F are read and will be used by Prolog when answering further questions from the user.
  • If another filed is ‘consulted’ at some later time during the same session, clauses from this new file are simply added at the end of the current set of clauses.
  • We don’t have to enter our program into a file and then request ‘consulting’ that file. Instead of reading a file, Prolog can also accept our program directly from the terminal, which corresponds to the pseudo-file user.

The built-in predicate reconsult is similar to consult. A goal:

1
?- reconsult(F).

will have the same effect at Consult(F) with one exception.

  • If there are clauses in F about a relation that has been previously defined, the old definition will be superseded by the new clauses about this relation in F.

The difference betwen consult and reconsult is that:

  • consult always adds new clauses
  • reconsult redefines previously defined relations. (reconsult(F) will not affect any relation about which there is no clause in F.)

Tricks

How to add new predicates from terminal?

We can use [user]. or consult(user). to enter new program clauses from the terminal. To exit this mode, press Ctrl +D . Then Prolog will parse our predicates and make them accessible.

Vocabularies