В Ruby почему равенство с nil ("Date.new == nil") возвращает nil? - PullRequest
17 голосов
/ 09 февраля 2010

Когда я писал сегодня несколько rspec, я столкнулся с неожиданным поведением, сравнивая экземпляры Date (и Time) с 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.new === nil
=> false

Есть ли объяснение, почему это происходит или почему это желаемое поведение? == кажется реализованным из Comparable. ==, однако документация по этому вопросу не дает никаких указаний на то, что он когда-либо вернет ноль. Какое дизайнерское решение для этого?

Обновление! Это не так в 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 ]

12 голосов
/ 09 февраля 2010

Я проверил источник и вот что я узнал:

Все операторы сравнения, определенные в Comparable, используют функцию rb_cmpint вместе с <=>. rb_cmpint вызывает исключение, когда один из операндов равен нулю.

Таким образом, операторы Comparable вызывают исключение, если rhs не сравнимо с lhs. То есть 5 < 2 ложно, но 5 < "la" вызывает исключение. Они делают это, чтобы различать случаи, когда < не соответствует действительности, потому что rhs меньше, и случаев, когда это не так, потому что rhs не сопоставимы. Или другими словами: когда x < y является ложным, это означает, что x >= y является истинным. Таким образом, в случаях, когда это не так, возникает исключение.

== вызов исключения будет плохим, потому что == обычно не требует (и не должен) сравнивать его операнды. Однако == использует тот же метод, что и другие операнды, что вызывает исключение. Таким образом, вся функция просто заключена в rb_rescue. И это возвращает nil, если выдается исключение.

Обратите внимание, что это относится только к ruby ​​1.8. Это было исправлено в 1.9, и теперь == никогда не возвращает nil (за исключением, конечно, если вы определите свой собственный ==, который это делает).

7 голосов
/ 09 февраля 2010

Если вы зависите от кода, вы всегда можете использовать .nil? метод, на который реагирует любой объект Ruby.

>> Date.new.nil?
=> false
4 голосов
/ 09 февраля 2010

Класс Date включает метод Comparable#==, но этот метод вызывает метод <=> получателя. В данном случае это Date#<=>, который ожидает другой объект Date. Когда он получает nil, он возвращает nil. Такое поведение, конечно, кажется непоследовательным, и я не знаю причин, стоящих за ним.

0 голосов
/ 09 февраля 2010

Это происходит потому, что вы не можете сравнить вещи, которые не определены. Это желательно, потому что если хотя бы один из ваших операндов не определен, вы не сможете сделать никакого вывода о результате, который отличается от утверждения истины.

Многие языки рассматривают ноль и ложь одинаково, что подозрительно только для удобства. Это конечно не математически правильно.

...