【问题标题】:Tail-recursion with objects对象的尾递归
【发布时间】:2017-05-14 19:37:28
【问题描述】:

我有一个递归函数,我想让它成为尾递归的。我的实际问题更复杂且取决于上下文。但我想解决的问题是用这个简单的程序演示的:

#include <iostream>

struct obj
{
    int n;

    operator int&() { return n; }
};

int tail(obj n)
{
    return tail(obj{ n + 1 > 1000 ? n - 1000 : n + 1 });
}

int main()
{
    tail(obj{ 1 });
}

这似乎是尾递归的。但是,这不是因为obj n 的析构函数每次都必须被调用。至少 MSVC13(编辑:)和 MSVC15 没有对此进行优化。如果我用 int 替换 obj 并相应地更改调用,它就会变成预期的尾递归。

我的实际问题是:除了将obj 替换为int 之外,还有一种简单的方法可以使这个尾递归吗?我的目标是提高性能,因此使用堆分配内存和new 很可能没有帮助。

【问题讨论】:

  • 最简单的方法:获得更好的编译器,反正你的编译器已经过时了......
  • msvc15 也不这样做
  • 你希望这个尾递归如何终止?
  • 您能否详细说明“每次都必须调用obj n 的析构函数”是什么意思?每个obj n 将在堆栈从您的递归基本案例(目前不存在)展开时被破坏。您希望何时/如何调用析构函数?
  • @SamVarshavchik 我没有。尾递归的一个常见测试是检查堆栈溢出(错误,而不是这个站点:))这就是我这样设计它的原因

标签: c++ recursion tail-recursion


【解决方案1】:

简短回答:不。

更长的答案:您可能会找到一种方法来实现这一点,但肯定不容易。 由于标准不要求尾调用优化,因此您永远无法确定对程序的一些细微更改是否会导致编译器无法优化代码。

更糟糕的是,考虑一下当您需要调试程序时会发生什么。编译器几乎肯定不会使用调试器标志优化高级尾​​调用,这意味着您的程序只能在发布模式下正常工作。这将使程序更难维护。

尾递归的替代方案 只写一个循环。它总是可以完成的,而且很可能不会那么复杂。它也不使用堆,因此开销会小得多。

【讨论】:

  • 谢谢!您的回答更直截了当,不过,StoryTeller 在大多数情况下更有帮助,这就是我将旗帜交给他的原因。我希望这是可以理解的
  • 嗯...有用的是写一个循环。我完全忘记在我的回答中提到这一点,所以我现在添加了它。 :-)
  • 也是个好主意,谢谢。可悲的是,我不能再次投票
【解决方案2】:

由于您使用临时对象,我假设您在递归调用之后不需要该对象。

一个相当老套的解决方案是分配一个对象,传递一个指向它的指针,然后在进行递归调用之前重新分配它,你将新构造的对象传递给它。

struct obj
{
    int n;

    operator int&() { return n; }
};

int tail_impl(obj*& n)
{
    int n1 = *n + 1 > 1000 ? *n - 1000 : *n + 1;
    delete n;
    n = new obj{n1};
    return tail_impl(n);
}

int tail(obj n)
{
  obj *n1 = new obj{n};
  auto ret = tail_impl(n1);
  delete n1;
  return ret;
}

int main()
{
    tail(obj{ 1 });
}

我显然省略了一些关键的异常安全细节。不过GCC is able to turn tail_impl into a loop,因为确实是尾递归。

【讨论】:

  • 好主意,确实!但是,我确实需要不改变对象。我的真实故事是,我有部分应该是尾递归的,而其他部分则不是......也许,无论如何你的解决方案都可以使用,我仍然会寻找一个非参考解决方案 - 如果有的话跨度>
  • tail_impl 递归如何结束?
  • @0x499602D2 - 不像在 OP 的帖子中那样。他们使用无限递归来测试代码是否被循环替换。如果没有,就会发生堆栈溢出。
  • 由于似乎没有更好的解决方案,我将其标记为已解决。尾递归可能会带来显着的好处,并且按值传递对象并不是什么奇怪的用例,所以我希望这将在未来的 C++ 编译器中得到改进
猜你喜欢
  • 2016-03-21
  • 2018-03-17
  • 1970-01-01
  • 2017-11-11
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2019-10-30
  • 1970-01-01
相关资源
最近更新 更多