有一个非常详细的Draft proposal for setup_call_cleanup/3。
让我引用我的问题的相关部分:
c)清理处理程序只调用一次;不迟于G.失败的早期时刻是:
如果G为真或假,则在最后一个解之后以及在G的最后一个可观察效果之后的实现相关时刻调用C.
这个例子:
setup_call_cleanup(S=1,G=2,write(S+G)).
Succeeds, unifying S = 1, G = 2.
Either: outputs '1+2'
Or: outputs on backtracking '1+_' prior to failure.
Or (?): outputs on backtracking '1+2' prior to failure.
在我的理解中,这基本上是因为统一是一个 可追溯的目标;它只是无法重新执行。因此它是 直到执行决定是否只是调用清理 在第一次执行之后(因为没有更多的可观察性 目标的影响),或推迟到第二次执行 现在失败的目标。
所以在我看来,这不能用于检测确定性
可移植。只有少数内置构造,如true
,fail
,!
真的是不可回溯的。
有没有其他方法可以检查确定性而不执行两次目标?
我目前正在使用SWI-prolog的deterministic/1
,但我肯定
欣赏便携性。
答案 0 :(得分:2)
没有。 setup_call_cleanup/3
无法以便携方式检测到确定性。因为这将限制其自由的实施。系统有不同的方式来实现索引。他们有不同的权衡。有些人只有第一个参数索引,其他人有更多。但系统提供更好的"索引通常非常随机。有些系统仅对非变量项进行索引,其他系统也允许只在头部有变量的子句 - 前提是它是最后一个子句。有些人可能会做"手册"选择点避免与safe tests prior to cuts和其他人只是省略这一点。简而言之,这实际上是一个非功能性问题,坚持在这个领域的可移植性等同于减缓系统速度。
然而,仍然如此:如果setup_call_cleanup/3
检测到确定性,那么就没有必要再使用第二个目标来确定决定论了!因此,它可以用于更有效地实现确定性检测。但是,在一般情况下,您必须执行两次目标。
setup_call_cleanup/3
的{{3}}也是精心设计的,例如允许实施也动态删除不必要的选择点。
在Call
成功和选择点的内部存在时,可以想象(不是我已经看到过这样的实现),如果可以检测到确定性,则实现可以检查当前选择点并将其删除。另一种可能性是在两者之间执行一些异步垃圾收集。当前规范所有这些选项都不排除。目前还不清楚它们是否会被实施,但一旦某些应用程序依赖于这样的功能,它可能会发生。这已经在Prolog中发生了几次,所以重复并不是完全幻想。事实上,我正在考虑将current definition变为更具决定性的特定案例。谁知道,也许你会走那条路!
以下是SWI中的索引取决于先前查询的历史记录的示例:
?- [user].
p(_,a). p(_,b). end_of_file.
true.
?- p(1,a).
true ;
false.
?- p(_,a).
true.
?- p(1,a).
true. % now, it's determinate!
这是一个例子,第二个参数的索引如何严格地弱于第一个参数索引:
?- [user].
q(_,_). q(0,0). end_of_file.
true.
?- q(X,1).
true ; % weak
false.
?- q(1,X).
true.
答案 1 :(得分:1)
由于您对可移植性感兴趣,Logtalk的lgtunit
工具为其支持的后端Prolog编译器定义了一个可移植的deterministic/1
谓词:
http://logtalk.org/library/lgtunit_0.html https://github.com/LogtalkDotOrg/logtalk3/blob/master/tools/lgtunit/lgtunit.lgt(从第1051行开始)
请注意,不同的系统使用不同的内置谓词近似预期的功能。