Menu
Coddy logo textTech

同一性と等価性

CoddyのRubyジャーニー「オブジェクト指向プログラミング」セクションの一部。レッスン 10/57。

変数はオブジェクトそのものではなく、オブジェクトへの参照を保持します。別の変数に代入すると参照がコピーされるため、両方の名前が同じオブジェクトを参照するようになります。

class Box
  attr_accessor :count

  def initialize
    @count = 0
  end
end

a = Box.new
b = a
b.count += 1
puts a.count

出力:

1

equal? は2つの参照が同じオブジェクトを指しているかどうかを尋ね、object_id は各オブジェクト固有の番号です。独自のクラスでは、デフォルトで == も同じ動作をするため、同じ値を持つ2つのオブジェクトは等しくありません。

class Point
  attr_reader :x, :y

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

a = Point.new(1, 2)
b = Point.new(1, 2)
c = a
puts a == b
puts a.equal?(b)
puts a.equal?(c)
puts a.object_id == c.object_id

出力:

false
false
true
true

値で比較するために == を定義します。配列の include?、index、delete は == を使用するため、等しいオブジェクトも見つけられるようになります。まず other のクラスを確認して、数値や nil と比較した場合に失敗するのではなく false を返すようにします:

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)
puts a == Point.new(1, 2)
puts a == 5
puts [Point.new(0, 0), Point.new(1, 2)].include?(a)
puts a.equal?(Point.new(1, 2))

出力:

true
false
true
false

dup はインスタンス変数のコピーを持つ新しいオブジェクトを作成します。コピーに新しい値を代入しても元のオブジェクトには影響しませんが、内部の配列や文字列は 2 つのオブジェクト間で共有されたままです。これは、コピーされたのが参照だけだからです。

class Team
  attr_accessor :name, :players

  def initialize(name)
    @name = name
    @players = []
  end
end

a = Team.new("Reds")
b = a.dup
b.name = "Blues"
b.players << "Ada"
puts a.name
p a.players

出力:

Reds
["Ada"]
challenge icon

チャレンジ

簡単

クラス Point を完成させ、同じ座標を持つ2つの点が == で等しいと判定され、それ以外のものと点を比較した場合は false が返されるようにしてください。to_s は (1, 2) を返します。

用意されているコードは、点を x,y 形式の行として読み取ります。各点について (1, 2) same as first: true または false を出力し、その後 include? を使って異なる点のリストを保持し、最後に distinct: 3 と、最初の点がテキスト "1,2" と等しいかどうかを出力します。

コードは 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)) }
points.each { |pt| puts "#{pt} same as first: #{pt == points[0]}" }
distinct = []
points.each { |pt| distinct << pt unless distinct.include?(pt) }
puts "distinct: #{distinct.size}"
puts "equals text: #{points[0] == input[0]}"
quiz icon腕試し

このレッスンには短いクイズがあります。レッスンを始めて解答し、進捗を記録しましょう。

オブジェクト指向プログラミングのすべてのレッスン

自分で練習してみよう: Rubyオンラインコンパイラ