Jaka jest różnica między equal?, eql?, ===, i==?

Próbuję zrozumieć różnicę między tymi czterema metodami. Wiem domyślnie, że == wywołuje metodę equal?, która zwraca true, gdy oba operandy odnoszą się do dokładnie tego samego obiektu.

=== Domyślnie również wywołuje == który wywołuje equal?... OK, więc jeśli wszystkie te trzy metody nie są przesadne, to chyba ===, == i zrobić dokładnie to samo?

Teraz nadchodzi eql?. Do czego to służy (domyślnie)? Czy dzwoni do hash/id operanda?

Dlaczego Ruby ma tak wiele znaków równości? Czy mają się różnić semantyką?

Author: Freedom_Ben, 2011-08-23

7 answers

Zamierzam mocno zacytowaćdokumentację obiektu tutaj, ponieważ myślę, że ma kilka świetnych wyjaśnień. Zachęcam do zapoznania się z nim, a także z dokumentacją tych metod, ponieważ są one nadpisywane w innych klasach, takich jak String.

uwaga: jeśli chcesz wypróbować je dla siebie na różnych obiektach, użyj czegoś takiego:

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}

== - rodzajnik "równość"

Na poziomie obiektu, == zwraca true tylko wtedy, gdy obj i other są tym samym obiektem. Zazwyczaj metoda ta jest nadpisywana w klasach potomnych, aby zapewnić specyficzne dla klasy znaczenie.

Jest to najczęstsze porównanie, a więc najbardziej fundamentalne miejsce, w którym Ty (jako autor klasy) decydujesz, czy dwa obiekty są "równe", czy nie.

=== - równość Przypadku

Dla obiektu klasy, efektywnie takiego samego jak wywołanie #==, ale zazwyczaj zastępowanego przez potomków, aby zapewnić sensowne semantyka w wypowiedziach case.

To jest niezwykle przydatne. Przykłady rzeczy, które mają ciekawe === implementacje:
  • zakres
  • Regex
  • Proc (w Ruby 1.9)

Więc możesz robić rzeczy takie jak:

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

Zobacz moja odpowiedź tutaj na schludny przykład jak case+Regex może sprawić, że kod będzie dużo czystszy. I oczywiście, dostarczając własną implementację ===, możesz uzyskać niestandardową semantykę case.

eql? - Hash równość

Metoda eql? zwraca true, Jeśli obj i other odnoszą się do tego samego klucza hashowego. Jest to używane przez Hash do testowania członków pod kątem równości. dla obiektów klasy Object, eql? jest synonimem ==. podklasy Zwykle kontynuują tę tradycję poprzez aliasing eql? do swojej nadpisanej metody ==, ale są wyjątki. Numeric typy, na przykład, wykonują konwersję typu w poprzek ==, ale nie w poprzek eql?, więc:

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

Więc jesteś wolny zastąp to dla własnych zastosowań, lub możesz zastąpić == i użyć alias :eql? :==, aby obie metody zachowywały się tak samo.

equal? - porównanie tożsamości

W przeciwieństwie do ==, metoda equal? nigdy nie powinna być nadpisywana przez podklasy: jest używana do określania tożsamości obiektu (tzn. a.equal?(b) iff a jest tym samym obiektem co b).

Jest to efektywne porównanie wskaźników.

 795
Author: jtbandes,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2020-06-20 09:12:55

Uwielbiam jtbandes answer, ale ponieważ jest dość długa, dodam własną zwartą odpowiedź:

==, ===, eql?, equal?
są 4 komparatory, tj. 4 sposoby porównania 2 obiektów w Ruby.
Ponieważ w Rubim wszystkie komparatory (i większość operatorów) są w rzeczywistości wywołaniami metod, możesz zmienić, nadpisać i zdefiniować semantykę tych porównujących metod. Jednak ważne jest, aby zrozumieć, gdy wewnętrzne konstrukcje języka Ruby używają, które

== (porównanie wartości)
Ruby używa : = = everywhere do porównania wartości z 2 obiektów, np. Hash-wartości:

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

=== (Porównanie przypadków)
Ruby używa : = = = in case / when constructs. Następujące fragmenty kodu są logicznie identyczne:

case foo
  when bar;  p 'do something'
end

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

eql? (Porównanie Hash-key)
Ruby używa: eql? (w połączeniu z metodą hash), aby porównać Klucze Hash. W większości klas: eql? jest identyczny z :==.
Wiedza na temat: eql? jest ważne tylko wtedy, gdy chcesz stworzyć własne specjalne klasy:

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

Uwaga: powszechnie używany zestaw klas Ruby również opiera się na porównaniu Hash-key.

equal? (porównanie tożsamości obiektu)
Ruby używa: equal? aby sprawdzić, czy dwa obiekty są identyczne. Metoda ta (klasy BasicObject) nie powinna być nadpisywana.

obj = obj2 = 'a'
obj.equal? obj2       # => true
obj.equal? obj.dup    # => false
 48
Author: Andreas Rayo Kniep,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2015-04-06 00:42:27

Operatory równości: = = i !=

