【发布时间】:2017-04-13 11:15:28
【问题描述】:
我正在尝试在 SWI-Prolog 中编写一个由互斥锁保护的关键部分,并且一直在研究使用 setup_call_cleanup/3 和 setup_call_catcher_cleanup/4。
我遇到的问题是我的 Goal 是一系列操作,其中任何一个都可能失败,这意味着系统会回溯到 setup_call_cleanup 的开头并调用 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]) ).
在第一种情况下,一切正常——我可以看到所有步骤都已完成。但在第二种情况下,我看不到 Step1 和 Step2 已经执行。我想看看它,因为它们可能具有回溯无法撤消的外部副作用。另外,我不想在 Goal 中包含错误处理,以使关键部分尽可能精简和快速。
我有两个想法:
- 用
nb_setval装饰每个步骤以存储一个值来指示已完成的步骤, - 重新编码步骤,以便它们抛出包含问题详细信息的异常。
前者会使代码相当臃肿,而后者对于我的需求来说似乎太重了。有setup_nb_call_cleanup之类的吗?
【问题讨论】:
-
第二种方法肯定已经比第一种方法好很多了!在处理异常时,还要考虑嵌套调用会发生什么。诸如全局更新之类的不纯谓词使这成为一场噩梦,甚至不可能做到正确。
-
@mat:嗯,是的,不是的。我认为,如果某些步骤像
assert(some_fact)一样简单,那么用 throw 子句包装这些步骤可能会使它变得比需要的更复杂。此外,与nb_setval相比,我不确定throw和catch的成本...我真的希望关键部分快。 -
请使用
time/1或statistics/2来测试这两种方法的性能。这将确保您使用最快的版本。很多时候,不纯的解决方案也是最慢的。 -
方法 (2) 的另一个问题是,有时当它只是失败时,很难将其视为异常。例如,
some_fact不成立,在这种情况下也不例外。我真的很想看到类似nb_catch或nb_setup_call_cleanup...
标签: prolog swi-prolog