(追記: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
ruby_pack8

100円〜で好評発売中!
M'ELBORNE BOOKS