53.計算字符
在字符串中獲取字符值的數量, 可以使用字符串字符屬性中的計數屬性:
let unusualMenagerie = "Koala ??, Snail ??, Penguin ??, Dromedary ??"
print("unusualMenagerie has \(unusualMenagerie.characters.count) characters")
// 打印 "unusualMenagerie has 40 characters"
注意 Swift 對字符值使用擴展字形集,意味著字符拼接和修改可能不會總是影響字符串的字符數。
例如, 如果你用四個字符的單詞咖啡來初始化一個新字符串, 然后添加 COMBINING ACUTE ACCENT (U+0301) 到字符串的尾部, 最后字符的數量還是4, 第四個字符是 é, 而不是 e:
var word = "cafe"
print("the number of characters in \(word) is \(word.characters.count)")
// 打印 "the number of characters in cafe is 4"
word += "\u{301}"? ? // COMBINING ACUTE ACCENT, U+0301
print("the number of characters in \(word) is \(word.characters.count)")
// 打印 "the number of characters in café is 4"
54.訪問和修改字符串
你可以通過方法和屬性來訪問和修改字符串, 或者用下標語法。
55.字符串索引
每個字符串值都有一個對應的索引類型, String.Index, 代表每個字符在字符串中的位置。
上面提到的,不同字符要求不等數量的內存, 所以為了決定哪個字符在一個特定的位置, 你必須從頭到尾枚舉每個 Unicode 標量。 因為這個原因, Swift 字符串不能通過整數值來索引。
使用 startIndex 屬性訪問字符串的首字符位置。 endIndex 屬性是字符串中最后一個字符的位置。 因此, endIndex 屬性不是一個字符串下標的有效參數。 如果一個字符串是空的, startIndex 和 endIndex 相等。
你可以用 index(before:) 和 index(after:) 字符串方法訪問給定索引前后的索引, 你可以使用 index(_:offsetBy:) 方法代替多次調用這些方法。
你可以使用下標語法訪問特定索引位置的字符。
let greeting = "Guten Tag!"
greeting[greeting.startIndex]
// G
greeting[greeting.index(before: greeting.endIndex)]
// !
greeting[greeting.index(after: greeting.startIndex)]
// u
let index = greeting.index(greeting.startIndex, offsetBy: 7)
greeting[index]
// a
嘗試訪問越界的索引和字符都會引發運行時的錯誤。
greeting[greeting.endIndex] // Error
greeting.index(after: greeting.endIndex) // Error
用字符屬性中的索引屬性獲取字符串中的所有字符。
for index in greeting.characters.indices {
print("\(greeting[index]) ", terminator: "")
}
// 打印 "G u t e n? T a g ! "
56.插入和移除
在特定位置向字符串插入一個字符, 使用 insert(_:at:) 方法, 插入其他字符串內容到制定索引使用 insert(contentsOf:at:) 方法。
var welcome = "hello"
welcome.insert("!", at: welcome.endIndex)
// welcome 等于 "hello!"
welcome.insert(contentsOf:" there".characters, at: welcome.index(before: welcome.endIndex))
// welcome 現在等于 "hello there!"
從指定索引移除字符, 使用 remove(at:) 方法, 在指定范圍移除一個字串, 用 removeSubrange(_:) 方法:
welcome.remove(at: welcome.index(before: welcome.endIndex))
// welcome 現在等于 "hello there"
let range = welcome.index(welcome.endIndex, offsetBy: -6)..
備注
你可以使用 insert(:at:), insert(contentsOf:at:), remove(at:), 和 removeSubrange(:) 這些方法,只要符合 RangeReplaceableCollection 協議的任何類型。 這包括 String, 還有集合類型 Array, Dictionary, 和 Set.
57.比較字符串
Swift 提供三種方式來比較文本值: 字符串和字符等式, 前綴等式, 和后綴等式。
字符串和字符等式
字符串和字符等式用“equal to” 運算符 (==) 和 “not equal to” 運算符 (!=) 來判斷, 在比較運算符中描述:
let quotation = "We're a lot alike, you and I."
let sameQuotation = "We're a lot alike, you and I."
if quotation == sameQuotation {
print("These two strings are considered equal")
}
// 打印 "These two strings are considered equal"
兩個字符串的值 (或者兩個字符的值) 如果他們的擴展字形集相同,就被認為是相等的。 擴展字形集如果有相同的語義和表現形式,就是相等的, 即使他們背后是由不同 Unicode 標量組成。
比如, LATIN SMALL LETTER E WITH ACUTE (U+00E9) 常規等于 LATIN SMALL LETTER E (U+0065) 加上 COMBINING ACUTE ACCENT (U+0301). 兩種擴展字形集都是有效的方式來表示字符 é, 隱藏它們被認為相等:
// "Voulez-vous un café?" using LATIN SMALL LETTER E WITH ACUTE
let eAcuteQuestion = "Voulez-vous un caf\u{E9}?"
// "Voulez-vous un café?" using LATIN SMALL LETTER E and COMBINING ACUTE ACCENT
let combinedEAcuteQuestion = "Voulez-vous un caf\u{65}\u{301}?"
if eAcuteQuestion == combinedEAcuteQuestion {
print("These two strings are considered equal")
}
// 打印 "These two strings are considered equal"
相反, LATIN CAPITAL LETTER A (U+0041, 或者 “A”), 英語中使用, 不等于俄語中使用的 CYRILLIC CAPITAL LETTER A (U+0410, 或者 “А”)。 這兩個字符看上去相似, 但是語義不同:
let latinCapitalLetterA: Character = "\u{41}"
let cyrillicCapitalLetterA: Character = "\u{0410}"
if latinCapitalLetterA != cyrillicCapitalLetterA {
print("These two characters are not equivalent.")
}
// 打印 "These two characters are not equivalent."
字符串和字符比較在Swift中不是地區敏感的。
58.前綴和后綴等式
判斷字符串是否有一個特定前綴或者后綴, 調用字符串的 hasPrefix(:) 和 hasSuffix(:) 方法, 兩個方法都一個 String 類型,然后返回一個布爾值。
下面這個例子有一個字符串數組,用來表示場景的位置,它們來自莎士比亞的羅密歐與朱麗葉的前兩個表演:
let romeoAndJuliet = [
"Act 1 Scene 1: Verona, A public place",
"Act 1 Scene 2: Capulet's mansion",
"Act 1 Scene 3: A room in Capulet's mansion",
"Act 1 Scene 4: A street outside Capulet's mansion",
"Act 1 Scene 5: The Great Hall in Capulet's mansion",
"Act 2 Scene 1: Outside Capulet's mansion",
"Act 2 Scene 2: Capulet's orchard",
"Act 2 Scene 3: Outside Friar Lawrence's cell",
"Act 2 Scene 4: A street in Verona",
"Act 2 Scene 5: Capulet's mansion",
"Act 2 Scene 6: Friar Lawrence's cell"
]
你可以用 hasPrefix(_:) 方法用 romeoAndJuliet 數組來技術表演1中的場景數:
var act1SceneCount = 0
for scene in romeoAndJuliet {
if scene.hasPrefix("Act 1 ") {
act1SceneCount += 1
?}
}
print("There are \(act1SceneCount) scenes in Act 1")
// 打印 "There are 5 scenes in Act 1"
相似的, 用 hasSuffix(_:) 方法來計算發生在 Capulet’s mansion 和 Friar Lawrence’s cell 的場景數:
var mansionCount = 0
var cellCount = 0
for scene in romeoAndJuliet {
if scene.hasSuffix("Capulet's mansion") {
mansionCount += 1
? } else if scene.hasSuffix("Friar Lawrence's cell") {
cellCount += 1
? }
}
print("\(mansionCount) mansion scenes; \(cellCount) cell scenes")
// 打印 "6 mansion scenes; 2 cell scenes"
59.Unicode 字符串表示
當一個 Unicode 字符串寫入文本文件或者別的存儲時,字符串里的 Unicode 標量會以一些Unicode 定義的編碼形式進行編碼。 每種字符串編碼在人們熟知的代碼單元塊中。 包括 UTF-8 編碼 (把字符串編碼成一個 8-位的代碼單元), UTF-16 編碼 (把字符串編碼成一個16-位的代碼單元), 和 UTF-32 編碼 (把字符串編碼成 32-位代碼單元)
Swift 提供了幾種不同的方式去訪問字符串的Unicode 形式。你可以用for-in語句遍歷字符串, 訪問作為Unicode 擴展字形集的單個字符值。這個過程在 使用字符 中描述。
或者, 或者用以下三種形式來訪問字符串的值:
UTF-8 代碼單元集合 (用字符串的utf8屬性來訪問)
UTF-16 代碼單元集合 (用字符串的utf16屬性來訪問)
21-位 Unicode 標量值的集合, 等于字符串的 UTF-32 編碼方式 (用字符串的 unicodeScalars 屬性訪問)
下面的例子展示字符串的不同表現形式,字符串由字符 D, o, g, ? (DOUBLE EXCLAMATION MARK, 或者 Unicode scalar U+203C), 和 ?? 字符 (DOG FACE, 或者 Unicode scalar U+1F436)組成:
let dogString = “Dog???”
60.UTF-8 形式與UTF-16 形式
UTF-8:
你可以通過遍歷字符串的utf8屬性來訪問它的 UTF-8 表現形式。 這是屬性是 String.UTF8View, 這是無符號的 8-位 (UInt8)值的集合, 每個字節一個:
for codeUnit in dogString.utf8 {
print("\(codeUnit) ", terminator: "")
}
print("")
// 68 111 103 226 128 188 240 159 144 182
上面這個例子, 前三個十進制 codeUnit 值 (68, 111, 103) 代表字符 D, o, 和 g, 它們的 UTF-8 形式和它們的 ASCII 表現形式一樣。 接下來三個十進制 codeUnit 值 (226, 128, 188) 是雙感嘆號的三個字節的 UTF-8 形式。 最后四個 codeUnit 值 (240, 159, 144, 182) 是個四個字節的 UTF-8 形式,代表小狗的臉字符。
UTF-16:
你可以通過遍歷字符串的utf16屬性來訪問它的 UTF-16 形式。 這個屬性是 String.UTF16View, 它是無符號16-位 (UInt16)的值的集合, 每個16-位代碼單元一個
for codeUnit in dogString.utf16 {
print("\(codeUnit) ", terminator: "")
}
print("")
// 打印 "68 111 103 8252 55357 56374 "
同樣, 前三個 codeUnit 值 (68, 111, 103) 代表字符 D, o, 和 g, 它們的 UTF-16 代碼單元和它們的 UTF-8 形式一樣(因為這些 Unicode 標量表示 ASCII 字符).
第四個 codeUnit 值 (8252) 是十六進制 203C的十進制數值, 用 Unicode 標量 U+203C表示雙感嘆號字符。 在 UTF-16 里表示為一個單獨的編碼單元。
第五六個 codeUnit 值 (55357 和 56374) 狗臉字符。 這些值是高八位 U+D83D (十進制值是 55357) 和一個第八位 U+DC36 (十進制值 56374)。
61.Unicode 標量形式
你可以通過遍歷字符串的 unicodeScalars 屬性來訪問它的Unicode 標量形式。 這個是屬性是 UnicodeScalarView,它是類型 UnicodeScalar 值的集合。
每個 UnicodeScalar 有一個值屬性,它返回這個標量的 21-位值, 用一個 UInt32 值表示:
for scalar in dogString.unicodeScalars {
print("\(scalar.value) ", terminator: "")
}
print("")
// 打印 "68 111 103 8252 128054 "
前三個 UnicodeScalar 值 (68, 111, 103) 再次代表 D, o, 和 g.
第四個 codeUnit 值 (8252) 是十六進制 203C 的十進制值, 是雙感嘆的 Unicode 標量 U+203C 。
第五個和最后一個 UnicodeScalar 值, 128054, 是十六進制 1F436的十進制值, 是狗臉字符的的 Unicode 標量 U+1F436。
作為查詢它們值屬性的替代, 每個 UnicodeScalar 值也可以用來構建新的字符串, 例如用做字符串插值:
for scalar in dogString.unicodeScalars {
print("\(scalar) ")
}
// D
// o
// g
// ?
// ??
62.集合類型
Swift 提供了三個主要的集合類型, 數組, 集合, 和字典, 用來存儲值的集合。 數組是有序的。 集合是唯一值的無序集。 字典是健值匹配的無序集合。
數組, 集合, 和字典在 Swift 里總是清楚的知道存儲的值的類型和能夠存儲的健。 這就意味著你不能往集合里插入錯誤類型的值。也意味著 你知道可以從集合里或者的值的類型。
備注
Swift 的數組, 集合, 和字典類型是用泛型集合實現的。更多泛型和集合, 參見泛型。
63.集合的不穩定性
如果你創建了一個數組, 一個集合, 或者一個字典, 然后賦給一個變量。創建的這個集合就是可變的。這就意味著你可以改變這個集合,方式是通過添加, 移除, 或者改變集合里的項。如果把它們賦給一個常量, 這個集合就是不可改變的, 它的大小和內容都不能改變。
備注
如果集合不需要變化,在所有情況下創建不可變的集合是個好的實踐。這樣做的好處是讓你容易理解自己的代碼,同時讓編譯器可以優化你創建的集合。
64.數組及數組類型縮寫語法
數組
有序存儲同類型的值。 相同的值可以多次出現在數組的不同位置。
Swift的數組類型跟 Foundation的 NSArray 類相橋接。
數組類型縮寫語法
T數組類型的全寫法是 Array\, 這里Element是數組運行存儲的值類型。 你可以縮寫數組類型為[Element]. 盡管這兩種形式功能一樣, 縮寫是優先考慮的而且這個教程后面一直這么寫。
創建空數組
你可以用初始化語法創建一個特定類的空數組:
var someInts = [Int]()
print("someInts is of type [Int] with \(someInts.count) items.")
// 打印 "someInts is of type [Int] with 0 items."
注意 someInts 變量的類型經過初始化類型被推斷為 [Int]
或者, 如果上下文已經提供類型信息, 例如一個函數參數或者一個確定類型的變量或者常量, 你可以用空的字面量來創建空數組, 這個數組通常寫作 [] (一個空的中括號對):
someInts.append(3)
// someInts now contains 1 value of type Int
someInts = []
// someInts is now an empty array, but is still of type [Int]
用默認值創建數組
Swift的數組類型也提供了一構造器來創建相同默認值固定大小的數組。 給構造器傳一個匹配類型的默認值 (調用 repeating): 然后是在新數組中需要重復的值的個數 (調用 count):
var threeDoubles = Array(repeating: 0.0, count: 3)
// threeDoubles 是 [Double]類型, 等于 [0.0, 0.0, 0.0]
通過合并數組創建數組
你可以通過合并兩個已存在的數組來創建新的數組,這兩個數組只要類型匹配就可以通過加法運算符 (+)來合并。 新數組的類型從合并的數組可以推斷出來:
var anotherThreeDoubles = Array(repeating: 2.5, count: 3)
// anotherThreeDoubles is of type [Double], and equals [2.5, 2.5, 2.5]
var sixDoubles = threeDoubles + anotherThreeDoubles
// sixDoubles is inferred as [Double], and equals [0.0, 0.0, 0.0, 2.5, 2.5, 2.5]
用字面量創建數組
你也可以用字面量來初始化數組, 這是種快速寫一個或者多個數組值的方式。 一個數組字面量寫作一列值, 用逗號分開, 一對方括號包括起來:
[value 1, value 2, value 3]
下面的的例子創建了一個購物列表來存儲字符串值:
var shoppingList: [String] = ["Eggs", "Milk"]
// shoppingList 初始有兩項
購物列表變量定義為 “字符串值的數組”, 寫作 [String]. 因為這個數組已經指定一個字符串類型值, 它就值運行存儲字符串值。 這里, 購物列表數組用兩個字符串值 (“Eggs” 和 “Milk”)來初始化, 寫在數字字面量里。
備注
這個數組被聲明為一個變量而不是一個常量,是因為在下面的例子將有更多的項目加入這個購物列表。
這種情況, 數組字面量只包含兩個字符串值。 這個匹配購物列表變量的聲明類型 (只能包括字符串值的數組), 用數組字面量賦值來初始化購物列表是被允許的一種方式。
由于 Swift 的類型推斷, 如果你用相同類型的字面量來初始化數組,就不需要寫出數組的類型。購物列表的初始化可以簡寫成以下形式:
var shoppingList = ["Eggs", "Milk"]
因為數組里所有的值都是同樣的類型, Swift 能夠推斷出 [String] 用作購物列表變量是正確的類型。
訪問和修改數組
你可以通過數組的方法,屬性或者下標語法來訪問和修改一個數組。
為了確定數組的項數, 使用只讀屬性count:
print("The shopping list contains \(shoppingList.count) items.")
// 打印 "The shopping list contains 2 items."
使用布爾屬性 isEmpty 來判斷數量屬性是否0:
if shoppingList.isEmpty {
print("The shopping list is empty.")
} else {
print("The shopping list is not empty.")
}
// 打印 "The shopping list is not empty."
通過調用 append(_:) 方法你可以在數組最后添加新項:
shoppingList.append("Flour")
// shoppingList 現在有3項
或者, 使用賦值運算 (+=) 添加一個或多個符合的項:
shoppingList += ["Baking Powder"]
// shoppingList 現在有4項
shoppingList += ["Chocolate Spread", "Cheese", "Butter"]
// shoppingList 現在有7項
用下標語法從數組中獲取一個值, 在數組后的方括號里傳入你想得到值的索引:
var firstItem = shoppingList[0]
// firstItem is equal to “Eggs”
備注
數組第一項的索引是0,不是1。 Swift 數組總是基于0開始索引。
你可以使用下標語法去改變給定索引下的值:
shoppingList[0] = "Six eggs"
// 第一項現在是 "Six eggs" 而不是 "Eggs"
你還可以通過下標語法一次改變一個范圍的值, 盡管取代值跟被取代的長度不同。 下面的例子用 “Bananas” 和 “Apples” 取代了 “Chocolate Spread”, “Cheese”, 和 “Butter”:
shoppingList[4...6] = ["Bananas", "Apples"]
// shoppingList 現在包含 6項
備注
你不能用下標語法添加新的項到數組最后。
在特定位置插入新項, 調用數組插入方法 insert(_:at:):
shoppingList.insert("Maple Syrup", at: 0)
// shoppingList now contains 7 items
// "Maple Syrup" is now the first item in the list
調用 insert(_:at:) 方法在購物列表頭插入一個新的項 “Maple Syrup”, 用索引0標明。
類似的, 移除一項使用 remove(at:) 方法。 這個方法移除指定索引的項然后返回這個移除項 (盡管你可能無視這個返回值):
let mapleSyrup = shoppingList.remove(at: 0)
// 位于索引0的項被移除
// 購物列表現在包含6項, 沒有 Maple Syrup
// mapleSyrup 常量現在等于被移除的 "Maple Syrup" 字符串
備注
如果越界訪問數組的值, 你會觸發運行時錯誤。你可以用它跟數組個數屬性對比來判斷索引的有效性。除了個數為0的情況 (意味著數組是空的), 最大索引值通常是數組個數 - 1, 因為數組是從0開始計數的。
一個數據項被移除數組空隙就會閉合,所以索引 0 的值再次等于 “Six eggs”:
firstItem = shoppingList[0]
// firstItem 現在等于 "Six eggs"
如果你想移除最后一項, 使用 removeLast() 方法而不是 remove(at:) 方法,這樣就避免需要查詢數組的個數屬性。和 remove(at:) 方法一樣, removeLast() 返回移除的項:
let apples = shoppingList.removeLast()
// 最后一項被移除
// 購物列表現在包含5項, 沒有蘋果
// apples 常量現在等于被移除的 "Apples" 字符串
65.遍歷數組
你可以用for-in循環來遍歷數組中的所有值集:
for item in shoppingList {
print(item)
}
// Six eggs
// Milk
// Flour
// Baking Powder
// Bananas
如果需要數據項的值和整數索引, 使用 enumerated() 方法來遍歷數組。對于數組中的每一項, enumerated() 方法返回一個包含索引和值的元組。 整數以0開始每項遞增; 如果你遍歷整個數組, 這些整數匹配數據項的索引。你可以分解元組到臨時的常量或者變量,來作為迭代的部分:
for (index, value) in shoppingList.enumerated() {
print("Item \(index + 1): \(value)")
}
// Item 1: Six eggs
// Item 2: Milk
// Item 3: Flour
// Item 4: Baking Powder
// Item 5: Bananas
66.集合
集合無序存儲同樣類型的不同值。 如果排序不重要可以選擇使用集合代替數組, 或者你需要確保每項只出現一次。
Swift 的 Set 類型跟 Foundation 的 NSSet 類橋接。
集合類型的哈希值
集合里一個類型為了存儲就必須可哈希—就是說, 這個類型必須提供一個方式去計算自己的哈希值。 一個哈希值是一個整型值, 這對于相等的對象都是一樣的, 如果 if a == b, 那么 a.hashValue == b.hashValue.
所有 Swift 的基本類型默認都是可哈希的 (比如 String, Int, Double, 和 Bool), 可以被用作集合值類型或者字典建類型。 枚舉分支值沒有相應的值默認也是可哈希的。
你可以使用自定義類型作為集合值類型或者字典健類型,通過讓它們遵守Swift 標準庫中的 Hashable 協議。 符合協議的類型必須提供一個可獲取的整形屬性 hashValue. 在相同程序中的不同執行,或者在不同程序中, 類型的哈希屬性返回的值不要求相同。
因為 Hashable 協議符合 Equatable, 符合類型必須提等號運算符 (==)的實現。 Equatable 協議要求任何符合==實現的都是一個相等關系。 就是說, == 的實現必須滿足下面的條件, 對于 a, b, 和 c 所有值:
a == a (自反性)
a == b 表示 b == a (對稱性)
a == b && b == c 表示 a == c (傳遞性)
集合類型語法
集合類型寫作 Set, 這里 Element 是集合允許存儲的類型。 跟數組不同, 集合沒有等價的縮寫形式。
創建和初始化空集合
你可以通過使用構造器語法創建特定類型的空集合:
var letters = Set()
print("letters is of type Set with \(letters.count) items.")
// 打印 "letters is of type Set with 0 items."
備注
letters 變量的類型推斷為 Set, 通過構造器的類型。
或者, 如果上下文已經提供了類型信息, 比如一個函數參數或者一個已經指定類型的變量和常量, 你可以用一個空的數組字面量來創建一個空集合:
letters.insert("a")
// letters 現在包含了一個字符類型的值
letters = []
// letters 現在是一個空集合, 但是依然是 Set 類型
用數組字面量創建集合
你可以用數組字面量初始化一個集合, 這是一個速寫方式來給一個集合賦值。
下面的例子創建一個存儲字符串值的集合 favoriteGenres:
var favoriteGenres: Set = ["Rock", "Classical", "Hip hop"]
// favoriteGenres 有了三個初始值
favoriteGenres 變量聲明成 “字符串值的集合”, 寫作 Set. 因為這個集合指定了一個字符串的值類型, 它只允許存儲字符串值, favoriteGenres 集合用三個字符串 (“Rock”, “Classical”, 和 “Hip hop”)來初始化, 寫在一個數組字面量里。
備注
favoriteGenres 集合聲明成一個變量而不是一個常量, 這是因為添加和移除的項在下面的示例里。
集合通過數組字面量不能推斷出類型, 所以類型必須顯示聲明。 不過, 由于Swift的類型推斷, 如果數組字面量包含的值類型相同,你就不需要寫集合的類型。favoriteGenres 初始化可以簡寫如下:
var favoriteGenres: Set = ["Rock", "Classical", "Hip hop"]
因為數組字面量里的值都是同樣類型, Swift 可以推斷 Set 用作favoriteGenres 變量是正確的類型。
訪問和修改集合
你可以通過集合的方法和屬性來訪問和修改它。
為了得到集合的項數, 使用只讀的 count 屬性:
print("I have \(favoriteGenres.count) favorite music genres.")
// 打印 "I have 3 favorite music genres."
使用布爾屬性 isEmpty 快速判斷 count 屬性是否等于 0:
if favoriteGenres.isEmpty {
print("As far as music goes, I'm not picky.")
} else {
print("I have particular music preferences.")
}
// 打印 "I have particular music preferences."
調用集合的 insert(_:) 方法插入一個新項:
favoriteGenres.insert("Jazz")
// favoriteGenres 現在包含 4 項
你可以用集合的 remove(_:) 方法從集合里移除項, 如果它是集合一員的話, 然后返回被移除的項, 如果集合不包含這項就返回nil。 或者, 移除所有項可以用 removeAll() 方法。
if let removedGenre = favoriteGenres.remove("Rock") {
print("\(removedGenre)? I'm over it.")
} else {
print("I never much cared for that.")
}
// 打印 "Rock? I'm over it."
檢查集合是否包含特定項, 用 contains(_:) 方法。
if favoriteGenres.contains("Funk") {
print("I get up on the good foot.")
} else {
print("It's too funky in here.")
}
// 打印 "It's too funky in here."
遍歷集合
你可以用for-in循環遍歷集合的值。
for genre in favoriteGenres {
print("\(genre)")
}
// Jazz
// Hip hop
// Classical
Swift 的集合類型是無序的。以特定順序去遍歷集合的值, 使用 sorted() 方法, 這個方法返回一個值數組,按照小于運算符排序。
for genre in favoriteGenres.sorted() {
print("\(genre)")
}
// Classical
// Hip hop
// Jazz
集合操作
你可以有效進行基礎的集合操作, 比如合并兩個集合, 判斷兩個集合是否有共同值, 或者判讀兩個集合是否包含所有幾個或者沒有任何相同值。
基本集合操作
下面的圖描述了兩個集合a 和 b, 共享區域表示集合操作結果。
用 intersection(:) 方法創建交集。
用 symmetricDifference(:) 方法創建交集之外的集合。
用 union(:) 方法創建并集。
用 subtracting(:) 方法創建去掉指定集合的集合。
let oddDigits: Set = [1, 3, 5, 7, 9]
let evenDigits: Set = [0, 2, 4, 6, 8]
let singleDigitPrimeNumbers: Set = [2, 3, 5, 7]
oddDigits.union(evenDigits).sorted()
// [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
oddDigits.intersection(evenDigits).sorted()
// []
oddDigits.subtracting(singleDigitPrimeNumbers).sorted()
// [1, 9]
oddDigits.symmetricDifference(singleDigitPrimeNumbers).sorted()
// [1, 2, 9]
# 集合和等式
下面的圖描述了三個集合 a, b 和 c, 重疊區域表示集合共享的元素。 集合a是集合b的超集, 因為a包含b的所有元素。 相反, 集合b是集合a的子集, 因為b中的元素都包含在集合a中。 集合b與集合c不相交, 因為它們沒有共同的元素。
用相等運算符 (==) 判斷兩個集合是否所有值都相同。
用 isSubset(of:) 方法判斷一個集合是否是另外一個集合的字集。
用 isSuperset(of:) 方法判斷一個集合是否是另外一個集合的超集。
用 isStrictSubset(of:) 或者 isStrictSuperset(of:) 方法判斷一個集合是否是一個子集或者超集, 但是不等于一個指定的集合。
用 isDisjoint(with:) 方法判斷是否兩個集合不相交。
let houseAnimals: Set = ["??", "??"]
let farmAnimals: Set = ["??", "??", "??", "??", "??"]
let cityAnimals: Set = ["??", "??"]
houseAnimals.isSubset(of: farmAnimals)
// true
farmAnimals.isSuperset(of: houseAnimals)
// true
farmAnimals.isDisjoint(with: cityAnimals)
// true
68.字典
字典無序存儲匹配的同類型的健值。每個值對應一個唯一的健, 它作為值在字典里的標識符。 跟數組項不同, 字典里的項是無序的。如果基于標識符查找值你可以用字典, 很像真實世界字典的用法。
字典類型速記語法
Swift 字典類型完整寫法是 Dictionary, Key 是可以用作字典值的值類型, Value 是字典為健存儲的值類型。
你也可以字典類型的簡寫形式 [Key: Value]. 盡管兩個形式功能一樣, 簡寫形式優先使用并貫穿整個教程。
創建一個空字典
跟數組一樣, 你可用用初始化語法創建一個特定類型的字典:
var namesOfIntegers = [Int: String]()
// namesOfIntegers is an empty [Int: String] dictionary
這個例子定義了一個空字典,類型是 [Int: String],用來存儲人類可讀的整數名字。健是整形, 值是字符串類型。
如果上下文已經提供了類型信息, 你可以用一個空的字典字面量來創建一個空字典, 寫作 [:] (一對中括號中間一個冒號):
namesOfIntegers[16] = "sixteen"
// namesOfIntegers 現在包含 1 key-value pair
namesOfIntegers = [:]
// namesOfIntegers 再次成為空,類型是[Int: String]
用字面量創建字典
你還可以用字面量來初始化一個字典, 跟早前數組字面量語法相似。字典字面量是定義一個或者多個鍵值對集合的速寫方法。
鍵值對是健和值的組合。 在一個字典字面量里, 健和值在鍵值對里用冒號分開。 鍵值對寫作一個列表, 用逗號分開, 包含在一對中括號里:
[key 1: value 1, key 2: value 2, key 3: value 3]
下面的例子創建一個字典來存儲國際機場名字。在這個字典里, 健是三個字母的國際航空公司編碼, 值是機場名:
var airports: [String: String] = ["YYZ": "Toronto Pearson", "DUB": "Dublin"]
airports 字典類型是 [String: String], 意思是 “一個字典健值都是字符串類型”.
備注:airports 字典聲明成一個變量而非常量, 因為下面的例子需要添加更多機場進去。
airports 用兩個鍵值對字面量初始化。第一個鍵值對有一個健 “YYZ” 和一個值 “Toronto Pearson”. 第二個鍵值對有一個健 “DUB” 和一個值“Dublin”.
字面量包含兩個 String: String 對。 簡直類型匹配機場類型的變量聲明 (一個字典只有字符串健和字符串值), 所以字面量賦值是允許的,可以用來初始化機場字典。
跟數組一樣, 如果你用相同類型的鍵值字典初始化,你就不需要寫出字典的聲明類型。初始化方法可以簡寫如下:
var airports = ["YYZ": "Toronto Pearson", "DUB": "Dublin"]
因為字面量的所有健類型都是一樣的, 同樣所有的值也是, Swift 可以推斷出來 [String: String] 是正確的類型聲明。
訪問和修改字典
你可以通過字典的方法和屬性來訪問和修改它, 或者使用下標語法。
跟數組一樣, 使用count 屬性獲取字典的項數:
print("The airports dictionary contains \(airports.count) items.")
// 打印 "The airports dictionary contains 2 items."
用 isEmpty 屬性判斷count 屬性是否等于 0:
if airports.isEmpty {
print("The airports dictionary is empty.")
} else {
print("The airports dictionary is not empty.")
}
// 打印 "The airports dictionary is not empty."
你可以用下標語法往字典添加新項。 使用對應的類型的鍵作為下標索引, 并賦一個對應的新值:
airports["LHR"] = "London"
// airports 現在包含 3 項
你可以用下標語法改變指定鍵對應的值:
airports["LHR"] = "London Heathrow"
// "LHR" 的值已經變成"London Heathrow"
作為下標的替代方案, 用字典的 updateValue(:forKey:) 方法設置或者更新指定鍵的值。 跟上面下標實例一樣, updateValue(:forKey:) 方法在鍵不存在時設置值, 如果鍵存在則進行更新。和下標不像的是, updateValue(_:forKey:) 方法在執行更新后返回老的值。 這個可以讓你知道更新是否生效。
updateValue(_:forKey:) 方法返回可選類型的值。對個存儲字符串值的字典來說, 比如, 這個方法返回 String?, 或者 “可選的 String”. 可選值包括未更新前存在的鍵對應的老值, 如果值不存在則包括nil:
if let oldValue = airports.updateValue("Dublin Airport", forKey: "DUB") {
print("The old value for DUB was \(oldValue).")
}
// 打印 "The old value for DUB was Dublin."
你可以用下標語法獲取特定鍵對應的值。因為有可能去請求不存在值的鍵, 字典的下標就會返回一個可選值。 如果字典包含請求鍵對應的值, 下標就返回一個可選值,這個值包含這個鍵對應的存在的值。否則返回 nil:
if let airportName = airports["DUB"] {
print("The name of the airport is \(airportName).")
} else {
print("That airport is not in the airports dictionary.")
}
// 打印 "The name of the airport is Dublin Airport."
你可以通過把鍵對應的值賦空的方式,用下標語法從字典移除一個鍵值對:
airports["APL"] = "Apple International"
// "Apple International" is not the real airport for APL, so delete it
airports["APL"] = nil
// APL 現在已經從字典移除
或者, 用 removeValue(forKey:) 這個方法來移除。 這個方法移除的鍵值對如果存在返回被移除的值,否則返回nil:
if let removedValue = airports.removeValue(forKey: "DUB") {
print("The removed airport's name is \(removedValue).")
} else {
print("The airports dictionary does not contain a value for DUB.")
}
// 打印 "The removed airport's name is Dublin Airport."
遍歷字典
你用for-in 循環來遍歷字典。 字典中的每一項都以 (key, value) 元組形式返回, 你可以分解元組到臨時的變量或者常量:
for (airportCode, airportName) in airports {
print("\(airportCode): \(airportName)")
}
// YYZ: Toronto Pearson
// LHR: London Heathrow
你可以通過訪問字典的鍵值屬性來獲取鍵值的集合:
for airportCode in airports.keys {
print("Airport code: \(airportCode)")
}
// Airport code: YYZ
// Airport code: LHR
for airportName in airports.values {
print("Airport name: \(airportName)")
}
// Airport name: Toronto Pearson
// Airport name: London Heathrow
如果你想使用字典的鍵值的數組實例, 用鍵值來初始化一個新的數組:
let airportCodes = [String](airports.keys)
// airportCodes is ["YYZ", "LHR"]
let airportNames = [String](airports.values)
// airportNames 是 ["Toronto Pearson", "London Heathrow"]
Swift的字典類型無序的。 為了用特定順序去遍歷字典的鍵值, 用這個 sorted() 方法。
79.控制流
Swift 提供了一系列控制流語句。 包括 while 循環來執行多次任務; if, guard, switch 語句來在不同條件執行不同代碼; 還有 break 和 continue 來轉移控制流。
Swift 同時提供了 for-in 循環使得遍歷數組, 字典, 范圍, 字符串, 和其他序列變得容易。
Swift 的 switch 語句比多數類 C 語言相同語句更加高效有力。 因為 Swift 的 switch 語句的分支不會落到下一分支, 這就避免了 C常見的錯誤。 分支可以匹配不同的模式, 包括區間匹配, 元組, 轉換成指定類型。 switch 分支匹配的值可以綁定到臨時的變量或者常量。每個分支可以用 where 族來表達符合匹配條件。
80.For-In 循環
你可以使用 for-in 循環遍歷一個序列, 例如數組里的項, 數值的范圍, 或者字符串中的字符。
下面的例子使用一個 for-in 循環遍歷數組里的項目:
let names = ["Anna", "Alex", "Brian", "Jack"]
for name in names {
print("Hello, \(name)!")
}
// Hello, Anna!
// Hello, Alex!
// Hello, Brian!
// Hello, Jack!
你可以遍歷字典的鍵值對。當字典被遍歷時, 字典中的每一項以 (key, value) 元組返回, 你可以把鍵值對分解成顯式的名字, 在循環中使用。在下面的代碼里, 字典的鍵解析到一個常量 animalName, 字典的值分解到另外一個常量 legCount.
let numberOfLegs = ["spider": 8, "ant": 6, "cat": 4]
for (animalName, legCount) in numberOfLegs {
print("\(animalName)s have \(legCount) legs")
}
// ants have 6 legs
// spiders have 8 legs
// cats have 4 legs
字典的內容本身是無序的, 遍歷它們不能保證它們獲取的順序。特別情況下, 你插入項目的順序不能定義它們遍歷的順序。
你也可以在數值范圍中使用 for-in 循環。下面的例子打印 5倍乘法表的前幾項:
for index in 1...5 {
print("\(index) times 5 is \(index * 5)")
}
// 1 times 5 is 5
// 2 times 5 is 10
// 3 times 5 is 15
// 4 times 5 is 20
// 5 times 5 is 25
這個序列遍歷的數值范圍是從 1 到 5, 包括它們, 使用閉合區間運算符表示 (…). 索引的值設置成數值范文的第一個 (1), 循環中的語句開始執行。在這個例子里, 循環體只有一條語句, 它打印 5 倍乘法表從當前索引值開始的一組值。在語句執行后, 索引值更新未數值范圍的第二個 (2), 然后 print(_:separator:terminator:) 函數再次調用。這個過程直到遍歷結束。
上面的例子里, 索引是一個常量, 它的值自動設置成循環遍歷的開始。索引在使用前不必要聲明。它會在循環聲明中隱式的聲明, 也不需要 let 關鍵字。
如果你不需要序列的每一項的值, 你可以在變量名的位置使用下劃線來忽略這個值。
let base = 3
let power = 10
var answer = 1
for _ in 1...power {
answer *= base
}
print("\(base) to the power of \(power) is \(answer)")
// 打印 "3 to the power of 10 is 59049"
上面的例子計算一個值的冪 (在這個例子里, 3的10次冪). 開始是3乘以1, 然后加倍, 使用一個從1 到 10 的閉合區間。針對這個計算, 每次循環并不需要單獨的計算器值—代碼只是簡單執行當前數字倍數的循環。下滑用來忽略這個值, 因為遍歷過程并不需要它。
在其他情景中, 你可能不想使用閉合區間, 它會包含兩個端點值。想像一下繪制每一分鐘的刻度。你想要繪制 60 個刻度。可以使用半開區間運算符 (..<) 包括較低的邊界而不包括較高的邊界。
let minutes = 60
for tickMark in 0..<minutes {
// 每分鐘繪制一次刻度 (60 次)
}
有些用戶想要更少的刻度。他們可能每五分鐘選擇一個刻度。使用 stride(from:to:by:) 函數跳過不想要的刻度。
let minuteInterval = 5
for tickMark in stride(from: 0, to: minutes, by: minuteInterval) {
// 每五分鐘繪制一次刻度 (0, 5, 10, 15 ... 45, 50, 55)
}
80.While 循環
while 循環執行一組語句,直到條件失敗。 首次遍歷前,遍歷次數不知道的時候使用這種方式是最好的。Swift 提供兩種 while 循環:
while 每次循環前評估條件。
repeat-while 每次循環后評估條件。
while 循環從評估條件開始。 如果條件是真的, 一組語句會重復執行直到條件變成假的。
這是 while 循環的基本形式:
while condition {
statements
}
這個例子是個蛇與梯子的游戲:
游戲規則如下:
棋盤有25個方格, 目標是占據或者超過25。
每次, 搖一個6邊的骰子然后移動對應數目方格, 沿著上圖的點箭頭的水平路徑移動。
如果位于梯子底部, 往梯子上方移動。
如果位于蛇頭, 往蛇下方移動。
游戲棋盤用整形值數組表示。大小基于 finalSquare 常量, 用來初始化數組并且判斷取勝的條件。 棋盤用26個0值初始化, 而不是25 (每個索引從0到25)。
let finalSquare = 25
var board = [Int](repeating: 0, count: finalSquare + 1)
一些方格會為蛇和梯子設置更多的指定值。梯子方格是正數,蛇方格是負數。
board[03] = +08; board[06] = +11; board[09] = +09; board[10] = +02
board[14] = -10; board[19] = -11; board[22] = -02; board[24] = -08
方格 3 包含了梯子的底部,可以爬到方格 11。 為了表示這個, board[03] 等于 +08, 這等于整數值 8 (3和11的差值) 加號 (+i) 和減號 (-i) 相等, 數值低于 10 的用 0 填充, 這樣所以棋盤都被定義了。
玩家開始方格是 “方格0”, 靠著棋盤的左下角。 第一次搖骰子總是讓玩家進入棋盤。
var square = 0
var diceRoll = 0
while square < finalSquare {
// roll the dice
diceRoll += 1
if diceRoll == 7 { diceRoll = 1 }
// move by the rolled amount
square += diceRoll
if square < board.count {
// if we're still on the board, move up or down for a snake or a ladder
square += board[square]
? ? ? }
}
print("Game over!")
上面的例子用了一個很簡單方法去搖骰子。 不用隨機數, 用一初始值為0的 diceRoll。 每次循環, diceRoll 加一然后判斷它是否過大。 當返回值等于 7, 骰子值過大然后重置為 1。diceRoll 值總是 1, 2, 3, 4, 5, 6, 1, 2 然后不停循環。
搖完篩子, 往前移動 diceRoll 方格。 如果移動超過25, 游戲結束??紤]邊界安全,在操作方格的時候。
備注 沒有這個判斷, board[square] 可能會越界訪問棋盤數組的值, 這會觸發一個錯誤。 如果方格等于 26, 代碼會判斷board[26] 的值, 這會超過數組的大小。
當前 while 循環執行然后結束, 然后循環的條件會被判斷是否再次執行。如果玩家已經移動或者超過25, 循環的條件變成假然后游戲就結束。
while 循環適合這個例子, 因為開始循環的時候游戲的長度并不清楚。 相反, 一個特定條件滿足后循環被執行了。
Repeat-While
另外一種 while 循環的變種, 就是 repeat-while 循環, 先執行一次循環, 然后再考慮循環的條件。 然后繼續執行直到條件變成假。
備注 Swift 里的 repeat-while 循環類似其他語言里的 do-while 循環。
這里是 repeat-while 循環的基本樣式:
repeat {
語句
} while 條件
這里還是蛇與梯子的例子, 用 repeat-while 循環來寫而不是 while 循環。finalSquare, board, square, 和 diceRoll 的值初始化方式跟 while 循環一樣。
let finalSquare = 25
var board = [Int](repeating: 0, count: finalSquare + 1)
board[03] = +08; board[06] = +11; board[09] = +09; board[10] = +02
board[14] = -10; board[19] = -11; board[22] = -02; board[24] = -08
var square = 0
var diceRoll = 0
這個游戲版本, 第一步是判斷梯子和蛇。 沒有梯子帶著玩家直接去方格 25, 因為不太可能通過爬梯子去贏得游戲。因此, 判斷有沒有蛇和梯子是很安全的。
游戲開始, 玩家在 “square zero”. board[0] 總是等于0沒有影響。
repeat {
// move up or down for a snake or ladder
square += board[square]
// roll the dice
diceRoll += 1
if diceRoll == 7 { diceRoll = 1 }
// move by the rolled amount
square += diceRoll
} while square < finalSquare
print("Game over!")
蛇與梯子判斷后, 搖動篩子玩家往前移動 diceRoll 方格。 當前循環執行然后結束。
循環條件 (while square < finalSquare) 和以前一樣, 不過這次開始不評估,直到第一次循環結束才評估。repeat-while 循環比 while loop 循環更適合這個游戲。上面的 repeat-while 循環, 當條件確認方格還在棋盤上后, square += board[square] 總是立即執行。 這就不需要像之前那樣判斷數組邊界。
條件語句
基于特定條件執行不同代碼片段經常用到。 在碰到錯誤時你可能想運行額外的代碼片段, 或者在數值變得太高或者太低的時候也是。為了做這件事, 你讓部分代碼帶有條件性。
Swift 提供兩種方式給你的代碼添加條件分支: if 語句和 switch 語句。你用 if 語句評估條件會有一些可能的結果。 switch 語句在比較復雜的多個排列時很有用, 在模式匹配可以幫助選擇合適代碼分支執行的情況也是。
If
最簡單的形式, if 語句有一個單一條件。 條件為真執行一個語句集合。
var temperatureInFahrenheit = 30
if temperatureInFahrenheit <= 32="" {="" print("it's="" very="" cold.="" consider="" wearing="" a="" scarf.")="" }="" 打印="" "it's="" scarf."="" <="" code="">
上面的例子判斷文檔是否小于等于華氏32度 (冰點)。 如果是, 打印一條信息。 否則, 不打印消息, 代碼在大括號后繼續執行。
if 語句還可以提供一個替代語句集合, 就是 else 字句, 針對條件是假的情況。 這些語句用else 關鍵字表示。
temperatureInFahrenheit = 40
if temperatureInFahrenheit <= 32="" {="" print("it's="" very="" cold.="" consider="" wearing="" a="" scarf.")="" }="" else="" not="" that="" wear="" t-shirt.")="" 打印="" "it's="" t-shirt."="" <="" code="">
兩個分支之一總會執行。 因為溫度已經增長到華氏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語句,沒有打印信息。
81.Switch
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 關鍵字開始。 除了跟指定值比較外, Swift 為分支提供了指定更復雜匹配模式的方法。這些選項本章后面會描述。
跟if語句體很像, 每個分支是代碼執行的單獨分支。 switch 語句決定是否選擇這個分支。這個過程叫分發。
每個switch 語句務必是詳盡的。 就是說, 每個能想到的類型值都要分支匹配。 為每個可能值都提供分支是不合適的。你可以定義一個默認分支覆蓋沒有顯示處理的值。 默認分支使用default關鍵字指明, 必須總是出現在最后。
這個例子使用一個 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"
switch 語句第一個分支匹配英文字母表第一個字母 a, 第二個分支匹配最后一個字母 z. 因為 switch 語句對于每個可能的字符都要有分支, 不是每個字母表字符都需要。這個switch 語句使用了一個default 分支 去處理a 和 z之外的其他所有字母。這保證了switch語句的詳細性。
82.非隱式 Fallthrough
和C 與 Objective-C 的switch語句對比, Swift 中的switch語句不會 fall through 分支底部并且跳到下一個分支。 相反, 在第一次匹配到分支后整個switch語句執行就完成了。不要求顯示的break語句。 這使得switch語句更安全,更容易使用,而且避免執行多個分支的錯誤。
備注
盡管Swift 不要求break, 你仍然可以在Swift中使用。
分支體必須包含至少一句可執行語句。下面的代碼的無效的, 因為第一個分支是空的:
let anotherCharacter: Character = "a"
switch anotherCharacter {
case "a": // Invalid, the case has an empty body
case "A":
print("The letter A")
default:
print("Not the letter A")
}
// 這會報一個編譯期錯誤
跟C中的switch語句不同, 這個 switch 語句不能同時匹配 “a” 和 “A”. 相反, 它會報一個編譯期錯誤,因為case “a”: 沒有任何可以執行的語句。這個方法避免了突然從一個分支跳入另外一個分支, 讓代碼意圖清晰安全。
如果要用一個分支同時匹配“a” 和 “A”, 把這兩個值合并到一個分支語句, 用逗號把它們分開。
let anotherCharacter: Character = "a"
switch anotherCharacter {
case "a", "A":
print("The letter A")
default:
print("Not the letter A")
}
// 打印 "The letter A"
為了可讀性, 復合分支也可以寫作多行。
備注:為了顯式fall through 到某個分支下面, 使用 fallthrough 關鍵字。
83.區間匹配
switch 分支里面的值可以包含在區間里判斷。 這個例子使用數字區間來提供一個對任何數字大小的自然語言統計。
let approximateCount = 62
let countedThings = "moons orbiting Saturn"
var naturalCount: String
switch approximateCount {
case 0:
naturalCount = "no"
case 1..<5: naturalcount="a few" case="" 5..<12:="" 12..<100:="" 100..<1000:="" default:="" }="" print("there="" are="" \(naturalcount)="" \(countedthings).")="" 打印="" "there="" dozens="" of="" moons="" orbiting="" saturn."="" <="" code="">
上面的例子, approximateCount 在switch語句里估算。 每個分支把這個值跟一個數字或者區間進行比較。因為 approximateCount 值落在 12 和 100之間, naturalCount 被賦值為 “dozens of”, 然后代碼執行跳出switch語句繼續執行。
84.元組
你可以在switch語句中使用元組測試多值。元組元素可以跟一個不同值或者值區間進行測試?;蛘? 使用通配下劃線 (_) 去匹配可能的值。
下面的例子有一個 (x, y) 點, 用一個簡單的元組類型 (Int, Int) 表示, 并且在下面的圖表中標示。
let somePoint = (1, 1)
switch somePoint {
case (0, 0):
print("(0, 0) is at the origin")
case (_, 0):
print("(\(somePoint.0), 0) is on the x-axis")
case (0, _):
print("(0, \(somePoint.1)) is on the y-axis")
case (-2...2, -2...2):
print("(\(somePoint.0), \(somePoint.1)) is inside the box")
default:
print("(\(somePoint.0), \(somePoint.1)) is outside of the box")
}
// 打印 "(1, 1) is inside the box"
switch 語句決定這個點是否位于原點 (0, 0), 紅色的 x-軸, 橙色 y-軸, 在藍色 4-by-4 方塊里原點在中心, 或者在方塊之外。
跟 C 不同, Swift 允許多個分支去處理相同的值。 事實上, (0, 0)點可以滿足例子的四個分支。不過, 如果多個匹配都可能, 第一個匹配上的總是執行。 (0, 0) 點首先匹配case (0, 0), 其他所有匹配的分支都會被忽略。
84.值綁定
一個 switch 分支 可以綁定值到臨時的常量或者變量, 用在分支體中。這個就是值綁定, 因為值綁定到分支體中的臨時常量或者變量上。
下面這個例子有一個(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-軸, 還是其他什么地方 (不在軸上)。
這三個 switch case聲明了占位符常量 x 和 y, 臨時存放來自 anotherPoint 的一個或者兩個元組值。第一個分支 case (let x, 0), 匹配任何y值為0 然后把坐標點x的值賦給臨時常量 x. 類似的, 第二個分支, case (0, let y), 匹配任何x值為0,然后把坐標點y值賦給臨時常量y.
臨時常量聲明后, 它們就可以用在分支體中。在這里, 它們用來打印這個點的標示。
這個 switch 語句沒有默認的default 分支. 最后一個分支, case let (x, y), 聲明了帶有兩個占位符常量的元組,它可以匹配任何值。因為 anotherPoint 總是兩個值的元組, 這個case 匹配所有可能的剩余值, 所以默認的default case 是不需要的。
85.Where
一個 switch 分支 可以用 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這個紫色對角線上,或者兩者都不在。
三個 switch 分支 都聲明了占位符常量 x 和 y, 臨時接受來自 yetAnotherPoint 兩個元組值。這些常量用作where字句的一部分, 去創建一個動態的過濾器。分支匹配坐標點的當前值,如果where字句條件為真的話。
和前面一個例子一樣, 最后的分支匹配所有剩余值, 不再需要 default 分支