這里的教程為Swift官方教程中文版。

函數(shù)(Functions)


1.0 翻譯:honghaoz 校對:LunaticM

2.0 翻譯+校對:dreamkidd

2.1 翻譯:DianQK 定稿:shanks

2.2 翻譯+校對:SketchK 2016-05-12

3.0 翻譯: crayygy 2016-09-12 校對: shanks 2016-09-27 3.0.1,shanks,2016-11-12

本頁包含內(nèi)容:

函數(shù)是一段完成特定任務(wù)的獨(dú)立代碼片段。你可以通過給函數(shù)命名來標(biāo)識某個(gè)函數(shù)的功能,這個(gè)名字可以被用來在需要的時(shí)候"調(diào)用"這個(gè)函數(shù)來完成它的任務(wù)。

Swift 統(tǒng)一的函數(shù)語法非常的靈活,可以用來表示任何函數(shù),包括從最簡單的沒有參數(shù)名字的 C 風(fēng)格函數(shù),到復(fù)雜的帶局部和外部參數(shù)名的 Objective-C 風(fēng)格函數(shù)。參數(shù)可以提供默認(rèn)值,以簡化函數(shù)調(diào)用。參數(shù)也可以既當(dāng)做傳入?yún)?shù),也當(dāng)做傳出參數(shù),也就是說,一旦函數(shù)執(zhí)行結(jié)束,傳入的參數(shù)值將被修改。

在 Swift 中,每個(gè)函數(shù)都有一個(gè)由函數(shù)的參數(shù)值類型和返回值類型組成的類型。你可以把函數(shù)類型當(dāng)做任何其他普通變量類型一樣處理,這樣就可以更簡單地把函數(shù)當(dāng)做別的函數(shù)的參數(shù),也可以從其他函數(shù)中返回函數(shù)。函數(shù)的定義可以寫在其他函數(shù)定義中,這樣可以在嵌套函數(shù)范圍內(nèi)實(shí)現(xiàn)功能封裝。

函數(shù)的定義與調(diào)用

當(dāng)你定義一個(gè)函數(shù)時(shí),你可以定義一個(gè)或多個(gè)有名字和類型的值,作為函數(shù)的輸入,稱為參數(shù),也可以定義某種類型的值作為函數(shù)執(zhí)行結(jié)束時(shí)的輸出,稱為返回類型

每個(gè)函數(shù)有個(gè)函數(shù)名,用來描述函數(shù)執(zhí)行的任務(wù)。要使用一個(gè)函數(shù)時(shí),用函數(shù)名來“調(diào)用”這個(gè)函數(shù),并傳給它匹配的輸入值(稱作 實(shí)參 )。函數(shù)的實(shí)參必須與函數(shù)參數(shù)表里參數(shù)的順序一致。

下面例子中的函數(shù)的名字是greet(person:),之所以叫這個(gè)名字,是因?yàn)檫@個(gè)函數(shù)用一個(gè)人的名字當(dāng)做輸入,并返回向這個(gè)人問候的語句。為了完成這個(gè)任務(wù),你需要定義一個(gè)輸入?yún)?shù)——一個(gè)叫做 personString 值,和一個(gè)包含給這個(gè)人問候語的 String 類型的返回值:

func greet(person: String) -> String {
    let greeting = "Hello, " + person + "!"
    return greeting
}

所有的這些信息匯總起來成為函數(shù)的定義,并以 func 作為前綴。指定函數(shù)返回類型時(shí),用返回箭頭 ->(一個(gè)連字符后跟一個(gè)右尖括號)后跟返回類型的名稱的方式來表示。

該定義描述了函數(shù)的功能,它期望接收什么作為參數(shù)和執(zhí)行結(jié)束時(shí)它返回的結(jié)果是什么類型。這樣的定義使得函數(shù)可以在別的地方以一種清晰的方式被調(diào)用:

print(greet(person: "Anna"))
// 打印 "Hello, Anna!"
print(greet(person: "Brian"))
// 打印 "Hello, Brian!"

調(diào)用 greet(person:) 函數(shù)時(shí),在圓括號中傳給它一個(gè) String 類型的實(shí)參,例如 greet(person: "Anna")。正如上面所示,因?yàn)檫@個(gè)函數(shù)返回一個(gè) String 類型的值,所以greet 可以被包含在 print(_:separator:terminator:) 的調(diào)用中,用來輸出這個(gè)函數(shù)的返回值。

