12. 可變參數(shù)函數(shù)
什么是可變參數(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)
}
在上面程序中 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)
}
在第 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...)
}
不直觀(guān)的錯(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)
}
你認(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)
}