Swift函數參數名稱
函數參數名稱(Function Parameter Names)
以上所有的函數都給它們的參數定義了參數名(parameter name)
:
func someFunction(parameterName: Int) {
// function body goes here, and can use parameterName
// to refer to the argument value for that parameter
}
但是,這些參數名僅在函數體中使用,不能在函數調用時使用。這種類型的參數名被稱作局部參數名(local parameter name)
,因爲它們只能在函數體中使用。
外部參數名(External Parameter Names)
有時候,調用函數時,給每個參數命名是非常有用的,因爲這些參數名可以指出各個實參的用途是什麼。
如果你希望函數的使用者在調用函數時提供參數名字,那就需要給每個參數除了局部參數名外再定義一個外部參數名
。外部參數名寫在局部參數名之前,用空格分隔。
func someFunction(externalParameterName localParameterName: Int) {
// function body goes here, and can use localParameterName
// to refer to the argument value for that parameter
}
注意:
如果你提供了外部參數名,那麼函數在被調用時,必須使用外部參數名。
以下是個例子,這個函數使用一個結合者(joiner)
把兩個字符串聯在一起:
func join(s1: String, s2: String, joiner: String) -> String {
return s1 + joiner + s2
}
當你調用這個函數時,這三個字符串的用途是不清楚的:
join("hello", "world", ", ")
// returns "hello, world"
爲了讓這些字符串的用途更爲明顯,我們爲 join
函數添加外部參數名:
func join(string s1: String, toString s2: String, withJoiner joiner: String) -> String {
return s1 + joiner + s2
}
在這個版本的 join
函數中,第一個參數有一個叫 string
的外部參數名和 s1
的局部參數名,第二個參數有一個叫toString
的外部參數名和 s2
的局部參數名,第三個參數有一個叫 withJoiner
的外部參數名和 joiner
的局部參數名。
現在,你可以使用這些外部參數名以一種清晰地方式來調用函數了:
join(string: "hello", toString: "world", withJoiner: ", ")
// returns "hello, world"
使用外部參數名讓第二個版本的 join
函數的調用更爲有表現力,更爲通順,同時還保持了函數體是可讀的和有明確意圖的。
注意:
當其他人在第一次讀你的代碼,函數參數的意圖顯得不明顯時,考慮使用外部參數名。如果函數參數名的意圖是很明顯的,那就不需要定義外部參數名了。
簡寫外部參數名(Shorthand External Parameter Names)
如果你需要提供外部參數名,但是局部參數名已經定義好了,那麼你不需要寫兩次這些參數名。相反,只寫一次參數名,並用井號(#)
作爲前綴就可以了。這告訴 Swift 使用這個參數名作爲局部和外部參數名。
下面這個例子定義了一個叫 containsCharacter
的函數,使用井號(#)
的方式定義了外部參數名:
func containsCharacter(#string: String, #characterToFind: Character) -> Bool {
for character in string {
if character == characterToFind {
return true
}
}
return false
}
這樣定義參數名,使得函數體更爲可讀,清晰,同時也可以以一個不含糊的方式被調用:
let containsAVee = containsCharacter(string: "aardvark", characterToFind: "v")
// containsAVee equals true, because "aardvark" contains a "v」
默認參數值(Default Parameter Values)
你可以在函數體中爲每個參數定義默認值
。當默認值被定義後,調用這個函數時可以略去這個參數。
注意:
將帶有默認值的參數放在函數參數表的最後。這樣可以保證在函數調用時,非默認參數的順序是一致的,同時使得相同的函數在不同情況下調用時顯得更爲清晰。
以下是另一個版本的join
函數,其中joiner
有了默認參數值:
func join(string s1: String, toString s2: String, withJoiner joiner: String = " ") -> String {
return s1 + joiner + s2
}
像第一個版本的 join
函數一樣,如果 joiner
被賦值時,函數將使用這個字符串值來連接兩個字符串:
join(string: "hello", toString: "world", withJoiner: "-")
// returns "hello-world"
當這個函數被調用時,如果 joiner
的值沒有被指定,函數會使用默認值(" "):
join(string: "hello", toString:"world")
// returns "hello world"
默認值參數的外部參數名(External Names for Parameters with Default Values)
在大多數情況下,給帶默認值的參數起一個外部參數名是很有用的。這樣可以保證當函數被調用且帶默認值的參數被提供值時,實參的意圖是明顯的。
爲了使定義外部參數名更加簡單,當你未給帶默認值的參數提供外部參數名時,Swift 會自動提供外部名字。此時外部參數名與局部名字是一樣的,就像你已經在局部參數名前寫了井號(#)
一樣。
下面是 join
函數的另一個版本,這個版本中並沒有爲它的參數提供外部參數名,但是 joiner
參數依然有外部參數名:
func join(s1: String, s2: String, joiner: String = " ") -> String {
return s1 + joiner + s2
}
在這個例子中,Swift 自動爲 joiner
提供了外部參數名。因此,當函數調用時,外部參數名必須使用,這樣使得參數的用途變得清晰。
join("hello", "world", joiner: "-")
// returns "hello-world"
注意:
你可以使用下劃線(_)
作爲默認值參數的外部參數名,這樣可以在調用時不用提供外部參數名。但是給帶默認值的參數命名總是更加合適的。
可變參數(Variadic Parameters)
一個可變參數(variadic parameter)
可以接受一個或多個值。函數調用時,你可以用可變參數來傳入不確定數量的輸入參數。通過在變量類型名後面加入(...)
的方式來定義可變參數。
傳入可變參數的值在函數體內當做這個類型的一個數組。例如,一個叫做 numbers
的 Double...
型可變參數,在函數體內可以當做一個叫 numbers
的 Double[]
型的數組常量。
下面的這個函數用來計算一組任意長度數字的算術平均數:
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
注意:
一個函數至多能有一個可變參數,而且它必須是參數表中最後的一個。這樣做是爲了避免函數調用時出現歧義。
如果函數有一個或多個帶默認值的參數,而且還有一個可變參數,那麼把可變參數放在參數表的最後。
常量參數和變量參數(Constant and Variable Parameters)
函數參數默認是常量。試圖在函數體中更改參數值將會導致編譯錯誤。這意味着你不能錯誤地更改參數值。
但是,有時候,如果函數中有傳入參數的變量值副本將是很有用的。你可以通過指定一個或多個參數爲變量參數,從而避免自己在函數中定義新的變量。變量參數不是常量,你可以在函數中把它當做新的可修改副本來使用。
通過在參數名前加關鍵字 var
來定義變量參數:
func alignRight(var string: String, count: Int, pad: Character) -> String {
let amountToPad = count - countElements(string)
for _ in 1...amountToPad {
string = pad + string
}
return string
}
let originalString = "hello"
let paddedString = alignRight(originalString, 10, "-")
// paddedString is equal to "-----hello"
// originalString is still equal to "hello"
這個例子中定義了一個新的叫做 alignRight
的函數,用來右對齊輸入的字符串到一個長的輸出字符串中。左側空餘的地方用指定的填充字符填充。這個例子中,字符串"hello"
被轉換成了"-----hello"
。
alignRight
函數將參數 string
定義爲變量參數。這意味着 string
現在可以作爲一個局部變量,用傳入的字符串值初始化,並且可以在函數體中進行操作。
該函數首先計算出多少個字符需要被添加到 string
的左邊,以右對齊到總的字符串中。這個值存在局部常量amountToPad
中。這個函數然後將 amountToPad
多的填充(pad)字符填充到 string
左邊,並返回結果。它使用了string
這個變量參數來進行所有字符串操作。
注意:
對變量參數所進行的修改在函數調用結束後便消失了,並且對於函數體外是不可見的。變量參數僅僅存在於函數調用的生命週期中。
輸入輸出參數(In-Out Parameters)
變量參數,正如上面所述,僅僅能在函數體內被更改。如果你想要一個函數可以修改參數的值,並且想要在這些修改在函數調用結束後仍然存在,那麼就應該把這個參數定義爲輸入輸出參數(In-Out Parameters)。
定義一個輸入輸出參數時,在參數定義前加 inout
關鍵字。一個輸入輸出參數有傳入函數的值,這個值被函數修改,然後被傳出函數,替換原來的值。
你只能傳入一個變量作爲輸入輸出參數。你不能傳入常量或者字面量(literal value),因爲這些量是不能被修改的。當傳入的參數作爲輸入輸出參數時,需要在參數前加&
符,表示這個值可以被函數修改。
注意:
輸入輸出參數不能有默認值,而且可變參數不能用inout
標記。如果你用inout
標記一個參數,這個參數不能被var
或者let
標記。
下面是例子,swapTwoInts
函數,有兩個分別叫做 a
和 b
的輸出輸出參數:
func swapTwoInts(inout a: Int, inout b: 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)
println("someInt is now \(someInt), and anotherInt is now \(anotherInt)")
// prints "someInt is now 107, and anotherInt is now 3」
從上面這個例子中,我們可以看到 someInt
和 anotherInt
的原始值在 swapTwoInts
函數中被修改,儘管它們的定義在函數體外。
注意:
輸出輸出參數和返回值是不一樣的。上面的swapTwoInts
函數並沒有定義任何返回值,但仍然修改了someInt
和anotherInt
的值。輸入輸出參數是函數對函數體外產生影響的另一種方式。