这是利用遍历链表的尾调用递归的最佳方法吗?

时间:2011-06-23 23:57:53

标签: c++ linked-list tail-call-optimization

基本上我正在创建一个基类,它将用于存储为链表的类,这些类会被一个返回bool的虚拟更新()函数所规定的遍历和删除。

我想知道这是否是最有效的案例(我喜欢这个事实,特别是它可以是一个单一的链表):

class Traversable
{
    public:
        Traversable();
        virtual ~Traversable();
        void traverse(Traversable** prevNext);
        virtual bool update()=0;
    protected:
    private:
        Traversable* next;
};


void Traversable::traverse(Traversable** prevNext)
{
    if (!update()) /// Virtual function that returns a death flag
    { /// Death
        if (next)
        {
            Traversable* localNext = next;
            delete this;
            localNext->traverse(prevNext);
        }
        else
        {
            *prevNext = NULL;
            delete this;
        }
    }
    else
    { /// This node stays alive, for now
        *prevNext = this;
        if (next)
        {
            next->traverse(&next);
        }
    }
}

请注意,链接列表以NULL结尾。

我认为在调用下一个遍历函数后,对局部变量的仔细缺乏分配操作将确保使用尾调用来使用此函数。任何人都可以发现我做错的任何事情,或者可能暗示一种稍微不那么复杂的方法:p

1 个答案:

答案 0 :(得分:1)

你故意混淆代码,以“诱惑”编译器创建特定的结果;这种情况是否发生很可能更依赖于所使用的编译器,有效的优化标志,甚至是使用上述编译的代码。以下是更紧凑的代码:

void Traversable::traverse(Traversable** prevNext)
{
    bool doUpdate = update();

    *prevNext = doUpdate ? this : next ? *prevNext : NULL;

    Traversable **argNext = doUpdate ? &next : prevNext;
    Traversable *localNext = next;

    do_the_traversal_action();     // not spec'ed ...

    if (!doUpdate)
        delete this;
    if (localNext)
        localNext->traverse(argNext);
}

并且仍然以单个尾部返回点结束该函数。使用条件的唯一原因是因为您正在更改prevNext

编辑:我想说的是,无论你如何编码,最终由编译器决定是否要对函数进行尾部优化。对于现代优化编译器,通常在GCC中切换(-fconserve-stack-foptimize-sibling-calls)对源代码本身的影响更大。

编辑2:是的,如果当然可以非递归地编写此函数;最后,它只是一种访客类型模式。所以实际的活动最终会像:

static void Traversable::traverse(Traversable *start)
{
    Traversable *cur, *next;

    for (cur = start; cur; cur = next) {
        next = cur->next;

        cur->do_the_traversal_action();     // not spec'ed ...

        if (cur->update())
            continue;                       // not supposed to remove this

        if (next)
            next->prevNext = cur->prevNext; // remove cur from list
        delete cur;
    }
}

虽然,当你这样编码时,下一个显而易见的问题是,为什么不为Traversable实现简单的迭代器类型,并使用std::remove_copy_if()进行访问和删除条件任务。或者使用STL列表开始。