Why don't I need to check in the member_of_set if Element and Element1 are different? - prolog

When using sets, adding an Element to the Set is done like this:
add_to_set(Element, [], [Element]).
add_to_set(Element, [Element | Set], [Element | Set]).
add_to_set(Element, [Element1 | Set], [Element1 | NewSet]) :-
not(Element = Element1),
add_to_set(Element, Set, NewSet).
Now, with this, I thought member_of_set would be like:
member_of_set(Element, [Element|_]).
member_of_set(Element, [Element1|Set]) :-
not(Element = Element1), /* Not necessary */
member_of_set(Element, Set).
This works like a charm, but in this case, not(Element = Element1) is not necessary. I can't seem to figure out why. If you ask for more answers from Prolog, won't it backtrack and succeed on the second clause of member_of_set?
An if it's not necessary in the member_of_set, then why is it necessary in the add_to_set?
Please keep in mind that I'm only studying since one month Prolog, so I'm still in some kind of mind switch...
I know that using cut, there's probably better alternatives, but cut shouldn't be used.

it's not necessary in the member_of_set
because it doesn't hurt if the list is not a set. Only you will end up - in case you pass a list with repeated elements inside - with multiple solution, but still each solution is valid.
OTOH, add_to_set leads to invalid data if you remove the test:
?- add_to_set(1,[],A),add_to_set(1,A,B).
A = B, B = [1] ;
A = [1],
B = [1, 1] ;
false.
for a friendly explanation of cuts, and why are necessary in Prolog, see this page

contains(Element, [Element|_]).
contains(Element, [_|Set]) :-
contains(Element, Set).
You can read this as: "Element is in a Set, if it's the head of that Set, or if it's in the tail of that Set". If you're checking if it's in the tail, there's no need to check if it's equal to the head, except for performance reasons.
Actually, this way, you're not even restricting it to a Set. It's a general contains.

Related

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.

I want to sum in SWI-Prolog depending on certain user answers

I need to sum in a variable depending on certain user answers and I'm starting to know Prolog syntax and paradigm.
Right now, I can read user data, and print it too, but I lack a way to accumulate the results, because right now the results are inconsistent.
What I have now is:
inicio :-
write('¿You have overweight yes/no?'),
read(R1),
write('¿Are you a smoker yes/no?'),
read(R2),
write('¿Do you have some direct relative with diabetes?'),
read(R3),
Risk is 0,
( R1 = yes -> Risk is Risk + 2 ; Risk is Risk + 0 ),
imprimir(['The result is ', Risk]),
( R2 = yes -> Risk is Risk + 1 ; Risk is Risk + 0 ),
imprimir(['The result is ', Risk]),
( R3 = yes -> Risk is Risk + 3 ; Risk is Risk + 0 ),
imprimir(['The result is ', Risk]).
imprimir([]).
imprimir([Term| Terms]) :-
write(Term),
imprimir(Terms).
I'm going to show you a fundamentally different way of approaching this program that leverages Prolog a little better. First, let's make a table of the penalties. Making tables for your program's configuration is often a useful thing to do:
risk_penalty(overweight, 2).
risk_penalty(smoker, 1).
risk_penalty(diabetes, 3).
Now that we have a uniform way of thinking about the problem, let's see if we can make a uniform way of getting information from the user. Let's use the dynamic store to keep track of what the user has told us, because it will simplify querying later:
:- dynamic risk/2.
ask(Prompt, Fact) :-
format('~a [yes/no]> ', [Prompt]),
read(Response),
assertz(risk(Fact, Response)).
Now we have a little predicate we can use to interview the user. This kind of print-read-assert function is pretty common in small expert systems like yours, because it helps you separate the logic of the system from its front-end. When you do ask('Do you have X?', has_x), the dynamic store will either receive risk(has_x, yes) or risk(has_x, no) depending on which the user has entered. It also gives you a natural place to make the user input more robust, by checking it and re-asking if you get something weird.
Now we can do your initial loop a little more cleanly:
inicio :-
ask('Are you overweight?', overweight),
ask('Are you a smoker?', smoker),
ask('Do you have some direct relative with diabetes?', diabetes).
This just does the interview portion. Now if you run through it once, say answering "yes", "no", "yes", then the database will contain these facts:
?- risk(Factor, Response).
Factor = overweight,
Response = yes ;
Factor = smoker,
Response = no ;
Factor = diabetes,
Response = yes.
What we need to do now is select out "yes" factors and then look up their penalties and add them up. To do this, we can use findall/3, which takes a Template, a Goal, and gives back a result list:
?- findall(risk(Factor, Response), risk(Factor, Response), Responses).
Responses = [risk(overweight, yes), risk(smoker, no), risk(diabetes, yes)].
As you can see, I used the same template and goal here, just to see all the results, but we can put "yes" in to filter it down to just the risk factors we care about:
?- findall(risk(Factor), risk(Factor, yes), Responses).
Responses = [risk(overweight), risk(diabetes)].
Now you can see that the Template (first argument) is just some arbitrary structure populated with the variables that findall/3 found by running Goal, the second argument. So we could also just obtain the list of penalty values, if we look them up inside the Goal query. Like this:
?- findall(Penalty, %% <- template
(risk(Factor, yes), risk_penalty(Factor, Penalty)), %% <- goal
Penalties). %% <- result
Penalties = [2, 3].
We can then follow this with just sumlist/2 to add everything up:
?- findall(Penalty,
(risk(Factor, yes), risk_penalty(Factor, Penalty)),
Penalties),
sumlist(Responsa, Score).
Responsa = [2, 3],
Score = 5.
Now we can finish the inicio/0 predicate:
inicio :-
retractall(risk(_, _)),
ask('Are you overweight?', overweight),
ask('Are you a smoker?', smoker),
ask('Do you have some direct relative with diabetes?', diabetes)
findall(Penalty,
(risk(Factor, yes), risk_penalty(Factor, Penalty)), Penalties),
sumlist(Penalties, Score),
format('The result is ~a~n', [Score]).
This now looks like this when run:
?- inicio.
Are you overweight? [yes/no]> yes.
Are you a smoker? [yes/no]> |: no.
Do you have some direct relative with diabetes? [yes/no]> |: yes.
The result is 5
true.
I hope you find the result pleasing to look at, much less procedural, and easier to modify and maintain.

