【问题标题】:Why the following function doesn't generate tail recursion with Clang?为什么以下函数不会使用 Clang 生成尾递归?
【发布时间】:2013-06-03 03:11:36
【问题描述】:

在 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});
}

是 clang 未能正确优化它还是有合理的原因?

为了强制运行时评估,x 是运行时整数素数 ≥ 13(一次递归)或足够大的 constexpr 素数,由于递归深度大,会阻止编译时评估:

is_prime(x);

【问题讨论】:

  • 删除 != 0 意味着三元语句的结果可能不是布尔值。结果必须转换为正确的类型,因此递归调用不是最后一个语句。
  • @krsteeve 我以前想过,但这对我来说没有意义。这是一个简单的分支,如果执行不到number % limit,它将被丢弃,因此,不需要为下一次调用保存状态,它可以被丢弃,不管它是否与!= 0,在我看来。
  • 经过一番挖掘 - 如果你有 exp1 ? exp2 : exp3, 判断 ?: 语句的类型,将 exp3 的类型转换为 exp2 的类型(如果可能)。这意味着您的漂亮 bool 结果被转换为类型 T 并且必须转换回 bool 才能返回。我很好奇如果你反转你的 ?: 声明会发生什么

标签: c++ clang tail-recursion


【解决方案1】:

如果您有exp1 ? exp2 : exp3,要确定?: 语句的类型,将exp3 的类型转换为exp2 的类型(如果可能)。这意味着您漂亮的 bool 结果被转换为类型 T 并且必须转换回 bool 才能返回。

这意味着递归调用不是最后一个语句。我相信如果你在三元运算符中颠倒顺序你会得到尾递归的结果。

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;
}

【讨论】:

  • 遗憾的是,我刚刚对其进行了测试,但它并没有按您的预期工作。您的示例不会产生尾递归版本(clang 版本 3.4 主干 183147)。我同意?: 强制转换规则是有道理的,因为exp2 可以防止调用上下文因is_prime 的尾调用而被丢弃,因为强制转换操作被堆叠以应用于此类调用。
猜你喜欢
  • 1970-01-01
  • 2023-03-25
  • 2015-06-27
  • 2013-09-14
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多