operator - ruby>>




В чем разница между равными ?, eql ?,=== и==? (5)

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

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

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

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


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

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

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

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

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

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

Операторы равенства: == и! =

Оператор ==, также известный как равенство или двойное равное, вернет true, если оба объекта равны и ложны, если они не являются.

"koan" == "koan" # Output: => true

Оператор! =, Неравенство AKA или bang-тильда, противоположно ==. Он вернет true, если оба объекта не равны и false, если они равны.

"koan" != "discursive thought" # Output: => true

Обратите внимание, что два массива с одинаковыми элементами в другом порядке не равны, прописные и строчные версии одной и той же буквы не равны и так далее.

При сравнении чисел разных типов (например, integer и float), если их числовое значение одно и то же, == вернет true.

2 == 2.0 # Output: => true

равны?

В отличие от оператора ==, который проверяет, равны ли оба операнда, метод равенства проверяет, относятся ли оба операнда к одному и тому же объекту. Это самая строгая форма равенства в Ruby.

Пример: a = "zen" b = "zen"

a.object_id  # Output: => 20139460
b.object_id  # Output :=> 19972120

a.equal? b  # Output: => false

В приведенном выше примере мы имеем две строки с одинаковым значением. Однако они представляют собой два разных объекта с разными идентификаторами объектов. Следовательно, равный? метод вернет false.

Давайте попробуем еще раз, только на этот раз b будет ссылкой на a. Обратите внимание, что идентификатор объекта одинаковый для обеих переменных, поскольку они указывают на один и тот же объект.

a = "zen"
b = a

a.object_id  # Output: => 18637360
b.object_id  # Output: => 18637360

a.equal? b  # Output: => true

EQL?

В классе Hash, eql? метод используется для проверки ключей для равенства. Для объяснения этого требуется некоторый фон. В общем контексте вычислений хеш-функция принимает строку (или файл) любого размера и генерирует строку или целое число фиксированного размера, называемое hashcode, обычно называемое только хэшем. Некоторыми обычно используемыми типами хэш-кодов являются MD5, SHA-1 и CRC. Они используются в алгоритмах шифрования, индексировании базы данных, проверке целостности файлов и т. Д. Некоторые языки программирования, такие как Ruby, предоставляют тип коллекции, называемый хеш-таблицей. Хэш-таблицы представляют собой словарные коллекции, которые хранят данные в парах, состоящие из уникальных ключей и их соответствующих значений. Под капотом эти ключи сохраняются как хэш-коды. Хэш-таблицы обычно называются просто хэшами. Обратите внимание, как слово hash может ссылаться на хэш-код или на хеш-таблицу. В контексте программирования Ruby слово хэш почти всегда ссылается на словарь-подобную коллекцию.

Ruby предоставляет встроенный метод, называемый хэш для генерации хэш-кодов. В приведенном ниже примере он берет строку и возвращает хэш-код. Обратите внимание, что строки с одинаковым значением всегда имеют один и тот же хэш-код, даже если они представляют собой разные объекты (с разными идентификаторами объектов).

"meditation".hash  # Output: => 1396080688894079547
"meditation".hash  # Output: => 1396080688894079547
"meditation".hash  # Output: => 1396080688894079547

Хэш-метод реализован в модуле Kernel, включенном в класс Object, который является корнем по умолчанию для всех объектов Ruby. Некоторые классы, такие как Symbol и Integer, используют реализацию по умолчанию, другие, такие как String и Hash, предоставляют свои собственные реализации.

Symbol.instance_method(:hash).owner  # Output: => Kernel
Integer.instance_method(:hash).owner # Output: => Kernel

String.instance_method(:hash).owner  # Output: => String
Hash.instance_method(:hash).owner  # Output: => Hash

В Ruby, когда мы храним что-то в хеше (коллекции), объект, предоставленный в виде ключа (например, строка или символ), преобразуется в хэш-код и сохраняется в нем. Позже, при извлечении элемента из хэша (коллекции), мы предоставляем объект в качестве ключа, который преобразуется в хэш-код и сравнивается с существующими ключами. Если есть совпадение, возвращается значение соответствующего элемента. Сравнение производится с использованием уравнения метод под капотом.

"zen".eql? "zen"    # Output: => true
# is the same as
"zen".hash == "zen".hash # Output: => true

В большинстве случаев уравнение метод ведет себя аналогично методу ==. Однако есть несколько исключений. Например, eql? не выполняет неявное преобразование типа при сравнении целого с float.

2 == 2.0    # Output: => true
2.eql? 2.0    # Output: => false
2.hash == 2.0.hash  # Output: => false

Оператор равенства случаев: ===

