Я не уверен, что Hash действительно лучшая структура данных здесь. Вы пытаетесь использовать его для представления дерева, и это хорошо, но все может быть немного яснее, если вы просто явно сделаете его деревом:
class Tree
attr_reader :name, :uri, :children, :parent
def initialize(name, uri, *children)
@children = children
@name, @uri = name, uri
end
def <<(child)
@children << child
end
def find(name)
each_branch.detect {|branch| branch.name == name }
end
def leaf?
@children.empty?
end
# The parameter `i` just decides whether or not to include self.
def each_branch( i=true, &blk )
enum = Enumerator.new do |y|
y.yield self if i
@children.each do |c|
next unless c.is_a? Tree
y.yield c
c.each_branch( false ).each {|b| y.yield b }
end
end
block_given? ? enum.each( &blk ) : enum
end
# This yields each leaf and its parent.
def each_leaf( parent=self, &blk )
enum = Enumerator.new do |y|
@children.each do |c|
if !c.leaf?
c.each_leaf( c ).each do |l,p|
y.yield l, p
end
else y.yield c, parent
end
end
end
block_given? ? enum.each( &blk ) : enum
end
end
(Я только что позаимствовал эти перечислители из древовидной структуры, которую я создал ранее - метод each_leaf
также может быть полезен, и вы можете проверить, не является ли класс Tree
вместо leaf?
, возвращающим true
если у вас есть древовидная структура, которая может содержать другие объекты, например строки).
Тогда вы могли бы сделать:
root_tree = Tree.new "Farming", "farming"
root_tree << Tree.new( "Organic Gas", "organic_gas" )
gas = root_tree.find "Organic gas"
gas << Tree.new(...)
Я думаю, что это просто случай поиска правильной структуры данных для работы. Даже если метод дерева был немного менее эффективен, он более понятен, и, вероятно, приведет к меньшему количеству ошибок в вашем динамическом коде в будущем.
Если проблема в том, что вы не хотите, чтобы исходное дерево было изменено, только скопировано, а затем просто переопределите:
class Tree
attr_accessor :children
def <<(child)
new_tree = self.dup
new_tree.children = @children + [child]
new_tree
end
end
Таким образом, вы сохраняете исходное дерево, но возвращаете новое дерево с добавленным дополнительным потомком.