オブジェクトの値をチェックして、それに基づいて異なるアクションを実行する方法が必要だと想像してください。
その前に、case式がどのように動作するか、また、上記の目的を達成するためにどのように使用できるかを見てみましょう。
ケースの仕組み
casecasewhenの部分です。
when===case 句は、それに渡される引数を定義します。
例を見てみましょう。
case awhen String #..end
ですから、上の例はString === aString.===(a)と考えることができます。
最後に、マッチしない場合に使えるelseという句もあります。
case my_objectwhen String "This is a string"else "I have no idea what this is"end
三重の等号演算子 (===)
デフォルトでは、case===) 演算子を使って比較を行います。
===の問題点は、等値性とは何の関係もないことです。 デフォルトでは、通常は 2 つのオブジェクトが同等の値を持つかどうかをチェックする double equals 演算子 (==) にエイリアスされていますが、何かを意味するように定義することもできます。
例えば、レンジでは includes?matchis_a? の別名として、procでは call の別名として定義できます。
この演算子はリテラルでは期待どおりに動作しますが、クラスでは動作しません。
1 === 1 # => trueNumeric === Numeric # => false
それは、===kind_of?kind_of?のドキュメントです。
kind_of?(class) → true or false
classがobjのクラスであるか、classがobjのスーパークラスの一つかobjに含まれるモジュールであればtrueを返します。
つまり、オブジェクトのクラスに対してcase ... whenを実行したい場合、これは動作しません
obj = 'Hi'case obj.classwhen String "It's a string"end
それは、String === Stringに変換されるからです。
デフォルトの演算子
casewhen String===演算子が使われるので、とても便利です。
しかし、別の演算子を使いたい場合もあります。 Rubyではそれが可能です。
casewhen a < 3 "Smaller than 3"end
Case is an expression
名前が示すように、全体が式です。 つまり、値に評価されるということです。 ですから、case式全体の結果を、次のように変数に代入することができます。
value = case when a < 3 "Smaller than 3" end
フォールスルーはありません
他の言語をお使いの方は、switchcasebreakのように流れを断ち切るキーワードを使っていないことに気づいたはずです。
それは、caseにはフォールスルーがないからです。
複数のマッチ
これまで、when句には1つの値しか使用していませんでした。
case awhen 1..3 "Small number"end
正規表現のマッチング
case 式を使って何にでもマッチさせることができます。 しかし、念のために言うと、正規表現やラムダにもマッチさせることができます。
even = ->(x) { x % 2 == 0 }case awhen even "It's even"when /^+$/ "It's an integer"end
独自の定義
あまり知られていないことですが、独自のコンパレータを定義することができます。
Text = Struct.new(:min_length) do def ===(string) string.size > min_length && string.is_a?(String) endendcase awhen Text.new(100) "It's text"end
上記の例では、acaseIt's text を返します。
Use polymorphism instead
OOP の利点についてはここでは説明しませんが、詳細は Object-Oriented Programming with Ruby の記事をご覧ください。
ケース編
class Person attr_reader :country def initialize(country) @country = country end def nationality case country when "USA" "This guy is an American" when "Romania" "This guy is a Romanian" end endendjohn = Person.new("USA")puts john.nationality # => This guy is an American
OOP編
class Person attr_reader :country def initialize(country) @country = country end def nationality country.nationality endendclass America def nationality "This guy is an American" endendclass Romania def nationality "This guy is a Romanian" endendjohn = Person.new(America.new)puts john.nationality # => This guy is an American
フロー制御のコードをできるだけ削除するのは良いアイデアです(すなわち、ifsやcase式など)をできるだけ削除するのが良いでしょう。 これにより、単一責任原則の遵守が向上し、一般的にプログラムがより読みやすくなります。