Prolog 结束递归的序言

Prolog 结束递归的序言,prolog,Prolog,因此,对于这个程序,我正在尝试制作一个倒计时程序,它将Y作为一个数字,从3倒计时到0,同时将每个数字添加到一个列表中,以便倒计时(3,Y)。应该生成结果Y=[3,2,1]。我运行此程序时似乎无法结束递归,我想知道是否有人可以帮助我 我似乎无法使此代码正常工作。有什么帮助吗?我似乎脱离了全局堆栈,因此我不知道如何结束递归。您的代码中有几个错误: 第一条不统一Y 第二个子句将append与第一个和第三个参数Y一起使用,只有当X=[]时,append才会成功 在该子句中,您试图用另一个总是失败的值统

因此,对于这个程序,我正在尝试制作一个倒计时程序,它将Y作为一个数字,从3倒计时到0,同时将每个数字添加到一个列表中,以便倒计时(3,Y)。应该生成结果
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)
    神奇地生成一个列表
  • 一个常见的Prolog习惯用法是在递归时构建列表。列表的尾部在每个递归中传递,而列表本身是不完整的。完整列表中最后一项是atom
    []
    ,表示空列表。以这种方式构建列表时,最后一项始终是一个变量,在递归成功之前,列表不会完整。因此,简单的解决方案就是在递归时构建列表:

    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]统一
    其中now
    Z
    [1]
    给出
    [2,1]
    。然后它再次返回调用方,调用方再次统一第二个参数;这次是
    [3 | Z]
    其中now
    Z
    [2,1]
    ,从而给出
    [3,2,1]的最终结果
    。啊,谢谢你现在完全有道理了,最后一个问题,倒计时(1,[1])。必须是第二个子句吗?如果我把它放在第一位就没关系了?是的,我把它放在第二个子句中,这样我使用的顶级解释器就不会留下最终会失败的选择点(如果X=1,那么当检查X>1时递归步骤将失败)。那么这是否意味着如果i c