鍍金池/ 教程/ iOS/ 函數(shù)(Functions)
方法 - Methods
關(guān)于 Swift
下標(biāo)腳本(Subscripts)
類和結(jié)構(gòu)體
類型轉(zhuǎn)換(Type Casting)
控制流
析構(gòu)過程(Deinitialization)
集合類型 (Collection Types)
構(gòu)造過程(Initialization)
Optional Chaining
枚舉(Enumerations)
自動(dòng)引用計(jì)數(shù)
繼承(Inheritance)
擴(kuò)展(Extensions)
泛型
字符串和字符(Strings and Characters)
函數(shù)(Functions)
高級(jí)運(yùn)算符
訪問控制
基本運(yùn)算符
嵌套類型
閉包(Closures)
協(xié)議
屬性 (Properties)

函數(shù)(Functions)

函數(shù)是用來完成特定任務(wù)的獨(dú)立的代碼塊。你給一個(gè)函數(shù)起一個(gè)合適的名字,用來標(biāo)識(shí)函數(shù)做什么,并且當(dāng)函數(shù)需要執(zhí)行的時(shí)候,這個(gè)名字會(huì)被“調(diào)用”。

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ù)都有一種類型,包括函數(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)用(Defining and Calling Functions)

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

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

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

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

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

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

    println(sayHello("Anna"))
    // prints "Hello, Anna!"
    println(sayHello("Brian"))
    // prints "Hello, Brian!"

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

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

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

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

    func sayHelloAgain(personName: String) -> String {
        return "Hello again, " + personName + "!"
    }
    println(sayHelloAgain("Anna"))
    // prints "Hello again, Anna!"

函數(shù)參數(shù)與返回值(Function Parameters and Return Values)

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

多重輸入?yún)?shù)(Multiple Input Parameters)

函數(shù)可以有多個(gè)輸入?yún)?shù),寫在圓括號(hào)中,用逗號(hào)分隔。

下面這個(gè)函數(shù)用一個(gè)半開區(qū)間的開始點(diǎn)和結(jié)束點(diǎn),計(jì)算出這個(gè)范圍內(nèi)包含多少數(shù)字:

    func halfOpenRangeLength(start: Int, end: Int) -> Int {
        return end - start
    }
    println(halfOpenRangeLength(1, 10))
    // prints "9"

無參函數(shù)(Functions Without Parameters)

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

    func sayHelloWorld() -> String {
        return "hello, world"
    }
    println(sayHelloWorld())
    // prints "hello, world"

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

無返回值函數(shù)(Functions Without Return Values)

函數(shù)可以沒有返回值。下面是 sayHello 函數(shù)的另一個(gè)版本,叫 waveGoodbye,這個(gè)函數(shù)直接輸出 String 值,而不是返回它:

    func sayGoodbye(personName: String) {
        println("Goodbye, \(personName)!")
    }
    sayGoodbye("Dave")
    // prints "Goodbye, Dave!"

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

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

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

    func printAndCount(stringToPrint: String) -> Int {
        println(stringToPrint)
        return countElements(stringToPrint)
    }
    func printWithoutCounting(stringToPrint: String) {
        printAndCount(stringToPrint)
    }
    printAndCount("hello, world")
    // prints "hello, world" and returns a value of 12
    printWithoutCounting("hello, world")
    // prints "hello, world" but does not return a value

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

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

多重返回值函數(shù)(Functions with Multiple Return Values)

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

下面的這個(gè)例子中,count 函數(shù)用來計(jì)算一個(gè)字符串中元音,輔音和其他字母的個(gè)數(shù)(基于美式英語的標(biāo)準(zhǔn))。

    func count(string: String) -> (vowels: Int, consonants: Int, others: Int) {
        var vowels = 0, consonants = 0, others = 0
        for character in string {
            switch String(character).lowercaseString {
            case "a", "e", "i", "o", "u":
                ++vowels
            case "b", "c", "d", "f", "g", "h", "j", "k", "l", "m",
                "n", "p", "q", "r", "s", "t", "v", "w", "x", "y", "z":
                ++consonants
            default:
                ++others
            }
        }
        return (vowels, consonants, others)
    }

你可以用 count 函數(shù)來處理任何一個(gè)字符串,返回的值將是一個(gè)包含三個(gè) Int 型值的元組(tuple):

    let total = count("some arbitrary string!")
    println("\(total.vowels) vowels and \(total.consonants) consonants")
    // prints "6 vowels and 13 consonants"

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

函數(shù)參數(shù)名稱(Function Parameter Names)

以上所有的函數(shù)都給它們的參數(shù)定義了參數(shù)名(parameter name)

    func someFunction(parameterName: Int) {
        // function body goes here, and can use parameterName
        // to refer to the argument value for that parameter
    }

