Equal ?, eql ?, === ve == arasındaki fark nedir?


552

Bu dört yöntem arasındaki farkı anlamaya çalışıyorum. Her iki işlenen de tamamen aynı nesneye başvurduğunda true döndüren ==yöntemi çağıran varsayılan olarak biliyorum equal?.

===varsayılan olarak da ==hangi çağrıları çağırır equal?... tamam, bu yüzden tüm bu üç yöntem geçersiz kılınmaz, o zaman sanırım ===, ==ve equal?tam olarak aynı şeyi?

Şimdi geliyor eql?. Bu ne işe yarar (varsayılan olarak)? Operand'ın hash / id kodunu arar mı?

Ruby neden bu kadar çok eşitlik işaretine sahip? Anlambilimde farklılık göstermeleri gerekiyor mu?


Ben sadece bir irb başladı ve senin çelişmektedir aşağıdaki sonucu vardı ... Bunların 3 Bütün doğrudur: "a" == "a", "a" === "a"ve "a".eql? "a". Ama bu yanlış: "a".equal? "a"(Benim yakut 1.9.2-p180)
PeterWong

7
@Peter: Bunun nedeni, dizelerin tüm eşitlik işleçlerini geçersiz kılmasıdır. Kullanarak çalışmak a = Object.new; b = Object.newbütün ardından ==, ===, .equal?, .eql?dönecektir trueiçin avs ave sahte avs b.
Nemo157

Yanıtlar:


785

Burada büyük bir açıklamaları olduğunu düşündüğüm için burada Object belgelerini ağır bir şekilde alıntılayacağım . Dize gibi diğer sınıflarda geçersiz kılındığı için ve bu yöntemlerin belgelerini de okumanızı öneririz .

Yan not: bunları farklı nesnelerde kendiniz denemek istiyorsanız, böyle bir şey kullanın:

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}

== - genel "eşitlik"

Nesne düzeyinde, ==yalnızca objve otheraynı nesne ise true değerini döndürür . Tipik olarak, sınıfa özgü bir anlam sağlamak için alt sınıflarda bu yöntem geçersiz kılınır.

Bu, en yaygın karşılaştırmadır ve bu nedenle (bir sınıfın yazarı olarak) iki nesnenin "eşit" olup olmadığına karar vereceğiniz en temel yerdir.

=== - dava eşitliği

Nesne sınıfı için, etkin bir şekilde çağırmakla aynıdır #==, ancak vaka ifadelerinde anlamlı anlambilim sağlamak için torunlar tarafından geçersiz kılınır.

