ruby operator В чем разница между равными ?, eql ?,=== и==?




ruby>> (6)

Я написал простой тест для всего вышеперечисленного.

def eq(a, b)
  puts "#{[a, '==',  b]} : #{a == b}"
  puts "#{[a, '===', b]} : #{a === b}"
  puts "#{[a, '.eql?', b]} : #{a.eql?(b)}"
  puts "#{[a, '.equal?', b]} : #{a.equal?(b)}"
end

eq("all", "all")
eq(:all, :all)
eq(Object.new, Object.new)
eq(3, 3)
eq(1, 1.0)

Я пытаюсь понять разницу между этими четырьмя методами. Я знаю по умолчанию, что == вызывает метод equal? который возвращает true, когда оба операнда относятся к одному и тому же объекту.

=== по умолчанию также вызывает == который вызывает equal? ... ладно, так что если все эти три метода не переопределены, то я думаю, === , == и equal? делать то же самое?

Теперь идет eql? , Что это делает (по умолчанию)? Выполняет ли он вызов хеша / id операнда?

Почему у Руби столько знаков равенства? Они должны отличаться в семантике?


Я люблю ответ jtbandes, но поскольку он довольно длинный, я добавлю свой собственный компактный ответ:

== , === , eql? , equal?
4 компаратора, т.е. 4 способа сравнения 2 объектов в Ruby.
Как и в Ruby, все компараторы (и большинство операторов) фактически являются вызовами методов, вы можете самостоятельно изменить, переписать и определить семантику этих методов сравнения. Однако важно понять, когда внутренние конструкции Ruby используют какой компаратор:

== (сравнение значений)
Ruby использует: == всюду для сравнения значений двух объектов, например. Хэш-значения:

{a: 'z'}  ==  {a: 'Z'}    # => false
{a: 1}    ==  {a: 1.0}    # => true

=== (сравнение случаев)
Ruby использует: === в случае / когда конструкции. Следующие фрагменты кода логически идентичны:

case foo
  when bar;  p 'do something'
end

if bar === foo
  p 'do something'
end

eql? (Сравнение хэш-ключей)
Ruby использует: eql? (в сочетании с хешем метода) для сравнения Хэш-ключей. В большинстве классов: eql? совпадает с: ==.
Знание о: eql? важно, когда вы хотите создавать свои собственные специальные классы:

class Equ
  attr_accessor :val
  alias_method  :initialize, :val=
  def hash()           self.val % 2             end
  def eql?(other)      self.hash == other.hash  end
end

h = {Equ.new(3) => 3,  Equ.new(8) => 8,  Equ.new(15) => 15}    #3 entries, but 2 are :eql?
h.size            # => 2
h[Equ.new(27)]    # => 15

Примечание. Обычно используемый набор Ruby-класса также основан на сравнении хэш-ключа.

equal? (сравнение идентичности объекта)
Ruby использует: equal? чтобы проверить, идентичны ли два объекта. Этот метод (класса BasicObject) не должен быть перезаписан.

obj = obj2 = 'a'
obj.equal? obj2       # => true
obj.equal? obj.dup    # => false

Я хотел бы расширить оператор === .

=== не является оператором равенства!

Не.

Давайте перейдем к этому вопросу.

Вы можете быть знакомы с === как оператор равенства в Javascript и PHP, но это просто не оператор равенства в Ruby и имеет принципиально другую семантику.

Так что делает === ?

=== это оператор сопоставления шаблонов!

  • === соответствует регулярным выражениям
  • === проверяет членство в диапазоне
  • === проверка экземпляра класса
  • === вызовы лямбда-выражения
  • === иногда проверяет равенство, но в основном это не

Итак, как это безумие имеет смысл?

  • Enumerable#grep использует === внутренне
  • case when операторы используют === внутренне
  • Забавный факт, rescue использует === внутренне

Вот почему вы можете использовать регулярные выражения, классы и диапазоны и даже лямбда-выражения в case when оператор.

Некоторые примеры

case value
when /regexp/
  # value matches this regexp
when 4..10
  # value is in range
when MyClass
  # value is an instance of class
