【问题标题】:Interpretation as a local variable overrides method name?解释为局部变量会覆盖方法名称吗?
【发布时间】:2012-10-03 10:33:15
【问题描述】:

this question 一样,当在其自己的赋值中使用未定义的局部变量时,它会被评估为nil

x = x # => nil 

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

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

【问题讨论】:

  • 我猜,解析器/解释器中有一些捷径,它检查变量的自赋值。 pastie.org/4901726

标签: ruby local-variables self-reference


【解决方案1】:

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

foo

可能或者表示“在self 上调用方法foo,不带参数”或“取消引用局部变量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'

【讨论】:

    【解决方案2】:

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

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

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

    【讨论】:

    • 好吧,实际上它也没有被执行。如果它真的被执行了,那么a 的值就是b;但事实并非如此。看起来执行的东西实际上是范围的改变——如果aa = b之后被引用,它是指本地的。同样,在这种情况下,Object 表达式在顶层与模块内部的含义不同:module Foo; class Object; end; end,由于范围不同。请参阅 Jörg W Mittag 的回答。
    • Kelvin,你把两个不相关的东西混在一起了。你是对的第一个。 a = b if a 实际上是一个太复杂的例子。奇怪的是例如。 a if a 失败,而 a = a if aa 初始化为零,即使 if 语句的内容无关紧要,因为它永远不会被执行。我知道即使它永远不会被执行,编译器也会按照从左到右的顺序进行处理。所以例如。 if b then b = b end 再次失败。但至于module Foo; class Object end end,这其实不是scope的问题,而是namespace的问题——你简直就是……
    • ... 在模块Foo 上定义一个常量。它可以等效地重写为Foo = Module.new; Foo::Object = Class.new
    • 也许我的例子不是最好的。看看this answer,我提到了陷阱。当内部没有被外部在词法上封闭时,内部模块无法访问外部模块的没有命名空间的常量。有些人可能会认为这很奇怪,就像本地人的自动初始化一样。但我想我的真正意思是,我不太清楚为什么你“不喜欢”当地人的行为——在现实世界中,你是否真的想在a 上获得NameError
    【解决方案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-12-29
      • 1970-01-01
      • 1970-01-01
      • 2015-06-25
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2018-06-21
      • 2023-04-06
      相关资源
      最近更新 更多