Swift 柯里化

前言

  • 因为柯里化在业务层的应用较少,因此从 Swift 3.0 开始移除了柯里化的用法,可是 Swift 的不少底层特性是使用柯里化来表达的。

一、柯里化

1.1 柯里化简介

  • 柯里化(Currying),又称部分求值(Partial Evaluation),是一种函数式编程思想,就是把接受多个参数的函数转换成接收一个单一参数(最初函数的第一个参数)的函数,而且返回一个接受余下参数的新函数技术。html

  • uncurried: 普通函数编程

    // 接收多个参数的函数(与类相关的函数,统称为方法,可是这里就直接说函数了,方便理解)
    func add(a: Int, b: Int, c: Int) -> Int {
    
        print("\(a) + \(b) + \(c)")
        return a + b + c
    }
  • curried: 柯里化函数swift

    // 柯里化函数,Swift 3.0 以前支持这样的语法,能够直接写
    func addCur(a: Int)(b: Int)(c: Int) -> Int {
    
        println("\(a) + \(b) + \(c)")
        return a + b + c
    }

1.2 如何定义柯里化函数

  • 定义柯里化函数闭包

    func function name (parameters)(parameters) -> return type {
    
        statements
    }

1.3 柯里化函数实现原理

  • uncurried: 普通函数并发

    class Currying {
    
        // 接收多个参数的函数
        func add(a: Int, b: Int, c: Int) -> Int {
    
            print("\(a) + \(b) + \(c)")
            return a + b + c
        }
    }
  • 系统自带的柯里化函数函数式编程

    class Currying {
    
        func addCur(a: Int)(b: Int)(c: Int) -> Int {
    
            print("\(a) + \(b) + \(c)")
            return a + b + c
        }
    }
  • 手动实现柯里化函数函数

    • 把上面的函数转换为柯里化函数,首先转成接收第一个参数 a,而且返回接收余下第一个参数 b 的新函数(采用闭包).单元测试

    • 这里为何能使用参数 a、b、c ?测试

      • 利用闭包的值捕获特性,即便这些值做用域不在了,也能够捕获到他们的值。
      • 闭包会自动判断捕获的值是值拷贝仍是值引用,若是修改了,就是值引用,不然值拷贝。
      • 注意只有在闭包中才能够,a、b、c 都在闭包中。
      class Currying {
      
          // (a: Int)                    : 参数
          // (b: Int) -> (c: Int) -> Int : 函数返回值(一个接收参数 b 的函数,而且这个函数又返回一个接收参数 c,
          //                               返回值为 Int 类型的函数)
      
          // 定义一个接收参数 a,而且返回一个接收参数 b 的函数,而且这个函数又返回一个接收参数 c,返回值为 Int 类型的函数。
          func add(a: Int) -> (b: Int) -> (c: Int) -> Int {
      
              // 返回一个接收参数 b 的函数,而且这个函数又返回一个接收参数 c,返回值为 Int 类型的函数
              return { (b:Int) -> (c: Int) -> Int in
      
                  // 返回一个接收余下第一个参数 c,而且返回结果为 Int 类型的函数
                  return { (c: Int) -> Int in
      
                      return a + b + c;
                  }
              }
          }
      }

1.4 如何调用柯里化函数

  • 建立柯里化类的实例lua

    var curryInstance = Currying()
  • 手动实现的柯里化函数调用

    var result: Int = curryInstance.add(a: 10)(b: 20)(c: 30)
    • 可能不少人都是第一次看这样的调用,感受有点难以想象。
    • 让咱们回顾下 OC 建立对象 [[Person alloc] init],这种写法应该都见过吧,就是一下发送了两个消息,alloc 返回一个实例,再用实例调用 init 初始化,上面也是同样,一下调用多个函数,每次调用都会返回一个函数,而后再次调用这个返回的函数。
  • 手动实现的柯里化函数拆解调用

    • curryInstance.add(a: 10) 调用一个接收参数 a,而且返回一个接收参数 b 的函数,而且这个函数又返回一个接收参数 c,返回值为 Int 类型的函数。

      // functionB: 一个接收参数 b 的函数,而且这个函数又返回一个接收参数 c,返回值为 Int 类型的函数
      let functionB = curryInstance.add(a: 10)
    • functionB(b: 20) 调用一个接收参数 b 的函数,而且这个函数又返回一个接收参数 c,返回值为 Int 类型的函数。

      // functionC: 一个接收参数 c,返回值为 Int 类型的函数
      let functionC = functionB(b: 20)
    • functionC(c: 30) 调用一个接收参数 c,返回值为 Int 类型的函数。

      // result: 函数的返回值
      var result: Int = functionC(c: 30);
  • 系统的柯里化函数调用

    var result: Int = curryInstance.addCur(a: 10)(b: 20)(c: 30)
  • 系统的柯里化函数拆解调用

    • curryInstance.addCur(a: 10) 调用一个接收参数 a,而且返回一个接收参数 b 的函数,而且这个函数又返回一个接收参数 c,返回值为 Int 类型的函数。

      // Swift是强类型语言,这里没有报错,说明调用系统柯里化函数返回的类型和手动的 functionB 类型一致
      // functionB: 一个接收参数 b 的函数,而且这个函数又返回一个接收参数 c,返回值为 Int 类型的函数
      functionB = curryInstance.addCur(a: 10)
    • functionB(b: 20) 调用一个接收参数 b 的函数,而且这个函数又返回一个接收参数 c,返回值为 Int 类型的函数。

      // functionC: 一个接收参数c,返回值为Int类型的函数
      functionC = functionB(b: 20)
    • functionC(c: 30) 调用一个接收参数 c,返回值为 Int 类型的函数。

      // result: 函数的返回值
      result = functionC(c: 30)
      
      // 打印 60,60,60 说明手动实现的柯里化函数,和系统的同样。
      print("\(r), \(res), \(result)")

