393,Swift中if case let 的模式匹配模式

if case

在學(xué)習(xí) if case之前泼橘,我們先想想,那些地方使用到了case 這個(gè)關(guān)鍵字岁疼,毫無疑問,絕大多數(shù)使用case的時(shí)候缆娃,是在 switch語法中捷绒。
Swift中的if case主要用于模式匹配。跟switch很類似贯要,但它本身有特別的使用場景暖侨,區(qū)別switch在于,switch用于對所有的對象可能值進(jìn)行判斷崇渗,而if case之需要對關(guān)注的可能值進(jìn)行判斷字逗。老規(guī)矩,還是代碼上看看宅广。
假設(shè)我有一個(gè)枚舉變量CarBrand,汽車品牌葫掉。它的結(jié)構(gòu)如下:

enum CarBrand{
   case  BMW
   case  AUDI
   case  BENZ
}

如果使用switch進(jìn)變量匹配,并按照不同的車的品牌輸出對應(yīng)的中文名字(這種需求當(dāng)然可以通過添加屬性值的方式更簡便的解決跟狱,但這里我們只討論if case的使用俭厚,暫時(shí)不關(guān)注其他的方式。)驶臊。我們可能是這樣做的:

let myBrand = CarBrand.BMW
switch myBrand{
    case .BMW: do{   print("寶馬")  }
    case .AUDI: do{   print("奧迪")  }
    case .BENZ: do{   print("奔馳")  }
}

實(shí)際上挪挤,我們可能僅僅是想找出BMW的品牌而已叼丑,但是我卻需要寫一部分跟這個(gè)品牌無關(guān)的代碼。我們可以進(jìn)行一點(diǎn)簡化:

switch myBrand{
    case .BMW: do{   print("寶馬")  }
    default: ()  // do  nothing
}

可能覺得這個(gè)方式還是過于繁瑣扛门,有什么辦法可以像if那樣對枚舉值判斷嗎鸠信?
if case應(yīng)該能幫到你:

let myBrand = CarBrand.BMW
if case .BMW = myBrand{
   print("寶馬")
}

這樣不就舒服多了,不僅沒有冗余的代碼论寨,還有具備了更好的可讀性星立。

if case let

if case let它實(shí)際上不是一個(gè)完整的關(guān)鍵字,它if caselet的組合葬凳。我們同樣會在對枚舉類型進(jìn)行模式匹配的時(shí)候用到它贞铣,不過這個(gè)情況稍微復(fù)雜一點(diǎn)。
假設(shè)我的CarBrand枚舉想要集合更多的東西沮明,比如它的中文名字和生產(chǎn)地。我們可以這樣定義這個(gè)枚舉:

enum CarBrand{
    case  BMW(name:String,Production:String)
    case  AUDI(name:String,Production:String)
    case  BENZ(name:String,Production:String)
}

現(xiàn)在我定一個(gè)枚舉變量:

let myCar = CarBrand.BMW(name: "寶馬",Production: "德國")

如果我想輸出myCar的關(guān)聯(lián)屬性窍奋,直接使用點(diǎn)語法什么的都顯然是不行的荐健。我們可在枚舉中自定義一些輸出關(guān)聯(lián)屬性的方法,但是這個(gè)做法比較繁瑣琳袄,并且破壞了枚舉結(jié)構(gòu)江场。
比較簡單的是可以使用switch:

switch myCar {
  case let CarBrand.BMW(name,Production):
    print("This car named \(name),from\(Production)")
  default: () // 不做任何處理
}

這樣確實(shí)可以的窖逗。同樣我們覺得這樣還是過于繁瑣了址否。

使用if case let

 let myBrand = CarBrand.BMW(name: "寶馬",Production: "德國")
 if case let CarBrand.BMW(name, Production) = myBrand{
     print("\(name),\(Production)")
}

是不是更加簡潔呢。當(dāng)然,if case let 還可一配合where從句寫出更加優(yōu)雅的代碼哦碎紊。

關(guān)于大于或者小于的匹配

//傳統(tǒng)用法
if x>=6 && x < 12 { 

}
//模式匹配用法
if case 6..<12 = x {

}

