[11 Go語言基礎-可變參數函數]
可變參數函數
什麼是可變參數函數
可變參數函數是一種參數個數可變的函數。
語法
如果函數最後一個參數被記作 ...T
,這時函數可以接受任意個 T
類型參數作為最後一個參數。
請注意只有函數的最後一個參數才允許是可變的。
通過一些例子理解可變參數函數如何工作
你是否曾經想過 append 函數是如何將任意個參數值加入到切片中的。這樣 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
相當於一個整型切片。
可變參數函數的工作原理是把可變參數轉換為一個新的切片。以上面程序中的第 22 行為例,find 函數中的可變參數是 89,90,95 。 find 函數接受一個 int 類型的可變參數。因此這三個參數被編譯器轉換為一個 int 類型切片 int []int{89, 90, 95} 然後被傳入 find函數。
在第 10 行, for
循環遍曆 nums
切片,如果 num
在切片中,則打印 num
的比特置。如果 num
不在切片中,則打印提示未找到該數字。
上面代碼的輸出值如下,
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 ...int
傳入任何參數。這也是合法的,在這種情况下 nums
是一個長度和容量為 0 的 nil
切片。
給可變參數函數傳入切片
下面例子中,我們給可變參數函數傳入一個切片,看看會發生什麼。
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
函數中。但是在這裏 nums
已經是一個 int 類型切片,編譯器試圖在 nums
基礎上再創建一個切片,像下面這樣
find(89, []int{nums})
這裏之所以會失敗是因為 nums
是一個 []int
類型 而不是 int
類型。
那麼有沒有辦法給可變參數函數傳入切片參數呢?答案是肯定的。
有一個可以直接將切片傳入可變參數函數的語法糖,你可以在在切片後加上 ... 後綴。如果這樣做,切片將直接傳入函數,不再創建新的切片
在上面的程序中,如果你將第 23 行的 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)
}
可變長參數(小練習)
package main
import "fmt"
// 可變長參數
func main() {
//test2("11111","2")
var a []string=[]string{"lqz","egon"}
test2(a...) // 把切片傳入
}
func test2(a ...string) { // a是切片
fmt.Println(a)
fmt.Println(a[1])
fmt.Printf("%T",a)
a=append(a,"lqz")
fmt.Println(a)
}