Parse To Prolog Variables Using DCG

I want to parse a logical expression using DCG in Prolog.
The logical terms are represented as lists e.g. ['x','&&','y'] for x ∧ y the result should be the parse tree and(X,Y) (were X and Y are unassigned Prolog variables).
I implemented it and everything works as expected but I have one problem:
I can't figure out how to parse the variable 'x' and 'y' to get real Prolog variables X and Y for the later assignment of truth values.
I tried the following rule variations:
v(X) --> [X].:
This doesn't work of course, it only returns and('x','y').
But can I maybe uniformly replace the logical variables in this term with Prolog variables? I know of the predicate term_to_atom (which is proposed as a solution for a similar problem) but I don't think it can be used here to achieve the desired result.
v(Y) --> [X], {nonvar(Y)}.:
This does return an unbound variable but of course a new one every time even if the logical variable ('x','y',...) was already in the term so
['X','&&','X'] gets evaluated to and(X,Y) which is not the desired result, either.
Is there any elegant or idiomatic solution to this problem?
Many thanks in advance!
EDIT:
The background to this question is that I'm trying to implement the DPLL-algorithm in Prolog. I thought it would by clever to directly parse the logical term to a Prolog-term to make easy use of the Prolog backtracking facility:
Input: some logical term, e.g T = [x,'&&',y]
Term after parsing: [G_123,'&&',G_456] (now featuring "real" Prolog variables)
Assign a value from { boolean(t), boolean(f) } to the first unbound variable in T.
simplify the term.
... repeat or backtrack until a assignment v is found so that v(T) = t or the search space is depleted.
I'm pretty new to Prolog and honestly couldn't figure out a better approach. I'm very interested in better alternatives! (So I'm kinda half-shure that this is what I want ;-) and thank you very much for your support so far ...)
You want to associate ground terms like x (no need to write 'x') with uninstantiated variables. Certainly that does not constitute a pure relation. So it is not that clear to me that you actually want this.
And where do you get the list [x, &&, x] in the first place? You probably have some kind of tokenizer. If possible, try to associate variable names to variables prior to the actual parsing. If you insist to perform that association during parsing you will have to thread a pair of variables throughout your entire grammar. That is, instead of a clean grammar like
power(P) --> factor(F), power_r(F, P).
you will now have to write
power(P, D0,D) --> factor(F, D0,D1), power_r(F, P, D1,D).
% ^^^^ ^^^^^ ^^^^
since you are introducing context into an otherwise context free grammar.
When parsing Prolog text, the same problem occurs. The association between a variable name and a concrete variable is already established during tokenizing. The actual parser does not have to deal with it.
There are essentially two ways to perform this during tokenization:
1mo collect all occurrences Name=Variable in a list and unify them later:
v(N-V, [N-V|D],D) --> [N], {maybesometest(N)}.
unify_nvs(NVs) :-
keysort(NVs, NVs2),
uniq(NVs2).
uniq([]).
uniq([NV|NVs]) :-
head_eq(NVs, NV).
uniq(NVs).
head_eq([], _).
head_eq([N-V|_],N-V).
head_eq([N1-_|_],N2-_) :-
dif(N1,N2).
2do use some explicit dictionary to merge them early on.
Somewhat related is this question.
Not sure if you really want to do what you asked. You might do it by keeping a list of variable associations so that you would know when to reuse a variable and when to use a fresh one.
This is an example of a greedy descent parser which would parse expressions with && and ||:
parse(Exp, Bindings, NBindings)-->
parseLeaf(LExp, Bindings, MBindings),
parse_cont(Exp, LExp, MBindings, NBindings).
parse_cont(Exp, LExp, Bindings, NBindings)-->
parse_op(Op, LExp, RExp),
{!},
parseLeaf(RExp, Bindings, MBindings),
parse_cont(Exp, Op, MBindings, NBindings).
parse_cont(Exp, Exp, Bindings, Bindings)-->[].
parse_op(and(LExp, RExp), LExp, RExp)--> ['&&'].
parse_op(or(LExp, RExp), LExp, RExp)--> ['||'].
parseLeaf(Y, Bindings, NBindings)-->
[X],
{
(member(bind(X, Var), Bindings)-> Y-NBindings=Var-Bindings ; Y-NBindings=Var-[bind(X, Var)|Bindings])
}.
It parses the expression and returns also the variable bindings.
Sample outputs:
?- phrase(parse(Exp, [], Bindings), ['x', '&&', 'y']).
Exp = and(_G683, _G696),
Bindings = [bind(y, _G696), bind(x, _G683)].
?- phrase(parse(Exp, [], Bindings), ['x', '&&', 'x']).
Exp = and(_G683, _G683),
Bindings = [bind(x, _G683)].
?- phrase(parse(Exp, [], Bindings), ['x', '&&', 'y', '&&', 'x', '||', 'z']).
Exp = or(and(and(_G839, _G852), _G839), _G879),
Bindings = [bind(z, _G879), bind(y, _G852), bind(x, _G839)].

