Why is prolog confused by my input file - prolog

I'm writing a list of lists to a file with
choice(2, X):-
nl, write('\tRead roster from a file:'),nl, write('\tEnter file name: '),read(N),
open(N,write,Stream), write(Stream, X), nl(Stream),close(Stream), write('\tRoster stored.'),nl,nl,menu(X).
then I'm reading it with
choice(1, X):-
nl, write('\tStore roster to a file:'),nl, write('\tEnter file name: '),read(N),
open(N,read,Stream), read(Stream, Y), close(Stream), write('\tRoster stored.'),nl,nl,menu(Y).
this is the contents of a sample from choice(2,X) that I tried to read with choice(1,X).
[[[49,48,48],[100,97,109,105,101,110],100]]
when I try to read it gives the error
ERROR: r2:1:44: Syntax error: Unexpected end of file

You get this error because read/2 can only read full prolog terms terminated with .. Contents of your file aren't terminated with dot, so end of file is "unexpected".
My suggestion is that you should modify that:
open(N,read,Stream), read(Stream, Y), close(Stream)
Into that:
open(N,read,Stream), read_line_to_codes(Stream, Codes), close(Stream), atom_codes(Atom, Codes), atom_to_term(Atom, Y, [])
Above code reads one line of character data, converts it to atom and then converts it to prolog term unified with Y.
This is my input and output from interpreter to prove it works:
?- open('test2.txt',read,Stream), read_line_to_codes(Stream, Codes), close(Stream), atom_codes(Atom, Codes), atom_to_term(Atom, List, []).
List = [[[49, 48, 48], [100, 97, 109, 105, 101, 110], 100]].
Contents of file test2.txt: [[[49,48,48],[100,97,109,105,101,110],100]]
(no dot on the end)

As #Grzegorz correctly said, to be able to read a term with read it needs to end with a full stop.
But I don't think that a correct solution to your problem is to read as codes. I think the better way is just to write full stop to the file.
The recommended way, I believe, is to use write_term with fullstop(true) option instead of write. But this is not working on my version of SWI-Prolog.
The easiest way is just to write full stop explicitly with write(Stream, '.').

Related

Retracting and asserting to another file in Prolog

