Swift 模式匹配總結(jié)

Swift 模式匹配總結(jié)

基本用法

對(duì)枚舉的匹配:

在swift中 不需要使用break跳出當(dāng)前匹配蚁阳,默認(rèn)只執(zhí)行一個(gè)case就結(jié)束

enum Weather {
    case rain, snow, wind, sunny
}

let todayWeather = Weather.rain

switch todayWeather {
case .rain:
    print("下雨")
case .snow:
    print("下雪")
case .wind:
    print("刮風(fēng)")
case .sunny:
    print("晴天")
}

一次匹配多個(gè)模式:

switch todayWeather {
case .rain, .snow:
    print("天氣不太好,出門要打傘")
case .wind:
    print("刮風(fēng)")
case .sunny:
    print("晴天")
}

枚舉匹配時(shí)還可以綁定枚舉的關(guān)聯(lián)值:

enum Weather {
    case rain(level: Int), snow(level: Int), wind(level: Int), sunny
}

let todayWeather = Weather.rain(level: 1)

switch todayWeather {
case .rain(let level):
    if level > 10 {
        print("大雨")
    }else {
        print("小雨")
    }
case let .snow(level):
    if level > 10 {
        print("大雪")
    }else {
        print("小雪")
    }
case .wind(let _):
    print("刮風(fēng)")
case .sunny:
    print("晴天")
}

// 這兩種寫法是等價(jià)的
case .rain(let level):
case let .rain(level):

可以使用固定值對(duì)枚舉關(guān)聯(lián)值進(jìn)行更進(jìn)一步的匹配:

// 下面的代碼中梅肤,首先匹配 .rain中的duration是否為24。如果不滿足條件則繼續(xù)后續(xù)case的匹配

enum Weather {
    case rain(level: Int, duration: Int), snow(level: Int), wind(level: Int), sunny
}

let todayWeather = Weather.rain(level: 1, duration: 24)

switch todayWeather {
case .rain(let _, 24):
    print("全天有雨")
case .rain(let level, let _):
    if level > 10 {
        print("大雨")
    }else {
        print("小雨")
    }
case let .snow(level):
    if level > 10 {
        print("大雪")
    }else {
        print("小雪")
    }
case .wind(let _):
    print("刮風(fēng)")
case .sunny:
    print("晴天")
}

雖然同樣是 .rain 但由于關(guān)聯(lián)值的不同燎孟,所以被視為兩個(gè)不同的case

case .rain(let _, 24): // 由于不關(guān)心 level证鸥,所以使用 _ 來進(jìn)行占位
case .rain(let level, let _):

配合 Where 使用,加強(qiáng)匹配效果

繼續(xù)上面的例子别凤,通過where語法進(jìn)行改造

改造前:

switch todayWeather {
case .rain(let _, 24):
    print("全天有雨")
case .rain(let level, let _):
    if level > 10 {
        print("大雨")
    }else {
        print("小雨")
    }
default:
    break
}

改造后:

switch todayWeather {
case let .rain(_, duration) where duration == 24:
    print("全天有雨")
case let .rain(level, _) where level > 10:
    print("大雨")
case let .rain(level, _) where level < 10:
    print("小雨")
default:
    break
}

對(duì)原生類型的匹配

不同于oc,swift 除了 enum 和 int類型之外领虹,還支持多種原生類型的匹配:String规哪,Tuple,Range等

String

let name = "Spiderman"

switch name {
case "Ironman":
    print("鋼鐵俠")
case "Spiderman":
    print("蜘蛛俠")
default: // 由于無法窮舉所有字符串塌衰,所以必須添加 default 
    print("不認(rèn)識(shí)")
}

注意:當(dāng)匹配的類型無法窮舉時(shí)诉稍,必須添加 default

Tuple

注意:switch是按照case順序從上到下進(jìn)行匹配,如果同時(shí)滿足多個(gè)case最疆,也只會(huì)執(zhí)行最上面的那個(gè)

let point = (x: 10, y: 0)

switch point {
case (0, 0): 
    print("原點(diǎn)")
case (0, _): 
    print("Y軸p偏移")
case (let x, 0):
    print("X軸偏移:\(x)")
case (let x, let y) where x == y: 
    print("X = Y")
default: 
    break
}

元組匹配類似于枚舉關(guān)聯(lián)值的匹配

Range

let index = 100

switch index {
case 0...20:
    print("20以內(nèi)")
case 21:
    print("正好21")
case 30..<100:
    print("30到100之間杯巨,不包括100")
default:
    print("其它范圍")
}

類型匹配