注意
print(_:separator:terminator:) 函數(shù)的第一個(gè)參數(shù)并沒有設(shè)置一個(gè)標(biāo)簽,而其他的參數(shù)因?yàn)橐呀?jīng)有了默認(rèn)值,因此是可選的。關(guān)于這些函數(shù)語法上的變化詳見下方關(guān)于 函數(shù)參數(shù)標(biāo)簽和參數(shù)名 以及 默認(rèn)參數(shù)值。

greet(person:) 的函數(shù)體中,先定義了一個(gè)新的名為 greetingString 常量,同時(shí),把對 personName 的問候消息賦值給了 greeting 。然后用 return 關(guān)鍵字把這個(gè)問候返回出去。一旦 return greeting 被調(diào)用,該函數(shù)結(jié)束它的執(zhí)行并返回 greeting 的當(dāng)前值。

你可以用不同的輸入值多次調(diào)用 greet(person:)。上面的例子展示的是用"Anna""Brian"調(diào)用的結(jié)果,該函數(shù)分別返回了不同的結(jié)果。

為了簡化這個(gè)函數(shù)的定義,可以將問候消息的創(chuàng)建和返回寫成一句:

func greetAgain(person: String) -> String {
    return "Hello again, " + person + "!"
}
print(greetAgain(person: "Anna"))
// 打印 "Hello again, Anna!"

函數(shù)參數(shù)與返回值

函數(shù)參數(shù)與返回值在 Swift 中非常的靈活。你可以定義任何類型的函數(shù),包括從只帶一個(gè)未名參數(shù)的簡單函數(shù)到復(fù)雜的帶有表達(dá)性參數(shù)名和不同參數(shù)選項(xiàng)的復(fù)雜函數(shù)。

無參數(shù)函數(shù)

函數(shù)可以沒有參數(shù)。下面這個(gè)函數(shù)就是一個(gè)無參數(shù)函數(shù),當(dāng)被調(diào)用時(shí),它返回固定的 String 消息:

func sayHelloWorld() -> String {
    return "hello, world"
}
print(sayHelloWorld())
// 打印 "hello, world"

盡管這個(gè)函數(shù)沒有參數(shù),但是定義中在函數(shù)名后還是需要一對圓括號。當(dāng)被調(diào)用時(shí),也需要在函數(shù)名后寫一對圓括號。

多參數(shù)函數(shù)

函數(shù)可以有多種輸入?yún)?shù),這些參數(shù)被包含在函數(shù)的括號之中,以逗號分隔。

下面這個(gè)函數(shù)用一個(gè)人名和是否已經(jīng)打過招呼作為輸入,并返回對這個(gè)人的適當(dāng)問候語:

func greet(person: String, alreadyGreeted: Bool) -> String {
    if alreadyGreeted {
        return greetAgain(person: person)
    } else {
        return greet(person: person)
    }
}
print(greet(person: "Tim", alreadyGreeted: true))
// 打印 "Hello again, Tim!"

你可以通過在括號內(nèi)使用逗號分隔來傳遞一個(gè)String參數(shù)值和一個(gè)標(biāo)識為alreadyGreetedBool值,來調(diào)用greet(person:alreadyGreeted:)函數(shù)。注意這個(gè)函數(shù)和上面greet(person:)是不同的。雖然它們都有著同樣的名字greet,但是greet(person:alreadyGreeted:)函數(shù)需要兩個(gè)參數(shù),而greet(person:)只需要一個(gè)參數(shù)。

無返回值函數(shù)

函數(shù)可以沒有返回值。下面是 greet(person:) 函數(shù)的另一個(gè)版本,這個(gè)函數(shù)直接打印一個(gè)String值,而不是返回它:

func greet(person: String) {
    print("Hello, \(person)!")
}
greet(person: "Dave")
// 打印 "Hello, Dave!"

因?yàn)檫@個(gè)函數(shù)不需要返回值,所以這個(gè)函數(shù)的定義中沒有返回箭頭(->)和返回類型。

注意
嚴(yán)格上來說,雖然沒有返回值被定義,greet(person:) 函數(shù)依然返回了值。沒有定義返回類型的函數(shù)會返回一個(gè)特殊的Void值。它其實(shí)是一個(gè)空的元組(tuple),沒有任何元素,可以寫成()。

