一些Prolog目标确定性成功的问题一再出现 - 至少 - 以下问题:
使用了不同的方法(例如,引发某些资源错误,或仔细查看Prolog toplevel给出的确切答案),但它们对我来说都有点不合适。
我正在寻找一种通用的,可移植的,符合ISO标准的方法,以确定某些Prolog目标(成功)的执行是否会留下一些选择点。一些元谓词,也许?
你可以向我暗示正确的方向吗?提前谢谢!答案 0 :(得分:13)
每个人都好消息:setup_call_cleanup/3
(目前是ISO的draft proposal)让你以一种非常便携和漂亮的方式做到这一点。
请参阅example:
setup_call_cleanup(true, (X=1;X=2), Det=yes)
以Det == yes
成功。
编辑:让我用一个简单的例子来说明这个构造的精妙之处,或者说是非常密切相关的谓词call_cleanup/2
:
在出色的CLP(B) documentation of SICStus Prolog中,我们在labeling/1
的说明中找到了一个非常有力的保证:
通过回溯计算所有解决方案,但仅在必要时创建选择点。
这确实是一个强有力的保证,起初可能很难相信它总是成立。对我们来说幸运的是,在Prolog中制定和生成系统测试用例非常容易,以验证这些属性,实质上是使用Prolog系统进行自我测试。
我们首先系统地描述了一个布尔表达式在CLP(B)中的样子:
:- use_module(library(clpb)).
:- use_module(library(lists)).
sat(_) --> [].
sat(a) --> [].
sat(~_) --> [].
sat(X+Y) --> [_], sat(X), sat(Y).
sat(X#Y) --> [_], sat(X), sat(Y).
实际上还有更多的案例,但现在让我们将自己局限于CLP(B)表达式的上述子集。
为什么我要使用DCG呢?因为它可以让我方便地描述(特定深度的所有布尔表达式的(子集),因此可以相当地枚举它们。例如:
?- length(Ls, _), phrase(sat(Sat), Ls). Ls = [] ; Ls = [], Sat = a ; Ls = [], Sat = ~_G475 ; Ls = [_G475], Sat = _G478+_G479 .
因此,我仅使用DCG来表示有多少可用的"令牌"生成表达式时已被消耗,限制了结果表达式的总深度。
接下来,我们需要一个小的辅助谓词labeling_nondet/1
,其作用与labeling/1
完全相同,但仅当选择点仍然时才会生效。这是call_cleanup/2
的来源:
labeling_nondet(Vs) :-
dif(Det, true),
call_cleanup(labeling(Vs), Det=true).
我们的测试用例(通过这个,我们实际上意味着一个无限的小测试用例序列,我们可以非常方便地用Prolog描述)现在旨在验证上述属性,即:
如果有选择点,那么还有一个解决方案。
换句话说:
labeling_nondet/1
的解决方案集是labeling/1
的正确子集。
让我们来描述上述属性的反例:
counterexample(Sat) :- length(Ls, _), phrase(sat(Sat), Ls), term_variables(Sat, Vs), sat(Sat), setof(Vs, labeling_nondet(Vs), Sols), setof(Vs, labeling(Vs), Sols).
现在我们使用这个可执行规范来找到这样一个反例。如果解算器按照记录的方式工作,那么我们永远不会找到反例。但在这种情况下,我们立即得到:
| ?- counterexample(Sat). Sat = a+ ~_A, sat(_A=:=_B*a) ? ;
所以实际上该属性不持有。细分为本质,虽然在以下查询中不再提供解决方案,Det
与true
不统一:
| ?- sat(a + ~X), call_cleanup(labeling([X]), Det=true).
X = 0 ? ;
no
在SWI-Prolog中,多余的选择点显而易见:
?- sat(a + ~X), labeling([X]). X = 0 ; false.
我不给出这个例子来批评SICStus Prolog或SWI的行为:没有人真正关心labeling/1
中是否留下了多余的选择点,最不重要的是在一个涉及普遍量化变量的人为例子中(对于使用labeling/1
的任务而言,这是非典型的。)
我 am 给出了这个例子来展示如何通过如此强大的检查谓词来测试文档和预期的方便性和保证...
...假设实现者有兴趣将他们的工作标准化,以便这些谓词实际上在不同的实现中以相同的方式工作!细心的读者会注意到,当在SWI-Prolog中使用时,搜索反例会产生完全不同的结果。
在意外的事件发生时,上述测试用例发现SWI-Prolog和SICStus的call_cleanup/2
实现存在差异。在SWI-Prolog(7.3.11)中:
?- dif(Det, true), call_cleanup(true, Det=true). dif(Det, true). ?- call_cleanup(true, Det=true), dif(Det, true). false.
而SICStus Prolog(4.3.2)中的两个查询都失败。
这是一个非常典型的案例:一旦您对测试特定属性感兴趣,就会发现许多阻碍测试实际属性的障碍。
在ISO draft proposal中,我们看到:
[清理目标]失败忽略。
在call_cleanup/2的SICStus文档中,我们看到:
在执行一些副作用后确定清除成功;否则,可能会导致意外行为。
在SWI variant中,我们看到:
清除成功或失败忽略
因此,为了便于携带,我们实际上应该将labeling_nondet/1
写为:
labeling_nondet(Vs) :-
call_cleanup(labeling(Vs), Det=true),
dif(Det, true).
答案 1 :(得分:1)
在setup_call_cleanup / 3中无法保证它会检测到确定性,即在目标成功时缺少选择点。 7.8.11.1描述draft proposal仅说:
c)清理处理程序只调用一次;不迟于
G失败后,早期的时刻是:
如果G为真或假,则在实施中调用C. 最后一个解决方案之后的依赖时刻和最后一个解决之后 G的可观察效果。
所以目前没有要求:
setup_call_cleanup(true, true, Det=true)
首先返回Det = true。这也反映在测试用例7.8.11.4 draf proposal给出的示例中,我们找到一个测试用例说:
setup_call_cleanup(true, true, X = 2).
Either: Succeeds, unifying X = 2.
Or: Succeeds.
因此,它既是一种有效的实现,也可以检测确定性而不是检测确定性。