6

Prologでは、最初に述語に格納された値を取得し、次に値を再計算し、最後にretractallandを使用して値を格納することによって、いくつかの算術計算 (またはプログラム全体で重要な状態情報) を含むコードを記述することがよくあります。assertを使用して変数に値を2回割り当てることはできませんis(したがって、変更が必要なほとんどすべての変数がグローバルになります)。これは Prolog では良い習慣ではないことがわかりました。この点について、私は次のように尋ねたいと思います。

  1. Prologでそれが悪い習慣なのはなぜですか(私自身は、一種の柔軟な(変更可能な)変数を持つためだけに上記の手順を実行するのは好きではありませんが)?

  2. この慣行を回避する一般的な方法にはどのようなものがありますか? 小さな例は大歓迎です。

PS Prologの学習を始めたばかりです。C言語などのプログラミング経験があります。

さらに明確にするために編集

私が言いたいことの悪い例 (win-prolog) を以下に示します。

:- dynamic(value/1).
:- assert(value(0)).

adds :- 
   value(X),
   NewX is X + 4,
   retractall(value(_)),
   assert(value(NewX)).

mults :-
   value(Y),
   NewY is Y * 2,
   retractall(value(_)),
   assert(value(NewY)).

start :-
   retractall(value(_)),
   assert(value(3)),
   adds,
   mults,
   value(Q),
   write(Q).

次に、次のようにクエリできます。

?- start.

ここでは、非常に些細なことですが、実際のプログラムやアプリケーションでは、上記のグローバル変数の方法は避けられません。...のような上記のリストassert(value(0))が非常に長くなり、より多くの変数を定義するためのアサート述語がさらに多くなることがあります。これは、異なる関数間の値の通信を可能にし、プログラムの実行中に変数の状態を保存するために行われます。

最後に、もう 1 つ知りたいことがあります。回避するためにさまざまな解決策が提案されているにもかかわらず、上記の慣行が避けられなくなるのはいつですか。

4

2 に答える 2

4

1 - (純粋な) Prolog プログラムが示す宣言型モデルを破壊するため、これは悪い習慣です。

次に、プログラマーは手続き的な観点から考える必要があり、Prolog の手続き型モデルはかなり複雑で従うのが困難です。

具体的には、プログラムがバックトラックしている間に、主張された知識の有効性について決定できなければなりません。つまり、(おそらく) 主張を引き起こした、既に試みられたものとは別の経路をたどります。

2 - 状態を維持するには、追加の変数が必要です。あまり直感的ではない実用的な方法は、単純な述語の代わりに文法規則 (DCG) を使用することです。文法規則は、通常は隠されている 2 つのリスト引数を追加して変換されます。これらの引数を使用して、状態を暗黙的に渡し、必要な場合にのみ参照/変更できます。

とても興味深い紹介があります: DCGs in Prolog by Markus Triska. を探してくださいImplicitly passing states around: この啓発的な小さな例を見つけることができます:

num_leaves(nil), [N1] --> [N0], { N1 is N0 + 1 }.
num_leaves(node(_,Left,Right)) -->
          num_leaves(Left),
          num_leaves(Right).

より一般的で、さらに実用的な例については、同じ著者のThinking in Statesを参照してください。

edit : 通常、データベースを変更する必要がある場合、またはbacktrackingに沿って計算結果を追跡する必要がある場合にのみ、assert/retract が必要です。私の(非常に)古いPrologインタープリターからの簡単な例:

findall_p(X,G,_):-
    asserta(found('$mark')),
    call(G),
    asserta(found(X)),
    fail.
findall_p(_,_,N) :-
    collect_found([],N),
    !.
collect_found(S,L) :-
    getnext(X),
    !,
    collect_found([X|S],L).
collect_found(L,L).
getnext(X) :-
    retract(found(X)),
    !,
    X \= '$mark'.

findall/3 は、基本的なすべてのソリューションの述語と見なすことができます。そのコードは、Clockins-Mellish の教科書 - Programming in Prolog とまったく同じである必要があります。私が実装した「本物の」findall/3をテストする際に使用しました。「$mark」がエイリアス化されているため、「再入可能」ではないことがわかります。

于 2013-09-25T13:35:53.707 に答える