Ruby
 Computer >> コンピューター >  >> プログラミング >> Ruby

Rubyのcaseステートメント徹底解説 ― 知られざる高度なマッチング術

caseステートメントほどシンプルで地味な構文はありません。C言語から受け継いだ構文で、「大量のif文を置き換えるためのもの」と思われがちです。それ以上の何者でもないのでしょうか?実は違います。

Rubyのcaseステートメントは、想像以上に豊かで奥深い機能を備えています。まずは次の例を見てください。

case "Hi there"
when String
  puts "case statements match class"
end

# 出力: "case statements match class"

この例が示すように、caseステートメントはオブジェクトのだけでなく、そのクラスまで判定できます。これが可能なのは、内部で===演算子(いわゆる「トリプルイコール」)を使用しているためです。

===演算子の基礎知識

Rubyでx === yと書くとき、これは「yはxが表すグループに属するか?」という問いになります。非常に汎用的な意味を持つ演算子であり、具体的な挙動は対象となる「グループ」の種類によって変わります。

# Class#===(item) が呼ばれ、item がそのクラスのインスタンスなら true を返す

String === "hello" # => true
String === 1       # => false

文字列(String)、正規表現(Regexp)、範囲(Range)などは、それぞれ独自の===(item)メソッドを定義しており、直感的に予想どおりの挙動をします。さらに、自分で作ったクラスにもトリプルイコールを実装できます。

この仕組みを理解すれば、caseステートメントでさまざまなテクニックが使えるようになります。

範囲(Range)とのマッチング

range === nrange.include?(n)と同じ結果を返すため、caseステートメントで範囲をそのまま使えます。公式ドキュメントにも明記されている仕様です。

case 5
when (1..10)
  puts "case statements match inclusion in a range"
end

# 出力: "case statements match inclusion in a range"

数値の判定だけでなく、時刻や文字列長など、範囲として表現できるあらゆる条件を簡潔に書けるのが魅力です。

正規表現とのマッチング

/regexp/ === "string"は、文字列が正規表現にマッチした場合にのみtrueを返します。Regexpクラスのドキュメントにも記載されているとおり、caseステートメントで正規表現をパターンとして使えるのです。

case "FOOBAR"
when /BAR$/
  puts "they can match regular expressions!"
end

# 出力: "they can match regular expressions!"

入力値の形式チェックやルーティング処理など、文字列パターンで分岐したい場面で非常に強力です。

Procとラムダとのマッチング

少し変わった使い方として、Procもあります。Proc#===(item)Proc#call(item)と同じ働きをするため、ラムダやprocをcaseステートメントの「動的なマッチャー」として利用できるのです。

case 40
when -> (n) { n.to_s == "40" }
  puts "lambdas!"
end

# 出力: "lambdas"

複雑な条件式をその場で定義して渡せるため、「どんな条件でもcaseのwhen節に書きたい」というニーズに応えられる柔軟性があります。

独自のマッチャークラスを作る

前述のとおり、自作クラスに===メソッドを定義するだけで、独自のcase挙動を追加できます。活用例のひとつが、複雑な条件ロジックを小さなクラスへ切り出すリファクタリングです。以下にそのイメージを示します。

class Success
  def self.===(item)
    item.status >= 200 && item.status < 300
  end
end

class Empty
  def self.===(item)
    item.response_size == 0
  end
end

case http_response
when Empty
  puts "response was empty"
when Success
  puts "response was a success"
end

条件の意味をクラス名で表現できるため、if文を羅列するよりも可読性と保守性が大きく向上します。「HTTPレスポンスの判定」「バリデーションの分類」など、ドメイン固有の条件分岐にぜひ試してみてください。

まとめ

Rubyのcaseステートメントは、単なるif文の代替品ではありません。背後にある===演算子の仕組みを理解すれば、クラス・範囲・正規表現・Proc、さらには独自クラスまで、あらゆるものをパターンとして扱えます。宣言的で読みやすい分岐処理を書くための強力な武器として、ぜひ活用してみましょう。

  1. JavaScriptのswitchステートメント内でreturnを使う方法|サンプルコード付きで解説

    switchステートメント内でreturnステートメントは使えるのか?結論から言うと、switchステートメントが関数の中にある場合は、returnステートメントを含めることができます。returnが実行されると、その時点で関数の処理が終了し、switchステートメントで指定された値が呼び出し元へ返されます。そのため、switchステートメントより後ろに記述したコードは実行されません。さらに、returnは関数自体を抜ける働きを持つため、各caseの末尾にbreakステートメントを記述する必要がない点も見逃せません。breakの書き忘れによる意図しないフォールスルー(次のcaseへの処理の漏れ

  2. C++における条件分岐の基礎:if-else文とswitch文の使い方を徹底解説

    条件分岐(意思決定)構造では、プログラマーがプログラムに評価・判定させる1つ以上の条件を指定し、その条件が「真(true)」と判定された場合に実行する文、さらに必要に応じて、条件が「偽(false)」と判定された場合に実行する別の文を記述する必要があります。 以下は、多くのプログラミング言語に共通する、典型的な条件分岐構造の一般的な流れを示した図です。 if-else文とは if文の後には、オプションとしてelse文を続けることができます。else文は、ifのブール式が「偽」と判定された場合に実行されます。C++におけるif...else文の基本構文は以下の通りです。 if(boolean