Rubyにおける演算子の再定義(オーバーロード)とは?

演算子のオーバーロードを学ぼう Rails

Rubyでは、+-といった演算子も、メソッドとして定義されています。そのため、クラスに対してこれらの演算子を「再定義」することで、既存の機能を拡張したり、新たな動作を割り当てたりすることが可能です。

この方法を「演算子の再定義(オーバーロード)」と呼びます。

この記事では、Rubyでの演算子の再定義の基本的なやり方から、利用する際のポイントや注意点について、具体例とともに詳しく解説します!

キーワード:Ruby、演算子、オーバーロード

演算子の再定義とは?

事前準備をしよう!

そもそも、演算子の再定義とは何でしょうか?具体例を見ながら確認していきましょう。

p 1+1

上記の簡単なプログラムを実行した時を考えます。

出力は2です。おそらく、これを読んでいる方は当たり前のことだろ!と思わずツッコミを入れてしまうと思います。

Rubyにおける演算子は、内部的にはメソッドとして扱われています。先ほど使用した+の演算子は、+というメソッドとして実行されます。つまり、1+1というコードは1.+(b)のようなメソッド呼び出しと同じ意味になります。このように演算子はクラスごとに定義されています。

演算子はメソッドとして扱われている!

もし、独自のクラスを作成した場合に+-などの演算子を使用すると、定義されていないためエラーが発生します

undefined method `+' for #<Point:0x0000000100c061f0 @x=1, @y=2> (NoMethodError)

そのため、クラスごとに演算子を定義してあげる必要があります1。また、すでに定義してある演算子もオーバーロードすることで、オリジナルの機能を持たせることもできます。

  1. 演算子を使わない場合は定義しなくても問題ない ↩︎

再定義の基本について

演算子の再定義について理解しました。次は、再定義をどのように行えばよいか確認していきます。

ここでは、2次元の座標を表すPointクラスを作成し、+演算子を再定義していきます

2次元空間における2点の加算を考えた時、期待する処理はそれぞれの成分同士を足し合わせることだと思います。OAベクトル+OBベクトルはそれぞれの成分同士に対して、普通の演算子+を適用させています。

class Point
  attr_accessor :x, :y

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

  def +(other)
    Point.new(self.x + other.x, self.y + other.y)
  end

  def to_s
    "(#{x}, #{y})"
  end
end

この例では、Pointクラスの+演算子を再定義し、Pointインスタンス同士を加算して新しいPointオブジェクトを返すようにしています。

やり方は、再定義したい演算子をメソッド名にして、普通のメソッドを定義するかのように記述してあげるだけです。この時、引数も忘れないようにしましょう。今回は二項演算子であるため、引数は1つです。

1+11.+(1)と同じ意味でした。よって引数は1つです!

これにより、point1 + point2のように直感的に座標を加算できるようになりました。

また、上記コードを見るとto_sメソッドが再定義されています。内容は、Pointクラスのオブジェクトを出力する時は”(x, y)“とすることです。これによって、Pointクラスのオブジェクトがさらに使いやすくなっています。

減算演算子-の場合も考えてみましょう!

解答例
def -(other)
    Point.new(@x-other.x, @y-other.y)
end

上記コードは一例に過ぎません。出力をよく確認してみましょう。

余裕のある方は以下の練習問題に挑戦してみましょう!解いている時間がない方は以下の練習問題の章は飛ばしても大丈夫です。

練習問題

再び、Pointクラスについて扱います。Pointクラスは現在、以下のように定義されています。

class Point
  attr_accessor :x, :y

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

 def +(other)
    Point.new(self.x + other.x, self.y + other.y)
  end

  def -(other)
    Point.new(self.x - other.x, self.y - other.y)
  end

  def to_s
    "(#{x}, #{y})"
  end
end

Pointクラスは2次元空間の座標を示すクラスで、x座標とy座標の値をそれぞれ保存しています。

Pointクラスに、同じ座標かどうかを判定する二項演算子==を再定義してください。ただし、同じ座標とは、xとyの値が両方ともに一致することを意味します。

解答例

解答例は以下のようになります。

def ==(other)
    self.x == other.x && self.y == other.y
end

xyの値が同じならTrueを、異なるならFalseを返します。

演算子の再定義をする際の注意点

演算子を再定義する上で以下のことに気をつけるとよいです。

  • 直感的な動作を保つ
  • 互換性
  • 不要な再定義を避ける

再定義はその演算子の一般的な意味に沿うように行いましょう。例えば、+は加算を意味するため、関連性のある動作を設定することが望ましいです。極端な話、+なのに割り算の処理をされたら困ります。直感に背くような再定義をすると開発効率が下がってしまいます。

再定義によって、他のコードやライブラリとの互換性が崩れる可能性もあります。例えば、==の再定義はeql?hashメソッドの動作にも影響するため注意が必要です。

また、必要最小限に留めるべきです。無闇な再定義はコードの理解を困難にし、予期しないバグを招く原因になります

まとめ

この記事のまとめ

今回は、演算子再定義の方法について解説しました。Rubyの演算子再定義を活用することで、コードの可読性表現力が向上します。

特に、数学的な演算やデータの集合操作を直感的に書けるようにすることで、コードがシンプルになり理解しやすくなります。

しかし、何でもかんでも再定義すればよいというわけではありません。直感的な動作や互換性に配慮しながら再定義は必要な場合にのみ行うのがベストです

最後までご覧いただきありがとうございます!お疲れ様でした。

コメント

タイトルとURLをコピーしました