recursionprologprolog-setof

Prolog Recursion skipping same results


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.


Solution

  • 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.