为什么这个递归不是无限的?

发布于 2024-09-19 01:13:23 字数 1359 浏览 2 评论 0原文

我和我的朋友正在做一些基本的 Ruby 练习来感受这门语言,我们遇到了一个我们尚无法理解的有趣行为。基本上,我们正在创建一种tree数据类型,其中只有一个类node,它只包含一个值和一个由零个或多个节点组成的数组代码>.我们正在使用 rspec 的 autospec 测试运行程序。在某一时刻,我们开始编写测试来禁止无限递归(循环树结构)。

这是我们的测试:

it "breaks on a circular reference, which we will fix later" do
  tree1 = Node.new 1
  tree2 = Node.new 1
  tree2.add_child tree1
  tree1.add_child tree2
  (tree1 == tree2).should be_false
end

这是 Node 类:

class Node
  attr_accessor :value
  attr_reader :nodes

  def initialize initial_value = nil
    @value = initial_value
    @nodes = []
  end

  def add_child child
    @nodes.push child
    @nodes.sort! { |node1, node2| node1.value <=> node2.value }
  end

  def == node
    return (@value == node.value) && (@nodes == node.nodes)
  end
end

我们期望测试的最后一行会导致无限递归,直到堆栈溢出,因为它应该不断地相互比较子节点,并且永远不会找到叶节点。 (我们的印象是,数组上的 == 运算符将迭代该数组,并基于 RubyDoc 的数组页面。)但是如果我们将 puts 放入== 方法来查看它的调用频率,我们发现它被调用了 3 次,然后测试就通过了。

我们缺少什么?

编辑:请注意,如果我们将测试中的 be_false 替换为 be_true,则测试将失败。所以它肯定认为数组不相等,它只是不对它们进行递归(除了对 == 的三个不同调用)。

My friends and I are working on some basic Ruby exercises to get a feel for the language, and we've run into an interesting behavior that we're yet unable to understand. Basically, we're creating a tree data type where there's just one class, node, which contains exactly one value and an array of zero or more nodes. We're using rspec's autospec test runner. At one point we started writing tests to disallow infinite recursion (a circular tree structure).

Here's our test:

it "breaks on a circular reference, which we will fix later" do
  tree1 = Node.new 1
  tree2 = Node.new 1
  tree2.add_child tree1
  tree1.add_child tree2
  (tree1 == tree2).should be_false
end

Here's the Node class:

class Node
  attr_accessor :value
  attr_reader :nodes

  def initialize initial_value = nil
    @value = initial_value
    @nodes = []
  end

  def add_child child
    @nodes.push child
    @nodes.sort! { |node1, node2| node1.value <=> node2.value }
  end

  def == node
    return (@value == node.value) && (@nodes == node.nodes)
  end
end

We expect the last line of the test to result in an infinite recursion until the stack overflows, because it should continually compare the child nodes with each other and never find a leaf node. (We're under the impression that the == operator on an array will iterate over the array and call == on each child, based on the array page of RubyDoc.) But if we throw a puts into the == method to see how often it's called, we discover that it's called exactly three times and then the test passes.

What are we missing?

Edit: Note that if we replace be_false in the test with be_true then the test fails. So it definitely thinks the arrays are not equal, it's just not recursing over them (aside from the three distinct calls to ==).

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(1

悲歌长辞 2024-09-26 01:13:23

如果单击链接到的 RubyDoc 的方法名称,您将看到 Array#== 方法的源代码(C 语言):

{
    // [...]
    if (RARRAY(ary1)->len != RARRAY(ary2)->len) return Qfalse;
    if (rb_inspecting_p(ary1)) return Qfalse;
    return rb_protect_inspect(recursive_equal, ary1, ary2);
}

此实现(特别是“recursive_equal”)建议 < code>Array#== 已经实现了您想要的无限递归保护。

If you click on the method name of the RubyDoc you linked to, you will see the source (in C) of the Array#== method:

{
    // [...]
    if (RARRAY(ary1)->len != RARRAY(ary2)->len) return Qfalse;
    if (rb_inspecting_p(ary1)) return Qfalse;
    return rb_protect_inspect(recursive_equal, ary1, ary2);
}

This implementation (specifically the "recursive_equal") suggests that Array#== already implements the infinite recursion protection you're after.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文