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):
- 除了可以匹配枚舉類型外漩仙,支持更多的原生類型匹配搓茬,包過Int、String队他、Float卷仑、Tuple、Range等
- 可以對(duì)類型進(jìn)行匹配麸折,配合 as锡凝、is 來使用
- 重載匹配操作符~=,可以支持自定義類型的匹配操作
- 可結(jié)合where磕谅、if私爷、guard雾棺、for來使用,使得代碼簡潔優(yōu)雅而高效
- 對(duì)Optional類型的匹配支持很友好衬浑,可簡化部分判斷邏輯