Prolog character stream error - prolog

My code is supposed to stop when it finds stop in the file its reading from, but its not. I keep getting an error:
% reads in a character and then checks whether this character is a blank,
% a carriage return or the end of the stream. In any of these cases a
% complete word has been read otherwise the next character is read.
calculate([stop],_) :- !.
calculate([],_):-!.
calculate([Word|Rest],X) :-
word_to_number(Word,Symbol),
concat(X,Symbol,NewX),
calculate(Rest,NewX),
atom_to_term(NewX,Eq,[]),
print('Calculating '),print(NewX),print(' The result is: '),
Result is Eq,
print(Result),nl,
execute.
Any help would be greatly appreciated!

Declare "plus", "minus" and "times" as operators, and you can use read/1 to read Prolog terms directly, since the input is then valid Prolog syntax.

The problem is that calculate is recursive. At some point, calculate([one], '03+') called, which in turn calls calculate([], '03+1'), which gives a result (4). It then invokes execute and processes the rest of the input.
Then, the calling calculate succeeds, and now goes on to applying atom_to_term to '03+', which gives the error.
You can fix this by moving the conversion to an atom into a separate predicate:
to_atom([Word], Symbol) :- word_to_number(Word, Symbol).
to_atom([Word|Rest], Term) :-
word_to_number(Word,Symbol),
to_atom(Rest, Symbol2),
concat(Symbol,Symbol2,Term).
...
calculate(List) :-
to_atom(List, NewX),
atom_to_term(NewX,Eq,[]),
...
Then you won't need the dummy 0 in the beginning, either.

Related

Why does my replace/2 program always return false?

My goal is to replace the '_' with logic variables in a given list. My code:
replace([], []).
replace(['_'|As], [_|Bs]) :-
replace(As, Bs).
replace([A|As], [B|Bs]) :-
A \= '_',
B = '#',
replace(As, Bs).
It will return a proper list but always ends up with false. Any help please?
Any input that matches replace(['_'|As], [_|Bs]), also matches replace([A|As], [B|Bs]). This means that while the first clause is executed, a choice point is left for the latter one.
After prolog has found the first result, it notices that there are still choice points open, so it will ask you if you want more results. If you say yes, it will not try to execute the other clause. This will always fail, since A \= '_' is never true.
Note that this behavior is not wrong. The 'false' doesn't mean that the program failed, it just means that no more results were found after those that had already been presented. In this case, you know that there will always only be one result, so you can tell prolog not to leave any choice point open by using the cut operator ! like this:
replace(['_'|As], [_|Bs]) :-
replace(As, Bs), !.
This essentially tells prolog that if this clause succeeded, the remaining possible matches should no longer be considered. As such, no choice points are left open, and once you get the first result, the execution is done and returns true.

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 print statement does not work as expected

I was trying to define a functor and print each individual items of list in Prolog, but Prolog is not printing in correct format.
rint(L):-
write(H).
the output is like
rint([a, s,v ,c]).
_L139
true.
This is what I expect to achieve by calling the functor, any help or thought is appreciated, I'm new to Prolog and learning it.
?- rint([a,b,c,d]).
.(a, .(b, .(c, .(d, []))))
I think it should be
rint(L) :- write(L).
Also if you want .(a, .(b, .(c, .(d, [])))) and not [a, b, c, d] in output, use display:
rint(L) :- display(L).
The problem is an error in your rule for rint.
Your definition says that rint(L) succeeds if write(H) succeeds. At that point, the interpreter knows nothing about H. So it writes a value it doesn't know, which is why you see the _L139, the internal representation of an uninitialised variable.
Having done that, write(H) has succeed, is true, so rint(L) is true. The interpreter tells you that: true.
To define your own rint/1 without relying on built-ins such as display/1, you would need to do something like
rint([]) :-
write([]).
rint([H|T]) :-
write('.('),
write(H),
write(', '),
rint(T),
write(')').
If you're trying to display an empty list, just write it. If you're trying to display any other list, write the opening period and parenthesis, write the Head, write the following comma and space, then call itself for the Tail of the list, then write the closing parenthesis.

Problem when reading backslash in Prolog

I'm writing a lexer in Prolog which will be used as a part of functional language interpreter. Language spec allows expressions like for example let \x = x + 2; to occur. What I want lexer to do for such input is to "return":
[tokLet, tokLambda, tokVar(x), tokEq, tokVar(x), tokPlus, tokNumber(2), tokSColon]
and the problem is, that Prolog seems to ignore the \ character and "returns" the line written above except for tokLambda.
One approach to solve this would be to somehow add second backslash before/after every occurrence of one in the program code (because everything works fine if I change the original input to let \\x = x + 2;) but I don't really like it.
Any ideas?
EDIT:
If anyone should have similar problems, that's how I solved it:
main(File) :-
open(File,read,Stream),
read_stream_to_codes(Stream, Codes),
lexer(X,Codes,[]),
... invoke other methods
Where did you get the string let \x = x + 2; from?
If it is in your Prolog program: yes, you have to double the backslashes.
If it is from an external file: How do you read it from there? Maybe that predicate is interpreting the backslash specially.
I got inspired by that problem and wrote a bit of code, which should be portable to all Prolog implementations:
% readline(-Line)
%
% Reads one line from the current input. The line is then returned as a list
% of one-character atoms, excluding the newline character.
% The returned line doesn't tell you whether the end of input has been reached
% or not.
readline(Line) :-
'readline:read'([], Reversed),
reverse(Line, Reversed).
'readline:read'(Current, Out) :-
get_char(C), 'readline:append'(C, Current, Out).
'readline:append'(-1, Current, Current) :- !.
'readline:append'('\n', Current, Current) :- !.
'readline:append'(C, Current, Line) :-
'readline:read'([C | Current], Line).
I tried it, and it worked for me.
Of course, as explained in question 1846199, you can also use read_line_to_codes/2.

Translate Prolog Words

I'm working on this this wonderful Prolog project and I'm stuck at this situation where I need to translate certain words into other words (e.g "i" into "you". "my into "your")
This is what I've done and I'm pretty sure it's kinda iffy. I enter the sentence and when It goes to convert it only changes the one word then goes on wacky on me. (e.g. "i feel happy" changes to "you" then it crashes.)
translate([]).
translate([H|T], [NewH|NewT]):-
means(H,NewH);
spit(T,NewT).
means(i,you).
means(my,your).
means(mine,yours).
Here's the fix:
translate([], []).
translate([H|T], [NewH|NewT]):-
means(H, NewH),
translate(T,NewT).
means(i, you) :- !.
means(my, your) :- !.
means(mine, yours) :- !.
means(X, X).
The changes are as follows:
I fixed the missing parameter to the first definition of translate (it's considered a completely independent function if the number of parameters don't match).
I'm calling translate on the rest of the list when the first item is translated.
I'm asserting that every word means itself unless specified otherwise. The :- ! part means if you match this, don't try the rest (this is to avoid lists always matching with themselves).
Example usage:
?- translate([this, is, i], X).
X = [this, is, you].

Resources