日本综合一区二区|亚洲中文天堂综合|日韩欧美自拍一区|男女精品天堂一区|欧美自拍第6页亚洲成人精品一区|亚洲黄色天堂一区二区成人|超碰91偷拍第一页|日韩av夜夜嗨中文字幕|久久蜜综合视频官网|精美人妻一区二区三区

RELATEED CONSULTING
相關咨詢
選擇下列產(chǎn)品馬上在線溝通
服務時間:8:30-17:00
你可能遇到了下面的問題
關閉右側工具欄

新聞中心

這里有您想知道的互聯(lián)網(wǎng)營銷解決方案
不學點高階函數(shù),如何愉快的裝逼!

如果你開始接觸函數(shù)式編程,你一定聽說過高階函數(shù)。在維基百科它的中文解釋是這樣的:

創(chuàng)新互聯(lián)建站是一家集網(wǎng)站建設,東洲企業(yè)網(wǎng)站建設,東洲品牌網(wǎng)站建設,網(wǎng)站定制,東洲網(wǎng)站建設報價,網(wǎng)絡營銷,網(wǎng)絡優(yōu)化,東洲網(wǎng)站推廣為一體的創(chuàng)新建站企業(yè),幫助傳統(tǒng)企業(yè)提升企業(yè)形象加強企業(yè)競爭力??沙浞譂M足這一群體相比中小企業(yè)更為豐富、高端、多元的互聯(lián)網(wǎng)需求。同時我們時刻保持專業(yè)、時尚、前沿,時刻以成就客戶成長自我,堅持不斷學習、思考、沉淀、凈化自己,讓我們?yōu)楦嗟钠髽I(yè)打造出實用型網(wǎng)站。

在數(shù)學和計算機科學中,高階函數(shù)是至少滿足下列一個條件的函數(shù):

  • 接受一個或多個函數(shù)作為輸入
  • 輸出一個函數(shù)

看起它就是ObjC語言中入?yún)⒒蛘叻祷刂禐閎lock的block或者函數(shù),在Swift語言中即為入?yún)⒒蛘叻祷刂禐楹瘮?shù)的函數(shù)。那它們在實際的開發(fā)過程中究竟起著什么樣的作用呢?我們將從入?yún)?、返回值和綜合使用三部分來看這個問題:

函數(shù)作為入?yún)?/strong>

函數(shù)作為入?yún)⑺坪鯚o論在ObjC時代還是Swift時代都是司空見慣的事情,例如AFNetworking就用兩個入?yún)lock分別回調(diào)成功與失敗。Swift中更是加了一個尾閉包的語法(最后一個參數(shù)為函數(shù),可以不寫括號或者寫到括號外面直接跟隨方法名),例如下面這樣:

 
 
 
 
  1. [1, 2, 3].forEach { item in 
  2.     print(item) 
  3. }  

我們可以將入?yún)楹瘮?shù)的函數(shù)分為兩類,escaping函數(shù)入?yún)⒑蚽oescape函數(shù)入?yún)ⅲ瑓^(qū)別在于這個入?yún)⒌暮瘮?shù)是在執(zhí)行過程內(nèi)被調(diào)用還是在執(zhí)行過程外被調(diào)用。執(zhí)行過程外被調(diào)用的一般用于callback用途,例如:

 
 
 
 
  1. Alamofire.request("https://httpbin.org/get").responseJSON { response in 
  2.     print(response.request)  // original URL request   
  3.     print(response.response) // HTTP URL response   
  4.     print(response.data)     // server data   
  5.     print(response.result)   // result of response serialization   
  6.  
  7.     if let JSON = response.result.value {        print("JSON: \(JSON)") 
  8.     } 
  9. }  

這個response的入?yún)⒑瘮?shù)就作為網(wǎng)絡請求回來的一個callback,并不會在執(zhí)行responseJSON這個函數(shù)的時候被調(diào)用。另外我們來觀察forEach的代碼,可以推斷入?yún)⒌暮瘮?shù)一定會在forEach執(zhí)行過程中使用,執(zhí)行完就沒有利用意義,這類就是noescape函數(shù)。

