绑定完请刷新页面
取消
刷新

分享好友

×
取消 复制
[译] part 12: goalng 变参函数
2019-04-18 09:08:59

什么是变参函数

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

语法

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

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

示例

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)
}
复制代码

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函数。

上述程序将打印,

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是一个长度和容量都为 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)
}
复制代码

Run in playground

在第 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 需要可变的 int 参数。在这个例子中,nums已经是一个 int 切片了,并且还尝试使用 nums 去创建切片,即编译器尝试执行

find(89, []int{nums}) 
复制代码

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

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

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

上述代码中,我们用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...)
}
复制代码

Run inplayground

注意事项

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

来看个例子。


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],那么恭喜你!你已经理解了变参函数和切片。如果你弄错了,没什么大不了的,让我解释一下我们为什么是这个输出。

上面程序的第 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)
}
复制代码

Run in playground

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

分享好友

分享这个小栈给你的朋友们,一起进步吧。

精选翻译文章
创建时间:2020-07-13 17:15:07
小栈内文章为翻译文章,请大家多多指点围观
展开
订阅须知

• 所有用户可根据关注领域订阅专区或所有专区

• 付费订阅:虚拟交易,一经交易不退款;若特殊情况,可3日内客服咨询

• 专区发布评论属默认订阅所评论专区(除付费小栈外)

栈主、嘉宾

查看更多
  • hawkliu
    栈主

小栈成员

查看更多
  • victoria_ltt
  • yuansj1987
戳我,来吐槽~