我正在编写一个prolog程序,以检查变量是否为整数。我“返回”结果的方式很奇怪,但我不认为这对回答我的问题很重要。
测验
我已经为这种行为写过了通过单元测试;它们在这里…
foo_test.pl
:- 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
count_ints(X, Answer) :-
integer(X),
Answer is 1.
count_ints(X, Answer) :-
\+ integer(X),
Answer is 0.输出量
测试正在通过,这是很好的,但是当我运行它们时,我正在收到警告。这是运行测试时的输出。
?- ['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.count_ints谓词组合为一个,使用;,但仍然会产生相同的警告。问题
发布于 2016-11-21 03:47:07
首先要做的是:SWI-Prolog Prolog单元测试包的文档相当不错。在第2.2款.编写测试体中解释了不同的模式。2.2.1中的相关句子是:
确定性谓词是必须成功一次的谓词,对于行为良好的谓词,不留下选择点。重点雷
什么是选择点?
在过程编程中,当调用一个函数时,它可以返回一个值或一组值;它可以修改状态(本地或全局);不管它做什么,它都会精确地执行一次。
在Prolog中,当计算谓词时,将搜索证明树以寻找解决方案。有可能有一个以上的解决方案!假设您像这样使用between/3:
对于x= 1,x是0,1,2吗?
?- between(0, 2, 1).
true.但你也可以问:
枚举所有x,使x在0、1、2中。
?- 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中,如下所示:
?- once( between(0, 2, X) ).或
?- once( count_ints(X, Answer) ).如果其他人使用您的代码,尽管所有的赌注都取消了。通过选择点成功意味着任何东西,从“有其他有用的解决方案”到“不再有解决方案,这将失败”到“其他解决方案,但不是您想要的解决方案”,到“现在进入无限循环!”
摆脱选择点
对于特定的例子:您有一个内置的integer/1,它将成功或失败,而不会留下选择点。因此,您最初定义的count_ints/2中的这两个子句对于(任何值的X )都是相互排斥的。
count_ints(X, Answer) :-
integer(X), ...
count_ints(X, Answer) :-
\+ integer(X), ...然而,Prolog并不知道这一点。它只看条款的标题,这两个是相同的:
count_ints(X, Answer) :- ...
count_ints(X, Answer) :- ...这两个参数是相同的,Prolog没有进一步判断子句头是否值得尝试,所以它尝试第二个子句,即使第一个参数确实是一个整数(这是警告中的“选择点”),而总是失败。
因为您知道这两个子句是相互排斥的,所以可以让Prolog忘记另一个子句。您可以使用once/1,如上面所示。当第一个参数确实是整数时,还可以剪切验证树的其余部分:
count_ints(X, 1) :- integer(X), !.
count_ints(_, 0).完全相同的操作语义,但Prolog编译器可能更容易优化:
count_ints(X, Answer) :-
( integer(X)
-> Answer = 1
; Answer = 0
)...。就像垫子上的答案一样。至于使用模式匹配,这很好,但是如果X来自其他地方,而不是来自您自己编写的代码,那么您仍然需要在某个时候进行检查。你最后的下场是:
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对我下面的评论的评论和答案本身一样重要(如果你正在编写真正的程序)。
https://stackoverflow.com/questions/40711908
复制相似问题