callback的用法大家應該比較熟悉了,介紹給大家noescape入?yún)⒌囊恍┯梅ǎ?/p>

1. 自由構造器

看過GoF設計模式的同學不知道是否還記得構造器模式,Android中的構造器模式類似如下:

 
 
 
 
  1. new AlertDialog.Builder(this) 
  2.   .setIcon(R.drawable.find_daycycle_icon) 
  3.   .setTitle("提醒") 
  4.   .create() 
  5.   .show();  
  1. 構造一個對象需要很多的參數(shù)
  2. 這些參數(shù)里面很多有默認值
  3. 這些參數(shù)對應的屬性未來不希望被修改

那么用這樣的模式就可以直觀又精巧的展示構建過程。

如果使用noescape入?yún)⒑瘮?shù)還可以更簡單的構造出這種代碼,只需要傳入一個入?yún)閎uilder的對象就可以了,如下:

 
 
 
 
  1. // 實現(xiàn)在這里  class SomeBuilder {    var prop1: Int 
  2.     var prop2: Bool 
  3.     var prop3: String 
  4.     init() {        // default value   
  5.         prop1 = 0 
  6.         prop2 = true 
  7.         prop3 = "some string" 
  8.     } 
  9. }class SomeObj { 
  10.     private var prop1: Int 
  11.     private var prop2: Bool 
  12.     private var prop3: String 
  13.     init(_ builderBlock:(SomeBuilder) -> Void) {        let someBuilder = SomeBuilder() 
  14.         builderBlock(someBuilder) // noescape 入?yún)⒌氖褂?nbsp;  
  15.         prop1 = someBuilder.prop1 
  16.         prop2 = someBuilder.prop2 
  17.         prop3 = someBuilder.prop3 
  18.     } 
  19. }// 使用的時候  let someOjb = SomeObj { builder in 
  20.     builder.prop1 = 15 
  21.     builder.prop2 = false 
  22.     builder.prop3 = "haha"}  

2. 自動配對操作

很多時候,我們開發(fā)過程中都會遇到必須配對才能正常工作的API,例如打開文件和關閉文件、進入edit模式退出edit模式等。雖然swift語言給我們defer這樣的語法糖避免大家忘記配對操作,但是代碼看起來還是不那么順眼

 
 
 
 
  1. func updateTableView1() {    self.tableView.beginUpdates()    self.tableView.insertRows(at: [IndexPath(row: 2, section: 0)], with: .fade)    self.tableView.deleteRows(at: [IndexPath(row: 5, section: 0)], with: .fade)  self.tableView.endUpdates() // 容易漏掉或者上面出現(xiàn)異常  }func updateTableView2() {    self.tableView.beginUpdates() 
  2.     defer {        self.tableView.endUpdates() 
  3.     }    self.tableView.insertRows(at: [IndexPath(row: 2, section: 0)], with: .fade)    self.tableView.deleteRows(at: [IndexPath(row: 5, section: 0)], with: .fade) 

利用noescape入?yún)?,我們可以將要操作的過程封裝起來,使得上層看起來更規(guī)整

 
 
 
 
  1. // 擴展一下UITableView  extension UITableView {    func updateCells(updateBlock: (UITableView) -> Void) { 
  2.         beginUpdates() 
  3.         defer { 
  4.             endUpdates() 
  5.         } 
  6.         updateBlock(self) 
  7.     } 
  8. }func updateTableView() {  // 使用的時候   
  9.     self.tableView.updateCells { (tableView) in 
  10.         tableView.insertRows(at: [IndexPath(row: 2, section: 0)], with: .fade) 
  11.         tableView.deleteRows(at: [IndexPath(row: 5, section: 0)], with: .fade) 
  12.     } 

函數(shù)作為入?yún)⒕秃唵谓榻B到這里,下面看看函數(shù)作為返回值。

函數(shù)作為返回值

在大家的日常開發(fā)中,函數(shù)作為返回值的情況想必是少之又少。不過,如果能簡單利用起來,就會讓代碼一下子清爽很多。

