模式匹配第二彈:元組,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/