復(fù)雜一點(diǎn)佑附,如果x是Optional對象,這里可以采用嵌套的模式匹配

//傳統(tǒng)用法
if let x = x, x>=6 && x < 12 { 

}
//模式匹配用法
if case .some(6..<12) = x {

}

pattern有很多種仗考,看一下官方文檔

GRAMMAR OF A PATTERN
pattern → wildcard-pattern
pattern → identifier-pattern
pattern → value-binding-pattern
pattern → tuple-pattern
pattern → enum-case-pattern
pattern → optional-pattern
pattern → type-casting-pattern
pattern → expression-pattern

這里不詳細(xì)介紹這些pattern音同,更多的應(yīng)該switch里面去學(xué)習(xí),也就是說switch里面可以使用的在if里面都可以使用
這里舉一些例子

1. type-casting-pattern

var t : Any = 10
if case is Int = t {
    print("bingo")
}
if t is Int {

}

上面兩種用法結(jié)果是一樣的秃嗜,一種是模式匹配权均,另外一種是常用的is operator

2. tuple-pattern

if case (1..<10, 1..<20) = (7, 8) {

}

在這里(1..<10, 1..<20)是一個(gè)pattern,而不是普通的tuple锅锨,這里會對左右兩邊的tuple的元素一一進(jìn)行patteren match
如果把前面換成一個(gè)tuple就會出錯(cuò)里叽赊,試試看下面的代碼

let pattern = (1..<10, 1..<20)
if case pattern = (7,8) {

}

3. optional-pattern

var t : Any? = 10
// 判斷t是不是nil,和判斷 t 必搞!= nil 等效
if case _? = t {

}
//判斷t是不是nil必指,如果有值則綁定到x
if case let x? = t {

}

4. expression-pattern

前面提到的case 6..<12 = x實(shí)際上就是這一種pattern,實(shí)際上這里調(diào)用了一個(gè)函數(shù)顾画,也是一個(gè)操作符~=

    func ~= (pattern: String, value: Int) -> Bool {
        return pattern == "\(value)"
    }
    if case "123" = 123 {

    }

通過重載~=操作符取劫,我們可以實(shí)現(xiàn)很多自定義的模式匹配匆笤,結(jié)合起來可以有很多有趣的用法

上面通過一些例子介紹了if中的模式匹配用法,在代碼中也有常常遇到這種用法谱邪,這種用法可以看成是switch case的一種簡易寫法炮捧,這樣理解起來就比較容易了

if case expression1 = expression2 {
    statements
}
//等價(jià)于
switch expression2 {
    case expression1:
        statements
    default:
        break
}

available用法

這是一種特殊的if用法,用來判斷運(yùn)行環(huán)境

if #available(platform name version, ..., *) {
    statements to execute if the APIs are available
} else {
    fallback statements to execute if the APIs are unavailable
}

這個(gè)比較簡單惦银,不再介紹

混合使用

上面四種if的使用方法可以混合使用咆课,在一個(gè)if語句中可以有多個(gè)condition,通過,分開扯俱,這些condition會依序執(zhí)行

var t : Any? = 10

if case let xs? = t, xs is Int {
    print("bingo")
}

這里會首先將t進(jìn)行optional-pattern的匹配和綁定书蚪,然后判斷xs是否是Int
condition之前常用&&操作符進(jìn)行的判斷也可以換成使用逗號分割,效果是一樣的

最后看一下官方文檔對ifcondition list的描述

可選類型的模式匹配

截屏2020-12-28 下午5.36.10.png
截屏2020-12-28 下午5.42.09.png

swift之模式(Pattern)

模式簡介

  • 模式代表單個(gè)值或者復(fù)合值的結(jié)構(gòu)迅栅。比如 (10,20)("Tom","Mary")在結(jié)構(gòu)上并無本質(zhì)差別殊校,都是包含兩個(gè)值的元組。

  • swift中的模式分為兩類读存,一類能成功匹配任何類型的值为流,另一類在運(yùn)行時(shí)匹配某個(gè)特定值時(shí)可能會失敗

  • 第一類用于解構(gòu)簡單變量让簿、常量和可選綁定中的值敬察。包括通配符模式標(biāo)識符模式以及包含這兩種模式的值綁定模式元組模式《保可以為這類模式制定一個(gè)類型標(biāo)注莲祸,從而限定他們只匹配某種特定類型的值。

  • 第二類用于全模式匹配椭迎。锐帜,這種情況下你視圖匹配的值在運(yùn)行時(shí)可能不存在。包括枚舉用例模式畜号、可選模式抹估、表達(dá)式模式類型轉(zhuǎn)換模式