1.5 柯里化函数使用注意

  • 必须按照参数的定义顺序来调用柯里化函数,不然就会报错。

  • 柯里化函数的函数体只会执行一次,只会在调用完最后一个参数的时候执行柯里化函数体。

    • 如下调用 functionC(c: 30) 才会执行函数体,这个能够本身断点调试。

      // curried: 柯里化函数
      func addCur(a: Int)(b: Int)(c: Int) -> Int {
      
          println("\(a) + \(b) + \(c)")
          return a + b + c
      }
      
      // 建立柯里化类的实例
      var curryInstance = Currying()
      
      // 不会执行柯里化函数体
      functionB = curryInstance.addCur(a: 10)
      
      // 不会执行柯里化函数体
      functionC = functionB(b: 20)
      
      // 执行柯里化函数体
      result = functionC(c: 30)

1.6 柯里化函数的好处

  • 这里就须要了解函数式编程思想了,柯里化函数就是运用了函数式编程思想,推荐看这篇文章函数式编程初探

  • 特色

    • 1)只用 “表达式” (表达式: 单纯的运算过程,老是有返回值),不用 “语句” (语句: 执行某种操做,没有返回值)。
    • 2)不修改值,只返回新值。
  • 好处

    • 1)代码简洁。
    • 2)提升代码复用性。
    • 3)代码管理方便,相互之间不依赖,每一个函数都是一个独立的模块,很容易进行单元测试。
    • 4)易于 “并发编程”,由于不修改变量的值,都是返回新值。

1.7 柯里化函数的实用性

  • 实用性一:复用性

    • 需求 1:地图类产品,不少界面都有相同的功能模块,好比搜索框。

      • 咱们能够利用柯里化函数,来组装界面,把界面分红一个个小模块,这样其余界面有相同的模块,直接运用模块代码,去从新组装下就行了。
  • 实用性二:延迟性

    • 柯里化函数代码须要前面的方法调用完成以后,才会来到柯里化函数代码中。

    • 需求 2:阅读类产品,一个界面的显示,依赖于数据,须要加载完数据以后,才能判断界面显示。

      • 这时候也能够利用柯里化函数,来组装界面,把各个模块加载数据的方法抽出来,等所有加载完成,再去执行柯里化函数,柯里化函数主要实现界面的组装。
  • 举例说明

    // 组合接口
    // 为何要定义接口,为了程序的扩展性,之后只须要在接口中添加对应的组合方法就行了。
    protocol CombineUI {
    
        func combine(top: () -> ())(bottom: () -> ())()
    }
    
    // 定义一个界面类,遵照组合接口
    class UI: CombineUI {
    
        func combine(top: () -> ())(bottom: () -> ())() {
    
            // 搭建顶部
            top()
    
            // 搭建底部
            bottom()
        }
    }

二、Swift 中实例方法的柯里化

  • Swift 中实例方法就是一个柯里化函数。

2.1 Swift 中实例方法的柯里化调用

  • Swift 中实例方法的柯里化调用

    • 示例结构体

      struct Example {
      
          var internalStr = ""
      
          func combine(externalStr: String) {
              print(internalStr + " " + externalStr)
          }
      }
    • 调用实例方法的经常使用格式

      let example = Example(internalStr: "hello")
      
      example.combine(externalStr: "word")            // hello word
    • 调用实例方法的柯里化格式

      let example = Example(internalStr: "hello")
      
      Example.combine(example)(externalStr: "word")   // hello word
相关文章
相关标签/搜索