Swift 柯里化

前言

  • 由于柯里化在业务层的应用较少,所以从 Swift 3.0 开始移除了柯里化的用法,但是 Swift 的很多底层特性是使用柯里化来表达的。

1、柯里化

1.1 柯里化简介

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

  • uncurried: 普通函数

    1
    2
    3
    4
    5
    6
    // 接收多个参数的函数(与类相关的函数,统称为方法,但是这里就直接说函数了,方便理解)
    func add(a: Int, b: Int, c: Int) -> Int {

    print("\(a) + \(b) + \(c)")
    return a + b + c
    }
  • curried: 柯里化函数

    1
    2
    3
    4
    5
    6
    // 柯里化函数,Swift 3.0 之前支持这样的语法,可以直接写
    func addCur(a: Int)(b: Int)(c: Int) -> Int {

    println("\(a) + \(b) + \(c)")
    return a + b + c
    }

1.2 如何定义柯里化函数

  • 定义柯里化函数

    1
    2
    3
    4
    func function name (parameters)(parameters) -> return type {

    statements
    }

1.3 柯里化函数实现原理

  • uncurried: 普通函数

    1
    2
    3
    4
    5
    6
    7
    8
    9
    class Currying {

    // 接收多个参数的函数
    func add(a: Int, b: Int, c: Int) -> Int {

    print("\(a) + \(b) + \(c)")
    return a + b + c
    }
    }
  • 系统自带的柯里化函数

    1
    2
    3
    4
    5
    6
    7
    8
    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 都在闭包中。

        1
        2
        3
        4
        5
        6
        7
        8
        9
        10
        11
        12
        13
        14
        15
        16
        17
        18
        19
        20
        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 如何调用柯里化函数

  • 创建柯里化类的实例

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

    1
    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 类型的函数。

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

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

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

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

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

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

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

      1
      2
      3
      4
      5
      // result: 函数的返回值
      result = functionC(c: 30)

      // 打印 60,60,60 说明手动实现的柯里化函数,和系统的一样。
      print("\(r), \(res), \(result)")

1.5 柯里化函数使用注意

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

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

    • 以下调用 functionC(c: 30) 才会执行函数体,这个可以自己断点调试。

      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15
      16
      17
      18
      // 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:阅读类产品,一个界面的显示,依赖于数据,需要加载完数据之后,才能判断界面显示。

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

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    // 组合接口
    // 为什么要定义接口,为了程序的扩展性,以后只需要在接口中添加对应的组合方法就好了。
    protocol CombineUI {

    func combine(top: () -> ())(bottom: () -> ())()
    }

    // 定义一个界面类,遵守组合接口
    class UI: CombineUI {

    func combine(top: () -> ())(bottom: () -> ())() {

    // 搭建顶部
    top()

    // 搭建底部
    bottom()
    }
    }

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

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

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

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

    • 示例结构体

      1
      2
      3
      4
      5
      6
      7
      8
      struct Example {

      var internalStr = ""

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

      1
      2
      3
      let example = Example(internalStr: "hello")

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

      1
      2
      3
      let example = Example(internalStr: "hello")

      Example.combine(example)(externalStr: "word") // hello word
文章目录
  1. 1. 前言
  2. 2. 1、柯里化
    1. 2.1. 1.1 柯里化简介
    2. 2.2. 1.2 如何定义柯里化函数
    3. 2.3. 1.3 柯里化函数实现原理
    4. 2.4. 1.4 如何调用柯里化函数
    5. 2.5. 1.5 柯里化函数使用注意
    6. 2.6. 1.6 柯里化函数的好处
    7. 2.7. 1.7 柯里化函数的实用性
  3. 3. 2、Swift 中实例方法的柯里化
    1. 3.1. 2.1 Swift 中实例方法的柯里化调用
隐藏目录