case式の対象オブジェクトのメソッドの戻り値を条件に使用する
うん、タイトルだけだと、意味わからん、という感じかと、、、
わかるかな?
なんて書いたらいいのか、悩んだので、
とりあえず、書いてみました。
コードで示した方が分かりやすいかと。
※やりたいと思った事(以下のコードは間違ってます)
case hoge when foo? (hoge.foo?がtrueだった時の処理) when "bar" ... end
みたいな事がやりたかったのです。
しかし、まぁ、当然、↑のコードはエラーになります。
さて、どうしたものか。
こんなことはできないのでしょうか?
if hoge.foo? ... else case hoge when "bar" ... end end
このコード、ダサくないですか?
クールじゃないですよね。
調べました。
結果、できました。
※以下のコードはRuby1.9以降
case hoge when :foo?.to_proc ... when "bar" ... end
これがクールかと言われると、微妙な所ですが、
少なくとも、ifで分岐するよりはスマートかと思います。
さて、では、なぜ↑の式は成り立つのでしょうか?
case式は、制御構造にもあるとおり
===演算子を使ったif〜elsif〜end式とほぼ等価な処理を行うように実装されています。
つまり、
if :foo?.to_proc === hoge ... elsif "bar" === hoge ... end
と、ほぼ等価なんですね。
ここからが大切です。
まずは、「Object#===」って何?って事です。
class Object
ここを見てみると、
サブクラスで再定義されない限りは、「Object#==」を呼び出すだけのようです。
では、再定義されてる組み込みクラスはあるのか? あります。
以下の4つです。
- Module
- kind_of?を呼んでいるだけ
- Proc
- callの別名
- Range
- include?を呼んでいるだけ
- Regexp
- =~やmatchとほぼ同じ
以前、ブログに書きましたが、
Symbolのto_procで生成されるProcオブジェクトは、
callの引数に渡されたオブジェクトをレシーバとして、
シンボル名と同じ名前のメソッドを呼び出します。
そして、↑にもあるとおり、
Procの===はcallの別名です。
つまり、
:foo?.to_proc === hoge
は
:foo?.to_proc.call(hoge)
ということになるわけです。
なので、
case hoge when :foo?.to_proc ...① when "bar" ...② end
これで、foo?がtrueであれば、①の部分が評価されるわけです。
うん。納得。