被調(diào)用時(shí),一個(gè)函數(shù)的返回值可以被忽略:

func printAndCount(string: String) -> Int {
    print(string)
    return string.characters.count
}
func printWithoutCounting(string: String) {
    let _ = printAndCount(string: string)
}
printAndCount(string: "hello, world")
// 打印 "hello, world" 并且返回值 12
printWithoutCounting(string: "hello, world")
// 打印 "hello, world" 但是沒有返回任何值

第一個(gè)函數(shù) printAndCount(string:),輸出一個(gè)字符串并返回 Int 類型的字符數(shù)。第二個(gè)函數(shù) printWithoutCounting(string:)調(diào)用了第一個(gè)函數(shù),但是忽略了它的返回值。當(dāng)?shù)诙€(gè)函數(shù)被調(diào)用時(shí),消息依然會由第一個(gè)函數(shù)輸出,但是返回值不會被用到。

注意:
返回值可以被忽略,但定義了有返回值的函數(shù)必須返回一個(gè)值,如果在函數(shù)定義底部沒有返回任何值,將導(dǎo)致編譯時(shí)錯誤(compile-time error)。

多重返回值函數(shù)

你可以用元組(tuple)類型讓多個(gè)值作為一個(gè)復(fù)合值從函數(shù)中返回。

下例中定義了一個(gè)名為 minMax(array:) 的函數(shù),作用是在一個(gè) Int 類型的數(shù)組中找出最小值與最大值。

func minMax(array: [Int]) -> (min: Int, max: Int) {
    var currentMin = array[0]
    var currentMax = array[0]
    for value in array[1..<array.count] {
        if value < currentMin {
            currentMin = value
        } else if value > currentMax {
            currentMax = value
        }
    }
    return (currentMin, currentMax)
}

minMax(array:) 函數(shù)返回一個(gè)包含兩個(gè) Int 值的元組,這些值被標(biāo)記為 minmax ,以便查詢函數(shù)的返回值時(shí)可以通過名字訪問它們。

minMax(array:) 的函數(shù)體中,在開始的時(shí)候設(shè)置兩個(gè)工作變量 currentMincurrentMax 的值為數(shù)組中的第一個(gè)數(shù)。然后函數(shù)會遍歷數(shù)組中剩余的值并檢查該值是否比 currentMincurrentMax 更小或更大。最后數(shù)組中的最小值與最大值作為一個(gè)包含兩個(gè) Int 值的元組返回。

因?yàn)樵M的成員值已被命名,因此可以通過 . 語法來檢索找到的最小值與最大值:

let bounds = minMax(array: [8, -6, 2, 109, 3, 71])
print("min is \(bounds.min) and max is \(bounds.max)")
// 打印 "min is -6 and max is 109"

需要注意的是,元組的成員不需要在元組從函數(shù)中返回時(shí)命名,因?yàn)樗鼈兊拿忠呀?jīng)在函數(shù)返回類型中指定了。

可選元組返回類型

如果函數(shù)返回的元組類型有可能整個(gè)元組都“沒有值”,你可以使用可選的( optional ) 元組返回類型反映整個(gè)元組可以是nil的事實(shí)。你可以通過在元組類型的右括號后放置一個(gè)問號來定義一個(gè)可選元組,例如 (Int, Int)?(String, Int, Bool)?

注意 可選元組類型如 (Int, Int)? 與元組包含可選類型如 (Int?, Int?) 是不同的.可選的元組類型,整個(gè)元組是可選的,而不只是元組中的每個(gè)元素值。

前面的 minMax(array:) 函數(shù)返回了一個(gè)包含兩個(gè) Int 值的元組。但是函數(shù)不會對傳入的數(shù)組執(zhí)行任何安全檢查,如果 array 參數(shù)是一個(gè)空數(shù)組,如上定義的 minMax(array:) 在試圖訪問 array[0] 時(shí)會觸發(fā)一個(gè)運(yùn)行時(shí)錯誤(runtime error)。

為了安全地處理這個(gè)“空數(shù)組”問題,將 minMax(array:) 函數(shù)改寫為使用可選元組返回類型,并且當(dāng)數(shù)組為空時(shí)返回 nil

