“nil或者零”的最佳Ruby惯用语

85

我正在寻找一种简洁的方法来检查一个值是否为nil或零。当前我在做如下操作:

if (!val || val == 0)
  # Is nil or zero
end

但这看起来非常笨拙。


1
如果val等于“false”,应该发生什么? - Andrew Grimm
22个回答

72

对象具有 nil? 方法

if val.nil? || val == 0
  [do something]
end

或者,只需一条指令:

[do something] if val.nil? || val == 0

5
注意使用 "or" 和 "and",它们与 || 和 && 相比具有不同且非常低的优先级。请参阅 http://blog.jayfields.com/2007/08/ruby-operator-precedence-of-and-which.html 了解更多信息。 - Mike Woodhouse
我同意某些在问题中提到的“气味”,然而对我来说这更符合 Ruby 的方式。 - Jonke
6
在这里,“or”的优先级不需要使用任何括号,对于有经验的Ruby程序员来说,这将完全自然地被理解。唯一会受到“||”和“or”之间区别影响的人是那些从未编写过Perl代码的人 :) - ephemient
1
你只需要在赋值时关注 || 和 OR 的优先级,而不是在布尔检查时。然而,这个词的低优先级对于错误检查非常有用。 - Robert K
3
我认为 val && val == 0 更好。 - Nakilon
有时候 nil.to_i == 0,因此我建议使用:val.to_i.zero? - Augustin Riedinger

43
从Ruby 2.3.0版本开始,您可以将安全导航运算符(&.)与Numeric#nonzero?结合使用。&.返回nil,如果实例为nil,而nonzero?则返回此数字是否为0
unless val&.nonzero?
  # Is nil or zero
end

或者后置表达式:

do_something unless val&.nonzero?

1
这是一个了不起的新的现代答案。此外,关于为什么在这里使用它的一篇很棒的文章:Ruby中的安全导航运算符(&.) - Michael Gaskill
1
非常简洁,但如果没有注释,我需要一点时间才能理解 unless val&.nonzero? 的意思是“如果 val 为 nil 或零”。 - Stefan
1
@Stefan,同意。我自己不会在生产代码中使用它。我包含它是为了完整性。相反的写法也很容易读懂 - do_something if val&.nonzero?(即如果val不是nil且不为零)。 - ndnenkov
谢谢。截至 Ruby 2.7.7,仍然有效。我能够使用您的解决方案来修复我的三元运算语句 my_value&.nonzero? ? do_something : do_something_else - B. Bulpett

38
如果你真的喜欢在方法名后面加问号:

if val.nil? || val.zero?
  # 做一些事情
end

你的解决方案很好,其他几个解决方案也不错。

如果你不小心的话,Ruby 可以让你寻找一种漂亮的方式来完成所有事情。


30

首先,我认为这是检查特定条件的最简洁方式。

其次,对我来说,这是一种代码异味,表明您的设计可能存在潜在缺陷。通常,nil和零不应该表示相同的含义。如果可能的话,在进入此代码之前,应尝试消除val为nil的可能性,例如通过在方法开始处检查或其他机制。

您可能有一个完全合法的原因来做这件事,在这种情况下,我认为您的代码是好的,但我至少会考虑尝试消除nil检查。


4
非常正确关于代码异味!+1 可以考虑了解NullObject模式 http://en.wikipedia.org/wiki/Null_Object_pattern - abyx

10

nil.to_i返回零,因此我经常这样做:

val.to_i.zero?

然而,如果val是一个不能响应#to_i方法的对象,就会收到一个异常。


参考Scott的回答,以及@AndrewGrimm对此的评论:“'5'.to_i == 5”,但你大致上是正确的。聪明,但实际上并不起作用。 - pjvleeuwen

9
您可以使用Object.nil?来专门测试nil(而不会混淆false和nil)。您还可以将一个方法打补丁到Object中。
class Object
   def nil_or_zero?
     return (self.nil? or self == 0)
   end
end

my_object = MyClass.new
my_object.nil_or_zero?
==> false

这并不被推荐,因为对Object的更改很难让同事进行追踪,并且可能会使你的代码变得不可预测。

我会修改 Numeric 类而不是 Object。 - epochwolf
6
如果你把这个放在 Numeric 类上,那么 nil? 将总是返回 false。nil? 只在 NilClass 上返回 true。 - Ryan McGeary
你可以在 ObjectNilClassNumeric 中实现该方法。Object 返回 falseNilClass 返回 true,而 Numeric 则返回 zero? - Stefan

4

我的解决方案也使用了改进(Refinements)技术,但是没有条件语句。

module Nothingness
  refine Numeric do
    alias_method :nothing?, :zero?
  end

  refine NilClass do
    alias_method :nothing?, :nil?
  end
end

using Nothingness

if val.nothing?
  # Do something
end

4

我认为你的代码是不正确的,实际上它会测试三个值:nilfalse和零。这是因为在Ruby中,!val表达式对于所有的false值都为true,这些false值包括nilfalse

目前我能想到的最好的方法是:

if val == nil || val == 0
  # do stuff
end

当然这并不是非常聪明,但是(非常)清楚。

你可以假设所涉及的变量是数字类型。如果你甚至不知道它是布尔型还是数字型,那么代码中存在更大的问题。 - Mike Deck

3
最短和最好的方法应该是:
if val&.>(0)
  # do something
end

对于val&.>(0),当val是nil时,它返回nil,因为>本质上也是一个方法,在ruby中nil等同于false。当val == 0时,它返回false。


3
最短的是相对于什么?最好的方式比什么更好? - Sebastián Palma

3

Short and clear

[0, nil].include?(val)


3
可以反过来写成:val.nil? || val.zero? - mahemoff
1
@mahemoff 那绝对是最恰当的说法! - intmarinoreturn0
@mahemoff 最好使用 include?,因为它比 in 更快。 - Lakhani Aliraza
不错的观点,但我刚刚在现代Ruby上运行了它,在单次调用中in?实际上更快,并且对于1000次调用,它们非常相似。两者都非常快速,足以让少数应用程序受到影响。https://gist.github.com/mahemoff/7232648e1ccb9d23e0f4670913400bd8 - mahemoff

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