跳到主要内容

11.可变函数

什么是可变函数?

函数通常只接受固定数量的参数。可变参数函数是接受可变数量参数的函数。如果函数定义的最后一个参数以省略号 ... 为前缀,则该函数可以接受该参数的任意数量的参数。

只有函数的最后一个参数可以是可变的。我们将在本教程的下一节中了解为什么会出现这种情况。

语法

func hello(a int, b ...int) {  
}

在上面的函数中,参数 b 是可变的,因为它以省略号为前缀,并且可以接受任意数量的参数.

hello(1, 2) //passing one argument "2" to b  
hello(5, 6, 7, 8, 9) //passing arguments "6, 7, 8 and 9" to b

在上面的代码中,第一行,我们用一个参数 2 作为参数传递给 b ,下一行将四个参数 6, 7, 8, 9 作为参数传递给 b 。

也可以将零参数传递给可变参数函数。hello(1)

到现在为止,我想你应该已经明白为什么可变参数应该只放在最后了。

示例和理解可变参数函数的工作原理

让我们创建自己的可变参数函数。我们将编写一个简单的程序来查找整数输入列表中是否存在整数。

package main

import (
"fmt"
)

func find(num int, nums ...int) { // 7
fmt.Printf("type of nums is %T\n", nums)
found := false
for i, v := range nums { //10
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) //25
}

在上面的程序中,第 7 行的 func find(num int, nums ...int) 为 nums 参数接受可变数量的参数。在函数 find 中, nums 的类型是 []int ,即整数切片

可变参数函数的工作方式是将可变数量的参数转换为可变参数类型的一部分。例如,在行号。上面程序的第 22 行中, find 函数的可变参数数量为 89、90、95。查找函数需要一个可变的 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 将是长度和容量为 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() {
find(89, []int{89, 90, 95})
find(45, []int{56, 67, 45, 90, 109})
find(78, []int{38, 56, 98})
find(87, []int{})
}

以下是使用可变参数而不是切片的优点。

无需在每次函数调用期间创建切片。如果你看一下上面的程序,我们在第 10 行的每个函数调用期间都创建了新的切片。 22、23、24 和 25。使用可变函数时可以避免创建额外的切片

在上面程序的第 25 行中,我们创建了一个空切片来满足 find 函数的签名。这在可变参数函数的情况下完全不需要。当使用可变函数时,这一行可以只是 find(87) 。

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() {
nums := []int{89, 90, 95}
find(89, nums) //23
}

在行号23,我们将一个切片传递给一个需要可变数量参数的函数。

这是行不通的。上面的程序将失败并出现编译错误 ./prog.go:23:10: cannot use nums (type []int) as type int in argument to find

为什么这不起作用?好吧,这很简单。 find 函数的签名如下所示,

根据可变参数函数的定义, nums ...int 意味着它将接受可变数量的 int 类型的参数。

在行号上面程序的第 23 行中, nums 是 []int 切片被传递给 find 函数,它需要一个可变的 int 参数。正如我们已经讨论过的,这些可变参数将被转换为 int 类型的切片,因为 find 需要可变 int 参数。在这种情况下, nums 已经是一个 []int 切片,编译器尝试创建一个新的 []int 即编译器尝试做

这将失败,因为 nums 是 []int 而不是 int 。

那么有没有办法将切片传递给可变参数函数呢?答案是肯定的。

有一个语法糖可用于将切片传递给可变参数函数。您必须在切片后加上省略号 ... 如果这样做,切片将直接传递给函数,而不会创建新切片

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...)
}