func minMax(array: [Int]) -> (min: Int, max: Int)? {
    if array.isEmpty { return nil }
    var currentMin = array[0]
    var currentMax = array[0]
    for value in array[1..<array.count] {
        if value < currentMin {
            currentMin = value
        } else if value > currentMax {
            currentMax = value
        }
    }
    return (currentMin, currentMax)
}

你可以使用可選綁定來檢查 minMax(array:) 函數(shù)返回的是一個(gè)存在的元組值還是 nil

if let bounds = minMax(array: [8, -6, 2, 109, 3, 71]) {
    print("min is \(bounds.min) and max is \(bounds.max)")
}
// 打印 "min is -6 and max is 109"

函數(shù)參數(shù)標(biāo)簽和參數(shù)名稱

每個(gè)函數(shù)參數(shù)都有一個(gè)參數(shù)標(biāo)簽( argument label )以及一個(gè)參數(shù)名稱( parameter name )。參數(shù)標(biāo)簽在調(diào)用函數(shù)的時(shí)候使用;調(diào)用的時(shí)候需要將函數(shù)的參數(shù)標(biāo)簽寫在對應(yīng)的參數(shù)前面。參數(shù)名稱在函數(shù)的實(shí)現(xiàn)中使用。默認(rèn)情況下,函數(shù)參數(shù)使用參數(shù)名稱來作為它們的參數(shù)標(biāo)簽。

func someFunction(firstParameterName: Int, secondParameterName: Int) {
    // 在函數(shù)體內(nèi),firstParameterName 和 secondParameterName 代表參數(shù)中的第一個(gè)和第二個(gè)參數(shù)值
}
someFunction(firstParameterName: 1, secondParameterName: 2)

所有的參數(shù)都必須有一個(gè)獨(dú)一無二的名字。雖然多個(gè)參數(shù)擁有同樣的參數(shù)標(biāo)簽是可能的,但是一個(gè)唯一的函數(shù)標(biāo)簽?zāi)軌蚴鼓愕拇a更具可讀性。

指定參數(shù)標(biāo)簽

你可以在函數(shù)名稱前指定它的參數(shù)標(biāo)簽,中間以空格分隔:

func someFunction(argumentLabel parameterName: Int) {
    // 在函數(shù)體內(nèi),parameterName 代表參數(shù)值
}

這個(gè)版本的 greet(person:) 函數(shù),接收一個(gè)人的名字和他的家鄉(xiāng),并且返回一句問候:

func greet(person: String, from hometown: String) -> String {
    return "Hello \(person)!  Glad you could visit from \(hometown)."
}
print(greet(person: "Bill", from: "Cupertino"))
// 打印 "Hello Bill!  Glad you could visit from Cupertino."

參數(shù)標(biāo)簽的使用能夠讓一個(gè)函數(shù)在調(diào)用時(shí)更有表達(dá)力,更類似自然語言,并且仍保持了函數(shù)內(nèi)部的可讀性以及清晰的意圖。

忽略參數(shù)標(biāo)簽

如果你不希望為某個(gè)參數(shù)添加一個(gè)標(biāo)簽,可以使用一個(gè)下劃線(_)來代替一個(gè)明確的參數(shù)標(biāo)簽。

func someFunction(_ firstParameterName: Int, secondParameterName: Int) {
     // 在函數(shù)體內(nèi),firstParameterName 和 secondParameterName 代表參數(shù)中的第一個(gè)和第二個(gè)參數(shù)值
}
someFunction(1, secondParameterName: 2)

如果一個(gè)參數(shù)有一個(gè)標(biāo)簽,那么在調(diào)用的時(shí)候必須使用標(biāo)簽來標(biāo)記這個(gè)參數(shù)。

默認(rèn)參數(shù)值

你可以在函數(shù)體中通過給參數(shù)賦值來為任意一個(gè)參數(shù)定義默認(rèn)值(Deafult Value)。當(dāng)默認(rèn)值被定義后,調(diào)用這個(gè)函數(shù)時(shí)可以忽略這個(gè)參數(shù)。

func someFunction(parameterWithoutDefault: Int, parameterWithDefault: Int = 12) {
    // 如果你在調(diào)用時(shí)候不傳第二個(gè)參數(shù),parameterWithDefault 會值為 12 傳入到函數(shù)體中。
}
someFunction(parameterWithoutDefault: 3, parameterWithDefault: 6) // parameterWithDefault = 6
someFunction(parameterWithoutDefault: 4) // parameterWithDefault = 12

