同じ結果をスキップするプロログ再帰
-
25-10-2019 - |
質問
私のコードは実行されますが、問題は同じ結果が複数回表示されていることです。これが私のコードです:
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]
).
しかし、私はそれを自分のコードに適用できませんでした。助けてください。
解決
あなたのプログラムには純粋なコアがあります - または医学用語に固執する - 純粋な心臓がありますが、これは癌のI/O組織と絡み合っています!このように、それを正しく行うことは非常に困難ではありませんが、不可能ではありません。たとえば、マイナーなエラーとして、プログラムは kevin
. 。しかし、あなたはおそらく成功することを意味していました。一方、あなたは神秘的なミスターのために成功するでしょう []
!あれは誰?
だから、純粋なものを不純なものから分離しましょう!
プログラムの純粋な部分は、症状のリストを診断の可能性に関連付けることです。あなたの実用的な仮定は、病気の兆候の一部である症状が1つある場合、その病気を診断します - 確かに。それで、なぜこれを呼んでみませんか 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.
それ以上のADOがなくても、私たちは持っていることに注意してください 以下 元のプログラムよりも冗長なソリューション。では、残りの冗長なソリューションを取り除くにはどうすればよいですか?これはトリックを行います:
?- 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].
それで、今、私たちはプリンストン・プレーンズボロ教育病院の準備ができています! Dr. HouseがPrologの診断を受け入れない場合にのみ迷信です!
不純な部分については、 @Mogのアプローチをご覧ください。
他のヒント
または、次のように書くことができます。
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-Inが少ないので、それは良い考えではありません、とにかく私はこの可能性について言及したと思いました!
症状をチェックするときにすでに収集した病気を覚えておく必要があります。リスト内の病気を収集(凝集させる)し、それを追加する前にリストにすでに病気が存在するかどうかを確認する必要があります。その後、最後にリストを印刷するか、リストに追加された新しい病気のそれぞれを印刷することができます。
私はそれをこのように実装します:
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)
次のステップは、明らかに、特定の症状の代替案を表し、これらの異なる代替案を選択することを表現する方法を見つけることです。クエリに症状がリストされているため、ケビンにはインフルエンザとHIVが必要ですが、妊娠はケビンの正しい診断であるとは思いません。これは、2番目の句に挿入したカットについてのコメントに関連しています diagnose/3
: :カットがなければ、それぞれが症状のセットに合った異なる疾患のセットを表す複数のソリューションを取得できます。カットを追加すると、最初の解決策(妊娠を含む)のみが取得されます。 2番目のソリューションには、インフルエンザとHIVのみが含まれています。
ところで、 member/2
組み込みの述語なので、独自を定義する必要はありません。