一から勉強させてください

最下級エンジニアが日々の学びをアウトプットしていくだけのブログです

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, ModuleClassです。

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でしたよー

ClassModule===では右辺のオブジェクトが左辺のクラス(もしくはそのサブクラス)のインスタンスだった場合、真を返します。 なのでString === Stringfalseですが、String === 'hoge'trueになります。

これは地味に間違えそうなやつですね。

まとめ

クラスによって比較の仕方が違うというのは、知らないとハマりポイントになりそうなので注意が必要です。また、例えばクラスの異なるオブジェクト同士を比較する場合は、どちらをレシーバにするかによって結果が変わる可能性もあるのでそこも注意が必要そうです。

今回も参考にしたのは安定のパーフェクト Ruby でございました。