Menu
Coddy logo textTech

동등성과 해시

Coddy Ruby 여정의 객체 지향 프로그래밍 섹션에 포함된 레슨. 57개 중 37번째.

Ruby에는 여러 종류의 동등성이 있습니다. ==는 값의 동등성으로, 클래스에 대해 정의하는 것입니다. Hash와 uniq는 대신 두 가지 다른 메서드인 eql?과 hash를 사용합니다. ==만 정의하면 값이 같은 Point도 여전히 서로 다른 해시 키입니다:

class Point
  attr_reader :x, :y

  def initialize(x, y)
    @x = x
    @y = y
  end

  def ==(other)
    other.is_a?(Point) && x == other.x && y == other.y
  end
end

a = Point.new(1, 2)
b = Point.new(1, 2)
puts a == b
puts [a, b].uniq.size
puts({ a => "first" }[b].inspect)

출력:

true
2
nil

Hash는 먼저 객체에서 계산된 숫자인 hash 값을 비교한 다음, eql?을 사용해 후보를 확인합니다. ==과 동일한 필드를 사용하여 두 메서드를 모두 정의하세요. alias eql? ==은 ==을 재사용하고, [x, y].hash는 필드들을 결합합니다:

class Point
  attr_reader :x, :y

  def initialize(x, y)
    @x = x
    @y = y
  end

  def ==(other)
    other.is_a?(Point) && x == other.x && y == other.y
  end
  alias eql? ==

  def hash
    [x, y].hash
  end
end

a = Point.new(1, 2)
b = Point.new(1, 2)
puts [a, b, Point.new(3, 4)].uniq.size
puts({ a => "first" }[b])

출력:

2
first

규칙은 다음과 같습니다. eql?인 객체는 동일한 hash를 가져야 합니다. 서로 다른 두 객체가 우연히 해시 값을 공유할 수 있기 때문에 eql?은 여전히 확인을 수행합니다. 그러면 Hash로 개수를 셀 때 객체의 식별자가 아니라 값을 기준으로 작동합니다.

class Word
  attr_reader :text

  def initialize(text)
    @text = text.downcase
  end

  def ==(other)
    other.is_a?(Word) && text == other.text
  end
  alias eql? ==

  def hash
    text.hash
  end
end

counts = Hash.new(0)
%w[Ruby ruby RUBY gem].each { |t| counts[Word.new(t)] += 1 }
counts.each { |word, n| puts "#{word.text}: #{n}" }

출력:

ruby: 3
gem: 1

equal?은 동일성을 유지해야 하며 절대로 재정의해서는 안 됩니다. 내장 값은 이미 이러한 규칙을 따르므로 문자열과 배열을 해시 키로 사용할 수 있습니다:

a == b          # 같은 값 (직접 정의함)
a.eql?(b)       # Hash 키와 uniq에 대한 같은 값 (==의 별칭)
a.hash          # 숫자; 같은 객체는 같은 숫자를 반환
a.equal?(b)     # 같은 객체 (절대 재정의하지 말 것)
challenge icon

챌린지

쉬움

좌표가 같은 점들이 ==, eql? 및 hash에서 동일하게 취급되고 Hash 키로 작동하도록 Point 클래스를 완성하세요. to_s는 (1, 2)를 반환합니다.

제공된 코드는 점을 x,y 형식의 줄로 읽습니다. uniq를 사용해 서로 다른 점의 개수를 출력한 다음, Hash에서 점별 방문 횟수를 세고 첫 방문 순서대로 (1, 2): 2와 같이 출력하며, 마지막으로 새 Point.new(0, 0)이 키인지 여부를 출력합니다.

코드는 point.rb에 작성하세요. main.rb에는 제공된 입력/출력 코드가 들어 있으며 수정할 수 없습니다.

직접 해보기

require_relative 'point'

# 제공된 입력/출력 코드: 그대로 유지하세요
input = []
while (line = gets)
  input << line.chomp
end
points = input.map { |line| Point.new(*line.split(",").map(&:to_i)) }
puts "distinct: #{points.uniq.size}"
visits = Hash.new(0)
points.each { |pt| visits[pt] += 1 }
visits.each { |pt, n| puts "#{pt}: #{n}" }
puts "origin visited: #{visits.key?(Point.new(0, 0))}"
quiz icon실력 점검

이 레슨에는 짧은 퀴즈가 포함되어 있습니다. 레슨을 시작해 문제를 풀고 진행 상황을 기록하세요.

객체 지향 프로그래밍의 모든 레슨

직접 연습해 보세요: 온라인 Ruby 컴파일러