3

Prolog で述語を作成していますが、述語が終了する前に終了する可能性があります。return;そのため、 (C++)に似たコマンドを探していました。私は a を使用しましたcut !が、それが文字通り何を表しているのか、そしてそれが正確に何をするのかについては疑問ですreturn;. 元:

pred(X) :- 
           X = 1 -> do this, !; else do that,
           write('x =/= 1').

void pred(int X) {
       if (X = 1) {do this; return;} else do that;
       cout << "x =/= 1";
}

上記の機能はまったく同じですか?

4

4 に答える 4

7

Prolog の実行メカニズムと従来の命令型言語の実行メカニズムに直接対応するものはありません。したがって、どの類推も、むしろあなたを死んだ道に導きます。

あなたの例では、カットは何の効果もありません。(->)/2単独ではすでにElseブランチが除外されます。ある意味で、それは「小さな」カットオンIfと代替手段です. に別の節がありますかpred/1、あなたのカットはそのブランチも除外します。

Prolog の実行メカニズムは、はるかに複雑です。命令型言語の類推を主張する場合は、反復子について考えてください。カットにより、カットのスコープ内のすべてのイテレータは、次の で完了を生成しnextます。だから、少し似ていbreakます。幾分。ただし、そもそもイテレータをサポートする言語でのみ。

Prolog を学びたいのであれば、これらの (半分の) 壊れた類推から自分の考えを発展させようとしないでください。

プロローグの述語がどのような関係を記述しているかを想像することから始めて、そこから述語の意味を概算することをお勧めします。手続き上の概念は、1 つずつ適合します。

于 2014-11-07T15:12:06.373 に答える
3

指摘されたように、Prolog は手続き型思考にうまく対応していません。

Prolog プログラムとその「データベース」をツリー (フォレスト?) と考える最良の方法を見つけました。グラフにはサイクル (再帰) が含まれているため、類推は少し大雑把です。

プロローグ エンジンに特定のアサーション (述語) の真偽を判断するように依頼すると、統合 (パターン マッチング) を使用してツリーの深さ優先の左から右への走査を開始し、走査をガイドします。トラバーサルがリーフ ノードに到達すると、述語はtrue. バックトラックでは、バックトラックしてツリー ウォークを続行します。到達可能なリーフ ノードがなくなると、述語は失敗します。

Prolog は記述言語です。述語計算の観点から成功の条件を記述します。次に、Prolog の推論エンジンに適切なソリューションを見つけさせるだけです。私の経験では、手続き的で命令的な思考をモデルに押し込もうとすると、そうでない場合よりも物事が難しくなるだけでなく、パフォーマンスの低下が保証されます。

Leon Sterling と Eliot Shapiro の教科書The Art of Prologは、Clocksin & Mellish のProgramming in Prologよりも非常に価値があり、はるかに有益で啓発的であることがわかりました。

プロローグ表紙のアート

注意して編集:あなたのサンプル述語

pred(X) :- 
   X = 1 -> do this , ! ; else do that ,
   write('x =/= 1')
   .

いくつかの問題があります。

  • まず、C や C#、またはandandor演算子の優先順位が異なる他の手続き型言語と同様に、式 like はif ( a && b || c && d ) ...おそらくあなたが思っているようにはバインドされません。行う:書かれているように、それは次のようにバインドします

    pred(X) :-
      X=1 ->
        ( do_this , ! )
      ;
        ( do_that , write( 'x =/= 1' ) )
      .
    

    あなたがおそらく望んでいたのは

    pred(X) :-
      ( X=1 ->
        ( do_this , ! )
      ;
        do_that ,
      ) ,
      write( 'x =/= 1' )
      .
    

    意図したバインディングを明示的にするには、括弧を使用する必要があります。

    pred(X) :- 
       ( X=1 ->
           ( do_this , ! )
       ;
           do_that
       ),
       write('x =/= 1')
       .
    
  • さらに、含意演算子はカットが含まれているかのように機能するため、例のカット(!)は不要->です。これ:

    foo(X) :-
      truthy(X) ->
        writeln('truthy!')
      ;
        writeln('falsy')
      .
    

    とほとんど同じです

    foo(X) :- truthy(X) , ! ,
              writeln( 'truthy' ) .
    foo(_) :- writeln( 'falsy'  ) .
    
  • 第三に、ヘッド内で統一とパターン マッチングを利用する必要があります。を無視するwrite/1と、あなたの例は次のように理にかなっているかもしれません

    pred(1) :- do_this , ! .
    pred(X) :- do_that . 
    

そして、一般的に、プロローグを学んでいるのであれば、一般的に含意演算子 (および代替 (論理 OR, ';'/2) を避けることをお勧めします。

foo(X) :- ( try_this(X) ; try_that(X) ; finally(X) ) .

好む

foo(X) :- try_this(X) .
foo(X) :- try_that(X) .
foo(X) :- finally(X) .

そして、含意の代わりに:

foo(X) :- X=1 -> try_this(X) ; try_that(X) .

次のようなものを好む:

foo(1) :- ! , try_this(X) .
foo(X) :- try_that(X) .

選択ポイント (およびその排除) が明確になるため、何が起こっているのかを理解しやすくなると思います。

于 2014-11-07T18:17:32.823 に答える
3

したがって、次のような手続き型コードがあります。

def foo():
  if cond1:
    handle_cond1()
    return

  if cond2:
    handle_cond2()
    return

  do_other_stuff()

手続き型ドメインでこれを変換して、明示的な戻りがないようにすることができます。最初にこれを行います。

def foo():
  if cond1:
    handle_cond1()
    return

  if cond2:
    handle_cond2()
  else:
    do_other_stuff()

そして、これを行う:

def foo():
  if cond1:
    handle_cond1()
  else:
    if cond2:
      handle_cond2()
    else:
      do_other_stuff()

ステートメントを削除したら、returnこれを Prolog に変換できます。

foo :-
  cond1 -> 
    handle_cond1
  ; (cond2 ->
       handle_cond2
     ; do_other_stuff 
     ).

Prologですぐに成功する方法はありません。( ですぐに失敗する可能性がありますfail)。同様のフローを実現するには、このような変換を実行する必要があります。何よりも、@false のアドバイスに従い、Prolog を独自の条件で学習することをお勧めします。

于 2014-11-07T16:19:57.973 に答える
2

しっかりしたコーディング ガイドラインがあることは、コードの可読性を高め、コードの脆弱性を回避するのにもかなり役立つことを付け加えたいと思います。

@DanielLyons のコードから始めます。

foo :-
  cond1 -> 
    handle_cond1
  ; (cond2 ->
       handle_cond2
     ; do_other_stuff 
     ).

実際には、ネストされた複数の if-then-else 構造のカスケードが発生します: "if-then-elseif-then-elseif-then-elseif-then-...-else"。

読みやすさを向上させるために、コード レイアウトをブラッシュ アップし、インデントのレベルを調整できます。

foo :-
   (  cond1 -> handle_cond1
   ;  cond2 -> handle_cond2
   ;           do_other_stuff 
   ).

コード行の幅が広すぎる場合は常に、幅を少し狭くして高さを高くするスタイルが望ましい場合があります。

foo :-
   (  cond1
   -> handle_cond1
   ;  cond2 
   -> handle_cond2
   ;  do_other_stuff 
   ).
于 2015-06-04T01:00:46.667 に答える