在CLP(FD)中生成任意长度的列表时不终止

时间:2016-12-15 16:20:06

标签: prolog clpfd

为什么在返回预期答案后,以下内容会以ERROR: Out of global stack退出?

?- L #>= 0, L #=< 3, length(X, L).

L = 0,
X = [] ;
L = 1,
X = [_G1784] ;
L = 2,
X = [_G1784, _G1787] ;
L = 3,
X = [_G1784, _G1787, _G1790] ;
ERROR: Out of global stack

更新:W.r.t。 @ Joan的回答,我试图理解为什么它没有终止,不一定找到 终止的解决方案。我的意思是,如果问题是无约束力,那么在贴标签之前它不应该同样产生任何答案,对吧?所以我的问题与产生答案(而不是终止)的机制有关,而不是修复代码。

2 个答案:

答案 0 :(得分:5)

问题是长度/ 2谓词是坚定的。您可以找到一些帖子来了解Stack Overflow中的坚定性,@mat的一个好问题是:Steadfastness: Definition and its relation to logical purity and termination。简单来说,坚定性是谓词最后评估它的参数的属性。

在您的示例中,您可以给出约束:

L #>= 0, L #=< 3

但在length(X, L). L中将在最后评估。所以会发生的是length(X, L)有无限的选择点(它会检查每个列表X),对于每个列表X,它将评估L,如果L符合约束,那么它将返回给你一个答案并继续检查导致无限循环的下一个列表。

您可以在跟踪模式中看到以下内容:

  Call: (8) length(_G427, _G438) ? creep
   Exit: (8) length([], 0) ? creep
   Call: (8) integer(0) ? creep
   Exit: (8) integer(0) ? creep
   Call: (8) 0>=0 ? creep
   Exit: (8) 0>=0 ? creep
   Call: (8) integer(0) ? creep
   Exit: (8) integer(0) ? creep
   Call: (8) 3>=0 ? creep
   Exit: (8) 3>=0 ? creep
X = [],
L = 0 ;
   Redo: (8) length(_G427, _G438) ? creep
   Exit: (8) length([_G1110], 1) ? creep
   Call: (8) integer(1) ? creep
   Exit: (8) integer(1) ? creep
   Call: (8) 1>=0 ? creep
   Exit: (8) 1>=0 ? creep
   Call: (8) integer(1) ? creep
   Exit: (8) integer(1) ? creep
   Call: (8) 3>=1 ? creep
   Exit: (8) 3>=1 ? creep
X = [_G1110],
L = 1 ;
   Redo: (8) length([_G1110|_G1111], _G438) ? creep
   Exit: (8) length([_G1110, _G1116], 2) ? creep
   Call: (8) integer(2) ? creep
   Exit: (8) integer(2) ? creep
   Call: (8) 2>=0 ? creep
   Exit: (8) 2>=0 ? creep
   Call: (8) integer(2) ? creep
   Exit: (8) integer(2) ? creep
   Call: (8) 3>=2 ? creep
   Exit: (8) 3>=2 ? creep
X = [_G1110, _G1116],
L = 2 ;
   Redo: (8) length([_G1110, _G1116|_G1117], _G438) ? creep
   Exit: (8) length([_G1110, _G1116, _G1122], 3) ? creep
   Call: (8) integer(3) ? creep
   Exit: (8) integer(3) ? creep
   Call: (8) 3>=0 ? creep
   Exit: (8) 3>=0 ? creep
   Call: (8) integer(3) ? creep
   Exit: (8) integer(3) ? creep
   Call: (8) 3>=3 ? creep
   Exit: (8) 3>=3 ? creep
X = [_G1110, _G1116, _G1122],
L = 3 ;
 Redo: (8) length([_G1110, _G1116, _G1122|_G1123], _G438) ? creep
   Exit: (8) length([_G1110, _G1116, _G1122, _G1128], 4) ? creep
   Call: (8) integer(4) ? creep
   Exit: (8) integer(4) ? creep
   Call: (8) 4>=0 ? creep
   Exit: (8) 4>=0 ? creep
   Call: (8) integer(4) ? creep
   Exit: (8) integer(4) ? creep
   Call: (8) 3>=4 ? creep
   Fail: (8) 3>=4 ? creep

正如您在第一次调用length([_G1110|_G1111], _G438)中所看到的那样,它不会从头开始计算L,但它会根据第一个参数计算它,然后检查约束。

答案 1 :(得分:2)

这只是因为当长度运行时,它仍然只是一个未绑定的变量。它直到:

  • 约束的域减少为单个值
  • 未绑定变量与实际值统一
  • 您明确标记了变量

该变量将绑定到单个值。

您可以通过执行以下操作来修复您的示例:

?- L #>= 0, L #=< 3, label([L]), length(X, L).

要查看第一点,您可以看到:

?- L #>= 1, L #=< 1, length(X, L).

也有效,因为变量被约束为单个值。