when ->(value) { ... }
  # lambda expression returns true
when a, b, c, d
  # value matches one of a through d with `===`
when *array
  # value matches an element in array with `===`
when x
  # values is equal to x unless x is one of the above
end

Все эти примеры работают со pattern === value , а также с помощью метода grep .

arr = ['the', 'quick', 'brown', 'fox', 1, 1, 2, 3, 5, 8, 13]
arr.grep(/[qx]/)                                                                                                                            
# => ["quick", "fox"]
arr.grep(4..10)
# => [5, 8]
arr.grep(String)
# => ["the", "quick", "brown", "fox"]
arr.grep(1)
# => [1, 1]

=== # - равенство

== # --- общее равенство

оба работают аналогично, но «===» даже делают заявления о случаях

"test" == "test"  #=> true
"test" === "test" #=> true

здесь разница

String === "test"   #=> true
String == "test"  #=> false

Ruby предоставляет несколько различных методов обработки равенства:

a.equal? ​​(b) # идентификатор объекта - a и b относятся к одному и тому же объекту

a.eql? (b) # эквивалентность объекта - a и b имеют одинаковое значение

a == b # эквивалентность объекта - a и b имеют одинаковое значение с преобразованием типов.

Продолжайте читать, нажав на ссылку ниже, это дало мне четкое обобщенное понимание.

https://www.relishapp.com/rspec/rspec-expectations/v/2-0/docs/matchers/equality-matchers

Надеюсь, это поможет другим.


Я собираюсь в значительной степени процитировать документацию по объекту здесь, потому что я думаю, что у нее есть отличные объяснения. Я рекомендую вам прочитать его, а также документацию для этих методов, поскольку они переопределены в других классах, таких как String .

Замечание: если вы хотите попробовать это для себя на разных объектах, используйте что-то вроде этого:

class Object
  def all_equals(o)
    ops = [:==, :===, :eql?, :equal?]
    Hash[ops.map(&:to_s).zip(ops.map {|s| send(s, o) })]
  end
end

"a".all_equals "a" # => {"=="=>true, "==="=>true, "eql?"=>true, "equal?"=>false}

== - общее "равенство"

На уровне Object == возвращает true, только если obj и other объекты являются одним и тем же объектом. Как правило, этот метод переопределяется в классах потомков, чтобы обеспечить значение, специфичное для класса.

Это наиболее распространенное сравнение и, следовательно, самое фундаментальное место, где вы (как автор класса) решаете, являются ли два объекта «равными» или нет.

=== - равенство случая

Для класса Object, фактически то же самое, что и вызов #== , но обычно переопределяется потомками для обеспечения значимой семантики в операторах case.

Это невероятно полезно. Примеры вещей, которые имеют интересные === реализации:

  • Спектр
  • Regex
  • Proc (в Ruby 1.9)

Таким образом, вы можете делать такие вещи, как:

case some_object
when /a regex/
  # The regex matches
when 2..4
  # some_object is in the range 2..4
when lambda {|x| some_crazy_custom_predicate }
  # the lambda returned true
end

См. Мой ответ здесь для аккуратного примера того, как case + Regex может сделать код намного чище. И, конечно, предоставляя свою собственную реализацию === , вы можете получить пользовательскую семантику case .

eql? - Hash

eql? метод возвращает true, если obj и other относятся к одному и тому же хэш-ключу. Это используется Hash для проверки членов для равенства. Для объектов класса Object , eql? является синонимом == . Подклассы обычно продолжают эту традицию посредством aliasing eql? к их переопределенному == методу, но есть исключения. Например, eql? типы выполняют преобразование типов через == , но не через eql? , так:

1 == 1.0     #=> true
1.eql? 1.0   #=> false

Таким образом, вы можете переопределить это для своих собственных целей или вы можете переопределить == и использовать alias :eql? :== alias :eql? :== так что оба метода ведут себя одинаково.

equal? - сравнение идентичности

В отличие от == , equal? метод никогда не должен быть переопределен подклассами: он используется для определения идентичности объекта (то есть a.equal?(b) iff a - это тот же объект, что и b ).

Это эффективное сравнение указателей.







equality