My code runs but the problem is it shows the same results more than once. Here's my code:
disease(hiv,[sore_throat,headache,fever,rash]).
disease(pregnancy,[fatigue,vomiting,light_headedness,increased_waistline]).
disease(flu,[fatigue,fever,tiredness,nasal_discharge]).
diagnose([], []).
diagnose(Name, [H|T]) :-
disease(The_Disease, Symptoms),
member(H, Symptoms),
write(Name), write(' has/is '), writeln(The_Disease),
diagnose(Name, T).
member(X,[X|_]).
member(X,[_|T]):-
member(X,T).
Result when executed in prolog:
?- diagnose(kevin,[sore_throat,fatigue,tiredness,rash]).
kevin has/is hiv
kevin has/is pregnancy
kevin has/is flu
kevin has/is hiv
kevin has/is flu
kevin has/is flu
kevin has/is hiv
false.
How do I avoid that same results? I tried using other method I found here:
filter_doubles([], []).
filter_doubles([X|L], Result) :-
(memberchk(X,L) ->
filter_doubles(L, Result)
;
filter_doubles(L, Result0),
Result = [X|Result0]
).
But I failed to apply it to my code. Help please.
Your program has a pure core - or to stick to medical terms - a pure heart, but this is intertwined with cancerous I/O tissue! In this manner doing it right is very difficult, if not impossible. For example, as a minor error, your program fails for kevin. But you probably meant it to succeed. On the other hand, you will succeed for the mysterious mister []! Who is that?
So lets separate the pure from the impure!
The pure part in your program is about relating a list of symptoms to possible diagnoses. Your working assumption is that if there is one symptom which is part of the indications for a malady, we will diagnose that malady - just to be sure. So why not call this symptoms_diagnosis/2?
symptoms_diagnosis(Symptoms, Diagnosis) :-
member(Symptom, Symptoms),
disease(Diagnosis, Indications),
member(Symptom, Indications).
?- symptoms_diagnosis([sore_throat,fatigue,tiredness,rash], Diagnosis).
Diagnosis = hiv
; Diagnosis = pregnancy
; Diagnosis = flu
; Diagnosis = flu
; Diagnosis = hiv
; false.
Note that even without any further ado, we have less redundant solutions than in your original program. So how to get rid of the remaining redundant solutions? This does the trick:
?- setof(t,symptoms_diagnosis([sore_throat,fatigue,tiredness,rash], Diagnosis),_).
Diagnosis = flu
; Diagnosis = hiv
; Diagnosis = pregnancy.
So whenever you get redundant solutions, simply wrap a setof(t, ..., _) around your goal.
You can use that whenever the answers are ground answers. That is, there is no variable left in the answer.
Maybe you prefer to get the diagnosis in a list of its own?
?- setof(Diagnosis,symptoms_diagnosis([sore_throat,fatigue,tiredness,rash],Diagnosis),Diagnoses).
Diagnoses = [flu,hiv,pregnancy].
So, now we are ready for the Princeton-Plainsboro Teaching Hospital! It is only superstition if Dr. House will not accept Prolog's diagnosis!
For the impure part, please look at #Mog's approach.
Alternatively, you can write :
disease(hiv,[sore_throat,headache,fever,rash]).
disease(pregnancy,[fatigue,vomiting,light_headedness,increased_waistline]).
disease(flu,[fatigue,fever,tiredness,nasal_discharge]).
diagnose(Name, Symptoms) :-
findall(D, (disease(D, S), intersection(S, Symptoms, I), I \== []), MayGot),
atomic_concat(Name, ' has/is ', Start),
maplist(atomic_concat(Start), MayGot, Temp),
maplist(writeln, Temp).
But if you're trying to learn Prolog it's not a good idea since it's more functionnal and less Prolog-ish, thought I'd mention this possibility anyway !
You have to remember what diseases you already collected when checking the symptoms. You need to collect (aggregate) the diseases in a list, and check whether a disease is already present in the list before adding it. You can then print the list at the end or print out each new disease as it is added to the list.
I would implement it this way:
diagnose(Name, Symptoms) :- diagnose0(Name, Symptoms, []).
diagnose0(Name, [], Diseases) :-
print_diseases(Name, Diseases).
diagnose0(Name, [H|T], DIn) :-
disease(Disease, Symptoms),
member(H, Symptoms),
% you may want to add a cut here to avoid choicepoints
(
member(Disease, DIn)
->
diagnose0(Name, T, DIn)
;
DOut = [Disease|DIn],
diagnose0(Name, T, DOut)
).
print_diseases(_Name, []).
print_diseases(Name, [H|T]) :-
write(Name), write(' has/is '), writeln(H),
print_diseases(Name, T).
The disease/2 facts are as in your code.
This gives:
?- diagnose(kevin, [sore_throat, fatigue, tiredness, rash]).
kevin has/is flu
kevin has/is pregnancy
kevin has/is hiv
Yes (0.00s cpu, solution 1, maybe more)
The next step then would obviously be to find a way to express that some diagnoses represent alternatives for a given symptom, and choose between these different alternatives. With the symptoms listed in the query, Kevin should have flu and HIV, but I doubt that pregnancy is a correct diagnosis for Kevin. This is related to the comment about a cut that I inserted in the second clause of diagnose/3: without the cut, you can get more than one solution, each representing a different set of diseases that match the set of symptoms. If you add a cut, you only get the first solution (including pregnancy). The second solution only contains flu and HIV.
BTW, member/2 is a built-in predicate, so you don't need to define your own.
Related
I have been working to understand the pumpming lemma better but I am quite stuck at proving these 2 languages is not regular:
L_1 = {(ab)^n c^m | n>=1, m>=2n }
L_2 = {(ab)^n a^k (ba)^n | k<3}
for the L_2 my approach was:
Let's say there is a number p.
Be the word z=(ab)^p a^k (ba)^p => |z| = 2p > p
and its decomposition may z=uvw with |uv| <= p & |v|>0.
It means that v= (ab)^j with 0<j<=p.
We choose i = 2 for uv^(i)w leads to (ab)^(p+j) a^k (ba)^p.
This Strings has more ab then ba, which means it does not belong to the language.
=> L_2 is not regular
I am actually confused with the (ab)^n, we should decomposed it, so it is necessary to consider different cases of v or is this sufficient?
For L_1, use the string (ab)^p c^2p and point out that pumping can only change the number of a's and b's, never c's, and pumping up will cause the string not to be of the proper form, or m to be less than 2n.
For L_2, use the string (ab)^p(ba)^p and argue that since pumping can only affect the prefix (ab)^p, if pumping keeps that part in the correct format, the number of b's will increase when pumping up, whereas the number of b's in the second part (which is deterministically found by looking after the only occurrence of bb) remains the same; so, the result can't be of the form (ab)^n a^k (ba)^n due to the mismatch in number of b's.
I'm supposed to write a predicate that does some math stuff. But I don't know how to pass numbers or return numbers.
Maybe you can give me an example?
Let's say a predicate divide/2 that takes two numbers a and b and returns a/b.
Yes, you pass numbers in in some arguments, and you get the result back in some other argument(s) (usually last). For example
divide( N, D, R) :-
R is N / D.
Trying:
112 ?- divide(100,5,X).
X = 20.
113 ?- divide(100,7,X).
X = 14.285714285714286.
Now, this predicate is divide/3, because it has three arguments: two for inputs and one for the output "information flow".
This is a simplified, restricted version of what a Prolog predicate can do. Which is, to not be that uni-directional.
I guess "return" is a vague term. Expression languages have expressions e-value-ated so a function's last expression's value becomes that function's "return" value; Prolog does not do that. But command-oriented languages return values by putting them into some special register. That's not much different conceptually from Prolog putting some value into some logvar.
Of course unification is more complex, and more versatile. But still, functions are relations too. Predicates "return" values by successfully unifying their arguments with them, or fail to do so, as shown in the other answer.
Prolog is all about unifying variables. Predicates don't return values, they just succeed or fail.
Typically when a predicate is expected to produce values based on some of the arguments then the left-most arguments are inputs and the right-most are the outputs. However, many predicates work with allowing any argument to be an input and any to be a output.
Here's an example for multiply showing how it is used to perform divide.
multiply(X,Y,Z) :- number(X),number(Y),Z is X * Y.
multiply(X,Y,Z) :- number(X),number(Z),X \= 0,Y is Z / X.
multiply(X,Y,Z) :- number(Y),number(Z),Y \= 0,X is Z / Y.
Now I can query it like this:
?- multiply(5,9,X).
X = 45 .
But I can easily do divide:
?- multiply(5,X,9).
X = 1.8 .
It even fails if I try to do a division by 0:
?- multiply(X,0,9).
false.
Here's another approach. So let's say you have a list [22,24,34,66] and you want to divide each answer by the number 2. First we have the base predicate where if the list is empty and the number is zero so cut. Cut means to come out of the program or just stop don't go to the further predicates. The next predicate checks each Head of the list and divides it by the number A, meaning (2). And then we simply print the Answer. In order for it to go through each element of the list we send back the Tail [24,34,66] to redo the steps. So for the next step 24 becomes the Head and the remaining digits [34,66] become the Tail.
divideList([],0,0):-!.
divideList([H|T],A,Answer):-
Answer is H//A,
writeln(Answer),
divideList(T,A,_).
?- divideList([22,24,34,66],2,L).
OUTPUT:
11
12
17
33
Another simpler approach:
divideList([],_,[]).
divideList([H|T],A,[H1|L]):-
H1 is H//A,!,
divideList(T,A,L).
?-divideList([22,4,56,38],2,Answer).
Answer = [11, 2, 28, 19]
I'm trying to write a predicate that calculates which destination a group of friends will visit.
The friends list their countries of preferences like this
choice(marie, [peru,greece,vietnam]).
choice(jean, [greece,peru,vietnam]).
choice(sasha, [vietnam,peru,greece]).
choice(helena,[peru,vietnam,greece]).
choice(emma, [greece,peru,vietnam]).
I want to write a predicate called where that takes 2 arguments to perform the calculation.
The formula I have in mind is that the first country is worth 3 points, the second one is worth 2 points, and the last one is worth 1 point.
Here's an example of what I'm trying to achieve.
?- where([marie,jean,sasha,helena,emma],Country).
peru .
So far I have this
where([], X).
where([H|T], N) :- choice(H, [A|B]), where(T,N).
It lets me iterate through all the different friends and shows their choices but I can't iterate through the list of choices and assign points to the destinations.
How should I go about iterating through the list of choices for each friend and assigning points to calculate the best destination?
While this will solve your problem, I know it uses many predicates that you have not seen. So think of this an opportunity to excel and learn a lot.
Even if you don't understand it all, there is enough detail and intermediate results in the test that you should be able to navigate your way to a proper solution you create.
Also this is by no means efficient, it was just a quick proof of concept I did to see how this could be done.
choice(marie, [peru,greece,vietnam]).
choice(jean, [greece,peru,vietnam]).
choice(sasha, [vietnam,peru,greece]).
choice(helena,[peru,vietnam,greece]).
choice(emma, [greece,peru,vietnam]).
destinations(Destinations) :-
findall(D1,choice(_,D1),D2),
flatten(D2,D3),
list_to_set(D3,Destinations).
init_weights(Destinations,Weights) :-
empty_assoc(Assoc),
init_weights(Destinations,Assoc,Weights).
init_weights([],Weights,Weights).
init_weights([H|T],Assoc0,Weights) :-
put_assoc(H,Assoc0,0,Assoc1),
init_weights(T,Assoc1,Weights).
update_weights([C1,C2,C3],Weights0,Weights) :-
del_assoc(C1,Weights0,Value0,Weights1),
Value1 is Value0 + 3,
put_assoc(C1,Weights1,Value1,Weights2),
del_assoc(C2,Weights2,Value2,Weights3),
Value3 is Value2 + 2,
put_assoc(C2,Weights3,Value3,Weights4),
del_assoc(C3,Weights4,Value4,Weights5),
Value5 is Value4 + 1,
put_assoc(C3,Weights5,Value5,Weights).
person_weight(Person,Weights0,Weights) :-
choice(Person,[C1,C2,C3]),
update_weights([C1,C2,C3],Weights0,Weights).
people(People) :-
findall(Person,choice(Person,_),People).
choice(Destination) :-
destinations(Destinations),
init_weights(Destinations,Weights0),
people(People),
update_choices(People,Weights0,Weights1),
cross_ref_assoc(Weights1,Weights),
max_assoc(Weights, _, Destination),
true.
cross_ref_assoc(Assoc0,Assoc) :-
assoc_to_list(Assoc0,List0),
maplist(key_reverse,List0,List),
list_to_assoc(List,Assoc).
key_reverse(Key-Value,Value-Key).
update_choices([],Weights,Weights).
update_choices([Person|People],Weights0,Weights) :-
person_weight(Person,Weights0,Weights1),
update_choices(People,Weights1,Weights).
Tests
:- begin_tests(destination).
test(destinations) :-
destinations([peru, greece, vietnam]).
test(init_weights) :-
destinations(Destinations),
init_weights(Destinations,Weights),
assoc_to_list(Weights,[greece-0, peru-0, vietnam-0]).
test(update_weights) :-
destinations(Destinations),
init_weights(Destinations,Weights0),
update_weights([peru,greece,vietnam],Weights0,Weights),
assoc_to_list(Weights,[greece-2,peru-3,vietnam-1]).
test(person_weight) :-
destinations(Destinations),
init_weights(Destinations,Weights0),
person_weight(jean,Weights0,Weights),
assoc_to_list(Weights,[greece-3,peru-2,vietnam-1]).
test(people) :-
people([marie,jean,sasha,helena,emma]).
test(update_choices) :-
destinations(Destinations),
init_weights(Destinations,Weights0),
people(People),
update_choices(People,Weights0,Weights),
assoc_to_list(Weights,[greece-10,peru-12,vietnam-8]).
test(cross_ref_assoc) :-
List0 = [1-a,2-b,3-c],
list_to_assoc(List0,Assoc0),
cross_ref_assoc(Assoc0,Assoc),
assoc_to_list(Assoc,[a-1,b-2,c-3]).
test(choice) :-
choice(peru).
:- end_tests(destination).
As suggested by GuyCoder, you need an accumulator to sum each person preferences, and foldl/N allows to does exactly this.
choice(marie, [peru,greece,vietnam]).
choice(jean, [greece,peru,vietnam]).
choice(sasha, [vietnam,peru,greece]).
choice(helena,[peru,vietnam,greece]).
choice(emma, [greece,peru,vietnam]).
where(People,Where) :-
foldl([Person,State,Updated]>>(choice(Person,C),update(State,C,Updated)),
People,
[0=greece,0=peru,0=vietnam],
Pref),
aggregate(max(S,S=W),member(S=W,Pref),max(_,_=Where)).
% sort(Pref,Sorted),
% last(Sorted,_=Where).
update(S0,[A,B,C],S3) :-
update(S0,3,A,S1),
update(S1,2,B,S2),
update(S2,1,C,S3).
update(L,V,C,U) :-
append(X,[Y=C|Z],L),
P is Y+V,
append(X,[P=C|Z],U).
I have left commented the last two goals replaced by the single goal aggregate/3, so you can try to understand the syntax...
I'm new to Prolog and quite confused. We're just moving in to recursion and members. I have the task to find different combinations of fast food given the type, calories, and price:
burger 450 $5
fries 250 $2
shake 500 $3
salad 150 $7
I created a rule:
findfood(Calories, Price, Burger, Fries, Shake, Salad) :-
member(Burger, [0,1]),
...,
C is Burger*450 + Fries*250 + ...,
Price is Burger*5 + Fries*2 + ...,
C =< Calories.
where '...' indicates a continuation of the previous to the end of the list of food items. Essentially, you can purchase 1 of each item and indicate such with a 0 or a 1 (this is all part of the specifications).
I need to find the most expensive food combination less than or equal to a certain caloric amount. I am allowed to create multiple rules to do this. It should be something like:
|?- most_expensive(1000, Price, Burger, Fries, Shake, Salad)
and it will return the most expensive combination of food for 1000 calories. I know I'll need to do some sort of recursive comparison, but I'm not sure how to proceed. For the record, I understand recursion pretty well, just not Prolog. Can anyone perhaps explain how I might accomplish this?
So, you elided some of the code, but I gather this is what you have:
findfood(Calories, Price, Burger, Fries, Shake, Salad) :-
member(Burger, [0,1]),
member(Fries, [0,1]),
member(Shake, [0,1]),
member(Salad, [0,1]),
Calories is Burger * 450 + Fries * 250 + Shake * 500 + Salad * 150,
Price is Burger * 5 + Fries * 2 + Shake * 3 + Salad * 7.
This seems sensible, if labor-intensive. Now you can easily enumerate all of the possibilities following #mbratch's advice:
all_combos(Combinations) :-
setof((C,P,B,F,Sh,Sa), findfood(C,P,B,F,Sh,Sa), Combinations).
You can also abuse Prolog to get the solution you want from this:
most_expensive_under(MaximumCalories, Calories, Price, Burgers, Fries, Shakes, Salads) :-
all_combos(Combos),
member((Calories, Price, Burgers, Fries, Shakes, Salads), Combos),
Calories =< MaximumCalories,
\+ (member((Calories2, Price2, _, _, _, _), Combos),
Calories2 =< MaximumCalories,
Price2 > Price).
This declares that the most expensive option is the one for which there is no other option satisfying the maximum calorie constraint with a greater price. Trying it seems to succeed:
?- most_expensive_under(1000, Calories, Price, Burgers, Fries, Shakes, Salads).
Calories = 850,
Price = 14,
Burgers = Fries, Fries = Salads, Salads = 1,
Shakes = 0 ;
false.
Bear in mind, this kind of solution is O(N^2). I bring it up really only to illustrate the power of unification—it's not a great idea, performance-wise. A better idea would be to use setof/3, which sorts its results, as #mbratch suggests, or library(aggregate), which I've never used but hear good things about.
How would you code a program in Prolog to print numbers from 1 to 10 using recursion?
I've tried the following but it doesn't work, can you tell me why?
print_numbers(10) :- write(10).
print_numbers(X) :- write(X),nl,X is X + 1, print_numbers(X).
Your code is very close to working. The problem is that you cannot reuse X, once it is instantiated, it cannot be changed (see here for more details). Use a new variable, like this:
print_numbers(10) :- write(10), !.
print_numbers(X) :- write(X), nl, Next is X + 1, print_numbers(Next).
Adding the cut (!) to the end will prevent the interpreter from asking if you want to see more results.
?- print_numbers(1).
1
2
3
4
5
6
7
8
9
10
Yes
?-
print_from_1_to_10 :-
print_from_X_to_10(1).
print_from_X_to_10(X) :-
(
X > 10
->
fail
;
writeln(X),
NewX is X + 1,
print_from_X_to_10(NewX)
).
Been a seriously long time since I wrote any prolog but I'd probably do things just a little differently. Something like this, though I can't test it at the momment.
print_increasing_numbers(From, To):- From > To, !, write('ERROR: From > To').
print_increasing_numbers(To, To):- !, write(To).
print_increasing_numbers(From, To):- write(From),
nl,
Next is From + 1,
print_increasing_numbers(Next, To).
A key difference here is the !, or cut operation, which stops backtracking. If you don't include it then you will get a solution with the first clause when X is 10,but if you ask for a second solution it will backtrack and match the second clause as well. That would result in a much larger list of numbers than you want.
Just call defineXandY from terminal
defineXandY :-
print_one_to_ten(1,10).
print_one_to_ten(X,Y) :-
X<Y,
write(X),nl,
NewX is X+1,
print_one_to_ten(NewX,Y).