equal ?, eql ?, === 및 ==의 차이점은 무엇입니까?


552

이 네 가지 방법의 차이점을 이해하려고합니다. 기본적으로 두 피연산자가 모두 동일한 객체를 참조 할 때 true를 반환 ==하는 메서드 를 호출 한다는 것을 알고 equal?있습니다.

===기본적으로도 전화 ==통화를 equal?... 좋아,이 모든 세 가지 방법 오버라이드 (override)하지 않는 경우는, 다음 같아요 ===, ==그리고 equal?정확히 같은 일을?

이제옵니다 eql?. 이 작업은 무엇입니까 (기본적으로)? 피연산자의 해시 / ID를 호출합니까?

루비에는 왜 많은 등호가 있습니까? 그것들은 의미론이 달라야합니까?


난 그냥 IRB를 시작하고 당신과 모순 다음과 같은 결과를했다 ...이 3 모두에 해당합니다 "a" == "a", "a" === "a"하고 "a".eql? "a". 그러나 이것은 거짓입니다 : "a".equal? "a"(
내는

7
@Peter : 문자열이 모든 항등 연산자보다 우선하기 때문입니다. 사용하려고 a = Object.new; b = Object.new하는 모든 다음 ==, ===, .equal?, .eql?반환 true을위한 aa와 false를 ab.
Nemo157

답변:


785

여기에 Object documentation 이 많이 인용 되어 있습니다. 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}

== — 일반적인 "평등"

객체 수준에서 및 동일한 객체 인 ==경우에만 true를 반환 합니다. 일반적으로이 메서드는 하위 클래스에서 재정의되어 클래스 별 의미를 제공합니다.objother

이것은 가장 일반적인 비교이므로 (클래스 작성자로서) 두 객체가 "동일한 지"여부를 결정할 수있는 가장 기본적인 장소입니다.

=== — 대등

Object 클래스의 경우, 호출과 사실상 동일 #==하지만 일반적으로 후손에 의해 재정의되어 경우 문에 의미가있는 의미를 제공합니다.

이것은 매우 유용합니다. 흥미로운 ===구현 이있는 것의 예 :

  • 범위
  • 정규식
  • 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

+ 가 코드를 훨씬 깨끗하게 만드는 방법에 대한 깔끔한 예는 여기 내 대답을 참조하십시오 . 물론 자체 구현 을 제공하여 사용자 정의 의미를 얻을 수 있습니다 .caseRegex===case

eql?Hash평등

eql?경우 메소드는 true를 돌려줍니다 objother동일한 해시 키를 참조하십시오. 이는 Hash구성원의 동등성을 테스트 하는 데 사용됩니다 . 클래스의 객체를 들어 Object, eql?동의어입니다 ==. 서브 클래스는 일반적으로 eql?재정의 된 ==메소드 에 별명 을 지정하여 이러한 전통을 이어가지 만 예외가 있습니다. Numeric예를 들어 유형은에 대해 유형 변환을 수행 ==하지만에 대해서는 수행 하지 않습니다 eql?.

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

따라서 자신의 용도에 따라 이것을 재정의하거나 두 방법이 동일한 방식으로 작동하도록 재정의 ==하고 사용할 수 있습니다 alias :eql? :==.

equal? — 정체성 비교

와 달리 ==, equal?메소드는 서브 클래스에 의해 재정의되어서는 안됩니다. 즉, 객체 아이덴티티를 결정하는 데 사용됩니다 (즉, a.equal?(b)iff는와 a같은 객체입니다 b).

이것은 효과적으로 포인터 비교입니다.


32
귀하의 답변에서 알 수 있듯이 엄격 성은 다음과 같습니다. <eql? <== <===. 일반적으로 ==를 사용합니다. 느슨한 목적으로 ===를 사용합니다. 엄격한 상황에서는 eql?을 사용하고 완전한 신원에는 equal?을 사용하십시오.
sawa

21
엄격함의 개념은 문서에서 시행되거나 제안되지 않았 Numeric으며,보다 엄격하게 처리합니다 ==. 그것은 실제로 수업의 저자에게 달려 있습니다. 명령문 ===외부에서는 자주 사용되지 않습니다 case.
jtbandes

4
==는 더 크거나 작은 측면에서도 동등합니다. 즉, Comparable을 포함하면 0을 반환하는 <=>의 관점에서 정의됩니다. 이것이 1 == 1.0이 true를 반환하는 이유입니다.
apeiros

5
@sawa 나는 보통 ==="일치하다"(거의)를 의미하는 것으로 생각한다 . "regexp가 문자열과 일치합니까?"또는 "범위가 숫자와 일치합니까?"
Kelvin

7
재미있는 사실 : 공식 문서는 이제이 답변에 연결됩니다 ( ruby-doc.org/core-2.1.5/… 참조 ).
Mark Amery

46

나는 jtbandes 답변을 좋아하지만 꽤 길기 때문에 내 자신의 간단한 답변을 추가 할 것입니다.

