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