Operator==, znany również jako equality lub double equal, zwróci true, jeśli oba obiekty są równe, a false, jeśli nie są.

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

The != operator, znany również jako nierówność, jest przeciwieństwem ==. Zwróci true, jeśli oba obiekty nie są równe i false, jeśli są równe.

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

Zauważ, że dwie tablice z tymi samymi elementami w innej kolejności nie są sobie równe, duże i małe wersje tej samej litery są nie równe i tak dalej.

Podczas porównywania liczb różnych typów (np. integer I float), jeśli ich wartość liczbowa jest taka sama, = = zwróci true.

2 == 2.0 # Output: => true
/ Align = "left" /

W przeciwieństwie do operatora==, który sprawdza, czy oba operandy są równe, metoda equal sprawdza, czy oba operandy odnoszą się do tego samego obiektu. Jest to najsurowsza forma równości w Ruby.

Przykład: a = " zen" b = " zen "

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

a.equal? b  # Output: => false

W powyższym przykładzie mamy dwa ciągi z ta sama wartość. Są to jednak dwa różne obiekty o różnych identyfikatorach. Stąd równi? metoda zwróci false.

Spróbujmy jeszcze raz, tylko tym razem b będzie odniesieniem do a.zauważ, że ID obiektu jest taki sam dla obu zmiennych, ponieważ wskazują one na ten sam obiekt.

a = "zen"
b = a

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

a.equal? b  # Output: => true

Eql?

W klasie Hash, eql? metoda służy do testowania kluczy równości. Niektóre tło jest wymagane, aby to wyjaśnić. W ogólnym kontekście informatyki, hash funkcja pobiera łańcuch znaków (lub plik) o dowolnym rozmiarze i generuje łańcuch lub liczbę całkowitą o stałym rozmiarze o nazwie hashcode, powszechnie nazywany tylko hash. Niektóre powszechnie używane typy hashcode to MD5, SHA-1 i CRC. Są one wykorzystywane w algorytmach szyfrujących, indeksowaniu baz danych, sprawdzaniu integralności plików itp. Niektóre języki programowania, takie jak Ruby, zapewniają typ kolekcji zwany tabelą hashową. Tabele Hash to słownikowe kolekcje, które przechowują dane w parach, składające się z unikalnych kluczy i ich odpowiednie wartości. Pod maską te klucze są przechowywane jako hashcodes. Tabele Hash są powszechnie nazywane po prostu hashami. Zauważ, jak słowo hash może odnosić się do kodu hash lub do tabeli hash. W kontekście programowania w języku Ruby, słowo hash prawie zawsze odnosi się do kolekcji podobnej do słownika.

Ruby dostarcza wbudowaną metodę o nazwie hash do generowania hashcodów. W poniższym przykładzie pobiera łańcuch znaków i zwraca hashcode. Zauważ, że ciągi o tej samej wartości zawsze mają ten sam hashcode, mimo że są odrębnymi obiektami (z różnymi identyfikatorami obiektów).

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

Metoda hash jest zaimplementowana w module Jądra, należącym do klasy Object, która jest domyślnym rootem wszystkich obiektów Ruby. Niektóre klasy, takie jak Symbol i Integer używają domyślnej implementacji, inne, takie jak String i Hash, dostarczają własnych implementacji.

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

W Rubim, gdy przechowujemy coś w hash (kolekcji), obiekt dostarczany jako klucz (np. string lub symbol) jest konwertowany i przechowywany jako hashcode. Później, podczas pobierania elementu z hash (kolekcji), dostarczamy obiekt jako klucz, który jest konwertowany na hashcode i porównywany z istniejącymi kluczami. W przypadku dopasowania zwracana jest wartość odpowiadającej pozycji. Porównanie odbywa się za pomocą eql? metoda pod maską.

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

W większości przypadków, eql? metoda zachowuje się podobnie do metody==. Istnieje jednak kilka wyjątków. Na przykład, eql? nie wykonuje konwersję typu implicit podczas porównywania liczby całkowitej z zmiennoprzecinkową.

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

Operator równości przypadków: = = =

Wiele wbudowanych klas Rubiego, takich jak String, Range i Regexp, dostarcza własnych implementacji operatora===, znanego również jako case-equality, triple equals lub threequals. Ponieważ jest zaimplementowany inaczej w każdej klasie, będzie się zachowywał inaczej w zależności od typu obiektu, do którego został wywołany. Ogólnie zwraca true, jeśli obiekt po prawej "należy do" lub "jest członkiem" obiektu po lewej. Na przykład, może być użyty do sprawdzenia, czy obiekt jest instancją klasy (lub jednej z jej podklas).

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

Ten sam wynik można osiągnąć za pomocą innych metod, które prawdopodobnie najlepiej nadają się do tego zadania. Zwykle lepiej jest pisać kod, który jest łatwy do odczytania, ponieważ jest tak wyraźny, jak to możliwe, bez poświęcania wydajności i zwięzłości.

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

