Мы с друзьями работаем над некоторыми базовыми упражнениями Ruby, чтобы почувствовать язык, и мы столкнулись с интересным поведением, которое мы пока не можем понять. По сути, мы создаем тип данных tree
, в котором есть только один класс, узел
, который содержит ровно одно значение и массив из нуля или более узлов
. Мы используем средство запуска тестов autospec rspec . В какой-то момент мы начали писать тесты, запрещающие бесконечную рекурсию (круговая древовидная структура).
Вот наш тест:
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 .) Но если мы бросим помещает
в метод ==
, чтобы увидеть, как часто он вызывается, мы обнаруживаем, что он вызывается ровно три раза, а затем тест проходит.
Что нам не хватает?
Edit : Обратите внимание, что если мы заменим be_false
в тесте на be_true
, то тест не пройдёт. Таким образом, он определенно считает, что массивы не равны, он просто не повторяется по ним (кроме трех разных вызовов ==
).