Prolog 结束递归的序言
因此,对于这个程序,我正在尝试制作一个倒计时程序,它将Y作为一个数字,从3倒计时到0,同时将每个数字添加到一个列表中,以便倒计时(3,Y)。应该生成结果Prolog 结束递归的序言,prolog,Prolog,因此,对于这个程序,我正在尝试制作一个倒计时程序,它将Y作为一个数字,从3倒计时到0,同时将每个数字添加到一个列表中,以便倒计时(3,Y)。应该生成结果Y=[3,2,1]。我运行此程序时似乎无法结束递归,我想知道是否有人可以帮助我 我似乎无法使此代码正常工作。有什么帮助吗?我似乎脱离了全局堆栈,因此我不知道如何结束递归。您的代码中有几个错误: 第一条不统一Y 第二个子句将append与第一个和第三个参数Y一起使用,只有当X=[]时,append才会成功 在该子句中,您试图用另一个总是失败的值统
Y=[3,2,1]。
我运行此程序时似乎无法结束递归,我想知道是否有人可以帮助我
我似乎无法使此代码正常工作。有什么帮助吗?我似乎脱离了全局堆栈,因此我不知道如何结束递归。您的代码中有几个错误:
- 第一条不统一Y
- 第二个子句将append与第一个和第三个参数Y一起使用,只有当X=[]时,append才会成功
- 在该子句中,您试图用另一个总是失败的值统一Y
- Y应该是头部的列表(根据您的评论),但您正在使用它来统一一个整数
countdown(0, Y).
countdown(X, Y):-
append(Y, X, Y),
Y is Y-1,
countdown(X, Y).
between/3
将为您提供从1到X的每个数字(回溯)。因此,findall/3
可以收集所有数字。这将为您提供升序,因此我们反向/2
以获得降序
如果要递归地编写自己的代码:
countdown(X, L):-
findall(Y, between(1, X, Y), R),
reverse(R, L).
基本情况(第2条)规定,数字1产生一个包含项目1的列表
Recursive子句(第一子句)声明,如果X大于1,则输出列表应包含X,并附加递归调用的结果。原始代码
countdown(X, [X|Z]):-
X > 1,
Y is X-1,
countdown(Y, Z).
countdown(1, [1]).
有一些问题:
倒计时(0,Y)。
不会将Y
与任何东西统一起来Y是Y-1
正在尝试将Y
与Y-1
的值统一起来。在Prolog中,变量一旦绑定到一个值,就不再是变量:它们成为统一的变量。因此,如果Y
是一个数值,Y是Y-1
将失败。如果Y
是一个变量,取决于您的Prolog实现,它将失败或抛出错误append(Y,X,Y)
神奇地生成一个列表[]
,表示空列表。以这种方式构建列表时,最后一项始终是一个变量,在递归成功之前,列表不会完整。因此,简单的解决方案就是在递归时构建列表:
countdown( 0 , Y ) .
countdown( X , Y ) :-
append(Y, X, Y),
Y is Y-1,
countdown(X, Y).
您可能会注意到,这将成功地进行倒计时(0,L)
,生成L=[]
。你可以通过稍微修改一下规则来解决它。特殊(终止)情况稍有不同,一般情况强制执行N>1
的下限,而不是N>0
countdown( 0 , [] ) . % The special case.
countdown( N , [N|Ns] ) :- % The general case: to count down from N...
N > 0 , % - N must be greater than 0.
N1 is N-1 , % - decrement N
countdown(N1,Ns) % - recurse down, with the original N prepended to the [incomplete] result list.
. % Easy!
如果您真的想使用append/3
,您可以。它引入了另一个常见的Prolog习语:携带状态并完成所有工作的helper谓词的概念。辅助谓词通常与“public”谓词具有相同的名称,具有更高的算术性。大概是这样的:
countdown( 1 , [1] ) .
countdown( N , [N|Ns] ) :-
N > 1 ,
N1 is N-1 ,
countdown(N1,Ns)
.
countdown(N,L) :- % to count down from N to 1...
N > 0 , % - N must first be greater than 0,
countdown(N,[],L) % - then, we just invoke the helper with its accumulator seeded as the empty list
. % Easy!
这里,countdown/2
是我们的“公共谓词”。它调用countdown/3
来完成工作。附加的参数带有所需的状态。该助手将如下所示:
countdown( 1 , [1] ) .
countdown( N , [N|Ns] ) :-
N > 1 ,
N1 is N-1 ,
countdown(N1,Ns)
.
countdown(N,L) :- % to count down from N to 1...
N > 0 , % - N must first be greater than 0,
countdown(N,[],L) % - then, we just invoke the helper with its accumulator seeded as the empty list
. % Easy!
您可能会注意到,像这样使用append/3
意味着每次调用时它都会在累加器上迭代,从而为您提供O(N2)性能,而不是期望的O(N)性能
避免这种情况的一种方法是以相反的顺序构建列表,并在最后反转。这只需要对列表进行一次额外的传递,这意味着您将获得O(2N)性能,而不是O(N2)性能。这为您提供了以下帮助:
countdown( 0 , L , L ) . % once the countdown is complete, unify the accumulator with the result list
countdown( N , T , L ) . % otherwise...
N > 0 , % - if N is greater than 0
N1 is N-1 , % - decrement N
append(T,[N],T1) , % - append N to the accumulator (note that append/3 requires lists)
countdown(N1,T1,L) % - and recurse down.
. %
您的第二个子句需要
X>0
,因为它只适用于这些条件。即使添加X>0,我仍然会脱离全局堆栈错误。请注意append
通常应该使用列表参数。您试图使用append
做什么?另外,Y是Y-1
也将始终失败,因为Y
和Y-1
值不能是相同的。您需要一个新变量,
Y1是Y-1,倒计时(X,Y1)。您不断地回溯到
append`,这将生成无限多的结果。因此,堆栈溢出。我试图创建一个列表Y,程序的每次运行都假设取X并将其放入Y中,然后从X减去1,因此如果X=3y,那么最后应该是Y=[3,2,1].Append不接受元素并将其附加到列表中。它附加两个列表,生成第三个列表。抱歉,我对prolog非常陌生,您能解释一下这是如何将新元素添加到列表中的吗?因为当我读到这篇文章时,我的大脑告诉我结果将是[1,2,3],而不是它得到的[3,2,1]您必须了解这将如何执行递归。当您使用3
调用它时,它将再次使用2
递归调用自己,然后使用1
。然后基本情况将第二个参数与[1]
统一,并返回给调用方,调用方还将返回头的第二个参数与[2|Z]统一
其中nowZ
是[1]
给出[2,1]
。然后它再次返回调用方,调用方再次统一第二个参数;这次是[3 | Z]
其中nowZ
是[2,1]
,从而给出[3,2,1]的最终结果
。啊,谢谢你现在完全有道理了,最后一个问题,倒计时(1,[1])。必须是第二个子句吗?如果我把它放在第一位就没关系了?是的,我把它放在第二个子句中,这样我使用的顶级解释器就不会留下最终会失败的选择点(如果X=1,那么当检查X>1时递归步骤将失败)。那么这是否意味着如果i c