【问题标题】:Recursion transformation without stack frames code repetitions没有堆栈帧代码重复的递归转换
【发布时间】:2016-01-28 14:08:02
【问题描述】:

我有以下伪代码:

function X(data, limit, level = 0)
{
    result = [];
    foreach (Y(data, level) as entity) {
        if (level < limit) {
            result = result + X(entity, limit, level + 1);
        } else {
            //trivial recursion case:
            result = result + Z(entity);
        }
    }
    return result;
}

我需要把它变成一个普通的(例如,没有递归调用)。到目前为止,我对如何优雅地做到这一点一无所知。在this 的回答之后,我看到我必须构建整个堆栈帧,它们基本上是代码重复(即,我将一次又一次地放置相同的代码并使用不同的返回地址)。

或者我尝试了 these suggestions 之类的东西 - 有一个短语

  1. 查找不是尾调用的递归调用。
  2. 确定该调用与其返回语句之间正在完成的工作。

但我不明白在内部循环中发生的情况下如何识别“工作”。

所以,我的问题是,上面的所有示例都提供了“可以轻松识别工作”的情况,因为函数内部没有控制指令。我了解编译级别递归背后的概念,但我要避免的是代码重复。所以,

我的问题:如何处理上述伪代码的转换,这并不意味着模拟堆栈帧的代码重复?

【问题讨论】:

    标签: algorithm recursion


    【解决方案1】:

    它看起来像是一种算法,将嵌套数据结构(列表的列表)降级并将其展平为单个列表。在问题中有这样一个简单的描述会很好。

    为此,您需要跟踪多个索引/迭代器/游标,在您下降的每一层都有一个。递归实现通过使用调用堆栈来做到这一点。非递归实现需要manually-implemented stack data structure,您可以在其中推送/弹出内容。

    由于您不必在调用堆栈上保存上下文(寄存器)和返回地址,只需保存实际的迭代器(例如数组索引),这样可以节省更多空间。

    当您循环 Y 的结果并需要调用 X 或 Z 时,将当前状态压入堆栈。分支回到foreach 的开头,并在新实体上调用 Y。当你到达一个循环的末尾时,如果有的话,弹出旧的状态,然后在循环的中间继续。

    【讨论】:

    • 你能用伪代码详细说明一下吗?就 X、Y 和 Z 调用而言?此外,它不是列表迭代的列表,它实际上是遍历某个级别并将其展平的树(但我同意它是相似的)
    • @AlmaDo:我扩展了一些。我添加的en.wikibooks.org/wiki/Data_Structures/Stacks_and_Queues 链接(谷歌第一次点击“堆栈数据结构”)有一个用于河内塔问题的递归和手动堆栈函数的示例。看看该函数如何使用堆栈。如果您仍然没有得到它,请在尝试解决此问题时扩展您遇到的确切位置。这是你如何保存foreach 循环的状态吗?我也不知道,因为您刚刚发布了一些可以返回列表的虚构语言的伪代码。推送整个列表是愚蠢的。
    • “手动实现的堆栈”等等 - 抱歉,我知道那件事。如何做到这一点 - 是个问题。我有明确实施的问题。我现在所拥有的只是复制堆栈帧(因此,每个帧都包含相同的代码,并且必须按照我们定义的级别重复多次)。如何将其转换为我现在无法掌握的清晰堆栈实现。请注意,这个问题源于这样一个事实,即在一个本地空间内实际上不是一个,而是多个递归调用(数量等于 Y() 返回的元素计数)
    • @AlmaDo:该链接不是我在上次编辑中添加的唯一内容。最后两段准确地解决了您要问的问题。检查差异。
    • 谢谢你,你有我的投票 - 虽然我已经解决了这个案子,但阅读一般见解仍然很有用
    猜你喜欢
    • 1970-01-01
    • 2015-06-06
    • 2018-12-02
    • 2017-03-12
    • 1970-01-01
    • 2014-07-14
    • 2018-03-25
    • 1970-01-01
    • 2012-02-23
    相关资源
    最近更新 更多