Ruby中的Nil和boolean

时间:2016-01-29 18:04:14

标签: ruby-on-rails ruby

有人可以解释这个推理吗?花了30分钟试图弄清楚为什么我的布尔方法返回nil并在Ruby中发现它:

2.2.1 :001 > nil && true
 => nil
2.2.1 :002 > nil && false
 => nil

由于nil是一个假值,我原以为nil && true的输出是假的。此外,它似乎违背了条件运算符应该返回一个布尔值的想法。

这背后的理由是什么?

布尔运算符不可交换是有道理的:

nil && false != false && nil

对于其他人看到这一点,我的问题是在rails中我有一个声明:

def some_method?
  object.attr && object.attr > something
end

但是当object.attr为零时,函数将为零。在大多数情况下这很好,但是当将布尔方法链接在一起时,并没有那么多。我只是改为:

def some_method?
  object.attr.present? && object.attr > something
end

我可以用香草Ruby做同样的事情:

def some_method?
  !!object.attr && object.attr > something
end

4 个答案:

答案 0 :(得分:8)

该陈述按顺序通过条件,在获得假结果时停止并返回上次执行评估的值。

与以{f}值停止的&&相反,||将停止在真值处。

答案 1 :(得分:4)

如果第一个操作数是假的(nilfalse),则&&的第二个操作数将不会被评估,因为falsy && whatever将始终为falsy }

答案 2 :(得分:1)

问题

您列出了两个相关问题:

  1. 您对返回nil而不是false的布尔表达式感到困惑。
  2. 当其中一个方法可能评估为nilfalse或其他一些不响应链中下一个方法的对象时,您会遇到令人惊讶的行为。
  3. Nil和False

    在您的示例中,您链接的方法有时可能会在nil上调用。在Ruby中,nil是一个假值(例如它不是“真”),但 实际上也是假的。考虑:

    nil == false
    #=> false
    

    他们甚至不是来自同一个班级。

    nil.class
    #=> NilClass
    
    false.class
    #=> FalseClass
    

    通常最好将nil视为一个特殊值,意思是“未定义”或“不适用”。将nil视为与null的特殊数据库值相似也可能有用。 Nil(如null)不是true,false或empty,理解这将有助于避免许多令人困惑的异常。

    短路评估

    当您评估nil && false时,您实际上正在评估两个单独的表达式:

    (nil) && (false)
    

    表达式nil不正确,因此短路并且永远不会评估表达式false。因此,表达式nil && false正确返回nil而不是false。这对某些人来说是一个惊喜,但对于有经验的Rubyists来说,这是预期的行为。

    解决方案

    除了短路表达式或后表达式条件,例如:

    # Send :to_s unless condition evaluates to false or nil.
    object.attr.to_s if object.attr
    

    你应该考虑Rails Object#try方法,或者Ruby 2.3.0安全导航操作符。

    使用Object#try或安全导航操作员

    对象#试

    如果您正在使用Rails,Object#try方法是避免在nil或其他没有#respond_to?链式方法的对象上调用方法的常用方法。例如:

    # Invoke #to_s without raising an exception. May return nil. 
    object.attr.try(:to_s)
    

    这与:

    类似,但不完全相同
    object.attr && object.attr.to_s
    

    Ruby 2.3.0 introduces the safe navigation operator&),它为Ruby的核心带来了类似的功能。

    &安全导航操作员

    Ruby 2.3.0引入了一个新的safe navigation operator。这似乎是一个实际的操作员,而不是一种方法。只要左侧的表达式是真实的,此运算符就可以链接方法。例如:

    object.attr&.to_s
    

    因为它链式方法,并且因为像>这样的比较运算符实际上是方法而不是解析器标记,所以现在可以执行以下操作:

    1&.> 0
    #=> true
    
    1&.> 2
    #=> false
    

    你是否发现object.attr&. > something比其他结构更优雅,可读或有用是一个观点问题,但在许多情况下它肯定是一种选择。 YMMV。

答案 3 :(得分:0)

了解变量/值"真实性":

首先,看看Ruby的真假想法:

if true
  'is true' # => "is true"
else
  'is false' # => 
end

if false
  'is true' # => 
else
  'is false' # => "is false"
end

if / else的较短形式使用三元:

true ? true : false # => true
false ? true : false # => false

继续:

nil ? true : false # => false
0 ? true : false # => true
1 ? true : false # => true

请注意,nil用作false等条件行为时,0为真。 (Perl将false0视为测试中的错误值,这在转移到Ruby时是一个令人困惑的问题。)1是真的,基本上,在Ruby中,只有nil并且false是假的,其他一切都是真的。

!执行" not",扭转价值的真实性:

!true ? true : false # => false
!false ? true : false # => true
!nil ? true : false # => true
!0 ? true : false # => false
!1 ? true : false # => false

!!" nots"值为两次,我们将其用作快速转换非真/假值的方法:

!!true ? true : false # => true
!!false ? true : false # => false
!!nil ? true : false # => false
!!0 ? true : false # => true
!!1 ? true : false # => true

了解这些在Ruby中的工作方式可以让您更容易理解其他人的代码,因为您经常会看到!!!

所有这些都可以追溯到使用&&||。使用||("或"),只有一方必须true才能返回true结果:

true || true # => true
false || false # => false
true || false # => true
false || true # => true

使用&&("和")双方必须true才能获得true结果:

true && true # => true
false && false # => false
true && false # => false
false && true # => false

不要混淆||or,以及&&and!!or都做类似的事情,&&and,但是他们的测试会在表达式的不同时间进行。这个优先顺序非常重要,但却是一个不同的问题。您可以通过简单的搜索找到大量有关该信息的信息。