具體實(shí)例

  • 通配符模式(Wildcard Pattern):不關(guān)注匹配的值
 for _ in 1...3 {
    // Do something three times
 }
  • 標(biāo)識符模式(Identifier Pattern):匹配任何值并且把匹配到的值綁定給變量或者常量弄兜。

  • 值綁定模式(Value-Binding pattern):把匹配到的值綁定給一個(gè)變量或者常量药蜻,綁定給常量時(shí)用let,綁定給變量時(shí)用var

 let point = (1,2)
 switch point {
 case let (x,y):
    print("The point is at (\(x), \(y))") //The point is at (1, 2)
 }
  • 元組模式
  • 逗號分隔的具有一個(gè)或者多個(gè)模式的列表
  • 可以使用類型標(biāo)注去限定一個(gè)元組模式可以匹配那些元組類型替饿。
  • 元組模式被用于for-in語句或者變量火證常量聲明時(shí)语泽,金可以包含通配符模式、標(biāo)識符模式视卢、可選模式或者其他包含這些模式的元組模式踱卵。
 let points = [(0,0),(1,0),(2,0),(0,3),(1,1),(2,1)]
 for (x,y) in points where y == 0 || x == 0 {
    print("Point(\(x), \(y)) is on axis!")
 }

 for point in points {
    switch point {
    case (_,0), (0,_):
        print("Point(\(point.0), \(point.1)) is on axis!")
    default:
        break
    }
 }
  • 枚舉用例模式(Enumeration Case Pattern)
 enum SomuEnum {
    case left,right
 }
 let direction: SomuEnum? = .left
 switch direction {
 case .left:
    print("Turn left!")
 case .right:
    print("Turn right!")
 case nil:
    print("Keep going straiht!")
 }


var someOptional: Optional<Int> = 43
 //枚舉用例模式匹配
 switch someOptional {
 case .some(let x):
    print(x) // 43
 case .none:
    print("nil")
 }
 if case .some(let x) = someOptional {
    print(x) // 43
 }

  • 可選項(xiàng)模式(Optional Pattern)

  • 可選項(xiàng)模式匹配Optional<Wrapped>枚舉在some(Wrapped)中包裝的值。

 var someOptional: Optional<Int> = 43
 if  case let x? = someOptional {
    print(x) // 43
 }

 let arrayOfOptionalInts: [Int?] = [nil,2,3,5,nil]
 for case let number? in arrayOfOptionalInts {
    print("Found a \(number) !")
 }

  • 類型轉(zhuǎn)換模式(Type-Casting Pattern)

  • is模式:只出現(xiàn)在switch語句的case標(biāo)簽中,當(dāng)一個(gè)值的類型在運(yùn)行時(shí)與模式右邊指定類型一致或者是其子類的情況下才會匹配惋砂。

  • as模式:當(dāng)一個(gè)值的類型在運(yùn)行時(shí)和右邊指定類型一致或者是其子類的情況下妒挎,才會匹配。如果匹配成功西饵,被匹配值的類型被撞換成as模式右邊指定的類型酝掩。

 class MediaItem {
    var name: String
    init(name: String) {
        self.name = name
    }
 }

 class Movie: MediaItem {
    var director: String
    init(name: String, director: String) {
        self.director = director
        super.init(name: name)
    }
 }
 
 class Song: MediaItem {
    var artist: String
    init( name: String, artist: String) {
        self.artist = artist
        super.init(name: name)
    }
 }

 let library: [MediaItem] = [
    Movie(name: "Casablanca", director: "Michael Curtiz"),
    Song(name: "Blue Suede Shoes", artist: "Elvis Presley"),
    Movie(name: "Citizen Kane", director: "Orson Welles"),
    Song(name: "The One And Only", artist: "Chesney Hawkes"),
    Song(name: "Never Gonna Give You Up", artist: "Rick Astley")
 ]

 for mediaItem in library {
    switch mediaItem {
    case is Song :
        print("Song: \(mediaItem.name)")
    case let movie as Movie:
        print("Movie: \(movie.name)  director: \(movie.director)")
    default:
        break
    }
 }

  • 表達(dá)式模式(Expression Pattern)

    • 只出現(xiàn)在switchcase標(biāo)簽中。

    • 表達(dá)式模式代表的表達(dá)式會使用Swift的標(biāo)準(zhǔn)庫中的~=運(yùn)算符與輸入表達(dá)式的值進(jìn)行比較眷柔,如果~=預(yù)算內(nèi)算符返回true,則匹配成功期虾。因此自定義類型要使用表達(dá)式模式匹配,需要重載~=運(yùn)算符驯嘱。

