3

この本では、次のレイアウトを使用して述語 left_of、right_of、above、below を定義するよう求められています。

% bike               camera
% pencil  hourglass  butterfly  fish

left_of(pencil, hourglass).
left_of(hourglass, butterfly).
left_of(butterfly, fish).

above(bike, pencil).
above(camera, butterfly).

right_of(Obj1, Obj2) :-
    left_of(Obj2, Obj1).

below(Obj1, Obj2) :-
    above(Obj2, Obj1).

これは正しい解決策を見つけるようです。

この本の後半で、left_of の再帰ルールを追加するよう求められます。私が見つけた唯一の解決策は、別のファンクター名 left_of2 を使用することです。だから私は基本的に先祖関係を再実装しました。

left_of2(Obj1, Obj2) :-
    left_of(Obj1, Obj2).
left_of2(Obj1, Obj2) :-
    left_of(Obj1, X),
    left_of2(X, Obj2).

left_of を再利用しようとすると、すべて正しい解決策を得ることができますが、最後のやり直しでスタック オーバーフローが発生します。正しい基本ケースが定義されていないためだと思います。これは、事実と再帰的な手順に対して left_of を使用してコーディングできますか?

4

1 に答える 1

5

コメントで述べたように、残念なことに、Prolog ではこれを行うには個別に名前を付けた述語が必要です。そうしないと、次のような結果になります。

left_of(X,Z) :- left_of(X,Y), left_of(Y,Z).

これにより、無制限の再帰が2回行われます。ファクトと述語が同じ名前を共有していても、原則として何も問題はありません。実際、基本ケース ルールがファクトのように見えることはよくあることです。このような推移的なクロージャの状況を処理すると、2 つのステップのいずれかが有限でない限り、スタック オーバーフローが発生するだけであり、Prolog でそれらを別々に命名する以外にそれを保証する方法はありません。

これは、作業を個別の述語に分割せざるを得ない Prolog の唯一のケースではありません。その他の一般的なケースには、イニシャライザまたはファイナライザを使用した計算ループが含まれます。

従来、事実とは異なる述語を命名することになってしまいます。たとえばdirectly_left_of、事実とleft_of述語です。モジュール システムまたは Logtalk を使用すると、「直接的な」バージョンを簡単に非表示にして、ユーザーに推移的なバージョンを使用するように促すことができます。left_of_. _

他の言語では、関数はより不透明でより大きな抽象化であり、かなりの作業を関数の背後に隠す機能があります。比較すると、Prolog の述語は「より単純」であり、以前は私を悩ませていました。最近では、他のことが十分に行われているので、それらがより単純になったことを嬉しく思います。可変アリティの述語やキーワード引数を理解する必要がないことを嬉しく思います (ただし、リストを使用して両方を簡単にシミュレートできます。する必要があります)。

于 2013-05-01T20:28:52.100 に答える