Prolog : How to convert an unknown type to String? - prolog

I need to get the first element of pairs however I don't know the type and it's giving me _20668 instead of C. I know it's because it's not a String but how do I convert it into a String to get a C (I can't change the input).
test([]).
test([Index-X|T]) :-
test(T),
get_pairs_value(X),
write('Index : '), write(Index), nl.
?- test([C-['Al'-2,'O'-3]]).
enter image description here
I tried atom_string() but it gives me _20668.

Related

How do I change all elements in list in Prolog

I am trying to complete an assignment for a lite version of Eliza through Prolog. I need the program to display a prompt in which the user types in a sentence as a list and the program will respond to the sentence derived from the original sentence. In the sentence, 'you' is changed to 'I'. 'I' is changed to 'why do you', 'are' is changed to 'am not', 'do' his changed to 'no, '. etc..
Below is the program however it only allows for the user to input one word. If I write 'you' only it works. However I get an error if I try to write a whole sentence or a word with [] surrounding.
"change(X,Y):- X = 'you', Y = 'I'.
change(X,Y):- X = 'i', Y = 'why do you'.
change(X,Y):- X = 'are', Y = 'am not'.
change(X,Y):- X = 'do', Y = 'no'.
change(X,Y):- X = 'portuguese', Y = 'russian'.
eliza:-
nl,
write("Ready> "),
read(X),
change(X,Y),
write('Eliza: '),
write(Y),
nl,
eliza.
:- initialization(Eliza).
May you inform me of what I need to change. Specifically which part and what it would look like.
[enter image description here][1]
[1]: https://i.stack.imgur.com/YbyuR.png
read(X) is for reading Prolog code terms, not general text. You would have to enter something like [you,are,portuguese] to make a list of atoms.
Then you need to apply change over all of the atoms in the list, which you could do with recursion but is less code to use maplist(change, X, Y).
And you may want a fact change(X,X). at the end, so that unknown words can pass through unchanged.
To go past that, you might use read_line_to_string(user_input, Text) instead of read(X) and split_string/4 to split on spaces and turn it into words, and then change your facts to use strings instead of atoms or pay atention to converting between strings and atoms, and then apply change to them, and then use atomics_to_string/3 to build the response string before writing it.

When the prolog returns sat as an output what does it mean?

When I run my code it returns the sat as the output ? Is it trying to convey an error or what does the output means ?
%Scenario 2:
%Amy: “My report is original.”
%Brian: “Mine as well”
report(2, [Amy,Brian]) :-
sat(Amy=:=Amy),
sat(Brian=:=Brian),
write('1 they are telling the truth , 0 they are lying').
The output that I get is :-
It is not trying to convey you an error, it is being as specific as it can be. That is sat(Amy=:=Amy), sat(Brian=:=Brian) is satisfiable whenever sat(Amy=:=Amy) and sat(Brian=:=Brian).
CLP(B) does not automatically designate variables in expressions to be boolean. You can use labeling(+Vs) to inform it as follows:
?- use_module(library(clpb)).
?- sat(Amy=:=Amy), sat(Brian=:=Brian), labeling([X,Y]).
X = Y, Y = 0,
sat(Amy=:=Amy),
sat(Brian=:=Brian)
This is only the first solution you can use backtrack to obtain the others.

Prolog: How to read data from console and store into database. Getting errors

update :-
write("Name?:"),
read(Name),
assert(Name),nl,
write("Age?:"),
read(Age),
assert(Age),
write("Continue(y or n)?:"),
read(Respond),
process(Respond).
process(y) :-
write('Name?:'),
read(Name),
assert(Name),nl,
write("Age?:"),
read(Age),
assert(Age),
repeat,
write("y or n"),
read(Respond),
process(Respond).
process(n) :- !.
I want to run this Prolog to assert in the name and age, but when I write age for the number, it shows
?- update.
Name?:fred.
Age?:|: 25.
ERROR: Type error: `callable' expected, found `25' (an integer)
ERROR: In:
ERROR: [9] assert(25)
ERROR: [8] update at c:/example.pl:11
ERROR: [7] <user>
?-
How to figure out this problem.
Problem 1
Incorrect input for assert/1
The problem is not with just Age it is with any input that uses assert, e.g.
?- update.
Name?:Fred
|: .
ERROR: Arguments are not sufficiently instantiated
ERROR: In:
ERROR: [9] assert(_4940)
ERROR: [8] update at c:/example.pl:8
ERROR: [7] <user>
?- update.
Name?:Jim.
ERROR: Arguments are not sufficiently instantiated
ERROR: In:
ERROR: [9] assert(_5826)
ERROR: [8] update at c:/example.pl:8
ERROR: [7] <user>
The problem is that assert/1 is not being given a fact or rule.
assert/1 says:
Assert a clause (fact or rule) into the database.
See facts and rules
In the example above Fred is not a fact because it does not end with a period (.).
In the example above with Jim. a period was given but because Jim starts with a capital letter, it is not a fact or rule but a variable.
When the age is entered as a number, again this is not a fact or rule it is an integer.
Problem 2
Use of read/1 which says:
Read the next Prolog term from the current input stream and unify it with Term.
When reading a Prolog term the input must end with a period.
This not only requires the input to be a term, but end with a . which is even more confusing given the prompt, e.g Age. Most of the examples you find do what you did, the corrected code below does what you want.
Problem 3
Competing ways or repeating.
The code is using two ways:
Use of repeat/0
It is recursive, e.g.
process(y) :-
...
process(Respond).
This is making it hard to get the code working.
Problem 4
Duplicate code, e.g.
write("Name?:"),
read(Name),
assert(Name),nl,
write("Age?:"),
read(Age),
assert(Age),
write("Continue(y or n)?:"),
read(Respond),
process(Respond).
Duplicated code is more likely to lead to problems when one copy is corrected and the other copy is not corrected.
Problem 1 fix
Make the input a fact before storing in the database with assert/1, e.g.
Values in variables
Name
Age
Variables converted to facts by adding a functor
name(Name)
age(Age)
The facts used with assert/1
assert(name(Name))
assert(age(Age))
Problem 2 fix
Use read_string/5, e.g.
read_string(user, "\n", "\r", End, Name)
This reads the input into the variable Name as a string. Now that the input is a string, and not a Prolog term, the period is no longer required. There are predicates that operate on strings.
Problem 3 fix
Use the recursion form and remove repeat/0.
This could also use repeat/0 instead of recursion. The corrected code below uses recursion to demonstrate the change to process/1.
Problem 4 fix
Just refactor the code. You can see this in the corrected code at the end.
Now with the fixes in place.
Change 1
Since the input for continue is no longer a term, e.g. y or n, but a string, the parameter for process needs to be a string, e.g.
process("y") :-
process("n") :-
Change 2
Age will be asserted as a string but would be better asserted as an integer.
number_string/2 can solve this, e.g.
number_string(Age_n,Age),
assert(age(Age_n))
Change 3
user27815 Asked in a comment:
do you need the cut in process("n") :- !. ?
Since
process(Respond).
is not creating a choice point, the cut is not needed.
Corrected code:
update :-
% Respond will be read as a string and not as a term, so it needs "".
process("y").
process("y") :-
write('Name: '),
read_string(user, "\n", "\r", End, Name),
assert(name(Name)),
write("Age: "),
read_string(user, "\n", "\r", End, Age),
number_string(Age_n,Age),
assert(age(Age_n)),
write("Continue: (y or n) "),
read_string(user, "\n", "\r", End, Respond),
process(Respond).
process("n").
Example run:
?- update.
Name: Fred
Age: 30
Continue: (y or n) y
Name: Jim
Age: 21
Continue: (y or n) n
true.
To check that the database was updated use listing/1
?- listing(name/1).
:- dynamic name/1.
name("Fred").
name("Jim").
true.
?- listing(age/1).
:- dynamic age/1.
age(30).
age(21).
true.
A free enhancement.
Keeping the facts of name and age separate doesn't keep the relation between them intact. A better solution would be a person fact with both Name and Age values.
Here is the necessary modified code.
update :-
% Respond will be read as a string and not as a term, so it needs "".
process("y").
process("y") :-
write('Name: '),
read_string(user, "\n", "\r", End, Name),
write("Age: "),
read_string(user, "\n", "\r", End, Age),
number_string(Age_n,Age),
assert(person(Name,Age_n)),
write("Continue: (y or n) "),
read_string(user, "\n", "\r", End, Respond),
process(Respond).
process("n").
Example run:
?- update.
Name: Fred
Age: 30
Continue: (y or n) y
Name: Jim
Age: 21
Continue: (y or n) n
true.
To check that the database was updated use listing/1
?- listing(person/2).
:- dynamic person/2.
person("Fred", 30).
person("Jim", 21).
true.
After noticing your deleted answer.
In your deleted answer you have
?- person(name(N), age(A)).
N = nancy,
A = 22;
N= steve,
A = 100;
true.
The change needed for this variation of the fact to be created is
assert(person(name(Name),age(Age_n)))
however that might not be the optimal way to go.
In Prolog, positions typically indicate the meaning of a value, e.g. first position is name and second position is age. In this variation by adding the functors name and age to the fact person/2 you are duplicating known knowledge, but more importantly the possibility the amount of work Prolog has to do.
For example:
If the fact was person(Name,Age). to get at Name and Age Prolog only needs one unification. But with person(Name,Age). Prolog now needs to unify with person(name(nancy),age(22)) then to get Name has to unify again with name(nancy) and to get Age has to unify with age(22). You could also use person(name(Name),age(Age)). which requires only one unification, but now makes your code more verbose.
When first learning Prolog this crutch helps, but when working with larger data sets, this starts to impact performance.
Another item of note in your deleted answer is that the names of the people are still based on using read/1, e.g. nancy and steve. While a lot of Prolog examples do this, there is no requirement to keep them as such, they can be strings. Odds are the code will never need to exactly match on nancy or steve and instead will always reference them as a value in a variable. The nice thing about keeping them as strings is that when writing them out, they will appear correctly as Nancy and Steve.
This is because assert does not work on variables. It asserts a fact or rule; in other words, assert(something) asserts that something must be true.
From the SWI-Prolog documentation:
Assert a clause (fact or rule) into the database.
An integer value is not a rule or a fact. It is (in this case) an integer, not something that evaluates to a boolean value. There's no point in asserting a value.
I would write some helpers:
read_assert(P,V) :- format('~w ? ',[P]), read(V), A =.. [P,V], assert(A).
?- maplist(read_assert, [name,age], Vs).
name ? capellic.
age ? 99.
Vs = [capellic, 99].
?- name(N).
N = capellic.

convert compound to atom in prolog

I want to use atom_chars/2 on the expression of 3+4, but I get
ERROR: atom_chars/2: Type error: 'atom' expected, found '3+4' (a compound).
I'm thinking that if I can add " " on both sides of the compound, it would work, e.g.
atom_chars("3+4", Result).
but I don't know how I can do that, or is there other approaches to do this?
Please give me some advice.
EDIT: What I mean is that the input has to be 3+4, instead of '3+4', so what I want to do is to write a predicate before the atom_chars/2 to convert 3+4 to '3+4'.
For instance: for compound2atom(X,Y),
-?compound2atom(3+4,Y).
Y='3+4'.
If you are using SWI-Prolog, there is with_output_to/2 or format/3:
?- with_output_to(atom(A), write(3+4)).
A = '3+4'.
?- with_output_to(chars(C), write(3+4)).
C = ['3', +, '4'].
?- format(atom(A), "~w", [3+4]).
A = '3+4'.
?- format(chars(C), "~w", [3+4]).
C = ['3', +, '4'].
But if you look hard enough you should be able to find some predicate that does that, for example term_to_atom/2.
My personal preference leans towards format/3.

How to convert a list to a string in Prolog?

I have a list L =[a+b,b+c] and I want to convert it to a string and print the output a+bb+c.
Can you please help me convert this list to a string? I tried using atomic_list_concat in SWI-Prolog but it gave a type error for a+b.
In SWI-Prolog:
?- with_output_to(atom(Atom), maplist(write, [a+b, b+c])).
Atom = 'a+bb+c'.
You can replace write with a call to a custom predicate if you need more control over how your terms (e.g. a+b) are written.
The members of your list are compound terms, so you have to make them atomic before calling atomic_list_concat:
custom_print(L) :-
maplist(term_to_atom, L, L1),
atomic_list_concat(L1, L2),
print(L2).

Resources