Writing to file (Prolog) - prolog

I've been trying to loop through a list and writing it to a file, why is the following not working?
loop_through_list(List) :-
member(Element, List),
write(Element),
write(' '),
fail.
write_list_to_file(Filename,List) :-
tell(Filename), % open file to be written
loop_through_list(List),
told. % flush buffer

First, why it fails :
You use fail to provoke backtracking, which can be a good technique, but not there. Because it will make your predicate false in the end, when member has ran out of solutions. Then, once loop_through_list is false, told isn't reached and the writing isn't made properly (when I test it, the file is created but nothing is written).
If you use :
loop_through_list([]).
loop_through_list([Head|Tail]) :-
write(Head),
write(' '),
loop_through_list(Tail).
instead, it works.
But, even with this code working, you might want to use
open(Filename, write, Stream), write(Stream, Element) and close(Stream) instead of tell and told for the reasons explained in the link at the bottom of this answer.
For example :
loop_through_list(_File, []) :- !.
loop_through_list(File, [Head|Tail]) :-
write(File, Head),
write(File, ' '),
loop_through_list(File, Tail).
write_list_to_file(Filename,List) :-
open(Filename, write, File),
loop_through_list(File, List),
close(File).
or
loop_through_list(File, List) :-
member(Element, List),
write(File, Element),
write(File, ' '),
fail.
write_list_to_file(Filename,List) :-
open(Filename, write, File),
\+ loop_through_list(File, List),
close(File).
using your code and joel76 trick.
See Prolog how to save file in an existing file
It covers the same matter.

I fail to see the reason to do use this method to write a list to a file.
Programming in prolog should generally not involve loops;
besides, this is not a loop structure, it's more like a hack (or even abuse).
(and just like your case leads to unexpected bugs and problems)
Just use recursion and print the elements of the list:
write_list([]).
write_list([H|T]):-
write(H),
write(' '),
write_list(T).
more elegant and could be more efficient too.
other than that, using open/4 etc (ISO IO) instead of tell/1 etc (Edinburgh IO) is generally better; check false's post

predicate loop_through_list(List), always fails, so to succed you just have to write \+loop_through_list(List),

Related

Run Prolog queries within prolog file and store the result in a txt file

I have a prolog file test1.Pmention below, I want to test multiple queries on this file, for this reason i have added 3-loops (query1,query2,quer3): I want to store the result of the query and time that query has took for evaluation to text file "result.txt". the problem is this code works fine but it only store the result of the last query in the file and does not store the time along side with query.
sol(Goal, Pair) :-
term_variables(Goal, Vars),
findall(Vars, Goal, Substs),
write_list_to_file('result.txt',Substs),
Pair = Vars-Substs.
ans(Goal, Pair):- statistics(runtime,[Start|_]),
sol(Goal, Pair),
statistics(runtime,[Stop|_]),
Runtime is Stop - Start,
write(Runtime).
loop_through_list(File, List) :-
member(Element, List),
write(File, Element),
write(File, ' '),
fail.
write_list_to_file(Filename,List) :-
open(Filename, write, File),
\+ loop_through_list(File, List),
close(File).
:- forall(ans((pred1(X,Y),pred2(X,'BaldEagle')),L), writeln(L)). //query1
:- forall(ans((pred1(X,Y), pred3(X,'Eagle')),L), writeln(L1)). //quer2
:- forall(ans((pred1(X,Y), pred4(Y,Z)),L), writeln(L2)). //query3
So instead of using 3 queries in test1.P I have created another file named as testQueries.P and write my queries like this:
query(ans((pred1(X,Y),pred2(X,'BaldEagle')),L), writeln(L)).
query(ans((pred1(X,Y), pred3(X,'Eagle')),L), writeln(L1)).
query(ans((pred1(X,Y), pred4(Y,Z)),L), writeln(L2)).
myforall(X,Y) :- \+ (X, \+ Y).
mywrite(T) :- write(T), nl.
mycount(Query, Count) :-
setof(Query, Query, Set),
length(Set, Count),
Count = 0.
test :-
mycount(query(Q), Count).
myforall(query(Q), (Q -> mywrite(yes:Q); mywrite(no:Q))),
halt.
:- initialization(['test1.P']).
:- initialization(test).
But still I am unable to store the results of all the queries and time in the text file. Also how can i get the number of results generated against query?
The problem is twofold:
You perform write(Runtime) of the "runtime in milliseconds" without the File as argument. Consequently, the output goes to "standard output" (or maybe "current output"), i.e. the terminal, not the file.
For each query, you open the file as open(Filename, write, File), write whatever you want to write, then close the file. The file is opened in write mode, which is really truncation mode: The file emptied, and writing begins anew at file position 0. This explains why only the information regarding the last query is seen. You have to either open the file in append mode: open(Filename, append, File) or write all that you want to write before closing the file.
An improved write_list_to_file/3, where we use forall/2 instead of a "failure-driven loop":
write_list_to_file(Filename,List,Mode) :-
must_be(oneof([write,append]),Mode),
setup_call_cleanup(
open(Filename, Mode, File), % but what's the encoding? It depends!
forall(
member(Element,List),
(
write(File,Element),
write(File,' ')
)
),
close(File)).
So, one has to call it once with Mode = write and twice with Mode = append. But this still doesn't help with getting the runtime milliseconds into the file.
The code is quite convoluted. Maybe it can be greatly simplified.
For example, this is still not ideal (in particular because it repeatedly opens and closes the file), but using the above write_list_to_file/3:
question1(X,Y) :-
pred1(X,Y),
pred2(X,'BaldEagle')).
question2(X,Y) :-
pred1(X,Y),
pred3(X,'Eagle')),
question3(X,Y,Z) :-
pred1(X,Y),
pred4(Y,Z).
time_goal(Goal,VarsBindings,Delta_ms) :-
statistics(runtime,[Start|_]),
term_variables(Goal, Vars),
findall(Vars,Goal,VarsBindings),
statistics(runtime,[Stop|_]),
Delta_ms is Stop - Start.
time_goal(Goal,Filename,Mode) :-
time_goal(Goal,VarsBindings,Delta_ms),
write_list_to_file(Filename,[Goal,Delta_ms],Mode),
write_list_to_file(Filename,VarsBindings,append).
time_all(Filename) :-
time_goal(question1(X,Y),Filename,write),
time_goal(question2(X,Y),Filename,append),
time_goal(question3(X,Y,Z),Filename,append).

