=== 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, можно спасти, перечислив только один из них.