为什么在添加冗余约束之前,此clpfd查询不会终止?

时间:2018-06-22 16:49:47

标签: prolog clpfd

我写了一些谓词,它们采用列表的长度并附加一些约束(这是要使用的正确词汇吗?):

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)的论文,但这是我唯一能找到的资料。如果我想回答这样的问题,那是我唯一的选择吗?

1 个答案:

答案 0 :(得分:3)

1mo,命名存在问题。请参考以前的答案 matme推荐关系名称。使用不适当的名称将不会走太远。因此,list_length/2list_fdlength/2将是一个适当的名称。因此,我们有list_fdlength/2list_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为非负数时,就不再有任何伪造的解决方案,因此您的原始程序也会更好。