Bu inanılmaz derecede faydalı. İlginç ===uygulamaları olan şeylere örnekler :

  • Aralık
  • regex
  • Proc (Ruby 1.9'da)

Böylece aşağıdakileri yapabilirsiniz:

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

Bkz burada cevabımı nasıl düzgün bir örneğin case+ Regexkodu bir çok temizleyici yapabilirsiniz. Ve elbette, kendi ===uygulamanızı sağlayarak özel caseanlambilim elde edebilirsiniz .

eql?- Hasheşitlik

eql?Eğer yöntem true döndürür objve otheraynı karma anahtarına bakın. Bu, Hashüyeleri eşitlik açısından test etmek için kullanılır . Sınıf nesneler için Object, eql?eş anlamlıdır ==. Alt sınıflar normalde bu geleneği eql?geçersiz kılınan ==yöntemlerine takma adıyla devam ettirir , ancak istisnalar vardır. Numerictürler, örneğin, tür dönüşümü gerçekleştirir ==, ancak tür değiştirmez eql?;

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

Böylece kendi kullanımlarınız için bunu geçersiz kılabilirsiniz veya iki yöntemin aynı şekilde davranması için geçersiz kılabilir ==ve kullanabilirsiniz alias :eql? :==.

equal? - kimlik karşılaştırması

Bunun aksine ==, equal?yöntem asla alt sınıflar tarafından geçersiz kılınmamalıdır: nesne kimliğini belirlemek için kullanılır (yani a.equal?(b)iff a,b ).

Bu etkili bir şekilde işaretçi karşılaştırmasıdır.


32
Cevabınızdan anladığım gibi, katılık eşittir mi? <eql? <== <===. Normalde == kullanırsınız. Bazı gevşek amaçlar için === kullanırsınız. Katı bir durum için, eql? Ve tam kimlik için eşit? Kullanıyorsunuz.
sawa

21
Sıkılık kavramı dokümantasyonda zorunlu değildir veya önerilmemektedir, sadece Numericdaha katı bir şekilde ele alır ==. Gerçekten sınıfın yazarına kalmış. ifadelerin ===dışında nadiren kullanılır case.
jtbandes

4
== da daha büyük / daha küçük anlamında eşitliktir. Yani, Karşılaştırılabilir'i eklerseniz, <=> 0 döndürme açısından tanımlanır. Bu nedenle 1 == 1,0 true değerini döndürür.
apeiros

5
@sawa Genellikle ==="eşleşmeler" anlamına gelir (kabaca). "Regexp, dizeyle eşleşiyor" veya "aralık, sayıyla eşleşiyor (dahil)".
Kelvin

7
İlginç gerçek: Resmi dokümanlar şimdi bu cevaba bağlanıyor (bkz. Ruby-doc.org/core-2.1.5/… ).
Mark Amery

46

Jtbandes cevabını seviyorum, ancak oldukça uzun olduğu için kendi kompakt cevabımı ekleyeceğim:

==, ===, eql?,equal?
4 karşılaştırıcı yani vardır. Ruby'de 2 nesneyi karşılaştırmanın 4 yolu.
Ruby'de, tüm karşılaştırıcılar (ve çoğu işleçler) aslında yöntem çağrıları olduğundan, bu karşılaştırma yöntemlerinin anlamlarını kendiniz değiştirebilir, üzerine yazabilir ve tanımlayabilirsiniz. Bununla birlikte, Ruby'nin iç dil yapılarının hangi karşılaştırıcıyı kullandığını anlamak önemlidir:

==(değer karşılaştırması)
Ruby şunu kullanır: == her yerde 2 nesnenin değerlerini karşılaştırmak için , ör. Hash değerleri:

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

===(vaka karşılaştırması)
Ruby şunu kullanır: === durumunda / yapılandırır. Aşağıdaki kod parçacıkları mantıksal olarak aynıdır:

case foo
  when bar;  p 'do something'
end

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

eql?(Hash-key karşılaştırması)
Ruby kullanır: eql? (karma yöntemi ile birlikte). Çoğu sınıfta: eql? ile aynıdır: ==.
Bilgi hakkında: eql? sadece kendi özel sınıflarınızı oluşturmak istediğinizde önemlidir:

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

Not: Yaygın olarak kullanılan Ruby sınıfı Seti ayrıca Hash-key karşılaştırmasına da dayanır.

equal?(nesne kimliği karşılaştırması)
Ruby kullanır: eşit mi? iki nesnenin aynı olup olmadığını kontrol etmek için. Bu yöntemin (BasicObject sınıfı) üzerine yazılmaması gerekir.

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

30
Bu iyi bir cevap, ama neredeyse jtbandes kadar uzun. :)
tuhaflık

2
@odigity, yaklaşık% 70 kadar. Bunu% 30 harcayacak birçok şey düşünebilirdim.
Cary Swoveland

Bence örneği eql?çok yanıltıcı. karmanın nasıl hesaplandığı ile tutarlıeql? bir eşitlik karşılaştırmasıdır , yani bunu garanti eder . O mu değil sadece hash kodları karşılaştırın. a.eql?(b)a.hash == b.hash
Andrey Tarantsov

vaka karşılaştırma gerçekten eşdeğer bar === foodeğil foo === bar? İkincisinin doğru olduğunu umuyorum ve derleyici sol tarafı çağırdığından önemlidir: === ``
Alexis Wilke

