尾递归调用(C primer plus book example)

时间:2016-01-01 18:19:24

标签: c tail-recursion

C Primer Plus (6th edition)中,它定义了tail recursion concept 以这种方式:

  

在最简单的递归形式中,递归调用位于函数的末尾,就在return语句之前。这称为尾递归或结束递归,因为递归调用最后会发生。尾递归是最简单的形式,因为它就像一个循环。

它给出了一个以尾递归方式计算阶乘的例子:

long rfact(int n) {
    long ans;
    if (n > 0)
        ans = n * rfact(n - 1);
    else
        ans = 1;
    return ans;
 }

它也是一个侧面说明,在我看来这是不正确的:

  

注意虽然对rfact()的递归调用不是函数中的最后一行,但它是n>时执行的最后一个语句。 0,所以它是尾递归。

可以清楚地看到最后一个语句是n * rfact(n - 1),如果你递归扩展,它将导致一系列延迟乘法。该过程本质上是递归的,因此实现不能像here所描述的那样是尾递归的。

这个例子具有误导性。你有什么意见?

2 个答案:

答案 0 :(得分:4)

至于一本好的C编程书,我使用的是C编程语言。

你说这不是尾递归是正确的。阶乘的典型尾递归示例是:

int factorial(int x) {
    return tailfactorial(x, 1);
}

int tailfactorial(int x, int multiplier) {
    if (x <= 0) {
        return multiplier;    
    }    
    return tailfactorial(x - 1, x * multiplier);
}

我想你的书没有解释尾递归的目的。尾递归用于不增加堆栈深度&#34;。编译器可以用&#34; goto&#34;替换递归调用。不增加堆栈深度的命令。只有在直接返回递归值时才会进行此编译器修改。您将在示例中注意到情况并非如此。

答案 1 :(得分:2)

给定的定义和示例都具有误导性。 tail recursion的定义是:

  

如果函数返回后除了返回其值之外没有任何操作,则称函数调用是尾递归的。

递归调用不应该在函数的return语句或last语句之前。查看示例:

function foo(data) {
    a(data);
    return b(data);
} 

在这种情况下,a就在return语句之前,但b处于尾部位置。

function bar(data) {
    if ( a(data) ) {
        return b(data);
    }
    return c(data);
}

在此示例中,bc都处于尾部位置,但b不在函数bar的末尾。

在你给出的例子中,最后一个函数在返回乘法之前执行

ans = n * rfact(n - 1);   

因此,它不是尾递归函数。

尾递归函数的例子是

factorial1(n, accumulator) {
    if (n == 0) return accumulator;
    return factorial1(n - 1, n * accumulator);  // The last thing, before return, performed 
                                                // by factorial1 is to call itself. 
  }

  factorial(n) {
    return factorial1(n, 1);
  }  

可以由编译器优化

factorial1(n, accumulator) {
    while (n != 0) {
      accumulator *= n;
      n -= 1;
    }
    return accumulator;
  }