函数通常只接受固定数量的参数。可变参数函数是接受可变数量参数的函数。如果函数定义的最后一个参数以省略号*…*为前缀,则该函数可以接受该参数的任意数量的参数。
只有函数的最后一个参数可以是可变参数。我们将在本教程的下一部分中了解为什么会出现这种情况。
func hello(a int, b ...int) {
}
在上面的函数中,参数b
是可变参数,因为它以省略号为前缀,并且可以接受任意数量的参数。可以使用语法调用该函数。
hello(1, 2)
hello(5, 6, 7, 8, 9)
在上面的代码中, 我们调用hello
函数数字1
作为第一个参数传递给a
,数字2
作为第二个参数传递给b
,
并在下一行中b
传递四个参数6, 7, 8, 9
。
也可以将不传递参数给可变参数函数。
hello(1)
在上面的代码中,我们hello
使用零参数调用b
. 这完全没问题。
现在我想你应该明白为什么可变参数只能放在最后。
让我们尝试将hello
函数的第一个参数设为可变参数。
语法看起来像
func hello(b ...int, a int) {
}
在上面的函数中,不可能将参数传递给a
参数,因为我们传递的任何参数都将被分配给第一个b
参数,因为它是可变参数。
因此,可变参数只能出现在函数定义的最后。上面的函数将无法编译并出现错误
syntax error: cannot use ... with non-final parameter b
让我们创建自己的可变参数函数。我们将编写一个简单的程序来查找整数输入列表中是否存在整数。
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)
}
Run in Playground
在上面的程序中,在函数func find(num int, nums ...int)
参数接受可变数量的参数nums
类型是 []int
整数切片。
可变参数函数的工作方式是将可变数量的参数转换为可变参数类型的切片。
例如,在上面程序中,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 []
在上面的程序中,最后一次调用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() {
find(89, []int{89, 90, 95})
find(45, []int{56, 67, 45, 90, 109})
find(78, []int{38, 56, 98})
find(87, []int{})
}
Run in Playground
以下是使用可变参数而不是切片的优点。
find
,我们创建一个空切片只是为了满足函数的签名find
。对于可变参数函数来说,这是完全不需要的。find(87)
仅当使用可变参数函数时才 可以使用此行。您是否想知道标准库中的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() {
nums := []int{89, 90, 95}
find(89, nums)
}
Run in Playground
在上述程序中,我们将一个切片传递给一个需要可变数量参数的函数。
这是行不通的。上述程序将因编译错误而失败./prog.go:23:10: cannot use nums (type []int) as type int in argument to find
为什么这不起作用?嗯,这很简单。find
下面提供了函数的签名,
func find(num int, nums ...int)
根据可变参数函数的定义,nums ...int
意味着它将接受可变数量的类型参数int
。
上面程序中,nums
即 []int slice 被传递给find
需要可变int
参数的函数。正如我们已经讨论过的,这些可变参数将被转换为类型切片,int
因为find
需要可变参数 int 参数。在这种情况下,nums
已经是一个[]int
切片,编译器尝试创建一个新的切片[]int
,即编译器尝试执行的操作
find(89, []int{nums})
这将会失败,因为nums
is a[]int
而不是 a int
。
那么有没有办法将切片传递给可变参数函数呢?答案是肯定的。
有一个语法糖可用于将切片传递给可变参数函数。您必须在切片后加上省略号。...
如果这样做,切片将直接传递给函数,而不会创建新切片。
在上面的程序中,如果您将上面的程序替换成find(89, nums...)
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...)
}
Run in Playground
上面程序输出
type of nums is []int
89 found at index 0 in [89 90 95]
当您修改可变参数函数内的切片时,请确保您知道自己在做什么。
让我们看一个简单的例子。
package main
import (
"fmt"
)
func change(s ...string) {
s[0] = "Go"
}
func main() {
welcome := []string{"hello", "world"}
change(welcome...)
fmt.Println(welcome)
}
Run in Playground
你认为上述程序的输出会是什么?如果您认为这会输出[Go world]`那么恭喜您!您已经了解了可变参数函数和切片。如果你错了,也没什么大不了的,让我解释一下我们是如何得到这个输出的。
我们使用语法糖...
并将切片作为可变参数传递给函数change
。
正如我们已经讨论过的,如果...
使用,welcome
切片本身将作为参数传递,而不创建新切片。因此welcome
将作为参数传递给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)
}
Run in Playground
我将把它作为练习,让您弄清楚上述程序是如何工作的
[Go world playground]
[Go world]