==, ===, eql?,equal?
4 비교기, 즉이다. Ruby에서 두 객체를 비교하는 4 가지 방법
Ruby에서 모든 비교기 (및 대부분의 연산자)는 실제로 메소드 호출이므로 이러한 비교 메소드의 의미를 직접 변경, 덮어 쓰기 및 정의 할 수 있습니다. 그러나 Ruby의 내부 언어 구조가 어떤 비교자를 사용하는지 이해하는 것이 중요합니다.

==(값 비교)
루비는 : ==를 사용하여 두 객체 의 을 비교합니다 . 해시 값 :

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

===(케이스 비교)
루비는 경우 / 구문에서 : ===를 사용합니다. 다음 코드 스 니펫은 논리적으로 동일합니다.

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 클래스 세트는 Hash-key-comparison에 의존합니다.

equal?(개체 식별 비교)
루비 용도 : 동일? 두 객체가 동일한 지 확인합니다. BasicObject 클래스의이 메소드는 겹쳐 써서는 안됩니다.

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

30
좋은 대답이지만 거의 jtbandes의 길이입니다. :)
odigity

2
@odigity, 약 70 % 길이입니다. 나는 그 30 %를 소비 할 많은 것들을 생각할 수있었습니다.
Cary Swoveland

나는 그 예가 eql?매우 잘못된 것이라고 생각합니다 . eql?는 해시 계산 방식과 일치 하는 평등 비교 a.eql?(b)입니다 a.hash == b.hash. 즉, 이를 보장합니다 . 단순히 해시 코드를 비교 하지는 않습니다 .
Andrey Tarantsov

