68

我有一个看起来像这样的程序:

$offset = Point.new(100, 200);

def draw(point)
  pointNew = $offset + point;
  drawAbsolute(point)
end

draw(Point.new(3, 4));

的使用$offset似乎有点奇怪。

在 C 中,如果我在任何函数之外定义一些东西,它就会自动成为一个全局变量。为什么在 Ruby 中它必须是$offset但不能是offset并且仍然是全局的?如果是offset,那么它是本地的吗?但是本地到哪里,因为它感觉非常全球化。

有没有更好的方法来编写上面的代码?起初使用$offset可能看起来有点难看。


更新:我可以将此偏移量放在class定义中,但是如果两个或多个类需要使用此常量怎么办?在这种情况下,我还需要定义一个$offset吗?

4

4 回答 4

112

Ruby 中的变量范围在某种程度上由 sigils 控制。以全局开头的$变量,@以实例变量开头的变量,@@表示类变量,以大写字母开头的名称为常量。所有其他变量都是本地变量。当您打开一个类或方法时,这是一个新的范围,并且在以前的范围中可用的本地变量不可用。

我通常更喜欢避免创建全局变量。有两种技术通常可以达到我认为更清洁的相同目的:

  1. 在模块中创建一个常量。因此,在这种情况下,您会将需要偏移量的所有类放在模块中Foo并创建一个常量Offset,这样所有类都可以访问Foo::Offset.

  2. 定义访问值的方法。您可以全局定义该方法,但同样,我认为最好将其封装在模块或类中。这样,数据就可以在您需要的地方使用,您甚至可以根据需要对其进行更改,但是您的程序结构和数据的所有权将更加清晰。这更符合OO设计原则。

于 2009-06-26T05:23:05.010 回答
53

您需要意识到的一件事是,在 Ruby 中,一切都是对象。鉴于此,如果您没有在Moduleor中定义您的方法Class,Ruby 会将其放在Object类中。因此,您的代码将是Object范围的本地代码。

面向对象编程的典型方法是将所有逻辑封装在一个类中:

class Point
  attr_accessor :x, :y

  # If we don't specify coordinates, we start at 0.
  def initialize(x = 0, y = 0)
    # Notice that `@` indicates instance variables.
    @x = x
    @y = y
  end

  # Here we override the `+' operator.
  def +(point)
    Point.new(self.x + point.x, self.y + point.y)
  end

  # Here we draw the point.
  def draw(offset = nil)
    if offset.nil?
      new_point = self
    else
      new_point = self + offset 
    end
    new_point.draw_absolute
  end

  def draw_absolute
    puts "x: #{self.x}, y: #{self.y}"
  end
end

first_point = Point.new(100, 200)
second_point = Point.new(3, 4)

second_point.draw(first_point)

希望这能澄清一点。

于 2009-06-25T12:10:03.270 回答
10

全局变量需要前缀(称为“sigil”)的原因之一是因为在 Ruby 中,与 C 不同,您不必在分配变量之前声明变量。印记被用作明确变量范围的一种方式。

如果没有全局变量的特定前缀,则pointNew = offset + point在您的draw方法中给定一个语句,然后offset引用该方法中的一个局部变量(NameError在这种情况下会导致 a )。用于@引用实例变量和@@类变量的相同。

在其他使用显式声明的语言中,例如等CJava声明的位置用于控制范围。

于 2009-06-25T07:39:59.820 回答
-3

我认为它是您声明偏移量的文件的本地文件。将每个文件视为方法本身。

也许把整个东西放到一个类中,然后用@@offset = Point.new(100, 200);?

于 2009-06-25T06:06:41.973 回答