Bildiğim kadarıyla, bar === fooRuby: sol tarafta vaka değerini ve sağ tarafta vaka değişkenini kullanır. Bunun NPE'lerden (Boş İşaretçi İstisnaları) kaçınmakla ilgisi olabilir.
Andreas Rayo Kniep

34

Eşitlik operatörleri: == ve! =

Eşitlik veya çift eşitlik olarak da bilinen == işleci, her iki nesne de eşitse true, değilse yanlış döndürür.

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

Eşitsizlik olarak da bilinen! = İşleci == ifadesinin tersidir. Her iki nesne de eşit değilse true, eşitse false döndürür.

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

Farklı bir sırayla aynı öğelere sahip iki dizinin eşit olmadığını, aynı harfin büyük ve küçük sürümlerinin eşit olmadığını unutmayın.

Farklı türdeki sayıları (örneğin, tamsayı ve kayan nokta) karşılaştırırken, sayısal değerleri aynı ise, == true değerini döndürür.

2 == 2.0 # Output: => true

eşit?

Her iki işlenenin de eşit olup olmadığını sınayan == işlecinin aksine, eşit yöntem iki işlenenin aynı nesneyi gösterip göstermediğini denetler. Bu Ruby'deki en katı eşitlik şeklidir.

Örnek: a = "zen" b = "zen"

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

a.equal? b  # Output: => false

Yukarıdaki örnekte, aynı değere sahip iki dizemiz var. Ancak, bunlar farklı nesne kimlikleri olan iki ayrı nesnedir. Peki, eşit mi? yöntemi false değerini döndürür.

Tekrar deneyelim, sadece bu sefer b a'ya bir referans olacaktır. Nesne tanıtıcısının, aynı nesneyi işaret ettiklerinden, her iki değişken için de aynı olduğuna dikkat edin.

a = "zen"
b = a

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

a.equal? b  # Output: => true

EQL?

Hash sınıfında, eql? yöntemi anahtarları eşitlik açısından test etmek için kullanılır. Bunu açıklamak için biraz arka plan gereklidir. Genel bilgi işlem bağlamında, bir karma işlevi herhangi bir boyutta bir dize (veya bir dosya) alır ve genellikle yalnızca karma olarak adlandırılan karma kod adı verilen sabit boyutlu bir dize veya tam sayı oluşturur. Yaygın olarak kullanılan bazı karma kod türleri MD5, SHA-1 ve CRC'dir. Şifreleme algoritmalarında, veritabanı indekslemesinde, dosya bütünlüğü kontrolünde vb. Kullanılırlar. Ruby gibi bazı programlama dilleri, karma tablosu adı verilen bir toplama türü sağlar. Karma tablolar, benzersiz anahtarlar ve karşılık gelen değerlerden oluşan verileri çiftler halinde depolayan sözlük benzeri koleksiyonlardır. Kaputun altında, bu anahtarlar hashcode olarak saklanır. Karma tablolar genellikle karma olarak adlandırılır. Hash kelimesinin bir karma koduna veya bir karma tablosuna nasıl başvurduğuna dikkat edin.

Ruby, karma kodlar oluşturmak için karma adı verilen yerleşik bir yöntem sağlar. Aşağıdaki örnekte, bir dize alır ve bir hashcode döndürür. Aynı değere sahip dizelerin, farklı nesneler olsa da (farklı nesne kimlikleriyle) her zaman aynı karma koduna sahip olduğuna dikkat edin.

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

Karma yöntemi, tüm Ruby nesnelerinin varsayılan kökü olan Object sınıfında bulunan Çekirdek modülünde uygulanır. Symbol ve Integer gibi bazı sınıflar varsayılan uygulamayı kullanır, String ve Hash gibi diğerleri kendi uygulamalarını sağlar.

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'de, bir şeyi bir karma (koleksiyon) içinde sakladığımızda, anahtar olarak sağlanan nesne (ör., Dize veya simge) bir karma kod olarak dönüştürülür ve saklanır. Daha sonra, bir öğeyi karma (koleksiyon) öğesinden alırken, bir nesneyi bir anahtar olarak sağlarız, bu bir karma koduna dönüştürülür ve mevcut anahtarlarla karşılaştırılır. Bir eşleşme varsa, karşılık gelen öğenin değeri döndürülür. Karşılaştırma eql? yöntemi altında.

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

