忽略參數標籤
如果你不希望爲某個參數添加一個標籤,可以使⽤一個下劃線( _ )來代替⼀個明確的參數標籤。
func someFunction(_ firstParameterName: Int, secondParameterName: Int) {
// 在函數體內,firstParameterName 和 secondParameterName 代表參數中的第一個和第二個參數值
}
someFunction(1, secondParameterName: 2)
如果⼀個參數有一個標籤,那麼在調用的時候必須使用標籤來標記這個參數。
默認參數值
你可以在函數體中通過給參數賦值來爲任意一個參數定義默認值(Deafult Value)。當默認值被定義後,調⽤這個函數時可以忽略
這個參數。
func someFunction(parameterWithoutDefault: Int, parameterWithDefault: Int = 12) {
// 如果你在調用時候不傳第二個參數,parameterWithDefault 會值爲 12 傳入到函數體中。
}
someFunction(parameterWithoutDefault: 3, parameterWithDefault: 6) // parameterWithDefault = 6
someFunction(parameterWithoutDefault: 4) // parameterWithDefault = 12
將不帶有默認值的參數放在函數參數列表的最前面。一般來說,沒有默認值的參數更加的重要,將不帶默認值的參數放在最前面
保證在函數調用時,非默認參數的順序是一致的,同時也使得相同的函數在不同情況下調用時顯得更爲清晰。
可變參數
一個可變參數(variadic parameter)可以接受零個或多個值。函數調用時,你可以用可變參數來指定函數參數可以被傳入不確定
數量的輸入值。通過在變量類型名後面加入( ... )的方式來定義可變參數。
可變參數的傳入值在函數體中變爲此類型的一個數組。例如,一個叫做 numbers 的 Double... 型可變參數,在函數體內可以當
做一個叫 numbers 的 [Double] 型的數組常量。
下面的這個函數用來計算一組任意長度數字的算術平均數(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 個數的平均數。
arithmeticMean(3, 8.25, 18.75)
// 返回 10.0, 是這 3 個數的平均數。
注意
一個函數最多隻能擁有⼀個可變參數。
輸入輸出參數
函數參數默認是常量。試圖在函數體中更改參數值將會導致編譯錯誤。這意味着你不能錯誤地更改參數值。如果你想要一個函數
可以修改參數的值,並且想要這些修改在函數調用結束後仍然存在,那麼就應該把這個參數定義爲輸入輸出參數 (In-Out
Parameters)。
定義一個輸入輸出參數時,在參數定義前加 inout 關鍵字。一個輸入輸出參數有傳入函數的值,這個值被函數修改,然後被傳出
函數,替換原來的值。想獲取更多的關於輸入輸出參數的細節和相關的編譯器優化,請查看《輸⼊入輸出參數》一 節。
你只能傳遞變量給輸入輸出參數。你不能傳入常量或者字⾯量,因爲這些值是不能被修改的。當傳入的參數作爲輸入輸出參數
時,需要在參數名前加 & 符,表示這個值可以被函數修改。
注意
輸入輸出參數不能有默認值,而且可變參數不能用 inout 標記。
下例中, swapTwoInts(_:_:) 函數有兩個分別叫做 a 和 b 的輸入輸出參數:
func swapTwoInts(_ a: inout Int, _ b: inout Int) {
let temporaryA = a
a=b
b = temporaryA
}
swapTwoInts(_:_:) 函數簡單地交換 a 與 b 的值。該函數先將 a 的值存到一個臨時常量 temporaryA 中, 然後將 b 的值賦給 a ,最
後將 temporaryA 賦值給 b 。
你可以用兩個 Int 型的變量來調用 swapTwoInts(_:_:) 。需要注意的是, someInt 和 anotherInt 在傳入swapTwoInts(_:_:) 函數前,
都加了 & 的前綴:
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”
從上⾯這個例子中,我們可以看到 someInt 和 anotherInt 的原始值在 swapTwoInts(_:_:) 函數中被修改,儘管它們的定義在函數
體外。
注意
輸⼊輸出參數和返回值是不一樣的。上面的 swapTwoInts 函數並沒有定義任何返回值,但仍然修改了 someInt 和
anotherInt 的值。輸入輸出參數是函數對函數體外產生影響的另一種方式。
函數類型
每個函數都有特定的函數類型,函數的類型由函數的參數類型和返回類型組成。例如:
func addTwoInts(_ a: Int, _ b: Int) -> Int {
return a + b
}
func multiplyTwoInts(_ a: Int, _ b: Int) -> Int {
return a * b
}
這個例子中定義了兩個簡單的數學函數: addTwoInts 和 multiplyTwoInts 。這兩個函數都接受兩個 Int 值, 返回一個 Int 值。
這兩個函數的類型是 (Int, Int) -> Int ,可以解讀爲: “這個函數類型有兩個 Int 型的參數並返回一個 Int 型的值”。 下⾯是另一個例
子,一個沒有參數,也沒有返回值的函數:
func printHelloWorld() {
print("hello, world")
}
這個函數的類型是: () -> Void ,或者叫“沒有參數,並返回值爲 Void 類型的函數”。
使用函數類型
在 Swift 中,使用函數類型就像使用其他類型一樣。例如,你可以定義一個類型爲函數的常量或變量,並將適當的函數賦值給它:
var mathFunction: (Int, Int) -> Int = addTwoInts
這段代碼可以被解讀爲:
”定義一個叫做 mathFunction 的變量,類型是‘一個有兩個 Int 型的參數並返回一個 Int 型的值的函數’,並讓這個新變量指向
addTwoInts 函數”。
addTwoInts 和 mathFunction 有同樣的類型,所以這個賦值過程在 Swift 類型檢查(type-check)中是允許的。 現在,你可以用
mathFunction 來調用被賦值的函數了:
print("Result: \(mathFunction(2, 3))")
// Prints "Result: 5"
有相同匹配類型的不同函數可以被賦值給同一個變量,就像非函數類型的變量一樣:
mathFunction = multiplyTwoInts
print("Result: \(mathFunction(2, 3))")
// Prints "Result: 6"
就像其他類型一樣,當賦值一個函數給常量或變量時,你可以讓 Swift 來推斷其函數類型:
let anotherMathFunction = addTwoInts
// anotherMathFunction 被推斷爲 (Int, Int) -> Int 類型
函數類型作爲參數類型
你可以⽤ (Int, Int) -> Int 這樣的函數類型作爲另一個函數的參數類型。這樣你可以將函數的一部分實現留給函數的調用者來提
供。
下⾯是另一個例子,正如上面的函數一樣,同樣是輸出某種數學運算結果:
func printMathResult(_ mathFunction: (Int, Int) -> Int, _ a: Int, _ b: Int) {
print("Result: \(mathFunction(a, b))")
}
printMathResult(addTwoInts, 3, 5)// 打印“Result: 8”
這個例子定義了 printMathResult(_:_:_:) 函數,它有三個參數:第⼀個參數叫 mathFunction ,類型是 (Int, Int) -> Int ,你可以傳⼊
任何這種類型的函數;第二個和第三個參數叫 a 和 b ,它們的類型都是 Int ,這兩個值作爲已給出的函數的輸⼊值。
當 printMathResult(_:_:_:) 被調用時,它被傳入 addTwoInts 函數和整數 3 和 5 。它用傳入 3 和 5 調用 addTwoInts ,並輸出結果:
8 。
printMathResult(_:_:_:) 函數的作用就是輸出另一個適當類型的數學函數的調⽤結果。它不關心傳入函數是如何實現的,只關心傳
⼊的函數是不是一個正確的類型。這使得 printMathResult(_:_:_:) 能以一種類型安全(type- safe)的方式將一部分功能轉給調用者
實現。
函數類型作爲返回類型
你可以用函數類型作爲另一個函數的返回類型。你需要做的是在返回箭頭(->)後寫一個完整的函數類型。
下⾯的這個例子中定義了兩個簡單函數,分別是 stepForward(_:) 和 stepBackward(_:) 。 stepForward(_:)函數返回一個比輸入值
⼤ 1 的值。 stepBackward(_:) 函數返回一個比輸入值小 1 的值。這兩個函數的類型都是(Int) -> Int :
func stepForward(_ input: Int) -> Int {
return input + 1
}
func stepBackward(_ input: Int) -> Int {
return input - 1
}
如下名爲 chooseStepFunction(backward:) 的函數,它的返回類型是 (Int) -> Int 類型的函數。 chooseStepFunction(backward:)
根據布爾值 backwards 來返回 stepForward(_:) 函數或 stepBackward(_:) 函數:
func chooseStepFunction(backward: Bool) -> (Int) -> Int {
return backward ? stepBackward : stepForward
}
你現在可以用 chooseStepFunction(backward:) 來獲得兩個函數其中的一個:
var currentValue = 3
let moveNearerToZero = chooseStepFunction(backward: currentValue > 0)
// moveNearerToZero 現在指向 stepBackward() 函數。
上⾯這個例子中計算出從 currentValue 逐漸接近到0是需要向正數走還是向負數走。 currentValue 的初始值是3 ,這意味着
currentValue > 0 爲真(true),這將使得 chooseStepFunction(_:) 返回stepBackward(_:) 函數。一個指向返回的函數的引⽤保存在
了 moveNearerToZero 常量中。
現在, moveNearerToZero 指向了正確的函數,它可以被用來數到零:
print("Counting to zero:")
// Counting to zero:
while currentValue != 0 {
print("\(currentValue)... ")
currentValue = moveNearerToZero(currentValue)
}
print("zero!")
// 3...
// 2...
// 1...
// zero!
嵌套函數
到目前爲止本章中你所見到的所有函數都叫全局函數(global functions),它們定義在全局域中。你也可以把函數定義在別的函數
體中,稱作嵌套函數(nested functions)。
默認情況下,嵌套函數是對外界不可見的,但是可以被它們的外圍函數(enclosing function)調用。一個外圍函數也可以返回它
的某一個嵌套函數,使得這個函數可以在其他域中被使用。
你可以用返回嵌套函數的方式重寫 chooseStepFunction(backward:) 函數:
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!