Swift - 函數(shù)

函數(shù)


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

Swift 統(tǒng)一的函數(shù)語(yǔ)法非常的靈活,可以用來(lái)表示任何函數(shù),包括從最簡(jiǎn)單的沒(méi)有參數(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ù),也就是說(shuō),一旦函數(shù)執(zhí)行結(jié)束,傳入的參數(shù)值將被修改。

在 Swift 中,每個(gè)函數(shù)都有一個(gè)由函數(shù)的參數(shù)值類型和返回值類型組成的類型。你可以把函數(shù)類型當(dāng)做任何其他普通變量類型一樣處理,這樣就可以更簡(jiǎn)單地把函數(shù)當(dāng)做別的函數(shù)的參數(shù),也可以從其他函數(shù)中返回函數(shù)。函數(shù)的定義可以寫(xiě)在其他函數(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ù)名,用來(lái)描述函數(shù)執(zhí)行的任務(wù)。要使用一個(gè)函數(shù)時(shí),用函數(shù)名來(lái)“調(diào)用”這個(gè)函數(shù),并傳給它匹配的輸入值(稱作實(shí)參)。函數(shù)的實(shí)參必須與函數(shù)參數(shù)表里參數(shù)的順序一致。

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

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

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

該定義描述了函數(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)用中,用來(lái)輸出這個(gè)函數(shù)的返回值。

注意

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

greet(person:) 的函數(shù)體中,先定義了一個(gè)新的名為 greetingString 常量,同時(shí),把對(duì) personName 的問(wèn)候消息賦值給了 greeting 。然后用 return 關(guān)鍵字把這個(gè)問(wèn)候返回出去。一旦 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ù)的定義,可以將問(wèn)候消息的創(chuàng)建和返回寫(xiě)成一句:

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ù)。

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

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

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

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

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

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

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

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!”

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

無(wú)返回值函數(shù)

函數(shù)可以沒(méi)有返回值。下面是 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ù)的定義中沒(méi)有返回箭頭(->)和返回類型。

注意

嚴(yán)格地說(shuō),即使沒(méi)有明確定義返回值,該 greet(Person:) 函數(shù)仍然返回一個(gè)值。沒(méi)有明確定義返回類型的函數(shù)的返回一個(gè) Void 類型特殊值,該值為一個(gè)空元組,寫(xiě)成 ()。

調(diào)用函數(shù)時(shí),可以忽略該函數(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”,但是沒(méi)有返回任何值

第一個(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ù)定義底部沒(méi)有返回任何值,將導(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í)可以通過(guò)名字訪問(wèn)它們。

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

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

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è)元組都“沒(méi)有值”,你可以使用可選的 元組返回類型反映整個(gè)元組可以是 nil 的事實(shí)。你可以通過(guò)在元組類型的右括號(hào)后放置一個(gè)問(wèn)號(hào)來(lái)定義一個(gè)可選元組,例如 (Int, Int)?(String, Int, Bool)?

注意

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

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

為了安全地處理這個(gè)“空數(shù)組”問(wèn)題,將 minMax(array:) 函數(shù)改寫(xiě)為使用可選元組返回類型,并且當(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)
}

你可以使用可選綁定來(lái)檢查 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ù)

如果一個(gè)函數(shù)的整個(gè)函數(shù)體是一個(gè)單行表達(dá)式,這個(gè)函數(shù)可以隱式地返回這個(gè)表達(dá)式。舉個(gè)例子,以下的函數(shù)有著同樣的作用:

func greeting(for person: String) -> String {
    "Hello, " + person + "!"
}
print(greeting(for: "Dave"))
// 打印 "Hello, Dave!"

func anotherGreeting(for person: String) -> String {
    return "Hello, " + person + "!"
}
print(anotherGreeting(for: "Dave"))
// 打印 "Hello, Dave!"

greeting(for:) 函數(shù)的完整定義是打招呼內(nèi)容的返回,這就意味著它能使用隱式返回這樣更簡(jiǎn)短的形式。anothergreeting(for:) 函數(shù)返回同樣的內(nèi)容,卻因?yàn)?return 關(guān)鍵字顯得函數(shù)更長(zhǎng)。任何一個(gè)可以被寫(xiě)成一行 return 語(yǔ)句的函數(shù)都可以忽略 return。

正如你將會(huì)在 簡(jiǎn)略的 Getter 聲明 里看到的, 一個(gè)屬性的 getter 也可以使用隱式返回的形式。

