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

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

但我未能将它应用到我的代码中。请帮忙。

4

3 回答 3

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 的方法。

于 2011-11-29T11:26:34.413 回答
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,我想我还是会提到这种可能性!

于 2011-11-29T09:40:17.937 回答
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:没有切口,您可以获得多个解决方案,每个解决方案代表与一组症状相匹配的一组不同的疾病。如果你添加一个切口,你只会得到第一个解决方案(包括怀孕)。第二种解决方案只包含流感和艾滋病毒。

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

于 2011-11-29T07:24:43.960 回答