8. Swift 模式匹配.png

模式匹配中两个最关键的关键词:case 与 where,其中 where 语句可以用来设置约束条件、限制类型等。

在枚举中的应用

  1. 基本用法。原始值枚举成员的遍历
  1. enum Direction {
  2. case North
  3. case South
  4. case East
  5. case West
  6. }
  7. extension Direction: CustomStringConvertible {
  8. var description: String {
  9. switch self {
  10. case .North: return "↑"
  11. case .South: return "↓"
  12. case .East: return "→"
  13. case .West: return "←"
  14. }
  15. }
  16. }
  1. 1 由于一个 case 可以匹配多个条件,可以根据实际情况做出修改
  1. extension Direction: CustomStringConvertible {
  2. var description: String {
  3. switch self {
  4. case .North, .South:
  5. return "vertical"
  6. case .West, .East:
  7. return "horizontal"
  8. }
  9. }
  10. }
  1. 再进一步。关联值枚举对象的匹配,若匹配成功则关联参数值会绑定到新创建的常量或变量上
  1. enum Media {
  2. case Book(title: String, author: String, year: Int)
  3. case Movie(title: String, director: String, year: Int)
  4. case WebSite(url: URL, title: String)
  5. }
  6. extension Media {
  7. var mediaTitle: String {
  8. switch self {
  9. case .Book(title: let aTitle, author: _, year: _):
  10. return aTitle
  11. case .Movie(title: let aTitle, director: _, year: _):
  12. return aTitle
  13. case .WebSite(url: _, title: let aTitle):
  14. return aTitle
  15. }
  16. }
  17. }
  1. 1 以下两行代码等价
  1. case .Book(title: let aTitle, author: _, year: _):
  2. case let .Book(title: aTitle, author: _, year: _):
  1. 2 关联参数值匹配固定值
  1. extension Media {
  2. var isFromJulesVerne: Bool {
  3. switch self {
  4. case .Book(title: _, author: "Jules Verne", year: _): return true
  5. case .Movie(title: _, director: "Jules Verne", year: _): return true
  6. default: return false
  7. }
  8. }
  9. }
  1. extension Media {
  2. func checkAuthor(author: String) -> Bool {
  3. switch self {
  4. case .Book(title: _, author: author, year: _): return true
  5. case .Movie(title: _, director: author, year: _): return true
  6. default: return false
  7. }
  8. }
  9. }
  1. 使用元组包含所有的关联参数
  1. extension Media {
  2. var mediaTitle: String {
  3. switch self {
  4. case let .Book(tuple): return tuple.title
  5. case let .Movie(tuple): return tuple.title
  6. case let .WebSite(tuple): return tuple.title
  7. }
  8. }
  9. }
  1. 1 同样可以不用指定特定的元组来匹配任何关联值,所以下面四个表达式是等价的
  1. case let .WebSite(tuple):
  2. case let .WebSite:
  3. case let .WebSite(_):
  4. case let .WebSite(_, _):
  1. 添加 where 子句
  1. extension Media {
  2. var publishedAfter1930: Bool {
  3. switch self {
  4. case let .Book(_, _, year) where year > 1930: return true
  5. case let .Movie(_, _, year) where year > 1930: return true
  6. case .WebSite: return true
  7. default: return false
  8. }
  9. }
  10. }

在元组中的应用

在 Swift 当中,switch 并不像 ObjC 一样只能对整型或枚举进行匹配。可以使用 switch 对很多类型进行匹配,比如元组。这意味着我们只要将多个数据组合在一个元组中,就可以一次性匹配多个数据。此外要注意的是,switch 是按 case 模式被指定的顺序来判断求值的,并且它会在匹配到第一个满足的 case 后跳出。

  1. let point = CGPoint(x: 7, y: 0)
  2. switch (point.x, point.y) {
  3. case (0, 0): print("On the origin!")
  4. case (0, _): print("x=0: on Y-axis!")
  5. case (_, 0): print("y=0: on X-axis!")
  6. case (let x, let y) where x == y: print("On y=x")
  7. default: print("Quite a random point here.")
  8. }

在字符中的应用

  1. let letter: Character = "J"
  2. switch letter {
  3. case "A", "E", "I", "O", "U", "Y": print("Vowel")
  4. default: print("Consonant")
  5. }

在区间中的应用

  1. let count = 7
  2. switch count {
  3. case Int.min..<0: print("Negative count, really?")
  4. case 0: print("Nothing")
  5. case 1: print("One")
  6. case 2..<5: print("A few")
  7. case 5..<10: print("Some")
  8. default: print("Many")
  9. }
  1. func charType(car: Character) -> String {
  2. switch car {
  3. case "A", "E", "I", "O", "U", "Y", "a", "e", "i", "o", "u", "y":
  4. return "Vowel"
  5. case "A"..."Z", "a"..."z":
  6. return "Consonant"
  7. default:
  8. return "Other"
  9. }
  10. }

在类型判断中的应用

