再定義できる演算子 で組み込み演算子風のメソッドを定義する
概要
再定義できる演算子 で組み込み演算子風のメソッドを定義する
Ruby の演算子はメソッドのシンタックスシュガー
多くの言語では論理演算子や算術演算子などは、言語の組み込み機能であり、
通常の関数やメソッドとは別の扱いであると思います。
Ruby の場合、これらの演算子の多くが、ただのメソッドとして定義されています。
+
演算子を利用した文字列の結合の例
例えば Ruby で文字列を結合する際に利用される + 演算子は実際の所
String#+ という String のメソッドに過ぎません。
print "hoge" + "hage" # => hogehage
# 上記は以下のシンタックスシュガー
print "hoge".+("hage") # => hogehage
下記ページの再定義可能な演算子(メソッド)の記号については、ドットを省略して呼び出すことで演算子の見た目にすることができます
るりま | 演算子式
- 再定義できる演算子(メソッド)
| ^ & <=> == === =~ > >= < <= << >>
+ - * / % ** ~ +@ -@ [] []= ` ! != !~
+
演算子を利用した配列の結合の例
例えば Ruby で配列を結合する際に利用される + 演算子は実際の所
Array#+ という Array のメソッドに過ぎません。
print [1, 2, 3] + [4, 5, 6] # => [1, 2, 3, 4, 5, 6]
# 上記は以下のシンタックスシュガー
print [1, 2, 3].+([4, 5, 6]) # => [1, 2, 3, 4, 5, 6]
演算子のオーバーライド
メソッドに過ぎないということは、オーバーライドも可能です。
Ruby の特徴的な機能であるオープンクラスを利用して、 1
String#+ をオーバーライドしてみます。
数字のみで構成される文字列同士の + の呼び出し時のみ
数値演算を行うようにしてみます。
puts "hoge" + "hage" # => hogehage
puts "1" + "2" # => 12
class String
NUMBER_REGEXP = /^\d+$/
def +(other)
# 数値同士の場合のみ数値演算として加算
if number?(self) && number?(other)
self.to_i + other.to_i
# 数値同士の場合以外は文字列演算として結合
else
"#{self}#{other}"
end
end
private
def number?(value)
value =~ NUMBER_REGEXP
end
end
puts "hoge" + "hage" # => hogehage
puts "1" + "2" # => 3
自作クラスに演算子風のメソッドを追加してみる
Team クラスを作成して
-
+
: メンバーの追加 -
-
: メンバーの削除 -
<<
: チームまるごとメンバーに追加
というクラスを作成してみます。
ソースコード
class Team
attr_reader :members
def initialize(members = [])
@members = members
end
# メンバーの追加
def +(member)
@members << member
end
# メンバーの削除
def -(member)
@members.delete(member)
end
# チームの追加
def <<(other)
fail 'error' unless other.respond_to?(:members)
@members += other.members
end
end
team_a = Team.new(%w(tanaka sato suzuki))
print team_a.members, "\n"
team_a + 'obokata'
print team_a.members, "\n"
team_a - 'obokata'
print team_a.members, "\n"
team_b = Team.new(%w(matz larry guido))
team_a << team_b
print team_a.members, "\n"
begin
team_a << "not Team class"
print team_a.members, "\n"
rescue => e
puts e.message
end
実行結果
["tanaka", "sato", "suzuki"]
["tanaka", "sato", "suzuki", "obokata"]
["tanaka", "sato", "suzuki"]
["tanaka", "sato", "suzuki", "matz", "larry", "guido"]
error
演算子風メソッドのユースケース
- 演算子風のメソッドを定義した方が、コードとして自然にみえる、理解しやすい場合
- 内部 DSL で演算子風のメソッドを提供したほうが設定として自然にみえる場合
外部資料
脚注
-
Rubyでは(組み込みクラスを含め)定義済みクラスにメソッドを追加・変更することができます。 ↩