介绍
变量函数是一个函数,它接受零、一个或多个值作为一个单一的参数,而变量函数不是常见的例子,但它们可以用来使代码更加清洁和可读。
变量函数比它们看起来更常见,最常见的函数是来自 fmt
包的 Println
函数。
1func Println(a ...interface{}) (n int, err error)
函数具有以一组(...
)为前方的参数,被认为是一个变量函数。 意味着所提供的参数可以为零,一个或多个值。
让我们创建一个使用fmt.Println
函数并通过零、一个或多个值的程序:
1[label print.go]
2package main
3
4import "fmt"
5
6func main() {
7 fmt.Println()
8 fmt.Println("one")
9 fmt.Println("one", "two")
10 fmt.Println("one", "two", "three")
11}
第一次称呼fmt.Println
,我们没有通过任何论点;第二次称呼fmt.Println
,我们只通过一个论点,值为一
。
让我们用以下命令运行程序:
1go run print.go
我们会看到以下结果:
1[secondary_label Output]
2
3one
4one two
5one two three
输出的第一行是空的,这是因为我们没有通过任何论点,第一次被称为fmt.Println
。第二次打印了一
的值,然后是一
和两个
,最后是一
,两
和三
。
现在我们已经看到如何调用变量函数,让我们看看我们如何定义自己的变量函数。
定义变量函数
我们可以通过在参数前面的(...
)来定义一个变量函数,让我们创建一个程序,当他们的名字发送到函数时欢迎人们:
1[label hello.go]
2package main
3
4import "fmt"
5
6func main() {
7 sayHello()
8 sayHello("Sammy")
9 sayHello("Sammy", "Jessica", "Drew", "Jamie")
10}
11
12func sayHello(names ...string) {
13 for _, n := range names {
14 fmt.Printf("Hello %s\n", n)
15 }
16}
我们创建了一个sayHello
函数,该函数只使用一个名为names
的参数,该参数是变量的,因为我们在数据类型...string
之前放置一个(...
)。
因为数据类型是 string
,所以‘names’参数可以像函数体内的字符串([]string
)一样对待,我们可以用 range
操作器创建循环,并通过字符串的字符串迭代。
如果我们运行该程序,我们将获得以下输出:
1[secondary_label Output]
2Hello Sammy
3Hello Sammy
4Hello Jessica
5Hello Drew
6Hello Jamie
请注意,第一次没有任何东西被打印,我们称之为sayHello
。这是因为变量参数是字符串
的空白字符串
。
让我们修改程序以检测没有发送值:
1[label hello.go]
2package main
3
4import "fmt"
5
6func main() {
7 sayHello()
8 sayHello("Sammy")
9 sayHello("Sammy", "Jessica", "Drew", "Jamie")
10}
11
12func sayHello(names ...string) {
13 if len(names) == 0 {
14 fmt.Println("nobody to greet")
15 return
16 }
17 for _, n := range names {
18 fmt.Printf("Hello %s\n", n)
19 }
20}
现在,使用一个 [if' 语句](https://andsky.com/tech/tutorials/how-to-write-conditional-statements-in-go#if-statements),如果没有传输任何值,那么
名称的长度将是
0,我们将打印出
没有人问候`:
1[secondary_label Output]
2nobody to greet
3Hello Sammy
4Hello Sammy
5Hello Jessica
6Hello Drew
7Hello Jamie
使用变量参数可以使您的代码更易于读取. 让我们创建一个函数,将单词与指定分界器结合在一起。
1[label join.go]
2package main
3
4import "fmt"
5
6func main() {
7 var line string
8
9 line = join(",", []string{"Sammy", "Jessica", "Drew", "Jamie"})
10 fmt.Println(line)
11
12 line = join(",", []string{"Sammy", "Jessica"})
13 fmt.Println(line)
14
15 line = join(",", []string{"Sammy"})
16 fmt.Println(line)
17}
18
19func join(del string, values []string) string {
20 var line string
21 for i, v := range values {
22 line = line + v
23 if i != len(values)-1 {
24 line = line + del
25 }
26 }
27 return line
28}
在这个程序中,我们将一个字符串(,
)作为加入
函数的界限。
1[secondary_label Output]
2Sammy,Jessica,Drew,Jamie
3Sammy,Jessica
4Sammy
由于该函数作为值
参数使用一个字符串片段,所以当我们调用加入
函数时,我们不得不将所有单词包装成片段。
现在,让我们写出相同的函数,但我们会使用变量函数:
1[label join.go]
2package main
3
4import "fmt"
5
6func main() {
7 var line string
8
9 line = join(",", "Sammy", "Jessica", "Drew", "Jamie")
10 fmt.Println(line)
11
12 line = join(",", "Sammy", "Jessica")
13 fmt.Println(line)
14
15 line = join(",", "Sammy")
16 fmt.Println(line)
17}
18
19func join(del string, values ...string) string {
20 var line string
21 for i, v := range values {
22 line = line + v
23 if i != len(values)-1 {
24 line = line + del
25 }
26 }
27 return line
28}
如果我们运行该程序,我们可以看到我们得到与以前的程序相同的输出:
1[secondary_label Output]
2Sammy,Jessica,Drew,Jamie
3Sammy,Jessica
4Sammy
虽然两个版本的加入
函数在程序上做同样的事情,但在被调用时,函数的变量版本更容易读取。
变量论点顺序
函数中只能有一个变量参数,它必须是函数中最后一个定义的参数。
1[label join.go]
2package main
3
4import "fmt"
5
6func main() {
7 var line string
8
9 line = join(",", "Sammy", "Jessica", "Drew", "Jamie")
10 fmt.Println(line)
11
12 line = join(",", "Sammy", "Jessica")
13 fmt.Println(line)
14
15 line = join(",", "Sammy")
16 fmt.Println(line)
17}
18
19func join(values ...string, del string) string {
20 var line string
21 for i, v := range values {
22 line = line + v
23 if i != len(values)-1 {
24 line = line + del
25 }
26 }
27 return line
28}
这一次,我们将值
参数放在加入
函数中的第一位,这会导致以下编译错误:
1[secondary_label Output]
2./join_error.go:18:11: syntax error: cannot use ... with non-final parameter values
在定义任何变量函数时,只有最后一个参数才能变量。
爆炸论点
到目前为止,我们已经看到,我们可以将零、一个或多个值传递给一个变量函数,但是,有时我们有一个值片段,我们想将它们发送到一个变量函数。
让我们从最后一节看一下我们的加入
功能,看看会发生什么:
1[label join.go]
2package main
3
4import "fmt"
5
6func main() {
7 var line string
8
9 names := []string{"Sammy", "Jessica", "Drew", "Jamie"}
10
11 line = join(",", names)
12 fmt.Println(line)
13}
14
15func join(del string, values ...string) string {
16 var line string
17 for i, v := range values {
18 line = line + v
19 if i != len(values)-1 {
20 line = line + del
21 }
22 }
23 return line
24}
如果我们运行此程序,我们会收到编译错误:
1[secondary_label Output]
2./join-error.go:10:14: cannot use names (type []string) as type string in argument to join
尽管变量函数将值...字符串
的参数转换为字符串的片段,字符串
,但我们不能将字符串的片段作为参数。
为了处理这个问题,我们可以通过用一组圆形(...
)来补充它并将其转化为分散的参数,这些参数将被转化为变量函数:
1[label join.go]
2package main
3
4import "fmt"
5
6func main() {
7 var line string
8
9 names := []string{"Sammy", "Jessica", "Drew", "Jamie"}
10
11 line = join(",", names...)
12 fmt.Println(line)
13}
14
15func join(del string, values ...string) string {
16 var line string
17 for i, v := range values {
18 line = line + v
19 if i != len(values)-1 {
20 line = line + del
21 }
22 }
23 return line
24}
这一次,当我们调用连接
函数时,我们通过附加(‘......’)来爆炸了名称
片段。
这使得该计划现在可以按预期运行:
1[secondary_label Output]
2Sammy,Jessica,Drew,Jamie
重要的是要注意,我们仍然可以通过一个零,一个或多个论点,以及我们爆炸的片段。
1[label join.go]
2package main
3
4import "fmt"
5
6func main() {
7 var line string
8
9 line = join(",", []string{"Sammy", "Jessica", "Drew", "Jamie"}...)
10 fmt.Println(line)
11
12 line = join(",", "Sammy", "Jessica", "Drew", "Jamie")
13 fmt.Println(line)
14
15 line = join(",", "Sammy", "Jessica")
16 fmt.Println(line)
17
18 line = join(",", "Sammy")
19 fmt.Println(line)
20
21}
22
23func join(del string, values ...string) string {
24 var line string
25 for i, v := range values {
26 line = line + v
27 if i != len(values)-1 {
28 line = line + del
29 }
30 }
31 return line
32}
1[secondary_label Output]
2Sammy,Jessica,Drew,Jamie
3Sammy,Jessica,Drew,Jamie
4Sammy,Jessica
5Sammy
我们现在知道如何将零、一个或许多论点以及我们爆炸的片段传递给变量函数。
结论
在本教程中,我们已经看到变量函数如何使您的代码更清洁,虽然您不需要总是使用它们,但您可能会发现它们有用:
- 如果你发现你正在创建一个临时片段只是为了传递到一个函数。
- 当输入参数的数量未知或在调用时会有所不同时。
若要了解有关创建和调用函数的更多信息,请参阅 如何在 Go 中定义和调用函数。