學習軌道
/
Swift
Swift
/
練習
/
調酒專家
調酒專家

調酒專家

學習練習

簡介

Switch

switch 敘述 讓你可以把一個值與多個可能的相符值做比較。相較於使用多個 else-if 敘述,它能讓你寫出更精簡的程式碼。switch 敘述以 switch 關鍵字開頭,後面接著要比較的值。接著你可以用 case 關鍵字定義各種情況,後面接著要比對的值。當沒有任何情況相符時,會用 default 關鍵字定義預設情況。和 else-if 敘述一樣,switch 敘述只會執行第一個相符情況所對應的程式區塊。

switch value {
case 1:
    print("One")
case 2:
    print("Two")
default:
    print("Other")
}

來看看下面這個例子:

if str == "apple" {
    print("Let's bake an apple crumble")
} else if str == "lemon" {
    print("Let's bake a lemon meringue pie!")
} else if str == "peach" {
    print("Let's bake a peach pie!")
} else {
    print("Let's buy ice cream.")
}

也可以改寫成:

switch str {
case "apple":
    print("Let's bake an apple crumble")
case "lemon":
    print("Let's bake a lemon meringue pie!")
case "peach":
    print("Let's bake a peach pie!")
default:
    print("Let's buy ice cream.")
}

While 迴圈

while 迴圈 是一種控制流程敘述,讓程式碼在特定條件為真時持續執行。Swift 提供兩種 while 迴圈:while 迴圈和 repeat-while 迴圈。while 迴圈的條件會在迴圈主體執行前先求值;如果條件為假,迴圈主體就完全不會執行。Swift 裡的 while 迴圈結構如下:

while <#condition#> {
  <#code#>
}

這裡的迴圈會在 count 大於 0 時持續執行,因此會依序印出數字 2、1 和 "Liftoff!"。

var count = 2
while count > 0 {
  print("\(count)…")
  count -= 1
}
print("Liftoff!")

// prints:
// 2…
// 1…
// Liftoff!

repeat-while 迴圈 和 while 迴圈類似,但條件是在迴圈主體執行後才求值。這代表迴圈主體至少會執行一次,即使條件為假也一樣。Swift 裡的 repeat-while 迴圈結構如下:

repeat {
  <#code#>
} while <#condition#>

這裡的迴圈只會執行一次,因為條件為假。由於它是 repeat-while 迴圈,迴圈主體會在條件求值前先執行一次。

var count = 2
repeat {
  print("\(count)…")
} while count < 0
print("Liftoff!")

控制轉移

Swift 有一組敘述可以用來改變迴圈正常的控制流程。這些敘述稱為控制轉移敘述。你已經看過 return;這個概念會介紹其他的,包括 continue、break 和標籤。這個概念不會涵蓋 fallthrough,但你可以在 Swift 書籍 中讀到相關內容。

Continue

有時候需要提早跳到迴圈的下一次疊代,而不完成目前這次疊代中其餘的敘述。這時可以使用 continue 關鍵字。執行 continue 時,迴圈會跳到下一次檢查,看看是否能執行下一次疊代,也就是 while 和 repeat-while 迴圈中的 while,或是 for-in 迴圈中檢查序列裡是否還有下一個元素。

count = 1
while count < 6 {
  count += 1
  if count == 4 { continue }
  print(count)
}

// prints:
// 2
// 3
// 5

Break

break 用來提早離開迴圈。執行 break 時,迴圈會立刻結束,程式會繼續執行迴圈後面的第一個敘述。

for fruit in ["banana", "grapes", "apple", "strawberry", "kiwi", "lemon"] {
  if !fruit.count.isMultiple(of: 2) { break }
  print(fruit)
}

// prints:
// banana
// grapes

標籤

當迴圈形成巢狀時,有時會想用 break 或 continue 來離開或重新開始外層迴圈。這些外層迴圈包住使用 break 或 continue 的那個迴圈。在這種情況下,可以用標籤指定要離開或重新開始的迴圈。只要在開始迴圈的 while、repeat 或 for 前面放上名稱加冒號,就能為迴圈加上標籤。

想看看這樣使用標籤的效果,請看下面這個迴圈。

outerLoop: for fruit in ["banana", "grapes", "apple", "strawberry", "kiwi", "lemon"] {
  print("\n--- \(fruit) ---")
  for letter in fruit {
    guard letter != "n" else { break outerLoop }
    print(letter)
  }
}

// prints:
// --- banana ---
// b
// a

說明

