为什么Ruby中的变量前缀允许在方法调用时省略括号?

6

2
其实是个好问题。严格来说,我不确定那个陈述是否正确。我一直认为前缀是用来指定作用域(即局部、实例、类、全局),而不是区分变量和方法(显然解释器正在通过区分局部变量名和方法调用来进行区分)。 - Cubic
当您询问特定参考资料时,如果在线上可用,请至少提供链接。 - sawa
@sawa:链接 - Amadan
2个回答

1

以下是我的不成熟看法。如果我错了,请纠正我。

假设实例变量没有@前缀,那么我们如何声明实例变量?

class MyClass
  def initialize
    # Here foo is an instance variable
    self.foo = 'bar'
  end

  # Here foo is an instance method
  def foo
  end

  def bar
    # Call method. No doubt.
    self.foo()

    # Call method? Access instance variable?
    self.foo
  end
end

在上述情况中,实例变量foo使用假设的语法进行初始化。
self.foo = 'bar'

因为我们必须有一种方法告诉 Ruby 解释器 foo 属于当前实例并且不是局部变量。
在方法 bar 中,如果没有强制使用括号进行方法调用,解释器如何判断 self.foo 是方法调用还是实例变量访问?
也许我们可以让实例变量在缺少括号时覆盖实例方法,但这会导致语言本身的语法不一致。例如,在 MyClass 定义之外:
obj = MyClass.new

# Call instance method
obj.foo

这是一个实例方法调用,因为所有实例变量在外部都是不可见的(至少不使用元编程的情况下)。但是,在方法bar中,self.foo(没有括号)是一个实例变量访问。这种语法上的不一致可能会导致错误。此外,它使解释器本身的实现变得困难。
那么让实例方法遮蔽实例变量呢?然后程序员必须确保实例变量名称不与实例方法名称冲突,否则实例变量将变得无法访问。如果一个类有一个长的祖先链,这就非常困难,因为人们必须检查当前类定义和其祖先中的冲突,这会破坏封装性。
无论哪种方式,代价都比给实例变量添加前缀要高得多。对于类和全局变量,情况类似。

0
让我们来看一个例子:
def foo
    10
end

p foo
#=> 10
foo = 'hi'
p foo
#=> hi
p foo()
#=> 10

如果我们在同一作用域中定义一个与方法名称相同的局部变量,那么我们将失去对方法名称的绑定,因为变量名称优先。为了消除歧义,必须使用括号。

这本书似乎详细阐述了这种现象,并设计了决策,以提供灵活性,其中需要使用符号(如@)来定义实例变量,以便可以调用相应的访问器方法(varvar=)而无需使用括号,人们会有一种访问变量的印象,即使实际上是调用方法。

……您会注意到Ruby变量名称开头的标点符号:全局变量以$为前缀,实例变量以@为前缀,类变量以@@为前缀。这些前缀可能需要一点时间来适应,但是过一段时间后,您可能会欣赏前缀告诉您变量范围的事实。这些前缀是必需的,以消除Ruby非常灵活的语法的歧义。变量前缀的一种思考方式是,它们是我们为能够省略方法调用周围的括号所付出的代价之一。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接