プロローグを学ぶコースを受講しました。カットをいつどのように使用するかを理解できませんでした。カットの大まかな考え方はわかっても、ちゃんと使えないようです。誰かがそれを簡単に説明したり、推奨できる「カット」についての良いチュートリアル(learnprolognow.orgではありません)を提供したりできますか?
TL; DR:しないでください。
カットにより、Prologの検索ツリーが削除されます。つまり、カットのない純粋なPrologプログラムとカットのある同じプログラムを考えると、唯一の違いは、カットのあるプログラムは無益なブランチで費やす時間が短くなるため、より効率的です。答えが少ないかもしれません。また、元のプログラムが終了しないのに終了する可能性もあります。
かなり無害に聞こえます...または便利ですよね?まあ、ほとんどの場合物事はより複雑です。
カットは、カットのないプログラムがまったく意味がないような方法で使用されることがよくあります。このようなカットは赤いカットと呼ばれます。より良いケースでは、それは非単調否定の大まかな形を実装するために使用されます。そして、他のいくつかのケースでは、それは半分否定であり、理解することが非常に難しいいくつかの手続き的意味の半分です。プログラムの読者のためだけでなく、その作家のためにも。実際、多くの場合、このような使用法では、意図しない安定性が不足しています。いずれの場合でも、これらのカットは既存のプログラムに配置されません。彼らは最初からそのプログラムにいることを意図しています。
このようなレッドカットをより構造化して使用するには、once/1
、(\+)/1
、または(;)/2
を使用することをお勧めします。代わりに( If -> Then ; Else )
のようなif-then-elseを使用します。さらに、instantiation_error
sを発行して、このような構造を意図しない使用から保護するようにしてください。または、インスタンス化エラーを生成する iwhen/2
または目標を遅延させるwhen/2
(SWI、YAP、SICStusで提供)を使用します。
無駄な選択ポイント(および冗長な回答)を削除するカットは、グリーンカットと呼ばれます。ただし、注意してください。単に押すだけでプログラムに入れることはできません。 ! いくつかの#00ff00
。ほとんどの場合、このカットによって#ff0000
が変更されないようにするために、クリーンな読み取り専用ガードが必要です。残りの選択ポイントを安全に削除する簡単な方法もあります: call_semidet/1
。関連するケースをいくつか示します。
最後に、カットはコミットオペレータではないことを指摘しておきます。それは時々それのように少し動作しますが、1であるためには多くの制限が必要になります。コミットオペレーターは、(\+)/1
の実装に(ab)使用することはできません。コミットでは、各句が互いに独立して試行される必要があります。したがって、各句には完全な保護が必要です。他のいくつかの条項が最初に試された後にのみ、試されることに依存することはできません。また、述語の各句でコミットが発生する必要があります。カットはどこでも発生する可能性があります。
カットコミットプロローグの目標は、実行された選択に対して証明されます。
プログラマーが知っている場合代替が利用可能であることが必要な場合(== --- ==)に使用する必要があります試されません。
最も顕著な使用は、失敗によるnegationの実装です。
_fact(a).
fact(b).
/* 1 */ neg(X) :- call(X), !, fail.
/* 2 */ neg(_).
_
ここでは、標準の否定演算子を(再)定義しました。通常は( \ + )/ 1です。
_?- neg(fact(c)).
true.
_
ルール1によるcall(fact(c))
は証明できず、代替ルール2が成功します。
_?- neg(fact(a)).
false.
_
fact(a)
canが証明されるため、カットは失敗する前に代替を破棄します。
_?- neg(fact(X)).
false.
_
fact(X)が成功するような少なくとも未知のXが存在します。
_?- neg(neg(fact(X))).
true.
_
二重否定には、変数が束縛されないという効果があります。これは、メタプログラミングを行うときに、「構造」を変更せずに句をフェッチするのに役立ちます。操作上の観点からは、何が起こっているのかは明らかですが(?)、プログラムはdeclarativeプロパティを失います。
初歩的なインタープリターでのみ有用な別の使用法は、システムに最後の呼び出しの最適化を実行するように指示し、再帰呼び出しの前にカットを付けることでした。その後、システムは、代替ポイントを追跡するために通常必要なスタックスペースの割り当てを回避できます。ダミーの例:
_print_list([E|Es]) :- print_element(E), !, print_list(Es).
print_list([]).
_
editチュートリアルについて:William Clocksinの「Clause and Effect」にカットに関する詳細な調査が含まれていることがわかりました:第4章「Choice and Commitment」長所と短所をカットすることに専念。一番下の行で、主に短所...
カットを使用する前に、述語が次の2つの基準を満たしている必要があります。
私の述語がそのように振舞うと、不要な非決定性を取り除くためにカットを追加することがあります。
たとえば、数値が正か負かゼロかをテストするための述語。
sign(N, positive) :-
N > 0.
sign(N, negative) :-
N < 0.
sign(N, zero) :-
N =:= 0.
各条項は、他の条項から完全に独立しています。これらの条項を並べ替えたり、条項を削除したりしても、残りの条項で期待どおりの回答が得られます。この場合、positive
句とnegative
句の最後に切り取りを入れて、他の句を調べてそれ以上の解決策が見つからないことをPrologシステムに伝えることができます。
-> ;
を使用して、同様の述語をカットせずに書くこともできますが、見た目が気に入らない人もいます。
sign(N, Sign) :-
( N > 0 -> Sign=positive
; N < 0 -> Sign=negative
; Sign=zero
).
カット述語はバックトラックを防ぎます。カット述語は感嘆符(!)として指定されます。 Cutは、検索ツリーを枝刈りし、プロローグインタープリターによってパストラックを短くします。意味的には、常に成功します。
read(a).
read(b).
color(p, red) :- red(p).
color(p,black) :- black(p),!.
color(p,unknown).
?-color(X,Y).
X = a,
Y = red;
X = b,
Y = black;
カットなしでは、ゴールはY =黒の後にY =不明を示します。
カット述語には2つのタイプがあります:
グリーンカット:グリーンカットは、宣言の意味に影響を与えなかったカットの一種です。これは、効率を向上させ、不要な計算を回避するためにのみ使用されます。プログラムからグリーンカットを削除しても、プログラムの意味は変わりません。
レッドカット:レッドカットは、宣言的な意味に影響を与えたものです。プログラムから赤いカットを削除すると、プログラムの意味が変わります。
once
述語が見つかると、コードからすべてを削除します。内部的には
once(X) :- X, !.
何かをする前に、それをどうやってやるかをしっかりと決めるのにとても便利だと思いました。
たとえば、ここに私の標準的なメタインタープリターがあります。 maybe1/1
句の引数には一意のファンクタがあるため、それらがわかると、適切なmaybe1/1
を完全に選択できます。
その固有の関数を見つける仕事は、Y
をX
に関する「何をすべきか」のステートメントに設定するmaybe0/2
プリプロセッサーに与えられます。
once
がなければ、これはカットでなぞられる必要があるかもしれません。例えば。 maybe1
には、X/Y
とor
の3つまたは2つの異なる解釈があり、トップダウン方式で確認する必要があります。しかし、それをチェックしてください。
maybe(X) :-
once(maybe0(X,Y)), maybe1(Y).
maybe0(true, true).
maybe0((X,Y), (X,Y)).
maybe0((X;Y), or(L)) :- o2l((X;Y),L).
maybe0(X, calls(X)) :- calls(X).
maybe0(X/Y, fact(X/Y)) :- clause(X/_, true).
maybe0(X/Y, rule(X/Y)) :- clause(X/_,_).
maybe0(X/Y, abducible(X/Y)).
maybe0(or([H|T]), or([H|T])).
maybe0(or([]), true).
maybe1(true).
maybe1((X,Y)) :- maybe(X),maybe(Y).
maybe1((X;Y)) :- maybe(X);maybe(Y).
maybe1(abducible(X)) :- assume(X,0).
maybe1(fact(X)) :- assume(X,1), one(X).
maybe1(rule(X)) :- assume(X,2), one(clause(X,Y)), maybe(Y).
maybe1(calls(X)) :- one(clause(X,Y)), maybe(Y).
maybe1(or([H|T])) :- any(One,Rest,[H|T]), ignore(maybe(One)), maybe(or(Rest)).