2

私は最近 Prolog を勉強し始め、1 つの奇妙な問題に直面しました。ここでは、関係のツリーと 2 つのタスクのソリューションを提供するコード例 (私はSWI-Prolog 7.2.3を使用しています)を見ることができます。

/*   File:    ancestors.pl
 Author:  Dave Robertson
 Purpose: Relationships in a family tree

Suppose we have a family tree like this :

alan andrea   bruce betty      eddie elsie   fred  freda
 |     |        |     |          |     |       |     |
 |_____|        |_____|          |_____|       |_____|
    |              |                |             |
  clive        clarissa            greg         greta
   |  |__________|___|              |             |
   |__________|__|                  |_____________|
          |   |                            |
        dave doris                        henry

which is defined in Prolog by the following 3 sets of predicates:

*/

%   parent(Parent, Child).
%   Parent is the parent of Child.

parent(alan, clive).
parent(andrea, clive).
parent(bruce, clarissa).
parent(betty, clarissa).
parent(clive, dave).
parent(clarissa, dave).
parent(clive, doris).
parent(clarissa, doris).
parent(eddie, greg).
parent(elsie, greg).
parent(fred, greta).
parent(freda, greta).
parent(greg, henry).
parent(greta, henry).

%%   PROBLEM 1
%%   How do you find out if someone is the ancestor of someone else ?
ancestor(X,Y) :- parent(X,Y).
ancestor(X,Y) :- parent(X,Z), ancestor(Z,Y).

%%   PROBLEM 3
%%   How do you know if someone is related to someone else ?
relative(X,Y) :-  ancestor(X,Y).
relative(X,Y) :-  ancestor(Y,X).
relative(X,Y) :-  ancestor(Z,X), ancestor(Z,Y), X\==Y.

dave の親戚を取得したいときは、次のようにします。

relative(dave,X).
X = clive ;
X = clarissa ;
X = alan ;
X = andrea ;
X = bruce ;
X = betty ;
X = doris ;
X = doris ;
X = clive ;
X = doris ;
X = clive ;
X = doris ;
X = clarissa ;
X = doris ;
X = clarissa ;
X = doris ;
false.

次に、相対次の方法の定義を変更します。

relative(X,Y) :-  ancestor(X,Y).
relative(X,Y) :-  ancestor(Y,X).
relative(X,Y) :-  X\==Y, ancestor(Z,X), ancestor(Z,Y).

最後のステートメントの目標の順序を変更するだけです。そして今、私は次の出力を持っています:

relative(dave,X).

X = clive ;
X = clarissa ;
X = alan ;
X = andrea ;
X = bruce ;
X = betty ;
X = dave ;
X = doris ;
X = dave ;
X = doris ;
X = clive ;
X = dave ;
X = doris ;
X = clive ;
X = dave ;
X = doris ;
X = clarissa ;
X = dave ;
X = doris ;
X = clarissa ;
X = dave ;
X = doris ;
false.

出力に dave が表示されます。どうしてそうなった?私は X \== Y と書きました... 誰かこれについて良い説明をしてくれませんか?

そしてもう1つ質問です。プログラムで同じ答えを書かないようにするにはどうすればよいですか?

ありがとうございました!

4

3 に答える 3

3

(\==)/2は純粋な関係ではなく、操作的にしか理解できません。これを使用すると、ゴールの順序を交換すると、宣言的に間違った結果が生じる可能性があります。

?- X \== Y, X = Y.
 X = Y.

dif/2代わりに、用語の不等式を述べるための純粋で完全に宣言的な方法として使用してください。

?- dif(X, Y), X = Y.
 false.

詳細については、 を参照してください。

于 2015-11-16T10:04:07.287 に答える
2

特に初心者としては、不純な構造の使用を控え、を維持するようにしてください。

どのように? を使用してください。

X \== Y単に書く代わりにdif(X, Y)

于 2015-11-16T10:04:43.380 に答える
1

Prolog は特定の解決方法に基づくプログラミング言語であり、あなたが説明する問題はまさにそれです: あなたのプログラムの問題 (まあ、私はそれをバグと呼びます)。句と目標の順序はアルゴリズムを制御する方法です。つまり、表現に対する定義済みの効果を持つ一連のステップです。その場合、そのような影響についての知識は私見では避けられないものであり、より複雑なものをコーディングしようとするときに、 ( \== )/2 をdif /2 に置き換えても、作業が楽になることはないと思います。少なくとも、私の経験では、コードをモデル化してデバッグしなければならないときに、さらに困難に直面しました。

(\==)/2 は、同一性のために変数を比較する必要がある場合に、メタプログラミングを容易にすることを目的としています。そのため、これは事実上かなり高度な機能であり、プログラムには必要ありません。しかし (おそらく C/C++/Java オペレーターに非常に似ているため)、その目的を過小評価するのは簡単です。

あなたの使用法では、 ( \= )/2 の方が適切に機能しますが、単純な使用のために、両方の引数がインスタンス化されている必要があります。これが真実であることは、目標の実際の呼び出しから生じる「推論グラフ」全体、つまり操作上のセマンティックに依存します。一般に、特定のパターンで述語を安全に呼び出すことができるかどうかを判断するのは簡単ではありません (または実行可能でさえあると思います)。私の単純なアサーションについてのコメントとして得たこの反例を考えてみましょう。

?- append(Xs,[a],Xs).

重複を避け、結果を一覧表示するには、setof /3を使用します

?- setof(R, relative(dave, R), Relatives), maplist(writeln, Relatives).
于 2015-11-16T14:47:43.717 に答える