Zauważ, że ostatni przykład zwrócił false, ponieważ liczby całkowite takie jak 2 są instancjami klasy Fixnum, która jest podklasą klasy Integer. = = = = , Is_a? a instance_of? metody zwracają true, jeśli obiekt jest instancją danej klasy lub dowolnej podklasy. Metoda instance_of jest bardziej rygorystyczna i zwraca true tylko wtedy, gdy obiekt jest instancją tej klasy, a nie podklasą.

Is_a? a kind_of? metody są zaimplementowane w module jądra, który jest mieszany przez klasę obiektu. Oba są aliasami tej samej metody. Sprawdźmy:

Kernel.instance_method (: kind_of?) = = Kernel.instance_method (: is_a?) # Output: = > true

Implementacja zakresu = = =

Gdy operator = = = jest wywołany na obiekcie range, zwraca true, jeśli wartość po prawej mieści się w zakresie po lewej.

(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

Pamiętaj, że operator = = = wywołuje metodę = = = obiektu lewej ręki. So (1..4) = = = 3 jest równoważne (1..4).=== 3. Innymi słowy, Klasa operandu lewej ręki będzie określ, która implementacja metody === zostanie wywołana, aby pozycje operandów nie były wymienne.

Implementacja Regexp = = =

Zwraca true, jeśli łańcuch znaków po prawej stronie odpowiada wyrażeniu regularnemu po lewej stronie. / zen / = = = "praktyka zazen dzisiaj" # wyjście: => true # is the same as "praktyka zazen dzisiaj" = ~/zen /

Implicite użycie operatora = = = na poleceniach case / when

Operator ten jest również używany pod maską na case / when statements. To jest jego najczęstsze zastosowanie.

minutes = 15

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

# Output: match

W powyższym przykładzie, jeśli Ruby pośrednio używał operatora Double equal ( = = ), zakres 10..20 Nie byłoby uważane za równe liczbie całkowitej takiej jak 15. Dopasowują się, ponieważ operator triple equal (===) jest domyślnie używany we wszystkich instrukcjach case / when. Kod w powyższym przykładzie jest równoważny:

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

Operatory pasujące do wzorca: = ~ and !~

The = ~(equal-tilde) and !~ operatory (bang-tilde) są używane do dopasowania łańcuchów i symboli do wzorców regex.

Implementacja metody =~ w klasach String i Symbol oczekuje wyrażenia regularnego (instancji klasy Regexp) jako argumentu.

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

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

Implementacja klasy Regexp oczekuje jako argumentu łańcucha znaków lub symbolu.

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

We wszystkich implementacjach, gdy łańcuch lub symbol pasuje do wzorca Regexp, Zwraca liczbę całkowitą, która jest pozycją (indeks) meczu. Jeśli nie ma dopasowania, zwraca zero. Pamiętaj, że w Rubim, każda wartość całkowita jest "prawdziwa", a nil jest" falsy", więc operator =~ może być użyty w instrukcjach if I trójnarowych operatorach.

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

Operatory dopasowujące wzorce są również przydatne do pisania krótszych instrukcji if. Przykład:

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

The ! operator ~ jest przeciwieństwem=~, zwraca true, gdy nie ma dopasowania i false, jeśli istnieje dopasowanie.

Więcej informacji można znaleźć na ten blog post .

 36
Author: BrunoFacca,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2019-03-12 16:44:03

Chciałbym rozszerzyć o === operator.

=== nie jest operatorem równości!

Nie. Przejdźmy do rzeczy.

Możesz być zaznajomiony z === jako operatorem równości w Javascript i PHP, ale nie jest to operator równości w Rubim i ma zasadniczo inną semantykę.

Więc co robi ===?

=== jest operatorem dopasowującym wzór!

  • === Mecze zwykłe wyrażenia
  • === sprawdza range członkostwa
  • === sprawdzanie instancji klasy
  • === wywołuje wyrażenia lambda
  • === czasami sprawdza równość, ale przeważnie nie]}
Więc jak to szaleństwo ma sens?
  • Enumerable#grep używa === wewnętrznie
  • case when wyrażenia użyj === wewnętrznie
  • ciekawostka, rescue wykorzystuje === wewnętrznie

Dlatego można używać wyrażeń regularnych i klasy i zakresy, a nawet wyrażenia lambda w instrukcji case when.

Niektóre przykłady

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

Wszystkie te przykłady działają również z pattern === value, a także z grep metodą.

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]
 9
Author: akuhn,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-03-12 02:02:57

Ruby wyświetla kilka różnych metod obsługi równości:

a.equal?(b) # object identity - a and b refer to the same object

a.eql?(b) # object equivalence - a and b have the same value

a == b # object equivalence - a and b have the same value with type conversion.

Czytaj dalej klikając poniższy link, dał mi jasne podsumowanie zrozumienia.

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

Mam nadzieję, że pomoże innym.
 9
Author: kalibbala,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2018-12-04 16:12:03

=== #---Case equality

== #--- generic equality

Oba działają podobnie, ale "= = = "nawet robią case' y

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

Tutaj różnica

String === "test"   #=> true
String == "test"  #=> false
 8
Author: Kishore Mohan,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2014-04-22 09:13:58

Napisałem prosty test dla wszystkich powyższych.

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)
 -9
Author: Tom Phan,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2014-04-25 23:02:49