2017-04-13 44 views
3

我正試圖編寫一個由SWI-Prolog中的互斥量守護的關鍵部分,並一直在尋找使用setup_call_cleanup/3setup_call_catcher_cleanup/4序言:臨界區,回溯,錯誤處理

我的問題是,我的目標操作序列,其中任何可能失敗,這意味着該系統回溯到的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])). 

在第一種情況一切正常 - 我可以看到完成的所有步驟。但在第二種情況下,我無法看到Step1Step2已經執行。我想看看它,因爲它們可能有回溯無法撤消的外部副作用。另外,我不希望在目標中包含錯誤處理,以使關鍵部分儘可能精簡和快速。

我有兩個想法:

  1. 裝飾與nb_setval每一步存儲的值來表示完成的步驟,
  2. 重新對碼步驟,讓他們扔攜帶問題的細節例外。

前者會使代碼相當臃腫,而後者似乎過重了我的需要。有什麼像setup_nb_call_cleanup

+1

第二種方法肯定已經比第一個建議好多了!處理異常時,總是要考慮* nested *調用發生的情況。像全球更新這樣的不純謂詞使得這成爲一場噩夢,甚至不可能變得正確。 – mat

+0

@mat:嗯,是的,沒有。如果某些步驟與「assert(some_fact)」一樣簡單,那麼使用throw子句包裝這些步驟可能會使它比需要的更復雜,我想。另外,與'nb_setval'相比,我不確定'throw'和'catch'的成本......我真的很想讓關鍵部分變快。 – Jacek

+1

請使用'time/1'或'statistics/2'來測試兩種方法的性能。這將確保您使用最快的版本。非常頻繁地,不純的解決方案也是最慢的。 – mat

回答

0

謝謝Jan的靈感;很有用。我結束了編碼類似step_by_step規則:

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 
    ). 

我不是很滿意,但(OutStep is InStep + 1 ; true), !沒能找到一個更好的辦法。

反正規則給了我什麼,我想:

- 如果一切進展正常,它只是運行在序列的所有步驟:

?- 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

我認爲,訣竅是逐個運行目標,防止錯誤和失敗,並返回失敗的步驟。良好的開端是

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不允許 處理互斥體,但是您可以輕鬆地將其包含在with_mutex/2之內。細節取決於你想如何處理非確定性。