17

今天在编写一些 rspec 时,我遇到了一些将日期(和时间)实例与 nil 进行比较的意外行为。这是一个使用原始 ruby​​ 的示例(没有 Rails 或其他库):

user@MacBook-Work ~ $ ruby -v
ruby 1.8.7 (2008-08-11 patchlevel 72) [universal-darwin10.0]
user@MacBook-Work ~ $ irb
>> 1 == nil
=> false
>> "string" == nil
=> false
>> :sym == nil
=> false
>> false == nil
=> false
>> [] == nil
=> false
>> {} == nil
=> false
>> Proc.new {} == nil
=> false

到目前为止,一切都很好,对吧?

>> Date.new == nil
=> nil
>> Time.new == nil
=> nil

Date 确实实现了自己的 ===,效果很好:

>> Date.new === nil
=> false

是否有任何解释为什么会发生这种情况或为什么这是所需的行为?== 似乎是从 Comparable.== 实现的,但是关于它的文档没有给出任何迹象表明它会返回 nil。对此的设计决定是什么?

更新!在 1.9.2 中情况并非如此:

$ irb
ruby-1.9.2-p136 :001 > require 'date'
 => true 
ruby-1.9.2-p136 :002 > Date.new == nil
 => false 
ruby-1.9.2-p136 :003 > Time.new == nil
 => false 
4

4 回答 4

12

我检查了来源,这是我发现的:

Comparable 定义的比较运算符都使用该函数rb_cmpint<=>. rb_cmpint当其中一个操作数为 nil 时引发异常。

因此,如果 rhs 与 lhs 不可比较,则 Comparable 的运算符会引发异常。即为5 < 2假,但5 < "la"引发异常。他们这样做是为了区分<由于 rhs 较小而不正确的情况和由于 rhs 不可比较而不正确的情况。或者换句话说:什么时候x < y是假的,这意味着它x >= y是真的。因此,在情况并非如此的情况下,它会引发异常。

==引发异常会很糟糕,因为==通常不(也不应该)要求其操作数具有可比性。但是==使用与其他操作数相同的方法,这确实会引发异常。所以整个函数只是简单地包装在一个rb_rescue. nil如果抛出异常,则返回。

请注意,这只适用于 ruby​​ 1.8。这已在 1.9 中修复,现在==永远不会返回nil(当然,除非您定义自己的==那样)。

于 2010-02-08T23:38:03.767 回答
7

如果您依赖此代码,您可以随时使用 .nil? 任何 Ruby 对象都响应的方法。

>> Date.new.nil?
=> false
于 2010-02-09T01:18:18.423 回答
4

Date 类包含该Comparable#==方法,但该方法调用<=>接收方的方法。在这种情况下Date#<=>,它需要另一个 Date 对象。当它收到nil它时返回nil。这种行为肯定看起来不一致,我不知道其背后的原因。

于 2010-02-08T23:34:20.890 回答
0

发生这种情况是因为您无法比较未定义的事物。这是可取的,因为如果您的至少一个操作数未定义,那么您就无法对结果得出任何结论,这与断言真理不同。

许多语言将 nil 和 false 视为相同,这可能纯粹是为了方便。这在数学上肯定是不正确的。

于 2010-02-09T01:43:53.743 回答