我正在尝试制作一个Prolog谓词,让我测试给定值是否大于0的整数,并给出一个给定变量的有效整数。它现在看起来像这样:
intgr(1).
intgr(X) :- intgr(Y), X is Y+1.
这将生成所有整数,但是当用作intgr(8)时,它会发现它是有效的,然后永远循环。有关如何解决这个问题的想法吗?
答案 0 :(得分:3)
首先是您的程序未终止的原因。要看到这一点,我会在您的计划中添加 false
目标:
intgr(1) :- false. intgr(X) :- intgr(Y), false,X is Y+1.
现在有趣的是以下属性。鉴于对您的程序的任何查询,
如果此片段(failure-slice)没有终止,那么原始程序也不会终止。
在这个片段中,可能更容易看到:该程序无法终止。变量X
仅在头部出现一次。在程序的可见部分没有进一步的参考,因此没有人关心X
的具体实例。那就是:无论你把什么作为intgr/1
的参数,你的定义都会循环。什么! intgr(-1)
循环,intgr(stop)
循环,intgr(X)
循环,intgr(1)
循环。
有关故障片及其使用方法的更多信息,请参阅标记failure-slice。
要解决此问题,必须在程序的可见部分更改某些内容。不要看透视部分 - 他们将无法改善终止。
我会使用包含0的length(_, N)
。这是另一种使用library(clpfd)
的方式:
?- X #> 0.
如果你坚持列举解决方案:
nat(0).
nat(X0) :-
X0 #> 0,
X1 #= X0-1,
nat(X1).
答案 1 :(得分:1)
为了生成自然数,您可以轻松编写尾递归(因此更节省空间)谓词。例如:
next_integer(I) :-
next_integer(1, I).
next_integer(I, I).
next_integer(I, J) :-
I2 is I + 1,
next_integer(I2, J).
但请注意,已经提供了几个Prolog编译器作为内置谓词或库谓词,事实上的标准between/3
谓词,它允许您枚举给定时间间隔内的整数。例如:
?- between(1, 3, I).
I = 1 ;
I = 2 ;
I = 3.
根据实现,您还可以使用此谓词来测试整数是否属于给定间隔而不会留下虚假选择点。例如:
?- between(1, 3, 2).
true.
回到你的问题,你的谓词将在测试时循环回溯,因为它中没有任何内容表明你传递的参数将不会再次通过继续在每次传递中添加一个来找到。一种解决方案是使用标准内置谓词var/1
测试参数,并在绑定参数时剪切第一个解决方案。例如:
next_integer(I) :-
( var(I) ->
next_integer(1, I).
; integer(I) ->
next_integer(1, I),
!
; fail % or error
)
next_integer(I, I).
next_integer(I, J) :-
I2 is I + 1,
next_integer(I2, J).
希望这有帮助。