如何在 Go 中使用变量函数

介绍

变量函数是一个函数,它接受零、一个或多个值作为一个单一的参数,而变量函数不是常见的例子,但它们可以用来使代码更加清洁和可读。

变量函数比它们看起来更常见,最常见的函数是来自 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 中定义和调用函数

Published At
Categories with 技术
Tagged with
comments powered by Disqus