天天看點

可變函數

可變函數

在上篇文章裡,講解了關于函數的概念和用法,函數接收的參數數目都是确定的。這一節,來講講可變參數函數 。

可變參數函數

可變參數函數,接收可變數量的參數的函數。如果一個函數的最後一個參數的表示形如:…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]      

最後,19 年我們為了以前吹過的牛逼,一起努力,相信美好的生活就在前方!請大家也多多關注的部落格,以後會持續更新的,點選下方閱讀原文,跳轉到我的個人部落格。如果覺得我的分享不錯,不妨分享出去讓更多的人看到。

練習時間:

給大家出了個小作業,下面的代碼會輸出什麼?大家可以點選下方留言留下自己的答案,鍛煉下自己的學習成果!

func change(s ...int) {         s = append(s,3)         fmt.Println(s)     }     func main() {         slice := make([]int,5,5)         slice[0] = 1         slice[1] = 2         change(slice...)         fmt.Println(slice)         change(slice[0:2]...)         fmt.Println(slice)     }      

希望這篇文章給你帶來收獲,Good Day !

如果我的文章對你有所幫助,點贊、轉發都是一種支援!

可變函數
可變函數
可變函數