Rubyのcase文はwhen節に渡すオブジェクトによって振る舞いが異なる
今回は Ruby の case 文についてです。
Ruby の case 文はどのように値を比較するか
Ruby の case 文では case 節に比較対象となる値を、when 節に case 節の値と比較する値を記述します。
んで、when 節の値をレシーバとして、===
メソッドによって比較が行われ、最初に真となった when 節に続く処理が実行されます。
hoge = 'hoge' case hoge when 'foo' puts 'foooo!' when 'bar' puts 'baaar!' when 'hoge' puts 'hogeee!' else puts "わかりまへーん" end #=> hogeee!
このように===
で比較するわけで、数値や文字列を比較する時は単純に==
的な比較を行います。
しかし、実はいくつかのクラスでは===
の振る舞いが異なります。具体的にはRange
, Regexp
, Proc
, Module
とClass
です。
Range クラスのオブジェクトと比較する場合
score = 43 case score when (1..40) puts '追試ですよー' when (41..80) puts '微妙ですよー' when (81..1000) puts 'いい感じですよー' end #=> 微妙ですよー
Range
の===
では引数が自身の範囲内に含まれていたら真を返します。今回はscore = 43
なので追試は間逃れています。
Regexp クラスのオブジェクトと比較する場合
lang = 'ruby' case lang when /php/ puts 'phpですよー' when /ruby/ puts 'rubyですよー' else puts 'そんなの知りませんよー' end #=> rubyですよー
Regexp
の===
は引数の文字列がマッチした場合に真を返します。今回は簡単な例ですが、ちゃんと正規表現を書けばかなり柔軟な比較ができそうです。
Proc クラスのオブジェクトと比較する場合
obj = 'hoge' case obj when proc { |x| x.is_a? String } puts 'Stringでしたよー' when proc { |x| x.is_a? Numeric } puts 'Numericでしたよー' else puts 'よくわからないですよー' end #=> Stringでしたよー
Proc
の===
では右辺の値を引数にしてProc
の処理を実行します。
よって上記のように条件判断のためのProc
オブジェクトを仕込んでおくと、case に渡された値を引数として処理を実行するので、柔軟な条件分岐が可能になります。
Class や Module クラスのオブジェクトと比較する場合
obj = 'hoge' case obj when String puts 'Stringでしたよー' when Numeric puts 'Numericでしたよー' else puts 'よくわからないですよー' end #=> Stringでしたよー
Class
やModule
の===
では右辺のオブジェクトが左辺のクラス(もしくはそのサブクラス)のインスタンスだった場合、真を返します。
なのでString === String
はfalse
ですが、String === 'hoge'
はtrue
になります。
これは地味に間違えそうなやつですね。
まとめ
クラスによって比較の仕方が違うというのは、知らないとハマりポイントになりそうなので注意が必要です。また、例えばクラスの異なるオブジェクト同士を比較する場合は、どちらをレシーバにするかによって結果が変わる可能性もあるのでそこも注意が必要そうです。
今回も参考にしたのは安定のパーフェクト Ruby でございました。