zoukankan      html  css  js  c++  java
  • Swift 模式匹配

    前言

    • 在 Swift 中模式匹配是个重要的概念。

      • 最常用的模式匹配是 switch 语法。

      • 模式匹配非常灵活,在使用 switch 进行一轮模式匹配时,不需要所有的 case 都是同一种风格。

        let i = 5
        
        switch i {
        
            case 1:
                print("The box is 1")
        
            case 2:
                print("The box is 2")
        
            case 3...100:
                print("Number in the box is not less than 3 and not more than 100")
        
            default:
                print("Guess not to come out")
        }
        
    • 很明显 “匹配” 与 “相等” 是不同的,case 中的类型不同时,匹配的标准也不同。

      • case 中只有一个单独的值时,我们可以认为相等就是匹配。
      • case 中是一个范围时,匹配的标准也改变了,因为一个值不可能与一个范围相等,此时我们会认为匹配的标准是该对象存在于该范围中。
    • 实际上很多可以进行模式匹配的对象,本身并不能判等。当你需要对某个对象做模式匹配时,需要提前预设好能够与对象做匹配的数据类型,并且指定匹配的规则。为了实现上述过程需要重载操作符 ~=

      func ~=(lhs: Int, rhs: Int) -> Bool {
          ...    
      }
      
      func ~=(lhs: Range<Int>, rhs: Int) -> Bool {
          ...
      }
      
      • lhs 参数代表 case 可以接受的类型,rhs 代表发起模式匹配的对象。
      • Switch 在表层的模式匹配语法之下封装了这些 ~= 函数,当执行到某个类型的 case 值时会转换成执行对应类型的 ~= 函数,并返回值判断该次匹配知否成功。

    1、枚举的模式匹配

    • 在 Swift 中使用枚举的好处是,可以把一些服务器返回的基础类型的值封装成有意义的对象。

      enum TestLevel: Int {
      
          case low    = 0
          case middle
          case high
      }
      
      • 一个普通的枚举类型是不可比较的,有原始值的枚举类型可以做比较,比较时会根据原始值进行比较。
      • 使用上面的简写会分别获得 1 和 2 的原始值。
    • 既然已经决定把服务器返回的状态位封装成一个枚举,那么在数据结构中就不要保留它的原始值,否则你可能不得不写出一个不太好的模式匹配版本。

      let type = TestLevel(rawValue: 0) ?? .low
      
      switch type {
          
          case .high:
              print("Level is high")
          
          case .middle:
              print("Level is middle")
          
          case .low:
              print("Level is low")
      }
      
      • 对枚举做模式匹配时,如果 switchcase 的值覆盖了枚举类型的所有情况时,不需要 default

    2、元组的模式匹配

    • 相比于枚举,元组中包含多个数据元素,可以匹配整个元组,也可以匹配元组中的某个数据成员,这是因为 Swift 已经为元组重载了多个版本的 ~= 操作符。

      let tuple = (9527, "xiaoming")
          
      switch tuple {
              
          case (1111, "lilei"):                   // 匹配整个枚举
              print("Found lilei")
              
          case (2222, _):                         // 只匹配部分元素
              print("Num. is 2222")
              
          case (9527, let name):                  // 匹配部分成员,并取出没有参与匹配的元素
              print("9527 name is (name)")
              
          default:
              break
      }
      
    • 在 Swift 中元组是匿名的,不具备复用的特性,所以经常被当作临时变量保存数据。除了保存数据,元组还可以把一个复杂的 if else 结构封装成模式匹配的格式,使得代码逻辑更加清晰。

      enum Limit: String {
          case admin
          case guest
      }
      
      func login(limit: Limit, userName: String, password: String) {
          
          switch (limit, userName, password) {
              
              case (.admin, "xiaoming", "abc123"):
                  print("login success")
              
              case (.admin, _, _):
                  print("name or password error")
              
              case (.guest, _, _):
                  print("guest login")
          }
      }
      
      • 通过元组的封装,把一个逻辑结构的问题转换成了一个数据结构的问题,模式匹配只有一层,判断的顺序一目了然,并且每一个 case 都需要列出 limit, userName, password 三者的具体情况,不会因为 if else 层次的加深而造成逻辑的缺失。

    3、if 和 guard 中的模式匹配

    • 除了前面的 switch 语句,Swift 2.0 中为更多的基础语法引入了模式匹配,比如 ifguard 语句。

      if case 可以接受的类型 = 发起模式匹配的对象 {
      
          // 模式匹配成功时执行的代码段
      }
      
      guard case 可以接受的类型 = 发起模式匹配的对象 else {
      
          // 模式匹配不成功时执行的代码段
          return
      }
      
      // 模式匹配成功时执行的代码段
      
    • 在判断条件中对基本数据类型使用模式匹配

      let a = 3
      
      // 原来的写法
      
      if a >= 0 && a <= 5 {
          print("include")
      }
      
      // 模式匹配的 if case 写法
      
      if case 0...5 = a {
          print("include")
      }
      
      // 模式匹配的 guard case 写法
      
      guard case 0...5 = a else {
          return
      }
      
      print("include")
      
    • 也可以在判断条件中对元组使用模式匹配过滤无用信息,结构看起来与 switch 中的 case 的格式类似。

      enum Limit: String {
          case admin
          case guest
      }
      
      func login(loginTyuple: (limit: Limit, userName: String, password: String)) {
      
          // 先进性黑名单判断
          
          if case (.admin, "lilei", _) = loginTyuple {
              return
          }
          
          // 在进行正常操作
          
          switch loginTyuple {
      
              case (.admin, "xiaoming", "abc123"):
                  print("login success")
      
              case (.admin, _, _):
                  print("name or password error")
      
              case (.guest, _, _):
                  print("guest login")
          }
      }
      

    4、for 中的模式匹配

    • 在循环中引入模式匹配,则循环只会处理哪些匹配的对象。

      for case 可以接受的类型 in 发起模式匹配的对象集合 {
          
          // 模式匹配成功时执行的代码段
      }
      
    • 比如现在有三个用户,我们只对管理员权限的用户进行操作。

      enum Limit: String {
          case admin
          case guest
      }
          
      let loginArr = [
          (Limit.admin, "xiaoming", "abc123"),
          (Limit.admin, "lilei", "222"),
          (Limit.guest, "hanmeimei", "333")
      ]
      
      for case let (Limit.admin, name, _) in loginArr {
          print(name)
      }
      

    5、模式匹配中的 where 关键字

    • 我们可以把嵌套的逻辑结构封装成元组,但是并不是所有的逻辑结构都适合封装成元组,这时你可以保留原始的模式匹配格式,然后使用 where 关键字在其上增加逻辑判断。

    • where 语句直接附在 case 语句之后,用来为模式匹配增加匹配条件,where 的优势是保持了模式匹配格式的整齐度,where 可以用于所有的模式匹配中。

      let arr = [
          (name: "heianqishi", imdb: 9.0, year: 2008),
          (name: "xingjichuanyue", imdb: 8.7, year: 2014),
          (name: "jiyisuipian", imdb: 8.5, year: 2000)
      ]
      
      // 未使用模式匹配的写法
      
      for case let (_, imdb, _) in arr {
          if imdb > 8.5 {
              print(imdb)
          }
      }
      
      • 使用模式匹配的写法

        // 用在 for 语句中
        
        for case let (_, imdb, _) in arr where imdb > 8.5 {
            print(imdb)
        }
        
        // 用在 if 语句中
        
        // Swift 3.0 之前
        if case 2001...2010 = arr[0].year where arr[0].imdb > 8.5 {
            print("21th first high imdb film")
        }
        
        // Swift 3.0 及之后
        if case 2001...2010 = arr[0].year, arr[0].imdb > 8.5 {
            print("21th first high imdb film")
        }
        
        // 用在 switch 语句中
        
        switch arr[0] {
        
            case (_, let imdb, _) where imdb > 8.0:
                print("more than 8.0")
        
            case (_, let imdb, _) where imdb <= 8.0:
                print("less than 8.0")
        
            default:
                break
        }
        
  • 相关阅读:
    使用HSQLDB 客户端(jvm自带数据库使用技巧)
    PageHelper分页
    使用exe4j打包Java程序
    有图形界面的聊天程序
    同时收发消息
    服务端和客户端互聊
    使用Scanner
    收发字符串
    收发数字
    建立连接
  • 原文地址:https://www.cnblogs.com/QianChia/p/8669416.html
Copyright © 2011-2022 走看看