Swift中的模式匹配

Swift里的switch比OC里面強大很多,switch的主要特性就是模式匹配。
下面先舉個非常簡單的例子。

let people = (age: 27, name: "karl")
switch people {
case (_, let name):
  print(name)
default:
  break
}
輸出 karl

看完上面的代碼,如果看不懂沒關(guān)系,下面將對模式匹配經(jīng)行詳解。

普通模式

這是最常用的寫法,用法與OC基本相同。

switch 1 {
case 1:
  print("1")
}

綁定變量

enum School {
  case Student(name: String, age: Int, address: String?)
  case Teacher(name: String, age: Int, address: String?)
}
let karl =  School.Student(name: "karl", age: 27, address: "AH")
switch student {
case .Student(let name, let age, let address):
  print("\(name) -- \(age) -- \(address)")
default:
  break
}

我們創(chuàng)建karl這個常量時給其設(shè)置了name,age,address,我們就可以在switch里面通過值綁定的形式將其取出來。
在上面的case .Student(let name, let age, let address):用了三個let顯得有些繁瑣,可以這樣寫case let .Student(name, age, address):


通配符

使用通配符表示忽略這個值,這個值不重要,我不需要使用到它,但是這個也分為兩種情況。

let student = School.Student(name: "karl", age: 27, address: nil)
let teacher = School.Teacher(name: "wang", age: 50, address: "AH")
  • _ :這個值完全不重要,可有可無,是什么我都不關(guān)心。
switch student {
case let .Teacher(name, _, _):
    print("name: \(name)")
default: break
} 

這里第二個參數(shù)和第三個參數(shù)就是使用了通配符表示,不關(guān)心它是什么值,只要前面參數(shù)能匹配成功就可以,就算成立。

  • _? :這個值我雖然不用,但是它必須有值。
switch student {
case let .Student(name, age, _?):
   print("name: \(name)")
default: break
} 

這里第三個參數(shù)我使用了_?,表示這個值我不用,但是不能為nil

使用于元祖

就是我最上面舉的那個例子。

let people = (age: 27, name: "karl")
switch people {
case (_, let name):
  print(name)
default:
  break
}

我并不關(guān)心age這個值,我只想取出name。


在條件中使用where語句

switch student {
case let .Student(_, age, _) where age > 30:
  print("\(age)")
default:
  break
}

當(dāng)case匹配成功,并且綁定的值要符合where的條件才算真的成立。


匹配Range

let number = 10
switch number {
case 0 ... 7:
  print("0 ... 7")
case 10 ..< 20:
  print("10 ..< 20")
default:
  break
}
輸出: 10 ..< 20

可以通過區(qū)間的形式來匹配


類型推斷和類型轉(zhuǎn)換

protocol Animal { }
struct Dog: Animal { }
struct Cat: Animal { }

var animalArray = [Animal]()
let dog = Dog()
let cat = Cat()
animalArray.append(dog)
animalArray.append(cat)

for item in animalArray {
  switch item {
  case is Dog:
    print("dog")
  case let cat as Cat:
    print("cat")
  default:
    break
  }
}

可以通過is判斷這個值是否是某個類型。是的話,表示匹配成功。
通過 as可以將這個值嘗試轉(zhuǎn)換為某個類型,如果成功,則表示匹配成功。


模式匹配操作符

struct Size {
  var width: CGFloat
  var height: CGFloat
}

let size = Size(width: 20, height: 20)
func ~=(lhs: CGFloat, rhs: Size) -> Bool {
  return lhs == rhs.height * rhs.width
}

switch size {
case 400:
  print("??")
default:
  print("!!!")
}

我們可以重載~=這個操作符,讓我們自定義switch匹配規(guī)則
看上面的代碼,我們重載了~=操作符,操作符有兩個參數(shù),返回一個Bool值。

  • 參數(shù)1:switch中case后的值
  • 參數(shù)2:switch中傳入的值
  • 返回值:是否匹配成功

我的這個~=方法定義的是size的width * height 等于 case的值就表示匹配成功。
這里只是一個簡單的實驗。具體用途需要看實際情況。


關(guān)于Optionals

let count: Int? = 5
switch count {
case 1?:
  print("1")
case 3?:
  print("3")
case 5?:
  print("5")
case _:
  print("nil")
default:
  break
}

匹配一個可選值,要使用?的形式,要不然會報類型不匹配的錯。
這個時候count其實相當(dāng)于Optional.some(5)
而下面的各個case相當(dāng)于

case Optional.some(1):
  print("1")
case Optional.some(3):
  print("3")
case Optional.some(5):
  print("5")

使用?的形式寫,算是一個語法糖


if case let

if case let a = b {
}

表示b是否能匹配上a。
這就相當(dāng)于

switch b {
  case let a:
    break
}

當(dāng)只有一個條件的時候,用switch會顯得冗余,直接用if case let會使代碼讀起來更便捷。
直接拿上面的綁定變量那一節(jié)的內(nèi)容來說,如果直接這么寫的話,會顯得更易讀,而且也會少點代碼。

if case let .Student(name, age, _?) = student {
  print(name)
}

當(dāng)然,這里也是可以用where語句的,但是在swift3以后,有些場景的where被逗號給代替了,如果給上面的if case let加上where條件的話,就是這樣:

if case let .Student(name, age, _?) = student, age > 20 {
  print(name)
}