Editing Eliza chatbot in Prolog

I've been struggling trying to edit Eliza chatbot in Prolog. every time I try to edit something, a new error show up. Is it protected to any sort of editing?
I edited using SWI-prolog editor. The problem is that I'm trying to minimize the code without fully understanding it. I'm trying to do a simple short version of it. So, I might removed something essential perhaps! like "my_char_type" for example. The error I got is " retract/1: No permission to modify static procedure `rules/1'"
Is there any code for a smaller chatbot that I can understand?
Please help :'(
Prolog has a static store and a dynamic store. If you open up a file, say program.pl and you put some lines in it like this:
foo(tabitha).
foo(darlene).
those facts wind up in the static store. They're not a mutable part of your program (by default).
The asserta/1, assertz/1 and retract/1 and retractall/1 procedures form the basis of the dynamic store. If you are just sitting at the console you could just add some facts to the dynamic store and remove them by doing something like this:
?- asserta(baz(tabitha)).
true.
?- baz(X).
X = tabitha.
?- retract(baz(tabitha)).
true.
?- baz(X).
false.
However, if you are sitting at the prompt after loading program.pl and you try to retract foo(tabitha) you're going to get the static procedure message:
?- retract(foo(tabitha)).
ERROR: retract/1: No permission to modify static procedure `foo/1'
ERROR: Defined at /Users/fusion/program.pl:1
The reason is because the foo/1 facts were placed in the static store rather than the dynamic store, because you didn't put them there with asserta/1 or assertz/1 or declare the predicate as dynamic, like this:
:- dynamic foo/1.
So there are two ways forward:
Edit the program source directly and reload it.
Declare the rules/1 predicate dynamic as above.
Incidentally, reloading in SWI-Prolog is best done by running make. from the prompt.
I would recommend option #1 since otherwise it will be difficult to reconstruct your working program's state when you like what it is doing.
SWISH has the simplest Eliza ever, I have the old code below, used to test my Prolog interpreter.
here is an example session
1 ?- eliza.
? i am hungry
how long have you been hungry ?
? very long
please go on
? bye
Goodbye. I hope I have helped you
true.
SWI-Prolog tested version, ported from below ELIZA.IL (alas, SWISH is apparently missing IO primitive like read_line_from_codes, so it's simpler to paste the full code)
eliza :-
write('? '), read_word_list(Input), eliza(Input), !.
eliza([bye]) :-
write('Goodbye. I hope I have helped you'), nl.
eliza(Input) :-
pattern(Stimulus, Response),
match(Stimulus, Dictionary, Input),
match(Response, Dictionary, Output),
reply(Output),
!, eliza.
match([N|Pattern], Dictionary, Target) :-
integer(N), lookup(N, Dictionary, LeftTarget),
append(LeftTarget, RightTarget, Target),
match(Pattern, Dictionary, RightTarget).
match([Word | Pattern], Dictionary, [Word | Target]) :-
atom(Word), match(Pattern, Dictionary, Target).
match([], _Dictionary, []).
pattern([i,am,1],[how,long,have,you,been,1,'?']).
pattern([1,you,2,me],[what,makes,you,think,i,2,you,'?']).
pattern([i,like,1],[does,anyone,else,in,your,family,like,1,'?']).
pattern([i,feel,1],[do,you,often,feel,that,way,'?']).
pattern([1,X,2],[can,you,tell,me,more,about,your,X,'?']) :- important(X).
pattern([1],[please,go,on]).
important(father).
important(mother).
important(son).
important(sister).
important(brother).
important(daughter).
reply([Head | Tail]) :-
write(Head), write(' '), reply(Tail).
reply([]) :- nl.
lookup(Key, [(Key, Value) | _Dict], Value).
lookup(Key, [(Key1, _Val1) | Dictionary], Value) :-
Key \= Key1, lookup(Key, Dictionary, Value).
read_word_list(Ws) :-
read_line_to_codes(user_input, Cs),
atom_codes(A, Cs),
tokenize_atom(A, Ws).
Older code: eliza and rwl

Prolog read predicate cant read user key in in initialization?

I want to have a prolog program which can auto run once I compile finish and it should read input from user keyboard. However when i use
:- write('Your name is: '), nl, read(X).
there is not any effect on read(X), which it means that there us not any prompt for users to key in. Is there any solution for this problem? My prolog compiler is WIN-Prolog 5.0, thanks for your helo :)
maybe you need something like
:- initialization(main). % changed to be ISO compliant, was :- initialization main.
main :- write('Your name is: '), nl, read(X).
at least, ISO Prologs do it in this way....
Since read/1 is meant to parse Prolog (it's a full fledged parser), you must supply a Prolog term, and don't forget, as Joel76 suggested, to complete input with a dot: for instance:
Your name is:
|: 'Carlo'.

how to write a recursive routine in prolog?

i have a to write a routine which lists all descendants so far i wrote
descend(X,Y) :- child(X,Y).
descend(X,Y) :- child(X,Z), descend(Z,Y).
which works fine so any descendent i need to find i just do descend(X,name). and it keeps giving me descendants of name in form of X= descend1, X = descend2
but to get the results i have to press ; every time what i am trying is to write is a routine descendb which gives the list of all descends without pressing ;
descendb(X) :- descend(A,X), write(A).
this is obviously wrong.
You can get all results with a 'failure driven' loop, aka forall/2
descendb(X) :- forall(descend(A,X), writeln(A)).
That's generally useful only when we have to do some 'side effect' on every solution found, like writeln (for instance) does.
Since you say you're after 'the list of all descends', try findall/3 instead:
descendb(X, Ds) :- findall(D, descend(D,X), Ds).
Since we have 2 arguments, you are not obliged to make a choice, descendb/1 and descendb/2 are effectively different predicates.

Undefined procedure in SWI-Prolog

Ok i got these two predicated hangman and graphics
fail([]).
hangman:-
getPhrase(Ans),
!,
write('Welcome to hangman.'),
nl,
fail(FailedList),
graphics(FailedList), %<--- The call is made here.
name(Ans,AnsList),
makeBlanks(AnsList, BlankList),
getGuess(AnsList,BlankList, FailedList).
graphics(FailedList):-
length(FailedList, L),
L == 0,
write('-----------'), nl,
write('|'), nl,
write('|'), nl,
write('|'), nl,
write('|'), nl,
write('|'), nl,
write('|'), nl,
write('|'), nl,
write('|'), nl,
write('/\'), nl.
Why do i get the error: ERROR: hangman/0: Undefined procedure: graphics/1?
Note that if i put the predicate graphics inside hangman in comments my program works fine.
(this answer is not really about the question asked so please let the answer to #SeçkinSavaşçı who did a great job answering the question, it's more of a code review)
Here, you visibly want to test if a list is empty and react accordingly by displaying some things. For the test if a list is empty part, you're doing it wrong:
graphics(FailedList):-
length(FailedList, L),
L == 0,
% some IO stuff
In prolog, you can use unification in a more straightforward manner:
graphics(FailedList):-
length(FailedList, 0),
% some IO stuff
or, better, where you directly test for the empty list in the head as a condition to execute the body of the predicate:
graphics([]):-
% some IO stuff
For the IO part, you're doing it kinda wrong again. SWI-Prolog, for example, has a writeln/1 predicate, that would make your code lighter:
graphics([]):-
writeln('-----------'),
writeln('|'),
writeln('|'),
writeln('|'),
writeln('|'),
writeln('|'),
writeln('|'),
writeln('|'),
writeln('|'),
writeln('/\\').
Still better, the format/1 predicate could be used:
graphics([]):-
format('-----------~n|~18~n|~n|~n|~n|~n|~n|~n|~n/\\').
The main predicate seems to have some problems too, but I'll let you look into it and ask questions if you're stuck somewhere :)
write('/\'), nl.
In the last line, you are escaping the ending quotation mark with \'. Change it to:
write('/\\'), nl.
BTW: #Mog has written the answer before I had a look at the comments, I tested it and now it finds graphics/1.

Resources