2011-11-29 44 views
4

我的代码运行但问题是它不止一次显示相同的结果。当在序言执行序言递归跳过相同的结果

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

结果:

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

但我没能将它应用到我的代码。请帮助。

+0

在回复诊断之前,它是否打算检查疾病的所有症状? – hardmath

+0

@hardmath是的,这是计划。 – Mezzan

回答

6

你的程序有一个纯粹的核心 - 或者坚持医学术语 - 一颗纯洁的心,但是它与癌症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的诊断,那只是迷信!

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

+1

谢谢@false!随着你的解释,我开始更好地理解序言。对不起,关于它应该是名称的神秘[]。是的,我宁愿将诊断列入自己的清单。 – Mezzan

+1

欢迎!在学习Prolog时,尽可能远离侧效内置插件。这是Prolog的纯粹部分,使它非常有趣。 – false

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的第二个句子中插入的剪辑的评论有关:如果没有剪切,您可以获得多个解决方案,每个解决方案代表与疾病集合相匹配的一组不同的疾病。如果你添加一个剪辑,你只能得到第一个解决方案(包括怀孕)。第二种解决方案只包含流感和艾滋病毒。

顺便说一句,member/2是一个内置的谓词,所以你不需要定义你自己的。

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它,因为它更functionnal和更少的Prolog十岁上下是不是一个好主意,以为我反正提到这种可能性!

+3

非常好!我发现maplist/2和maplist/3也是非常Prolog-ish - 使代码更具有Prolog-ish,这是一个小小的建议:与疾病/ 2相反,我们可以将谓词例如disease_symptoms/2称为更具描述性。 – mat

+2

@Mog像你一样执行I/O更好,而不是失败驱动循环中的“传统”方式!通过这种方式,I/O部分中的错误将以非预期的失败告终。 – false

+0

To mat:我试图不重命名OP的事实,但我同意disease_symptoms会更具可读性!为了虚假:谢谢你的支持! – m09