为什么attr_accessor不能用于设置方法

问题描述 投票:0回答:1

我在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访问根,但是类不应该直接与其实例变量交互。这就是我们需要attr_accessor提供的getter和setter方法的原因。但它不起作用。我错过了什么?

以下是POODR一书的截图。它表示即使在课堂上也不要直接使用实例变量。

enter image description here enter image description here

ruby class oop getter-setter object-oriented-analysis
1个回答
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替换你引用@root的每个地方。

我认为你正在寻找的方式(但不是“正确的”方式,我不建议)是调用访问者定义的rootroot=方法。

如果你采用这条路线,你还必须定义#insert只将value作为参数,并用node替换你引用root的每个地方。这样可行,但这不是解决问题的正确方法。如果你这样解决,请在CodeReview.se上提问,这样我就可以澄清如何使代码更好。

为什么它不起作用

在#insert方法中,您正在操作传递给方法的node参数,而不是root。 Ruby是通过值传递而不是通过引用传递(sorta),所以当你将mybst.root传递给#insert时,你实际上是在传递nil,因为mybst.root == nil。然后mybst.insert调用返回一个新的Node,但是你没有对该返回值做任何事情。如果你想将root设置为该返回值,你可以这样做:

mybst = BST.new
p mybst.root
mybst.root = mybst.insert(1, mybst.root)
mybst.root = mybst.insert(2, mybst.root)
mybst.root = mybst.insert(10, mybst.root)
mybst.root = mybst.insert(12, mybst.root)

p mybst

解释该教科书试图说什么

我认为这里令人困惑的部分是教科书所说的:

隐藏变量,甚至可以从定义变量的类中隐藏变量

这是正确的,但我认为你误解了它。本节说您应该隐藏该实例之外的任何实例变量。在该实例中,使用它们是完全可以的,这实际上是实例变量存在的原因 - 在实例中存储状态。定义行为方法而不是直接暴露实例变量只是更好。当然,这只是一个要记住的规则 - 我确信你会遇到这种建议不适用的情况,但通常你想要将实例变量保持在内部。

© www.soinside.com 2019 - 2024. All rights reserved.