函數(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)簽寫(xiě)在對(duì)應(yīng)的參數(shù)前面。參數(shù)名稱在函數(shù)的實(shí)現(xiàn)中使用。默認(rèn)情況下,函數(shù)參數(shù)使用參數(shù)名稱來(lái)作為它們的參數(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ú)一無(wú)二的名字。雖然多個(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),并且返回一句問(wèn)候:

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á)力,更類似自然語(yǔ)言,并且仍保持了函數(shù)內(nèi)部的可讀性以及清晰的意圖。

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

如果你不希望為某個(gè)參數(shù)添加一個(gè)標(biāo)簽,可以使用一個(gè)下劃線(_)來(lái)代替一個(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)簽來(lái)標(biāo)記這個(gè)參數(shù)。

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

你可以在函數(shù)體中通過(guò)給參數(shù)賦值來(lái)為任意一個(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ù)列表的最前。一般來(lái)說(shuō),沒(méi)有默認(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ù)來(lái)指定函數(shù)參數(shù)可以被傳入不確定數(shù)量的輸入值。通過(guò)在變量類型名后面加入(...)的方式來(lái)定義可變參數(shù)。

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

下面的這個(gè)函數(shù)用來(lái)計(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ù)后的第一個(gè)行參前必須加上實(shí)參標(biāo)簽。實(shí)參標(biāo)簽用于區(qū)分實(shí)參是傳遞給可變參數(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ù),替換原來(lái)的值。想獲取更多的關(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 型的變量來(lái)調(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ù)并沒(méi)有定義任何返回值,但仍然修改了 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è)沒(méi)有參數(shù),也沒(méi)有返回值的函數(shù):

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

這個(gè)函數(shù)的類型是:() -> Void,或者叫“沒(méi)有參數(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è)賦值過(guò)程在 Swift 類型檢查(type-check)中是允許的。

現(xiàn)在,你可以用 mathFunction 來(lái)調(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 來(lái)推斷其函數(shù)類型:

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

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

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

下面是另一個(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ù)的返回類型。你需要做的是在返回箭頭(->)后寫(xiě)一個(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 來(lái)返回 stepForward(_:) 函數(shù)或 stepBackward(_:) 函數(shù):

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

你現(xiàn)在可以用 chooseStepFunction(backward:) 來(lái)獲得兩個(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ù),它可以被用來(lái)數(shù)到零:

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

嵌套函數(shù)

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

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

你可以用返回嵌套函數(shù)的方式重寫(xiě) 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!

繼續(xù)閱讀 Swift - 閉包

最后編輯于
?著作權(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)書(shū)系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

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

  • 函數(shù)是用來(lái)完成特定任務(wù)的獨(dú)立的代碼塊。給一個(gè)函數(shù)起一個(gè)合適的名字,用來(lái)標(biāo)識(shí)函數(shù)做什么,并且當(dāng)函數(shù)需要執(zhí)行的時(shí)候,這...
    窮人家的孩紙閱讀 867評(píng)論 2 1
  • 函數(shù)是一段完成特定任務(wù)的獨(dú)立代碼片段。你可以通過(guò)給函數(shù)命名來(lái)標(biāo)識(shí)某個(gè)函數(shù)的功能,這個(gè)名字可以被用來(lái)在需要的時(shí)候"調(diào)...
    CDLOG閱讀 250評(píng)論 0 0
  • 函數(shù)定義與調(diào)用 無(wú)參數(shù),無(wú)返回值(沒(méi)有定義返回類型的函數(shù)會(huì)返回Void,它其實(shí)是一個(gè)空的元組,可以寫(xiě)成()) 有參...
    皆為序幕_閱讀 603評(píng)論 0 1
  • 函數(shù)的定義與調(diào)用 無(wú)參數(shù)函數(shù),有返回值(返回值類型String) 多參數(shù)函數(shù),有返回值(返回值類型String) ...
    小驢拉磨閱讀 205評(píng)論 0 0
  • 函數(shù) 函數(shù)是一段完成特定任務(wù)的獨(dú)立代碼片段。你可以通過(guò)給函數(shù)命名來(lái)標(biāo)識(shí)某個(gè)函數(shù)的功能,這個(gè)名字可以被用來(lái)在需要的時(shí)...
    趙哥窟閱讀 381評(píng)論 0 0

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