는 IS 의 경우 비교는 정말 상응 bar === foo하지 foo === bar? 나는 후자가 정확하기를 바라고 컴파일러가 왼쪽을 호출하기 때문에 중요합니다 : ===` '
Alexis Wilke

내가 아는 한, bar === fooRuby는 왼쪽의 대소 문자 값과 오른쪽의 대소 문자 변수를 사용합니다. 이것은 NPE를 피하는 것과 관련이있을 수 있습니다 (널 포인터 예외).
Andreas Rayo Kniep

33

같음 연산자 : == 및! =

등호 또는 이중 등호라고도하는 == 연산자는 두 객체가 모두 같으면 true를 반환하고 그렇지 않으면 false를 반환합니다.

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

부등식이라고도하는! = 연산자는 ==의 반대입니다. 두 객체가 같지 않으면 true를 반환하고 같으면 false를 반환합니다.

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

동일한 순서로 요소가 동일한 두 배열이 같지 않고 같은 문자의 대문자 및 소문자 버전이 같지 않습니다.

다른 유형 (예 : 정수 및 부동 소수점)의 숫자를 비교할 때 숫자 값이 같으면 ==가 true를 반환합니다.

2 == 2.0 # Output: => true

같은?

두 피연산자가 같은지 테스트하는 == 연산자와 달리 equal 메서드는 두 피연산자가 동일한 객체를 참조하는지 확인합니다. 이것은 루비에서 가장 엄격한 평등 형태입니다.

예 : a = "zen"b = "zen"

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

a.equal? b  # Output: => false

위의 예에서 값이 같은 두 개의 문자열이 있습니다. 그러나 이들은 서로 다른 객체 ID를 가진 두 개의 별개의 객체입니다. 따라서 동등합니까? 메소드는 false를 리턴합니다.

다시 시도하겠습니다. 이번에는 b 만 a에 대한 참조입니다. 객체 ID는 동일한 객체를 가리 키기 때문에 두 변수에 대해 동일합니다.

a = "zen"
b = a

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

a.equal? b  # Output: => true

eql?

해시 클래스에서 eql? 이 방법은 키가 동일한 지 테스트하는 데 사용됩니다. 이것을 설명하기 위해서는 약간의 배경이 필요합니다. 컴퓨팅의 일반적인 맥락에서, 해시 함수는 임의의 크기의 문자열 (또는 파일)을 취하여 일반적으로 해시라고하는 해시 코드라고하는 고정 크기의 문자열 또는 정수를 생성합니다. 일반적으로 사용되는 일부 해시 코드 유형은 MD5, SHA-1 및 CRC입니다. 이들은 암호화 알고리즘, 데이터베이스 색인 작성, 파일 무결성 검사 등에 사용됩니다. Ruby와 같은 일부 프로그래밍 언어는 해시 테이블이라는 콜렉션 유형을 제공합니다. 해시 테이블은 고유 키와 해당 값으로 구성된 쌍으로 데이터를 저장하는 사전과 유사한 콜렉션입니다. 후드 아래에서 해당 키는 해시 코드로 저장됩니다. 해시 테이블은 일반적으로 해시라고합니다. hash라는 단어가 해시 코드 또는 해시 테이블을 나타내는 방법에 주목하십시오.

Ruby는 해시 코드 생성을위한 해시라는 내장 메소드를 제공합니다. 아래 예제에서 문자열을 가져와 해시 코드를 반환합니다. 동일한 값을 가진 문자열이 다른 객체 ID를 가진 별개의 객체 임에도 불구하고 항상 동일한 해시 코드를 갖는 방법에 주목하십시오.

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

해시 메소드는 모든 Ruby 객체의 기본 루트 인 Object 클래스에 포함 된 커널 모듈에서 구현됩니다. 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에서 해시 (컬렉션)에 무언가를 저장하면 키 (예 : 문자열 또는 기호)로 제공된 객체가 해시 코드로 변환되어 저장됩니다. 나중에 해시 (컬렉션)에서 요소를 검색 할 때 해시 코드로 변환되어 기존 키와 비교되는 키로 객체를 제공합니다. 일치하는 항목이 있으면 해당 항목의 값이 반환됩니다. 비교는 eql? 후드 아래의 방법.

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

대부분의 경우 eql? 메소드는 == 메소드와 유사하게 작동합니다. 그러나 몇 가지 예외가 있습니다. 예를 들어 eql? 정수를 부동 소수점과 비교할 때 암시 적 유형 변환을 수행하지 않습니다.

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

대소 문자 평등 연산자 : ===

String, Range 및 Regexp와 같은 Ruby의 내장 클래스는 대소 문자 구분, 트리플 같음 또는 삼등이라고도하는 === 연산자의 자체 구현을 제공합니다. 각 클래스에서 다르게 구현되므로 호출 한 객체의 유형에 따라 다르게 동작합니다. 일반적으로 오른쪽에있는 개체가 왼쪽에있는 개체의 "포함"또는 "구성원"이면 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

2와 같은 정수는 Integer 클래스의 서브 클래스 인 Fixnum 클래스의 인스턴스이므로 마지막 예제는 false를 리턴했습니다. ===, 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과 같습니다. 즉, 왼쪽 피연산자의 클래스는 === 메소드의 구현을 정의합니다. 피연산자 위치는 서로 호환되지 않습니다.

=== 정규 표현식 구현

오른쪽의 문자열이 왼쪽의 정규식과 일치하면 true를 반환합니다. / zen / === "오늘 연습 zazen"# 출력 : => true # "오늘 연습 zazen"과 같습니다 == / zen /

case / w 문에서 === 연산자의 내재적 사용법

이 연산자는 대 / 소문자를 명시 할 때도 사용됩니다. 그것이 가장 일반적으로 사용됩니다.

minutes = 15

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

# Output: match

위의 예에서 Ruby가 암시 적으로 double equal operator (==)를 사용한 경우 10..20 범위는 15와 같은 정수와 같지 않습니다. triple equal operator (===)는 모든 경우 / 언제든지 진술에 암시 적으로 사용됩니다. 위 예제의 코드는 다음과 같습니다.

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

패턴 일치 연산자 : = ~ 및! ~

= ~ (등호) 및! ~ (bang-tilde) 연산자는 문자열과 기호를 정규식 패턴과 일치시키는 데 사용됩니다.

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에서 정수 값은 "truthy"이고 nil은 "falsy"이므로 = ~ 연산자는 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를 반환합니다.

자세한 내용은 이 블로그 게시물 에서 확인할 수 있습니다 .


6
나는 이것이 좋은 예를 제공하고 다른 종류의 평등의 의미와 그것이 존재하는 이유 / 사용되는 이유에 대해 모호하지 않기 때문에 현재 받아 들여진 대답보다 더 나은 대답을 찾습니다.
Qqwy 2016 년

1
매우 자세한 답변이지만 irb (ruby v 2.2.1) :zen === "zen"에서 false를 반환합니다.
Mike R

@MikeR 알려 주셔서 감사합니다. 답변을 수정했습니다.
BrunoFacca

나는 당신이 type_of를 의미한다고 생각합니까? "2와 같은 정수는 Integer 클래스의 서브 클래스 인 Fixnum 클래스의 인스턴스이므로 마지막 예제는 false를 리턴했습니다. ===, is_a? 및 instance_of? (TYPE_OF?)"?
user1883793

1
나는이 답변을 좋아한다. 감사합니다
Abdullah Fadhel

9

루비는 평등을 처리하기위한 여러 가지 방법을 노출합니다

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.

아래 링크를 클릭하여 계속 읽으면 요약 된 내용이 명확하게 나옵니다.

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

그것이 다른 사람들을 돕기를 바랍니다.


8

=== #-대소 문자 평등

== # --- 일반 평등

둘 다 비슷하게 작동하지만 "==="경우에도 진술을 수행

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

여기 차이점

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

3
그들은 하지 않습니다 이 때 사실 경향에도 불구하고, 유사하게 작동 a==ba===b. 그러나 a===b훨씬 더 강력합니다. ===대칭이 아니며, a===b매우 다른 것을 의미 b===a커녕를 a==b.
mwfearnley

8

===연산자 를 확장하고 싶습니다 .

=== 평등 연산자가 아닙니다!

아니.

그 요점을 실제로 살펴 보겠습니다.

===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 === valuegrep방법 뿐만 아니라 작동 합니다.

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

위의 모든 것에 대한 간단한 테스트를 작성했습니다.

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)
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.