Swift 3關(guān)于函數(shù)類型的一項(xiàng)重要提議

本系列文章為個(gè)人學(xué)習(xí)筆記:禁止轉(zhuǎn)載

對(duì)于一個(gè)函數(shù)來說,把它的參數(shù)和返回值放在一起,就形成了它的簽名,這是用于描述函數(shù)自身屬性的一種類型。

參數(shù)名稱不再是函數(shù)類型的一部分

不過沒關(guān)系,我們從一個(gè)簡(jiǎn)單的例子開始。對(duì)于上一節(jié)中,我們實(shí)現(xiàn)的mul

func mul(m: Int, of n: Int) -> Int {
    return m * n
}

如果我們用type inference定義一個(gè)和mul類型相同的函數(shù)變量:

let fnMul = mul

之后,我們?cè)撊绾问褂?code>fnMul呢?在Swift 2里,是這樣的:

// ONLY 6 in Swift 2
// WRONG in Swift 3
fnMul(m: 2, n: 3)

而到了Swift 3里,編譯器會(huì)報(bào)錯(cuò)。
所以我們只能這樣使用fnMul

fnMul(2, 3) // 6

這說明什么呢?從結(jié)果上來看m:n:已經(jīng)在不再是mul簽名的一部分了。在Swift 3里,mulfnMul的類型,實(shí)際上都是:(Int, Int) -> Int

然而,為什么要做這樣的改動(dòng)呢?我們繼續(xù)來看一個(gè)在Swift 2中因?yàn)楹瘮?shù)簽名帶來的詭異的問題。

假設(shè),我們還有一個(gè)用于計(jì)算整數(shù)除法的函數(shù):

func div(a: Int, b: Int) -> Int {
    return a / b
}

然后,對(duì)于下面這段代碼,你期望最后fnDiv得到什么結(jié)果呢?

// !!! Swift 2 ONLY !!!
var fnDiv: (a: Int, b: Int) -> Int = div
fnDiv = mul

fnDiv(a: 2, b: 3) // This will call mul(m: 2, n: 3)
// !!! Swift 2 ONLY !!!

結(jié)果會(huì)是6,fnDiv(a: 2, b: 3)實(shí)際上調(diào)用了mul。造成這個(gè)結(jié)果的原因有2:

  1. 首先,muldiv都接受2個(gè)Int作為參數(shù),并返回一個(gè)Int,因此,盡管它們的參數(shù)名不同,但它們的類型是兼容的,我們可以讓fnDiv = mul;
  2. 其次,由于參數(shù)名又是函數(shù)簽名的一部分,我們使用fnDiv的時(shí)候,必須帶上a:b:;

因此,就造成了這種完全是div的用法,而暗地里執(zhí)行了mul的錯(cuò)覺。如果這段代碼隱藏在你的源文件中,你能輕易發(fā)現(xiàn)問題的原因么?

正是由于這種原因,在Swift 3里,參數(shù)名不再是函數(shù)簽名的一部分了,muldiv的類型都是(Int, Int) -> Int。這樣:

  • 之前我們定義的fnMul,只能用fnMul(2, 3)這樣的形式調(diào)用;
  • 無論fnMul被賦值成任何函數(shù),調(diào)用方式都是統(tǒng)一的:fnMul(num1, num2),這樣就不會(huì)有因?yàn)閰?shù)名帶來的調(diào)用錯(cuò)覺了;

為什么管函數(shù)類型叫做“一等公民”?

理解了函數(shù)類型之后,接下來我們來聊另外一個(gè)話題。你也許經(jīng)常會(huì)聽到類似“函數(shù)在Swift中是一等公民”這樣的說法。然而,對(duì)一門編程語言來說,“一等公民”意味著什么呢?

簡(jiǎn)單來說,就是函數(shù)這種類型,和Swift其它類型有著完全相同的語法功能。它們主要包括:

  • 可以用來定義變量,例如我們之前定義的fnMulfnDiv,這種類型的變量同樣可以當(dāng)作函數(shù)來調(diào)用;
  • 可以當(dāng)成函數(shù)參數(shù);
  • 可以被函數(shù)返回;

而對(duì)于后兩種情況之所以是很重要的語言特性,是因?yàn)樗鼈儤?gòu)成了函數(shù)式編程的基礎(chǔ)。例如,我們定義一個(gè)表達(dá)計(jì)算概念的函數(shù):

func calc<T>(_ first: T,
             _ second: T,
             _ fn: (T, T) -> T) -> T {
    return fn(first, second)
}

在這個(gè)函數(shù)的定義里,calc<T>就接受一個(gè)函數(shù)類型的參數(shù),然后,我們可以像傳遞數(shù)字一樣,來傳遞函數(shù)變量:

calc(2, 3, mul) // 6
calc(2, 3, div) // 0

類似的,我們也可以把函數(shù)類型當(dāng)成返回值,例如下面這個(gè)有點(diǎn)兒特殊的mul方法:

func mul(_ a: Int) -> (Int) -> Int {
    func innerMul(_ b: Int) -> Int {
        return a * b
    }

    return innerMul
}

它只接受一個(gè)參數(shù),并返回一個(gè)函數(shù),在返回的函數(shù)里,接受另外一個(gè)參數(shù),并最終完成相乘的運(yùn)算。因此,我們可以用兩種不同的方式來調(diào)用它:

let mul2By = mul(2)
mul2By(3) // 6

mul(2)(3) // 6

第一種方式,我們先用一個(gè)變量保存mul返回的函數(shù),然后通過再傳遞一個(gè)單參數(shù)來最終完成運(yùn)算?;蛘?,我們可以直接通過()把兩次函數(shù)調(diào)用串聯(lián)起來。因此,盡管currying function在Swift 3中被廢除了,但我們還是可以通過這樣的方法來實(shí)現(xiàn)同樣的效果。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容