Swift5.0 - day9-字面量协议、模式匹配

一、字面量

  • 1.1、常见字面量的默认类型

    public typealias IntegerLiteralType = Int 
    public typealias FloatLiteralType = Double 
    public typealias BooleanLiteralType = Bool 
    public typealias StringLiteralType = String
    
    • 举例,下面代码中的10、false、"Jack" 就是字面量

      var age = 10
      var isRed = false
      var name = "Jack"
      
    • 可以通过typealias修改字面量的默认类型,建议不要修改

      typealias FloatLiteralType = Float 
      typealias IntegerLiteralType = UInt8 
      var age = 10 // UInt8
      var height = 1.68 // Float
      
    • Swift自带的绝大部分类型,都支持直接通过字面量进行初始化,如:Bool、Int、Float、Double、String、Array、Dictionary、Set、Optional

  • 1.2、字面量协议, Swift自带类型之所以能够通过字面量初始化,是因为它们遵守了对应的协议

    Bool : ExpressibleByBooleanLiteral
    Int : ExpressibleByIntegerLiteral
    Float、Double : ExpressibleByIntegerLiteral、ExpressibleByFloatLiteral 
    Dictionary : ExpressibleByDictionaryLiteral
    String : ExpressibleByStringLiteral
    Array、Set : ExpressibleByArrayLiteral 
    Optional : ExpressibleByNilLiteral
    

    左边类型,右边协议

    var b: Bool = false // ExpressibleByBooleanLiteral
    var i: Int = 10 // ExpressibleByIntegerLiteral
    var f0: Float = 10 // ExpressibleByIntegerLiteral
    var f1: Float = 10.0 // ExpressibleByFloatLiteral
    var d0: Double = 10 // ExpressibleByIntegerLiteral
    var d1: Double = 10.0 // ExpressibleByFloatLiteral
    var s: String = "jack" // ExpressibleByStringLiteral
    var arr: Array = [1, 2, 3] // ExpressibleByArrayLiteral
    var set: Set = [1, 2, 3] // ExpressibleByArrayLiteral
    var dict: Dictionary = ["jack" : 60] // ExpressibleByDictionaryLiteral 
    var o: Optional<Int> = nil // ExpressibleByNilLiteral
    
  • 1.3、字面量协议应用一

    extension Int : ExpressibleByBooleanLiteral {
         public init(booleanLiteral value: Bool) { self = value ? 1 : 0 }
    }
    var num: Int = true
    print(num) // 1
    
    class Student : ExpressibleByIntegerLiteral, ExpressibleByFloatLiteral, ExpressibleByStringLiteral, CustomStringConvertible {
    
         var name: String = ""
         var score: Double = 0
         required init(floatLiteral value: Double) { self.score = value }
         required init(integerLiteral value: Int) { self.score = Double(value) }
    
         required init(stringLiteral value: String) { 
              self.name = value
         }
         required init(unicodeScalarLiteral value: String) { 
              self.name = value 
         }
         required init(extendedGraphemeClusterLiteral value: String) { 
              self.name = value 
         } 
         var description: String { "name=\(name),score=\(score)" }
    }
    
    var stu: Student = 90
    print(stu) // name=,score=90.0 stu = 98.5
    stu = 98.5
    print(stu) // name=,score=98.5 stu = "Jack"
    stu = "Jack"
    print(stu) // name=Jack,score=0.0
    
  • 1.4、字面量协议应用二

    struct Point {
        var x = 0.0, y = 0.0
    }
    
    extension Point : ExpressibleByArrayLiteral, ExpressibleByDictionaryLiteral {
        init(arrayLiteral elements: Double...) {
           guard elements.count > 0 else { return }
           self.x = elements[0]
           guard elements.count > 1 else { return }
           self.y = elements[1]
        }
        init(dictionaryLiteral elements: (String, Double)...) {
             for (k, v) in elements {
                 if k == "x" { 
                     self.x = v 
                 } else if k == "y" { 
                     self.y = v
                }
             }
         }
    }
    
    var p: Point = [10.5, 20.5]
    print(p) // Point(x: 10.5, y: 20.5)
    p = ["x" : 11, "y" : 22]
    print(p) // Point(x: 11.0, y: 22.0)
    

