10

次の話は、N. Wirth の (1976) Algorithms + Datastructures = Programs からのものです。

私は成人した娘(彼女を D と呼ぶ)を持つ未亡人(彼女を W としましょう)と結婚しました。よく訪ねてくれた父(F)は、継娘に恋をして結婚しました。したがって、父が義理の息子になり、義理の娘が母になりました。数か月後、妻は息子(S1)を出産し、父の義理の兄弟であり、叔父でもありました。この父の妻、つまり義理の娘にも息子がいました(S2)。

プロローグでこれらの関係をモデル化しようとしているので、最終的には次のように入力できるようになります。

| ?- grandfather(i,i).

そして、私が自分のおじいちゃんかどうかについて「はい」または「いいえ」が与えられます。

これまでに書いたコード (grandpa.pl) は次のとおりです。

aunt(X,Y):-
    sibling(X,Z),
    parent(Z,Y),
    female(X).

brother(X,Y):-
    sibling(X,Y),
    male(X).

brother_in_law(X,Y):-
    child(X,Z),
    married(Z,W),
    parent(W,Y),
    not(sibling(X,Y)),
    male(X).

brother_in_law(s1,f).

child(X,Y):-
    parent(Y,X).

daughter(X,Y):-
    parent(Y,X),
    child(X,Y),
    female(X).

daughter(d,w).

father(X,Y):-
    parent(X,Y),
    male(X).

father(f,i).

father_in_law(X,Y):-
    child(X,Z),
    married(Y,Z),
    not(child(X,Y)),
    male(X).

grandparent(X,Y):-
    parent(X,Z),
    parent(Z,Y).

grandmother(X,Y):-
    grandparent(X,Y),
    female(X).

grandfather(X,Y):-
    grandparent(X,Y),
    male(X).

grandchild(X,Y):-
    child(X,Z),
    child(Z,Y).

married(X,Y):-
    wife(X,Y),
    female(X).

married(X,Y):-
    husband(X,Y),
    male(X).

married(i,w).
married(f,d).

mother(X,Y):-
    parent(X,Y),
    female(X).

parent(X,Y):-
    child(Y,X).

sibling(X,Y):-
    parent(Z,X),
    parent(Z,Y).

sister(X,Y):-
    sibling(X,Y),
    female(X).

son(X,Y):-
    parent(Y,X),
    male(X).

son(s1,w).
son(s2,d).

son_in_law(X,Y):-
    child(X,Z),
    not(child(X,Y)),
    married(Z,Y),
    male(X).

son_in_law(f,i).

step_daughter(X,Y):-
    child(X,Z),
    married(Z,Y),
    not(child(X,Y)),
    female(X).

step_daughter(d,i).

step_parent(X,Y):-
    married(X,Z),
    parent(Z,Y),
    not(parent(X,Y)).

step_father(X,Y):-
    step_parent(X,Y),
    male(X).

step_mother(X,Y):-
    step_parent(X,Y),
    female(X).

step_mother(d,i).

uncle(X,Y):-
    sibling(X,Z),
    parent(Z,Y),
    male(X).

uncle(s1,i).

現在、循環定義で多くの問題を抱えているため、クエリを実行すると無限ループに陥ります:祖父(i、i)。

たとえば、私は持っています:

(1 ミリ秒) はい {トレース} | ?- 祖父(i,i)。1 1 コール: おじいさん(i,i) ?
2 2 呼び出し: 祖父母(i,i) ?
3 3 呼び出し: 親(i,_103) ?
4 4 コール: child(_127,i) ?
5 5 呼び出し: 親(i,_151) ?
6 6 コール: child(_175,i) ?
7 7 呼び出し: 親(i,_199) ?
8 8 コール: child(_223,i) ?
9 9 呼び出し: 親(i,_247) ?
10 10 コール: child(_271,i) ?
11 11 呼び出し: 親(i,_295) ?
12 12 コール: child(_319,i) ?
13 13 呼び出し: 親(i,_343) ?
14 14 コール: child(_367,i) ?
15 15 呼び出し: 親(i,_391) ?
...

これは、子が親を持っていると定義し、親が子を持っていると定義しているためです (私が投稿した上記の述語でわかるように)。

私が自分のおじいちゃんかどうかを判断できるように、これらの関係の述語を再定義するのを手伝ってくれる人はいますか?

4

4 に答える 4

6

あなたのコードで不要なものをすべて削除し、いくつかの変更を加えた結果、次のようになりました。

