鍍金池/ 教程/ iOS/ 函數(shù)(Functions)
特性(Attributes)
Access Control 權(quán)限控制的黑與白
基本運(yùn)算符(Basic Operators)
基礎(chǔ)部分(The Basics)
閉包(Closures)
擴(kuò)展
泛型參數(shù)(Generic Parameters and Arguments)
訪問控制和 protected
語句(Statements)
模式(Patterns)
WWDC 里面的那個(gè)“大炮打氣球”
關(guān)于語言參考(About the Language Reference)
語法總結(jié)(Summary of the Grammar)
嵌套類型
類型(Types)
Swift 初見(A Swift Tour)
泛型
枚舉(Enumerations)
高級(jí)運(yùn)算符
繼承
析構(gòu)過程
關(guān)于 Swift(About Swift)
訪問控制
類和結(jié)構(gòu)體
內(nèi)存安全
Swift 與 C 語言指針友好合作
協(xié)議
屬性(Properties)
可選類型完美解決占位問題
錯(cuò)誤處理
字符串和字符(Strings and Characters)
聲明(Declarations)
自動(dòng)引用計(jì)數(shù)
Swift 里的值類型與引用類型
表達(dá)式(Expressions)
Swift 文檔修訂歷史
造個(gè)類型不是夢(mèng)-白話 Swift 類型創(chuàng)建
歡迎使用 Swift
詞法結(jié)構(gòu)(Lexical Structure)
集合類型(Collection Types)
下標(biāo)
方法(Methods)
可選鏈?zhǔn)秸{(diào)用
版本兼容性
類型轉(zhuǎn)換
構(gòu)造過程
The Swift Programming Language 中文版
函數(shù)(Functions)
Swift 教程
控制流(Control Flow)

函數(shù)(Functions)


1.0 翻譯:honghaoz 校對(duì):LunaticM

2.0 翻譯+校對(duì):dreamkidd

2.1 翻譯:DianQK 定稿:shanks

2.2 翻譯+校對(duì):SketchK 2016-05-12

3.0 翻譯: crayygy 2016-09-12 校對(duì): shanks 2016-09-27

3.0.1,shanks,2016-11-12

4.0 校對(duì):kemchenj 2017-09-21

4.1 翻譯+校對(duì):mylittleswift

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

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

Swift 統(tǒng)一的函數(shù)語法非常的靈活,可以用來表示任何函數(shù),包括從最簡(jiǎn)單的沒有參數(shù)名字的 C 風(fēng)格函數(shù),到復(fù)雜的帶局部和外部參數(shù)名的 Objective-C 風(fēng)格函數(shù)。參數(shù)可以提供默認(rèn)值,以簡(jiǎ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)做任何其他普通變量類型一樣處理,這樣就可以更簡(jiǎn)單地把函數(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è)右尖括號(hào))后跟返回類型的名稱的方式來表示。

該定義描述了函數(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í),在圓括號(hào)中傳給它一個(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í),把對(duì) 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é)果。

為了簡(jiǎn)化這個(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ù)的簡(jiǎn)單函數(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ù)名后還是需要一對(duì)圓括號(hào)。當(dāng)被調(diào)用時(shí),也需要在函數(shù)名后寫一對(duì)圓括號(hào)。

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

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

下面這個(gè)函數(shù)用一個(gè)人名和是否已經(jīng)打過招呼作為輸入,并返回對(duì)這個(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!"

你可以通過在括號(hào)內(nèi)使用逗號(hào)分隔來傳遞一個(gè) String 參數(shù)值和一個(gè)標(biāo)識(shí)為 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ù)會(huì)返回一個(gè)特殊的 Void 值。它其實(shí)是一個(gè)空的元組,沒有任何元素,可以寫成 ()

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

func printAndCount(string: String) -> Int {
    print(string)
    return string.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í),消息依然會(huì)由第一個(gè)函數(shù)輸出,但是返回值不會(huì)被用到。

注意

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

多重返回值函數(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ù)會(huì)遍歷數(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è)元組都“沒有值”,你可以使用可選的 元組返回類型反映整個(gè)元組可以是 nil 的事實(shí)。你可以通過在元組類型的右括號(hào)后放置一個(gè)問號(hào)來定義一個(gè)可選元組,例如 (Int, Int)?(String, Int, Bool)?

注意

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

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

為了安全地處理這個(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)簽寫在對(duì)應(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 會(huì)值為 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ì)算一組任意長(zhǎng)度數(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ù)值將會(huì)導(dǎo)致編譯錯(cuò)誤。這意味著你不能錯(cuò)誤地更改參數(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)化,請(qǐng)查看輸入輸出參數(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ù)簡(jiǎn)單地交換 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ù)對(duì)函數(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è)簡(jiǎn)單的數(shù)學(xué)函數(shù):addTwoIntsmultiplyTwoInts。這兩個(gè)函數(shù)都接受兩個(gè) Int 值, 返回一個(gè) Int 值。

這兩個(gè)函數(shù)的類型是 (Int, Int) -> Int,可以解讀為:

“這個(gè)函數(shù)類型有兩個(gè) Int 型的參數(shù)并返回一個(gè) Int 型的值”。

下面是另一個(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è)簡(jiǎn)單函數(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ù)是對(duì)外界不可見的,但是可以被它們的外圍函數(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!