Prolog递归跳过相同的结果

时间:2011-11-29 04:22:05

标签: recursion prolog prolog-setof

我的代码运行但问题是它不止一次显示相同的结果。这是我的代码:

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

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

如何避免同样的结果?我尝试使用我在这里找到的其他方法:

filter_doubles([], []).
filter_doubles([X|L], Result) :-
    (memberchk(X,L) ->
        filter_doubles(L, Result)
    ;
        filter_doubles(L, Result0),
        Result = [X|Result0]
    ).

但是我没有把它应用到我的代码中。请帮助。

3 个答案:

答案 0 :(得分:7)

你的程序有一个纯粹的核心 - 或坚持医学术语 - 纯洁的心脏,但这与癌症的I / O组织交织在一起!以这种方式做正确的事情是非常困难的,如果不是不可能的话。例如,作为次要错误,您的程序在kevin失败。但你可能意味着它会成功。另一方面,你将成功为神秘的先生[]!那是谁?

所以让我们把纯净与不纯洁分开!

您的计划中的纯粹部分是将症状列表与可能的诊断相关联。你的工作假设是,如果有一种症状是疾病症状的一部分,我们将诊断出这种疾病 - 只是为了确定。那么为什么不称之为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.

请注意,即使没有任何进一步的麻烦,我们还有冗余解决方案,而不是原始程序。那么如何摆脱剩余的冗余解决方案呢?这就是诀窍:

?- setof(t,symptoms_diagnosis([sore_throat,fatigue,tiredness,rash], Diagnosis),_).
Diagnosis = flu ;
Diagnosis = hiv ;
Diagnosis = pregnancy.

因此,无论何时获得冗余解决方案,只需将setof(t, ..., _)包裹在目标周围即可。 只要答案是基础答案,您就可以使用它。也就是说,答案中没有剩下变量。

也许您更愿意将诊断列入自己的列表中?

?- setof(Diagnosis,symptoms_diagnosis([sore_throat,fatigue,tiredness,rash],Diagnosis),Diagnoses).
Diagnoses = [flu, hiv, pregnancy].

所以,现在我们已经为普林斯顿普林斯伯勒教学医院做好了准备!如果House博士不接受Prolog的诊断,那只是迷信!

对于不纯的部分,请看@Mog的方法。

答案 1 :(得分:3)

或者,你可以写:

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

但是,如果你正在努力学习Prolog,那它不是一个好主意,因为它更具功能性而且更少Prolog-ish,我认为无论如何我都会提到这种可能性!

答案 2 :(得分:2)

您必须记住在检查症状时已收集的疾病。您需要在列表中收集(聚合)疾病,并在添加之前检查列表中是否已存在疾病。然后,您可以在结尾处打印列表,或在每个新疾病添加到列表时打印出来。

我会这样实现:

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

disease/2事实与您的代码一样。

这给出了:

?- 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)

接下来的一步显然是找到一种方法来表达某些诊断代表给定症状的替代方案,并在这些不同的替代方案之间进行选择。鉴于查询中列出的症状,凯文应该感染流感和艾滋病病毒,但我怀疑怀孕是凯文的正确诊断。这与我在diagnose/3的第二个条款中插入的关于切割的评论有关:没有切割,您可以获得多个解决方案,每个解决方案代表与症状集匹配的不同疾病集。如果你添加一个切口,你只能获得第一个解决方案(包括怀孕)。第二种解决方案只含有流感和HIV。

BTW,member/2是内置谓词,因此您无需定义自己的谓词。