Çoğu durumda, eql? yöntemi == yöntemine benzer şekilde davranır. Ancak, birkaç istisna vardır. Örneğin, eql? bir tamsayıyı bir kayan noktalı sayı ile karşılaştırırken örtük tür dönüştürme gerçekleştirmez.

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

Vaka eşitliği operatörü: ===

Ruby'nin String, Range ve Regexp gibi yerleşik sınıflarının birçoğu, büyük / küçük harf eşitliği, üçlü eşitlik veya üçlü değer olarak da bilinen === operatörünün kendi uygulamalarını sağlar. Her sınıfta farklı uygulandığından, çağrıldığı nesnenin türüne bağlı olarak farklı davranacaktır. Genel olarak, sağdaki nesne "nesnesine" aitse veya "soldaki nesnenin" üyesi ise true değerini döndürür. Örneğin, bir nesnenin bir sınıfın (ya da alt sınıflarından birinin) bir örneği olup olmadığını test etmek için kullanılabilir.

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

Aynı sonuç, muhtemelen işe en uygun diğer yöntemlerle de elde edilebilir. Verimlilik ve kısalıktan ödün vermeden, olabildiğince açık olarak okunması kolay kod yazmak genellikle daha iyidir.

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

2 gibi tamsayılar, Integer sınıfının bir alt sınıfı olan Fixnum sınıfının örnekleri olduğu için false döndürülen son örneğe dikkat edin. ===, is_a? ve örnek_o? nesne verilen sınıfın veya alt sınıfların bir örneği ise yöntemler true değerini döndürür. İnstance_of yöntemi daha katıdır ve yalnızca nesne bir alt sınıfın değil, o sınıfın bir örneğiyse true değerini döndürür.

İs_a? ve tür_o? yöntemleri Object sınıfı tarafından karıştırılan Çekirdek modülünde uygulanır. Her ikisi de aynı yöntemin takma adlarıdır. Doğrulayalım:

Çekirdek.instance_method (: kind_of?) == Çekirdek.instance_method (: is_a?) # Çıktı: => true

=== Aralık Uygulaması

Bir aralık nesnesinde === işleci çağrıldığında, sağdaki değer soldaki aralık içine düşerse true değerini döndürür.

