Reputation: 55
I have a database of facts that has entries like this snippet
symptom(shingles,headache).
symptom(shingles,fever).
symptom(shingles,malaise).
symptom(shingles,headache).
symptom(shingles,itching).
symptom(shingles,hyperesthesia).
symptom(shingles,paresthesia).
test(shingles,blood).
test(shingles,pcr).
locale(shingles,all).
treatment(shingles,calamine).
treatment(shingles,aciclovir).
treatment(shingles,valaciclovir).
treatment(shingles,famciclovir).
treatment(shingles,corticosteroids).
I then have a predicate that gets a list of symptoms from the user.
getSymptoms(Symptoms) :-
write('Please enter symptoms now, enter "Done" when finished: ' ),
read_string(user, "\n", "\r", _, Response),
(
Response == "Done"
->
Symptoms = []
;
getSymptoms(Symptoms0),
Symptoms = [Response|Symptoms0]
).
My question is how do I compare the lists of user symptoms to the symptoms fact second atom, and then add the disease to another list? For example, the user enters fever. Since fever is in the symptom fact for shingles, it would add shingles to a list.
Upvotes: 1
Views: 152
Reputation: 24976
This will work, but allows for duplicate symptoms to be entered. I am posting it now so you can see the first part of the transformation and will post the part without the duplicates when I get that working.
getSymptoms(Symptoms) :-
write('Please enter symptoms now, enter "Done" when finished: ' ),
read_string(user, "\n", "\r", _, Response),
(
Response == "Done"
->
Symptoms = []
;
atom_string(Symptom,Response),
valid_symptom(Symptom,Symptoms)
).
valid_symptom(Symptom,Symptoms) :-
(
symptom(_,Symptom)
->
% Symptom was valid
% so get next symptom and
% add to list on backtracking
getSymptoms(Symptoms0),
Symptoms = [Symptom|Symptoms0]
;
% Symptom was invalid
% so warn user of invalid symptom and what they input
% and get next symptom.
% Do not add invalid Symptom to list on backtracking.
format('Invalid symptom: `~w''~n',[Symptom]),
getSymptoms(Symptoms0),
Symptoms = Symptoms0
).
Since the values entered are strings and the symptoms are atoms in symptom/2
facts, the input needs to be converted to an atom for comparison. This is done using atom_string/2
atom_string(Symptom,Response)
To give a user feedback if the symptom is invalid format/2 is used. It is better to use than write/1 since it gives you more control over the output.
format('Invalid symptom: `~w''~n',[Symptom])
If an invalid value is entered for as a symptom it should not be added to the list. This is a classic if/then type of scenario and in Prolog is done with ->/2. Here is the standard template
(
<conditional>
->
<true branch>
;
<false branch>
)
the conditional is
symptom(_,Symptom)
Notice also with the conditional, that it reads the symptom/2
facts and ignores the first part of the compound structure, i.e. _
, and matches the input symptom with a symptom in the facts. This is were the comparison is done, but it is done via unification and not with a compare predicate such as ==/2.
The true branch is the same as before
getSymptoms(Symptoms0),
Symptoms = [Symptom|Symptoms0]
however the false branch is
format('Invalid symptom: `~w''~n',[Symptom]),
getSymptoms(Symptoms0),
Symptoms = Symptoms0
Notice that the invalid Symptom
is not added to the list with [Symptom|Symptoms0]
and also that both branches (true and false) should update the same variables, Symptoms
, which in the false branch is done with Symptoms = Symptoms0
which is not assignment but =/2 (unification).
The code for valid_symptom/2
could have been inlined with getSymptoms/1
but I pulled it out so you could see how it is done in case you need to do that in the future.
Example run:
?- getSymptoms(Symptoms).
Please enter symptoms now, enter "Done" when finished: wrong
Invalid symptom: `wrong'
Please enter symptoms now, enter "Done" when finished: headache
Please enter symptoms now, enter "Done" when finished: malaise
Please enter symptoms now, enter "Done" when finished: headache
Please enter symptoms now, enter "Done" when finished: Done
Symptoms = [headache, malaise, headache].
Here is the next variation that removes duplicates while building the list.
getSymptoms(Result) :-
getSymptoms_helper([],Result).
getSymptoms_helper(Symptoms,Result) :-
write('Please enter symptoms now, enter "Done" when finished: ' ),
read_string(user, "\n", "\r", _, Response),
(
Response == "Done"
->
Result = Symptoms
;
atom_string(Symptom,Response),
valid_symptom(Symptom,Symptoms,Result)
).
valid_symptom(Symptom,Symptoms,Result) :-
(
memberchk(Symptom,Symptoms)
->
% Symptom was a duplicate
% Do not add duplicate Symptom to list.
getSymptoms_helper(Symptoms,Result)
;
(
symptom(_,Symptom)
->
% Symptom was valid
% so get next symptom and
% add to list.
getSymptoms_helper([Symptom|Symptoms],Result)
;
% Symptom was invalid
% so warn user of invalid symptom and what they input
% and get next symptom.
% Do not add invalid Symptom to list.
format('Invalid symptom: `~w''~n',[Symptom]),
getSymptoms_helper(Symptoms,Result)
)
).
The major change here is that an accumulator Symptoms
is threaded through the predicates so that the list of valid symptoms can be built and used for testing the next input value. Since the accumulator needs to be initialized at the start, the previous predicate renamed to getSymptoms_helper
so that the accumulator can be initialized with
getSymptoms_helper([],Result)
Notice []
that passes to
getSymptoms_helper(Symptoms,Result)
thus setting the initial value of Symptoms
to []
.
When Done
is entered, the list is unified with Result
and passed back upon back-chaining. Normally the variables would be named Symptoms0
and Symptoms
but I kept them this way so they are easier to follow. Other wise there would be variables Symptom
, Symptoms
and Symptoms0
, but once you get use to them are easier to follow.
The check for duplicates is done using memberchk/2 which is better than using member/2 for checking. Again this adds another conditional to the mix.
Example run:
?- getSymptoms(Symptoms).
Please enter symptoms now, enter "Done" when finished: headache
Please enter symptoms now, enter "Done" when finished: malaise
Please enter symptoms now, enter "Done" when finished: headache
Please enter symptoms now, enter "Done" when finished: Done
Symptoms = [malaise, headache].
Upvotes: 1