Prolog结束递归

时间:2014-12-12 16:38:44

标签: prolog

 countdown(0, Y).
 countdown(X, Y):-
    append(Y, X, Y),
    Y is Y-1,
    countdown(X, Y).

因此,对于这个节目,我正在尝试制作一个倒计时程序,它将Y取一个数字并从3到0倒计时,同时将每个数字添加到列表中,以便倒计时(3,Y)。应该产生结果Y=[3,2,1].当我运行这个时,我似乎无法结束递归,我想知道是否有人可以帮助我?

我似乎无法获得此代码以获得任何帮助?我似乎离开了全局堆栈,所以我不明白如何结束递归。

2 个答案:

答案 0 :(得分:0)

您的代码中存在多个错误:

  • 第一条不统一Y。
  • 第二个子句使用附加第一个和第三个参数Y,只有在X = []。
  • 时才会成功
  • 在该子句中,您试图将Y与另一个始终失败的值统一起来。
  • Y应该是一个列表(根据你的评论)在头部,但你用它来统一一个整数。

你可以这样做:

countdown(X, L):-
  findall(Y, between(1, X, Y), R), 
  reverse(R, L).

between/3将为您提供从1到X的每个数字(回溯)。因此findall/3可以收集所有数字。这会给你升序,所以我们reverse/2得到降序。

如果你想以递归方式编码自己:

countdown(X, [X|Z]):-
  X > 1,
  Y is X-1,
  countdown(Y, Z).
countdown(1, [1]).

基本情况(第2条)规定,数字1会产生一个包含第1项的列表。

Recursive clause(first clause)声明如果X大于1,那么输出列表应该包含附加了递归调用结果的X.

答案 1 :(得分:0)

您的原始代码

countdown( 0 , Y ) .
countdown( X , Y ) :-
  append(Y, X, Y),
  Y is Y-1,
  countdown(X, Y).

有一些问题:

  1. countdown(0,Y).并未将Y与任何内容统一起来。
  2. Y is Y-1正在尝试将YY-1的值统一起来。在Prolog中,变量一旦绑定到一个值,就不再变量了:它们就变成了统一的变量。因此,如果Y是数值,则Y is Y-1将失败。如果Y是一个变量,根据您的Prolog实现,它将失败或抛出错误。
  3. 您永远不会使用列表。您希望append(Y,X,Y)能够神奇地制作一个列表。
  4. 一个常见的Prolog习语是在你递归时建立列表。列表的尾部在每次递归时传递,列表本身不完整完整列表是最后一项是原子[]的列表,表示空列表。在以这种方式构建列表时,最后一项是始终一个变量,并且列表不会完成,直到递归成功为止。因此,简单的解决方案就是在递归时构建列表:

    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!
    

    您可能会注意到countdown(0,L)会成功生成L = []。您可以通过更改我们的规则来修复它。特殊(终止)情况略有不同,一般情况强制执行N > 1的下限而不是N > 0

    countdown( 1 , [1]    ) .
    countdown( N , [N|Ns] ) :-
      N > 1 ,
      N1 is N-1 ,
      countdown(N1,Ns)
      .
    

    如果真的想要使用append/3,你可以。它介绍了另一种常见的Prolog习语: helper 谓词的概念,它承载状态并完成所有工作。辅助谓词通常与" public"相同。谓词,具有更高的统一性。像这样:

    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( 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.   
      .                      %
    

    你可能会注意到使用这样的append/3意味着它会在每次调用时迭代累加器,从而得到O(N 2 )性能而不是所需的O(N)性能

    避免这种情况的一种方法是以相反的顺序构建列表,并在最后反转它。这只需要在列表上进行一次额外的传递,这意味着您可以获得O(2N)性能而不是O(N 2 )性能。这给了你这个帮手:

    countdown( 0 , T , L ) :- % once the countdown is complete,
      reverse(T,L)            % reverse the accumulator and unify it 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.   
      .                       %