Go 經(jīng)典入門(mén)系列 12:可變參數(shù)函數(shù)
歡迎來(lái)到 Golang 系列教程第 12 部分。
什么是可變參數(shù)函數(shù)
可變參數(shù)函數(shù)是一種參數(shù)個(gè)數(shù)可變的函數(shù)。
語(yǔ)法
如果函數(shù)最后一個(gè)參數(shù)被記作 ...T,這時(shí)函數(shù)可以接受任意個(gè) T 類(lèi)型參數(shù)作為最后一個(gè)參數(shù)。
請(qǐng)注意只有函數(shù)的最后一個(gè)參數(shù)才允許是可變的。
通過(guò)一些例子理解可變參數(shù)函數(shù)如何工作
你是否曾經(jīng)想過(guò) append 函數(shù)是如何將任意個(gè)參數(shù)值加入到切片中的。這樣 append 函數(shù)可以接受不同數(shù)量的參數(shù)。
func?append(slice?[]Type,?elems?...Type)?[]Type
上面是 append 函數(shù)的定義。在定義中 elems 是可變參數(shù)。這樣 append 函數(shù)可以接受可變化的參數(shù)。
讓我們創(chuàng)建一個(gè)我們自己的可變參數(shù)函數(shù)。我們將寫(xiě)一段簡(jiǎn)單的程序,在輸入的整數(shù)列表里查找某個(gè)整數(shù)是否存在。
package?main
import?(
?"fmt"
)
func?find(num?int,?nums?...int)?{
?fmt.Printf("type?of?nums?is?%T\n",?nums)
?found?:=?false
?for?i,?v?:=?range?nums?{
??if?v?==?num?{
???fmt.Println(num,?"found?at?index",?i,?"in",?nums)
???found?=?true
??}
?}
?if?!found?{
??fmt.Println(num,?"not?found?in?",?nums)
?}
?fmt.Printf("\n")
}
func?main()?{
?find(89,?89,?90,?95)
?find(45,?56,?67,?45,?90,?109)
?find(78,?38,?56,?98)
?find(87)
}
在線運(yùn)行代碼[1]
在上面程序中 func find(num int, nums ...int) ?中的 nums 可接受任意數(shù)量的參數(shù)。在 find 函數(shù)中,參數(shù) nums 相當(dāng)于一個(gè)整型切片。
可變參數(shù)函數(shù)的工作原理是把可變參數(shù)轉(zhuǎn)換為一個(gè)新的切片。以上面程序中的第 22 行為例,find 函數(shù)中的可變參數(shù)是 89,90,95 。find 函數(shù)接受一個(gè) int 類(lèi)型的可變參數(shù)。因此這三個(gè)參數(shù)被編譯器轉(zhuǎn)換為一個(gè) int 類(lèi)型切片 int []int{89, 90, 95} 然后被傳入 find 函數(shù)。
在第 10 行, for 循環(huán)遍歷 nums 切片,如果 num 在切片中,則打印 num 的位置。如果 num 不在切片中,則打印提示未找到該數(shù)字。
上面代碼的輸出值如下,
type?of?nums?is?[]int
89?found?at?index?0?in?[89?90?95]
type?of?nums?is?[]int
45?found?at?index?2?in?[56?67?45?90?109]
type?of?nums?is?[]int
78?not?found?in??[38?56?98]
type?of?nums?is?[]int
87?not?found?in??[]
在上面程序的第 25 行,find 函數(shù)僅有一個(gè)參數(shù)。我們沒(méi)有給可變參數(shù) nums ...int 傳入任何參數(shù)。這也是合法的,在這種情況下 nums 是一個(gè)長(zhǎng)度和容量為 0 的 nil 切片。
給可變參數(shù)函數(shù)傳入切片
下面例子中,我們給可變參數(shù)函數(shù)傳入一個(gè)切片,看看會(huì)發(fā)生什么。
package?main
import?(
?"fmt"
)
func?find(num?int,?nums?...int)?{
?fmt.Printf("type?of?nums?is?%T\n",?nums)
?found?:=?false
?for?i,?v?:=?range?nums?{
??if?v?==?num?{
???fmt.Println(num,?"found?at?index",?i,?"in",?nums)
???found?=?true
??}
?}
?if?!found?{
??fmt.Println(num,?"not?found?in?",?nums)
?}
?fmt.Printf("\n")
}
func?main()?{
?nums?:=?[]int{89,?90,?95}
?find(89,?nums)
}
在線運(yùn)行代碼[2]
在第 23 行中,我們將一個(gè)切片傳給一個(gè)可變參數(shù)函數(shù)。
這種情況下無(wú)法通過(guò)編譯,編譯器報(bào)出錯(cuò)誤 main.go:23: cannot use nums (type []int) as type int in argument to find 。
為什么無(wú)法工作呢?原因很直接,find 函數(shù)的說(shuō)明如下,
func?find(num?int,?nums?...int)
由可變參數(shù)函數(shù)的定義可知,nums ...int 意味它可以接受 int 類(lèi)型的可變參數(shù)。
在上面程序的第 23 行,nums 作為可變參數(shù)傳入 find 函數(shù)。前面我們知道,這些可變參數(shù)參數(shù)會(huì)被轉(zhuǎn)換為 int 類(lèi)型切片然后在傳入 find 函數(shù)中。但是在這里 nums 已經(jīng)是一個(gè) int 類(lèi)型切片,編譯器試圖在 nums 基礎(chǔ)上再創(chuàng)建一個(gè)切片,像下面這樣
find(89,?[]int{nums})
這里之所以會(huì)失敗是因?yàn)?nums 是一個(gè) []int類(lèi)型 而不是 int類(lèi)型。
那么有沒(méi)有辦法給可變參數(shù)函數(shù)傳入切片參數(shù)呢?答案是肯定的。
有一個(gè)可以直接將切片傳入可變參數(shù)函數(shù)的語(yǔ)法糖,你可以在在切片后加上 ... 后綴。如果這樣做,切片將直接傳入函數(shù),不再創(chuàng)建新的切片
在上面的程序中,如果你將第 23 行的 find(89, nums) 替換為 find(89, nums...) ,程序?qū)⒊晒幾g并有如下輸出
type?of?nums?is?[]int
89?found?at?index?0?in?[89?90?95]
下面是完整的程序供您參考。
package?main
import?(
?"fmt"
)
func?find(num?int,?nums?...int)?{
?fmt.Printf("type?of?nums?is?%T\n",?nums)
?found?:=?false
?for?i,?v?:=?range?nums?{
??if?v?==?num?{
???fmt.Println(num,?"found?at?index",?i,?"in",?nums)
???found?=?true
??}
?}
?if?!found?{
??fmt.Println(num,?"not?found?in?",?nums)
?}
?fmt.Printf("\n")
}
func?main()?{
?nums?:=?[]int{89,?90,?95}
?find(89,?nums...)
}
在線運(yùn)行代碼[3]
不直觀的錯(cuò)誤
當(dāng)你修改可變參數(shù)函數(shù)中的切片時(shí),請(qǐng)確保你知道你正在做什么。
下面讓我們來(lái)看一個(gè)簡(jiǎn)單的例子。
package?main
import?(
?"fmt"
)
func?change(s?...string)?{
?s[0]?=?"Go"
}
func?main()?{
?welcome?:=?[]string{"hello",?"world"}
?change(welcome...)
?fmt.Println(welcome)
}
在線運(yùn)行代碼[4]
你認(rèn)為這段代碼將輸出什么呢?如果你認(rèn)為它輸出 [Go world] 。恭喜你!你已經(jīng)理解了可變參數(shù)函數(shù)和切片。如果你猜錯(cuò)了,那也不要緊,讓我來(lái)解釋下為什么會(huì)有這樣的輸出。
在第 13 行,我們使用了語(yǔ)法糖 ... 并且將切片作為可變參數(shù)傳入 change 函數(shù)。
正如前面我們所討論的,如果使用了 ... ,welcome 切片本身會(huì)作為參數(shù)直接傳入,不需要再創(chuàng)建一個(gè)新的切片。這樣參數(shù) welcome 將作為參數(shù)傳入 change 函數(shù)
在 change 函數(shù)中,切片的第一個(gè)元素被替換成 Go,這樣程序產(chǎn)生了下面的輸出值
[Go?world]
這里還有一個(gè)例子來(lái)理解可變參數(shù)函數(shù)。
package?main
import?(
?"fmt"
)
func?change(s?...string)?{
?s[0]?=?"Go"
?s?=?append(s,?"playground")
?fmt.Println(s)
}
func?main()?{
?welcome?:=?[]string{"hello",?"world"}
?change(welcome...)
?fmt.Println(welcome)
}
在線運(yùn)行代碼[5]
我將把它作為一個(gè)練習(xí)留個(gè)你,請(qǐng)你指出上面的程序是如何運(yùn)行的 :) 。
以上就是關(guān)于可變參數(shù)函數(shù)的介紹。感謝閱讀。歡迎您留下有價(jià)值的反饋和意見(jiàn)。祝您生活愉快。
下一教程 - Maps[6]
via: https://golangbot.com/variadic-functions/
作者:Nick Coghlan[7]譯者:fengchunsgit[8]校對(duì):Noluye[9]
本文由 GCTT[10] 原創(chuàng)編譯,Go 中文網(wǎng)[11] 榮譽(yù)推出
參考資料
在線運(yùn)行代碼: https://play.golang.org/p/7occymiS6s
[2]在線運(yùn)行代碼: https://play.golang.org/p/7occymiS6s
[3]在線運(yùn)行代碼: https://play.golang.org/p/7occymiS6s
[4]在線運(yùn)行代碼: https://play.golang.org/p/7occymiS6s
[5]在線運(yùn)行代碼: https://play.golang.org/p/7occymiS6s
[6]Maps: https://studygolang.com/articles/12251
[7]Nick Coghlan: https://golangbot.com/about/
[8]fengchunsgit: https://github.com/fengchunsgit
[9]Noluye: https://github.com/Noluye
[10]GCTT: https://github.com/studygolang/GCTT
[11]Go 中文網(wǎng): https://studygolang.com/
推薦閱讀
