Reading from textfile in prolog - prolog

I have some trouble with reading from text files in GNU Prolog. I want to read a .txt file and move the file to a list. I have tried to follow some previous examples on stackoverflow to read the file, but have not been able to access the file (it seems). Here is what I've got so far:
readFile:-
open('text.txt', read, File),
read_lines(File, Lines),
close(File),
write(Lines), nl.
read_lines(File,[]):-
at_end_of_stream(File).
read_lines(File,[X|L]):-
\+ at_end_of_stream(File),
read(File,X),
read_lines(File,L).
When I try to call: ?- readFile. gives me an errormessage: uncaught exception: error(syntax_error('text.txt:2 (char:1) . or operator expected after expression'),read/2).
Thanks in advance!
Edit:
As provided by David, GNU Prolog's Character input/output library and get_char worked for me!
Working code:
readFile:-
open('text.txt', read, File),
read_lines(File, Lines),
close(File),
write(Lines), nl.
read_lines(File,[]):-
at_end_of_stream(File).
read_lines(File,[X|L]):-
\+ at_end_of_stream(File),
get_char(File,X),
read_lines(File,L).

You can improve on your edit answer a bit. First note that the read_line/2 predicate name is misleading. You're reading a text file to a list of characters, not to a list of individual lines. You're also calling the at_end_of_stream/1 predicate twice and creating a spurious choice-point at each call to your read_lines /2 predicate. Moreover, the at_end_of_stream/1 predicate, albeit a standard predicate, doesn't have a reliable implementation in all Prolog systems. A possible rewrite of your code is:
read_file(File, Chars) :-
open(File, read, Stream),
get_char(Stream, Char),
read_file(Stream, Char, Chars),
close(Stream).
read_file(Stream, Char, Chars) :-
( Char == end_of_file ->
Chars = []
; Chars = [Char| Rest],
get_char(Stream, Next),
read_file(Stream, Next, Rest)
).
Now that your intent is clearer, the same functionality can be accomplished using the Logtalk reader library with a single call:
reader::file_to_chars(File, Chars)
But, if learning is is your primary goal, writing your own solutions instead of relying on existing libraries is a good choice.

Related

Prolog - Write out facts and reading a users input

I am quite new to Prolog and have had some trouble understanding it.
I have some facts named 'problem' I wish to first print out these facts to the user and then ask them to input a value, this value is then read and used later.
From my understanding thus far, it would be best to use a forall to print out these facts and then use read to read the value inputted, but I am having some issue implementing this. Here is what I have so far, any explanation would be appreciated
My question: How do I read in the input from the user regarding the problem and apply that into a variable for later use?
tellMeYourProblem:-
forall(problem(P),
writeln(P)),
answer = read(X),
problem('1').
problem('2').
problem('3').
problem('4').
problem('5').
problem('6').
problem('7').
problem('8').
problem('9').
problem('10').
Note: This answer uses SWI-Prolog.
How do I read in the input from the user regarding the problem?
You are doing that already with read(X), however read/1 reads terms (terms end with periods) and you probably want to read characters. If you are using SWI-Prolog take a look at Primitive character I/O for reading characters and Predicates that operate on strings for reading strings.
How do I apply that into a variable for later use?
When doing basic I/O with a user at a text level, a REPL is a good way to start. Adding a REPL is a bit more complicated so I will give you the code.
tellMeYourProblem :-
output_problems,
read_input.
output_problems :-
forall(problem(P),
writeln(P)).
read_input :-
repeat,
read_string(user_input, "\n", "\r\t ", _, Line),
process_input(Line).
process_input(Line) :-
string(Line),
atom_number(Line, N),
integer(N),
do_something_with(Line),
fail.
process_input("quit") :-
write('Finished'), nl,
!, true.
do_something_with(X) :-
writeln(X).
problem('1').
problem('2').
problem('3').
problem('4').
problem('5').
problem('6').
problem('7').
problem('8').
problem('9').
problem('10').
Also with Prolog, the style is to use snake casing so tellMeYourProblem should be tell_me_your_problem.
Normally in Prolog a REPL is done with ->/2, (Read Input till quit statement Prolog) , but I changed this to add more guard statements so that the exit condition would work, e.g.
string(Line),
atom_number(Line, N),
integer(N)
or putting the guard in the head, e.g.
process_input("quit")
When doing I/O to a screen and keyboard, the thought is to use stdIn and stdOut but for the keyboard SWI-Prolog uses user_input instead. See: Input and output
After all of the boiler plate code for the REPL is the next part you seek which is to do something with the input value, in this case just print it out.
do_something_with(X) :-
writeln(X).
The easiest to write out the facts of problem/1,
is to use the builtin listing/[0,1]. This builtin
accepts a so called predicate indicator. You can
write out the facts via:
?- listing(problem/1).
The predicate is supported by many Prolog systems
such as GNU Prolog, etc.. For how to read input see
for example the post by Guy Coder.

