=== vs. == в Ruby
В Ruby, в чем разница между == и ===? RDoc говорит
Случайное равенство - для класса Object, фактически так же, как вызов # ==, но обычно переопределяется потомков для обеспечения значимых семантика в операторах case.
Является ли #==
тем же, что и ==
? И вы могли бы привести пример того, когда/как это используется в операторах case?
Ответы
Ответ 1
Оба действительно не имеют ничего общего друг с другом. В частности, #==
- оператор равенства, а #===
не имеет абсолютно никакого отношения к равенству. Лично мне кажется довольно неудачным, что #===
выглядит так похож на #==
, использует знак равенства и часто называется оператором равенства случая, тройным равен оператор оператора или трикваса, когда он действительно не имеет никакого отношения к равенству.
Я называю #===
оператором субобъекта (это лучшее, что я мог придумать, я открыт для предложений, особенно от носителей английского языка).
Лучший способ описать a === b
- "если у меня есть ящик с меткой a
, имеет смысл положить в него b
?
Итак, например, Module#===
проверяет, b.is_a?(a)
. Если у вас есть Integer === 2
, имеет смысл положить 2
в поле с надписью Integer
? Да. Что насчет Integer === 'hello'
? Очевидно, что нет.
Другим примером является Regexp#===
. Он проверяет соответствие. Имеет ли смысл поставить 'hello'
в поле с надписью /el+/
? Да, это так.
Для таких коллекций, как диапазоны, Range#===
определяется как тест на членство: имеет смысл поместить элемент в поле, помеченное коллекцией, если этот элемент находится в коллекции.
Итак, что делает #===
: он проверяет, может ли аргумент быть включен в ресивер.
Что это значит с выражениями case
? Простой:
case foo
when bar
baz
end
совпадает с
if bar === foo
baz
end
Ответ 2
Да, в #==
документы означают "метод экземпляра ==
текущего объекта".
===
используется в операциях case как таковых:
case obj
when x
foo
when y
bar
end
То же, что и
if x === obj
foo
elsif y === obj
bar
end
Некоторые классы, которые определяют свои собственные ===
, являются Range (для того, чтобы действовать как include?
), Class (для того, чтобы действовать как obj.is_a?(klass)
) и Regexp
(чтобы действовать как =~
, за исключением возврата к логическому). Некоторые классы, которые не определяют свои собственные ===
, являются числовыми классами и строкой.
Итак,
case x
when 0
puts "Lots"
when Numeric
puts(100.0 / x)
when /^\d+$/
puts(100.0 / x.to_f)
default
raise ArgumentError, "x is not a number or numeric string"
end
совпадает с
if 0 == x
puts "Lots"
elsif x.is_a? Numeric
puts(100.0 / x)
elsif x =~ /^\d+$/
puts(100.0 / x.to_f)
else
raise ArgumentError, "x is not a number or numeric string"
end
Ответ 3
Забавный факт, ===
также используется для соответствия исключениям в rescue
Вот пример
class Example
def self.===(exception)
puts "Triple equals has been called."
true
end
end
raise rescue Example
# => prints "Triple equals has been called."
# => no exception raised
Это используется для соответствия системным ошибкам.
SystemCallError.===
определено для возврата true, если два имеют одинаковые errno. С помощью этих ошибок системного вызова с таким же номером ошибки, как Errno::EAGAIN
и Errno::EWOULDBLOCK
, можно спасти, перечислив только один из них.