我写了一些谓词,它们采用列表的长度并附加一些约束(这是要使用的正确词汇吗?):
clp_length([], 0).
clp_length([_Head|Rest], Length) :-
Length #>= 0, Length #= Length1 + 1,
clp_length(Rest, Length1).
clp_length2([], 0).
clp_length2([_Head|Rest], Length) :-
Length #= Length1 + 1,
clp_length2(Rest, Length1).
第一个终止于此简单查询,但第二个不终止:
?- Small in 1..2, clp_length(Little, Small).
Small = 1,
Little = [_1348] ;
Small = 2,
Little = [_1348, _2174] ;
false.
?- Small in 1..2, clp_length2(Little, Small).
Small = 1,
Little = [_1346] ;
Small = 2,
Little = [_1346, _2046] ;
% OOPS %
这对我来说很奇怪,因为Length明显大于0。要弄清楚您可以搜索,找到零,然后推断从零开始加数只会增加数字,或者您可以传播{{ 1}}约束下降。感觉到多余的子句是多余的!并不是说我对clpfd的思维模式是完全错误的。
所以我想我有两个问题(希望对第二个问题的回答作为评论)
具体地说,为什么这个附加约束导致查询正常工作?
通常,是否可以使用资源来了解clpfd的实现方式,而不仅仅是看一些如何使用clpfd的示例?我不想读马库斯·特里斯卡(Markus Triska)的论文,但这是我唯一能找到的资料。如果我想回答这样的问题,那是我唯一的选择吗?
答案 0 :(得分:3)
1mo,命名存在问题。请参考以前的答案
mat
和me推荐关系名称。使用不适当的名称将不会走太远。因此,list_length/2
或list_fdlength/2
将是一个适当的名称。因此,我们有list_fdlength/2
和list_fdlength2/2
。
2do,请考虑list_fdlength2/2
的规则。没有任何迹象表明0与您相关。因此,如果您使用0或1或-1或其他基本情况,则该规则将完全相同。那么,这个糟糕的规则怎么会意识到0代表了你的结局?最好考虑一下概括:
list_fdlength2(fake(N), N) :- % Extension to permit fake lists N #< 0. list_fdlength2([], 0). list_fdlength2([_Head|Rest], Length) :- Length #= Length1 + 1, list_fdlength2(Rest, Length1).
这种概括显示所有真实答案以及虚假答案。请注意,我尚未更改规则,仅添加了此替代事实。因此,伪造的解决方案实际上是由以下规则引起的:
?- list_fdlength2(L, 1). L = [_A] ; L = [_A, _B|fake(-1)] ; L = [_A, _B, _C|fake(-2)] ; ... ?- list_fdlength2(L, 0). L = [] ; L = [_A|fake(-1)] ; L = [_A, _B|fake(-2)] ; ...
每个子句仅在子句的范围内尝试为解决方案做出贡献。但是无法(通过内置的Prolog执行机制)得出某些规则不再相关的信息。您必须像以前一样明确声明带有冗余约束。
现在,回到包含冗余约束Length #>= 0
的原始解决方案。根本不应该有任何这种伪造的解决方案。
list_fdlength(fake(N), N) :- N #< 0. list_fdlength([], 0). list_fdlength([_Head|Rest], Length) :- Length #>= 0, Length #= Length1 + 1, list_fdlength(Rest, Length1). ?- list_fdlength(L, 1). L = [_A] ; L = [_A, _B|fake(-1)] % totally unexpected ; false. ?- list_fdlength(L, 0). L = [] ; L = [_A|fake(-1)] % eek ; false.
也有假答案!真丑!至少,它们的数量是有限的。但是,您可以通过使用
Length #>= 1
代替Length #>=0
。有了这个很小的变化,当N为非负数时,就不再有任何伪造的解决方案,因此您的原始程序也会更好。