【问题标题】:method's local variable with same name as another method方法的局部变量与另一个方法同名
【发布时间】:2013-04-03 14:02:05
【问题描述】:

我试图弄清楚 Ruby 如何处理与self 类中的方法同名的局部变量,结果发现了一个我不明白的行为:

class A
  def val
    10
  end

  def test
    val = val
  end
end

p A.new.test

此代码打印nil。为什么?!

【问题讨论】:

标签: ruby


【解决方案1】:

我认为局部变量一被声明就被声明了。在 ruby​​ 中,查找首先查找局部变量,如果存在则使用它,如果不存在则查找方法。这意味着 val = val 将第一个 val 声明为本地,然后左侧的 val 与之匹配(不确定我应该检查 ruby under microscope 以确定)

如果你尝试

class A
  def val
    10
  end

  def test
    back = []
    x = val
    back << x
    val = x + 1
    back << val
    x = val
    back << x
  end
end

p A.new.test

然后一切都很好,它打印 [10, 11, 11] 这意味着第一个 x = val 调用方法,第二个调用局部变量,大概。

【讨论】:

  • 谢谢。这是一个合理的解释。它还解释了为什么它不会在 val = val 中引发异常。
【解决方案2】:

其他答案解释一切都很好,我只是想将有同样问题的读者指向official documentation

Local Variables and Methods

在 Ruby 中,局部变量名和方法名几乎相同。如果您没有指定这些模棱两可的名称之一,ruby 将假定您希望调用一个方法。一旦您指定了名称,ruby 将假定您希望引用一个局部变量。

局部变量是在解析器遇到赋值时创建的,而不是在赋值发生时创建的:

a = 0 if false # does not assign to a

p local_variables # prints [:a]

p a # prints nil

方法名和局部变量名的相似会导致代码混乱,例如:

def big_calculation
  42 # pretend this takes a long time
end

big_calculation = big_calculation()

现在对big_calculation 的任何引用都被视为局部变量并将被缓存。要调用该方法,请使用self.big_calculation

您可以通过使用如上所示的空参数括号或使用像self 这样的显式接收器来强制方法调用。如果方法的可见性不公开或接收者是文字 self,则使用显式接收者可能会引发 NameError

另一个容易混淆的情况是使用修饰符if

p a if a = 0.zero?

您收到的是NameError,而不是打印“true”,而是“未定义的局部变量或方法‘a’”。由于 ruby​​ 首先解析 if 左侧的 a 并且尚未看到对 a 的分配,因此它假定您希望调用一个方法。然后 Ruby 看到分配给 a 并假设您正在引用本地方法。

混淆来自于表达式的无序执行。首先局部变量被赋值,然后你尝试调用一个不存在的方法。

【讨论】:

    【解决方案3】:

    它是nil,因为val 是您尝试传递的方法,并且您没有在任何地方调用val,它会覆盖自身。你基本上陷入了一个循环。

    每个函数的末尾都有一个隐式返回,它返回最后一个值,如果它没有返回值,Ruby 将返回 nil,但您可能期待一个函数?

    这与 Python 中的情况类似,没有返回的函数总是返回 None

    这可以通过将左手val 变成具有@ 内涵的实例属性来解决。

    我猜你希望它使用val() 方法打印10

    def test
        @val = val()
    end
    
    puts A.new.test
    

    以下也是有效的:

    def test
        val = self.val() #but this will produce the same as above to no real benefit.
    end
    

    关键是您必须调用val 方法才能让val 变量获取值。

    【讨论】:

    • 谢谢。我不会说我被困在一个循环中,因为程序终止了。我期待10,而不是函数。这肯定与括号有关。如果我的test 方法只有一行val,那么它会返回10。但对我来说令人困惑的部分是分配导致val 成为nil
    • 并非如此,这与您试图告诉 Ruby val 是它自己的事实有关。这会导致 Ruby 无法确定您想要什么的循环。
    • 没有循环。程序终止。我很困惑。
    【解决方案4】:

    这应该分成两个问题:

    1. 当您有a = a 时,首先评估什么?如果你做echo 'p a = a' | ruby,你会得到nil,而不是未定义的异常,所以定义是第一位的。

    2. 当局部变量与方法同名时会发生什么?答:方法是不可见的,除非你使用self.

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2011-06-16
      • 1970-01-01
      • 1970-01-01
      • 2014-09-14
      • 1970-01-01
      • 1970-01-01
      • 2016-10-12
      相关资源
      最近更新 更多