[译] part 12: goalng 变参函数

https://juejin.im/post/5cb83e8de51d456e6154b423

什么是变参函数

变参函数是一个可以接受参数数量可变的函数。

语法

如果函数的最后一个参数用...T表示,那么该函数最后一个参数可以接受任意数量的T类型的参数。

注意,只允许函数的最后一个参数为可变参数。

示例

append函数可以增加任意数量的参数给切片。变参函数就是用到了切片的这个原理。

  1. func append(slice []Type, elems ...Type) []Type

以上是append函数的定义。在这个定义中,elems是一个可变参数。通过这个语法,append可以接受可变数量的参数。

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

  1. package main
  2. import (
  3. "fmt"
  4. )
  5. func find(num int, nums ...int) {
  6. fmt.Printf("type of nums is %T\n", nums)
  7. found := false
  8. for i, v := range nums {
  9. if v == num {
  10. fmt.Println(num, "found at index", i, "in", nums)
  11. found = true
  12. }
  13. }
  14. if !found {
  15. fmt.Println(num, "not found in ", nums)
  16. }
  17. fmt.Printf("\n")
  18. }
  19. func main() {
  20. find(89, 89, 90, 95)
  21. find(45, 56, 67, 45, 90, 109)
  22. find(78, 38, 56, 98)
  23. find(87)
  24. }

Run in playground

在上面的程序中,func find(num int, nums ...int)接收可变数量的参数nums。在函数find中,nums的类型等价于[]int,即整数切片。

变参函数的原理是将传递的可变参数转换为一个新切片。例如,在上面程序的第 22 中,find函数的可变参数是 89, 90, 95。find函数需要一个可变的int参数。因此,这三个参数将由编译器转换为 int 型的[] int {89,90,95}切片,然后它将被传递给find函数。

上述程序将打印,

  1. type of nums is []int
  2. 89 found at index 0 in [89 90 95]
  3. type of nums is []int
  4. 45 found at index 2 in [56 67 45 90 109]
  5. type of nums is []int
  6. 78 not found in [38 56 98]
  7. type of nums is []int
  8. 87 not found in []

上面程序的第 25 行,find函数调用只有一个参数。我们没有向可变参数传递任何值。这是完全合法的,在这种情况下,nums是一个长度和容量都为 0 的切片。

使用切片作为变参函数的参数

我们将一个切片传递给一个变参函数,并看看下面的例子会发生什么。

  1. package main
  2. import (
  3. "fmt"
  4. )
  5. func find(num int, nums ...int) {
  6. fmt.Printf("type of nums is %T\n", nums)
  7. found := false
  8. for i, v := range nums {
  9. if v == num {
  10. fmt.Println(num, "found at index", i, "in", nums)
  11. found = true
  12. }
  13. }
  14. if !found {
  15. fmt.Println(num, "not found in ", nums)
  16. }
  17. fmt.Printf("\n")
  18. }
  19. func main() {
  20. nums := []int{89, 90, 95}
  21. find(89, nums)
  22. }

Run in playground

在第 23 行,我们将一个切片传递给一个变参函数。

上面的程序将会出现编译错误main.go:23: cannot use nums (type []int) as type int in argument to find

为什么这个例子不能运行呢?因为find函数的签名如下所示,

  1. func find(num int, nums ...int)

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

在第 23 行中,nums作为可变参数传递给find函数。正如我们已经讨论过的,这些可变参数将被转换为 int 类型的切片,因为 find 需要可变的 int 参数。在这个例子中,nums已经是一个 int 切片了,并且还尝试使用 nums 去创建切片,即编译器尝试执行

  1. find(89, []int{nums})

因为 nums 是[] int而不是 int,所以会失败。

那么有没有办法将切片传递给可变参数函数?当然可以。

有一个语法糖可用于将切片传递给变参函数。就是在切片后使用...。如果这样做,切片将直接传递给函数,而不会创建新切片。

上述代码中,我们用find(89, nums...)替换find(89, nums),程序将运行并打印,

  1. type of nums is []int
  2. 89 found at index 0 in [89 90 95]

这是完整的程序供参考。

  1. package main
  2. import (
  3. "fmt"
  4. )
  5. func find(num int, nums ...int) {
  6. fmt.Printf("type of nums is %T\n", nums)
  7. found := false
  8. for i, v := range nums {
  9. if v == num {
  10. fmt.Println(num, "found at index", i, "in", nums)
  11. found = true
  12. }
  13. }
  14. if !found {
  15. fmt.Println(num, "not found in ", nums)
  16. }
  17. fmt.Printf("\n")
  18. }
  19. func main() {
  20. nums := []int{89, 90, 95}
  21. find(89, nums...)
  22. }

Run inplayground

注意事项

当你在变参函数中修改切片时,请确保知道自己在做什么。

来看个例子。

  1. package main
  2. import (
  3. "fmt"
  4. )
  5. func change(s ...string) {
  6. s[0] = "Go"
  7. }
  8. func main() {
  9. welcome := []string{"hello", "world"}
  10. change(welcome...)
  11. fmt.Println(welcome)
  12. }

Run in playground

你觉得上面的代码会输出什么?如果你觉得是 [Go world],那么恭喜你!你已经理解了变参函数和切片。如果你弄错了,没什么大不了的,让我解释一下我们为什么是这个输出。

上面程序的第 13 行,我们使用语法糖...并将切片作为可变参数传递给change函数。

正如我们已经讨论的那样,如果使用了...welcome切片本身将作为参数传递,而不会创建新的切片。因此welcome将作为参数传递给change函数。

change函数内部,切片的第一个元素更改为 Go。因此该程序输出

  1. [Go world]

这是另一个了解变参函数的程序。

  1. package main
  2. import (
  3. "fmt"
  4. )
  5. func change(s ...string) {
  6. s[0] = "Go"
  7. s = append(s, "playground")
  8. fmt.Println(s)
  9. }
  10. func main() {
  11. welcome := []string{"hello", "world"}
  12. change(welcome...)
  13. fmt.Println(welcome)
  14. }

Run in playground

这个作为练习让你弄清楚上面的程序是如何工作的:)。

ft_authoradmin  ft_create_time2019-08-03 16:34
 ft_update_time2019-08-03 16:36