一行代碼就可以寫出一個很強的表達式。


guard case let

guard case let的用法和if case let是一樣的,區(qū)別也僅僅是ifguard的用法區(qū)別,就不多啰嗦。


for case let

一個數(shù)組里存放的是Any類型的可選值,我想輸出這個數(shù)組里面大于10的數(shù)字。

let array: [Any?] = [1,2,nil,4,54,65,76, ""]

在不知道 for case let之前,寫法是這樣的。
先遍歷出所有的元素,在嘗試元素轉(zhuǎn)成Int,在判斷是否大于10。

for item in array {
  if let element = item as? Int, element > 10 {
    
  }
}

在看看下面這種寫法。
先將遍歷出來的元素綁定到x上,并且x不能為nil,然后在用where增加一個要是>10的條件。
是不是感覺清爽很多,又是一行代碼寫出一個強有力的表達式。

for case let x? as Int? in array where x > 10 {
  print(x)
}

在舉個例子,還用一下之前定義的enum School

let xiaoming = School.Student(name: "xiaoming", age: 10, address: nil)
let wang = School.Teacher(name: "wang", age: 50, address: "AH")
let li = School.Teacher(name: "li", age: 50, address: "ZJ")
let xiaoqiang = School.Student(name: "xiaoming", age: 14, address: "BJ")
let xiaohao = School.Student(name: "xiaohao", age: 17, address: "SH")

這里有5個School類型的常量,有2個Teacher,3個Student
把他們都加到一個數(shù)組里,

let schoolArray = [xiaoming, wang, xiaoqiang, li, xiaohao]

我現(xiàn)在想獲取到數(shù)組里的Student,并且address不能為空,并且age要小于15的。
看看我們使用for case let怎么解決。

for case let .Student(name, age, _?) in schoolArray where age < 15 {
  print(name)
}

還是一行代碼,帥!


很多時候,我們在網(wǎng)上看到一些新的語法,看著介紹很牛逼,也都能看明白,但是一讓自己放到項目里就懵逼,因為沒用過,所有壓根不知道這種形式改怎么用到項目里。
這個舉個最常見的的簡單的例子,網(wǎng)絡(luò)請求。

enum Result {
  case success(Data)
  case error(Error)
}

func loadNetworkData(_ urlString: String, completion: @escaping (Result) -> ()) {
  guard let url = URL(string: urlString) else {
    fatalError("url 錯誤")
  }
  let task = URLSession.shared.dataTask(with: url) { (data, _, error) in
    var result: Result!
    if let e = error {
      result = Result.error(e)
      completion(result)
      return
    }
    if let d = data {
      result = Result.success(d)
      completion(result)
    }
  }
  task.resume()
}

loadNetworkData("") { (result) in
  switch result {
  case let .success(data):
    print("scrress: \(data)")
  case let .error(error):
    print("error: \(error)")
  }
}

定義了一個enum Result,里面有兩個case,分別表示網(wǎng)絡(luò)請求成功和失敗,成功則給一個Data,失敗則給一個Error
然后有一個加載網(wǎng)絡(luò)數(shù)組的方法func loadNetworkData,方法就是最普通的網(wǎng)絡(luò)請求,在請求回調(diào)里,判斷error是否有值,如果有值則創(chuàng)建一個error類型的Result,如果data有值,則創(chuàng)建一個success類型的Result。
在發(fā)起網(wǎng)絡(luò)請求的方法回調(diào)里,就可以通過模式匹配的方法判斷回調(diào)回來的Result到底是什么類型,然后通過值綁定的形式,取出successdata或者是error的值。
當(dāng)然這只是一個簡單的寫法,只是提供一點思路。具體怎么在項目里面用還是需要去實踐。


這就是個人總結(jié)的一些模式匹配的用法,都是自己在工作過程和學(xué)習(xí)過程中總結(jié)的一些經(jīng)驗,有自己的使用總結(jié),當(dāng)然也有在網(wǎng)上看一些博客的總結(jié),希望對能讀到這篇文章的同學(xué)有所幫助。
如果文章中有什么使用不當(dāng)和使用錯誤的,一定請指出來。謝謝!

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

  • Spring Cloud為開發(fā)人員提供了快速構(gòu)建分布式系統(tǒng)中一些常見模式的工具(例如配置管理,服務(wù)發(fā)現(xiàn),斷路器,智...
    卡卡羅2017閱讀 136,506評論 19 139
  • SwiftDay011.MySwiftimport UIKitprintln("Hello Swift!")var...
    smile麗語閱讀 4,078評論 0 6
  • title: "Swift 中枚舉高級用法及實踐"date: 2015-11-20tags: [APPVENTUR...
    guoshengboy閱讀 2,688評論 0 2
  • 我一直以為,無論想要搞定什么都首先要搞定自己,包括搞定自己的認知、搞定自己的情緒、搞定自己的言行。 今天的晨讀內(nèi)容...
    來鄭坤茹閱讀 313評論 0 1
  • 又想到了李茂對弦子的告白:“我的婚姻無關(guān)于物質(zhì),無關(guān)與所謂合適門當(dāng)戶對,年齡,無關(guān)于我是否應(yīng)該結(jié)婚,我的婚姻只與一...
    稚友丶閱讀 268評論 0 0

友情鏈接更多精彩內(nèi)容