【问题标题】:Traverse and reconstruct a Ruby object graph efficiently without recursion无需递归即可高效地遍历和重构 Ruby 对象图
【发布时间】:2014-10-02 13:45:21
【问题描述】:

我可能只是想在这里做一些疯狂的事情,所以让我先解释一下我的用例:

我在 Ruby 中有一个对象图,它只包含基本的 JSON 类型(字符串、数字、数组、哈希、真、假、空值)。我希望最终将此图序列化为 JSON。

问题是我无法控制图中所有对象的来源。这意味着对象图中包含的某些字符串可能被标记为错误的编码(例如,实际上只是一堆随机垃圾字节的字符串最终被标记为 UTF-8 编码)。这会导致 JSON 序列化失败(因为 JSON 只支持 UTF-8 编码的字符串)。

我有一个处理这些有问题的字符串的策略,基本上包括用每个字符串的转换版本替换它们(确切的转换并不真正相关)。

为了将这种转换应用于字符串,我需要遍历整个对象图并找到所有这些对象。这对于使用标准深度优先搜索递归实现是微不足道的。一个问题是我想避免改变原始对象图或其中的任何字符串,所以我基本上是在遍历它时构建对象图的副本(只有无问题的叶节点直接从新图,以及所有其他被欺骗的节点)。

这一切都有效,并且相当有效,在转换后的对象图中保存了非叶节点的重复。问题是它有时会得到非常深嵌套的对象图,因此递归有时会产生SystemStackError

我已经使用带有Enumerator 对象堆栈的 DFS 实现了一个非递归解决方案,但它似乎比递归解决方案慢得多(可能是因为 Enumerators 和在每个 Enumerator 末尾引发的愚蠢的 StopIteration 异常。

广度优先搜索似乎不合适,因为我认为在访问给定节点时没有办法确定返回到根的路径,我认为我需要它来构建树的副本。

我对 BFS 的看法有误吗?是否有其他技术可以用来在不递归的情况下完成这种遍历?这都是疯子吗?

【问题讨论】:

  • 为什么需要遍历栈进行拷贝?要复制图,请保留将原始节点映射到复制节点的哈希。搜索原文。检查每条边的目的地,看看它是否在散列中。如果不是,则复制目标,将 original->copy 对放入哈希中,然后将新副本添加到搜索队列/堆栈中。如果它已经在散列中,只需将目标指针设置为副本。使用此算法,您使用的搜索无关紧要。一堆枚举器很疯狂。保留一堆完全未探索的孩子,而不是部分探索的父母!
  • JSON 对象需要嵌套多深才能为您提供SystemStackError?我印象深刻。

标签: ruby json algorithm recursion


【解决方案1】:

除了使用递归之外,您还可以使用堆栈显式查看更多详细信息:

Way to go from recursion to iteration

http://haacked.com/archive/2007/03/04/Replacing_Recursion_With_a_Stack.aspx/

【讨论】:

    猜你喜欢
    • 2013-03-19
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-08-25
    • 2020-12-06
    • 2021-11-09
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多