まず第一に、あなたが提供したコードにタイプミスがありcame_in_contact
ますcontact
。マイナーな問題。
2番目の問題:これが何を意味するのかわかりません:ここで単に の代わりにfindall([X], sick(X), R).
使用する特別な理由はなく、この変更により結果が少し良く見えます:[X]
X
X = [ella, ella, james, james, james, carl, carl|...].
より重要な問題は、スタイル的にsetill
は、ステートフルな名前にもかかわらず、100% 副作用があることです。setill
誰かを「病気」に「設定」するのではなく、誰かが病気であることを標準出力に出力するだけです。これが MVC である場合、これは「ビュー」の一部であると言うかもしれません。そして、あなたが抱えている問題の一部は、この「ビュー」コードを「モデル」の奥深くから呼び出していることですsick/2
.
あなたはエラがアウトブレイクの起源であると括弧書きで述べていますが、あなたの Prolog データベースには事実がないため、Prolog は確かにそれを認識していません。さらに、あなたは感染がたどった「パス」に関心があるようですが、Prolog はパスについて何も知りません。実際、事実データベースを吐き出しているだけです。それを証明するために、一番上に新しい事実を追加しましょう:
interact(gail, hank).
案の定、ゲイルとハンクはグラフの残りの部分から分離されていますが、これが最初の「ソリューション」になりました。
gail is ill
gail has had contact with hank
… (old output repeated)
...
だから、あなたはここの雑草の中でちょっとオフです。不完全なファクト データベースがあり、ルールが問題のロジックを実際に捉えておらず、ロジックが印刷に散在しています。ここまで完了すると、コードはかなり異なったものになっているはずです。これが宿題かどうかはわかりませんが、独学のように聞こえますが、宿題のような雰囲気があるので、すべてをまとめずにどのように進めるかをスケッチしてみます.
まず、解を計算するために必要なすべての事実を Prolog に認識させる必要があります。つまり、発信者に関する事実を追加する必要があります。
infected(ella) :- !.
これがベースケースになります。ここで、帰納的推論を使用して、その人が感染した人と接触した場合、その人は感染していると言う必要があります。
infected(X) :- interact(X, Y), X \= Y, infected(Y), !.
注: これらのカットはかなり重要です。人が感染しているかどうかにかかわらず、別の解を計算する必要はありません。いずれかのブランチで感染していることを証明できれば、何も言うことはありません。
これで、一部の人々にとって合理的な解決策を得ることができます。
?- infected(ella).
true.
?- infected(gail).
false.
他の人は解決策がないようです:
?- infected(james).
(I typed Ctrl+C)
^CAction (h for help) ? abort
% Execution Aborted
James が解決策にたどり着けない理由は、Prolog が深さ優先探索を使用しているためです。手軽に、次にやらなければならないことは、感染経路を発見することです。そのため、Prolog が既に経路内にいる人を試すのを防ぐことができれば、必要な経路を取得することで問題を解決できます。同様の基本ケース/誘導ケース構造を採用する必要がありますが、感染経路について追加の引数を渡す必要があります。この種の例はいたるところにあるので、ここで詳細を説明して退屈させることはしません。
これに注意してください:問題のロジックと結果の表示を混在させるつもりはありません。これは、バックトラッキングがあるため、Prolog の適切なポリシーです。ここでバインドが成功し、次の用語で失敗したために何かを印刷すると、失敗全体が印刷出力を超えて戻ってきて、混乱したユーザーが残る可能性があります。Prologをだまして、後で失敗したソリューションから嘘を印刷させることは簡単です。したがって、ソリューションを見つけて個別に表示するように、常にProlog を作成する必要があります。モデル-ビュー-コントローラーを考えてみてください。
path/3
では、述語(おそらく)を見つけたと仮定しましょうpath(Source, Last, Path)
。実行すると、次のようなソリューションが得られます。
?- path(ella, X, Path).
X = sven
Path = [ella, james, ...] ;
X = sven
Path = [ella, tyrone, ...] ;
false.
これは、 でラップしたい述語です。findall/3
次に、結果を調べて、必要な部分をパスごとに出力します。
編集:あなたのコメントに応えて、あなたの新しい述語を見てみましょう:
path(_, X, P) :- findall(X, interact(_, X), P).
残念ながら、これは以前よりも近くにはありません。自分自身にパスを尋ねるとどうなるか見てみましょう。
?- path('Daniel Lyons', X, Path).
Path = [james, tyrone, ben, frank, carl, james, evan, mike|...].
実際、そこには絶対に何でも入れることができ、まったく同じ結果が得られます。
?- path('Jack Donaghy', X, Path).
Path = [james, tyrone, ben, frank, carl, james, evan, mike|...].
?- path(3.1415926, X, Path).
Path = [james, tyrone, ben, frank, carl, james, evan, mike|...].
?- path([a,b,c,d,e], X, Path).
Path = [james, tyrone, ben, frank, carl, james, evan, mike|...].
これは、ルールが最初の位置にあるものすべてに当てはまるためです。より多くの節がある場合、他の節の 1 つがこの引数について何かを言うことができるため、これは意味のあるものになる可能性がありますが、それが実際に何かを意味することを欠いています。したがって、述語は次のように書くこともできます。
path(X, P) :- findall(X, interact(_, X), P).
Every_
は完全に一意のバインドです。それらは互いにまったく影響を与えないため、そこで効果を期待している場合は、次のようなものが必要になります。
path(F, X, P) :- findall(X, interact(F, X), P).
そして、これがあまり役に立たないことがすぐにわかります。
?- path(ella, X, P).
P = [james, tyrone].
それでは、すでに問題を解決しましょう。
person(X) :- interact(X, _) ; interact(_, X).
これは、相互作用の左側または右側にいるかどうかに関係なく、全員を返す単なるヘルパーです。
path(Originator, Path) :-
setof(X, person(X), People),
path(Originator, Path, People).
このヘルパーは、特定の人からパスを取得します。ヘルパー関数に依存しています。この後すぐに説明します。すべての人のリストから始めることで、可能性を合理的なものに絞り込みます。そうすれば、まだ調査していない人のリストから次の人を選ぶことができ、サイクルや再帰が多すぎることを心配する必要がありません。
path(Originator, [], _).
path(Originator, [NextPerson|Rest], Considering) :-
select(NextPerson, Considering, RemainingToConsider),
interact(Originator, NextPerson),
path(NextPerson, Rest, RemainingToConsider).
最初の節は、いつでもできることだと言っています。発信者から nobody へのパスは空のパスです。これは、私たちの誘導の基本ケースです。
2 番目の節は、検討するために残っている人のリストから誰かを選択すると述べています。誰かが発信者とやり取りしたこと。次に、その人から考慮すべき残りの人々への道を見つけます。(select/3
最初の引数なしで、3 番目の引数を 2 番目の引数と統合します)。
実行を見てみましょう:
?- path(ella, X).
X = [] ;
X = [james] ;
X = [james, ben] ;
X = [james, carl] ;
X = [james, carl, evan] ;
X = [james, carl, evan, kelly] ;
X = [james, carl, evan, kelly, ben] ;
X = [james, carl, evan, kelly, frank] ;
X = [james, carl, evan, mike] ;
X = [james, carl, evan, mike, frank] ;
X = [james, frank] ;
X = [tyrone] ;
false.
さて、最初の質問で、ベンとフランクについて何か言いましたが、他の道には興味がありませんでした。これらのケースを区別する論理的な読み方はまだわかりませんが、少なくとも次のように、最長パスをすべて見つけることができます。
longest_paths(Originator, Path) :-
path(Originator, Path),
\+ (path(Originator, Path2),
length(Path, MaxLen),
length(Path2, NextLen),
NextLen > MaxLen).
これは非常に効率的ではありませんが、これより長いパスが他にないように、Originator からのパス Path を見つけてください。これにより、次の 3 つの解決策が見つかります。
?- longest_paths(ella, X).
X = [james, carl, evan, kelly, ben] ;
X = [james, carl, evan, kelly, frank] ;
X = [james, carl, evan, mike, frank] ;
false.
そして、これはあなたが望む解決策にあなたを導くことができると私が思う限りです. それが役立つことを願っています!