ruby 解释为局部变量覆盖方法名?

nfeuvbwi  于 11个月前  发布在  Ruby
关注(0)|答案(4)|浏览(193)

this question一样,当一个没有定义的局部变量在它自己的赋值中使用时,它的值为nil

x = x # => nil

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

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

6za6bjd0

6za6bjd01#

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

foo

字符串
可以 either 表示“调用方法foo on self with no arguments”或“解引用局部变量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


为什么局部变量可以“隐藏”方法而不是其他方法呢?如果方法隐藏了局部变量,那么就没有办法对这些局部变量进行解引用了。但是,如果局部变量隐藏了方法,那么仍然有办法调用这些方法:记住,这种二义性只存在于receiverless argumentless方法调用中,如果你添加了一个显式的receiver或一个显式的参数列表,你仍然可以调用该方法:

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'

tjvv9vkg

tjvv9vkg2#

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

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

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

uhry853o

uhry853o3#

我采用这种模式是为了确保作用域符合我的预期,而不会在没有大量self的情况下污染代码库

def some_class_method
  another_class_method_local = self.another_class_method
  # do stuff with the *_local variable
  self.another_class_method = another_class_method_local
end

字符串

myzjeezk

myzjeezk4#

我认为在你的情况下,这是因为它是什么的预期: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>'

字符串
示例eval在示例级别评估代码,因此您声明的每个散列都是不同的。

1.9.3-p194 :003 > {}.instance_eval{keys = self.keys
1.9.3-p194 :004?>   keys = keys}
 => []

相关问题