做者:Olivier Halligon,原文連接,原文日期:2016-05-16
譯者:walkingway;校對:Cee;定稿:numbbbbbgit
如今咱們來從新回顧下前三彈模式匹配的各類語法 第一彈,第二彈,第三彈,第四彈是本系列的最後一篇文章,本章會教你們使用 if case let
,for case where
等一些高級語法,讓咱們拭目以待吧!github
本篇文章會結合本系列前三篇文章提到的語法,而後將它們應用在一些更先進表達式中。swift
語句 case let x = y
模式容許你檢查 y
是否能匹配 x
。數組
而 if case let x = y { … }
嚴格等同於 switch y { case let x: … }
:當你只想與一條 case 匹配時,這種更緊湊的語法尤爲有用。有多個 case 時更適合使用 switch
。架構
例如,咱們有一個與以前文章相似的枚舉數組:函數
enum Media { case Book(title: String, author: String, year: Int) case Movie(title: String, director: String, year: Int) case WebSite(urlString: String) }
而後咱們能夠這樣寫:學習
let m = Media.Movie(title: "Captain America: Civil War", director: "Russo Brothers", year: 2016) if case let Media.Movie(title, _, _) = m { print("This is a movie named \(title)") }
改用 switch 後更冗長的版本:this
switch m { case let Media.Movie(title, _, _): print("This is a movie named \(title)") default: () // do nothing, but this is mandatory as all switch in Swift must be exhaustive }
咱們固然還能夠將 if case let
和 where
從句組合在一塊兒用:url
if case let Media.Movie(_, _, year) = m where year < 1888 { print("Something seems wrong: the movie's year is before the first movie ever made.") }
這種方式能夠組合成一個至關強大的表達式,而改用 switch
實現可能會變得很是複雜,須要寫不少行代碼來檢測那一個特定的 case。
固然,guard case let
相似於 if case let
,你可使用 guard case let
和 guard case let … where …
來確保匹配一個模式或一個條件,而當沒法匹配模式或知足條件時就退出。
enum NetworkResponse { case Response(NSURLResponse, NSData) case Error(NSError) } func processRequestResponse(response: NetworkResponse) { guard case let .Response(urlResp, data) = response, let httpResp = urlResp as? NSHTTPURLResponse where 200..<300 ~= httpResp.statusCode else { print("Invalid response, can't process") return } print("Processing \(data.length) bytes…") /* … */ }
將 for
和 case
組合在一塊兒也能讓你有條件地遍歷一個集合對象。使用 for case …
語義上相似於 for
循環,並且將它整個循環體封裝在了 if case
的結構之中:它只會遍歷、處理那些模式匹配了的元素。
let mediaList: [Media] = [ .Book(title: "Harry Potter and the Philosopher's Stone", author: "J.K. Rowling", year: 1997), .Movie(title: "Harry Potter and the Philosopher's Stone", director: "Chris Columbus", year: 2001), .Book(title: "Harry Potter and the Chamber of Secrets", author: "J.K. Rowling", year: 1999), .Movie(title: "Harry Potter and the Chamber of Secrets", director: "Chris Columbus", year: 2002), .Book(title: "Harry Potter and the Prisoner of Azkaban", author: "J.K. Rowling", year: 1999), .Movie(title: "Harry Potter and the Prisoner of Azkaban", director: "Alfonso Cuarón", year: 2004), .Movie(title: "J.K. Rowling: A Year in the Life", director: "James Runcie", year: 2007), .WebSite(urlString: "https://en.wikipedia.org/wiki/List_of_Harry_Potter-related_topics") ] print("Movies only:") for case let Media.Movie(title, _, year) in mediaList { print(" - \(title) (\(year))") } /* Output: Movies only:
Harry Potter and the Philosopher's Stone (2001)
Harry Potter and the Chamber of Secrets (2002)
Harry Potter and the Prisoner of Azkaban (2004)
J.K. Rowling: A Year in the Life (2007)
*/
爲 for case
增長一個 where
從句,能使其變得更增強大:
print("Movies by C. Columbus only:") for case let Media.Movie(title, director, year) in mediaList where director == "Chris Columbus" { print(" - \(title) (\(year))") } /* Output: Movies by C. Columbus only:
Harry Potter and the Philosopher's Stone (2001)
Harry Potter and the Chamber of Secrets (2002)
*/
?注意:使用 for … where
而不帶 case
模式匹配依然是符合 Swift 語法規則的。好比你這樣寫也是 OK 的:
for m in listOfMovies where m.year > 2000 { … }
這裏沒有使用模式匹配(沒有 case 或 ~=),所以有點超出了本系列的主題範圍,可是這種寫法是徹底有效的,並且這種構造也很是有用---特別是避免了將一個巨大的判斷邏輯 if
結構(或是一個 guard … else { continue }
)封裝在 for
的循環體內。
如今咱們終於要迎來這系列文章的大結局了:把咱們以前所學從頭至尾串聯起來(包括一些咱們在以前章節學習到的相似 x?
這種語法糖):
extension Media { var title: String? { switch self { case let .Book(title, _, _): return title case let .Movie(title, _, _): return title default: return nil } } var kind: String { /* Remember part 1 where we said we can omit the `(…)` associated values in the `case` if we don't care about any of them? */ switch self { case .Book: return "Book" case .Movie: return "Movie" case .WebSite: return "Web Site" } } } print("All mediums with a title starting with 'Harry Potter'") for case let (title?, kind) in mediaList.map({ ($0.title, $0.kind) }) where title.hasPrefix("Harry Potter") { print(" - [\(kind)] \(title)") }
上面的代碼可能看上去有點複雜,咱們先拆分一下:
使用 map
函數將 Array<Media>
類型的數組 mediaList
轉換成一個包含元組 [(String?, String)]
的數組,而其中的元組包含兩個元素:第一個是標題(String? 類型),第二個是元素的種類(String 類型)
它只當 title? 匹配時整個表達式纔會匹配──還記得第三彈的那個語法糖嗎:「當 switch 處理一個可選值 x?
時,你能夠識別問號標記的可選值」,所以這裏的 title?
至關於 .Some(title)
,它是不會匹配 title 爲 nil 的狀況的(譯者注:至於爲何要寫成 title? 上一彈也有說明:由於後面與之匹配的是一個可選值(mediaList.map(...)
的 title),匹配類型要一致,不然會報錯。)──所以匹配的結果是剔除全部 $0.title
爲 nil
的 media
(也就是 title 爲 Optional.None
)──最終剩下的 media 中不包括 WebSite
類型,由於它沒有 title
。
而後再進一步去遍歷 media,判斷他們的 title
是否知足 title.hasPrefix("Harry Potter")
條件
最後這段代碼將遍歷每個 medium,篩選出那些以 「Harry Potter」 開頭的,在這一過程當中將丟棄那些沒有標題的,好比 WebSite,還有那些標題不以 "Harry Potter" 開頭的 medium,這也包括做者 J.K.羅琳的記錄片。
最終的輸出結果以下,只有和 Harry Potter 相關的書籍和電影:
All medium with a title starting with 'Harry Potter'
[Book] Harry Potter and the Philosopher's Stone
[Movie] Harry Potter and the Philosopher's Stone
[Book] Harry Potter and the Chamber of Secrets
[Movie] Harry Potter and the Chamber of Secrets
[Book] Harry Potter and the Prisoner of Azkaban
[Movie] Harry Potter and the Prisoner of Azkaban
若是不使用模式匹配、where 從句、或是前面提到的各類語法糖,代碼寫出來多是這樣的:
print("All mediums with a title and starting with 'Harry Potter'") for media in mediaList { guard let title = media.title else { continue } guard title.hasPrefix("Harry Potter") else { continue } print(" - [\(media.kind)] \(title)") }
有些人可能以爲這種寫法可讀性更好,但你沒法否定 for case let (title?, kind) in … where …
確實強大,能夠在小夥伴面前裝個逼,並且能夠將循環 + 模式匹配 + where
從句組合起來是使用 ✨。
「模式匹配」系列文章到此就所有結束了,但願你能喜歡它,並真正學到了一些有趣的東西 ?。
下一篇文章我將聚焦 Swift 的設計模式與架構,而再也不是語言自己的語法了。
? 若是關於 Swift 你有什麼特別想要從我這裏瞭解到的,請不要猶豫,直接在 Twitter 上聯繫我吧,個人下一篇文章的靈感極可能就來自於大家的建議。
模式匹配和參數的順序顛倒會致使語法錯誤。爲了方便記憶,想像一下
switch
中case let Media.Movie(…)
的順序,他們是一致的。這樣你就知道應該是if case let Media.Movie(…) = m
,而不是if case let m = Media.Movie(…)
,後者是徹底不會編譯的。和 switch 中所作的同樣,將case
和模式((Media.Movie(title, _, _)
)放在一塊兒,而不是與變量(m
)在一塊兒。
本文由 SwiftGG 翻譯組翻譯,已經得到做者翻譯受權,最新文章請訪問 http://swift.gg。