匹配模式可以應(yīng)用于類型上,這時(shí)我們需要用到兩個(gè)關(guān)鍵字 is肚菠、as (注意:不是as?舔箭,盡管它們的機(jī)制很相似,但是它們的語義是不同的(“嘗試進(jìn)行類型轉(zhuǎn)換蚊逢,如果失敗就返回 nil” vs “判斷這個(gè)模式是不是匹配這種類型”))

protocol Animal {
    var name: String { get }
}

struct Dog: Animal {
    var name: String {
        return "dog"
    }
    
    var runSpeed: Int
}

struct Bird: Animal {
    var name: String {
        return "bird"
    }
    
    var flightHeight: Int
}

struct Fish: Animal {
    var name: String {
        return "fish"
    }
    
    var depth: Int
}

let animals = [Dog.init(runSpeed: 55), Bird.init(flightHeight: 2000), Fish.init(depth: 100)]

for animal in animals {
    switch animal {
    case let dog as Dog:
        print("\(dog.name) can run \(dog.runSpeed)")
    case let fish as Fish:
        print("\(fish.name) can dive depth \(fish.depth)")
    case is Bird:
        print("bird can fly!")
    default:
        print("unknown animal!")
    }
}

自定義類型匹配

通常情況下,我們自定的類型是無法進(jìn)行模式匹配的箫章,也就是不能在 switch/case 語句中使用烙荷。如果想要達(dá)到可匹配的效果,那么就有必有了解一下匹配操作符 ~=

struct BodyFatRate {
    var weight: Float
    var fat: Float
}

let player = BodyFatRate(weight: 180, fat: 30)

func ~=(lhs: Range<Float>, rhs: BodyFatRate) -> Bool {
    return lhs.contains(rhs.fat / rhs.weight)
}

switch player {
case 0.0..<0.15:
    print("難以置信")
case 0.15..<0.2:
    print("健康")
case 0.21..<0.99:
    print("該減肥了")
default:
    break
}

上面的代碼中檬寂,我們重載的~=操作符终抽,簡單的實(shí)現(xiàn)了體脂率BodyFatRate和range的匹配。該方法一共接收兩個(gè)參數(shù)并返回一個(gè)bool類型的匹配結(jié)果桶至。第一個(gè)參數(shù)lhs為case值昼伴,是體脂率的范圍。第二個(gè)參數(shù)為switch傳入的值player镣屹。兩個(gè)參數(shù)的意義千萬不要搞混了圃郊。

關(guān)于 Optional 匹配

當(dāng)switch傳入的值為optional時(shí),如果不想解包女蜈,可以使用x?(相當(dāng)于Optional.some(x))語法糖來匹配可選值持舆。

let optionalValue: Int? = 5

switch optionalValue {
case 1?:
    print("it's one")
case 2?:
    print("it's two")
case .none:
    print("it's nil")
default:
    print("it's others")
}

上面的代碼中色瘩,optionalValue相當(dāng)于 Optional.some(5),所以也需要同Optional.some(x)進(jìn)行比較逸寓。如果case中的值沒有加上 居兆?則會(huì)報(bào)錯(cuò):expression pattern of type 'Int' cannot match values of type 'Int?'。當(dāng) optionalValue 為nil時(shí)竹伸,則與 .none 匹配泥栖。在Swift中,Int型被認(rèn)為是無法窮舉的勋篓,故必須有default聊倔。

一些簡介高效的匹配語法

除了上面的常規(guī)的模式匹配方式,還有一些簡潔而高效的匹配語法生巡。在簡化了代碼結(jié)構(gòu)的同時(shí)耙蔑,也能提高開發(fā)效率。

if case let

某些場景下孤荣,我們只想與特定的一個(gè)case進(jìn)行匹配甸陌。這時(shí)可以使用 if case let x = y { … } 形式的語法。這種方式等同于 switch y { case let x: … }盐股。文章一開始的例子:

enum Weather {
    case rain(level: Int), snow(level: Int), wind(level: Int), sunny
}

let todayWeather = Weather.rain(level: 1)

當(dāng)我們只想判斷是否是雨天并打印雨的等級(jí)時(shí)钱豁,一般的寫法是這樣子:

switch todayWeather {
case let .rain(level):
    print("雨的等級(jí):\(level)")
default:
    break
}

使用 if case let 后:

if case let .rain(level) = todayWeather {
    print("雨的等級(jí):\(level)")
}

顯然這種寫法更加簡潔緊湊,可讀性也有一定提高疯汁。在這基礎(chǔ)之前還可以配合where來使用牲尺。

if case let where

if case let .rain(level) = todayWeather where level < 5 {
    print("下小雨")
}

