條件語句
根據特定的條件執行特定的代碼通常是十分有用的。當錯誤發生時,你可能想運行額外的代碼;或者,當值太大或太小時,向用戶
顯示一條消息。要實現這些功能,你就需要使用條件語句。
Swift 提供兩種類型的條件語句: if 語句和 switch 語句。通常,當條件較爲簡單且可能的情況很少時,使用 if 語句。⽽ switch
語句更適用於條件較複雜、有更多排列組合的時候。並且 switch 在需要用到模式匹配(pattern- matching)的情況下會更有用。
If
if 語句最簡單的形式就是隻包含一個條件,只有該條件爲 true 時,才執行相關代碼:
var temperatureInFahrenheit = 30
if temperatureInFahrenheit <= 32 {
print("It's very cold. Consider wearing a scarf.")
}
// 輸出“It's very cold. Consider wearing a scarf.”
上面的例子會判斷溫度是否小於等於 32 華⽒度(水的冰點)。如果是,則打印一條消息;否則,不打印任何消息,繼續執⾏ if 塊後
⾯的代碼。
當然, if 語句允許二選一執⾏,叫做 else 從句。也就是當條件爲 false 時,執⾏ else 語句:
temperatureInFahrenheit = 40
if temperatureInFahrenheit <= 32 {
print("It's very cold. Consider wearing a scarf.")
} else {
print("It's not that cold. Wear a t-shirt.")
}
// 輸出“It's not that cold. Wear a t-shirt.”
顯然,這兩條分支中總有一條會被執⾏。由於溫度已升至 40 華氏度,不算太冷,沒必要再圍巾。因此, else 分⽀就被觸發了。
你可以把多個 if 語句鏈接在一起,來實現更多分支:
temperatureInFahrenheit = 90
if temperatureInFahrenheit <= 32 {
print("It's very cold. Consider wearing a scarf.")
} else if temperatureInFahrenheit >= 86 {
print("It's really warm. Don't forget to wear sunscreen.")
} else {
print("It's not that cold. Wear a t-shirt.")
}
// 輸出“It's really warm. Don't forget to wear sunscreen.”
在上面的例子中,額外的 if 語句用於判斷是不是特別熱。而最後的 else 語句被保留了下來,用於打印既不冷也不熱時的消息。
實際上,當不需要完整判斷情況的時候,最後的 else 語句是可選的:
temperatureInFahrenheit = 72
if temperatureInFahrenheit <= 32 {
print("It's very cold. Consider wearing a scarf.")
} else if temperatureInFahrenheit >= 86 {
print("It's really warm. Don't forget to wear sunscreen.")
}
在這個例子中,由於既不冷也不熱,所以不會觸發 if 或 else if 分支,也就不會打印任何消息。
Switch
switch 語句會嘗試把某個值與若干個模式(pattern)進⾏匹配。根據第一個匹配成功的模式, switch 語句會執⾏對應的代碼。當
有可能的情況較多時,通常用 switch 語句替換 if 語句。
switch 語句最簡單的形式就是把某個值與一個或若干個相同類型的值作比較:
switch some value to consider {
case value 1:
respond to value 1
case value 2,
value 3:
respond to value 2 or 3
default:
otherwise, do something else
}
switch 語句由多個 case 構成,每個由 case 關鍵字開始。爲了匹配某些更特定的值,Swift 提供了幾種方法來進⾏更複雜的模式匹配,
這些模式將在本節的稍後部分提到。
與 if 語句類似,每一個 case 都是代碼執⾏的一條分支。 switch 語句會決定哪一條分支應該被執⾏,這個流程被稱作根據給定的
值切換(switching)。
switch 語句必須是完備的。這就是說,每一個可能的值都必須至少有一個 case 分支與之對應。在某些不可能涵蓋所有值的情況
下,你可以使用默認( default )分支來涵蓋其它所有沒有對應的值,這個默認分支必須在 switch 語句的最後面。
下⾯的例子使用 switch 語句來匹配一個名爲 someCharacter 的小寫字符:
let someCharacter: Character = "z"
switch someCharacter {
case "a":
print("The first letter of the alphabet")
case "z":
print("The last letter of the alphabet")
default:
print("Some other character")
}
// 輸出“The last letter of the alphabet”
在這個例子中,第一個 case 分支用於匹配第一個英文字母 a ,第二個 case 分支用於匹配最後一個字母 z 。因爲switch 語句必
須有一個case 分支用於覆蓋所有可能的字符,而不僅僅是所有的英文字母,所以 switch 語句使用default 分支來匹配除了 a 和 z
外的所有值,這個分支保證了 swith 語句的完備性。
不存在隱式的貫穿
與 C 和 Objective-C 中的 switch 語句不同,在 Swift 中,當匹配的 case 分支中的代碼執⾏完畢後,程序會終⽌switch 語句,⽽
不會繼續執⾏下一個 case 分支。這也就是說,不需要在 case 分支中顯式地使用 break 語句。這使得 switch 語句更安全、更易
用,也避免了漏寫 break 語句導致多個語言被執⾏的錯誤。
注意
雖然在 Swift 中 break 不是必須的,但你依然可以在 case 分支中的代碼執⾏完畢前使用 break 跳出。
每⼀個 case 分支都必須包含至少一條語句。像下面這樣書寫代碼是⽆效的,因爲第一個 case 分支是空的:
let anotherCharacter: Character = "a"
switch anotherCharacter {
case "a": // ⽆效,這個分支下面沒有語句
case "A":
print("The letter A")
default:
print("Not the letter A")
}
// 這段代碼會報編譯錯誤
不像 C 語言里的 switch 語句,在 Swift 中, switch 語句不會一起匹配 "a" 和 "A" 。相反的,上面的代碼會引起編譯期錯誤: case
"a": 不包含任何可執⾏語句 ——這就避免了意外地從一個 case 分支貫穿到另外一個,使得代碼更安全、也更直觀。
爲了讓單個 case 同時匹配 a 和 A ,可以將這個兩個值組合成一個複合匹配,並且用逗號分開:
let anotherCharacter: Character = "a"
switch anotherCharacter {
case "a", "A":
print("The letter A")
default:
print("Not the letter A")
}
// 輸出“The letter A”
爲了可讀性,符合匹配可以寫成多⾏形式。
注意
如果想要顯式貫穿 case 分支,請使用 fallthrough 語句
區間匹配
case 分支的模式也可以是一個值的區間。下面的例子展示了如何使用區間匹配來輸出任意數字對應的⾃然語言格式:
let approximateCount = 62
let countedThings = "moons orbiting Saturn"
let naturalCount: String
switch approximateCount {
case 0:
naturalCount = "no"
case 1..<5:
naturalCount = "a few"
case 5..<12:
naturalCount = "several"
case 12..<100:
naturalCount = "dozens of"
case 100..<1000:
naturalCount = "hundreds of"
default:
naturalCount = "many"
}
print("There are \(naturalCount) \(countedThings).")
// 輸出“There are dozens of moons orbiting Saturn.”
在上例中, approximateCount 在一個 switch 聲明中被評估。每一個 case 都與之進⾏比較。因爲approximateCount 落在了 12
到 100的區間,所以 naturalCount 等於 "dozens of" 值,並且此後的執行跳出了 switch 語句。
元組
我們可以使用元組在同一個 switch 語句中測試多個值。元組中的元素可以是值,也可以是區間。另外,使用下劃線 ( _ )來匹配
所有可能的值。
下⾯的例子展示了如何使⽤一個 (Int, Int) 類型的元組來分類下圖中的點 (x, y):
let somePoint = (1, 1)
switch somePoint {
case (0, 0):
print("\(somePoint) is at the origin")
case (_, 0):
print("\(somePoint) is on the x-axis")
case (0, _):
print("\(somePoint) is on the y-axis")
case (-2...2, -2...2):
print("\(somePoint) is inside the box")
default:
print("\(somePoint) is outside of the box")
}
// 輸出“(1, 1) is inside the box”
在上面的例子中, switch 語句會判斷某個點是否是原點 (0, 0),是否在紅⾊的 x 軸上,是否在橘黃色的 y 軸上,是否在一個以原
點爲中⼼的 4x4 的藍色矩形里,或者在這個矩形外面。
不像 C 語言,Swift 允許多個 case 匹配同一個值。實際上,在這個例子中,點 (0, 0)可以匹配所有四個 case。但是,如果存在
多個匹配,那麼只會執⾏第一個被匹配到的 case 分支。考慮點 (0, 0)會首先匹配 case (0, 0) ,因此剩下的能夠匹配的分⽀都會
被忽視掉。
值綁定
case 分支允許將匹配的值聲明爲臨時常量或變量,並且在 case 分支體內使用 —— 這種⾏爲被稱爲值綁定(value binding),因
爲匹配的值在 case 分支體內,與臨時的常量或變量綁定。
下⾯的例子將下圖中的點 (x, y),使用 (Int, Int) 類型的元組表示,然後分類表示:
let anotherPoint = (2, 0)
switch anotherPoint {
case (let x, 0):
print("on the x-axis with an x value of \(x)")
case (0, let y):
print("on the y-axis with a y value of \(y)")
case let (x, y):
print("somewhere else at (\(x), \(y))")
}
// 輸出“on the x-axis with an x value of 2”
在上⾯的例子中, switch 語句會判斷某個點是否在紅色的 x 軸上,是否在橘⻩色的 y 軸上,或者不在座標軸上。
這三個 case 都聲明了常量 x 和 y 的佔位符,⽤於臨時獲取元組 anotherPoint 的一個或兩個值。第一個 case —— case (let x, 0)
將匹配一個縱座標爲 0 的點,並把這個點的橫座標賦給臨時的常量 x 。類似的,第二個 case —— case (0, let y) 將匹配一個橫坐
標爲 0 的點,並把這個點的縱座標賦給臨時的常量 y 。
一旦聲明了這些臨時的常量,它們就可以在其對應的 case 分支⾥使用。在這個例子中,它們用於打印給定點的類型。
請注意,這個 switch 語句不包含默認分支。這是因爲最後一個 case —— case let(x, y) 聲明瞭一個可以匹配餘下所有值的元
組。這使得 switch 語句已經完備了,因此不需要再書寫默認分支。
Where
case 分支的模式可以使用 where 語句來判斷額外的條件。
下⾯的例子把下圖中的點 (x, y)進⾏了分類:
let yetAnotherPoint = (1, -1)
switch yetAnotherPoint {
case let (x, y) where x == y:
print("(\(x), \(y)) is on the line x == y")
case let (x, y) where x == -y:
print("(\(x), \(y)) is on the line x == -y")
case let (x, y):
print("(\(x), \(y)) is just some arbitrary point")
}
// 輸出“(1, -1) is on the line x == -y”
在上面的例子中, switch 語句會判斷某個點是否在綠色的對角線 x == y 上,是否在紫色的對角線 x == -y 上, 或者不在對⻆線上。
這三個 case 都聲明了常量 x 和 y 的佔位符,⽤於臨時獲取元組 yetAnotherPoint 的兩個值。這兩個常量被用作where 語句的一
部分,從而創建一個動態的過濾器(filter)。當且僅當 where 語句的條件爲 true 時,匹配到的 case 分⽀纔會被執⾏。
就像是值綁定中的例子,由於最後一個 case 分支匹配了餘下所有可能的值, switch 語句就已經完備了,因此不需要再書寫默認
分支。
複合型 Cases
當多個條件可以使用同一種⽅法來處理時,可以將這幾種可能放在同一個 case 後面,並且用逗號隔開。當 case 後⾯的任意一
種模式匹配的時候,這條分支就會被匹配。並且,如果匹配列表過長,還可以分行書寫:
let someCharacter: Character = "e"
switch someCharacter {
case "a", "e", "i", "o", "u":
print("\(someCharacter) is a vowel")
case "b", "c", "d", "f", "g", "h", "j", "k", "l", "m",
"n", "p", "q", "r", "s", "t", "v", "w", "x", "y", "z":
print("\(someCharacter) is a consonant")
default:
print("\(someCharacter) is not a vowel or a consonant")
}
// 輸出“e is a vowel”
這個 switch 語句中的第一個 case,匹配了英語中的五個小寫元音字母。相似的,第⼆個 case 匹配了英語中所有的⼩寫輔音字
母。最終, default 分支匹配了其它所有字符。
複合匹配同樣可以包含值綁定。複合匹配⾥所有的匹配模式,都必須包含相同的值綁定。並且每一個綁定都必須獲取到相同類型
的值。這保證了,⽆論複合匹配中的哪個模式發生了匹配,分⽀體內的代碼,都能獲取到綁定的值,並且綁定的值都有一樣的類
型。
let stillAnotherPoint = (9, 0)
switch stillAnotherPoint {
case (let distance, 0), (0, let distance):
print("On an axis, \(distance) from the origin")
default:
print("Not on an axis")
}
// 輸出“On an axis, 9 from the origin”
上⾯的 case 有兩個模式: (let distance, 0) 匹配了在 x 軸上的值, (0, let distance) 匹配了在 y 軸上的值。 兩個模式都綁定了
distance ,並且 distance 在兩種模式下,都是整型——這意味着分支體內的代碼,只要 case 匹配,都可以獲取到 distance 值。