但是,這些參數(shù)名僅在函數(shù)體中使用,不能在函數(shù)調(diào)用時(shí)使用。這種類型的參數(shù)名被稱作局部參數(shù)名(local parameter name),因?yàn)樗鼈冎荒茉诤瘮?shù)體中使用。

外部參數(shù)名(External Parameter Names)

有時(shí)候,調(diào)用函數(shù)時(shí),給每個(gè)參數(shù)命名是非常有用的,因?yàn)檫@些參數(shù)名可以指出各個(gè)實(shí)參的用途是什么。

如果你希望函數(shù)的使用者在調(diào)用函數(shù)時(shí)提供參數(shù)名字,那就需要給每個(gè)參數(shù)除了局部參數(shù)名外再定義一個(gè)外部參數(shù)名。外部參數(shù)名寫在局部參數(shù)名之前,用空格分隔。

    func someFunction(externalParameterName localParameterName: Int) {
        // function body goes here, and can use localParameterName
        // to refer to the argument value for that parameter
    }

注意: 如果你提供了外部參數(shù)名,那么函數(shù)在被調(diào)用時(shí),必須使用外部參數(shù)名。

以下是個(gè)例子,這個(gè)函數(shù)使用一個(gè)結(jié)合者(joiner)把兩個(gè)字符串聯(lián)在一起:

    func join(s1: String, s2: String, joiner: String) -> String {
        return s1 + joiner + s2
    }

當(dāng)你調(diào)用這個(gè)函數(shù)時(shí),這三個(gè)字符串的用途是不清楚的:

    join("hello", "world", ", ")
    // returns "hello, world"

為了讓這些字符串的用途更為明顯,我們?yōu)?join 函數(shù)添加外部參數(shù)名:

    func join(string s1: String, toString s2: String, withJoiner joiner: String) -> String {
        return s1 + joiner + s2
    }

在這個(gè)版本的 join 函數(shù)中,第一個(gè)參數(shù)有一個(gè)叫 string 的外部參數(shù)名和 s1 的局部參數(shù)名,第二個(gè)參數(shù)有一個(gè)叫 toString 的外部參數(shù)名和 s2 的局部參數(shù)名,第三個(gè)參數(shù)有一個(gè)叫 withJoiner 的外部參數(shù)名和 joiner 的局部參數(shù)名。

現(xiàn)在,你可以使用這些外部參數(shù)名以一種清晰地方式來調(diào)用函數(shù)了:

    join(string: "hello", toString: "world", withJoiner: ", ")
    // returns "hello, world"

使用外部參數(shù)名讓第二個(gè)版本的 join 函數(shù)的調(diào)用更為有表現(xiàn)力,更為通順,同時(shí)還保持了函數(shù)體是可讀的和有明確意圖的。

注意: 當(dāng)其他人在第一次讀你的代碼,函數(shù)參數(shù)的意圖顯得不明顯時(shí),考慮使用外部參數(shù)名。如果函數(shù)參數(shù)名的意圖是很明顯的,那就不需要定義外部參數(shù)名了。

簡(jiǎn)寫外部參數(shù)名(Shorthand External Parameter Names)

