Swift 中的高階函數和函數嵌套


高階函數

在Swift中,函數可做為“一等公民”的存在,也就意味着,我們可以和使用 int 以及 String 一樣,將函數當做 參數、值、類型來使用。

其中,將函數當作一個參數和值來使用可見下:

 typealias  addTwoInts = (Int,Int)->(Int)  
    
    var funcType = addTwoInts.self
    func aAddb(a:Int,b:Int) -> Int {
        return a+b
    }
    
    func addFunc(_ add:addTwoInts,_ a:Int,_ b:Int) -> Int {
        return add(a,b)
    }
//調用
     self.addFunc(aAddb, 5, 6)       //  print  -->   11     

調用函數 “ self.addFunc(aAddb, 5, 6) ” 時候,aAddb就是一個典型的“值”, 盡管它實際上是一個函數。 與此同時, 它還做為addFunc的參數來使用。

雖然這看起來多此一舉,但實際這恰恰體現了高階函數的特點,犧牲一點點代碼的簡短,將重點體現在邏輯的清晰上。

一、一個高階函數的例子

我更喜歡叫下面的這個函數為高階函數:

     var names:[String] = ["61","95","8","248","42"]  //一個包含字符串的數組
     names = names.sorted { (s1, s2) -> Bool in   return s1<s2  }
     print(names)     ----->    ["248", "42", "61", "8", "95"] 
 
        

這是一個排序函數。不要在意結果並沒有按照數字的大小排序,那是因為這是字符串排序,規則將按照首個字母的asc值進行比較。

先看看這個函數的原型:

    public func sorted(by areInIncreasingOrder: (Element, Element) -> Bool) -> [Element]

這個函數顯然是將一個  (Element, Element) -> Bool 類型的函數做為他的參數

一眼看過去,並不是那么好理解,來看下其內部的實現大概是這樣的: 

extension Array{
    
    typealias  IncreasingOrder = (String,String) -> Bool
    
    mutating func mySorted(_ increasingOrder:IncreasingOrder) -> [String] {
        var newString:[String] = [String]()

        // 假設這里采用簡單選擇排序
        for n in 0..<self.count{
            var tamper:String = self[n] as! String
            for i in n+1..<self.count {  //
                var next:String = self[i] as! String
                guard !increasingOrder(tamper,next) else {
                    continue
                }
                swap(&tamper, &next)
                swap(&self[n], &self[i])
            }
            newString.append(tamper)
        }
        return newString
    }
}

調用: 

names = ["61","95","8","248","42"]
  names = names.mySorted { (s1, s2) -> Bool in
            return s1<s2
  }
  print(names)  ----->    ["248", "42", "61", "8", "95"] 

這里為了簡便, 直接將Element替換成了String,針對String 類型來說,這個函數的功能和系統的Sotred的功能是一樣的。 如果需要支持更多的類型,可能要使用到泛型,甚至是where的可選綁定。其實系統的排序已經實現可選綁定式的排序了:重載了sorted函數,根據Element的不同類型,推斷是否需要進行可選綁定動作。

通過這個例子,可以看到,所謂的高階函數,其實就是將一個函數做為另一個函數的參數的語法。這個語法的基礎是Swift中的特性:函數的一等公民性質。

我們可以通過這種類型的語法,將類似的函數的內部代碼實現隱藏,只根據參數函數的值定義如何執行內部代碼。這中方式實現的代碼的靈活度將大大的提高。這為在Swift 中寫出使用一個函數替代多個同質化的函數提供了一種手段。 當然,做到這種效果可能還需要使用泛型編程。

 

函數嵌套

大部分情況下,遇到的所有功能都是全局函數,它們在全局范圍內定義。如果在函數局部定義一個函數,則稱為嵌套函數。

默認情況下,嵌套函數從外部世界隱藏,但在局部仍然可以正常低調用。一個閉包的函數也可以返回一個嵌套函數,以允許在其他范圍內使用嵌套函數。

func chooseStepFunction(backward: Bool) -> (Int) -> Int {    //全局函數
    func stepForward(input: Int) -> Int { return input + 1 }     //嵌套函數 1
    func stepBackward(input: Int) -> Int { return input - 1 }    //嵌套函數 2
    return backward ? stepBackward : stepForward                 //返回一個函數
}

調用:

var currentValue = -4
let moveNearerToZero = chooseStepFunction(backward: currentValue > 0)
// moveNearerToZero now refers to the nested stepForward() function
while currentValue != 0 {
    print("\(currentValue)... ")    ---->  // -4... // -3...// -2...// -1... 
 currentValue = moveNearerToZero(currentValue) } print("zero!") // zero!

嵌套函數的本質是返回一個函數,之前所說的將函數當作參數基於同一個特性 -- 函數是swift的 一等公民。

同樣的道理,一個基本類型的既然能夠做為局部的變量來存在,函數為什么不行呢? 當然可以。這就是局部函數的由來,看來還是基於一等公民的身份啊。 函數嵌套將遵守與基本類型一樣的原則,局部的函數,職能夠在局部去訪問,在外部是沒有效果的。

如果我們將嵌套的函數匿名的話,也即是我們下面的這種形式:

  typealias  adds = (Int)->(Int)  
  func add(_ c:Int) -> adds {
        return { a in
           return a + c
        }
  }

調用:

let addStart = self.add(0)
let addTwo = addStart(2)
let addFive = addStart(5)
print(addTwo)         ----->   2
print(addFive)        ----->   5

在add函數中,定義了一個起始的數值: 0,返回一個adds類型的函數。 之后我們可以通過給adds類型的實例 addTwo和addFive傳遞相應的參數即可實現多個函數的套用。 

比如,如果我們在做一個以一個起始值做為加減的時候,這中用法就靈活很多,比如,如果我需要以 10做為初始值:

let add = self.add(10)
let addTwo = add(2)
let addFive = add(5)
print(addTwo)      ==> 12
print(addFive)     --> 15    

add已經是另一個函數了。

而實際上,這就函數嵌套的另一種使用,這有很多的叫法 ,我更願意叫它 -- 柯里化。

柯里化

在計算機科學中,柯里化(Currying)是把接受多個參數的函數變換成接受一個單一參數(最初函數的第一個參數)的函數,並且返回接受余下的參數且返回結果的新函數的技術。這個技術由 Christopher Strachey 以邏輯學家 Haskell Curry 命名的,盡管它是 Moses Schnfinkel 和 Gottlob Frege 發明的。

在一般函數中,並不能輕易做到柯里化,可能需要借助其他的方案,比如,將多個參數使用替換成一個struct或者class。

然而高階函數和嵌套函數卻可以很輕易的做到,並保證代碼的邏輯清晰。然而為什么要使用柯里化,可以閱讀這個文章。 私人覺得,柯里化的優勢是去同質化的代碼 以及 注重函數的實現減少參數的干擾,簡潔提升邏輯。上面的例子剛好解釋了柯里化的使用。

 

 

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM