Go 的切片類似於其他語言的陣列。 切片可以存放多個特定型別(或介面)的元素。
Go 的切片是以陣列為基礎。 陣列的大小是固定的。 相對地,切片則是動態調整大小、具彈性的陣列元素檢視。
切片的寫法類似[]T,其中T是切片中元素的型別:
var empty []int // an empty slice
withData := []int{0,1,2,3,4,5} // a slice pre-filled with some data
你可以使用方括號語法,取得或設定指定以 0 為起算索引的元素:
withData[1] = 5
x := withData[1] // x is now 5
你可以從現有的切片取得一段元素範圍,建立新的切片。 同樣是使用方括號語法,但要同時指定起始索引(含)與結束索引(不含)。 如果沒有指定起始索引,預設為 0。 如果沒有指定結束索引,預設為切片的長度。
newSlice := withData[2:4]
// => []int{2,3}
newSlice := withData[:2]
// => []int{0,1}
newSlice := withData[2:]
// => []int{2,3,4,5}
newSlice := withData[:]
// => []int{0,1,2,3,4,5}
你可以使用append函式將元素加入切片。
下面我們把4和2加入a切片。
a := []int{1, 3}
a = append(a, 4, 2)
// => []int{1,3,4,2}
append總是會回傳一個新的切片;當我們只是想把元素加入現有的切片時,通常會像上面那樣,把它重新指定回我們當作第一個引數傳入的切片變數。
append也可以用來合併兩個切片:
nextSlice := []int{100,101,102}
newSlice := append(withData, nextSlice...)
// => []int{0,1,2,3,4,5,100,101,102}
一般來說,Go 的函式只接受固定數量的引數。 不過,在 Go 中也可以撰寫可變參數函式。
可變參數函式是指能接受數量不定之引數的函式。
如果函式定義中最後一個參數的型別前面加上省略符號...,那麼這個函式就能針對該參數接受任意數量的引數。
func find(a int, b ...int) {
// ...
}
在上面的函式中,參數b是可變參數,我們可以傳入 0 個或多個引數給b。
find(5, 6)
find(5, 6, 7)
find(5)
可變參數必須是函式的最後一個參數。
可變參數函式的運作方式,是把數量不定的引數轉換成一個型別為該可變參數型別的切片。
以下是可變參數函式實作的範例。
func find(num int, nums ...int) {
fmt.Printf("type of nums is %T\n", nums)
for i, v := range nums {
if v == num {
fmt.Println(num, "found at index", i, "in", nums)
return
}
}
fmt.Println(num, "not found in ", nums)
}
func main() {
find(89, 90, 91, 95)
// =>
// type of nums is []int
// 89 not found in [90 91 95]
find(45, 56, 67, 45, 90, 109)
// =>
// type of nums is []int
// 45 found at index 2 in [56 67 45 90 109]
find(87)
// =>
// type of nums is []int
// 87 not found in []
}
在上面程式中的find(89, 90, 91, 95)這一行,傳給 find 函式的數量不定引數是90、91和95。
find函式預期num之後有一個可變的 int 參數。
因此,編譯器會把這三個引數轉換成型別為int的切片[]int{90, 91, 95},然後以nums傳給 find 函式。
有時候你已經有一個切片,想把它傳給可變參數函式。
這可以透過在切片後面加上...來達成。
這會告訴編譯器在可變參數函式中直接使用該切片。
在這種情況下,前面提到建立切片的步驟會直接省略。
list := []int{1, 2, 3}
find(1, list...) // "find" defined as shown above
身為準魔術師,Elyse 需要練習一些基本功。她有一疊卡片,想要加以操作。
為了方便一些,她只用 1 到 10 的卡片。
練習卡片時,Elyse 喜歡從整副牌中最喜歡的三張開始:2、6 和 9。請寫一個函式FavoriteCards,回傳一個依序包含這三張卡片的切片。
cards := FavoriteCards()
fmt.Println(cards)
// Output: [2 6 9]
從給定的牌堆中回傳位置index的卡片。
card := GetItem([]int{1, 2, 4, 1}, 2) // card == 4
如果索引超出範圍(也就是負數,或是超出牌堆末端),我們想回傳-1:
card := GetItem([]int{1, 2, 4, 1}, 10) // card == -1
在 Go 的慣例中,會回傳錯誤,而不是回傳「超出範圍」的值。這裡當預期是正整數時,「超出範圍」的值是-1。回傳錯誤時,通常會一併回傳 zero value 與錯誤。如何回傳錯誤並附上正確的回傳值,會在之後的練習中介紹。
將位置index的卡片替換成提供的新卡片,並回傳調整後的牌堆。請注意,這會修改傳入的切片,而這是預期的行為。
index := 2
newCard := 6
cards := SetItem([]int{1, 2, 4, 1}, index, newCard)
fmt.Println(cards)
// Output: [1 2 6 1]
如果索引超出範圍(也就是負數,或是超出牌堆末端),我們想把新卡片加到牌堆末端:
index := -1
newCard := 6
cards := SetItem([]int{1, 2, 4, 1}, index, newCard)
fmt.Println(cards)
// Output: [1 2 4 1 6]
將value參數指定的卡片加到牌堆頂端。
slice := []int{3, 2, 6, 4, 8}
cards := PrependItems(slice, 5, 1)
fmt.Println(cards)
// Output: [5 1 3 2 6 4 8]
如果沒有給value參數任何引數,結果就會等於原始的切片。
slice := []int{3, 2, 6, 4, 8}
cards := PrependItems(slice)
fmt.Println(cards)
// Output: [3 2 6 4 8]
從牌堆中移除位置index的卡片,並回傳牌堆。請注意,這可能會修改傳入的切片,這沒有問題。
cards := RemoveItem([]int{3, 2, 6, 4, 8}, 2)
fmt.Println(cards)
// Output: [3 2 4 8]
如果索引超出範圍(也就是負數,或是超出牌堆末端),我們想讓牌堆保持不變:
cards := RemoveItem([]int{3, 2, 6, 4, 8}, 11)
fmt.Println(cards)
// Output: [3 2 6 4 8]