了解golang的不定參數(... parameters),這一篇就夠了
在實際開發中,總有一些函數的參數個數是在編碼過程中無法確定的,比如我們最常用的fmt.Printf和fmt.Println:
fmt.Printf("一共有%v行%v列\n", rows, cols) fmt.Println("共計大小:", size)
當你需要實現類似的接口時,就需要我們的不定參數出場了。
golang的不定參數
顧名思義,不定參數就是一個占位符,你可以將1個或者多個參數賦值給這個占位符,這樣不管實際參數的數量是多少,都能交給不定參數來處理,我們看一下不定參數的聲明:
func Printf(format string, a ...interface{}) (n int, err error) func Println(a ...interface{}) (n int, err error)
不定參數使用name ...Type的形式聲明在函數的參數列表中,而且需要是參數列表的最後一個參數,這點與其他語言類似;
不定參數在函數中將轉換為對應的[]Type類型,所以我們可以像使用slice時一樣來獲取傳給函數的參數們;
有一點值得註意,golang的不定參數不需要強制綁定參數的出現。
舉個例子,我想在c語言中實現一個求和任意個整數的函數sum:
int sum(int num, ...) { // todo }
我們只有先指定至少一個非不定參數的形參(num)才能使用...不定參數,在golang中是不需要這樣做的:
func sum(nums ...int) int { //todo }
這也是golang語法簡潔的其中一個體現。
傳遞參數給...不定參數
傳遞參數給帶有不定參數的函數有兩種形式,第一種與通常的參數傳遞沒有什麽區別,拿上一節的sum舉個例子:
sum(1, 2, 3) sum(1, 2, 3, 4, 5, 6, 7, 8, 9, 10)
除了參數的個數是動態變化的之外和普通的函數調用是一致的。
第二種形式是使用...運算符以變量...的形式進行參數傳遞,這裏的變量必須是與不定參數類型相同的slice,而不能是其他類型(沒錯,數組也不可以),看個例子:
numbers := []int{1, 2, 3, 4, 5, 6, 7, 8, 9, 10} sum(numbers...) // 和sum(1, 2, 3, 4, 5, 6, 7, 8, 9. 10)等價
這種形式最常用的地方是在內置函數append裏:
result := []int{1, 3} data := []int{5, 7, 9} result = append(result, data...) // result == []int{1, 3, 5, 7, 9}
是不是和python的解包操作很像,沒錯,大部分情況下你可以把...運算符當做是golang的unpack操作,不過有幾點不同還是要註意的:
第一,只能對slice類型使用...運算符:
arr := [...]int{1, 2, 3, 4, 5} sum(arr...) // 編譯無法通過
你會見到這樣的報錯信息:cannot use arr (type [5]int) as type []int in argument to sum
這是因為不定參數實際是個slice,...運算符是個語法糖,它把前面的slice直接復制給不定參數,而不是先解包成獨立的n個參數再傳遞,這也是為什麽我只說...運算符看起來像unpack的原因。
第二個需要註意的地方是不能把獨立傳參和...運算符混用,再看個例子:
slice := []int{2, 3, 4, 5} sum(1, slice...) // 無法通過編譯
這次你會見到一個比較長的報錯:
too many arguments in call to sum have (number, []int...) want (...int)
這是和前面所說的原因是一樣的,...運算符將不定參數直接替換成了slice,這樣就導致前一個獨立給出的參數不再算入不定參數的範圍內,使得函數的參數列表從(...int)變成了(int, ...int),最終使得函數類型不匹配編譯失敗。
正確的做法也很簡單,不要混合使用...運算符給不定參數傳參即可。
讀了這篇文章,再加上一些簡單的聯系,我相信你們一定也能掌握golang不定參數的使用。
參考:
https://golang.org/ref/spec#Passing_arguments_to_..._parameters
https://golang.org/doc/effective_go.html#append
了解golang的不定參數(... parameters),這一篇就夠了