如果你需要提供外部參數(shù)名,但是局部參數(shù)名已經(jīng)定義好了,那么你不需要寫兩次參數(shù)名。相反,只寫一次參數(shù)名,并用井號(hào)(#)作為前綴就可以了。這告訴 Swift 使用這個(gè)參數(shù)名作為局部和外部參數(shù)名。

下面這個(gè)例子定義了一個(gè)叫 containsCharacter 的函數(shù),使用井號(hào)(#)的方式定義了外部參數(shù)名:

    func containsCharacter(#string: String, #characterToFind: Character) -> Bool {
        for character in string {
            if character == characterToFind {
                return true
            }
        }
        return false
    }

這樣定義參數(shù)名,使得函數(shù)體更為可讀,清晰,同時(shí)也可以以一個(gè)不含糊的方式被調(diào)用:

    let containsAVee = containsCharacter(string: "aardvark", characterToFind: "v")
    // containsAVee equals true, because "aardvark" contains a "v”

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

你可以在函數(shù)體中為每個(gè)參數(shù)定義默認(rèn)值。當(dāng)默認(rèn)值被定義后,調(diào)用這個(gè)函數(shù)時(shí)可以忽略這個(gè)參數(shù)。

注意: 將帶有默認(rèn)值的參數(shù)放在函數(shù)參數(shù)列表的最后。這樣可以保證在函數(shù)調(diào)用時(shí),非默認(rèn)參數(shù)的順序是一致的,同時(shí)使得相同的函數(shù)在不同情況下調(diào)用時(shí)顯得更為清晰。

以下是另一個(gè)版本的join函數(shù),其中joiner有了默認(rèn)參數(shù)值:

    func join(string s1: String, toString s2: String, withJoiner joiner: String = " ") -> String {
        return s1 + joiner + s2
    }

像第一個(gè)版本的 join 函數(shù)一樣,如果 joiner 被賦值時(shí),函數(shù)將使用這個(gè)字符串值來連接兩個(gè)字符串:

    join(string: "hello", toString: "world", withJoiner: "-")
    // returns "hello-world"

當(dāng)這個(gè)函數(shù)被調(diào)用時(shí),如果 joiner 的值沒有被指定,函數(shù)會(huì)使用默認(rèn)值(" "):

    join(string: "hello", toString:"world")
    // returns "hello world"

默認(rèn)值參數(shù)的外部參數(shù)名(External Names for Parameters with Default Values)

在大多數(shù)情況下,給帶默認(rèn)值的參數(shù)起一個(gè)外部參數(shù)名是很有用的。這樣可以保證當(dāng)函數(shù)被調(diào)用且?guī)J(rèn)值的參數(shù)被提供值時(shí),實(shí)參的意圖是明顯的。

為了使定義外部參數(shù)名更加簡(jiǎn)單,當(dāng)你未給帶默認(rèn)值的參數(shù)提供外部參數(shù)名時(shí),Swift 會(huì)自動(dòng)提供外部名字。此時(shí)外部參數(shù)名與局部名字是一樣的,就像你已經(jīng)在局部參數(shù)名前寫了井號(hào)(#)一樣。

下面是 join 函數(shù)的另一個(gè)版本,這個(gè)版本中并沒有為它的參數(shù)提供外部參數(shù)名,但是 joiner 參數(shù)依然有外部參數(shù)名:

    func join(s1: String, s2: String, joiner: String = " ") -> String {
        return s1 + joiner + s2
    }

在這個(gè)例子中,Swift 自動(dòng)為 joiner 提供了外部參數(shù)名。因此,當(dāng)函數(shù)調(diào)用時(shí),外部參數(shù)名必須使用,這樣使得參數(shù)的用途變得清晰。

    join("hello", "world", joiner: "-")
    // returns "hello-world"

注意: 你可以使用下劃線(_)作為默認(rèn)值參數(shù)的外部參數(shù)名,這樣可以在調(diào)用時(shí)不用提供外部參數(shù)名。但是給帶默認(rèn)值的參數(shù)命名總是更加合適的。

可變參數(shù)(Variadic Parameters)

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

傳入可變參數(shù)的值在函數(shù)體內(nèi)當(dāng)做這個(gè)類型的一個(gè)數(shù)組。例如,一個(gè)叫做 numbersDouble... 型可變參數(shù),在函數(shù)體內(nèi)可以當(dāng)做一個(gè)叫 numbersDouble[] 型的數(shù)組常量。

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

    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)
    // returns 3.0, which is the arithmetic mean of these five numbers
    arithmeticMean(3, 8, 19)
    // returns 10.0, which is the arithmetic mean of these three numbers

注意: 一個(gè)函數(shù)至多能有一個(gè)可變參數(shù),而且它必須是參數(shù)表中最后的一個(gè)。這樣做是為了避免函數(shù)調(diào)用時(shí)出現(xiàn)歧義。

如果函數(shù)有一個(gè)或多個(gè)帶默認(rèn)值的參數(shù),而且還有一個(gè)可變參數(shù),那么把可變參數(shù)放在參數(shù)表的最后。

常量參數(shù)和變量參數(shù)(Constant and Variable Parameters)

函數(shù)參數(shù)默認(rèn)是常量。試圖在函數(shù)體中更改參數(shù)值將會(huì)導(dǎo)致編譯錯(cuò)誤。這意味著你不能錯(cuò)誤地更改參數(shù)值。

但是,有時(shí)候,如果函數(shù)中有傳入?yún)?shù)的變量值副本將是很有用的。你可以通過指定一個(gè)或多個(gè)參數(shù)為變量參數(shù),從而避免自己在函數(shù)中定義新的變量。變量參數(shù)不是常量,你可以在函數(shù)中把它當(dāng)做新的可修改副本來使用。

通過在參數(shù)名前加關(guān)鍵字 var 來定義變量參數(shù):

    func alignRight(var string: String, count: Int, pad: Character) -> String {
        let amountToPad = count - countElements(string)
        if amountToPad < 1 {
            return string
        }
        let padString = String(pad)
        for _ in 1...amountToPad {
            string = padString + string
        }
        return string
    }
    let originalString = "hello"
    let paddedString = alignRight(originalString, 10, "-")
    // paddedString is equal to "-----hello"
    // originalString is still equal to "hello"

