How do I change all elements in list in Prolog - 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.

Related

Prolog, Outputting a Variable After a Relation is Used

This is my first-day learning prolog and I would like to write a program that decides what shoes I should wear based on the weather and what kind of appointment I have at the office. The main "function" would be declared for example:
"go :- outfit(snow, 15, casual, F1), write(F1)."
Where snow is the weather, 15 is the temperature(not relevant now), and casual is the formality of the appointment. "write(F1)" will display the "output" so the variable F1 needs to be the result of said relation(s). Here are the rules for what shoes to wear:
%Rules for weather
rain(Weather) :- (Weather = 'rain').
snow(Weather) :- (Weather = 'snow').
nice(Weather) :- (Weather = 'nice').
clear(Weather) :- (Weather = 'clear').
%Rules for formality
formal(Appointment) :- (Appointment = 'formal').
semiformal(Appointment) :- (Appointment = 'semiformal').
casual(Appointment) :- (Appointment = 'casual').
%Rules for when to wear a type of footwear
dressShoes(Appointment) :- formal(Appointment).
boots(Appointment, Weather) :- not(formal(Appointment)), (rain(Weather);snow(Weather)).
sneakers(Appointment, Weather) :- not(formal(Appointment)), (nice(Weather);clear(Weather)).
This is where my issue is, I am not sure how to tie the last three relations to a single relation that fills the variable "F1" for my final "outfit" function. I am a C++ guy, so I would like to essentially place a string into F1 like "[sneakers]" or "[boots]" but this is part of my growing pains with prolog. Any help is much appreciated.
Some misunderstandings about Prolog I guess. This kind of rule:
rule(Variable) :- (Variable = 'value').
You don't need to quote 'value', it is already an atom. Whatever book you are reading, look up atoms. It becomes:
rule(Variable) :- (Variable = value).
You don't need the extra parentheses in the rule definition. It becomes:
rule(Variable) :- Variable = value.
You don't need the explicit unification in the body. There is nothing else happening between the head and the body. So you don't need a variable, either. It becomes:
rule(value).
Applying this to your program, I get:
rain(rain).
snow(snow).
nice(nice).
clear(clear).
%Rules for formality
formal(formal).
semiformal(semiformal).
casual(casual).
Those rules say pretty much nothing ;-)
Your example at the very top:
go :- outfit(snow, 15, casual, F1), write(F1).
Does exactly the same as just calling outfit(snow, 15, casual, F1). So what is the purpose of the "go" and the "write"? Skip them I guess.
The logic of your program: can you explain it without code? Your code is so unusual that I have to guess.
If you want to say, "If the appointment is formal, put on dress shoes", you could write it like this:
occasion_shoes(Occasion, Shoes) :-
formality_occasion(Formality, Occasion),
formality_shoes(Formality, Shoes).
formality_occasion(formal, evening_party).
formality_occasion(semi_formal, office).
formality_shoes(formal, dress_shoes).
Do you see what is going on? You match the occasion to the shoes. To do that, you look up the formality of the occasion in the table formality_occasion/2 and then match it to the formality of the shoes in the formality_shoes/2 table.
If you are struggling to model your problem, you can also read up on relational database design.

Pattern matching using list of characters

I am having difficulty pattern matching words which are converted to lists of characters:
wordworm(H1,H2,H3,V1,V2) :-
word(H1), string_length(H1,7),
word(H2), string_length(H2,5),
word(H3), string_length(H3,4),
word(V1), string_length(V1,4),
word(H3) \= word(V1),
atom_chars(H2, [_,_,Y,_,_]) = atom_chars(V1, [_,_,_,Y]),
word(V2), string_length(V2,5),
word(H2) \= word(V2),
atom_chars(H3, [_,_,_,Y]) = atom_chars(V2, [_,_,_,_,Y]).
Above this section, I have a series of 600 words in the format, word("prolog"). The code runs fine, without the atom_chars, but with it, I get a time-out error. Can anyone suggest a better way for me to structure my code?
Prolog predicate calls are not like function calls in other languages. They do not have "return values".
When you write X = atom_chars(foo, Chars) this does not execute atom_chars. It builds a data structure atom_chars(foo, Chars). It does not "call" this data structure.
If you want to evaluate atom_chars on some atom H2 and then say something about the resulting list, call it like:
atom_chars(H2, H2Chars),
H2Chars = [_,_,Y,_,_]
So overall maybe your code should look more like this:
...,
atom_chars(H2, H2Chars),
H2Chars = [_,_,Y,_,_],
atom_chars(V1, V1Chars),
V1Chars = [_,_,_,Y],
...
Note that you don't need to assert some kind of "equality" between these atom_chars goals. The fact that their char lists share the same variable Y means that there will be a connection: The third character of H2 must be equal to the fourth character of V1.

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.

