Rubyでは、+
や-
といった演算子も、メソッドとして定義されています。そのため、クラスに対してこれらの演算子を「再定義」することで、既存の機能を拡張したり、新たな動作を割り当てたりすることが可能です。
この方法を「演算子の再定義(オーバーロード)」と呼びます。
この記事では、Rubyでの演算子の再定義の基本的なやり方から、利用する際のポイントや注意点について、具体例とともに詳しく解説します!
キーワード:Ruby、演算子、オーバーロード
演算子の再定義とは?
そもそも、演算子の再定義とは何でしょうか?具体例を見ながら確認していきましょう。
p 1+1
上記の簡単なプログラムを実行した時を考えます。
出力は2
です。おそらく、これを読んでいる方は当たり前のことだろ!と思わずツッコミを入れてしまうと思います。
Rubyにおける演算子は、内部的にはメソッドとして扱われています。先ほど使用した+
の演算子は、+
というメソッドとして実行されます。つまり、1+1
というコードは1.+(b)
のようなメソッド呼び出しと同じ意味になります。このように演算子はクラスごとに定義されています。
演算子はメソッドとして扱われている!
もし、独自のクラスを作成した場合に+
や-
などの演算子を使用すると、定義されていないためエラーが発生します。
undefined method `+' for #<Point:0x0000000100c061f0 @x=1, @y=2> (NoMethodError)
そのため、クラスごとに演算子を定義してあげる必要があります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つです。
これにより、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
x
とy
の値が同じならTrueを、異なるならFalseを返します。
演算子の再定義をする際の注意点
演算子を再定義する上で以下のことに気をつけるとよいです。
- 直感的な動作を保つ
- 互換性
- 不要な再定義を避ける
再定義はその演算子の一般的な意味に沿うように行いましょう。例えば、+
は加算を意味するため、関連性のある動作を設定することが望ましいです。極端な話、+
なのに割り算の処理をされたら困ります。直感に背くような再定義をすると開発効率が下がってしまいます。
再定義によって、他のコードやライブラリとの互換性が崩れる可能性もあります。例えば、==
の再定義はeql?
やhash
メソッドの動作にも影響するため注意が必要です。
また、必要最小限に留めるべきです。無闇な再定義はコードの理解を困難にし、予期しないバグを招く原因になります。
まとめ
今回は、演算子再定義の方法について解説しました。Rubyの演算子再定義を活用することで、コードの可読性や表現力が向上します。
特に、数学的な演算やデータの集合操作を直感的に書けるようにすることで、コードがシンプルになり理解しやすくなります。
しかし、何でもかんでも再定義すればよいというわけではありません。直感的な動作や互換性に配慮しながら再定義は必要な場合にのみ行うのがベストです。
最後までご覧いただきありがとうございます!お疲れ様でした。
コメント