let point = (0,0)
 func ~= (pattern: String, value: Int) -> Bool {
    return  pattern == "\(value)"
 }
 switch point {
 case ("0","0"):
    print("(0,0) is at the origin.")
 default:
    print("The point is at (\(point.0), \(point.1))")
 }

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末镶苞,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子鞠评,更是在濱河造成了極大的恐慌茂蚓,老刑警劉巖,帶你破解...
    沈念sama閱讀 218,122評論 6 505
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件剃幌,死亡現(xiàn)場離奇詭異煌贴,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī)锥忿,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,070評論 3 395
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來怠肋,“玉大人敬鬓,你說我怎么就攤上這事◇细鳎” “怎么了钉答?”我有些...
    開封第一講書人閱讀 164,491評論 0 354
  • 文/不壞的土叔 我叫張陵,是天一觀的道長杈抢。 經(jīng)常有香客問我数尿,道長,這世上最難降的妖魔是什么惶楼? 我笑而不...
    開封第一講書人閱讀 58,636評論 1 293
  • 正文 為了忘掉前任右蹦,我火速辦了婚禮,結(jié)果婚禮上歼捐,老公的妹妹穿的比我還像新娘何陆。我一直安慰自己,他們只是感情好豹储,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,676評論 6 392
  • 文/花漫 我一把揭開白布贷盲。 她就那樣靜靜地躺著,像睡著了一般剥扣。 火紅的嫁衣襯著肌膚如雪巩剖。 梳的紋絲不亂的頭發(fā)上铝穷,一...
    開封第一講書人閱讀 51,541評論 1 305
  • 那天,我揣著相機(jī)與錄音佳魔,去河邊找鬼曙聂。 笑死,一個(gè)胖子當(dāng)著我的面吹牛吃引,可吹牛的內(nèi)容都是我干的筹陵。 我是一名探鬼主播,決...
    沈念sama閱讀 40,292評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼镊尺,長吁一口氣:“原來是場噩夢啊……” “哼朦佩!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起庐氮,我...
    開封第一講書人閱讀 39,211評論 0 276
  • 序言:老撾萬榮一對情侶失蹤语稠,失蹤者是張志新(化名)和其女友劉穎,沒想到半個(gè)月后弄砍,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體仙畦,經(jīng)...
    沈念sama閱讀 45,655評論 1 314
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,846評論 3 336
  • 正文 我和宋清朗相戀三年音婶,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了慨畸。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 39,965評論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡衣式,死狀恐怖寸士,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情碴卧,我是刑警寧澤弱卡,帶...
    沈念sama閱讀 35,684評論 5 347
  • 正文 年R本政府宣布,位于F島的核電站住册,受9級特大地震影響婶博,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜荧飞,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,295評論 3 329
  • 文/蒙蒙 一凡人、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧叹阔,春花似錦划栓、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,894評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春委煤,著一層夾襖步出監(jiān)牢的瞬間堂油,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 33,012評論 1 269
  • 我被黑心中介騙來泰國打工碧绞, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留府框,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 48,126評論 3 370
  • 正文 我出身青樓讥邻,卻偏偏與公主長得像迫靖,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個(gè)殘疾皇子兴使,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,914評論 2 355

推薦閱讀更多精彩內(nèi)容