欢迎来到 Golang教程系列的第12部分
什么是变参函数?
变参函数是一个可以接受参数数量可变的函数。
语法
如果一个函数的最后一个参数是以 ...T
为标记,则该函数的最后一个参数可以接受任何数量类型为 T
的参数。
请注意,一个函数仅最后一个参数允许参数可变
示例和理解变参函数如何工作
你是否想过被用来 append 变量到一个 slice
的 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
函数。
在第10行,for
循环将在遍历 nums
切片如果它在切片中出现,打印 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
是一个 nil
切片,长度和容量都是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
期望的可变整型参数。在这个例子中 nums
已经是一个整型的切片同时一个新的 []int 切片尝试使用 nums
创建。即编译器尝试
find(89, []int{nums})
将失败,因为 nums
是一个 []int
而不是 int
。
那么是否有一种方式可以给可变参数传递一个切片呢?答案当然是肯定的。
有一个语法糖可以被用来给变参函数传递一个切片。你必须将切片以 ...
为后缀,如果这样做后,切片会被直接传递给函数而不创建一个新的切片。
在上面的程序中,如果你使用 find(89, nums...)
替换23行的 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)
}
我会把它作为练习让你弄清楚上面的程序是如何工作的:).
这些是变参函数,感谢阅读,请留下您宝贵的反馈和意见。
下一个教程 Maps