01 April 2013
Rubyの条件分岐をメソッド化する
(追記:2013-04-02) 4月1日が終わったのでタイトルを変えました。
変数の値が真か偽で制御を分岐する場合、Rubyでは普通「if-else」を使います。
truth = "ruby is now twenty years old."
lie = "ruby was merged with perl."
april_first = true
ruby_status =
if april_first
lie
else
truth
end
ruby_status # => "ruby was merged with perl."
条件分岐で返す式が短い場合、条件演算子「**? : **」もよく使われています。
ruby_status = april_first ? lie : truth
ruby_status # => "ruby was merged with perl."
しかしこれらの制御構造にはそれらが制御構造であるがゆえの欠点があります。
それは、そのままではメソッドチェーンにちょっと乗せづらいということです。
ruby_status = begin
if april_first
lie
else
truth
end
end.capitalize
ruby_status # => "Ruby was merged with perl."
ruby_status = (april_first ? lie : truth).capitalize
ruby_status # => "Ruby was merged with perl."
そんなわけで…
条件分岐をメソッド化した「_?」メソッドというものを考えてみました。
[true, false, nil].each do |obj|
obj.instance_eval do
define_singleton_method(:_?) do |*args, &blk|
arg = args[obj ? 0 : 1]
unless arg.nil?
arg
else
blk ? blk[self] : self
end
end
end
end
true, false, nilの各オブジェクトに_?メソッドを定義しています。このメソッドの第1引数にはselfがtrueのときに返す値を、第2引数にはselfがfalseのときに返す値を渡します。
先の例を_?メソッドを使って書き換えてみます。
truth = "ruby is now twenty years old."
lie = "ruby was merged with perl."
april_first = true
ruby_status = april_first._?(lie, truth).capitalize
ruby_status # => "Ruby was merged with perl."
april_first = false
ruby_status = april_first._?(lie, truth).capitalize
ruby_status # => "Ruby is now twenty years old."
_?はブロックを取ることができ、手続きを返したいときはブロックで実現します。ブロック引数にはselfが渡るので、ブロックの中で条件分岐して手続きを書きます。ちょっとダサいですけど。
april_first = true
april_first._? do |bool|
if bool
puts "*" * lie.size
puts lie.upcase
puts "*" * lie.size
else
puts "#" * truth.size
puts truth.upcase
puts "#" * truth.size
end
end
# >> **************************
# >> RUBY WAS MERGED WITH PERL.
# >> **************************
ということで、どうやらRubyはPerlにマージされたそうですよ。
blog comments powered by Disqus