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
相關文章
相關標籤/搜索