如何区分尾递归 calls/non-tail 递归调用?

How can I distinguish tail recursive calls/non-tail recursive calls?

int gcd(int a,int b){
    if(a == b) return a;
    else if (a>b) return gcd(a-b,b);
    else return gcd(a,b);
    }

例如,我认为这是尾递归,因为你不调用另一个函数。

int gcd(int a,int b){
int x;
    if(a == b) x=a;
    else if (a>b) x= gcd(a-b,b);
    else x= gcd(a,b);
    return x;
    }

而且这是非尾递归,因为它调用了函数 gcd。

我说的对吗?或者有没有更简单的方法来区分tail/non-tail递归调用?

首先,出于 g++ TCO(tail-call 优化)的目的,无论您是进行递归调用还是完全调用不同的函数都没有关系 - 函数调用将被无条件跳转替换.

其次,当调用其他函数和 return 之间没有发生任何事情时,就会发生尾调用。它可以是 return 之前的最后一行,而不是 return 或 return 本身之前的最后一行。

例如,

} else {
   x = gcd(a,b);
   return x;
}

是一个 tail-call,因为 x 的值是 return 未修改的(没有任何反应)。

另一方面,

} else {
   x = gcd(a,b);
   return x + 1;
}

这不符合 TCO 的条件,因为 return 值已修改 - 正在发生。

但乐趣才刚刚开始!让我们谈谈 C++ 和析构函数。考虑以下代码:

int do2();

int do() {
    std::string x;
    // ...
    return do2();
}

是tail-call吗?第一印象 - 是的,是的。什么都没有发生,对吧?第二印象——不,不是! x 析构函数需要发生!第三印象 - 是的 - 因为编译器看到 x 在调用之后没有被使用,所以可以很容易地在调用之前破坏 x

但是,看看那个:

int do2(const std::string& );

int do() {
    std::string x;
    // ...
    return do2(x);
}

这里不是tail-call! x 必须比 do2 长寿,所以回到我原来的(故意模糊的)定义,一些事情 正在发生。

Tail-calls很有趣!