Многие из встроенных классов Ruby, таких как String, Range и Regexp, предоставляют свои собственные реализации оператора ===, также известные как case-equal, triple equals или threequals. Поскольку он реализован по-разному в каждом классе, он будет вести себя по-разному в зависимости от типа объекта, на который он был вызван. Как правило, он возвращает true, если объект справа «принадлежит» или «является членом» объекта слева. Например, его можно использовать для проверки, является ли объект экземпляром класса (или одного из его подклассов).

String === "zen"  # Output: => true
Range === (1..2)   # Output: => true
Array === [1,2,3]   # Output: => true
Integer === 2   # Output: => true

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

2.is_a? Integer   # Output: => true
2.kind_of? Integer  # Output: => true
2.instance_of? Integer # Output: => false

Обратите внимание, что последний пример возвращает false, поскольку целые числа, такие как 2, являются экземплярами класса Fixnum, который является подклассом класса Integer. ===, is_a? и instance_of? методы возвращают true, если объект является экземпляром данного класса или любых подклассов. Метод instance_of более строгий и возвращает true, если объект является экземпляром этого точного класса, а не подкласса.

Is_a? и kind_of? методы реализуются в модуле Kernel, который смешивается классом Object. Оба являются алиасами того же метода. Давайте проверим:

Kernel.instance_method (: kind_of?) == Kernel.instance_method (: is_a?) # Вывод: => true

Внедрение диапазона ===

Когда оператор === вызывается в объекте диапазона, он возвращает значение true, если значение справа находится в пределах диапазона слева.

(1..4) === 3  # Output: => true
(1..4) === 2.345 # Output: => true
(1..4) === 6  # Output: => false

("a".."d") === "c" # Output: => true
("a".."d") === "e" # Output: => false

Помните, что оператор === вызывает метод === для левого объекта. Итак, (1..4) === 3 эквивалентно (1..4). === 3. Другими словами, класс левого операнда определит, какая реализация метода === будет так что позиции операнда не взаимозаменяемы.

Regexp Реализация ===

Возвращает true, если строка справа соответствует регулярному выражению слева. / zen / === "Практика дзадзэн сегодня" # Вывод: => истина # такая же, как "практика дзадзэн сегодня" = ~ / zen /

Неявное использование оператора === для операторов case / when

Этот оператор также используется под капотом на операторах case / when. Это его наиболее распространенное использование.

minutes = 15

case minutes
  when 10..20
    puts "match"
  else
    puts "no match"
end

# Output: match

В приведенном выше примере, если Ruby неявно использовал оператор двойного равенства (==), диапазон 10..20 не будет считаться равным целому числу, например 15. Они совпадают, поскольку оператор с тройным равным (===) равен неявно используется во всех случаях case / when. Код в приведенном выше примере эквивалентен:

if (10..20) === minutes
  puts "match"
else
  puts "no match"
end

Операторы сопоставления шаблонов: = ~ и! ~

Операторы = ~ (equal-tilde) и! ~ (Bang-тильда) используются для сопоставления строк и символов с шаблонами регулярных выражений.

Реализация метода = ~ в классах String и Symbol предполагает регулярное выражение (экземпляр класса Regexp) в качестве аргумента.

"practice zazen" =~ /zen/   # Output: => 11
"practice zazen" =~ /discursive thought/ # Output: => nil

:zazen =~ /zen/    # Output: => 2
:zazen =~ /discursive thought/  # Output: => nil

Реализация в классе Regexp ожидает строку или символ в качестве аргумента.

/zen/ =~ "practice zazen"  # Output: => 11
/zen/ =~ "discursive thought" # Output: => nil

Во всех реализациях, когда строка или символ соответствуют шаблону Regexp, он возвращает целое число, которое является позицией (индексом) совпадения. Если нет совпадения, он возвращает nil. Помните, что в Ruby любое целочисленное значение является «правдивым», а nil - «ложным», поэтому оператор = ~ может использоваться в операторах if и тройных операторах.

puts "yes" if "zazen" =~ /zen/ # Output: => yes
"zazen" =~ /zen/?"yes":"no" # Output: => yes

Операторы сопоставления шаблонов также полезны для написания более коротких операторов if. Пример:

if meditation_type == "zazen" || meditation_type == "shikantaza" || meditation_type == "kinhin"
  true
end
Can be rewritten as:
if meditation_type =~ /^(zazen|shikantaza|kinhin)$/
  true
end

Оператор! ~ Противоположный = ~, он возвращает true, если нет совпадения и false, если есть совпадение.

Дополнительная информация доступна в этом блоге .


Я люблю ответ 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

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

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)

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

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

Не.

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

Вы можете быть знакомы с === как оператор равенства в 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]




equality