2013-09-22 54 views
9

Dưới đây là một ví dụ về vấn đề tôi đang gặp:Go: Chức năng biến thể và quá nhiều đối số?

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

Khi tôi chạy này, tôi nhận được lỗi too many arguments in call to foo. Tôi đoán tôi có thể hiểu lý do tại sao điều này xảy ra, nhưng điều không rõ ràng đối với tôi là làm thế nào tôi có thể thực hiện nó mà không cần phải tạo bản sao b với một khe phụ ở đầu cho aa (mà tôi không muốn làm, mã này sẽ chạy khá thường xuyên và với b là hơi dài).

Vì vậy, câu hỏi của tôi là: Tôi chỉ làm điều này sai? Và nếu không, điều gì sẽ là cách hiệu quả nhất để làm những gì tôi đang cố gắng làm?

(Ngoài ra, tôi không thể thay đổi chữ ký của foo).

Trả lời

10

Trong thời gian chạy, hàm variadic là implemented như thể nó có thông số bổ sung ở cuối thay vì tham số variadic.

Ví dụ:

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) 

Về lý thuyết Go có thể xử lý các trường hợp một số một đối số variadic được quy định trực tiếp và phần còn lại được mở rộng ra của một mảng/lát. Nhưng, nó sẽ không hiệu quả.

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

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

Bạn có thể thấy rằng Go sẽ tạo bản sao của b dù sao. The ethos of Go is to be as small as possible and yet still useful. Vì vậy, các tính năng nhỏ như thế này được giảm xuống. Bạn có thể tranh luận về đường cú pháp này, vì nó có thể được thực hiện hiệu quả hơn một chút so với cách tiếp cận thẳng về phía trước của append.

Lưu ý rằng expanding a slice with ... does not create a copy of the underlying array để sử dụng làm thông số. Tham số chỉ bí danh biến. Nói cách khác nó thực sự hiệu quả.

+0

Biến 'b' trông giống như một lát thay vì một mảng. – Eonil

+0

Bắt tốt. Nó quá dễ dàng để phá vỡ quy tắc "Bạn không nên gọi một lát một mảng!" –

+0

Khi mở rộng một lát, cấu trúc lát được sao chép tất nhiên, nhưng mảng cơ bản không được sao chép. Chỉ để được chính xác. – PickBoy

7

Bạn có thể làm một cái gì đó như thế này:

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

Khi mong b ...int, bạn cần phải vượt qua một []int... hoặc int 's như thông số. Không thể kết hợp int[]int...

Các vấn đề liên quan