二、模式匹配

  • 2.1、什么是模式?

    • 模式是用于匹配的规则,比如 switchcase、捕捉错误的catch、if\guard\while\for语句的条件等
    • Swift中的模式有
      • 通配符模式(Wildcard Pattern)
      • 标识符模式(Identifier Pattern)
      • 值绑定模式(Value-Binding Pattern)
      • 元组模式(Tuple Pattern)
      • 枚举Case模式(Enumeration Case Pattern)
      • 可选模式(Optional Pattern)
      • 类型转换模式(Type-Casting Pattern)
      • 表达式模式(Expression Pattern)
  • 2.2、通配符模式(Wildcard Pattern)
    _ 匹配任何值
    _? 匹配非nil值

    enum Life {
        case human(name: String, age: Int?)
        case animal(name: String, age: Int?)
    }
    
    func check(_ life: Life) {
        switch life {
        case .human(let name, _):
            print("human", name)
        case .animal(let name, _?): 
            print("animal", name)
       default:
            print("other")
       }
    }
    
    check(.human(name: "Rose", age: 20))// human Rose
    check(.human(name: "Jack", age: nil)) // human Jack
    check(.animal(name: "Dog", age: 5)) // animal Dog
    check(.animal(name: "Cat", age: nil)) // other
    

    提示:平时遇到的 值?一般都表示非nil

    var num: Int? = 10
    switch num {
    case let v?:
          print(v)
    case nil:
          print("nil")
    }
    
  • 2.3、标识符模式(Identifier Pattern)
    给对一个的常量、变量名赋值

    var age = 10
    let name = "Jack"
    
  • 2.4、值绑定模式(Value-Binding Pattern)

    let point = (3, 2)
    switch point {
    case let (x, y):
        print("The point is at (\(x), \(y)).")
    }
    
  • 2.5、元组模式(Tuple Pattern)

    let points = [(0, 0), (1, 0), (2, 0)]
    for (x, _) in points {
        print(x)
    }
    
    let name: String? = "jack"
    let age = 18
    let info: Any = [1, 2]
    switch (name, age, info) {
    case (_?, _ , _ as String):
        print("case")
    default:
        print("default")
    }
    
    // default
    
    var scores = ["joan" : 8,"jack" : 98, "rose" : 100, "kate" : 86]
    for (name, score) in scores {
        print(name, score)
    }
    
  • 2.6、枚举Case模式(Enumeration Case Pattern)
    if case 语句等价于只有 1caseswitch 语句

    let age = 2
    // 原来的写法
    if age >= 0 && age <= 9 {
         print("[0, 9]")
    }
    // 枚举Case模式
    if case 0...9 = age {
         print("[0, 9]")
    }
    
    func test() {
         guard case 0...9 = age else {
             print("不在此范围")
             return
         }
         print("[0, 9]")
    }
    
    test()
    
    switch age {
    case 0...9: print("[0, 9]")
    default: break
    }
    
    let ages: [Int?] = [2, 3, nil, 5]
    for case nil in ages {
       print("有nil值")
       break
    }
    
    let points = [(1, 0), (2, 1), (3, 0)]
    for case let (x, 0) in points {
        print(x)
    } // 1 3
    
  • 2.7、可选模式(Optional Pattern)

    • 不为 nil

      let age: Int? = 42
      if case .some(let x) = age { print(x) }
      if case let x? = age { print(x) }
      
    • 匹配 case let age? in ages 中的 age? 不为 nil

      let ages: [Int?] = [nil, 2, 3, nil, 5]
      for case let age? in ages {
           // 2 3 5
           print(age)
      }
      

      提示:上面的代码等价于下面的

      let ages1: [Int?] = [nil, 2, 3, nil, 5]
      for item in ages1 {
           if let age = item {
                print(age)
           }
      }
      
    • 多个case匹配

      func check(_ num: Int?) {
           switch num {
           case 2?: print("2")
           case 4?: print("4")
           case 6?: print("6")
           case _?: print("other")
           case _: print("nil")
           }
      }
      
      check(4) // 4
      check(8) // other
      check(nil) // nil
      

      提示:2? 代表值不为nil,值为 2;_?代表值不为nil,什么值都可以;_ 代表任何值都可以

    • 下面两段代码等效

      var age: Int? = 10
      
      switch age {
      case let x?:
          print(x)
      case nil:
          print("nil")
      }
      
      switch age {
      case .some(let x):
          print(x)
      case .none:
          print("nil")
      }
      

      枚举就只有:nonesome

  • 2.8、类型转换模式(Type-Casting Pattern)

    let num: Any = 6
    switch num {
    case is Int:
       // 编译器依然认为num是Any类型
       print("is Int", num)
       // case let n as Int:
       // print("as Int", n + 1)
    default:
       break
    }
    

    提示:case is Int 是在匹配 num 是不是 Int 类型

    • case let n as Int:在匹配的时候 num 还是Any 类型,但是 n 是 Int 类型
    class Animal { func eat() { print(type(of: self), "eat") } }
    class Dog : Animal { func run() { print(type(of: self), "run") } }
    class Cat : Animal { func jump() { print(type(of: self), "jump") } }
    
    func check(_ animal: Animal) {
       switch animal {
       case let dog as Dog:
           dog.eat()
           dog.run()
       case is Cat:
           animal.eat()
       default:
           break
       }
    }
    // Dog eat
    // Dog run
    check(Dog())
    // Cat eat
    check(Cat())
    

    重点指出的是:check(Cat()) 在执行的时候,case is Cat:里面调用的是 animal.eat(),打印的是 Cat eat,那是因为 type(of: self) 可以识别是谁在真的调用,在case is Cat:里面是只能调用 Animal类里面的方法的

  • 2.9、表达式模式(Expression Pattern)

    • 表达式模式用在 case 中

      let point = (1, 2)
      switch point {
      case (0, 0):
         print("(0, 0) is at the origin.")
      case (-2...2, -2...2):
         print("(\(point.0), \(point.1)) is near the origin.")
      default:
         // (1, 2) is near the origin.
         print("The point is at (\(point.0), \(point.1)).") } 
      
    • 自定义表达式模式 1 :可以通过重载运算符,自定义表达式模式的匹配规则,比较复杂的switch 是调用 ~=运算符

      struct Student {
          var score = 0, name = ""
          static func ~= (pattern: Int, value: Student) -> Bool {
              value.score >= pattern
          }
          static func ~= (pattern: ClosedRange<Int>, value: Student) -> Bool { 
              pattern.contains(value.score)
          }
          static func ~= (pattern: Range<Int>, value: Student) -> Bool {  
              pattern.contains(value.score)
          }
      }
      
      var stu = Student(score: 75, name: "Jack")
      switch stu {
      case 100: print(">= 100")
      case 90: print(">= 90")
      case 80..<90: print("[80, 90)")
      case 60...79: print("[60, 79]")  // [60, 79]
      case 0: print(">= 0")
      default: break
      }
      

      在重载 ~= 运算符的时候,有些格式是固定的,如:static func ~= (pattern: Int, value: Student) -> Bool:pattern 是case后面放的东西,value 是 switch 里面放的东西,返回值 Bool 是固定的

      if case 60 = stu {
          print(">= 60") // >= 60
      }
      
      var info = (Student(score: 70, name: "Jack"), "及格")
      switch info {
      case let (60, text):
          print(text)  // 及格
      default: break
      }
      
    • 自定义表达式模式 2 :匹配字符串 前缀后缀 的模式

      extension String {
          static func ~= (pattern: (String) -> Bool, value: String) -> Bool {
               pattern(value)
          }
      }
      
      func hasPrefix(_ prefix: String) -> ((String) -> Bool) { { $0.hasPrefix(prefix) } }
      func hasSuffix(_ suffix: String) -> ((String) -> Bool) { { $0.hasSuffix(suffix) } }
      
      var str = "jack"
      switch str {
      case hasPrefix("j"), hasSuffix("k"):
         print("以j开头,以k结尾")
      default: break
      }
      

      提示:hasPrefix 后面的 { { $0.hasPrefix(prefix) } } 是简写,全部代码如下,同样

      func hasPrefix(_ prefix: String) -> ((String) -> Bool) { 
             return { 
                     (str: String) -> Bool in
                     str.hasPrefix(prefix) 
             } 
      }
      
    • 自定义表达式模式 3 :奇数和偶数的判断

      func isEven(_ i: Int) -> Bool { i % 2 == 0 }
      func isOdd(_ i: Int) -> Bool { i % 2 != 0 }
      extension Int {
          static func ~= (pattern: (Int) -> Bool, value: Int) -> Bool {
              pattern(value)
          }
      }
      var age = 9
      switch age {
      case isEven:
         print("偶数")
      case isOdd:
         print("奇数")
      default:
         print("其他")
      }
      
    • 自定义表达式模式 4 :自定义符号,下面是前置运算符

      prefix operator ~>
      prefix operator ~>=
      prefix operator ~<
      prefix operator ~<=
      
      prefix func ~> (_ i: Int) -> ((Int) -> Bool) { { $0 > i } }
      prefix func ~>= (_ i: Int) -> ((Int) -> Bool) { { $0 >= i } }
      prefix func ~< (_ i: Int) -> ((Int) -> Bool) { { $0 < i } }
      prefix func ~<= (_ i: Int) -> ((Int) -> Bool) { { $0 <= i } }
      
      extension Int {
          static func ~= (pattern: (Int) -> Bool, value: Int) -> Bool {
             pattern(value)
          }
      }
      
      var age = 9
      switch age {
      case ~>=0:
          print("1")
      case ~>10:
          print("2")
      default: break
      } 
      
  • 2.10、可以使用 where 为模式匹配增加匹配条件

    • (1)、 switch 里面使用

      var data = (10, "Jack")
      switch data {
      case let (age, _) where age > 10:
          print(data.1, "age>10")
      case let (age, _) where age > 0:
          print(data.1, "age>0")
      default: break
      }
      
    • (2)、 for in 里面使用

      var ages = [10, 20, 44, 23, 55]
      for age in ages where age > 30 {
          print(age) // 44 55
      } 
      
    • (3)、 协议中关联类型里面使用

      protocol Stackable { associatedtype Element }
      protocol Container {
          associatedtype Stack : Stackable where Stack.Element : Equatable
      }
      
    • (4)、 函数里面使用

      func equal<S1: Stackable, S2: Stackable>(_ s1: S1, _ s2: S2) -> Bool where S1.Element == S2.Element, S1.Element : Hashable {
         return false
      }
      
    • (5)、 扩展里面使用

       extension Container where Self.Stack.Element : Hashable { }
      
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 206,013评论 6 481
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 88,205评论 2 382
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 152,370评论 0 342
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 55,168评论 1 278
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 64,153评论 5 371
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,954评论 1 283
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,271评论 3 399
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,916评论 0 259
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 43,382评论 1 300
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,877评论 2 323
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,989评论 1 333
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,624评论 4 322
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,209评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,199评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,418评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,401评论 2 352
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,700评论 2 345

推荐阅读更多精彩内容