模式匹配第二彈:元組,range 和類型
在上一篇文章中,我們已經看過了使用 switch 來對枚舉進行基本的模式匹配。那如果對除枚舉外的其它類型使用 switch來進行模式匹配會怎樣呢?
對元組進行模式匹配
在 Swift 當中,switch 并不像 ObjC 一樣只能對整型或枚舉進行匹配。
事實上,我們可以使用 switch 對很多類型進行匹配,包括(但不僅限于)元組。
這意味著我們只要將多個數據組合在一個元組中,就可以一次性匹配多個數據。比如說,如果你有一個 CGPoint,并且你想確定這個點是否位于某個坐標軸上,則可以使用 switch 來匹配它的 .x 和 .y 屬性!
|
注意我們在上一篇文章中使用過的 _ 通配符,以及第四個 case 使用到的 (let x, let y) 可以對變量進行綁定,然后使用 where 來檢查它們是否相等。
Case 是按順序判斷的
還有一點要注意的是,switch 是按 case 模式被指定的順序來判斷求值的,并且它會在匹配到第一個滿足的 case 后跳出。與 C 和 Objective-C 不同,我們不需要使用 break 關鍵字1。
這意味著在上面的代碼中,如果坐標是 (0, 0),則它會匹配第一個 case 打印出 "On the origin!",并就此打住,就算(0, _) 和 (_, 0) 也符合匹配的條件,它也不會再去進行匹配。因為它已經在第一個匹配之后跳出了。
字符串與字符
為什么要止步于元組呢?在 Swift 當中,我們也可以使用 switch 來對很多原生類型進行匹配,包括字符串和字符,比如:
|
可以注意到,我們可以使用按逗號分隔的多個模式來進行匹配,使符合這些模式的匹配(這里是匹配所有的元音字母)都執行同一段代碼。這可以避免我們寫很多重復的代碼。
Range
Range 在模式匹配中也很有用。提醒一下,Range<T> 是一個泛型類型,它包含了 T 類型的 start 和 end 成員,同時T 必須是一個 ForwardIndexType。這包括 Int 和 Character 在內的許多類型。
我們可以使用 Range(start: 1900, end: 2000) 來顯式地聲明一個 range,也可以使用語法糖操作符 ..<(不包含最后一個數 end)或 ...(包含最后一個數 end),所以我們也可以將上面的 range 寫為 1900..<2000(更方便也更易讀)
那么我們如何在 switch 當中使用它們呢?其實相當簡單,在 case 模式中使用 range 來判斷值是否落于這個范圍內!
|
可以看到我們在 case 當中混用了 Int 整型值與 Range<Int> 的值。這樣的使用并沒有任何問題,只要我們保證覆蓋了所有可能的情況。
雖說 Int 是最常用的 range 類型,我們也可以使用其它的 ForwardIndexType 類型,包括… Character!還記得上面寫的代碼么?它有一點問題,那就是對于標點符號以及其它不是 A-Z 的字符,它也會打印出 “Consonant”。讓我們來解決這個問題2(同時也增加了小寫字母):
|
類型
至此一切順利,但我們能不能更進一步呢?答案是當然沒問題:讓我們把模式匹配用在… 類型上!
在這里,我們定義了三個結構體,并遵守相同的協議:
|
然后我們要如何對 Medium 使用 switch 的模式匹配,讓它對 Book 和 Movie 做不同的事呢?簡單,在模式匹配中使用as 和 is!
|
注意到對 Book 和 Movie 使用的 as,我們需要確定它們是不是特定的類型,如果是,則將它們轉換后的類型賦值給一個常量(let b 或 let m),因為我們之后要使用到這個常量3。
而另一方面,對 WebSite 我們只使用了 is,因為我們只需要檢查 medium 是不是一個 Website 類型,如果是,我們并沒有對它進行轉換與存儲在常量中(我們不需要在 print 語句中使用到它)。這與使用 case let _as Website 有點類似,因為我們只關心它是不是 Website 類型,而不需要它的對象的值。
注意:如果必須在
swtich匹配中使用到as和is,這里有可能存在代碼異味,比如,在上面這個特定的例子中,在protocol Medium當中添加一個releaseInfo: String { get }屬性就比使用switch來對不同的類型進行匹配要好。
下一步
在接下來的部分,我們會學習如何創建可以直接使用于模式匹配的自定義類型,探索更多的語法糖,并看到如何在 switch語句之外使用模式匹配,以及更加復雜的匹配表達式… 迫不及待了吧!
- 可以使用
fallthrough關鍵字來讓求值判斷流向下一個case。但是在實踐上要使用到這個關鍵字的場景很少,并不經常會碰到。 - 當然,這種字符串的分析方法并不是最好的,也不是值得推薦的 —— 因為 Unicode 字符以及本地化都比這復雜得多。所以類似這樣的功能我們更應該使用
NSCharacterSet,考慮當前的NSLocale把哪些字母定義為元音(“y” 是元音嗎?還有 “?” or “?” 呢?),等等。不要把這個例子看得太認真,我只是用它來展示switch+Range的強大而已。 - 盡管與
if let b = medium as? Book表達式很相似 —— 當medium可以被轉換為特定類型的時候,它們都將其綁定到一個變量上 —— 但是在模式匹配中我們要使用as而非as?。盡管它們的機制很相似,但是它們的語義是不同的(“嘗試進行類型轉換,如果失敗就返回nil” vs “判斷這個模式是不是匹配這種類型”)。
來源:http://swift.gg/2016/04/27/pattern-matching-2/