我在ruby中实现BST并使用递归编写插入方法。
我正在尝试使用attr_accessor来设置和获取root,但它不起作用。任何人都可以帮忙吗?
class Node
attr_accessor :value, :left_child, :right_child
def initialize (value)
@value = value
@left_child = nil
@right_child = nil
end
end
class BST
attr_accessor :root
def initialize
@root = nil
end
def insert(value, node)
if node == nil
node = Node.new(value)
return node
end
return node if node.value == value
if node.value > value
if node.left_child == nil
node.left_child = Node.new(value)
return
else
insert(value, node.left_child)
end
else
if node.right_child == nil
node.right_child = Node.new(value)
return
else
insert(value, node.right_child)
end
end
end
end
mybst = BST.new
p mybst.root
mybst.insert(1, mybst.root)
mybst.insert(2, mybst.root)
mybst.insert(10, mybst.root)
mybst.insert(12, mybst.root)
p mybst
上面的代码显示了Node类的简单实现和带有insert方法的BST类。给了我#<BST:0x00557398d02378 @root=nil>
如果我使用self.root它可以工作。
可以使用@root访问root,但是类不应该直接与其实例变量进行交互。这就是我们需要attr_accessor提供的getter和setter方法的原因。但它不起作用。我缺少什么?
以下是POODR一书的截图。它表示即使在课堂上也不要直接使用实例变量。
答案 0 :(得分:2)
实际上完全可以在实例方法中使用实例变量。事实上,这就是他们的目标! Setter和getter允许实例外部的东西访问实例内的变量。他们(基本上)定义类的实例方法,如下所示:
class Foo
# getter -- Same as attr_reader :root
def root
@root
end
# setter -- Same as attr_writer :root
def root=(root)
@root = root
end
# attr_accessor defines a setter *and* a getter.
end
因此,您可以通过定义#insert
来简化代码,以便只需要一个参数(value
)并将node
引用的每个位置替换为{{1} }}
我认为您正在寻找的方式(但不是&#34;正确的方式,而且我不建议)是致电@root
和{ {1}}访问者定义的方法。
如果您采用此路线,则还必须定义root
以仅将root=
作为参数,并将#insert
引用的每个地点替换为{{1} }}。这样可行,但这不是解决问题的正确方法。如果你这样解决,请在CodeReview.se上提问,这样我就可以澄清如何使代码更好。
在#insert方法中,您将操纵传递给方法的value
参数,而不是node
。 Ruby是按值传递而不是通过引用传递(sorta),因此当您将root
传递给node
时,您有效地传递root
,因为mybst.root
。然后,#insert
调用会返回一个新节点,但您不会对该返回值执行任何操作。如果您想将nil
设置为该返回值,则可以执行以下操作:
mybst.root == nil
我认为这里令人困惑的部分是教科书所说的内容:
隐藏变量,甚至是从定义变量的类
这是正确的,但我认为你错误地解释了它。本节说您应该隐藏实例变量来自该实例之外的任何内容。在那个实例中,使用它们是完全可以的,实际上存在实例变量的原因 - 在实例中存储状态。仅仅考虑更好地定义行为方法而不是直接暴露实例变量。当然,这只是一个要记住的规则 - 我确定您会遇到这种建议不适用的情况,但通常您希望将实例变量保持在内部。