2013-06-03 13 views
2

在铛下面的函数产生目标码为一个尾递归函数:为什么下面的函数不会产生Clang的尾递归?

template<typename T> 
constexpr bool is_prime(T number, T limit, T counter) 
{ 
    return counter >= limit 
     ? number % limit != 0 
     : number % counter 
      ? is_prime(number, number/counter, counter + 2) 
      : false; 
} 

template<typename T> 
constexpr bool is_prime(T n) 
{ 
    return n == 2 || n == 3 || n == 5 
     ? true 
     : n <= 1 || n % 2 == 0 
      ? false 
      : is_prime(n, n/3, T{3}); 
} 

但改变一条线(让一个布尔结果“非归一化”),它就不再产生尾递归对象代码:

template<typename T> 
constexpr bool is_prime(T number, T limit, T counter) 
{ 
    return counter >= limit 
     ? number % limit // changed here 
     : number % counter 
      ? is_prime(number, number/counter, counter + 2) 
      : false; 
} 

template<typename T> 
constexpr bool is_prime(T n) 
{ 
    return n == 2 || n == 3 || n == 5 
     ? true 
     : n <= 1 || n % 2 == 0 
      ? false 
      : is_prime(n, n/3, T{3}); 
} 

它铛没有正确地优化它或有一个合乎逻辑的原因吗?

要强制运行评价,x是一个运行时不可或缺的首要≥ 13(一个递归),或足够大的constexpr主要是将防止由于大量递归深度编译时评价:

is_prime(x); 
+0

删除!= 0意味着三元语句的结果可能不是一个布尔值。结果必须转换为正确的类型,因此递归调用不是最后一个语句。 – krsteeve

+0

@krsteeve我以前就想过这个,但对我来说这没有意义。这是一个简单的分支,如果执行不会达到'number%limit',它将被丢弃,所以,没有必要为下一次调用保存状态,它可以被丢弃,尽管它是'!= 0 '在我看来,不是。 –

+0

经过多一点挖掘 - 如果你有exp1? exp2:exp3,确定?:语句的类型,将exp3的类型转换为exp2的类型(如果可能)。这意味着你不错的布尔结果被转换成T类型,并且必须被转换回布尔值才能返回。我很好奇,如果你反悔你的?:声明 – krsteeve

回答

2

如果你有exp1 ? exp2 : exp3 ,确定?:声明的类型exp3的类型被转换为exp2(如果可能)的类型。这意味着你的漂亮布尔结果被转换为T类型,必须转换回布尔返回。

这意味着递归调用不是最后一个语句。我相信如果你逆转三元运算符的顺序,你将得到尾递归结果。

template<typename T> 
constexpr bool is_prime(T number, T limit, T counter) 
{ 
    return counter < limit 
     ? (number % counter 
      ? is_prime(number, number/counter, counter + 2) 
      : false) 
     : number % limit; 
} 
+0

会发生什么情况,我只是测试了一下,它没有像你期望的那样工作。您的示例不会生成尾递归版本(ng版本3.4中继183147)。我同意'?:'转换规则是有意义的,因为'exp2'可以防止在调用is_prime尾部时被抛弃的调用上下文,因为一个转换操作被堆叠以应用于这种调用。 –