【问题标题】:Making "deterministic success" of Prolog goals explicit明确 Prolog 目标的“确定性成功”
【发布时间】:2015-04-23 12:12:59
【问题描述】:

某些 Prolog 目标的确定性成功问题已经一次又一次地出现在——至少——以下问题中:

使用了不同的方法(例如,引发某些资源错误,或仔细查看 Prolog 顶层给出的确切答案),但在我看来,它们都有些不合时宜。

我正在寻找一种通用、可移植且符合 ISO 标准的方法来确定某些 Prolog 目标的执行(成功)是否留下了一些选择点。也许是一些元谓词?

你能提示我正确的方向吗?提前谢谢!

【问题讨论】:

  • 这是广告黑客em

标签: prolog


【解决方案1】:

大家好消息: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?因为它让我可以方便地描述(一个子集)所有布尔表达式特定深度,从而公平地枚举它们。例如:

?- 长度(Ls,_),短语(sat(Sat),Ls)。 LS = [] ; LS = [], 星期六 = a ; LS = [], 星期六 = ~_G475 ; Ls = [_G475], 周六 = _G478+_G479

因此,我使用 DCG 仅表示在生成表达式时已经消耗了多少可用“令牌”,从而限制了生成表达式的总深度。

接下来,我们需要一个小辅助谓词labeling_nondet/1,它完全作为labeling/1,但如果选择点仍然保持 em>,则只是真实。这就是call_cleanup/2 的用武之地:

labeling_nondet(Vs) :-
        dif(Det, true),
        call_cleanup(labeling(Vs), Det=true).

我们的测试用例(实际上,我们的意思是无限序列的小测试用例,我们可以用 Prolog 非常方便地描述)现在旨在验证上述属性,即:

如果有选择点,那么就有进一步的解决方案。

换句话说:

labeling_nondet/1 的解集是labeling/1 的解集。

让我们描述一下上述属性的反例是什么样子的:

反例(周六):- 长度(Ls,_), 短语(sat(Sat), Ls), term_variables(周六,Vs), 星期六(星期六), setof(Vs, labeling_nondet(Vs), Sols), 集合(Vs,标签(Vs),Sols)。

现在我们使用这个可执行规范来找到这样一个反例。如果求解器按记录工作,那么我们将永远找不到反例。但在这种情况下,我们立即得到:

| ?- 反例(周六)。 星期六 = a+ ~_A, 坐(_A=:=_B*a)? ;

所以事实上该属性确实成立。分解到本质,虽然下面的查询中没有更多的解决方案,但Dettrue并不统一:

| ?- sat(a + ~X), call_cleanup(labeling([X]), Det=true).
X = 0 ? ;
no

在 SWI-Prolog 中,多余的选择点很明显:

?- sat(a + ~X), labeling([X])。 X = 0 ; 错误

不是举这个例子来批评 SICStus Prolog 或 SWI 的行为:没有人真正关心是否在 labeling/1 中留下了多余的选择点,尤其是在涉及普遍量化变量的人工示例(这对于使用labeling/1 的任务来说是非典型的)。

am 给出这个例子是为了展示如何用如此强大的检查谓词很好和方便地测试记录和预期的保证......

...假设实现者有兴趣标准化他们的工作,以便这些谓词在不同的实现中实际上以相同的方式工作!细心的读者会注意到,在 SWI-Prolog 中使用反例搜索会产生截然不同的结果。

出人意料的是,上述测试用例在 SWI-Prolog 和 SICStus 的 call_cleanup/2 实现中发现了差异。在 SWI-Prolog (7.3.11) 中:

?- 差异(Det,true),call_cleanup(true,Det=true)。 dif(Det, true)。 ?- call_cleanup(true, Det=true), 差异(Det, true)。 错误

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

【讨论】:

  • 我们都喜欢好的 hack。但是,与 Duff 的 Device 一样,这突出了语言结构的一个问题:声称用于清理目的的内置函数会泄漏实现级别的信息。
  • 我发现这个谓词如此多才多艺真是太棒了,虽然表面上与清理有关,但它足以表达确定性信息,我认为这些信息在多种情况下都很有价值,因此应该可以访问, 可以很容易地用它提取出来。
  • 当然,它“棒极了”。我要说的是它的设计很糟糕;)
  • @jschimpf:在此期间,您是否有一个不是(如您所说)“糟糕设计”的提案?
  • @mat: w.r.t.您最近的更改:要改进当前文档post-N215,还有很多工作要做。欢迎任何贡献!对于您的示例,还需要对 dif/2 进行编码,但目前还没有...
【解决方案2】:

在 setup_call_cleanup/3 中不能保证它检测到确定性,即在目标成功时缺少选择点。 7.8.11.1 描述draft proposal 只说:

c) 清理处理程序只被调用一次;不迟于
在 G 失败时。较早的时刻是:
如果 G 为真或假,则在 实现
中调用 C 最后一个解决方案之后和最后一个解决方案之后的依赖时刻
G的可观察效果。

所以目前没有要求:

setup_call_cleanup(true, true, Det=true)

首先返回 Det=true。这也反映在draf proposal给出的测试用例7.8.11.4示例中,我们发现一个测试用例说:

setup_call_cleanup(true, true, X = 2).
Either: Succeeds, unifying X = 2.
Or: Succeeds.

所以它是一个有效的实现,检测确定性而不是检测确定性。

【讨论】:

  • 作为实施者,我们始终建议您超越标准规定的范围。您必须阅读该标准以确保满足 绝对最低 要求,并且正如您在 setup_call_cleanup/3 的所有实际实现中已经看到的那样,可用系统在实际尽快调用清理时远远超出此最低要求!
  • 没关系,尽管仍然没有理由拒绝一个显示如何在超出标准规定的系统中使用此谓词的答案,甚至在其文档中包含这样的示例。
  • 我明确地说它是相当可移植的。请实际阅读您投票的帖子。
  • 好消息是,目前由 Ulrich Neumerkel 维护的大多数 ISO 文档、草案或标准的规范都非常谨慎。许多特性都依赖于实现,因此并非所有 Prolog 系统实现都需要。
  • 也许误解来自这里的错误声明:“setup_call_cleanup/3 也可用于测试目标的确定性,提供确定性/1 的可移植替代方案”,swi-prolog.org/pldoc/man?predicate=setup_call_cleanup/3
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2012-10-20
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多