Prolog:关键部分,回溯,错误处理

时间:2017-04-13 11:15:29

标签: prolog swi-prolog

我正在尝试编写一个由SWI-Prolog中的互斥锁保护的关键部分,并且一直在考虑使用setup_call_cleanup/3setup_call_catcher_cleanup/4

我遇到的问题是我的目标是一系列操作,任何操作都可能失败,这意味着系统回溯到setup_call_cleanup的开头并调用清理< / em>的。不幸的是,通过回溯,我无法正确报告错误。为了说明我的问题,让我们考虑一下这个简单的例子:

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. 重新编码步骤,因此它们会抛出带有问题详细信息的异常。
  3. 前者会让代码变得臃肿,而后者似乎对我的需求来说太重了。有什么像setup_nb_call_cleanup

2 个答案:

答案 0 :(得分: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内。细节主要取决于您如何处理非决定论。

答案 1 :(得分: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).