Translation: Ruby Basics - Equal Operators

https://mauricio.github.io/2011/05/30/ruby-basics-equality-operators-ruby.html

Ruby has a lot of equality operators, some of which are everywhere in our application (such as the common double equal sign - "=="), and some may not be as common (such as the third equal sign - "===") Now let's dig deeper into how Ruby compares objects.

What does equality mean?

Perhaps because of my past Java experience, I think it makes sense to define what is "equal" first. Objects in Ruby have their identifiers, which can be calledobject_idThe method is easy to view.

some_string = 'some string'
=> "some string"
another_string = 'another_string'
=> "another_string"
[ some_string.object_id, another_string.object_id ]
=> [2164900860, 2164888440]

As you continue to create new objects, Ruby assigns object_id to each object, which you can use to distinguish between objects.

matz = 'matz'
=> "matz"
matz_2 = 'matz'
=> "matz"
[ matz.object_id, matz_2.object_id ]
=> [2164840660, 2164818480]

The values ​​of the above two objects are exactly the same, but they have different object_ids, so Ruby does not think that the two values ​​are the same. Object_id can be thought of as a shortcut to identify objects, and each object can be compared to other objects.
Two strings representing the same character should be equal, and two people with the same ID number should be the same person. Programming languages ​​must provide the means to achieve this level of comparison, which are these equal methods in Ruby.

"=="-double equal sign

The double equal sign method should implement the object's universal identification algorithm. This usually means comparing object properties, not whether they are the same object in memory. Since Ruby is a dynamically typed language, don't rely too much on types. You should rely on methods. Don't judge whether an object is from a class, but check if it can respond to a specific method:

class Meter

  def initialize( value )
    @value = value
  end

  def to_meters
    @value.to_f
  end

  def ==( other )
    if other.respond_to?( :to_meters )
      self.to_meters == other.to_meters
    end
  end

end

First verify that the object has a method that you expect to use instead of verifying its type, then compare the two objects. If 'false' is returned, the two objects are not equal.

"hash" method

If you have implemented the "==" method, you should also implement the "eql?" and "hash" methods, even if you have never seen where these methods have been called. Because if your object is used as a hash key, these are the methods used by the hash object to compare with your object. The hash needs to quickly determine if the key already exists, so avoid comparing each individual object. by usinghashThe return value of the method classifies the objects into groups and then uses them in batches.eql?Contrast objects.
When searching for key values ​​in a hash, first call the keyhashMethod to determine which group it belongs to, then useeql?Compare with other objects in the same group. Even in the worst case, we only need to compare with three objects (the hash has nine objects), which is acceptable. But if we use arrays, we need to make 9 comparisons in the worst case.
Give an example:

class StringWithoutHash

  def initialize(text)
    @text = text
  end

  def to_text
    @text
  end  
  
  def ==(other_value)
    if other_value.respond_to?(:to_text)
      self.to_text == other_value.to_text
    end
  end

end

The "==" method has been implemented correctly, but "hash" and "eql?" have not been implemented yet. So in the hash it is not possible to judge that they belong to the same type (will not be assigned to the same group for comparison).

context 'without hash and eql? methods' do

  it 'should be equal' do
    @first  = StringWithoutHash.new('first')
    @second = StringWithoutHash.new('first')
    @first.should == @second
  end

  it 'should add as two different keys in the hash' do

    @texts = {}

    10.times do
      @texts[ StringWithoutHash.new('first') ] = 'one'
    end

    @texts.keys.size.should == 10

  end

end