首先沒有爭議的就是我們有很多的API都是需要函數(shù)作為入?yún)⒌?,無論是上一節(jié)提到過的escaping入?yún)⑦€是noescape入?yún)?。所以很多的時候,大家寫的代碼重復率會很高,例如:

 
 
 
 
  1. let array = [1, 3, 55, 47, 92, 77, 801]let array1 = array.filter { $0 > 3 * 3}let array2 = array.filter { $0 > 4 * 4}let array3 = array.filter { $0 > 2 * 2}let array4 = array.filter { $0 > 5 * 5} 

一段從數(shù)組中找到大于某個數(shù)平方的代碼,如果不封裝,看起來應該是這樣的。為了簡化,通常會封裝成如下的兩個樣子:

 
 
 
 
  1. func biggerThanPowWith(array: [Int], value: Int) -> [Int] { 
  2.     return array.filter { $0 > value * value} 
  3.  
  4. let array1 = biggerThanPowWith(array: array, value: 3) 
  5. let array2 = biggerThanPowWith(array: array, value: 4) 
  6. let array3 = biggerThanPowWith(array: array, value: 2) 
  7. let array4 = biggerThanPowWith(array: array, value: 5) 

如果用高階函數(shù)的返回值函數(shù),可以做成這樣一個高階函數(shù):

 
 
 
 
  1. // 一個返回(Int)->Bool的函數(shù)  func biggerThanPow2With(value: Int) -> (Int) -> Bool {    return { $0 > value * value } 
  2. }let array1 = array.filter(biggerThanPow2With(value: 3))let array2 = array.filter(biggerThanPow2With(value: 4))let array3 = array.filter(biggerThanPow2With(value: 2))let array4 = array.filter(biggerThanPow2With(value: 5)) 

你一定會說,兩者看起來沒啥區(qū)別。所以這里面需要講一下使用高階返回函數(shù)的幾點好處

1. 不需要wrapper函數(shù)也不需要打開原始類

如同上面的簡單封裝,其實就是一個wrapper函數(shù),把array作為入?yún)脒M來。這樣寫代碼和看代碼的時候就稍微不爽一點,畢竟大家都喜歡OOP嘛。如果要OOP,那就勢必要對原始類進行擴展,一種方式是加extension,或者直接給類加一個新的方法。

2. 閱讀代碼的時候一目了然

使用簡單封裝的時候,看代碼的人并不知道內(nèi)部使用了filter這個函數(shù),必須要查看源碼才能知道。但是用高階函數(shù)的時候,一下子就知道了使用了系統(tǒng)庫的filter。

3. 更容易復用

這也是最關鍵的一點,更細粒度的高階函數(shù),可以更方便的復用,例如我們知道Set也是有filter這個方法的,復用起來就這樣:

 
 
 
 
  1. let set = Set(arrayLiteral: 1, 3, 7, 9, 17, 55, 47, 92, 77, 801)let set1 = set.filter(biggerThanPow2With(value: 3))let set2 = set.filter(biggerThanPow2With(value: 9)) 

回憶下上面的簡單封裝,是不是就無法重用了呢?

類似的返回函數(shù)的高階函數(shù)還可以有很多例子,例如上面說過的builder,假如每次都需要定制成特殊的樣子,但是某個字段不同,就可以用高階函數(shù)很容易打造出來:

 
 
 
 
  1. func builerWithDifferentProp3(prop3: String) -> (SomeBuilder) -> Void {    return { builder in 
  2.         builder.prop1 = 15 
  3.         builder.prop2 = true 
  4.         builder.prop3 = prop3 
  5.     } 
  6. }let someObj1 = SomeObj.init(builerWithDifferentProp3(prop3: "a"))let someObj2 = SomeObj.init(builerWithDifferentProp3(prop3: "b"))let someObj3 = SomeObj.init(builerWithDifferentProp3(prop3: "c")) 

介紹完入?yún)⑴c返回值,還有另外的一個組合模式,那就是入?yún)⑹且粋€函數(shù),返回值也是一個函數(shù)的情況,我們來看看這種情況。

