可變函式
在上篇文章裡,講解了關於函式的概念和用法,函式接收的引數數目都是確定的。而可變引數函式的引數數目是不確定的,這一節,就來講講 可變引數函式 。
可變引數函式
可變引數函式,接收可變數量的引數的函式。如果一個函式的最後一個引數的表示形如:...Type,則該引數(形參)可以接受不同數目的引數(實參)。
func f(elems ...Type) 複製程式碼
需要 注意 的一點,只允許最後一個引數是可變引數。 Go 中的一些內建函式都是可變引數函式,例如:append() 函式:
func append(slice []Type, elems ...Type) []Type 複製程式碼
在上面的函式宣告中,elems 就是可變引數,可以接收任意數目的實參。
s := []int{1,2,3} s = append(s,4,5,6,7,8) 複製程式碼
如何工作
你是不是也很想知道,為什麼可變引數函式能夠接收任意數目的引數呢? 剛開始學的時候,我也很疑惑。現在一步步給你揭開它的面紗。
func change(str1 string, s ...string) { fmt.Println(str1) fmt.Printf("%T\n",s)// %T 輸出變數型別 fmt.Println(s) } func main() { blog := "seekload.net" change(blog,"Hello","World","Go") } 複製程式碼
輸出 :
seekload.net []string [Hello World Go] 複製程式碼
從輸出結果看出,change() 函式中,引數 s 是 []string 型別的切片。
可變引數函式的工作原理就是把可變引數轉化成切片。呼叫 change() 函式時可變引數是 Hello、World、Go,這三個引數被編譯器轉化成切片 []string{"Hello","World","Go"},然後被傳入 change() 函式。
另外,呼叫 change() 函式時候,可以不傳可變引數,在 Go 語言裡也是合法的,這種情況下, s
是一個長度和容量都是 0 的 nil 切片。
其他用法
將切片傳遞給可變引數函式
func change(str1 string, s ...string) { fmt.Println(str1) fmt.Printf("%T\n",s) fmt.Println(s) } func main() { slice := []string{"Hello","World","Go"} blog := "seekload.net" change(blog,slice) } 複製程式碼
上述程式碼中,將切片 slice 傳給可變引數函式 change(),結果編譯出錯:cannot use slice (type []string) as type string in argument to change 。
原因很簡單,由可變引數函式的定義可知,s ...string 意味它可以接受 string 型別的可變引數。程式碼第 10 行,slice 作為可變引數傳入 change() 函式。前面我們知道,可變引數會被轉換為 string 型別切片然後再傳入 change() 函式中。但 slice 是一個 string 型別的切片,編譯器試圖通過下面這種方式在 slice 基礎上再建立一個切片:
change("seekload.net", []string{s}) 複製程式碼
之所以會失敗,因為 slice 是 []string 型別,而不是 string 型別。
慶幸的是,Go 提供了將切片傳入可變引數函式的語法糖:直接在切片後加上 ...
字尾。這樣,切片將直接傳入函式,不會再建立新的切片。
修改上面的程式碼:
change(blog,slice...) 複製程式碼
輸出 :
seekload.net []string [Hello World Go] 複製程式碼
前面提到一點,通過 Go 提供的語法糖將可變引數函式傳入切片,不會建立新的切片。如果在函式中改變切片的值會發生什麼呢?
func change(s ...string) { s[0] = "seekload.net" fmt.Printf("%T\n",s) fmt.Println(s) } func main() { slice := []string{"Hello","World","Go"} change(slice...) fmt.Println(slice) } 複製程式碼
輸出 :
[]string [seekload.net World Go] [seekload.net World Go] 複製程式碼
從結果可以看出,main() 函式的切片已經改變了。
為什麼會有這樣的輸出呢?程式碼第 9 行,使用了語法糖 ...
並且將切片作為可變引數傳入 change() 函式。如上面討論的,如果使用了 ...
,切片本身會作為引數直接傳入,不會再建立一個新的切片。所以在 change() 函式中,第一個元素被替換成seekload.net,會影響到 main() 函式的切片。
另外,我們也可以通過將陣列轉化成切片傳遞給可變引數函式。
func change(s ...string) { s[0] = "seekload.net" fmt.Printf("%T\n",s) fmt.Println(s) } func main() { arr := [3]string{"Hello","World","Go"} change(arr[:]...) fmt.Println(arr) } 複製程式碼
輸出結果跟上面的例子是一樣的。
其他
給大家列幾種比較常見的寫法:
func change(s ...string) { fmt.Printf("%T\n",s) fmt.Println(s) } func main() { slice1 := []string{"Hello","World","Go"} slice2 := []string{"Aa","Bb"} // 1、 change(append(slice1,"Again")...) // 2、 change(append(slice1,slice2...)...) } 複製程式碼
輸出:
[]string [Hello World Go Again] []string [Hello World Go Aa Bb] 複製程式碼
希望這篇文章對你有用!
參考:
2、Go 語言文件:...語法糖、 append() 函式
(全文完)
原創文章,若需轉載請註明出處!
歡迎掃碼關注公眾號「 Golang來了 」或者移步 www.seekload.net ,檢視更多精彩文章。