Arguments not sufficiently instantiated when consulting file

I'm running SWI-Prolog on a Mac through the Terminal. I'm trying to access an Atom file by writing the usual after opening up swipl in the terminal:
?- [hwk1-my_name].
Instead of swipl having the knowledge base to play with, it's giving me this:
ERROR: Arguments are not sufficiently instantiated
I'm new to Prolog, and my program as it stands now is simply the copied-and-pasted code provided by my professor to get the assignment started. Does this mean that the error is likely due to something within the code below, and if so, what is prompting this? Here is the code provided to me:
father(Dad, Child) :-
parent(Dad, Child),
male(Dad).
mother(Mom, Child) :-
parent(Mom, Child),
female(Mom).
had_a_child(Man, Woman) :-
father(Man, Child),
mother(Woman, Child).
sibling(Sibling1, Sibling2) :-
parent(Parent, Sibling1),
parent(Parent, Sibling2),
Sibling1 \= Sibling2.
brother(Brother, Sib) :-
sibling(Brother, Sib),
male(Brother).
sister(Sister, Sib) :-
sibling(Sister, Sib),
female(Sister).
Your obvious problem is the - inside the file name. The text editor you are using is completely irrelevant. Even confusing, as one of Prolog's data types is the atom.
You have two options:
Use file names that would be valid Prolog atoms even without quoting. This means that they cannot start with a capital or a digit, and can contain only letters, digits, and underscores (_). Then, your file can still have the .pl extension and you can consult it like you do: foo.pl ---> ?- [foo].
Use the complete filename, extension included, and put single quotes around it: foo-bar.baz ---> ?- ['foo-bar.baz'].. As you will see, you don't even need the .pl extension any more.
Whenever you are in doubt about what Prolog sees, you can try write_canonical/1:
?- write_canonical(hwk1-my_name).
-(hwk1, my_name)
true.
In other words, Prolog takes this as the compound term -/2 with the atoms hwk1 and my_name as the first and second argument.

Read the whole fact from external file

