二十一點

二十一點

學習練習

簡介

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 涵蓋運算子(===)

case 涵蓋運算子(===)和等號運算子(==)有點不同。這個運算子會檢查右邊是不是左邊所描述集合的成員。也就是說,每個運算元放在哪一側是有影響的。實際的行為取決於左邊的型別,例如Range會檢查右邊是否落在該範圍內,而Object則會檢查右邊是不是Object的實例。

(1..3) == 1  # => false
(1..3) === 1 # => true

String == "foo"  # => false
String === "foo" # => true

含有多個運算式的 case

一個分支可以比對多個運算式,只要用逗號分隔每個可能的值。只要其中任何一個運算式符合,就會執行對應的程式碼。當單一分支可能對應多個值時,這就很方便。

case 1
when 1, 2
  puts "One or two"
else
  puts "Other"
end

搭配範圍的 case

case 也能檢查某個值是否落在某個範圍內。只要把範圍當成 when 運算式就可以了。

case var
when 1..3
  puts "One to three"
else
  puts "Other"
end

省略 case 運算式

當不需要 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

單行的 when

Crystal 允許單行的 case 敘述,適合用來處理簡單的單行敘述。單行的 when 敘述寫成when <expression> then <statement>.,用在 else 敘述時則寫成else <statement>。

case var
when 1 then puts "One"
when 2 then puts "Two"
else puts "Other"
end

搭配型別的 case

case 也可以搭配型別來比對,比對時會使用is_a?運算子來檢查變數的型別。舉例來說,這讓聯合型別中的每個型別都能有自己的分支。

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

說明

在這個練習中,我們會模擬一局 Blackjack 遊戲的第一回合。

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
Note

一般來說,ace 可以取 1 或 11 的值,但為了簡化,我們假設 ace 只能取 11 的值。

實作Blackjack.parse_card方法,它會接受一張牌作為字串引數。 這個方法應該以整數回傳該張牌的值。

Blackjack.parse_card("ace")
# => 11

2. 為值範圍命名。

玩家的分數必須被歸類到不同的值範圍中。 玩家的分數是將兩張玩家牌的值相加而得。 所使用的範圍如下:

範圍 值
low [4, 11]
mid [12, 16]
high [17, 20]
blackjack [21]

實作Blackjack.card_range方法,它會接受兩張牌作為字串引數。 這個方法應該回傳這兩張牌所屬的值範圍名稱。

Blackjack.card_range("ten", "king")
# => "high"

3. 實作第一回合的決策邏輯。

根據你的兩張牌和莊家的牌,遊戲的第一回合有一套策略,你有以下選項:

  • 停牌(S)
  • 要牌(H)
  • 分牌(P)
  • 自動獲勝(W)

雖然還不是最佳策略,但你要遵循你朋友 Alex 一直在開發的策略,內容如下:

  • 如果你有一對 ace,就一定要分牌。
  • 如果你有 Blackjack(兩張牌加起來的值為 21),而莊家沒有 ace、人頭牌或 ten,你就會自動獲勝。 如果莊家有其中任何一張牌,你就得停牌,等待另一張牌揭曉。
  • 如果你的牌加起來的值落在 [17, 20] 這個範圍內,你就應該總是停牌。
  • 如果你的牌加起來的值落在 [12, 16] 這個範圍內,你應該總是停牌,除非莊家有 7 或更高,這種情況你就應該總是要牌。
  • 如果你的牌加起來是 11 或更低,你就應該總是要牌。

實作Blackjack.first_turn方法,它會接受三張牌作為字串引數。 這個方法應該以字串回傳你應該採取的決策。

Blackjack.first_turn("ace", "ace", "two")
# => "P"
透過 GitHub 編輯 連結會在新視窗或分頁中開啟
Crystal Exercism

準備好開始 二十一點 了嗎?

註冊 Exercism,透過 26 個概念142 個練習 和真人引導來學習並精通 Crystal,全部免費。