一個(gè)語言特性往往具備多種 API 表現(xiàn)形式,理解特性比硬記 API 更有助于我們掌握一門新語言。Swift 作為現(xiàn)代編程語言的集大成者,具備很多優(yōu)秀的特性來幫助開發(fā)者快速高效的編寫代碼,Pattern Matching 就是其中之一。
程序員 vs 編程語言
我們所寫的代碼,或者說一個(gè) App 所具備的功能,最終都是程序員和編程語言共同作用的成果。我們使用某種編程語言寫代碼的時(shí)候,會(huì)受語言設(shè)計(jì)者所制定的各種規(guī)則制約,這些制約背后都隱含著數(shù)十年行業(yè)經(jīng)驗(yàn)的積累,和設(shè)計(jì)者高明的設(shè)計(jì)技巧。理解這些規(guī)則所帶來的好處和設(shè)計(jì)值的良苦用心,能在大大小小的方面,讓我們的代碼更加可靠和穩(wěn)定。
前段時(shí)間,「Clean Code」的作者,Bob 大叔,在博客里吐槽了 Swift 這類新型編程語言可能會(huì)將開發(fā)者帶入「Dark Path」。文章舉了不少例子,大意是說以 Swift 為代表的新語言正在嘗試替開發(fā)者做更多的事情, 編程語言做的越多,開發(fā)者自然就做的越少,看上去是件好事,畢竟開發(fā)者更容易犯錯(cuò),但 Bob 大叔認(rèn)為規(guī)則是死的,人是活的,程序員應(yīng)該在代碼的編寫中承擔(dān)更大的責(zé)任,而且有些生硬的規(guī)則會(huì)反過來會(huì)給代碼的表達(dá)產(chǎn)生負(fù)面效果。這個(gè)觀點(diǎn)顯得有些偏激,也引起了不小的爭論,對錯(cuò)姑且不論,對于我們開發(fā)者來說,至少要搞明白 Swift 這類新語言所包含的規(guī)則背后的意義,才能既合理的使用規(guī)則又不被規(guī)則所制,寫出漂亮的代碼。
Bob 大叔博客地址:http://blog.cleancoder.com
Pattern Matching
Pattern Matching 早在 Scala, Haskell 這些語言中就存在,作為編程語言的特性,旨在替程序員分擔(dān)一部分的工作。顧名思義,Pattern Matching 可以替我們解決類型匹配的的問題,這簡單的一句話,在一些不同的場景下,有著不同的表現(xiàn)形式,表現(xiàn)形式一多,又會(huì)讓簡單的概念理解起來變得復(fù)雜。對于復(fù)雜概念的理解,我們最好能用一句話做高度精煉簡潔的概括,以不變應(yīng)萬變。
Pattern Matching 簡單來說,就是編程語言替我們程序員節(jié)省了一件事,這件事可以用兩個(gè)單詞來描述:Check 和 Extract。
Check 是指檢查條件是否滿足,類型是否匹配。這么說有些抽象,我們可以把 Check 想象成一個(gè)函數(shù),這個(gè)函數(shù)接收兩個(gè)參數(shù),參數(shù)一是源數(shù)據(jù),參數(shù)二是目標(biāo)數(shù)據(jù),返回值是一個(gè) Bool 值,Bool 值表示兩個(gè)參數(shù)之間的某種關(guān)系是否成立,至于這個(gè)關(guān)系具體指什么,就因場景而異了。我們可以用如下一個(gè)函數(shù)來表達(dá):
func check(targetData: [Type1], sourceData: [Type2]) -> Bool {
//check relation, return true or false.
}
源數(shù)據(jù)和目標(biāo)數(shù)據(jù)可以是不同的類型,只要他們之間滿足某種關(guān)系,關(guān)系的定義取決于 check 函數(shù)內(nèi)部的實(shí)現(xiàn)。系統(tǒng)默認(rèn)替我們實(shí)現(xiàn)了一些關(guān)系,也允許我們通過操作符重載的方式自定義關(guān)系。看幾個(gè)簡單的例子:
var result: String? = getResult()
switch result {
case .none:
print("no result")
case let r:
print("result is \(r)")
}
上面是 Pattern Matching 在 switch 當(dāng)中的應(yīng)用。源數(shù)據(jù)是 result,type 為 optional。目標(biāo)數(shù)據(jù)是 .none 和 r,type 為具體的 String。二者之間的關(guān)系是個(gè)等式關(guān)系,這個(gè)關(guān)系默認(rèn)由系統(tǒng)提供。這個(gè)關(guān)系說白了,無非是一個(gè) if,else 語句,來判斷 result 到底是 .none 還是具體的值。
當(dāng)然我們也可以直接寫 if,else 來替代 Pattern Matching,在 Objective C 中,沒有 Pattern Matching,我們確實(shí)是用 if,else 來做 check 的。但明顯使用 Pattern Matching 我們可以寫更少的代碼,同時(shí)讓代碼更容易閱讀,表達(dá)更清晰,這也是 Swift 中引入 Pattern Matching 的原意所在。
Pattern Matching 除了做 check 之外,很多時(shí)候也被用來做 extract。Extract 是提取數(shù)據(jù)的意思,Pattern Matching 配合其他關(guān)鍵字就可以完成數(shù)據(jù)的 extract。比如上面的代碼中,switch 配合 case let r: 就將 result 中所包含的值提取到了 r 中。嚴(yán)格來說第一步還是 check,check 關(guān)系滿足之后,再做 extract。
Pattern Matching 雖然表現(xiàn)形式繁多,但最后都會(huì)落實(shí)到 check 和 extract 這兩個(gè)單詞之上,大家可以嘗試去發(fā)掘下其他 Swift 語境下 Pattern Matching 是如何做 check 和 extract 的。
深入了解過 Pattern Matching 的同學(xué)應(yīng)該知道,其實(shí) Pattern Matching 本質(zhì)上就是一個(gè)特殊的操作符:~=。我們可以把這個(gè)操作符等同于我們上面提到的 check 函數(shù)。當(dāng)我們針對自己定義的數(shù)據(jù)類型做 ~= 的操作符重載之后,就可以實(shí)現(xiàn)自定義規(guī)則的 Pattern Matching 了。
再換句話來描述 Pattern Matching:檢查兩個(gè)數(shù)據(jù)之間是否滿足某種關(guān)系。
Pattern Matching 并不是 Swift 所獨(dú)有,但在 Swift 語境下,主要是通過 case 這個(gè)關(guān)鍵字來體現(xiàn)。在 Objective C 的語境下,case 是和 switch 搭配使用,和 if 的含義接近,而在 Swift 中 case 關(guān)鍵字具備了 Pattern Matching 的含義之后,case 關(guān)鍵字可以在更多的場景下來使用,可以和更多其他的關(guān)鍵字配合,以延伸 Pattern Matching 的使用場景。除了 switch case 之外,我們還可以用 for case,while case,if case,guard case,無論關(guān)鍵字如何搭配,其本意都是在做 Pattern Matching。這也是為什么初次接觸 Swift 會(huì)感覺語法太過靈活且不易掌握的原因。
總結(jié)
希望這篇文章能幫助一些朋友簡單點(diǎn)理解 Pattern Matching 的意義所在。另一個(gè)目的是介紹一個(gè)學(xué)習(xí)小技巧:對于新知識(shí)點(diǎn)的學(xué)習(xí),記 API 或者編程語言的各種表現(xiàn)形式,不如去理解其背后的設(shè)計(jì)思想。不過總結(jié)和提煉思想,往往需要花費(fèi)更多的時(shí)間和精力,回報(bào)是對知識(shí)的掌握更深刻和牢固。
歡迎關(guān)注公眾號(hào):MrPeakTech