(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

=== operatörünün sol nesnenin === yöntemini çağırdığını unutmayın. Yani (1..4) === 3 (1..4) 'e eşittir. === 3. Başka bir deyişle, sol taraftaki işlenenin sınıfı === yönteminin hangi uygulamasının olacağını tanımlayacaktır. denir, bu nedenle işlenen konumları değiştirilemez.

Normal İfade ===

Sağdaki dize, soldaki normal ifadeyle eşleşiyorsa true değerini döndürür. / zen / === "bugün zazen alıştırması" # Çıktı: => true # "bugün zazen alıştırması" ile aynıdır = ~ / zen /

Vaka / when ifadelerinde === operatörünün örtülü kullanımı

Bu operatör ayrıca case / when ifadelerinde başlık altında da kullanılır. En yaygın kullanımı budur.

minutes = 15

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

# Output: match

Yukarıdaki örnekte, Ruby çift eşit işleç (==) örtük olarak kullansaydı, 10..20 aralığı 15 gibi bir tam sayıya eşit kabul edilmezdi. Üç eşit eşit işleç (===) her durumda / when ifadelerinde dolaylı olarak kullanılır. Yukarıdaki örnekteki kod şuna eşdeğerdir:

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

Örüntü eşleme işleçleri: = ~ ve! ~

= ~ (Eşit-tilde) ve! ~ (Bang-tilde) işleçleri, dizeleri ve sembolleri normal ifadelere göre eşleştirmek için kullanılır.

= ~ Yönteminin String ve Symbol sınıflarında uygulanması, bağımsız değişken olarak normal bir ifade (Regexp sınıfının bir örneği) bekler.

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

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

Regexp sınıfındaki uygulama, bağımsız değişken olarak bir dize veya simge bekliyor.

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

Tüm uygulamalarda, dize veya sembol Regexp deseniyle eşleştiğinde, eşleşmenin konumu (dizin) olan bir tamsayı döndürür. Eşleşme yoksa sıfır döndürür. Ruby'de, herhangi bir tamsayı değerinin "doğruluk" ve nil "yanlış" olduğunu unutmayın, bu nedenle = ~ operatörü if ifadelerinde ve üçlü operatörlerde kullanılabilir.

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

Örüntü eşleştirme işleçleri, daha kısa if ifadeleri yazmak için de yararlıdır. Misal:

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

! ~ İşleci = ~ öğesinin tersidir, eşleşme olmadığında true değerini ve eşleşme varsa false değerini döndürür.

Bu blog gönderisinde daha fazla bilgi bulabilirsiniz .


6
Bunu şu anda kabul edilen cevaptan daha iyi bir cevap buluyorum, çünkü güzel örnekler veriyor ve farklı eşitlik türlerinin ne anlama geldiği ve neden var oldukları / nerede kullanıldıkları konusunda daha az belirsiz.
Qqwy

1
Çok ayrıntılı cevap, ama benim irb (ruby v 2.2.1) :zen === "zen"yanlış döndürür
Mike R

@MikeR Bana bildirdiğiniz için teşekkür ederim. Cevabı düzelttim.
BrunoFacca

Sanırım tip_of mu demek istediniz? Msgstr "2 gibi tamsayılar Integer sınıfının bir alt sınıfı olan Fixnum sınıfının örnekleri olduğu için, son döndürülen son örneğe dikkat edin. ===, is_a? Ve instance_of? (TYPE_OF?)"?
user1883793

1
Bu cevabı seviyorum. Teşekkürler
Abdullah Fadhel

9

Ruby, eşitliği ele almak için birkaç farklı yöntem ortaya koyar:

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.

Aşağıdaki bağlantıyı tıklayarak okumaya devam et, bana net bir özet verdi.

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

Umarım başkalarına yardımcı olur.


8

=== # --- dava eşitliği

== # --- genel eşitlik

her ikisi de benzer çalışır ancak "===" vaka ifadeleri bile yapar

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

işte fark

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

3
Onlar yok o zaman bu doğru olma eğilimindedir rağmen benzer şekilde çalışır a==bsonra a===b. Ama a===bçok daha güçlü. ===simetrik değildir ve a===bçok farklı bir şeyi ifade b===adursun, a==b.
mwfearnley

8

===Operatöre genişlemek istiyorum .

=== eşitlik operatörü değildir!

Değil.

Bu noktayı gerçekten karşılayalım.

===Javascript ve PHP'de bir eşitlik operatörü olarak aşina olabilirsiniz , ancak bu sadece Ruby'de bir eşitlik operatörü değildir ve temel olarak farklı anlambilime sahiptir.

Peki ne yapar ===?

=== kalıp eşleştirme operatörüdür!

  • === düzenli ifadelerle eşleşir
  • === menzil üyeliğini kontrol eder
  • === bir sınıf örneği olup olmadığını kontrol eder
  • === lambda ifadelerini çağırır
  • === bazen eşitliği kontrol eder, ama çoğunlukla

Peki bu delilik nasıl mantıklı?

  • Enumerable#grep===dahili olarak kullanır
  • case whenifadeler ===dahili olarak kullanılır
  • Eğlenceli gerçek, dahili olarak rescuekullanır===

Bu yüzden bir ifadede düzenli ifadeleri ve sınıfları, aralıkları ve hatta lambda ifadelerini kullanabilirsiniz case when.

Bazı örnekler

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

Tüm bu örnekler yöntemle pattern === valueolduğu gibi, aynı zamanda da çalışır 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]

-8

Yukarıdakilerin tümü için basit bir test yazdım.

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)
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.