首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >什么是PL-Unit中的“带有选择点的测试成功”警告,以及如何修复它?

什么是PL-Unit中的“带有选择点的测试成功”警告,以及如何修复它?
EN

Stack Overflow用户
提问于 2016-11-21 02:29:11
回答 1查看 3.2K关注 0票数 8

我正在编写一个prolog程序,以检查变量是否为整数。我“返回”结果的方式很奇怪,但我不认为这对回答我的问题很重要。

测验

我已经为这种行为写过了通过单元测试;它们在这里…

foo_test.pl

代码语言:javascript
复制
:- begin_tests('foo').
:- consult('foo').

test('that_1_is_recognised_as_int') :-
    count_ints(1, 1).

test('that_atom_is_not_recognised_as_int') :-
    count_ints(arbitrary, 0).

:- end_tests('foo').
:- run_tests.

“守则”

这是通过测试的代码..。

foo.pl

代码语言:javascript
复制
count_ints(X, Answer) :-
  integer(X),
  Answer is 1.

count_ints(X, Answer) :-
  \+ integer(X),
  Answer is 0.

输出量

测试正在通过,这是很好的,但是当我运行它们时,我正在收到警告。这是运行测试时的输出。

代码语言:javascript
复制
?- ['foo_test'].
%  foo compiled into plunit_foo 0.00 sec, 3 clauses
% PL-Unit: foo 
Warning: /home/brandon/projects/sillybin/prolog/foo_test.pl:11:
        /home/brandon/projects/sillybin/prolog/foo_test.pl:4:
        PL-Unit: Test that_1_is_recognised_as_int: Test succeeded with choicepoint
. done
% All 2 tests passed
% foo_test compiled 0.03 sec, 1,848 clauses
true.
  • 我使用SWI(多线程,64位,版本6.6.6)
  • 我尝试将两个count_ints谓词组合为一个,使用;,但仍然会产生相同的警告。
  • 我正在使用Debian 8(我怀疑这会有什么不同)。

问题

  • 这个警告是什么意思?还有..。
  • 我该怎么预防呢?
EN

回答 1

Stack Overflow用户

发布于 2016-11-21 03:47:07

首先要做的是:SWI-Prolog Prolog单元测试包的文档相当不错。在第2.2款.编写测试体中解释了不同的模式。2.2.1中的相关句子是:

确定性谓词是必须成功一次的谓词,对于行为良好的谓词,不留下选择点。重点雷

什么是选择点?

在过程编程中,当调用一个函数时,它可以返回一个值或一组值;它可以修改状态(本地或全局);不管它做什么,它都会精确地执行一次。

在Prolog中,当计算谓词时,将搜索证明树以寻找解决方案。有可能有一个以上的解决方案!假设您像这样使用between/3

对于x= 1,x是0,1,2吗?

代码语言:javascript
复制
?- between(0, 2, 1).
true.

但你也可以问:

枚举所有x,使x在0、1、2中。

代码语言:javascript
复制
?- between(0, 2, X).
X = 0 ;
X = 1 ;
X = 2.

获得第一个解决方案后,X = 0、Prolog停止并等待;这意味着:

查询between(0, 2, X)至少有一个解决方案,X = 0。它可能有更多的解决方案;按;和Prolog将搜索下一个解决方案的验证树。

选择点是Prolog在找到解决方案后在搜索树中放置的标记。它将从这个标记恢复寻找下一个解决方案。

警告"Test with choicepoint“的意思是:

Prolog找到的解决方案正是测试所期望的解决方案;但是,它留下了一个选择点,因此它不是“良好的行为”。

选择点是个问题吗?

选择点,你没有故意放在那里可能是个问题。如果不详细说明,它们可以防止某些优化,并造成效率低下。这是可以的,但有时只有第一个解决方案是您(程序员)想要的解决方案,下一个解决方案可能会误导或错误。或者,著名的是,在给出一个有用的答案之后,Prolog可以进入一个无限循环。

同样,如果您知道的话,这也很好:在计算这个谓词时,您永远不会要求一个以上的解决方案。您可以将其包装在once/1中,如下所示:

代码语言:javascript
复制
?- once( between(0, 2, X) ).

代码语言:javascript
复制
?- once( count_ints(X, Answer) ).

如果其他人使用您的代码,尽管所有的赌注都取消了。通过选择点成功意味着任何东西,从“有其他有用的解决方案”到“不再有解决方案,这将失败”到“其他解决方案,但不是您想要的解决方案”,到“现在进入无限循环!”

摆脱选择点

对于特定的例子:您有一个内置的integer/1,它将成功或失败,而不会留下选择点。因此,您最初定义的count_ints/2中的这两个子句对于(任何值的X )都是相互排斥的。

代码语言:javascript
复制
count_ints(X, Answer) :-
  integer(X), ...

count_ints(X, Answer) :-
  \+ integer(X), ...

然而,Prolog并不知道这一点。它只看条款的标题,这两个是相同的:

代码语言:javascript
复制
count_ints(X, Answer) :- ...

count_ints(X, Answer) :- ...

这两个参数是相同的,Prolog没有进一步判断子句头是否值得尝试,所以它尝试第二个子句,即使第一个参数确实是一个整数(这是警告中的“选择点”),而总是失败

因为您知道这两个子句是相互排斥的,所以可以让Prolog忘记另一个子句。您可以使用once/1,如上面所示。当第一个参数确实是整数时,还可以剪切验证树的其余部分:

代码语言:javascript
复制
count_ints(X, 1) :- integer(X), !.
count_ints(_, 0).

完全相同的操作语义,但Prolog编译器可能更容易优化:

代码语言:javascript
复制
count_ints(X, Answer) :-
    (   integer(X)
    ->  Answer = 1
    ;   Answer = 0
    ).

..。就像垫子上的答案一样。至于使用模式匹配,这很好,但是如果X来自其他地方,而不是来自您自己编写的代码,那么您仍然需要在某个时候进行检查。你最后的下场是:

代码语言:javascript
复制
variable_tagged(X, T) :-
    (   integer(X) -> T = i(X)
    ;   float(X)   -> T = f(X)
    ;   atom(X)    -> T = a(X)
    ;   var(X)     -> T = v(X)
    % and so on
    ;   T = other(X)
    ).

这时,您可以按照mat的建议编写您的count_ints/2,而Prolog通过查看子句头就知道您的两个子句是相互排斥的。

我的曾经问过一个问题归结为相同的Prolog行为以及如何处理它。mat的回答推荐了同样的方法。mat对我下面的评论的评论和答案本身一样重要(如果你正在编写真正的程序)。

票数 10
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/40711908

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档