2017-04-13 8 views
3

SWI-Prologのミューテックスで保護されたクリティカルセクションを作成しようとしており、setup_call_cleanup/3setup_call_catcher_cleanup/4を使用しています。Prolog:クリティカルセクション、バックトラッキング、エラー処理

私が持っている問題は、私のゴールは、いずれかが失敗する可能性があるの一連の操作であることであり、それは、システムがsetup_call_cleanupの先頭にバックトラックとクリーンアップ呼び出すことを意味します。残念ながら、バックトラッキングではエラーを適切に報告できません。

setup_call_cleanup(
     mutex_lock(mtx), 
     (Step1 = true, Step2 = true, Step3 = true), 
     (mutex_unlock(mtx), writeln([Step1, Step2, Step3])). 

をし、次のと比較:説明するために、私の問題は、のは、この単純な例を考えてみましょう最初のケースで

setup_call_cleanup(
     mutex_lock(mtx), 
     (Step1 = true, Step2 = true, fail, Step3 = true), 
     (mutex_unlock(mtx), writeln([Step1, Step2, Step3])). 

をすべてはokです - 私は行って、すべての手順を見ることができます。しかし、2番目のケースでは、私はStep1Step2が実行されていることがわかりません。私はバックトラックが取り消すことができない外部の副作用があるかもしれないので、それを見たいと思います。また、私はゴールにエラー処理を含めたくないので、クリティカルセクションを可能な限り速く、速くするようにします。

私は2つのアイデアを持っている:

  1. は、完了したステップを示す値を格納するnb_setvalとの各ステップを飾る
  2. 再コードのステップなので、彼らは、問題の詳細を伝える例外をスローします。

前者はコードがむしろ肥大化しますが、後者は自分の必要性に対して重すぎるようです。 setup_nb_call_cleanupのようなものはありますか?

+1

第2のアプローチは、最初の提案よりもはるかに優れています。例外を扱うときは、常にネストされた*呼び出しで何が起きるか考えてください。グローバルな更新のような不確かな述語は、これを悪夢にしたり、正しくすることも不可能です。 – mat

+0

@mat:うーん、はい、いいえ。いくつかのステップが 'assert(some_fact)'のように単純な場合、それらをth​​row節でラップすると、必要以上に複雑になる可能性があります。また、 'nb_setval'と比較して' throw'と 'catch'のコストについてはわかりません...私は本当にクリティカルセクションを速くしたいと思っていました。 – Jacek

+1

両方のアプローチのパフォーマンスをテストするには、 'time/1'または' statistics/2'を使用してください。これは、あなたが最も速いバージョンを使用することを確認します。非常に頻繁に、不純なソリューションも最も遅いです。 – mat

答えて

0

感謝の気持ちであります。非常に便利。私は(OutStep is InStep + 1 ; true), !と満足していないが、より良い方法を見つけることができませんでした

step_by_step(Goal, Steps, Error) :- 
    step_by_step_(Goal, 0, Steps, Error). 

step_by_step_((A, B), InStep, OutStep, Error) :- 
    !, 
    step_by_step_(A, InStep, OutStep1, Error), 
    (var(Error) -> 
     step_by_step_(B, OutStep1, OutStep, Error) 
    ; 
     OutStep = InStep 
    ). 

step_by_step_(Goal, InStep, OutStep, Error) :- 
    (catch(Goal, Ex, (Error = exception(Ex), OutStep = InStep)) *-> 
     (OutStep is InStep + 1 ; true), ! 
    ; 
     Error = false(Goal), 
     OutStep = InStep 
    ). 

:私は、同様のstep_by_stepルールをコーディングすることになりました。

とにかく、ルールは私が欲しいものを私に与えます:

- すべてがOKになった場合、それだけでシーケンスのすべてのステップを実行します:

?- step_by_step((Step1 = true, Step2 = true, Step3 = true), Steps, Error). 
Step1 = Step2, Step2 = Step3, Step3 = true, 
Steps = 3. 

- ステップの一つに障害が発生したりスローした場合

?- step_by_step((Step1 = true, Step2 = true, fail, Step3 = true), Steps, Error). 
Step1 = Step2, Step2 = true, 
Steps = 2, 
Error = false(fail). 

または例外:

例外は、それが正常に完了したステップの数と失敗した目標を返します。
?- step_by_step((Step1 = true, Step2 = true, throw(bomb), Step3 = true), Steps, Error). 
Step1 = Step2, Step2 = true, 
Steps = 2, 
Error = exception(bomb). 
2

私は、ゴールを1つずつ実行し、エラーと失敗を守り、失敗したステップを返すことをトリックと考えています。良いスタートが

until_failure((A,B), Result) :- 
    !, 
    until_failure(A, Result), 
    ( var(Result) 
    -> until_failure(B, Result) 
    ; true 
    ). 
until_failure(G, Result) :- 
    ( catch(G, Result, true) 
    *-> true 
    ; Result = false(G) 
    ). 

いる今、あなたは、例えば実行することができ、

?- until_failure((Step1 = true, 
       Step2 = true, 
       fail, 
       Step3 = true), Result), 
    writeln([Step1, Step2, Step3]). 

[true, true, _5742] 
Result = false(fail) 

http://swish.swi-prolog.org/p/ReQWsvCg.swinbを参照してください。 SWISHでは、mutexを扱う は許可されていませんが、with_mutex/2の中でこれを簡単に囲むことができます。詳細は、特に非決定論をどのように扱いたいかに依存します。