將不帶有默認(rèn)值的參數(shù)放在函數(shù)參數(shù)列表的最前。一般來說,沒有默認(rèn)值的參數(shù)更加的重要,將不帶默認(rèn)值的參數(shù)放在最前保證在函數(shù)調(diào)用時(shí),非默認(rèn)參數(shù)的順序是一致的,同時(shí)也使得相同的函數(shù)在不同情況下調(diào)用時(shí)顯得更為清晰。

可變參數(shù)

一個(gè)可變參數(shù)(variadic parameter)可以接受零個(gè)或多個(gè)值。函數(shù)調(diào)用時(shí),你可以用可變參數(shù)來指定函數(shù)參數(shù)可以被傳入不確定數(shù)量的輸入值。通過在變量類型名后面加入(...)的方式來定義可變參數(shù)。

可變參數(shù)的傳入值在函數(shù)體中變?yōu)榇祟愋偷囊粋€(gè)數(shù)組。例如,一個(gè)叫做 numbersDouble... 型可變參數(shù),在函數(shù)體內(nèi)可以當(dāng)做一個(gè)叫 numbers[Double] 型的數(shù)組常量。

下面的這個(gè)函數(shù)用來計(jì)算一組任意長度數(shù)字的 算術(shù)平均數(shù)(arithmetic mean)

func arithmeticMean(_ numbers: Double...) -> Double {
    var total: Double = 0
    for number in numbers {
        total += number
    }
    return total / Double(numbers.count)
}
arithmeticMean(1, 2, 3, 4, 5)
// 返回 3.0, 是這 5 個(gè)數(shù)的平均數(shù)。
arithmeticMean(3, 8.25, 18.75)
// 返回 10.0, 是這 3 個(gè)數(shù)的平均數(shù)。

注意:
一個(gè)函數(shù)最多只能擁有一個(gè)可變參數(shù)。

輸入輸出參數(shù)

函數(shù)參數(shù)默認(rèn)是常量。試圖在函數(shù)體中更改參數(shù)值將會導(dǎo)致編譯錯誤(compile-time error)。這意味著你不能錯誤地更改參數(shù)值。如果你想要一個(gè)函數(shù)可以修改參數(shù)的值,并且想要在這些修改在函數(shù)調(diào)用結(jié)束后仍然存在,那么就應(yīng)該把這個(gè)參數(shù)定義為輸入輸出參數(shù)(In-Out Parameters)。

定義一個(gè)輸入輸出參數(shù)時(shí),在參數(shù)定義前加 inout 關(guān)鍵字。一個(gè)輸入輸出參數(shù)有傳入函數(shù)的值,這個(gè)值被函數(shù)修改,然后被傳出函數(shù),替換原來的值。想獲取更多的關(guān)于輸入輸出參數(shù)的細(xì)節(jié)和相關(guān)的編譯器優(yōu)化,請查看輸入輸出參數(shù)一節(jié)。

你只能傳遞變量給輸入輸出參數(shù)。你不能傳入常量或者字面量,因?yàn)檫@些量是不能被修改的。當(dāng)傳入的參數(shù)作為輸入輸出參數(shù)時(shí),需要在參數(shù)名前加 & 符,表示這個(gè)值可以被函數(shù)修改。

注意 輸入輸出參數(shù)不能有默認(rèn)值,而且可變參數(shù)不能用 inout 標(biāo)記。

下例中,swapTwoInts(_:_:) 函數(shù)有兩個(gè)分別叫做 ab 的輸入輸出參數(shù):

func swapTwoInts(_ a: inout Int, _ b: inout Int) {
    let temporaryA = a
    a = b
    b = temporaryA
}

swapTwoInts(_:_:) 函數(shù)簡單地交換 ab 的值。該函數(shù)先將 a 的值存到一個(gè)臨時(shí)常量 temporaryA 中,然后將 b 的值賦給 a,最后將 temporaryA 賦值給 b。

你可以用兩個(gè) Int 型的變量來調(diào)用 swapTwoInts(_:_:)。需要注意的是,someIntanotherInt 在傳入 swapTwoInts(_:_:) 函數(shù)前,都加了 & 的前綴:

