caseは(他の言語ではしばしばswitchと呼ばれます)、if-elseのような制御式の一種です。caseを使うと、複数のif-else-if文を連ねて書けるので、より読みやすくなり、強力な構文も使えます。
caseは、キーワードcaseと、それに続く省略可能な式で定義します。次に、それぞれのケースごとにキーワードwhenを使い、そのあとにcase式と比較される式を書きます。whenキーワードはcaseキーワードからインデントしないようにします。whenキーワードの後ろには、case式がwhen式と一致したときに実行されるコードを書きます。caseには省略可能なelse文があり、どのケースにも一致しなかったときに実行されます。
case式は評価されたあと、各when式と比較されます。比較にはcase包摂演算子(===)が使われます。
case 1
when 1
puts "One"
when 2
puts "Two"
else
puts "Other"
end
# => One
# This is the same as:
tmp = 1
if 1 === tmp
puts "One"
elsif 2 === tmp
puts "Two"
else
puts "Other"
end
===)case包摂演算子(===)は、等価演算子(==)とは少し違います。この演算子は、右辺が左辺の表す集合の要素であるかどうかを調べます。つまり、どちらのオペランドをどちら側に置くかが重要になります。どのように判定されるかは左辺の型によって変わり、たとえばRangeなら右辺がその範囲に入っているかどうかを、Objectなら右辺がそのObjectのインスタンスかどうかを調べます。
(1..3) == 1 # => false
(1..3) === 1 # => true
String == "foo" # => false
String === "foo" # => true
caseでは、1つのケースに複数の式を指定して一致させることができます。取りうる値はそれぞれカンマで区切ります。どれか1つでも一致すれば、そのコードが実行されます。1つのケースに複数の取りうる値があるときに便利です。
case 1
when 1, 2
puts "One or two"
else
puts "Other"
end
caseでは、値がある範囲に入っているかどうかも調べられます。そのためには、when式に範囲を指定します。
case var
when 1..3
puts "One to three"
else
puts "Other"
end
case式が不要なときは、省略できます。こうすると、それぞれのwhen式が真とみなされるかどうかで評価されるようになります。つまり、if-else-if文と同じように動作します。
case
when 1 == 1
puts "One is equal to one"
when 1 > 2
puts "One is greater than two"
else
puts "Other"
end
Crystalでは、case文を1行で書けます。シンプルな1行の文を書くときに使えます。1行で書くときは、when <expression> then <statement>.のように書きます。else文で使う場合は、else <statement>のように書きます。
case var
when 1 then puts "One"
when 2 then puts "Two"
else puts "Other"
end
caseでは、型によるマッチングができます。比較にはis_a?演算子を使って、変数の型を調べます。たとえば、ユニオン型のそれぞれの型ごとに分岐を1つずつ書けます。
case var
when Int32
puts "Int32"
when String
puts "String"
else
puts "Other"
end
caseでは、暗黙のオブジェクトを使えます。これにより、たとえばwhen式をcase式に対するメソッド呼び出しにできます。メソッドが真とみなされる値を返すかどうかで、そのケースが一致するかどうかが決まります。
case 1
when .odd?
puts "Odd"
when .even?
puts "Even"
end
この演習では、ブラックジャックのゲームの最初のターンをシミュレーションします。
2枚のカードが配られ、ディーラーの表向きのカードが1枚見えています。 カードはすべて、"ace"、"king"、"three"、"two"などの文字列で表されます。 各カードの値は次のとおりです。
| カード | 値 | カード | 値 |
|---|---|---|---|
| ace | 11 | eight | 8 |
| two | 2 | nine | 9 |
| three | 3 | ten | 10 |
| four | 4 | jack | 10 |
| five | 5 | queen | 10 |
| six | 6 | king | 10 |
| seven | 7 | other | 0 |
通常、エースは1または11の値を取ることができますが、ここでは簡単にするため、11の値しか取らないものとします。
カードを文字列として引数に取るBlackjack.parse_cardメソッドを実装してください。
このメソッドは、カードの値を整数で返します。
Blackjack.parse_card("ace")
# => 11
プレイヤーのスコアは、値の範囲に分類する必要があります。 プレイヤーのスコアは、プレイヤーの2枚のカードの値を合計して計算します。 使用する範囲は次のとおりです。
| 範囲 | 値 |
|---|---|
| low | [4, 11] |
| mid | [12, 16] |
| high | [17, 20] |
| blackjack | [21] |
2枚のカードを文字列として引数に取るBlackjack.card_rangeメソッドを実装してください。
このメソッドは、2枚のカードが該当する値の範囲の名前を返します。
Blackjack.card_range("ten", "king")
# => "high"
手持ちの2枚のカードとディーラーのカードに応じて、ゲームの最初のターンには戦略があり、次の選択肢があります。
まだ最適ではありませんが、友人のAlexが開発中の戦略に従います。その内容は次のとおりです。
3枚のカードを文字列として引数に取るBlackjack.first_turnメソッドを実装してください。
このメソッドは、取るべき判断を文字列で返します。
Blackjack.first_turn("ace", "ace", "two")
# => "P"