Translate a sentence in prolog

can anyone help me to display untranslated word? for example i want to translate, 'i want to eat', in fact, there is no argument of to...so it will display 'saya mahu to makan'...the word to is remain the same it is and need to display it..this is the sample of my code..
:-dynamic(item/1).
kamus(saya,i).
kamus(suka,love).
kamus(awak,you).
run:-
write('Enter a sentence:'),
read(V),
printed(V,C).
%malay to eng
check(S,W,R):-
kamus(R,S),name(S,W).
%eng to malay
check(S,W,R):-
kamus(S,R),name(S,W).
check(Y,R):-
item(Z),name(Z,Y).
try2(P,R):-
name(P,Q),
split(Q,32,E),
member(Z,E),
assert(item(Z)),
(check(Y,R);check(S,W,R)),Z=W.
printed(V,C):-
try2(V,C),write(C),write(' '),fail.
split(String, Space, [Word|List]) :-
append(Word, [Space|Tail], String),
!,
split(Tail, Space, List).
split(String, _Space, [String]).
example of output:
?-run.
Enter the sentence : 'saya benci awak'.
i you no.
**there is no 'benci' in the fact but i need it to display in the output---> 'i benci you'
try to add a 'catch-all' rule, after the cut addiction
check(S,W,R):-
kamus(S,R),name(S,W), !.
% after edit. HTH
check(S,_,R):-
name(S,R).
check(Y,R):-
item(Z),name(Z,Y), !.
check(Y,Y).
cuts ensure that the default in not engaged when there are more appropriate rules, listed in your rule db.
edit: I didn't noticed that the first check has arity 3 (then the first cut was useless). Of course another default should go where noted above.

Prolog, how to show multiple output in write()

go :- match(Mn,Fn),
write('--Matching Result--'),
nl,
write(Mn),
write(' match with '),
write(Fn),
match(Mn1,Fn1).
person(may,female,25,blue).
person(rose,female,20,blue).
person(hock,male,30,blue).
person(ali,male,24,blue).
match(Mn,Fn):-person(Fn,'female',Fage,Fatt),
person(Mn,'male',Mage,Matt),
Mage>=Fage,
Fatt=Matt.
Hi,this is my code...but it's only can show the 1 output...but there are 3 pair of matching in match(X,Y).how to show them all in my go function.
Thank you
You get all your matches if you force backtracking, usually by entering ; (e.g. in SWI Prolog). But you also see that you are getting unnecessary outputs true. This is because the last clause in go is match(Mn1,Fn1). This clause succeeds three times and binds the variables Mn1,Fn1 but then only true is output, because you do not write() after that clause. The fourth time match(Mn1,Fn1) fails and by backtracking you come back to the first clause match(Mn,Fn) that matches, the match is output, etc.
You surely do not want to have this behavior. You should remove the last clause match(Mn1,Fn1) in go. Now by pressing ; you get the 3 matches without any output true in between.
But what you likely want is that the program does the backtracking. To achieve this, you just need to force backtracking by adding false as the last clause. To get proper formatting of the output, use the following program. The last clause go2. is added to get true at the very end.
go2 :- write('--Matching Result--'), nl,
match(Mn,Fn),
write(Mn), write(' match with '), write(Fn), nl,
fail.
go2.
This technique is called failure driven loop.
If you have any predicate that has multiple results and want to to find all of them, you should use findall/3
For example, in your case, you could do something like:
findall([X,Y], match(X,Y),L).
L will be a list that will contain all the X,Y that satisfy match(X,Y) in the format [X,Y].
for example, assuming that:
match(m1,f1).
match(m2,f2).
the result will be L = [ [m1,f1], [m2,f2] ]
note that you can define the format as you wish, for example you could write:
findall(pair(X,Y), match(X,Y), L).
L = [ pair(m1,f1), pair(m2,f2) ]
findall( X, match(X,Y), L).
L = [ m1, m2]
findall( 42, match(X,Y), L).
L = [42, 42]
then you have to recurse on the list to print them.
However, if you wish to find one result, run some code and then continue you could use forall/2:
forall(match(X,Y), my_print(X,Y).
Prolog is a lazy language. Which means that it will stop once it has found a condition that made your problem true. This will be the very first match alone.
IF your code is working (I haven't tried it), then you should try and run the match-statement like this in your prolog inspector: match(X,Y)
The prolog inspector will return all states and print them for you.

Resources