你是一位專業的調酒師,負責為夜晚外出放鬆的客人調製美味飲料。但你想到,只要寫一點程式碼來協助工作,就能讓日子輕鬆一些。

1. 判斷製作一份訂單需要多少時間

不同的飲料需要的準備時間不同:

  • "beer"、"soda"和"water"各需要 0.5 分鐘。
  • "shot"需要 1.0 分鐘,"mixed drink"需要 1.5 分鐘,
  • "fancy drink"需要 2.5 分鐘,
  • "frozen drink"需要 3.0 分鐘。

實作函式timeToPrepare(drinks:),它接受引數drinks,該引數是一個字串陣列,代表需要準備的飲料。 這個函式應回傳準備陣列中所有飲料所需的總時間。

timeToPrepare(drinks: ["beer", "frozen drink", "shot"])
// returns 4.5

2. 補充你的萊姆角存量

開始值班時,你要做的第一件事之一,就是確保裝萊姆角的小盒子為接下來的一班補滿。 萊姆有不同的大小,能切出的萊姆角數量也不同:一顆 "small" 萊姆可以切出 6 個萊姆角,一顆 "medium" 萊姆可以切出 8 個,一顆 "large" 萊姆則可以切出 10 個。

只要你還需要更多萊姆角,而且還有萊姆可以切,就從陣列中取出第一顆萊姆,並從你需要的數量中扣掉它對應的萊姆角數量。 持續這個過程,直到你不再需要任何萊姆角,或是萊姆用完為止。

實作函式makeWedges(needed:limes:),它接受引數needed,代表你需要切出的萊姆角數量,以及limes,代表一個字串陣列,內含你可以切的萊姆大小。 這個函式應回傳你總共切了幾顆萊姆。

makeWedges(needed: 25, limes: ["small", "small", "large", "medium", "small"])
// returns 4

3. 完成你的值班

恭喜!漫長的工作日快要結束了,你很快就能回家開發你的新應用程式。 但在你把訂單交接給下一班、離開之前,還有一連串的飲料訂單要做。

只要值班時間還有剩,你就要製作陣列中的第一筆訂單,然後把該筆訂單從陣列中移除。 接著,你可以依照函式timeToPrepare(drinks:)算出準備那筆訂單所花費的時間,並從剩餘的值班時間中扣掉。

實作函式finishShift(minutesLeft:remainingOrders:),它接受你值班剩餘的分鐘數(保證大於 0),以及一個非空的飲料訂單陣列。 這個函式應回傳在你值班結束前還來不及處理的訂單陣列。

finishShift(minutesLeft: 5, remainingOrders: [["beer", "frozen drink", "shot"], ["fancy drink", "soda"], ["beer", "beer", "water"], ["mixed drink", "frozen drink"]])
// returns [["beer", "beer", "water"], ["mixed drink", "frozen drink"]]

4. 追蹤特定的訂單

為了確保啤酒和汽水保持新鮮,你需要追蹤它們的訂單以及下單的時間。

實作函式orderTracker(orders: [(drink: String, time: String)]) -> (beer: (first: String, last: String, total: Int)?, soda: (first: String, last: String, total: Int)?),它在給定一份訂單與時間的清單時,回傳一對選擇性的元組,內含啤酒與汽水第一筆及最後一筆訂單的時間,以及各自的訂單總數;若沒有那些飲料的訂單,則值為nil。你不需要追蹤其他任何飲料。

實作函式orderTracker(orders:),它接受一個元組陣列,其中每個元組包含所點的飲料以及下單的時間。 這個函式應回傳一個元組,內含啤酒第一筆與最後一筆的下單時間、啤酒訂單總數、汽水第一筆與最後一筆的下單時間,以及汽水訂單總數。 其格式應為:(beer: (first: String, last: String, total: Int)?, soda: (first: String, last: String, total: Int)?)。

let orders = [
    (drink: "beer", time: "10:01"), (drink: "frozen drink", time: "10:02"),
    (drink: "shot", time: "10:05"), (drink: "fancy drink", time: "10:06"),
    (drink: "soda", time: "10:09"), (drink: "beer", time: "10:15"), (drink: "beer", time: "10:22"),
    (drink: "water", time: "10:26"), (drink: "mixed drink", time: "10:28"),
    (drink: "frozen drink", time: "10:33"),
]

orderTracker(orders: orders)

// returns (beer: (first "10:01", last "10:22", total 3), soda: (first "10:09", last "10:09", total 1))
透過 GitHub 編輯 連結會在新視窗或分頁中開啟
Swift Exercism

準備好開始 調酒專家 了嗎?

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