8

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

class A
  def val
    10
  end

  def test
    val = val
  end
end

p A.new.test

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

4

4 回答 4

4

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

如果你试试

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 调用方法,第二个调用局部变量,大概。

于 2013-04-03T14:26:36.050 回答
3

其他答案很好地解释了一切,我只是想向有同样问题的读者指出有关此问题的官方文档

局部变量和方法

在 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. NameError如果方法的可见性不是公共的或接收者是文字,则使用显式接收者可能会引发 a self

另一个常见的令人困惑的情况是使用修饰符时if

p a if a = 0.zero?

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

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

于 2018-05-04T22:04:04.923 回答
0

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

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

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

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

我猜您希望它10使用该val()方法打印?

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变量获取值。

于 2013-04-03T14:11:15.790 回答
0

这应该分为两个问题:

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

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

于 2014-10-12T18:45:42.680 回答