演算子オーバーロードとは
Rubyの演算子の多くはインスタンスメソッドで実装されているので、新たに定義して意味を変える(再定義)ことができます。これをオーバーロードと言います。
Rubyでは演算子のオーバーロードが認められており、ある演算子を特定のプログラムでどのように使用するかを定義することができます。
例えば、「+」演算子を定義すると、足し算の代わりに引き算にしたり、その逆も可能です。オーバーロードできる演算子は +, -, /, *, **, % などで、オーバーロードできない演算子は &, &&, |, ||, (), {}, ~ などです。
演算子関数は、通常の関数と同じです。
唯一の違いは、演算子関数の名前は常に演算子オブジェクトに続く演算子のシンボルであることです。演算子関数は、対応する演算子が使用されたときに呼び出されます。演算子のオーバーロードは可換ではないので、3 + a と a + 3 は同じではありません。
以下は、Rubyの演算子のオーバーロードの例です。
オーバーロードの例
算術演算子
class Member
attr_accessor:name, :number
def initialize(name, number)
@name = name
@number = number
end
def +(obj)
return Member.new("#{self.name}#{obj.name}",
"#{self.number}#{obj.number}")
end
end
a = Member.new("Ogino", "0")
b = Member.new("Fujiwara", "2")
puts (a+b).inspect
# 出力結果
# <Member:0x000000010a07df18 @name="OginoFujiwara", @number="02">
このように、「+」演算子がオーバーロードされているため、名前と番号の2つの文字列を連結した出力が返されます。
同じコードの別の例ですが、以下は「+」演算子の代わりに「/」演算子をオーバーロードしています。
class Member
attr_accessor:name, :number
def initialize(name, number)
@name = name
@number = number
end
def /(obj)
return Member.new("#{self.name}#{obj.name}",
"#{self.number}#{obj.number}")
end
end
a = Member.new("Ogino", "0")
b = Member.new("Fujiwara", "2")
puts (a/b).inspect
# 出力結果
# <Member:0x000000010829e628 @name="OginoFujiwara", @number="02">
上記のケースでは、連結を行うために「/」演算子をオーバーロードしているので、出力が同じであることがわかります。したがって、通常の使用法に関係なく、任意の演算子をオーバーロードすることができます。
比較演算子
以下の例では、比較演算子をオーバーロードしてみます。
(注: ここでは、Ruby の Comparable モジュールを使用します。Ruby では、Comparable モジュールは、オブジェクトを順序付けることができるクラスで使用されます。受け手が他のオブジェクトより小さければ -1、受け手が他のオブジェクトと等しければ 0、受け手が他のオブジェクトより大きければ 1 を返します)
class Comparable_operator
include Comparable
attr_accessor:name
def initialize(name)
@name=name
end
def <=>(obj)
return self.name<=>obj.name
end
end
a = Comparable_operator.new("Gold ship")
b = Comparable_operator.new("Orfevre")
puts a<=>b
# 出力結果
# -1
上記の例では、ASCIIコード ‘G'(ASCII=71) は ‘O'(ASCII=79) よりも小さいので、71が79よりも大きいかどうかをチェックした後、出力は-1となります。(注意: =, ==, の演算子を使ってチェックすることもできます)
同じコードで別の例を示しますが、今回は実際の文字列を比較します。
class Comparable_operator
include Comparable
attr_accessor:name
def initialize(name)
@name=name
end
def <=>(obj)
return self.name<=>obj.name
end
end
puts "Gold ship"<=>"Orfevre"
# 出力結果
# -1
上記の例では、ASCIIコード「G」が「O」よりも小さいため、出力は「-1」となります。
整数値
以下の例では、演算子を整数値でオーバーロードしてみます。
class Test
attr_accessor:num
def initialize(num)
@num = num
end
def +(obj)
return @num+obj
end
def *(obj)
return @num*obj
end
def **(obj)
return @num**obj
end
end
a = Test.new(5)
puts a + 3
puts a * 3
puts a ** 3
# 出力結果
# 8
# 15
# 125
class Test
attr_accessor:num
def initialize(num)
@num = num
end
def +(obj)
return self.num+obj.num
end
def *(obj)
return self.num*obj.num
end
def **(obj)
return self.num**obj.num
end
end
a = Test.new(5)
b = Test.new(4)
puts a + b
puts a * b
puts a ** b
# 出力結果
# 9
# 20
# 625
要素参照演算子
以下の例では、要素参照演算子のオーバーロードしてみます。
(注意: ‘+=’ 演算子は + 演算子を介して定義する必要があります。つまり、’+’ 演算子を定義するだけで、コンパイラは自動的に ‘+=’ と ‘<<‘ 演算子が配列の最後に要素を追加する意味でそれを使用します)
class Array_Operators
attr_accessor:arr
def initialize(*arr)
@arr = arr
end
def [](x)
@arr[x]
end
def [](x, value)
@arr[x] = value
end
def <<(x)
@arr << x
return ('#{@arr}')
end
end
a = Array_Operators.new(0, 3, 9, 27, 81)
puts a[4]
a[5] = 51
puts a[5]
puts a << 41
puts a[6]
# 出力結果
# 81
# 51
# [0, 3, 9, 27, 81, 51, 41]
# 41
このように、演算子は定義通りに動作し、配列のすべての要素が表示されていることがわかります。
Rubyの演算子のほとんどは、必要に応じて簡単にオーバーロードすることができます。