模式匹配是swift語言的亮點(diǎn),如果運(yùn)用得當(dāng),能有效提升代碼的 逼格。比方說if case,guard case,for case。如過你還沒有使用過它們,或者不確信它們是否可行,不妨試試下面的內(nèi)容。
if case
case let x = y 表達(dá)式可以完成檢查 y 是否匹配模式 x。
表達(dá)式 if case let x = y { … } 則等同于表達(dá)式 switch y { case let x: … } 。前者是后者的一種簡寫,switch適用于多分支的模式匹配,而if case則適合于單條case的匹配情況。
比方說我們有這樣一個(gè)枚舉:
enum Media {
case Book(title: String, author: String, year: Int)
case Movie(title: String, director: String, year: Int)
case WebSite(urlString: String)
}
如果使用switch進(jìn)行匹配的話:
let m = Media.Movie(title: "Captain America: Civil War", director: "Russo Brothers", year: 2016)
switch m {
case let Media.Movie(title, _, _):
print("This is a movie named \(title)")
default: ()
}
我們只用到了單分支匹配,但需要描述 switch,default,啰嗦不?所以要:變
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)")
}
這樣寫有沒有感覺就像理了頭發(fā),清爽了許多,心情瞬間美好起來?其實(shí)不只是模式匹配,完全可以在混合一下條件表達(dá)式:
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.")
}
通過if case表達(dá)式,我們幾乎把switch的所有本事都搬來了,愉快。
guard case
從模式匹配的角度來看,guard case與if case的語法結(jié)構(gòu)是完全相同的,它們的區(qū)別僅僅體現(xiàn)在guard關(guān)鍵字與if關(guān)鍵字的區(qū)別上。直接看一個(gè)例子。
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這兩個(gè)關(guān)鍵字聯(lián)合起來,能夠讓我們在迭代集合的時(shí)候,進(jìn)行模式匹配,這感覺就好像我們在for迭代中,使用了if case。我們先來創(chuàng)建一個(gè)Media類型的數(shù)組,數(shù)組中包括電影和圖書。
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")
]
現(xiàn)在我們來完成這樣一個(gè)任務(wù),我們把集合中所有的電影信息打印出來:
for case let Media.Movie(title, _, year) in mediaList {
print(" - \(title) (\(year))")
}
如果配合上where子句,我們會得到更加強(qiáng)大的能力,比方說我們只打印某一位導(dǎo)演的電影。
for case let Media.Movie(title, director, year) in mediaList where director == "Chris Columbus" {
print(" - \(title) (\(year))")
}
百尺竿頭再進(jìn)一步
我們現(xiàn)在擴(kuò)展一下Media,讓它可以描述title和種類,我們用extension關(guān)鍵字。
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 {
switch self {
case .Book: return "Book"
case .Movie: return "Movie"
case .WebSite: return "Web Site"
}
}
}
現(xiàn)在我們打印一下mediaList中所有元素的title和kind
for case let (title?, kind) in mediaList.map({ ($0.title, $0.kind) })
where title.hasPrefix("Harry Potter") {
print(" - [\(kind)] \(title)")
}
這個(gè)代碼的可讀性并不是很高,我們先來簡單解釋一下上面這個(gè)代碼片段
- 首先是map函數(shù)將 mediaList 映射成一個(gè)[(String?,String)]類型的數(shù)組。
- 然后我們使用case let 進(jìn)行了title 與 kind的值綁定,因?yàn)槲覀冎该髁藅itle?所以要求數(shù)組元素中必須包含title
- 最后使用where子句進(jìn)行條件表達(dá)式過濾。
因?yàn)榇a的可讀性決定了代碼了可維護(hù)性,所以寫出容易閱讀的代碼是至關(guān)重要的,我們其實(shí)可以使用guard關(guān)鍵字,讓上面的這個(gè)段代碼變得更加清晰簡潔(雖然長度會略長一些)。
for media in mediaList {
guard let title = media.title else {
continue
}
guard title.hasPrefix("Harry Potter") else {
continue
}
print(" - [\(media.kind)] \(title)")
}
希望if case可以更多的出現(xiàn)在我們的代碼中,來讓我們的代碼更優(yōu)美。
本文參考
https://www.natashatherobot.com/swift-2-pattern-matching-with-if-case/
http://alisoftware.github.io/swift/pattern-matching/2016/05/16/pattern-matching-4/