According to the below when try to assert the fact I have type error callable expected , I think the insertion o facts line by line happens successfully.But,the asserta does not work well.Despite that,I tried to convert to string using ( string_codes(?String, ?Codes) ) or insert as line of code but it does not success
start:-
writeToFile,
readFromFile,
usereduc(C,D),
writef(C),
writef(D).
writeToFile:-
writef('What is your Name'),nl,
read(Name),
writef('What is your country'),nl,
read(Country),
writef('What is your education'),nl,
read(Education),
open('output.txt',write,Out),
write(Out,usercountry(Name,Country)),nl(Out),
write(Out,usereduc(Name,Education)),
close(Out).
readFromFile:-
open('output.txt',read,In),
repeat,
read_line_to_codes(In,X),nl,
readfactsFromfile(X),asserta(X),
close(In).
readfactsFromfile(end_of_file).
readfactsFromfile(X):-
writef(X),
string_codes(S, X),
asserta(S),!,
fail.
You are trying to write and then read back Prolog terms. For this you should use the combination write_term/3 and read_term/3.
Since read/1 requires you to add a dot to the end of the input term, I have added the option fullstop/1 to write_term/3. The working code then looks as follows:
:- dynamic(usereduc/2).
start:-
writeToFile,
readFromFile,
usereduc(C,D),
writef(C),
writef(D).
writeToFile:-
writef('What is your Name'),nl,
read(Name),
writef('What is your country'),nl,
read(Country),
writef('What is your education'),nl,
read(Education),
setup_call_cleanup(
open('output.txt',write,Out),
(
write_term(Out,usercountry(Name,Country), [fullstop(true)]),nl(Out),
write_term(Out,usereduc(Name,Education), [fullstop(true)])
),
close(Out)
).
readFromFile:-
setup_call_cleanup(
open('output.txt',read,In),
(
repeat,
read_term(In, X, []),
readfactsFromfile(X),asserta(X), !
),
close(In)
).
readfactsFromfile(end_of_file):- !.
readfactsFromfile(X):-
asserta(X),!,
fail.
Notice that I have added the following additional improvements to your code:
* The declaration of usereduc/2 as a dynamic predicate. If this is left out Prolog complains that the predicate does not exists, since it is asserted at run time.
* Removed unwanted determinism using the cut ! at two spots.
* Use of setup_call_cleanup/3 to ensure that opened streams get closed even if the operations performed on the stream are buggy.
Notice that the code is still non-deterministic, giving you the same result twice. This is due to the code asserting the same terms twice.
Hope this helps!
This is a good example where code-injection can be exploited in Prolog without proper care.
My name is 'a,b).\n:- initialization(dobadthings). %'. So output.txt will look like
usercountry(a,b).
:- initialization(dobadthings). %whatevercountry).
userreduc(a,whatevere).
The built-in predicate read/1 accepts full Prolog syntax.
Unfortunately, the analogon to read/1 is not write/1, nor writeq/1 (which is close) but rather:
write_term(T, [quoted(true)]).
Additional options like variable_names/1 may help in a specific situation where variable names should be retained.
Another (potential) problem is the usage of the idiosyncratic writef/1 which seems to be unique to SWI and does some specific interpretation of certain characters. In any case, not a good idea to use. A simple write/1 would be of same value.

Prolog - How to write all prolog answers to .txt file?

man(alan).
man(john).
man(george).
list_all:-
man(X),
write(X),
fail.
Question ?-list_all gives the answer:
alan
john
george
false
So I have all the men from the database. It works! My problem: I want to get the same list, but exported to .txt file. I tried to use this code to do this:
program :-
open('file.txt',write,X),
current_output(CO),
set_output(X),
man(X),
write(X),
fail,
close(X),
set_output(CO).
The effect is: Program gives answer false and text: alan john george are not in .txt file - because of using fail predicate.
Is there an option to get all the items in the list into a .txt file (writing all options which are in database) without using fail predicate?
How can I do this? Please help me.
You're almost there. But the call to fail/0 prevents the stream to be closed. Try for example:
program :-
open('file.txt',write, Stream),
( man(Man), write(Stream, Man), fail
; true
),
close(Stream).
An alternative using the de facto standard forall/2 predicate could be:
program :-
open('file.txt',write, Stream),
forall(man(Man), write(Stream,Man)),
close(Stream).
, , ,

Reading a string (from a file) in Prolog

I have written a lexer and a parser in Prolog. It unifies a string with its AST. This is part for a compiler/interpreter project I am working on. Naturally, I now want to read the string from a file to parse it. However, the predicates I have found for this is read, and it only reads Prolog atoms and predicates, like files with
hello.
I have been twiddling with the double_quotes settings, but with no success.
I want to be able to read a file with something like this
let id = \x.x in id (S (S Z))
and then send this string to the parsing predicates.
You can use read_line_to_codes/2 or read_line_to_codes/3. An example program which reads individual lines from stdin and prints them to stdout is the following:
read_lines([H|T]) :-
read_line_to_codes(user_input, H), H \= end_of_file, read_lines(T).
read_lines([]).
write_lines([]).
write_lines([H|T]) :-
writef("%s\n", [H]), write_lines(T).
main :-
read_lines(X), write_lines(X).
(This uses writef/2 for printing.) There are also read_stream_to_codes/2 and read_stream_to_codes/3, which are not concerned with lines. The following code prints all input from stdin in one go to stdout:
main :-
read_stream_to_codes(user_input, X), writef("%s", [X]).
Of course it's also possible to read from a file instead of stdin. For more, see the readutil library.

Resources