Even though @firsh and @second represent exactly the same value, since we didn't implement the "hash" and "eql?" methods, we still generate two keys instead of one in the hash. The general practice is to overload "hash" and "eql?" if you override the "==" method. A basic rule when implementing "hash" is that if two objects are "==", then they should generate the exact same hash value (which can be found in the hash object), but with the same hash value. The two objects are still different (they belong to the same group but do not belong to the same object).
Correct implementation in the following exampleeql?withhashThe StringWithHash of the method is a subclass of StringWithoutHash (eql?Method can be used directly==, we don't even have to write code for it)

class StringWithHash < StringWithoutHash

  def eql?( other )
    self == other
  end  
  
  def hash
    @text.hash
  end

end

The corresponding test code is as follows:

context 'with hash and eql? methods' do

  it 'should be equal' do
    @first  = StringWithHash.new('first')
    @second = StringWithHash.new('first')
    @first.should == @second
  end

  it 'should add as a single key in the hash' do

    @texts = {}

    50.times do
      @texts[ StringWithHash.new('first') ] = 'one'
    end

    @texts.keys.size.should == 1

  end

end

Because we implemented it correctlyeql?with hashThe method, even if you add 50 objects to the hash, the number of hashes is still 1.
Unless you know what you are doing (and understand how hashes are implemented in Ruby), don't try to implement them yourself.hashMethod, just likeNumberwithStringAs the class does, use it directlyBasicObjectmiddlehashThe method is just fine. The following example:

class Meter

  # all the other methods

  def hash
    self.to_meters.hash
  end

  def eql?( other )
    self == other
  end 

end

Here, myhashThe method is called directlyFloatofhash. If there is no special need, you can completely copy my implementation here.

"===" - third class

The third sign is an interesting operator that is scattered throughout the Ruby source code, but most developers rarely see it in actual development. Why is this so? Because it is hidden in a common control structurecase/whenin. When usedcase/whenWhen it is actually in use===Operator. It is also===The operator makes Ruby's case condition more powerful than C or Java.
Take a look at the following example:

age = 19

case age
  when 1..18
    puts 'just out of college'
  when 19..30
    puts 'wild years'
  when 31..40
    puts 'i better find a job in a big corp'
  else
    puts 'retirement plan'
end

This code will be called===And print out 'wild years'. See how it works? The following isifVersion implementation:

if 1..18 === age
  puts 'just out of college'
elsif 19..30 === age
  puts 'wild years'
elsif 31..40 === age
  puts 'i better find a job in a big corp'
else
  puts 'retirement plan'
end

In general,case/whenThe statement is simply simplified and beautified===withifThe operation of a statement combination. For the language itself, the third equal sign is used more as a "grouping" operation. The grouping is determined by passing in a value.

Intelligent Recommendation

Translation: Ruby dynamic method

Usually, we use the keyword def to define the method. But when there are a series of methods with the same structure and logic, continuing to do so seems a bit repetitive and violates the DRY principl...

Equal operators with no access to the accessories

Equal operators with no access to the accessories == Logic equal operator === The same operation operation If the number of two operations is different, the compatible operator will be expanded to the...

Equal operators "==" and strict "==="

The introduction of an interesting question When you use "==", have you found it? This A and B are obviously not equal. How does it perform the content of the cycle body! strangeness? For ex...

Why is nil.object_id in ruby ​​equal to 4

The reason is because nil is a fixed object. The ruby.h header file in $rubyhome\lib\1.8\i386-mswin32 has the following special object structure.     In the active support of rails, there is...

More Recommendation

Ruby several easy to ignore operators

== equal value Eql? values ​​are equal, type is equal Equal? ​​equal values, equal memory addresses    ...

Understanding of response_to? and send and '.' operators in ruby

Transfer from: Like other OO languages, in ruby, the function of the object is completed by sending a message to the object, such as str.upcase , which is to send the upcase message to str, the dot op...

Objects in JavaScript are equal (translation)

Original source:Object Equality in JavaScript Equality is the most confusing part of JavaScript at first.==with===The comparison, the order of the mandatory types, and so on, complicate the problem. T...

Ruby Japanese manual translation 1

Ruby's sentence structure: 1: Variable 2: Comment 3: embedded documents 4: Reserved Words Ruby current implementation uses the ASCII character set. Uppercase and lowercase letters can be recognized. I...

Ruby Japanese manual translation 2

program: 1: formula (that is, lines of code, huh, huh) 2: terminate the program Program is a formula composed of side by side. Use a semicolon or newline to distinguish between expression and expressi...

Copyright  DMCA © 2018-2026 - All Rights Reserved - www.programmersought.com  User Notice

Top