入?yún)⒑瘮?shù) && 返回值函數(shù)

這樣的一個函數(shù)看起來會很恐怖,swift會聲明成:

 
 
 
 
  1. func someFunc(_ a: (A) -> B)-> (C) -> D 

objective-c會聲明成

 
 
 
 
  1. - (id (^)(id))someFunc:(id (^)(id))block 

讓我們先從一個小的例子來講起,回憶一下我們剛剛做的biggerThanPow2With這個函數(shù),如果我們要一個notBiggerThanPow2With怎么辦呢?你知道我一定不會說再寫一個。所以我告訴你我會這樣寫:

 
 
 
 
  1. func not(_ origin_func: @escaping (T) -> Bool) -> (T) -> Bool {    return { !origin_func($0) } 
  2. }let array5 = array.filter(not(biggerThanPow2With(value: 9))) 

并不需要一個notBiggerThanPow2With函數(shù),我們只需要實現(xiàn)一個not就可以了。它的入?yún)⑹且粋€(T) -> Bool,返回值也是(T) -> Bool,只需要在執(zhí)行block內(nèi)部的時候用個取反就可以了。這樣不單可以解決剛才的問題,還可以解決任何(T) -> Bool類型函數(shù)的取反問題,比如我們有一個odd(_: int)方法來過濾奇數(shù),那我們就可以用even=not(odd)得到一個過濾偶數(shù)的函數(shù)了。

 
 
 
 
  1. func odd(_ value: Int) -> Bool {    return value % 2 == 1}let array6 = array.filter(odd)let array7 = array.filter(not(odd))let even = not(odd)let array8 = array.filter(even) 

大家可以看下上面的biggerThanPow2With時我們討論過的,如果biggerThanPow2With不是一個返回函數(shù)的高階函數(shù),那它就不太容易用not函數(shù)來加工了。

綜上,如果一個入?yún)⒑头祷刂刀际呛瘮?shù)的函數(shù)就是這樣的一個轉(zhuǎn)換函數(shù),它能夠讓我們用更少的代碼組合出更多的函數(shù)。另外需要注意一下,如果返回的函數(shù)里面閉包了入?yún)⒌暮瘮?shù),那么入?yún)⒑瘮?shù)就是escaping入?yún)⒘恕?/p>

下面再展示給大家兩個函數(shù),一個交換參數(shù)的函數(shù)exchangeParam,另一個是柯里化函數(shù)currying:

 
 
 
 
  1. func exchangeParam(_ block: @escaping (A, B) -> C) -> (B, A) -> C {    return { block($1, $0) } 
  2. }func currying(_ block: @escaping (A, B) -> C, _ value: A) -> (B) -> C {    return { block(value, $0) } 

swift語言里面>是一個入?yún)?a, b)的函數(shù),所以>(5, 3) == true。我們使用exchangeParam交換參數(shù)就變成了(b, a),這時exchangeParam(>)(5, 3)就等于false了。

而currying函數(shù)又把參數(shù)b固定為一個常量9,所以currying(exchangeParam(>), 9)就是大于9的函數(shù)意思。

這個例子里就利用了全部的預制函數(shù)和通用函數(shù),沒有借助任何的命令與業(yè)務函數(shù)聲明實現(xiàn)了一個從數(shù)組中過濾大于9的子數(shù)組的需求。試想一下,如果我們更多的使用這樣的高階函數(shù),代碼中是不是很多的邏輯可以更容易的互相組合,而這就是函數(shù)式編程的魅力。

總結

高階函數(shù)的引入,無論是從函數(shù)式編程還是從非函數(shù)式編程都帶給我們代碼一定程度的簡化,使得我們的API更加簡易可用,復用更充分。然而本文的例子不過是冰山一角,更多的內(nèi)容還需要大家的不斷嘗試和創(chuàng)新,也可以通過學習更多的函數(shù)式編程范式來加深理解。

作者介紹:


本文題目:不學點高階函數(shù),如何愉快的裝逼!
文章位置:http://www.dlmjj.cn/article/dppeeci.html