標籤:
前言
此次文章,講述的是Swift的一個新特性(柯裡化函數),可能很多iOS開發人員是第一次聽這個詞彙,包括我自己也是,自己也用了幾天時間才總結出來,希望能協助到各位咯,個人感覺偏向有開發經驗的碼友,如果零基礎的看懂,希望能給個贊,??!
如果喜歡我的文章,可以關注我,隨著後續不斷學習Swift中,陸續還會有更新ing....
什麼是柯裡化函數?
柯裡化(Currying),又稱部分求值(Partial Evaluation),是一種函數式編程思想,就是把接受多個參數的函數轉換成接收一個單一參數(最初函數的第一個參數)的函數,並且返回一個接受餘下參數的新函數技術。
class Currying{ // uncurried:普通函數 // 接收多個參數的函數(與類相關的函數,統稱為方法,但是這裡就直接說函數了,方便理解) func add(a: Int, b: Int, c: Int) -> Int{ println("\(a) + \(b) + \(c)") return a + b + c } // curried:柯裡化函數 // 柯裡化函數,Swift中已經支援這樣的文法了,可以直接寫 func addCur(a: Int)(b: Int)(c: Int) -> Int{ println("\(a) + \(b) + \(c)") return a + b + c }}
如何定義柯裡化函數?
定義柯裡化函數:
柯裡化函數實現原理
class Currying{ /*** uncurried:普通函數 ***/ // 接收多個參數的函數 func add(a: Int, b: Int, c: Int) -> Int{ println("\(a) + \(b) + \(c)") return a + b + c } /*** 手動實現柯裡化函數 ***/ // 把上面的函數轉換為柯裡化函數,首先轉成接收第一個參數a,並且返回接收餘下第一個參數b的新函數(採用閉包) // 為了讓大家都能看懂,我幫你們拆解來看下 // (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; 註解: 這裡為什麼能使用參數a,b,c? 利用閉包的值捕獲特性,即使這些值範圍不在了,也可以捕獲到他們的值。 閉包會自動判斷捕獲的值是值拷貝還是值引用,如果修改了,就是值引用,否則值拷貝。 注意只有在閉包中才可以,a,b,c都在閉包中。 } } } /*** curried: 系統內建的柯裡化函數 ***/ func addCur(a: Int)(b: Int)(c: Int) -> Int{ println("\(a) + \(b) + \(c)") return a + b + c }}
如何調用柯裡化函數
由於定義的是一個執行個體方法,因此調用需要依賴對象.
調用柯裡化函數:
// 建立柯裡化類的執行個體var curryInstance = Currying()/*** 調用手動實現的柯裡化函數 **/var r: Int = curryInstance.add(10)(b: 20)(c: 30)// 可能很多人都是第一次看這樣的調用,感覺有點不可思議。// 讓我們回顧下OC建立對象 [[Person alloc] init],這種寫法應該都見過吧,就是一下發送了兩個訊息,alloc返回一個執行個體,再用執行個體調用init初始化,上面也是一樣,一下調用多個函數,每次調用都會返回一個函數,然後再次調用這個返回的函數。 /***** 柯裡化函數分解調用 *****/// 讓我來幫你們拆解下,更容易看懂// curryInstance.add(10): 調用一個接收參數a,並且返回一個接收參數b的函數,並且這個函數又返回一個接收參數c,傳回值為Int類型的函數// functionB: 一個接收參數b的函數,並且這個函數又返回一個接收參數c,傳回值為Int類型的函數let functionB = curryInstance.add(10) // functionB(b: 20):調用一個接收參數b的函數,並且這個函數又返回一個接收參數c,傳回值為Int類型的函數// functionC: 一個接收參數c,傳回值為Int類型的函數let functionC = functionB(b: 20) // functionC(c: 30): 調用一個接收參數c,傳回值為Int類型的函數// result: 函數的傳回值var res: Int = functionC(c: 30); // 這裡會有疑問?,為什麼不是調用curryInstance.add(a: 10),而是curryInstance.add(10),functionB(b: 20),functionC(c: 30),怎麼就有b,c,這是因為func add(a: Int) -> (b:Int) -> (c: Int) -> Int這個方法中a是第一個參數,預設是沒有外部參數名,只有餘下的參數才有外部參數名,b,c都屬於餘下的參數。/***** 系統的柯裡化函數調用 *****/var result: Int = curryInstance.addCur(10)(b: 20)(c: 30)/***** 系統的柯裡化函數拆解調用 *****/// 注意:Swift是強型別語言,這裡沒有報錯,說明調用系統柯裡化函數返回的類型和手動的functionB類型一致 // curryInstance.addCur(10) : 調用一個接收參數a,並且返回一個接收參數b的函數,並且這個函數又返回一個接收參數c,傳回值為Int類型的函數// functionB: 一個接收參數b的函數,並且這個函數又返回一個接收參數c,傳回值為Int類型的函數functionB = curryInstance.addCur(10) // functionC: 一個接收參數c,傳回值為Int類型的函數functionC = functionB(b: 20) // result: 函數的傳回值res = functionC(c: 30) // 列印 60,60,60說明手動實現的柯裡化函數,和系統的一樣。println("\(r),\(res),\(result)")
柯裡化函數使用注意
1、必須按照參數的定義順序來調用柯裡化函數,否則就會報錯。
var result: Int = curryInstance.addCur(10)(b: 20)(c: 30)
2、柯裡化函數的函數體只會執行一次,只會在調用完最後一個參數的時候執行柯裡化函數體。以下調用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(10)// 不會執行柯裡化函數體functionC = functionB(b: 20)// 執行柯裡化函數體res = functionC(c: 30)
Swift中執行個體方法就是一個柯裡化函數
如何擷取執行個體方法?可以直接通過類擷取執行個體方法.
注意:方法是什麼類型,就返回什麼類型的函數,不過需要傳入一個參數(類執行個體)才能擷取到,如果方法中有外部參數名,外部參數名也屬於類型的一部分
使用類擷取執行個體方法:
Swift中執行個體方法的另一種調用方式(柯裡化調用)
// 建立柯裡化類的執行個體var curryInstance = Currying() // 調用function方法Currying.function(curryInstance)() // 拆解調用function方法// 1.擷取function方法let function = Currying.function(curryInstance)// 2.調用function方法function()
註解: 步驟都是一樣,首先擷取執行個體方法,在調用執行個體方法,執行個體方法怎麼調用,就不需要在教了。
柯裡化函數有什麼好處?為什麼要使用它?
這裡就需要瞭解函數式編程思想了,推薦看這篇文章函數式編程初探
特點:
1.只用“運算式”(運算式:單純的運算過程,總是有傳回值),不用“語句”(語句:執行某種操作,沒有傳回值)。2.不修改值,只返回新值。
好處:
1.代碼簡潔
2.提高代碼複用性
3.代碼管理方便,相互之間不依賴,每個函數都是一個獨立的模組,很容易進行單元測試。
4.易於“並發編程”,因為不修改變數的值,都是返回新值。
柯裡化函數就是運用了函數式編程思想,因此它也有以上的好處。
在iOS開發中如何運用柯裡化函數(實用性)
實用性一:複用性
需求1:地圖類產品,很多介面都有相同的功能模組,比如搜尋方塊。
我們可以利用柯裡化函數,來組裝介面,把介面分成一個個小模組,這樣其他介面有相同的模組,直接運用模組代碼,去重新組裝下就好了。
實用性二:延遲性,柯裡化函數代碼需要前面的方法調用完成之後,才會來到柯裡化函數代碼中。
需求2:閱讀類產品,一個介面的顯示,依賴於資料,需要載入完資料之後,才能判斷介面顯示。
這時候也可以利用柯裡化函數,來組裝介面,把各個模組載入資料的方法抽出來,等全部載入完成,在去執行柯裡化函數,柯裡化函數主要實現介面的組裝。
舉例說明:
// 組合介面// 為什麼要定義介面,為了程式的擴充性,以後只需要在介面中添加對應的組合方法就好了。protocol CombineUI{ func combine(top: () -> ())(bottom: () -> ())()} // 定義一個介面類,遵守組合介面class UI: CombineUI{ func combine(top: () -> ())(bottom: () -> ())() { // 搭建頂部 top() // 搭建底部 bottom() }}
點擊下載:原始碼
來源:啊崢的簡書
微博:吖了個崢,歡迎交流。
|--> Copyright (c) 2015 Bing Ma.
|--> GitHub RUL: https://github.com/SpongeBob-GitHub
Swift # 柯裡化函數