I'm trying to retract and assert a fact in another file. One (fruit1.pl) contains a couple of facts, and another (fruit.pl) contains a predicate start which designates which fact that another predicate insert_fruit will update:
fruit1.pl
fruit(apple, [[2, yellow], [1, brown]]).
fruit(orange, [[3, orange], [2, orange]]).
fruit.pl
:- dynamic fruit/2.
start :-
consult('fruit1.pl'),
File = 'fruit1.pl',
Name = apple,
Price = 2,
Color = red,
insert_fruit(File, Name, Price, Color).
insert_fruit(File, Name, Price, Color) :-
open(File, update, Stream),
retract(fruit(Name, Information)),
assert(fruit(Name, [[Price, Color]|Information])),
close(Stream).
However insert_fruit is not working as intended, as I believe it needs to include Stream to modify the other file, although I have no idea how (retract(Stream, ...) doesn't work). Is there some I would be able to get the retract and assert predicates to function in the other file?
In SWI-Prolog you can assert/retract facts from a file that is used as a persistent fact store by using library persistency:
You declare fruit/3 as persistent. Optionally: you annotate the arguments with a type for automatic type checking.
You attach a file that will serve as the persistent fact store upon initialization of the fruit module (in this case fruit1.pl).
You add predicates for inserting (i.e., add_fruit/3) and querying (i.e., current_fruit/3) fruity facts. Retraction is handled similarly.
Notice that you can use the fact store in a multi-threaded environment by using with_mutex/2 (especially useful when you start retracting facts as well).
Code
:- module(
fruit,
[
add_fruit/3, % +Name:atom, +Price:float, +Color:atom
current_fruit/3 % ?Name:atom, ?Price:float, ?Color:atom
]
).
:- use_module(library(persistency)).
:- persistent(fruit(name:atom, price:float, color:atom)).
:- initialization(db_attach('fruit1.pl', [])).
add_fruit(Name, Price, Color):-
with_mutex(fruit_db, assert_fruit(Name, Price, Color)).
current_fruit(Name, Price, Color):-
with_mutex(fruit_db, fruit(Name, Price, Color)).
Illustration of use
Start Prolog, load fruit.pl, execute:
?- add_fruit(apple, 1.10, red).
Close Prolog, start Prolog (again), execute:
?- current_fruit(X, Y, Z).
X = apple,
Y = 1.1,
Z = red
You are now reading facts from fruit1.pl!
Illustration of automatic type checking
As mentioned before, the library also performs type checking for you, e.g.:
?- add_fruit(pear, expensive, green).
ERROR: Type error: `float' expected, found `expensive' (an atom)

Prolog: Read list of sublists from file while keeping elements of each sublist as their original type

So i have a program which saves a large list of sublists to a file. Each sublist has an ID, Name and grade. ID and name are stored as single quoted strings. example (quotes get removed when i save the file and the underlying list is stored as something like:
[ [005,Chester,100], [001,Bob,99], [002,Andy,77] ... ]
My function to save this to a file looks like (X is the list of sublists):
% ----- OPTION 2, STORE TO FILE
process(2, X) :-
open('hogwarts.txt',write,Stream),
write(Stream,X), write(Stream, '.'),
close(Stream),
nl, nl, menu(X).
My function to load the saved file looks like:
% ----- OPTION 1, LOAD FROM FILE
process(1, X) :-
open('hogwarts.txt',read,Str),
read(Str,TOHERE),
close(Str),
write(TOHERE), nl, menu(TOHERE).
when i write(TOHERE) after loading the saved file i get stuff like:
[[2, _G316, 67], [1, _G328, 100]]
is it possible to keep the elements of the sublist as their proper types after loading the list from a saved file? (keep ID & Name fields as single quoted strings and grade as a number)
thanks.
I would suggest using writeq in your predicate:
process(2, X) :-
open('hogwarts.txt',write,Stream),
writeq(Stream,X), write(Stream, '.'),
close(Stream),
nl, nl, menu(X).
This will maintain the quoting in your atoms, which you require.
Can you edit the file?
Then I would suggest: 'Chester' and 'Bob'. If you put them into ' then they are terms. Otherwise they are free variables.

reading files in Prolog

I am trying to read the file 'nouns.csv' which looks like this in notepad:
femin,femin,1,f,woman,women.
aqu,aqu,1,f,water,waters.
I have tried using this in SWI-Prolog:
18 ?- read(X).
X = end_of_file.
19 ?- see('nouns.csv').
true.
20 ?- seeing(X).
X = <stream>(000000000017F770).
21 ?- read(X).
X = end_of_file.
22 ?-
I have absolutely no experience with Input and Output (in any language) and I am confused. I thought read(X) would return the whole file as a string (which I thought is a stream). I want to read in each line and apply this predicate to it:
nounstage(Input) -->
["noun("],
[Adjust],
[")."],
{
append(Adjust,[46],Input)
}.
nounlineparse(X,Y) :-
phrase(nounstage(X),N),
flatten(N,Y),
asserta(Y).
I presumed I would make a giant list of every line in the nouns.csv, then iterate through the list and apply nounlineparse to each element. How can I get my file/stream into this giant list (or is the giant list way a bad way of doing this?).
In SWI-Prolog, the easier route is library(csv).
If you need to do specific processing on read items, library(pure_input) can help you.
The basic skeleton is like
:- use_module(library(pure_input)).
:- use_module(library(dcg/basics)).
read_csv(Path, Records) :-
phrase_from_file(read_csv_file(Records), Path).
read_csv_file([Tokens|Lines]) -->
read_csv_line(Tokens), read_csv_file(Lines).
read_csv_file([]) --> [].
read_csv_line([Token]) -->
string(TokenS), ( ".\n" ; "." ), {atom_codes(Token, TokenS)} .
read_csv_line([Token|Tokens]) -->
string(TokenS), ",", {atom_codes(Token, TokenS)}, !, read_csv_line(Tokens).
You will need to pay much attention to details...

Illegal argument to format sequence

I'm trying to write a getline predicate in Prolog that will grab an entire line. I'm running into problems when trying to use sformat to append strings:
getline(_, Str, 10) :-
format('NL. String: ~s\n', [Str]).
getline(_, Str, -1) :-
format('EOF. String: ~s\n', [Str]).
getline(InStr, Str, Cd) :-
sformat(NewString, '~s~c', [Str, Cd]),
get_code(InStr, C),
getline(InStr, NewString, C).
getline(InStr, Str) :-
get_code(InStr, C),
getline(InStr, Str, C).
test(InFile) :-
open(InFile, read, InStr),
getline(InStr, Line).
running the goal test("in.txt"). with the above gets me this error:
ERROR: Prolog initialisation failed:
ERROR: format/3: Illegal argument to format sequence ~s: _G940
It seems like _G940 is some sort of pointer, but I'm not sure where to go from there. Any help would be appreciated!
EDIT: Now it kinda works (i.e. no errors), but still leaves the problem of getting the complete string back up to the caller without causing the same problem again:
getline(_, Strn, 10) :-
format('NL. String: ~s\n', [Strn]).
getline(_, Strn, -1) :-
format('EOF. String: ~s\n', [Strn]).
getline(InStrm, Strn, Cd) :-
format(string(NewString), '~s~c', [Strn, Cd]),
get_code(InStrm, C),
getline(InStrm, NewString, C).
getline(InStrm) :-
get_code(InStrm, Cd),
getline(InStrm, '', Cd).
test(InFile) :-
open(InFile, read, InStrm),
getline(InStrm).
I have adapted your code a bit so that it now (1) reads the first line from the given text file, and (2) returns the first line of that text file in a string to the caller.
:- use_module(library(readutil)).
test(File, String):-
setup_call_cleanup(
open(File, read, In),
read_line_to_string(In, String),
close(In)
).
Notice that I use library readutil to do part of the job
Also, I use setup_call_cleanup/3 to make sure the stream gets closed in case reading a line fails or throws an exception.
Hope this helps!

Prolog remove character spaces from atom for using term_to_atom

At some point of my program I have an atom formed by what previously were also atoms, and I want to remove the character spaces within it so that later I can use without any problem:
term_to_atom(Result, 'second2(second2),region(ºMediterranean Sea),months(no_value),third3(third3),recog(ºNew Type),distance(no_value)').
and obtain this
Result = (second2(second2), region(ºMediterraneanSea), months(no_value), third3(third3), recog(ºNewType), distance(no_value))
or also the original would work
Result = (second2(second2), region(ºMediterranean Sea), months(no_value), third3(third3), recog(ºNew Type), distance(no_value))
because if I don't delete those character spaces then term_to_atom will complain about it. How can I solve it?
You can use this procedure:
strip_spaces(S, NoSpaces) :-
atom_codes(S, Cs), delete(Cs, 0' , X), atom_codes(NoSpaces, X).
but delete/3 is deprecated. Another possibility is
strip_spaces(S, NoSpaces) :-
atomic_list_concat(L, ' ', S),
atomic_list_concat(L, NoSpaces).
Either of these will 'eat' each space, but from your problem description, in comments you exchanged with gusbro, this doesn't seems to me the right way to go. Changing the literals seems at DB interface could ask for trouble later.
Parsing your input to a list, instead of a conjunction, can be done with DCGs:
:- [library(http/dcg_basics)].
parse_result(X, R) :-
atom_codes(X, Cs),
phrase(parse_list(R), Cs).
parse_list([T|Ts]) -->
parse_term(T), (",", parse_list(Ts) ; {Ts=[]}).
parse_term(T) -->
string(F), "(", string(Arg), ")",
{atom_codes(Fa,F), atom_codes(Arga,Arg), T =.. [Fa,Arga]}.

Resources