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

Why do variable prefixes in ruby allow omission of parenthesis in method invocations?

在 David Flanagan 的 Ruby 编程语言中;松本行弘,they state that the variable prefixes ($, @, @@) are one price we pay for being able to omit parentheses around method invocations。谁可以给我解释一下这个?

这是我不成熟的意见。如果我错了,请指正。

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

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(没有括号)是实例变量访问。这种语法不一致可能会导致错误。而且,它给解释器本身的实现带来了困难。

那么让实例方法隐藏实例变量呢?然后程序员必须确保实例变量名称不会与实例方法名称冲突,否则实例变量将无法访问。如果一个 class 有一个很长的祖先链,这是非常困难的,因为必须不仅在当前 class 定义中检查冲突,而且在它的祖先中检查冲突,这会破坏封装。

无论哪种方式,代价都比给实例变量加前缀高很多。对于class和全局变量,情况类似。

让我们看一个例子:

def foo
    10
end

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

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

这本书似乎详细说明了这一现象,并采取了设计决策来提供灵活性,其中需要使用像 @ 这样的符号来定义实例变量,以便相应的访问器方法(var & var=) 可以在没有括号的情况下被调用,并且给人一种正在访问变量的印象,即使实际上是在调用方法。

... you’ll notice punctuation characters at the start of Ruby variable names: global variables are prefixed with $, instance variables are prefixed with @, and class variables are prefixed with @@. These prefixes can take a little getting used to, but after a while you may come to appreciate the fact that the prefix tells you the scope of the variable. The prefixes are required in order to disambiguate Ruby’s very flexible grammar. One way to think of variable prefixes is that they are one price we pay for being able to omit parentheses around method invocations.