为什么在Ruby中,当`a`未定义时,`a || 1`会抛出错误,但是`a = a || 1`不会呢?

7
当变量 a 未定义时,a || 1 会抛出错误,但是 a = a || 1 不会。这不是有点不一致吗?
irb(main):001:0> a
NameError: undefined local variable or method 'a' for main:Object
        from (irb):1
        from c:/ruby/bin/irb:12:in '<main>'

irb(main):002:0> a || 1
NameError: undefined local variable or method 'a' for main:Object
        from (irb):2
        from c:/ruby/bin/irb:12:in '<main>'

irb(main):003:0> a = a || 1
=> 1

请仅返回翻译文本:Ruby边缘情况。重复问题,已在stackoverflow上发表。 - Andrew Grimm
3个回答

9
a

在这里,你正在评估未定义的a。因此,你会收到一个异常。

a || 1

在这里,你仍然需要评估a以确定布尔表达式的值。就像上面一样,a没有被定义。因此,你会得到一个异常。

a = a || 1

在这里,“a”被定义为未初始化的局部变量。在Ruby中,未初始化的变量求值为“nil”,因此赋值表达式的右侧求值为“nil || 1”,它求值为“1”,因此赋值表达式的返回值是“1”,并且副作用是将“a”初始化为“1”。编辑:似乎有些人对Ruby中变量何时定义和何时初始化存在一些困惑。它们在解析时间被定义,但在运行时被初始化。您可以在这里看到它:
 foo # => NameError: undefined local variable or method `foo' for main:Object

foo未定义。

if false
  foo = 'This will never get executed'
end

此时,foo已经定义,尽管这行代码永远不会被执行。事实上,这行代码永远不会被执行是完全无关紧要的,因为解释器与此无关:局部变量由解析器定义,而解析器显然会看到这行代码。

foo # => nil

没有错误,因为foo已经定义,由于未初始化,它的值为nil


+1,虽然我认为说“它被定义为未初始化的局部变量。”有点令人困惑。当你执行var = expr时,你并不是定义var为未初始化。你是将var定义为expr的值。只是当expr被求值时,var恰好未初始化,这意味着在expr内部对var的任何引用都将求值为nil。 - sepp2k
1
@sepp2k:它们是两个非常独立的步骤。变量定义发生在解析器中,初始化发生在解释器中。根据Ruby实现方式不同,在这两个事件之间可能会有很长时间。例如,在BlueRuby中,Ruby源代码被解析为BRIL(BlueRuby中间语言),然后在安装 Ruby程序时存储在数据库中。直到有人实际运行该程序,可能需要多年时间。在此期间,变量已定义但未初始化。这两个事件甚至可以在不同的计算机上发生。 - Jörg W Mittag

1

当你执行 a || 1 时,你要求它查找未定义的 a 的值。

当你执行 a = a || 1 时,你要求它将 a 分配给 a 的值,这似乎不会产生错误。

因此,尽管很奇怪,我认为它并不矛盾。


这是错误的。a = a || 1 不会被解析为 (a = a) || 1。它被解析为 a = (a || 1),因此“将 a 分配给 a”的结果与此无关,因为 a 永远不会被分配给 a。 - sepp2k
@sepp2k:我完全没有试图暗示那个意思。如果我让你感到困惑,我很抱歉。我相信Jörg已经解释得更清楚了。 - Cetra

0

这是你的意思吗?

if !(defined? a) then
    a = 1
end

使用默认值1来声明变量可能更简单。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接