var someInt = 3
var anotherInt = 107
swapTwoInts(&someInt, &anotherInt)
print("someInt is now \(someInt), and anotherInt is now \(anotherInt)")
// 打印 "someInt is now 107, and anotherInt is now 3"

從上面這個(gè)例子中,我們可以看到 someIntanotherInt 的原始值在 swapTwoInts(_:_:) 函數(shù)中被修改,盡管它們的定義在函數(shù)體外。

注意:
輸入輸出參數(shù)和返回值是不一樣的。上面的 swapTwoInts 函數(shù)并沒有定義任何返回值,但仍然修改了 someIntanotherInt 的值。輸入輸出參數(shù)是函數(shù)對函數(shù)體外產(chǎn)生影響的另一種方式。

函數(shù)類型

每個(gè)函數(shù)都有種特定的函數(shù)類型,函數(shù)的類型由函數(shù)的參數(shù)類型和返回類型組成。

例如:

func addTwoInts(_ a: Int, _ b: Int) -> Int {
    return a + b
}
func multiplyTwoInts(_ a: Int, _ b: Int) -> Int {
    return a * b
}

這個(gè)例子中定義了兩個(gè)簡單的數(shù)學(xué)函數(shù):addTwoIntsmultiplyTwoInts。這兩個(gè)函數(shù)都接受兩個(gè) Int 值, 返回一個(gè) Int 值。

這兩個(gè)函數(shù)的類型是 (Int, Int) -> Int,可以解讀為“這個(gè)函數(shù)類型有兩個(gè) Int 型的參數(shù)并返回一個(gè) Int 型的值?!薄?/p>

下面是另一個(gè)例子,一個(gè)沒有參數(shù),也沒有返回值的函數(shù):

func printHelloWorld() {
    print("hello, world")
}

這個(gè)函數(shù)的類型是:() -> Void,或者叫“沒有參數(shù),并返回 Void 類型的函數(shù)”。

使用函數(shù)類型

在 Swift 中,使用函數(shù)類型就像使用其他類型一樣。例如,你可以定義一個(gè)類型為函數(shù)的常量或變量,并將適當(dāng)?shù)暮瘮?shù)賦值給它:

var mathFunction: (Int, Int) -> Int = addTwoInts

這段代碼可以被解讀為:

”定義一個(gè)叫做 mathFunction 的變量,類型是‘一個(gè)有兩個(gè) Int 型的參數(shù)并返回一個(gè) Int 型的值的函數(shù)’,并讓這個(gè)新變量指向 addTwoInts 函數(shù)”。

addTwoIntsmathFunction 有同樣的類型,所以這個(gè)賦值過程在 Swift 類型檢查(type-check)中是允許的。

現(xiàn)在,你可以用 mathFunction 來調(diào)用被賦值的函數(shù)了:

print("Result: \(mathFunction(2, 3))")
// Prints "Result: 5"

有相同匹配類型的不同函數(shù)可以被賦值給同一個(gè)變量,就像非函數(shù)類型的變量一樣:

mathFunction = multiplyTwoInts
print("Result: \(mathFunction(2, 3))")
// Prints "Result: 6"

就像其他類型一樣,當(dāng)賦值一個(gè)函數(shù)給常量或變量時(shí),你可以讓 Swift 來推斷其函數(shù)類型:

let anotherMathFunction = addTwoInts
// anotherMathFunction 被推斷為 (Int, Int) -> Int 類型

函數(shù)類型作為參數(shù)類型

你可以用 (Int, Int) -> Int 這樣的函數(shù)類型作為另一個(gè)函數(shù)的參數(shù)類型。這樣你可以將函數(shù)的一部分實(shí)現(xiàn)留給函數(shù)的調(diào)用者來提供。

下面是另一個(gè)例子,正如上面的函數(shù)一樣,同樣是輸出某種數(shù)學(xué)運(yùn)算結(jié)果:

func printMathResult(_ mathFunction: (Int, Int) -> Int, _ a: Int, _ b: Int) {
    print("Result: \(mathFunction(a, b))")
}
printMathResult(addTwoInts, 3, 5)
// 打印 "Result: 8"

這個(gè)例子定義了 printMathResult(_:_:_:) 函數(shù),它有三個(gè)參數(shù):第一個(gè)參數(shù)叫 mathFunction,類型是 (Int, Int) -> Int,你可以傳入任何這種類型的函數(shù);第二個(gè)和第三個(gè)參數(shù)叫 ab,它們的類型都是 Int,這兩個(gè)值作為已給出的函數(shù)的輸入值。