現(xiàn)在上面這種寫法會(huì)報(bào)錯(cuò):expected ',' joining parts of a multi-clause condition
if case let .rain(level) = todayWeather where level < 5 ,應(yīng)該改成如下形式:

if case let .rain(level) = todayWeather, level < 5 {
    print("下小雨")
}

guard case let

與if case let 對(duì)應(yīng)的也有 guard case let幌蚊,用法就不多說了谤碳。

for case

當(dāng)需要對(duì)數(shù)組元素進(jìn)行模式匹配時(shí),就可以使用 for case 語法溢豆。比如有下面一組天氣

let weatherInWeek = [Weather.rain(level: 1),
                     Weather.snow(level: 9),
                     Weather.sunny,
                     Weather.rain(level: 7),
                     Weather.snow(level: 9),
                     Weather.sunny,
                     Weather.snow(level: 3)]

想要篩選出下大雨的天氣蜒简,level > 5

for case let .rain(level) in weatherInWeek where level > 5 {
    print("下大雨")
}

總結(jié)

Swift中的匹配模式要比OC中強(qiáng)大的多。歸納起來大概分為以下幾點(diǎn):

  1. 除了可以匹配枚舉類型外漩仙,支持更多的原生類型匹配搓茬,包過Int、String队他、Float卷仑、Tuple、Range等
  2. 可以對(duì)類型進(jìn)行匹配麸折,配合 as锡凝、is 來使用
  3. 重載匹配操作符~=,可以支持自定義類型的匹配操作
  4. 可結(jié)合where磕谅、if私爷、guard雾棺、for來使用,使得代碼簡潔優(yōu)雅而高效
  5. 對(duì)Optional類型的匹配支持很友好衬浑,可簡化部分判斷邏輯
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末捌浩,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子工秩,更是在濱河造成了極大的恐慌尸饺,老刑警劉巖,帶你破解...
    沈念sama閱讀 212,686評(píng)論 6 492
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件助币,死亡現(xiàn)場離奇詭異浪听,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī)眉菱,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,668評(píng)論 3 385
  • 文/潘曉璐 我一進(jìn)店門迹栓,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人俭缓,你說我怎么就攤上這事克伊。” “怎么了华坦?”我有些...
    開封第一講書人閱讀 158,160評(píng)論 0 348
  • 文/不壞的土叔 我叫張陵愿吹,是天一觀的道長。 經(jīng)常有香客問我惜姐,道長犁跪,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 56,736評(píng)論 1 284
  • 正文 為了忘掉前任歹袁,我火速辦了婚禮坷衍,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘宇攻。我一直安慰自己惫叛,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 65,847評(píng)論 6 386
  • 文/花漫 我一把揭開白布逞刷。 她就那樣靜靜地躺著,像睡著了一般妻熊。 火紅的嫁衣襯著肌膚如雪夸浅。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 50,043評(píng)論 1 291
  • 那天扔役,我揣著相機(jī)與錄音帆喇,去河邊找鬼。 笑死亿胸,一個(gè)胖子當(dāng)著我的面吹牛坯钦,可吹牛的內(nèi)容都是我干的预皇。 我是一名探鬼主播,決...
    沈念sama閱讀 39,129評(píng)論 3 410
  • 文/蒼蘭香墨 我猛地睜開眼婉刀,長吁一口氣:“原來是場噩夢啊……” “哼吟温!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起突颊,我...
    開封第一講書人閱讀 37,872評(píng)論 0 268
  • 序言:老撾萬榮一對(duì)情侶失蹤鲁豪,失蹤者是張志新(化名)和其女友劉穎,沒想到半個(gè)月后律秃,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體爬橡,經(jīng)...
    沈念sama閱讀 44,318評(píng)論 1 303
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,645評(píng)論 2 327
  • 正文 我和宋清朗相戀三年棒动,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了糙申。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 38,777評(píng)論 1 341
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡船惨,死狀恐怖柜裸,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情掷漱,我是刑警寧澤粘室,帶...
    沈念sama閱讀 34,470評(píng)論 4 333
  • 正文 年R本政府宣布,位于F島的核電站卜范,受9級(jí)特大地震影響衔统,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜海雪,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 40,126評(píng)論 3 317
  • 文/蒙蒙 一锦爵、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧奥裸,春花似錦险掀、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,861評(píng)論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至侠鳄,卻和暖如春埠啃,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背伟恶。 一陣腳步聲響...
    開封第一講書人閱讀 32,095評(píng)論 1 267
  • 我被黑心中介騙來泰國打工碴开, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 46,589評(píng)論 2 362
  • 正文 我出身青樓潦牛,卻偏偏與公主長得像眶掌,于是被迫代替她去往敵國和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子巴碗,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 43,687評(píng)論 2 351

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