[奔跑的 Go] 教程十、深入学习 Go 语言的「可变参数函数」

Golang

可变函数接受无限个参数,所有这些参数都存储在切片类型的参数中。

什么是可变函数?

正如我们在 functions课程中看到的 , 函数是一段专门用于完成特定工作的代码。 函数接受一个或多个参数,并返回一个或多个值。 可变函数 也是函数,但它们也可以采用 无限 或 变量  的 参数. 听起来很蠢,但我们在slices课中看到过,当append函数接受变量参数时。

func f(elem ...Type)

可变参数函数的典型语法如上所示。...操作符被称为 打包操作符指明将所有类型为Type的参数存储在名为 elem的参数中。使用这种语法,go创建了类型为[]typeelem的变量,它是一个切片。因此,传递给这个函数的所有参数都存储在这个名为elem的切片中。

让我们来看一下append函数的例子。

append([]Type, args, arg2, argsN)

append函数要求第一个参数是类型Typeslice,之后可以有可变数量的参数。如果我们有一个切片s2我们想要添加到一个切片s1中,要如何实现呢?

append函数语法来看,我们不能传递另一个切片作为参数,它必须是一个或多个type类型的参数。因此,我们将使用解包操作符 将切片分解到一系列参数中( 以满足*append*函数的参数要求)。

append(s1, s2...)

...表示pack(打包)unpack(解包)操作符,如果省略号位于一个切片的尾部位置,则会分解这个切片。

这里 s1 和 s2 是两个相同的切片类型。通常函数的入参及其数量都是已知的。但是 **append** 函数是如何知晓其入参的数量的呢?

如果你看下 append 函数的签名,

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

你会发现 elems ...Type 指的是将所有入参变量打包进首个参数之后的 elems 切片中。

值得注意的是只允许函数的最后一个参数是可变的。

所以 append 首个参数是切片类型是因为它要求如此,但是其后的变量则会被打包进一个 elems 变量中。希望你已经清楚明了,现在我们来创建自自定义的 可变 函数。

☛ 如何创建可变函数?

如前所述,可变函数只是一个接受可变数量参数的函数。为了使函数接受可变数量的参数,我们需要使用pack(打包) 操作符... Type

解包操作符以... 结束如slice ...,而打包操作以...开头,如... Type

下面让我们来编写getMultiples函数,其第一个参数是int(整形)类型,它作为乘法因子,后面的变量参数(*名为** 可变 参数 *)实则是一个名为args的切片,这个切片的类型为int(整形)

在这个函数中,我们使用make函数创建了一个名为multiples的空切片,其长度等于args切片的长度。然后使用for range,我们将factorargs切片中的每个元素相乘并将它们填充到multiples的切片中。之后,我们返回这个进行相乘之后的切片multiples

func getMultiples(factor int, args ...int) []int {
    multiples := make([]int, len(args))

    for index, val := range args {
        multiples[index] = val * factor
    }

    return multiples
}

这很简单,我们可以像这样在 main 函数中实现这个函数:

func main() {
    s := []int{10, 20, 30}
    mult1 := getMultiples(2, s...)
    mult2 := getMultiples(3, 1, 2, 3, 4)

    fmt.Println(mult1)
    fmt.Println(mult2)
}

https://play.golang.org/p/BgU6H9orhrn

在上面的例子中,当你将切片 s 作为参数直接传递到 getMultiples 函数时,会发生什么?显然,编译器会报错 cannot use s (type []int) as type int in argument to getMultiples 因为切片的类型是 []int 而 getMultiples 函数期望的参数类型是 int

slice 是如何传递给可变参数函数的?

由于切片是对数组的引用,那么当您使用解包操作符将切片传递给可变参数函数时会发生什么? go 会创建新的切片 args 还是保持切片 s 不变呢?因为我们没有任何的工具可以来判断 args == s,所以我们需要对 args 切片本身来进行修改,以检查原始切片是否发生了改变。

https://play.golang.org/p/fbNgVGu6JZO

在上面的程序中,我们稍微修改了 getMultiples 可变参数函数,我们没有创建新的切片,而是将乘积赋值给 args 本身,用乘积元素替换传入的元素。

在上面的结果中,我们可以看到,切片 s 的值发生了改变。这意味着, go 在使用 unpack 操作符将 slice 作为参数传递给可变参数函数时,会使用其关联的底层数组构建一个新的切片。所以,请小心使用。


Practice makes perfect.

原文地址:https://medium.com/rungo/variadic-functi...

译文地址:https://learnku.com/golang/t/29192

讨论数量: 0
(= ̄ω ̄=)··· 暂无内容!

请勿发布不友善或者负能量的内容。与人为善,比聪明更重要!

社区文档:

短小精湛的 Golang 入门教程,是 The Little Go Book 的中文翻译
Github 上《The Way to Go》中文译本的镜像,优化了排版
@无闻 出品的一套面向新手级别学习者 Go 语言的视频教程
一步步带你进入 Go Web 编程的世界,让我们开始探索吧!
号称宇宙最快的 Iris Web 框架的中文文档翻译
Gin 是一个用 Go 语言编写的 WEB 框架,本文档为其中文版本
GORM 是优秀的 Golang ORM 类库,本文档是其官方文档的中文翻译,每年更新