當(dāng) printMathResult(_:_:_:) 被調(diào)用時(shí),它被傳入 addTwoInts 函數(shù)和整數(shù) 35。它用傳入 35 調(diào)用 addTwoInts,并輸出結(jié)果:8。

printMathResult(_:_:_:) 函數(shù)的作用就是輸出另一個(gè)適當(dāng)類型的數(shù)學(xué)函數(shù)的調(diào)用結(jié)果。它不關(guān)心傳入函數(shù)是如何實(shí)現(xiàn)的,只關(guān)心傳入的函數(shù)是不是一個(gè)正確的類型。這使得 printMathResult(_:_:_:) 能以一種類型安全(type-safe)的方式將一部分功能轉(zhuǎn)給調(diào)用者實(shí)現(xiàn)。

函數(shù)類型作為返回類型

你可以用函數(shù)類型作為另一個(gè)函數(shù)的返回類型。你需要做的是在返回箭頭(->)后寫一個(gè)完整的函數(shù)類型。

下面的這個(gè)例子中定義了兩個(gè)簡單函數(shù),分別是 stepForward(_:)stepBackward(_:)。stepForward(_:)函數(shù)返回一個(gè)比輸入值大 1 的值。stepBackward(_:) 函數(shù)返回一個(gè)比輸入值小 1 的值。這兩個(gè)函數(shù)的類型都是 (Int) -> Int

func stepForward(_ input: Int) -> Int {
    return input + 1
}
func stepBackward(_ input: Int) -> Int {
    return input - 1
}

如下名為 chooseStepFunction(backward:) 的函數(shù),它的返回類型是 (Int) -> Int 類型的函數(shù)。chooseStepFunction(backward:) 根據(jù)布爾值 backwards 來返回 stepForward(_:) 函數(shù)或 stepBackward(_:) 函數(shù):

func chooseStepFunction(backward: Bool) -> (Int) -> Int {
    return backward ? stepBackward : stepForward
}

你現(xiàn)在可以用 chooseStepFunction(backward:) 來獲得兩個(gè)函數(shù)其中的一個(gè):

var currentValue = 3
let moveNearerToZero = chooseStepFunction(backward: currentValue > 0)
// moveNearerToZero 現(xiàn)在指向 stepBackward() 函數(shù)。

上面這個(gè)例子中計(jì)算出從 currentValue 逐漸接近到0是需要向正數(shù)走還是向負(fù)數(shù)走。currentValue 的初始值是 3,這意味著 currentValue > 0 為真(true),這將使得 chooseStepFunction(_:) 返回 stepBackward(_:) 函數(shù)。一個(gè)指向返回的函數(shù)的引用保存在了 moveNearerToZero 常量中。

現(xiàn)在,moveNearerToZero指向了正確的函數(shù),它可以被用來數(shù)到零:

print("Counting to zero:")
// Counting to zero:
while currentValue != 0 {
    print("\(currentValue)... ")
    currentValue = moveNearerToZero(currentValue)
}
print("zero!")
// 3...
// 2...
// 1...
// zero!

嵌套函數(shù)

到目前為止本章中你所見到的所有函數(shù)都叫全局函數(shù)(global functions),它們定義在全局域中。你也可以把函數(shù)定義在別的函數(shù)體中,稱作 嵌套函數(shù)(nested functions)

默認(rèn)情況下,嵌套函數(shù)是對外界不可見的,但是可以被它們的外圍函數(shù)(enclosing function)調(diào)用。一個(gè)外圍函數(shù)也可以返回它的某一個(gè)嵌套函數(shù),使得這個(gè)函數(shù)可以在其他域中被使用。

你可以用返回嵌套函數(shù)的方式重寫 chooseStepFunction(backward:) 函數(shù):

func chooseStepFunction(backward: Bool) -> (Int) -> Int {
    func stepForward(input: Int) -> Int { return input + 1 }
    func stepBackward(input: Int) -> Int { return input - 1 }
    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)... ")
    currentValue = moveNearerToZero(currentValue)
}
print("zero!")
// -4...
// -3...
// -2...
// -1...
// zero!
? 控制流 閉包 ?
?