Go: Variadic function and too many arguments?

huangapple go评论81阅读模式
英文:

Go: Variadic function and too many arguments?

问题

这是我遇到的问题的一个示例:

package main

import "fmt"

func foo(a int, b ...int) {
    fmt.Println(a, b)
}

func main() {
    a := 0
    aa := 1
    b := []int{2, 3, 4}
    foo(a, aa, b...)
}

当我运行这段代码时,我得到了错误消息 too many arguments in call to foo。我猜我可以理解为什么会出现这个错误,但我不清楚如何解决它,而又不必在 b 的开头复制一个额外的位置给 aa(我不太愿意这样做,因为这段代码会经常运行,并且 b 的长度可能相对较长)。

所以我的问题是:我是不是做错了什么?如果没有,那么最高效的解决方法是什么?

(另外,我不能更改 foo 的函数签名)。

英文:

Here's an example of the problem I'm having:

package main

import "fmt"

func foo(a int, b ...int) {
	fmt.Println(a,b)
}

func main() {
	a := 0
	aa := 1
	b := []int{2,3,4}
	foo(a, aa, b...)
}

When I run this I get the error too many arguments in call to foo. I guess I could understand why this is happening, but what's not clear to me is how I can get around it without having to make a copy of b with an extra slot at the beginning for aa (which I'd rather not do, as this code would be running quite often and with b being somewhat long).

So my question is: Am I just doing this wrong? And if not, what would be the most efficient way to do what I'm trying to do?

(Also, I can't change the signature of foo).

答案1

得分: 18

在Go运行时中,可变参数函数的实现方式是在最后添加一个额外的切片参数,而不是可变参数。

例如:

func Foo(a int, b ...int)
func FooImpl(a int, b []int)

c := 10
d := 20

// 这个调用
Foo(5, c, d)

// 实际上被实现为
b := []int{c, d}
FooImpl(5, b)

理论上,Go可以处理一些可变参数直接指定,而其余参数从数组/切片中展开的情况。但是,这样做效率不高。

// 这个调用
Foo(5, c, b...)

// 实际上被实现为
v := append([]int{c}, b...)
FooImpl(5, v)

你可以看到,Go仍然会创建b的副本。Go的理念是尽可能保持简洁,同时又能发挥作用。因此,像这样的小特性被舍弃了。你可能会认为这种语法糖更高效,因为它可以比直接使用append的方法稍微更高效地实现。

请注意,使用...展开切片并不会为参数创建底层数组的副本,参数只是对变量的别名。换句话说,这是非常高效的。

英文:

In the Go runtime a variadic function is implemented as if it had an extra slice parameter at the end instead of a variadic parameter.

For example:

func Foo( a int, b ...int )
func FooImpl( a int, b []int )

c := 10
d := 20

//This call
Foo(5, c, d)

// is implemented like this
b := []int{c, d}
FooImpl(5, b)

In theory Go could handle the case where some of a variadic arguments are specified directly and the rest are expanded out of an array/slice. But, it would not be efficient.

//This call
Foo(5, c, b...)

// would be implemented like this.
v := append([]int{c},b...)
FooImpl(5, v)

You can see that Go would be creating a copy of b anyways. The ethos of Go is to be as small as possible and yet still useful. So small features like this get dropped. You may be able to argue for this syntactic sugar, as it can be implemented slightly more efficiently than straight forward approach of append.

Note that expanding a slice with ... does not create a copy of the underlying array for use as the parameter. The parameter just aliases the variable. In other words it's really efficient.

答案2

得分: 12

你可以这样做:

package main

import "fmt"

func foo(a int, b ...int) {
    fmt.Println(a, b)
}

func main() {
    a := 0
    aa := 1
    b := []int{2, 3, 4}
    foo(a, append([]int{aa}, b...)...)
}

当期望参数为 b ...int 时,你需要传递 []int...int 类型的参数。不要混合使用 int[]int...

英文:

You can do something like this:

package main

import "fmt"

func foo(a int, b ...int) {
    fmt.Println(a,b)
}

func main() {
    a := 0
    aa := 1
    b := []int{2,3,4}
    foo(a, append([]int{aa}, b...)...)
}

When expecting b ...int, you need to pass a []int... or int's as parameters. Don't can't mix int and []int...

huangapple
  • 本文由 发表于 2013年9月23日 01:42:29
  • 转载请务必保留本文链接:https://go.coder-hub.com/18946837.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定