【问题标题】:Prolog Recursion skipping same resultsProlog递归跳过相同的结果
【发布时间】:2011-11-29 04:22:05
【问题描述】:

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

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

但我未能将其应用于我的代码。请帮忙。

【问题讨论】:

  • 您是否打算在返回诊断结果之前检查该疾病的所有症状是否存在?
  • @hardmath 是的,这就是计划。

标签: recursion prolog prolog-setof


【解决方案1】:

您的程序有一个纯粹的核心 - 或者坚持医学术语 - 一个纯粹的心脏,但这与癌性 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的做法。

【讨论】:

  • 谢谢@false!通过您的解释,我开始更好地理解prolog。对不起,它应该是名称的神秘 []。是的,我更喜欢在自己的列表中得到诊断。
  • 欢迎您!在学习 Prolog 时,尽量远离有副作用的内置函数。正是 Prolog 的纯粹部分使它如此有趣。
【解决方案2】:

或者,你可以写:

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,我想我还是会提到这种可能性!

【讨论】:

  • 非常好!我发现 maplist/2 和 maplist/3 也非常类似于 Prolog ——为了使代码更类似于 Prolog,一个小建议:我们可以将例如 disease_symptoms/2 的谓词称为更具描述性的谓词,而不是 disease/2。
  • @Mog 像你一样执行 I/O 比失败驱动循环中的“传统”方式要好得多!这样,I/O 部分中的错误就会变成意外故障。
  • 致垫:我尽量不重命名 OP 的事实,但我同意 disease_symptoms 会更具可读性!错误:感谢您指出这一点!
  • @Mog 非常感谢!您的解决方案完美!我需要了解所有 findall/3 和 maplist/2 和 maplist/3,然后才能继续进行。
  • 如果您遇到困难,请随时提出更多问题,因为有时很难找到文档!
【解决方案3】:

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

我会这样实现它:

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 是一个内置谓词,因此您无需定义自己的谓词。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2018-03-04
    • 1970-01-01
    • 1970-01-01
    • 2020-11-02
    相关资源
    最近更新 更多