5

这个问题中,当在其自己的赋值中使用未定义的局部变量时,它被评估为nil

x = x # => nil 

但是当局部变量的名称与现有的方法名称冲突时,就比较棘手了。为什么下面的最后一个示例返回nil

{}.instance_eval{a = keys} # => []
{}.instance_eval{keys = self.keys} # => []
{}.instance_eval{keys = keys} # => nil
4

3 回答 3

13

在 Ruby 中,因为可以在没有显式接收者和括号的情况下调用方法,所以在局部变量引用和无接收者无参数方法调用之间存在语法歧义:

foo

可能意味着“不带参数调用方法fooself“取消引用局部变量foo”。

如果foo范围内存在局部变量,则始终将其解释为局部变量取消引用,而不是方法调用。

那么,局部变量“在范围内”意味着什么?这是在解析时在语法上确定的,而不是在运行时在语义上确定的。这个非常重要!局部变量是在解析时定义的:如果解析器看到对局部变量的赋值,则从那时起,局部变量就在范围内。但是,它仅在运行时初始化,没有对代码进行编译时评估:

if false
  foo = 42 # from this point on, the local variable foo is in scope
end

foo # evaluates to nil, since it is declared but not initialized

为什么局部变量对“影子”方法有意义,而不是反过来?好吧,如果方法确实隐藏了局部变量,那么将不再有取消引用这些局部变量的方法。但是,如果局部变量隐藏方法,那么仍然有一种方法可以调用这些方法:请记住,歧义仅存在于无接收者无参数方法调用中,如果添加显式接收器或显式参数列表,您仍然可以调用该方法:

def bar; 'Hello from method' end; public :bar

bar # => 'Hello from method'

bar = 'You will never see this' if false

bar # => nil

bar = 'Hello from local variable'

bar      # => 'Hello from local variable'
bar()    # => 'Hello from method'
self.bar # => 'Hello from method'
于 2012-10-03T12:31:33.527 回答
1

简短的回答是,因为 Matz 是这样定义的。这种行为是我不喜欢 Ruby 的少数几件事之一。它甚至变得更好:

a = b if a
=> nil
a
=> nil

a即使理论上 a = b 语句在任何情况下都不应该被执行,变量也会被初始化为 nil。

于 2012-10-03T10:37:02.993 回答
-3

我认为在您的情况下,这是因为这是预期的:P

1.9.3-p194 :001 > {}.instance_eval{a=1}
 => 1 
1.9.3-p194 :002 > {}.instance_eval{a}     
NameError: undefined local variable or method `a' for {}:Hash
    from (irb):2:in `block in irb_binding'
    from (irb):2:in `instance_eval'
    from (irb):2
    from /Users/rafael/.rvm/rubies/ruby-1.9.3-p194/bin/irb:16:in `<main>'

Instance eval 在实例级别评估代码,因此您声明的每个哈希都是不同的。如果您想返回密钥,则此方法有效

1.9.3-p194 :003 > {}.instance_eval{keys = self.keys
1.9.3-p194 :004?>   keys = keys}
 => [] 
于 2012-10-03T10:40:38.407 回答