這個(gè)例子中定義了一個(gè)新的叫做 alignRight 的函數(shù),用來右對(duì)齊輸入的字符串到一個(gè)長(zhǎng)的輸出字符串中。左側(cè)空余的地方用指定的填充字符填充。這個(gè)例子中,字符串"hello"被轉(zhuǎn)換成了"-----hello"。

alignRight 函數(shù)將參數(shù) string 定義為變量參數(shù)。這意味著 string 現(xiàn)在可以作為一個(gè)局部變量,用傳入的字符串值初始化,并且可以在函數(shù)體中進(jìn)行操作。

該函數(shù)首先計(jì)算出多少個(gè)字符需要被添加到 string 的左邊,以右對(duì)齊到總的字符串中。這個(gè)值存在局部常量 amountToPad 中。這個(gè)函數(shù)然后將 amountToPad 多的填充(pad)字符填充到 string 左邊,并返回結(jié)果。它使用了 string 這個(gè)變量參數(shù)來進(jìn)行所有字符串操作。

注意: 對(duì)變量參數(shù)所進(jìn)行的修改在函數(shù)調(diào)用結(jié)束后便消失了,并且對(duì)于函數(shù)體外是不可見的。變量參數(shù)僅僅存在于函數(shù)調(diào)用的生命周期中。

輸入輸出參數(shù)(In-Out Parameters)

變量參數(shù),正如上面所述,僅僅能在函數(shù)體內(nèi)被更改。如果你想要一個(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ù),替換原來的值。

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

注意: 輸入輸出參數(shù)不能有默認(rèn)值,而且可變參數(shù)不能用 inout 標(biāo)記。如果你用 inout 標(biāo)記一個(gè)參數(shù),這個(gè)參數(shù)不能被 var 或者 let 標(biāo)記。

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

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

這個(gè) 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)
    println("someInt is now \(someInt), and anotherInt is now \(anotherInt)")
    // prints "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ù)類型(Function Types)

每個(gè)函數(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() {
        println("hello, world")
    }

這個(gè)函數(shù)的類型是:() -> (),或者叫“沒有參數(shù),并返回 Void 類型的函數(shù)”。沒有指定返回類型的函數(shù)總返回 Void。在Swift中,Void 與空的元組是一樣的。

使用函數(shù)類型(Using Function Types)

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

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

這個(gè)可以讀作:

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

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

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

    println("Result: \(mathFunction(2, 3))")
    // prints "Result: 5"

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

    mathFunction = multiplyTwoInts
    println("Result: \(mathFunction(2, 3))")
    // prints "Result: 6"

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

    let anotherMathFunction = addTwoInts
    // anotherMathFunction is inferred to be of type (Int, Int) -> Int

函數(shù)類型作為參數(shù)類型(Function Types as Parameter Types)

你可以用(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) {
        println("Result: \(mathFunction(a, b))")
    }
    printMathResult(addTwoInts, 3, 5)
    // prints "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è)合適類型的數(shù)學(xué)函數(shù)的調(diào)用結(jié)果。它不關(guān)心傳入函數(shù)是如何實(shí)現(xiàn)的,它只關(guān)心這個(gè)傳入的函數(shù)類型是正確的。這使得 printMathResult 可以以一種類型安全(type-safe)的方式來保證傳入函數(shù)的調(diào)用是正確的。

函數(shù)類型作為返回類型(Function Type as Return Types)

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

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

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

下面這個(gè)叫做 chooseStepFunction 的函數(shù),它的返回類型是 (Int) -> Int 的函數(shù)。chooseStepFunction 根據(jù)布爾值 backwards 來返回 stepForward 函數(shù)或 stepBackward 函數(shù):

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

你現(xiàn)在可以用 chooseStepFunction 來獲得一個(gè)函數(shù),不管是那個(gè)方向:

    var currentValue = 3
    let moveNearerToZero = chooseStepFunction(currentValue > 0)
    // moveNearerToZero now refers to the stepBackward() function

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

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

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

嵌套函數(shù)(Nested Functions)

這章中你所見到的所有函數(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 函數(shù):

    func chooseStepFunction(backwards: Bool) -> (Int) -> Int {
        func stepForward(input: Int) -> Int { return input + 1 }
        func stepBackward(input: Int) -> Int { return input - 1 }
        return backwards ? stepBackward : stepForward
    }
    var currentValue = -4
    let moveNearerToZero = chooseStepFunction(currentValue < 0)
    // moveNearerToZero now refers to the nested stepForward() function
    while currentValue != 0 {
        println("\(currentValue)... ")
        currentValue = moveNearerToZero(currentValue)
    }
    println("zero!")
    // -4...
    // -3...
    // -2...
    // -1...
    // zero!
上一篇:訪問控制下一篇:繼承(Inheritance)