- 原文地址:Part 12: Variadic Functions
- 原文作者:Naveen R
- 譯者:咔嘰咔嘰 轉載請註明出處。
什麼是變參函式
變參函式是一個可以接受引數數量可變的函式。
語法
如果函式的最後一個引數用...T
表示,那麼該函式最後一個引數可以接受任意數量的T
型別的引數。
注意,只允許函式的最後一個引數為可變引數。
示例
append
函式可以增加任意數量的引數給切片。變參函式就是用到了切片的這個原理。
func append(slice []Type, elems ...Type) []Type
複製程式碼
以上是append
函式的定義。在這個定義中,elems
是一個可變引數。通過這個語法,append
可以接受可變數量的引數。
來建立一個變參函式。我們將編寫一個查詢輸入列表中是否存在指定整數的簡單程式。
package main
import (
"fmt"
)
func find(num int, nums ...int) {
fmt.Printf("type of nums is %T\n", nums)
found := false
for i, v := range nums {
if v == num {
fmt.Println(num, "found at index", i, "in", nums)
found = true
}
}
if !found {
fmt.Println(num, "not found in ", nums)
}
fmt.Printf("\n")
}
func main() {
find(89, 89, 90, 95)
find(45, 56, 67, 45, 90, 109)
find(78, 38, 56, 98)
find(87)
}
複製程式碼
在上面的程式中,func find(num int, nums ...int)
接收可變數量的引數nums
。在函式find
中,nums
的型別等價於[]int
,即整數切片。
變參函式的原理是將傳遞的可變引數轉換為一個新切片。例如,在上面程式的第 22 中,find
函式的可變引數是 89, 90, 95。find
函式需要一個可變的int
引數。因此,這三個引數將由編譯器轉換為 int 型的[] int {89,90,95}
切片,然後它將被傳遞給find
函式。
上述程式將列印,
type of nums is []int
89 found at index 0 in [89 90 95]
type of nums is []int
45 found at index 2 in [56 67 45 90 109]
type of nums is []int
78 not found in [38 56 98]
type of nums is []int
87 not found in []
複製程式碼
上面程式的第 25 行,find
函式呼叫只有一個引數。我們沒有向可變引數傳遞任何值。這是完全合法的,在這種情況下,nums
是一個長度和容量都為 0 的切片。
使用切片作為變參函式的引數
我們將一個切片傳遞給一個變參函式,並看看下面的例子會發生什麼。
package main
import (
"fmt"
)
func find(num int, nums ...int) {
fmt.Printf("type of nums is %T\n", nums)
found := false
for i, v := range nums {
if v == num {
fmt.Println(num, "found at index", i, "in", nums)
found = true
}
}
if !found {
fmt.Println(num, "not found in ", nums)
}
fmt.Printf("\n")
}
func main() {
nums := []int{89, 90, 95}
find(89, nums)
}
複製程式碼
在第 23 行,我們將一個切片傳遞給一個變參函式。
上面的程式將會出現編譯錯誤main.go:23: cannot use nums (type []int) as type int in argument to find
為什麼這個例子不能執行呢?因為find
函式的簽名如下所示,
func find(num int, nums ...int)
複製程式碼
根據變參函式的定義,nums ...int
意味著它將接受 int 型別的可變數量的引數。
在第 23 行中,nums
作為可變引數傳遞給find
函式。正如我們已經討論過的,這些可變引數將被轉換為 int 型別的切片,因為 find 需要可變的 int 引數。在這個例子中,nums
已經是一個 int 切片了,並且還嘗試使用 nums 去建立切片,即編譯器嘗試執行
find(89, []int{nums})
複製程式碼
因為 nums 是[] int
而不是 int,所以會失敗。
那麼有沒有辦法將切片傳遞給可變引數函式?當然可以。
有一個語法糖可用於將切片傳遞給變參函式。就是在切片後使用...
。如果這樣做,切片將直接傳遞給函式,而不會建立新切片。
上述程式碼中,我們用find(89, nums...)
替換find(89, nums)
,程式將執行並列印,
type of nums is []int
89 found at index 0 in [89 90 95]
複製程式碼
這是完整的程式供參考。
package main
import (
"fmt"
)
func find(num int, nums ...int) {
fmt.Printf("type of nums is %T\n", nums)
found := false
for i, v := range nums {
if v == num {
fmt.Println(num, "found at index", i, "in", nums)
found = true
}
}
if !found {
fmt.Println(num, "not found in ", nums)
}
fmt.Printf("\n")
}
func main() {
nums := []int{89, 90, 95}
find(89, nums...)
}
複製程式碼
注意事項
當你在變參函式中修改切片時,請確保知道自己在做什麼。
來看個例子。
package main
import (
"fmt"
)
func change(s ...string) {
s[0] = "Go"
}
func main() {
welcome := []string{"hello", "world"}
change(welcome...)
fmt.Println(welcome)
}
複製程式碼
你覺得上面的程式碼會輸出什麼?如果你覺得是 [Go world]
,那麼恭喜你!你已經理解了變參函式和切片。如果你弄錯了,沒什麼大不了的,讓我解釋一下我們為什麼是這個輸出。
上面程式的第 13 行,我們使用語法糖...
並將切片作為可變引數傳遞給change
函式。
正如我們已經討論的那樣,如果使用了...
,welcome
切片本身將作為引數傳遞,而不會建立新的切片。因此welcome
將作為引數傳遞給change
函式。
在change
函式內部,切片的第一個元素更改為 Go。因此該程式輸出
[Go world]
複製程式碼
這是另一個瞭解變參函式的程式。
package main
import (
"fmt"
)
func change(s ...string) {
s[0] = "Go"
s = append(s, "playground")
fmt.Println(s)
}
func main() {
welcome := []string{"hello", "world"}
change(welcome...)
fmt.Println(welcome)
}
複製程式碼
這個作為練習讓你弄清楚上面的程式是如何工作的:)。