如何区分尾递归调用/非尾递归调用?

时间:2016-06-15 16:51:13

标签: tail-recursion

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。

我是对的吗?或者有没有更简单的方法来区分尾/非尾递归调用?

1 个答案:

答案 0 :(得分:2)

首先,出于g ++ TCO(尾部调用优化)的目的,无论是进行递归调用还是完全调用其他函数都无关紧要 - 函数调用将被无条件跳转替换。

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

例如,

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

是一个尾调用,因为x的值是未修改的(没有发生)。

另一方面,

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

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

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

int do2();

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

这是一个尾声吗?第一印象 - 是的,它是。什么都没发生,对吧?第二印象 - 不,它不是!析构函数x需要发生!第三印象 - 是的,它是 - 因为在调用之后看不到x的编译器可以在之前轻易破坏x

但是,看看那个:

int do2(const std::string& );

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

这不是尾声! x必须比do2更长,所以回到我原来的(故意模糊的)定义,某事正在发生。

尾巴电话很有趣!