Go: вариационная функция и слишком много аргументов?

Вот пример проблемы, с которой я сталкиваюсь:

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).


person Mediocre Gopher    schedule 22.09.2013    source источник


Ответы (2)


В среде выполнения Go функция с переменным числом аргументов реализована так, как если бы она имела дополнительный параметр среза. в конце вместо вариативного параметра.

Например:

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)

Теоретически Go может справиться со случаем, когда некоторые переменные аргументы указываются напрямую, а остальные расширяются из массива/среза. Но это было бы неэффективно.

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

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

Вы можете видеть, что Go в любом случае создаст копию b. Идея Go — быть как можно меньше, но при этом полезно . Таким образом, небольшие функции, подобные этой, отбрасываются. Вы можете возразить в пользу этого синтаксического сахара, так как его можно реализовать немного эффективнее, чем прямолинейный подход append.

Обратите внимание, что расширение фрагмента с помощью ... не создает копию базового массива для использования в качестве параметра . Параметр просто псевдоним переменной. Другими словами, это действительно эффективно.

person deft_code    schedule 22.09.2013
comment
Переменная b выглядит как срез, а не как массив. - person eonil; 26.10.2013
comment
Хороший улов. Слишком легко нарушить правило Не называйте срез массивом! - person deft_code; 29.10.2013
comment
При расширении среза структура среза, конечно, копируется, но базовый массив не копируется. Просто чтобы быть точным. - person PickBoy; 15.01.2017

Вы можете сделать что-то вроде этого:

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...

person creack    schedule 22.09.2013