自拍偷在线精品自拍偷,亚洲欧美中文日韩v在线观看不卡

Swift中的模式匹配

移動(dòng)開發(fā) iOS
例如,假設(shè)你想判斷一個(gè)整數(shù)是大于、小于還是等于零,你可以用if-else if-else語句,盡管這并不美觀:

[[156836]]

Swift有一個(gè)很好的特性,那就是模式匹配的擴(kuò)展。模式是用于匹配的規(guī)則值,如switch語句的case,do語句的catch子句,以及if、while、guard、for-in語句的條件。

例如,假設(shè)你想判斷一個(gè)整數(shù)是大于、小于還是等于零,你可以用if-else if-else語句,盡管這并不美觀:

  1. let x = 10 
  2. if x > 0 { 
  3. print("大于零"
  4. else if x < 0 { 
  5. print("小于零"
  6. else { 
  7. print("等于零"

用switch語句會(huì)好很多,我理想的代碼是這樣:

  1. // 偽代碼 
  2. switch x { 
  3. case > 0
  4. print("大于零"
  5. case < 0
  6. print("小于零"
  7. case 0
  8. print("等于零"

但模式匹配默認(rèn)并不支持不等式。讓我們看看能不能改變這個(gè)現(xiàn)狀。為了使過程更加清晰,我先忽略>0的情況,用greaterThan(0)來代替它,過后我再來定義這個(gè)操作符。

擴(kuò)展模式匹配

Swift的模式匹配是基于~=操作符的,如果表達(dá)式的~=值返回true則匹配成功。標(biāo)準(zhǔn)庫(kù)自帶四個(gè)~=操作符的重載:一個(gè)用于Equatable,一個(gè)用于Optional,一個(gè)用于Range,一個(gè)用于Interval。這些都不符合我們的需求,盡管Range和Interval很接近了,關(guān)于它們你可以看這篇文章。

所以我們要實(shí)現(xiàn)我們自己的~=。這個(gè)方法的原型是:

  1. func ~=(pattern: ???, value: ???) -> Bool 

我們知道這個(gè)方法必須返回一個(gè)Bool,那正是我們需要的,我們需要知道這個(gè)值是否匹配模式。接下來要問我們自己的是:參數(shù)的類型是什么?

對(duì)于值,我們可以使用Int,這正是我們?cè)谥暗睦又行枰?。但讓我們把它一般化,讓它能夠接受任何類型。在我們的情況里,模式形如greaterThan(001.png)或lessThan(001.png)。更一般化,模式應(yīng)該是一個(gè)方法,一個(gè)能夠?qū)⒅底鳛閰?shù)并返回true或false的方法。值的類型為T,所以模式的類型應(yīng)為T -> Bool:

  1. func ~=(pattern: T -> Bool, value: T) -> Bool { 
  2. return pattern(value) 

現(xiàn)在我們需定義方法greaterThan和lessThan來創(chuàng)建模式。注意不要把模式greaterThan(0)中的0和我們想匹配的值混淆了。greaterThan的參數(shù)是模式的一部分,這個(gè)部分將在第二步中用到。舉個(gè)例子,greaterThan(0) ~= x和greaterThan(0)(x)是一樣的。

我們知道方法greaterThan(0)必須返回一個(gè)方法,這個(gè)方法要能接受一個(gè)值并返回Bool。所以greaterThan必須是一個(gè)方法,接受另一個(gè)值并返回之前方法。我們把參數(shù)限制成Comparable,為了能在實(shí)現(xiàn)中用Swift的>和<操作符:

  1. func greaterThan(a: T) -> (T -> Bool) { 
  2. return { (b: T) -> Bool in b > a } 

這個(gè)方法接受一個(gè)參數(shù),調(diào)用接受不止一個(gè)參數(shù)的方法并返回,像這樣的方法這被稱為Curried functions。(Swift的部分實(shí)例方法就是一種Curried functions)Swift提供了一種特別的語法用于Curried functions,正如它們的名字一樣形象。使用這種語法,我們的方法變成了這樣:

  1. func greaterThan(a: T)(_ b: T) -> Bool { 
  2. return b > a 
  3. func lessThan(a: T)(_ b: T) -> Bool { 
  4. return b < a 

這樣我們有了***個(gè)版本的switch語句:

  1. switch x { 
  2. case greaterThan(0): 
  3. print("大于零"
  4. case lessThan(0): 
  5. print("小于零"
  6. case 0
  7. print("等于零"
  8. default
  9. fatalError("不會(huì)發(fā)生"

很不錯(cuò),但看看default,這個(gè)解決方案不能給編譯器任何提示進(jìn)行完整性檢查,所以我們不得不提供一個(gè)default。如果你確定模式覆蓋了每一個(gè)可能的值,在default下調(diào)用fatalError()是一個(gè)不錯(cuò)的主意,這表明這段代碼絕對(duì)不會(huì)執(zhí)行到。

自定義操作符

回想一開始的想法,以及那段偽代碼。理想情況下,我們想用>0和<0取代greaterThan(0)和lessThan(0)。

自定義操作符存在爭(zhēng)議,因?yàn)槠渌x者經(jīng)常不熟悉這些,它們降低了可讀性?;氐轿覀兊睦又?,類似greaterThan(0)則是完全可讀,所以完全可以認(rèn)為不需要自定義操作符。但同時(shí),每個(gè)人都知道>0意味著什么。所以讓我們來嘗試一下,但正如我們將看到的,它不會(huì)很漂亮。

我們自定義的操作符是一元的——它們只有一個(gè)操作數(shù)。同時(shí),它們是前置操作符(而不是后置,那種操作符在操作數(shù)后的)。在一元操作符和操作數(shù)之間不能有空格,因?yàn)镾wift用空格來區(qū)分一元和二元操作符。此外,<不允許用作前置操作符,我們只好用別的東西代替。(>允許前置,但不是允許后置)。

我建議我們使用~>和~<。雖然~>只是非常像箭頭并不理想,但波浪號(hào)暗示了模式匹配操作符~=。其他我可以想出的操作符(如>>和<<)則容易造成混淆。

9月25日更新:我從Nate Cook那了解到操作符~>在標(biāo)準(zhǔn)庫(kù)中已經(jīng)存在。雖然它的實(shí)現(xiàn)都沒有公有,但Nate發(fā)現(xiàn)它是用來增加集合的索引。鑒于此,為一個(gè)完全不同的目的而使用相同的操作符可能不是一個(gè)好主意。你可以選個(gè)別的。

真正的實(shí)現(xiàn)并不重要。我們要做的就只是聲明操作符和實(shí)現(xiàn)方法,這些只是我們已有的方法greaterThan和lessThan的委托:

  1. prefix operator ~> { } 
  2. prefix operator ~< { } 
  3. prefix func ~>(a: T)(_ b: T) -> Bool { 
  4. return greaterThan(a)(b) 
  5. prefix func ~ Bool { 
  6. return lessThan(a)(b) 

這樣,我們的switch語句變成:

  1. switch x { 
  2. case ~>0
  3. print("大于零"
  4. case ~<0
  5. print("小于零"
  6. case 0
  7. print("等于零"
  8. default
  9. fatalError("不會(huì)發(fā)生"

再次提醒,操作符和操作數(shù)之間沒有空格。

這樣已是我們的極限,很接近原始計(jì)劃,但顯然并不***。

9月19日更新:Joseph Lord提醒我,Swift有一個(gè)類似的語法:

  1. switch x { 
  2. case _ where x > 0
  3. print("大于零"
  4. case _ where x < 0
  5. print("小于零"
  6. case 0
  7. print("等于零"
  8. default
  9. fatalError("不會(huì)發(fā)生"

這個(gè)語法,雖然它可能不像我們定制的解決方案那么簡(jiǎn)潔,但絕對(duì)足夠好,因?yàn)槟悴粦?yīng)該為這么一個(gè)簡(jiǎn)單的目的此創(chuàng)建一個(gè)自定義語法。然而,我們的解決方案是一般化的,能在不同的地方應(yīng)用。繼續(xù)往下看。

其他應(yīng)用

順便說一句,這里給出的解決方案是非常一般化的。我們重載的模式匹配操作符~=適用任何T類型和任何接受T類型返回Bool的方法。換句話說,我們的實(shí)現(xiàn)使得pattern ~= value和pattern(value)一樣好用。更進(jìn)一步,switch value { case pattern: ... }和 if pattern(value) { ... }一樣好用。

檢查數(shù)字奇偶性

舉幾個(gè)例子。首先,一個(gè)簡(jiǎn)單的例子說明了其可應(yīng)用性,雖然其實(shí)際意義不大。假設(shè)你有一個(gè)方法isEven用來檢查數(shù)數(shù)字是不是偶數(shù):

  1. func isEven(a: T) -> Bool { 
  2. return a % 2 == 0 

現(xiàn)在:

  1. switch isEven(x) { 
  2. case true: print("偶數(shù)"
  3. case false: print("奇數(shù)"

可以變成:

  1. switch x { 
  2. case isEven: print("偶數(shù)"
  3. default: print("奇數(shù)"

注意default,下面的代碼無效:

  1. switch x { 
  2. case isEven: print("偶數(shù)"
  3. case isOdd: print("奇數(shù)"
  4. // error: Switch must be exhaustive, consider adding a default clause 

匹配字符串

舉一個(gè)更實(shí)際的例子,假設(shè)你想要匹配一個(gè)字符串的前綴或后綴。我們先寫兩個(gè)方法hasPrefix和hasSuffix,它們接受兩個(gè)字符串,并檢查***個(gè)參數(shù)是否是第二個(gè)參數(shù)的前綴/后綴。這些只是現(xiàn)有標(biāo)準(zhǔn)庫(kù)中String.hasPrefix和String.hasSuffix方法的變形,只是使參數(shù)有一個(gè)方便的順序(前綴/后綴***,完整的字符串第二)。如果你經(jīng)常使用Partial Applied Function(偏應(yīng)用方法,缺少部分參數(shù)的方法)并將它們傳遞給其他方法,你會(huì)發(fā)現(xiàn)你常常需要重復(fù)出現(xiàn)參數(shù)來符合被調(diào)用方法的參數(shù)。煩人,但這不難。

  1. func hasPrefix(prefix: String)(value: String) -> Bool { 
  2. return value.hasPrefix(prefix) 
  3. func hasSuffix(suffix: String)(value: String) -> Bool { 
  4. return value.hasSuffix(suffix) 

現(xiàn)在我們可以這樣,在我看來這很容易閱讀了:

  1. let str = "ABCDEFGHIJKLMNOPQRSTUVWXYZ" 
  2. switch str { 
  3. case hasPrefix("B"), hasPrefix("C"): 
  4. print("以B或C開頭"
  5. case hasPrefix("D"): 
  6. print("以D開頭"
  7. case hasSuffix("Z"): 
  8. print("以Z結(jié)尾"
  9. default
  10. print("其他情況"

結(jié)論

為了解決我們最初的問題,我們提出了一個(gè)一般化的解決方案,它可以解決很多不同的問題。我發(fā)現(xiàn)這種情況經(jīng)常發(fā)生,當(dāng)你將方法看作值來傳遞,它可以用在你通常想不到的地方。這是函數(shù)式編程改進(jìn)可組合性這一說法背后的核心概念之一。

擴(kuò)展Swift的模式匹配系統(tǒng),使其有了新的功能,無論是對(duì)于內(nèi)置類型還是自定義類型,都是極其強(qiáng)大的。一如既往,注意不要把它擴(kuò)展太多。即使一個(gè)自定義的語法看上去比保守的解決方案更為干凈,但對(duì)于那些不熟悉它的人它使代碼更加難讀了。

責(zé)任編輯:chenqingxiang 來源: CocoaChina
相關(guān)推薦

2022-08-29 15:26:58

MySQLSQL模式

2023-10-30 10:20:45

2010-07-21 13:35:22

Perl模式匹配

2010-06-04 10:14:14

MySQL匹配模式

2009-09-09 11:37:08

Scala的模式匹配

2015-07-08 16:43:02

Configurati

2015-03-16 10:33:14

Swift指針

2015-01-21 16:25:29

Swift指針

2010-07-15 17:58:31

Perl模式

2010-07-26 10:51:26

Perl模式匹配

2010-07-26 11:02:19

Perl模式匹配

2023-11-28 13:20:00

Rust匹配枚舉

2023-04-11 08:54:57

字符串匹配算法

2022-05-11 09:01:54

Swift類型系統(tǒng)幻象類型

2022-07-04 08:54:39

Swift處理器項(xiàng)目

2010-07-16 09:14:49

Perl模式

2011-08-23 15:34:56

Lua模式 匹配

2014-02-19 10:19:12

YARA惡意軟件

2010-07-26 10:37:00

Perl模式匹配

2010-07-21 13:27:06

Perl模式匹配
點(diǎn)贊
收藏

51CTO技術(shù)棧公眾號(hào)