为什么这个递归不是无限的?
我和我的朋友正在做一些基本的 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 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(1)
如果单击链接到的 RubyDoc 的方法名称,您将看到
Array#==
方法的源代码(C 语言):此实现(特别是“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:This implementation (specifically the "recursive_equal") suggests that
Array#==
already implements the infinite recursion protection you're after.