函數(shù)是用來(lái)完成特定任務(wù)的獨(dú)立的代碼塊殊轴。給一個(gè)函數(shù)起一個(gè)合適的名字,用來(lái)標(biāo)識(shí)函數(shù)做什么袒炉,并且當(dāng)函數(shù)需要執(zhí)行的時(shí)候旁理,這個(gè)名字會(huì)被用于“調(diào)用”函數(shù)。
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ù)都有一種類型,包括函數(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é)束的輸出(稱為返回類型,return type)惧所。
每個(gè)函數(shù)有個(gè)函數(shù)名骤坐,用來(lái)描述函數(shù)執(zhí)行的任務(wù)。要使用一個(gè)函數(shù)時(shí)下愈,用函數(shù)名“調(diào)用”纽绍,并傳給它匹配的輸入值(稱作實(shí)參,arguments)势似。一個(gè)函數(shù)的實(shí)參必須與函數(shù)參數(shù)表里參數(shù)的順序一致拌夏。
在下面例子中的函數(shù)叫做"sayHello(_:)"
,之所以叫這個(gè)名字,是因?yàn)檫@個(gè)函數(shù)用一個(gè)人的名字當(dāng)做輸入履因,并返回給這個(gè)人的問(wèn)候語(yǔ)障簿。為了完成這個(gè)任務(wù),定義一個(gè)輸入?yún)?shù)-一個(gè)叫做 personName
的 String
值栅迄,和一個(gè)包含給這個(gè)人問(wèn)候語(yǔ)的 String
類型的返回值:
func sayHello(personName: String) -> String {
let greeting = "Hello, " + personName + "!"
return greeting
}
所有的這些信息匯總起來(lái)成為函數(shù)的定義站故,并以 func
作為前綴。指定函數(shù)返回類型時(shí),用返回箭頭 ->
(一個(gè)連字符后跟一個(gè)右尖括號(hào))后跟返回類型的名稱的方式來(lái)表示西篓。
該定義描述了函數(shù)做什么愈腾,它期望接收什么和執(zhí)行結(jié)束時(shí)它返回的結(jié)果是什么類型。這樣的定義使得函數(shù)可以在別的地方以一種清晰的方式被調(diào)用:
print(sayHello("Anna"))
// prints "Hello, Anna!"
print(sayHello("Brian"))
// prints "Hello, Brian!"
調(diào)用 sayHello(_:)
函數(shù)時(shí)岂津,在圓括號(hào)中傳給它一個(gè) String
類型的實(shí)參虱黄,例如 sayHello("Anna")
。因?yàn)檫@個(gè)函數(shù)返回一個(gè) String
類型的值吮成,sayHello
可以被包含在 print(_:separator:terminator:)
的調(diào)用中橱乱,用來(lái)輸出這個(gè)函數(shù)的返回值,正如上面所示赁豆。
在 sayHello(_:)
的函數(shù)體中仅醇,先定義了一個(gè)新的名為 greeting
的 String
常量,同時(shí)賦值了給 personName
的一個(gè)簡(jiǎn)單問(wèn)候消息魔种。然后用 return
關(guān)鍵字把這個(gè)問(wèn)候返回出去。一旦 return greeting
被調(diào)用粉洼,該函數(shù)結(jié)束它的執(zhí)行并返回 greeting
的當(dāng)前值节预。
可以用不同的輸入值多次調(diào)用 sayHello(_:)
。上面的例子展示的是用"Anna"
和"Brian"
調(diào)用的結(jié)果属韧,該函數(shù)分別返回了不同的結(jié)果安拟。
為了簡(jiǎn)化這個(gè)函數(shù)的定義,可以將問(wèn)候消息的創(chuàng)建和返回寫成一句:
func sayHelloAgain(personName: String) -> String {
return "Hello again, " + personName + "!"
}
print(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ù)锅棕。
無(wú)參函數(shù)(Functions Without Parameters)
函數(shù)可以沒(méi)有參數(shù)拙泽。下面這個(gè)函數(shù)就是一個(gè)無(wú)參函數(shù),當(dāng)被調(diào)用時(shí)裸燎,它返回固定的 String
消息:
func sayHelloWorld() -> String {
return "hello, world"
}
print(sayHelloWorld())
// prints "hello, world"
盡管這個(gè)函數(shù)沒(méi)有參數(shù)顾瞻,但是定義中在函數(shù)名后還是需要一對(duì)圓括號(hào)。當(dāng)被調(diào)用時(shí)德绿,也需要在函數(shù)名后寫一對(duì)圓括號(hào)荷荤。
多參數(shù)函數(shù) (Functions With Multiple Parameters)
函數(shù)可以有多種輸入?yún)?shù),這些參數(shù)被包含在函數(shù)的括號(hào)之中移稳,以逗號(hào)分隔蕴纳。
這個(gè)函數(shù)取得一個(gè)人的名字和是否被招呼作為輸入,并對(duì)那個(gè)人返回適當(dāng)?shù)膯?wèn)候語(yǔ):
func sayHello(personName: String, alreadyGreeted: Bool) -> String {
if alreadyGreeted {
return sayHelloAgain(personName)
} else {
return sayHello(personName)
}
}
print(sayHello("Tim", alreadyGreeted: true))
// prints "Hello again, Tim!"
通過(guò)在括號(hào)內(nèi)傳遞一個(gè)String
參數(shù)值和一個(gè)標(biāo)識(shí)為alreadyGreeted
的Bool
值个粱,使用逗號(hào)分隔來(lái)調(diào)用sayHello(_:alreadyGreeted:)
函數(shù)古毛。
當(dāng)調(diào)用超過(guò)一個(gè)參數(shù)的函數(shù)時(shí),第一個(gè)參數(shù)后的參數(shù)根據(jù)其對(duì)應(yīng)的參數(shù)名稱標(biāo)記几蜻。
無(wú)返回值函數(shù)(Functions Without Return Values)
函數(shù)可以沒(méi)有返回值喇潘。下面是 sayHello(_:)
函數(shù)的另一個(gè)版本体斩,叫 sayGoodbye(_:)
,這個(gè)函數(shù)直接輸出 String
值颖低,而不是返回它:
func sayGoodbye(personName: String) {
print("Goodbye, \(personName)!")
}
sayGoodbye("Dave")
// prints "Goodbye, Dave!"
因?yàn)檫@個(gè)函數(shù)不需要返回值絮吵,所以這個(gè)函數(shù)的定義中沒(méi)有返回箭頭(->)和返回類型。
func sayGoodbye(personName: String) -> Void {
print("Goodbye, \(personName)!")
}
func sayGoodbye(personName: String) () {
print("Goodbye, \(personName)!")
}
注意
嚴(yán)格上來(lái)說(shuō)忱屑,雖然沒(méi)有返回值被定義蹬敲,sayGoodbye(_:)
函數(shù)依然返回了值。沒(méi)有定義返回類型的函數(shù)會(huì)返回特殊的值莺戒,叫Void
伴嗡。它其實(shí)是一個(gè)空的元組(tuple),沒(méi)有任何元素从铲,可以寫成()
瘪校。
被調(diào)用時(shí),一個(gè)函數(shù)的返回值可以被忽略:
func printAndCount(stringToPrint: String) -> Int {
print(stringToPrint)
return stringToPrint.characters.count
}
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ù)定義底部沒(méi)有返回任何值静稻,將導(dǎo)致編譯錯(cuò)誤(compile-time error)警没。
多重返回值函數(shù)(Functions with Multiple Return Values)
可以用元組(tuple)類型讓多個(gè)值作為一個(gè)復(fù)合值從函數(shù)中返回。
下面的這個(gè)例子中姊扔,定義了一個(gè)名為minMax(_:)
的函數(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(_:)
函數(shù)返回一個(gè)包含兩個(gè)Int
值的元組恰梢,這些值被標(biāo)記為min
和max
佛南,以便查詢函數(shù)的返回值時(shí)可以通過(guò)名字訪問(wèn)它們。
minMax(_:)
的函數(shù)體中嵌言,在開(kāi)始的時(shí)候設(shè)置兩個(gè)工作變量currentMin
和currentMax
的值為數(shù)組中的第一個(gè)數(shù)嗅回。然后函數(shù)會(huì)遍歷數(shù)組中剩余的值并檢查該值是否比currentMin
和currentMax
更小或更大。最后數(shù)組中的最小值與最大值作為一個(gè)包含兩個(gè)Int
值的元組返回摧茴。
因?yàn)樵M的成員值已被命名绵载,因此可以通過(guò)點(diǎn)語(yǔ)法來(lái)檢索找到的最小值與最大值:
let bounds = minMax([8, -6, 2, 109, 3, 71])
print("min is \(bounds.min) and max is \(bounds.max)")
// prints "min is -6 and max is 109"
需要注意的是,元組的成員不需要在元組從函數(shù)中返回時(shí)命名,因?yàn)樗鼈兊拿忠呀?jīng)在函數(shù)返回類型中指定了娃豹。
可選元組返回類型(Optional Tuple Return Types)
如果函數(shù)返回的元組類型有可能整個(gè)元組都“沒(méi)有值”焚虱,可以使用可選的(Optional) 元組返回類型反映整個(gè)元組可以是nil
的事實(shí)《妫可以通過(guò)在元組類型的右括號(hào)后放置一個(gè)問(wèn)號(hào)來(lái)定義一個(gè)可選元組鹃栽,例如(Int, Int)?
或(String, Int, Bool)?
注意
可選元組類型如(Int, Int)?
與元組包含可選類型如(Int?, Int?)
是不同的.可選的元組類型,整個(gè)元組是可選的躯畴,而不只是元組中的每個(gè)元素值民鼓。
前面的minMax(_:)
函數(shù)返回了一個(gè)包含兩個(gè)Int
值的元組。但是函數(shù)不會(huì)對(duì)傳入的數(shù)組執(zhí)行任何安全檢查蓬抄,如果array
參數(shù)是一個(gè)空數(shù)組丰嘉,如上定義的minMax(_:)
在試圖訪問(wèn)array[0]
時(shí)會(huì)觸發(fā)一個(gè)運(yùn)行時(shí)錯(cuò)誤。
為了安全地處理這個(gè)“空數(shù)組”問(wèn)題嚷缭,將minMax(_:)
函數(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)
}
可以使用可選綁定來(lái)檢查minMax(_:)
函數(shù)返回的是一個(gè)實(shí)際的元組值還是nil
:
if let bounds = minMax([8, -6, 2, 109, 3, 71]) {
print("min is \(bounds.min) and max is \(bounds.max)")
}
// prints "min is -6 and max is 109"
函數(shù)參數(shù)名稱(Function Parameter Names)
函數(shù)參數(shù)都有一個(gè)外部參數(shù)名(external parameter name)和一個(gè)局部參數(shù)名(local parameter name)。外部參數(shù)名用于在函數(shù)調(diào)用時(shí)標(biāo)注傳遞給函數(shù)的參數(shù)峭状,局部參數(shù)名在函數(shù)的實(shí)現(xiàn)內(nèi)部使用克滴。
func someFunction(firstParameterName: Int, secondParameterName: Int) {
// function body goes here
// firstParameterName and secondParameterName refer to
// the argument values for the first and second parameters
}
someFunction(1, secondParameterName: 2)
一般情況下,第一個(gè)參數(shù)省略其外部參數(shù)名优床,第二個(gè)以及隨后的參數(shù)使用其局部參數(shù)名作為外部參數(shù)名。所有參數(shù)必須有獨(dú)一無(wú)二的局部參數(shù)名誓焦。盡管多個(gè)參數(shù)可以有相同的外部參數(shù)名胆敞,但不同的外部參數(shù)名能讓代碼更有可讀性。
指定外部參數(shù)名(Specifying External Parameter Names)
可以在局部參數(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è)版本的sayHello(_:)
函數(shù)蠕嫁,接收兩個(gè)人的名字而咆,會(huì)同時(shí)返回對(duì)他倆的問(wèn)候:
func sayHello(to person: String, and anotherPerson: String) -> String {
return "Hello \(person) and \(anotherPerson)!"
}
print(sayHello(to: "Bill", and: "Ted"))
// prints "Hello Bill and Ted!"
為每個(gè)參數(shù)指定外部參數(shù)名后,在調(diào)用sayHello(to:and:)
函數(shù)時(shí)兩個(gè)外部參數(shù)名都必須寫出來(lái)。
使用外部函數(shù)名可以使函數(shù)以一種更富有表達(dá)性的類似句子的方式調(diào)用幅慌,并使函數(shù)體意圖清晰,更具可讀性阱持。
忽略外部參數(shù)名(Omitting External Parameter Names)
如果不想為第二個(gè)及后續(xù)的參數(shù)設(shè)置外部參數(shù)名答恶,用一個(gè)下劃線(_
)代替一個(gè)明確的參數(shù)名。
func someFunction(firstParameterName: Int, _ secondParameterName: Int) {
// function body goes here
// firstParameterName and secondParameterName refer to
// the argument values for the first and second parameters
}
someFunction(1, 2)
注意
因?yàn)榈谝粋€(gè)參數(shù)默認(rèn)忽略其外部參數(shù)名稱挣跋,顯式地寫下劃線是多余的三圆。
默認(rèn)參數(shù)值(Default Parameter Values)
可以在函數(shù)體中為每個(gè)參數(shù)定義默認(rèn)值(Deafult Values)
。當(dāng)默認(rèn)值被定義后,調(diào)用這個(gè)函數(shù)時(shí)可以忽略這個(gè)參數(shù)舟肉。
func someFunction(parameterWithDefault: Int = 12) -> Int {
// function body goes here
// if no arguments are passed to the function call,
// value of parameterWithDefault is 12
return parameterWithDefault
}
someFunction(6) // parameterWithDefault is 6
someFunction() // parameterWithDefault is 12
注意
將帶有默認(rèn)值的參數(shù)放在函數(shù)參數(shù)列表的最后修噪。這樣可以保證在函數(shù)調(diào)用時(shí),非默認(rèn)參數(shù)的順序是一致的路媚,同時(shí)使得相同的函數(shù)在不同情況下調(diào)用時(shí)顯得更為清晰黄琼。
可變參數(shù)(Variadic Parameters)
一個(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è)叫做 numbers
的 Double...
型可變參數(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)
// returns 3.0, which is the arithmetic mean of these five numbers
arithmeticMean(3, 8.25, 18.75)
// returns 10.0, which is the arithmetic mean of these three numbers
注意
一個(gè)函數(shù)最多只能有一個(gè)可變參數(shù)丈氓。
如果函數(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ù)的變量值副本將是很有用的库倘。可以通過(guò)指定一個(gè)或多個(gè)參數(shù)為變量參數(shù)论矾,從而避免自己在函數(shù)中定義新的變量教翩。變量參數(shù)不是常量,可以在函數(shù)中把它當(dāng)做新的可修改副本來(lái)使用贪壳。
通過(guò)在參數(shù)名前加關(guān)鍵字 var
來(lái)定義變量參數(shù):
func alignRight(var string: String, totalLength: Int, pad: Character) -> String {
let amountToPad = totalLength - string.characters.count
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, totalLength: 10, pad: "-")
// paddedString is equal to "-----hello"
// originalString is still equal to "hello"
這個(gè)例子中定義了一個(gè)叫做 alignRight(_:totalLength:pad:)
的新函數(shù)饱亿,用來(lái)將輸入的字符串對(duì)齊到更長(zhǎng)的輸出字符串的右邊緣。左側(cè)空余的地方用指定的填充字符填充闰靴。這個(gè)例子中彪笼,字符串"hello"
被轉(zhuǎn)換成了"-----hello"
。
alignRight(_:totalLength:pad:)
函數(shù)將輸入?yún)?shù) string
定義為變量參數(shù)传黄。這意味著 string
現(xiàn)在可以作為一個(gè)局部變量杰扫,被傳入的字符串值初始化,并且可以在函數(shù)體中進(jìn)行操作膘掰。
函數(shù)首先計(jì)算出有多少字符需要被添加到string
的左邊章姓,從而將其在整個(gè)字符串中右對(duì)齊佳遣。這個(gè)值存儲(chǔ)在一個(gè)稱為amountToPad
的本地常量。如果不需要填充(也就是說(shuō)凡伊,如果amountToPad
小于1)零渐,該函數(shù)簡(jiǎn)單地返回沒(méi)有任何填充的輸入值string
。
否則系忙,該函數(shù)用pad
字符創(chuàng)建一個(gè)叫做padString
的臨時(shí)String
常量诵盼,并將amountToPad
個(gè) padString
添加到現(xiàn)有字符串的左邊。(一個(gè)String
值不能被添加到一個(gè)Character
值上银还,所以padString
常量用于確保+
操作符兩側(cè)都是String
值)风宁。
注意
對(duì)變量參數(shù)所進(jìn)行的修改在函數(shù)調(diào)用結(jié)束后便消失了,并且對(duì)于函數(shù)體外是不可見(jiàn)的蛹疯。變量參數(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ù)理郑,替換原來(lái)的值太示。
只能傳遞變量給輸入輸出參數(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è)分別叫做 a
和 b
的輸入輸出參數(shù):
func swapTwoInts(inout a: Int, inout _ b: Int) {
let temporaryA = a
a = b
b = temporaryA
}
這個(gè) swapTwoInts(_:_:)
函數(shù)簡(jiǎn)單地交換 a
與 b
的值氓扛。該函數(shù)先將 a
的值存到一個(gè)臨時(shí)常量 temporaryA
中枯芬,然后將 b
的值賦給 a
论笔,最后將 temporaryA
賦值給 b
。
可以用兩個(gè) Int
型的變量來(lái)調(diào)用 swapTwoInts(_:_:)
千所。需要注意的是狂魔,someInt
和 anotherInt
在傳入 swapTwoInts(_:_:)
函數(shù)前,都加了 &
的前綴:
var someInt = 3
var anotherInt = 107
swapTwoInts(&someInt, &anotherInt)
print("someInt is now \(someInt), and anotherInt is now \(anotherInt)")
// prints "someInt is now 107, and anotherInt is now 3"
從上面這個(gè)例子中淫痰,我們可以看到 someInt
和 anotherInt
的原始值在 swapTwoInts(_:_:)
函數(shù)中被修改最楷,盡管它們的定義在函數(shù)體外。
注意
輸入輸出參數(shù)和返回值是不一樣的待错。上面的swapTwoInts
函數(shù)并沒(méi)有定義任何返回值籽孙,但仍然修改了someInt
和anotherInt
的值。輸入輸出參數(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ù):addTwoInts
和 multiplyTwoInts
烛占。這兩個(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ù)類型(Using Function Types)
在 Swift 中称杨,使用函數(shù)類型就像使用其他類型一樣。例如筷转,可以定義一個(gè)類型為函數(shù)的常量或變量姑原,并將適當(dāng)?shù)暮瘮?shù)賦值給它:
var mathFunction: (Int, Int) -> Int = addTwoInts
這個(gè)可以解讀為:
“定義一個(gè)叫做 mathFunction
的變量,類型是‘一個(gè)有兩個(gè) Int
型的參數(shù)并返回一個(gè) Int
型的值的函數(shù)’呜舒,并讓這個(gè)新變量指向 addTwoInts
函數(shù)”锭汛。
addTwoInts
和 mathFunction
有同樣的類型,所以這個(gè)賦值過(guò)程在 Swift 類型檢查中是允許的袭蝗。
現(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 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)用者來(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)
// prints "Result: 8"
這個(gè)例子定義了 printMathResult(_:_:_:)
函數(shù),它有三個(gè)參數(shù):第一個(gè)參數(shù)叫 mathFunction
段誊,類型是(Int, Int) -> Int
闰蚕,可以傳入任何這種類型的函數(shù);第二個(gè)和第三個(gè)參數(shù)叫 a
和 b
连舍,它們的類型都是 Int
没陡,這兩個(gè)值作為已給出的函數(shù)的輸入值。
當(dāng) printMathResult(_:_:_:)
被調(diào)用時(shí)索赏,它被傳入 addTwoInts
函數(shù)和整數(shù)3
和5
盼玄。它用傳入3
和5
調(diào)用 addTwoInts
,并輸出結(jié)果:8
潜腻。
printMathResult(_:_:_:)
函數(shù)的作用就是輸出另一個(gè)適當(dāng)類型的數(shù)學(xué)函數(shù)的調(diào)用結(jié)果埃儿。它不關(guān)心傳入函數(shù)是如何實(shí)現(xiàn)的,它只關(guān)心這個(gè)傳入的函數(shù)類型是正確的融涣。這使得 printMathResult(_:_:_:)
能以一種類型安全(type-safe)的方式將一部分功能轉(zhuǎn)給調(diào)用者實(shí)現(xiàn)童番。
函數(shù)類型作為返回類型(Function Types as Return Types)
可以用函數(shù)類型作為另一個(gè)函數(shù)的返回類型。需要做的是在返回箭頭(->
)后寫一個(gè)完整的函數(shù)類型威鹿。
下面的這個(gè)例子中定義了兩個(gè)簡(jiǎn)單函數(shù)剃斧,分別是 stepForward
和stepBackward
。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
來(lái)返回 stepForward(_:)
函數(shù)或 stepBackward(_:)
函數(shù):
func chooseStepFunction(backwards: Bool) -> (Int) -> Int {
return backwards ? stepBackward : stepForward
}
現(xiàn)在可以用 chooseStepFunction(_:)
來(lái)獲得兩個(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ù)买决,它可以被用來(lái)數(shù)到0
:
print("Counting to zero:")
// Counting to zero:
while currentValue != 0 {
print("\(currentValue)... ")
currentValue = moveNearerToZero(currentValue)
}
print("zero!")
// 3...
// 2...
// 1...
// zero!
嵌套函數(shù)(Nested Functions)
本文所見(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ù)的方式重寫 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 {
print("\(currentValue)... ")
currentValue = moveNearerToZero(currentValue)
}
print("zero!")
// -4...
// -3...
// -2...
// -1...
// zero!