コンテンツへスキップ

case

caseは、パターンマッチングに少し似た機能を持つ制御式です。セマンティクスを少し変更し、さらに強力な構造を持つif-else-ifの連鎖を記述できます。

基本形では、ある値を他の値と比較できます

case exp
when value1, value2
  do_something
when value3
  do_something_else
else
  do_another_thing
end

# The above is the same as:
tmp = exp
if value1 === tmp || value2 === tmp
  do_something
elsif value3 === tmp
  do_something_else
else
  do_another_thing
end

式をcaseの対象と比較するために、コンパイラはケース包含演算子 ===を使用します。これはObjectのメソッドとして定義されており、サブクラスによってオーバーライドして、case文で意味のあるセマンティクスを提供できます。たとえば、Classは、オブジェクトがそのクラスのインスタンスである場合にケース包含を定義し、Regexは値が正規表現に一致する場合、Rangeは値がその範囲に含まれる場合に定義します。

whenの式が型である場合、is_a?が使用されます。さらに、case式が変数または変数代入である場合、変数の型が制限されます。

case var
when String
  # var : String
  do_something
when Int32
  # var : Int32
  do_something_else
else
  # here var is neither a String nor an Int32
  do_another_thing
end

# The above is the same as:
if var.is_a?(String)
  do_something
elsif var.is_a?(Int32)
  do_something_else
else
  do_another_thing
end

暗黙のオブジェクト構文を使用すると、when内でcase式のメソッドを呼び出すことができます。

case num
when .even?
  do_something
when .odd?
  do_something_else
end

# The above is the same as:
tmp = num
if tmp.even?
  do_something
elsif tmp.odd?
  do_something_else
end

when条件の後でthenを使用して、本文を1行に配置できます。

case exp
when value1, value2 then do_something
when value3         then do_something_else
else                     do_another_thing
end

最後に、caseの値を省略できます。

case
when cond1, cond2
  do_something
when cond3
  do_something_else
end

# The above is the same as:
if cond1 || cond2
  do_something
elsif cond3
  do_something_else
end

これにより、場合によってはより自然に読めるコードになることがあります。

タプルリテラル

case式がタプルリテラルである場合、when条件もタプルリテラルである場合、いくつかのセマンティックな違いがあります。

タプルのサイズは一致する必要がある

case {value1, value2}
when {0, 0} # OK, 2 elements
  # ...
when {1, 2, 3} # Syntax error: wrong number of tuple elements (given 3, expected 2)
  # ...
end

アンダースコアが許可される

case {value1, value2}
when {0, _}
  # Matches if 0 === value1, no test done against value2
when {_, 0}
  # Matches if 0 === value2, no test done against value1
end

暗黙のオブジェクトが許可される

case {value1, value2}
when {.even?, .odd?}
  # Matches if value1.even? && value2.odd?
end

型との比較は is_a? チェックを実行する

case {value1, value2}
when {String, Int32}
  # Matches if value1.is_a?(String) && value2.is_a?(Int32)
  # The type of value1 is known to be a String by the compiler,
  # and the type of value2 is known to be an Int32
end

網羅的なcase

whenの代わりにinを使用すると、網羅的なcase式が生成されます。網羅的なcaseでは、必要なin条件のいずれかを省略するとコンパイル時エラーになります。網羅的なcasewhenまたはelse句を含めることはできません。

コンパイラは、次のin条件をサポートしています。

共用体型のチェック

caseの式が共用体の値である場合、共用体の各型を条件として使用できます。

# var : (Bool | Char | String)?
case var
in String
  # var : String
in Char
  # var : Char
in Bool
  # var : Bool
in nil # or Nil, but .nil? is not allowed
  # var : Nil
end

Bool値

caseの式がBool値の場合、truefalseのリテラルを条件として使用できます。

# var : Bool
case var
in true
  do_something
in false
  do_something_else
end

Enum値

caseの式がフラグではないEnum値の場合、そのメンバーを定数または述語メソッドとして条件として使用できます。

enum Foo
  X
  Y
  Z
end

# var : Foo
case var
in Foo::X
  # var == Foo::X
in .y?
  # var == Foo::Y
in .z? # :z is not allowed
  # var == Foo::Z
end

タプルリテラル

条件は、case式の要素の可能なすべての組み合わせを網羅する必要があります

# value1, value2 : Bool
case {value1, value2}
in {true, _}
  # value1 is true, value2 can be true or false
  do_something
in {_, false}
  # here value1 is false, and value2 is also false
  do_something_else
end

# Error: case is not exhaustive.
#
# Missing cases:
#  - {false, true}