使用 as 和 is 匹配指定类型

  1. protocol Medium {
  2. var title: String { get }
  3. }
  4. struct Book: Medium {
  5. let title: String
  6. let author: String
  7. let year: Int
  8. }
  9. struct Movie: Medium {
  10. let title: String
  11. let director: String
  12. let year: Int
  13. }
  14. struct WebSite: Medium {
  15. let url: NSURL
  16. let title: String
  17. }
  1. let media: [Medium] = [
  2. Book(title: "20,000 leagues under the sea", author: "Jules Vernes", year: 1870),
  3. Movie(title: "20,000 leagues under the sea", director: "Richard Fleischer", year: 1955)
  4. ]
  5. for medium in media {
  6. print(medium.title)
  7. switch medium {
  8. case let b as Book:
  9. print("Book published in \(b.year)")
  10. case let m as Movie:
  11. print("Movie released in \(m.year)")
  12. case is WebSite: // equal to: let _ as WebSite
  13. print("A WebSite with no date")
  14. default:
  15. print("No year info for \(medium)")
  16. }
  17. }

在可选类型中的应用

  1. let anOptional: Int? = 2
  2. switch anOptional {
  3. case 0: print("Zero") // or `case 0?:`
  4. case 1: print("One") // or `case 1?:`
  5. case 2: print("Two") // or `case 2?:`
  6. case nil: print("None")
  7. default: print("Other")
  8. }

简洁高效的匹配模式 *

if case、guard case、for case [let …] [where …] 都是对 switch-case 的简化,与特定 case 匹配。

比如 if case let x = y { … } 严格等同于 switch y { case let x: … }:当你只想与一条 case 匹配时,这种更紧凑的语法尤其有用。有多个 case 时更适合使用 switch。

另外 if 或 guard 后面如果接 where 子句,需将 where 关键字替换成逗号。

  1. enum Media {
  2. case Book(title: String, author: String, year: Int)
  3. case Movie(title: String, director: String, year: Int)
  4. case WebSite(urlString: String)
  5. }
  6. let m = Media.Movie(title: "Captain America: Civil War", director: "Russo Brothers", year: 2016)
  7. if case let Media.Movie(title, _, _) = m {
  8. print("This is a movie named \(title)")
  9. }
  1. enum NetworkResponse {
  2. case Response(URLResponse, NSData)
  3. case Error(NSError)
  4. }
  5. func processRequestResponse(response: NetworkResponse) {
  6. guard case let .Response(urlResp, data) = response,
  7. let httpResp = urlResp as? HTTPURLResponse, 200..<300 ~= httpResp.statusCode else {
  8. print("Invalid response, can't process")
  9. return
  10. }
  11. print("Processing \(data.length) bytes…")
  12. }
  1. enum Media {
  2. case Book(title: String, author: String, year: Int)
  3. case Movie(title: String, director: String, year: Int)
  4. case WebSite(urlString: String)
  5. }
  6. let mediaList: [Media] = [
  7. .Book(title: "Harry Potter and the Philosopher's Stone", author: "J.K. Rowling", year: 1997),
  8. .Movie(title: "Harry Potter and the Philosopher's Stone", director: "Chris Columbus", year: 2001),
  9. .Book(title: "Harry Potter and the Chamber of Secrets", author: "J.K. Rowling", year: 1999),
  10. .Movie(title: "Harry Potter and the Chamber of Secrets", director: "Chris Columbus", year: 2002),
  11. ]
  12. print("Movies only:")
  13. for case let Media.Movie(title, _, year) in mediaList {
  14. print(" - \(title) (\(year))")
  15. }

在循环遍历中的应用 where

  1. let scores = [20, 8, 59, 60, 70, 80]
  2. scores.forEach {
  3. switch $0 {
  4. case let x where x >= 60:
  5. print("及格")
  6. default:
  7. print("不及格")
  8. }
  9. }
  1. for value in 0..<5 where value > 2 {
  2. print(value)
  3. }
  1. for (index, value) in (0..<5).enumerated() where index > 2 && value > 2 {
  2. print(index, value)
  3. }

在异常捕获中的应用 where

  1. enum ExceptionError: Error {
  2. case httpCode(Int)
  3. }
  4. func throwError() throws {
  5. throw ExceptionError.httpCode(500)
  6. }
  7. do {
  8. try throwError()
  9. } catch ExceptionError.httpCode(let httpCode) where httpCode >= 500{
  10. print("server error")
  11. } catch {
  12. print("other error")
  13. }

在扩展中的应用 where

  1. protocol SomeProtocol {}
  2. class ClassA: SomeProtocol {
  3. let param = 100
  4. }
  5. class ClassB {
  6. let param = 200
  7. }
  8. extension SomeProtocol where Self: ClassA {
  9. func showParam() {
  10. print("ClassA showParam: \(self.param)")
  11. }
  12. }
  13. extension SomeProtocol where Self: ClassB {
  14. func showParam() {
  15. print("ClassB showParam: \(self.param)")
  16. }
  17. }
  18. let a = ClassA()
  19. let b = ClassB()
  20. a.showParam()
  21. // b.showParam()

在泛型中的应用 where

  1. protocol SomeProtocol {}
  2. func test<T: SomeProtocol>(value: T) {}
  3. func test<T>(value: T) where T: SomeProtocol {}

在协议中的应用 where

  1. protocol Sequence {
  2. associatedtype Element where Self.Element == Self.Iterator.Element
  3. }

参考文章