Prolog Undefined Procedure

I'm trying to to get a list of three items with their relevant information based on a collection of information:
product(I):-
I = [_,_,_,_], %Type,Brand,Category,Value
cheaper(item(apple,_,_),item(_,kay,_,_),I),
cheaper(item(bar,_,_,_),item(_,_,fruit,_),I),
member(item(_,kay,_,2),I),
member(item(apple,granny,_,_),I),
member(item(bar,_,chocolate,_),I),
/* Below not given */
member(item(cracker,_,_,_),I),
member(item(_,_,biscuit,_),I),
member(item(_,_,_,4),I),
member(item(_,_,_,5),I).
cheaper(X,Y,H) :- %Used to determine the item values
item(X,_,_,A),
item(Y,_,_,B),
A<B.
When I try running it I encounter an error:
?- product(I).
ERROR: cheaper/3: Undefined procedure: item/4
Exception: (8) item(item(apple, _G3604, _G3605), _G3651, _G3652, _G3653) ?
I understand that item isn't a procedure, however what can I used for checking the value for apple against the value for bar?
First, the obvious note, you're calling cheaper wrong once:
cheaper(item(apple,_,_),item(_,kay,_,_),I),
↑
Only three values, not four.
If item isn't a procedure, you mustn't call it, but use destructuring.
Also you want those items you're checking with cheaper to be part of the list, right? If so, you'll have to check that. And you can use unification to extract the values that you need:
cheaper(X,Y,I) :-
member(X,I),
member(Y,I),
[item(_,_,_,A),item(_,_,_,B)] = [X,Y],
A<B.
Now you'll get some errors regarding not instantiated argument. That's because you are checking not (yet) set variables if they are greater than each other. To avoid this, move the cheaper/3 calls to the end of your clause body:
product(I):-
I = [_,_,_,_], %Type,Brand,Category,Value
member(item(_,kay,_,2),I),
member(item(_,_,_,4),I),
member(item(_,_,_,5),I),
member(item(apple,granny,_,_),I),
member(item(bar,_,chocolate,_),I),
/* Below not given */
member(item(cracker,_,_,_),I),
member(item(_,_,biscuit,_),I),
cheaper(item(apple,_,_,_),item(_,kay,_,_),I), % note the 4th argument
cheaper(item(bar,_,_,_),item(_,_,fruit,_),I).
With this, you'll get one solution and then it fails with an error. This is, because you only give three values for the price slot and you have four items and prolog will check A > 2.
Sorry, in my other answer I didn't look for what the poster was trying to achieve and I think that this is better than a complete reedit. (glorious SO mods let me know if I'm wrong)

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