In this chapter, we will see some techniques to handle inputs and outputs through prolog. We will use some built in predicates to do these tasks, and also see file handling techniques.
Following topics will be discussed in detail −
Handling inputs and outputs
File handling using Prolog
Using some external file to read lines and terms
Character manipulation for input and output
Constructing and decomposing atoms
Consulting prolog files into other prolog program techniques.
So far we have seen that we can write a program and the query on the console to execute. In some cases, we print something on the console, that are written in our prolog code. So here we will see that writing and reading tasks in more detail using prolog. So this will be the input and output handling techniques.
To write the output we can use the write() predicate. This predicate takes the parameter as input, and writes the content into the console by default. write() can also write in files. Let us see some examples of write() function.
| ?- write(56). 56 yes | ?- write('hello'). hello yes | ?- write('hello'),nl,write('world'). hello world yes | ?- write("ABCDE") . [65,66,67,68,69] yes
From the above example, we can see that the write() predicate can write the contents into the console. We can use ’nl’ to create a new line. And from this example, it is clear that, if we want to print some string on the console, we have to use single quotes (‘string‘). But if we use double quote (“string”), then it will return a list of ASCII values.
The read() predicate is used to read from console. User can write something in the console, that can be taken as input and process it. The read() is generally used to read from console, but this can also be used to read from files. Now let us see one example to see how read() works.
cube :- write('Write a number: '), read(Number), process(Number). process(stop) :- !. process(Number) :- C is Number * Number * Number, write('Cube of '),write(Number),write(': '),write(C),nl, cube.
| ?- [read_write]. compiling D:/TP Prolog/Sample_Codes/read_write.pl for byte code... D:/TP Prolog/Sample_Codes/read_write.pl compiled, 9 lines read - 1226 bytes written, 12 ms (15 ms) yes | ?- cube. Write a number: 2. Cube of 2: 8 Write a number: 10. Cube of 10: 1000 Write a number: 12. Cube of 12: 1728 Write a number: 8. Cube of 8: 512 Write a number: stop . (31 ms) yes | ?-
The tab() is one additional predicate that can be used to put some blank-spaces while we write something. So it takes a number as an argument, and prints those many number of blank spaces.
| ?- write('hello'),tab(15),write('world'). hello world yes | ?- write('We'),tab(5),write('will'),tab(5),write('use'),tab(5),write('tabs'). We will use tabs yes | ?-
In this section, we will see how we can use files to read from, and write into the files. There are some built-in predicates, that can be used to read from file and write into it.
If we want to write into a file, except the console, we can write the tell() predicate. This tell()predicate takes filename as argument. If that file is not present, then create a new file, and write into it. That file will be opened until we write the told command. We can open more than one file using tell(). When told is called, all files will be closed.
| ?- told('myFile.txt'). uncaught exception: error(existence_error(procedure,told/1),top_level/0) | ?- told("myFile.txt"). uncaught exception: error(existence_error(procedure,told/1),top_level/0) | ?- tell('myFile.txt'). yes | ?- tell('myFile.txt'). yes | ?- write('Hello World'). yes | ?- write(' Writing into a file'),tab(5),write('myFile.txt'),nl. yes | ?- write("Write some ASCII values"). yes | ?- told. yes | ?-
Hello World Writing into a file myFile.txt [87,114,105,116,101,32,115,111,109,101,32,65,83,67,73,73,32,118,97,108,117,101,115]
Similarly, we can also read from files. Let us see some example of reading from file.
When we want to read from file, not from the keyboard, we have to change current input stream. So we can use see() predicate. This will take filename as input. When the read operation is completed, then we will use seen command.
likes(lili, cat). likes(jhon,dog).
| ?- see('sample_predicate.txt'), read(X), read(Y), seen, read(Z). the_end. X = end_of_file Y = end_of_file Z = the_end yes | ?-
So from this example, we can see that using the see() predicate we can read from the file. Now after using seen command, the control transfers to the console again. So finally it takes input from console.
We have seen how to read specific contents (few lines) of a file. Now if we want to read/process all the contents of a file, we need to write a clause to process file (process_file), until we reach the end of the file.
process_file :- read(Line), Line \== end_of_file, % when Line is not not end of file, call process. process(Line). process_file :- !. % use cut to stop backtracking process(Line):- %this will print the line into the console write(Line),nl, process_file.
likes(lili, cat). likes(jhon,dog). domestic(dog). domestic(cat).
| ?- [process_file]. compiling D:/TP Prolog/Sample_Codes/process_file.pl for byte code... D:/TP Prolog/Sample_Codes/process_file.pl compiled, 9 lines read - 774 bytes written, 23 ms yes | ?- see('sample_predicate.txt'), process_file, seen. likes(lili,cat) likes(jhon,dog) domestic(dog) domestic(cat) true ? (15 ms) yes | ?-
Using read() and write() we can read or write the value of atoms, predicates, strings, etc. Now in this section we will see how to write single characters into the current output stream, or how to read from current input stream. So there are some predefined predicates to do these tasks.
We can use put(C) to write one character at a time into the current output stream. The output stream can be a file or the console. This C can be a character or an ASCII code in other version of Prolog like SWI prolog, but in GNU prolog, it supports only the ASCII value. To use the character instead of ASCII, we can use put_char(C).
| ?- put(97),put(98),put(99),put(100),put(101). abcde yes | ?- put(97),put(66),put(99),put(100),put(101). aBcde (15 ms) yes | ?- put(65),put(66),put(99),put(100),put(101). ABcde yes | ?-put_char('h'),put_char('e'),put_char('l'),put_char('l'),put_char('o'). hello yes | ?-
To read a single character from the current input stream, we can use the get_char(C) predicate. This will take the character. if we want the ASCII code, we can use get_code(C).
| ?- get_char(X). A. X = 'A' yes uncaught exception: error(syntax_error('user_input:6 (char:689) expression expected'),read_term/3) | ?- get_code(X). A. X = 65 yes uncaught exception: error(syntax_error('user_input:7 (char:14) expression expected'),read_term/3) | ?-
The atom constructing means from a list of characters, we can make one atom, or from a list of ASCII values also we can make atoms. To do this, we have to use atom_chars() and atom_codes() predicates. In both cases, the first argument will be one variable, and the second argument will be a list. So atom_chars() constructs atom from characters, but atom_codes() construct atoms from ASCII sequence.
| ?- atom_chars(X, ['t','i','g','e','r']). X = tiger yes | ?- atom_chars(A, ['t','o','m']). A = tom yes | ?- atom_codes(X, [97,98,99,100,101]). X = abcde yes | ?- atom_codes(A, [97,98,99]). A = abc yes | ?-
The atom decomposing means from an atom, we can get a sequence of characters, or a sequence ASCII codes. To do this, we have to use the same atom_chars() and atom_codes() predicates. But one difference is that, in both cases, the first argument will be one atom, and the second argument will be a variable. So atom_chars() decomposes atom to characters, but atom_codes() decomposes atoms to ASCII sequence.
| ?- atom_chars(tiger,X). X = [t,i,g,e,r] yes | ?- atom_chars(tom,A). A = [t,o,m] yes | ?- atom_codes(tiger,X). X = [116,105,103,101,114] yes | ?- atom_codes(tom,A). A = [116,111,109] (16 ms) yes | ?-
The consulting is a technique, that is used to merge the predicates from different files. We can use the consult() predicate, and pass the filename to attach the predicates. Let us see one example program to understand this concept.
Suppose we have two files, namely, prog1.pl and prog2.pl.
likes(mary,cat). likes(joy,rabbit). likes(tim,duck).
| ?- [prog1]. compiling D:/TP Prolog/Sample_Codes/prog1.pl for byte code... D:/TP Prolog/Sample_Codes/prog1.pl compiled, 2 lines read - 443 bytes written, 23 ms yes | ?- likes(joy,rabbit). yes | ?- likes(suman,mouse). no | ?- consult('prog2.pl'). compiling D:/TP Prolog/Sample_Codes/prog2.pl for byte code... D:/TP Prolog/Sample_Codes/prog2.pl compiled, 1 lines read - 366 bytes written, 20 ms warning: D:/TP Prolog/Sample_Codes/prog2.pl:1: redefining procedure likes/2 D:/TP Prolog/Sample_Codes/prog1.pl:1: previous definition yes | ?- likes(suman,mouse). yes | ?- likes(joy,rabbit). no | ?-
Now from this output we can understand that this is not as simple as it seems. If two files have completely different clauses, then it will work fine. But if there are same predicates, then while we try to consult the file, it will check the predicates from the second file, when it finds some match, it simply deletes all of the entry of the same predicates from the local database, then load them again from the second file.