% married(Husband, Wife)
married(i,w).
married(f,d).

と思われるかもしれませmarried(X,Y) :- married(Y,X)んが、それは厄介な循環証明につながるため、慣例により夫を最初に置きます。

親であることについても、同様の問題が生じます。なぞなぞはそれに依存しているため、義理の両親を本当の両親と見なす必要があります。私たちは、あなたが自分自身の生物学的祖先になることは決してできないことを知っています!

ただし、parent(X,Y) :- parent(Z,Y), married(X,Z)同じ問題が発生するため、bio_parent生物学的な親子関係を示すようにしました。

bio_parent(f,i).
bio_parent(w,d).
bio_parent(w,s1).
bio_parent(i,s1).
bio_parent(d,s2).
bio_parent(f,s2).

結婚によって生物学的な親子関係を結論付ける方法はないため、両親について明確にする必要があることに注意してください。また、指定の仕方にも問題がありました。あなたは次のようなものを持っていました:

son(X,Y) :- child(X,Y), male(X).
son(a,b).

しかし、これらの規則から Prolog は を推測できなかっchild(a,b)たので、あなたは子供ではない息子になってしまいました! これは、コードで数回発生しました。bから派生する場合はa、常にa事実として述べてください。一見、これは Prolog の欠点のように思えるかもしれませんが、そうではありません。すべての句は、特定の述語を証明する 1 つの方法にすぎないことを忘れないでください。上記の例では、すべての男の子が息子であり、aたまたま の息子であると述べましたb。男の子であることが唯一の方法であるとはどこにも言われていaませんが、例外かもしれません.

次のものは、私たちの定義がmarried継父を継母とは別に扱うことを強制するので、少し冗長です. しかし、すぐに継父母に統一します。

step_father(X,Y) :- married(X,Z),bio_parent(Z,Y),\+bio_parent(X,Y).
step_mother(X,Y) :- married(Z,X),bio_parent(Z,Y),\+bio_parent(X,Y).
step_parent(X,Y) :- step_father(X,Y).
step_parent(X,Y) :- step_mother(X,Y).

上で言ったように、私たちは義理の両親を両親と見なす必要があります!

parent(X,Y) :- step_parent(X,Y).
parent(X,Y) :- bio_parent(X,Y).

grandparent(X,Y):-
    parent(X,Z),
    parent(Z,Y).

私が取り出したコードには他にもいくつかの間違いがありました。いくつかの例を示して、そこから学ぶことができます.

まず、女性の妻は既婚、男性の夫は既婚という。したがって、男性の妻は未婚になります。その逆のはずなのに、既婚女性は妻と呼ばれる!

% wrong:
%
% married(X,Y):-
%    wife(X,Y),
%    female(X).
%
% married(X,Y):-
%    husband(X,Y),
%    male(X).
% 
% right:
% wife(X,Y) :- married(Y,X). % according to our new definition of 'married'
% husband(X,Y) :- married(X,Y).

ここで最後の行を追加しました。これは、通常、自分自身を自分の兄弟とは考えていないためです。

% sibling(X,Y):-
%    parent(Z,X),
%    parent(Z,Y),
%    X \= Y. % added this

最後の 2 つは、間違った述語に関する事実です。基本的に、Prolog の推論をそれらでオーバーライドします。それらは事実として述べられるのではなく、差し引かれるべきです!

% son_in_law(f,i).
% step_mother(d,i).

では、このようなプログラムを試してみてください。驚かないでください: 祖父母になるのはあなただけではありません! ;-)

于 2013-02-21T15:01:11.873 に答える
4

私のプロローグコースはずっと前のことですが、削除するのはどうですか

parent(X,Y):-
   child(Y,X).

の使用法parent(A,B)child(B,A)?逆のルールが引き続き使用できるため、親に関するファクトを追加することもできます。そのルールを削除することもできますが、その場合、親に関するファクトを使用できなくなり、すべてのファクトを子として書き込む必要があります(a、b)同じように。

同じですね。

于 2009-09-15T22:32:53.887 に答える
2

私のPrologの知識は古い(そして決してそれほど深くはない)ことに注意してください...

親(または子)をプライマリにする必要があると思います(他の関係に依存しない)。

child(X,Y):-
    parent(Y,X).

parent(X,Y):-
    child(Y,X).

おそらくループを引き起こしているものです。

于 2009-09-15T22:37:24.850 に答える