14

有时它很简单(如果 self 调用是最后一个语句,它就是尾递归),但仍有一些情况让我感到困惑。一位教授告诉我“如果在自调用之后没有执行指令,那就是尾递归”。这些例子怎么样(忽略它们没有多大意义的事实):

a)这个应该是尾递归的,看看自调用是最后一个语句,并且在它之后没有任何东西可以执行。

function foo(n)
{
    if(n == 0)
        return 0;
    else
        return foo(n-2);
}

b) 但是这个呢?它应该是一个尾调用,因为如果条件为真,除了它之外什么都不会被执行,但它不是最后一个语句?

function foo(n)
{
    if(n != 0)
        return foo(n-2);
    else
        return 0;
}

c) 这个怎么样?在这两种情况下,self 调用都是最后执行的:

function foo(n)
{
    if(n == 0)
        return 0;
    else    
    {
        if(n > 100)
            return foo(n - 2);
        else
            return foo(n - 1);
    }
}
4

4 回答 4

19

它可能会帮助您从实际实现尾调用优化的方式来考虑这一点。当然,这不是定义的一部分,但它确实激发了定义。

通常,当调用函数时,调用代码会将以后需要的任何寄存器值存储在堆栈上。它还将存储一个返回地址,指示调用后的下一条指令。它将做任何需要做的事情来确保为被调用者正确设置堆栈指针。然后它会跳转到目标地址[*](在这种情况下,同样的函数)。返回时,它知道返回值位于调用约定(寄存器或堆栈槽)指定的位置。

对于尾调用,调用者不会这样做。它忽略任何寄存器值,因为它知道以后不需要它们。它设置堆栈指针,以便被调用者将使用与调用者相同的堆栈,并且它不会将自己设置为返回地址,它只是跳转到目标地址。因此,被调用者将覆盖相同的堆栈区域,它会将其返回值放在调用者放置其返回值的相同位置,并且当它返回时,它不会返回给它的调用者,而是会返回到它的调用者的呼叫者。

因此,非正式地,当尾调用优化可能发生并且尾调用的目标是函数本身时,函数是尾递归的。效果或多或少与函数包含循环相同,并且尾调用不是调用自身,而是跳转到循环的开头。这意味着在调用之后必须不需要任何变量(实际上也没有“工作要做”,这在像 C++ 这样的语言中意味着什么都不会被破坏),并且尾调用的返回值必须由调用者返回。

这都是为了简单/琐碎的尾递归。有一些转换可以用来做一些尚未尾递归的东西,例如引入额外的参数,这些参数存储一些由“最底层”递归级别使用的信息,以完成原本可以完成的工作出路”。例如:

int triangle(int n) {
    if (n == 0) return 0;
    return n + triangle(n-1);
}

可以由程序员或由足够聪明的编译器自动进行尾递归,如下所示:

int triangle(int n, int accumulator = 0) {
    if (n == 0) return accumulator;
    return triangle(n-1, accumulator + n);
}

因此,谈论足够聪明的语言/编译器的人可能会将前一个函数描述为“尾递归”。为该变体用法做好准备。

[*] 存储返回地址、移动堆栈指针和跳转,可能会或可能不会被架构包含在单个操作码中,但即使不是这样,通常也会发生这种情况。

于 2010-09-09T18:22:32.980 回答
6

是的; 我认为您的教授的意思是,在任何路径中,如果最终指令是递归的,那么它就是尾递归。

因此,所有三个示例都是尾递归的。

于 2010-09-09T16:36:30.273 回答
6

你所有的函数都是尾递归的。

自调用后没有指令

意思是:自调用后,你从函数中返回,即不再需要执行代码,并不是说函数中没有更多的代码行。

于 2010-09-09T16:36:49.813 回答
3

所有三个示例都是尾递归的。一般来说,它是尾递归,如果函数的结果(“return”关键字后面的表达式)是对函数本身的单独调用。表达式的最外层不得涉及其他运算符。如果对自身的调用只是表达式的一部分,那么机器必须执行调用,然后必须返回到对所述表达式的评估中,也就是说,它不是在函数执行的尾部,而是在一种表达。然而,这不适用于递归调用可能采用的任何参数:那里允许任何参数,包括对自身的递归调用(例如“return foo(foo(0));”)。当然,只有外部调用才能优化对跳转的调用。

于 2010-09-09T16:52:21.777 回答