Go 語言中的兩種 slice 表示式

QF董懂發表於2019-03-25

本文只是關注於 slice 的表示方式,它們可以建立兩種型別的值:

截斷的 string

指向 array 或者 slice 的指標

Go語言對 slice有兩種表示方式:簡略表示式與完整表示式。

簡略表示式

Slice 的簡略表示式是:

1 Input[low:high]

其中,low和 high是 slice 的索引(index),其數值必須是整數,它們指定了輸入運算元(Input)的哪些元素可以被放置在結果的slice中。輸入運算元可以是 string,array,或者是指向 array或slice 的指標。結果 slice 的長度就是 high-low。如下例所示:

1 numbers := [10]int{0,1,2,3,4,5,6,7,8,9}2 s := numbers[2:4:6]3 fmt.Println(s) // [2, 3]4 fmt.Println(cap(s)) // 4

將 slice 表示式應用到 array 的指標中,這是第一次取消對該指標的引用,然後以常規的方式應用 slice 表示式。將 slice 表示式應用於陣列的指標,是先解引用該指標,然後按常規方式應用切片表示式的簡寫形式。

1 numbers := [5]int{1, 2, 3, 4, 5}2 fmt.Println((&numbers)[1:3]) // [2, 3]

Slice 的索引low 和high 可以省略,low 的預設值是0,high 的預設值為slice 的長度:

1 fmt.Println("foo"[:2]) // "fo"2 fmt.Println("foo"[1:]) // "oo"3 fmt.Println("foo"[:]) // "foo"

但是 slice 的索引不可以是以下幾種型別的值:

1 low < 0 or high < 02 low <= high3 high <= len(input)4 fmt.Println("foo"[-1:]) // invalid slice index -1 (index must be non-negative)5 //fmt.Println("foo"[:4]) // invalid slice index 4 (out of bounds for 3-byte string)6 fmt.Println("foo"[2:2]) // ""(blank)7 //fmt.Println("foo"[2:1]) // invalid slice index: 2 > 1

否則,即使一個超過slice 範圍的索引不能在編譯的時候被檢測到,那麼在執行時就會發生一個panic 。

1 func low() int {2 return 43 }4 5 func main() {6 fmt.Println("foo"[low():])7 }8 panic: runtime error: slice bounds out of range910 goroutine 1 [running]:11 panic(0x102280, 0x1040a018)12 /usr/local/go/src/runtime/panic.go:500 +0x72013 main.main()14 /tmp/sandbox685025974/main.go:12 +0x120

完整表示式這種方法可以控制結果 slice 的容量,但是隻能用於 array 和指向 array 或 slice 的指標(string 不支援),在簡略表示式中結果 slice 的容量是從索引low開始的最大可能容量(slice 的簡略表示式):

1 numbers := [10]int{0,1,2,3,4,5,6,7,8,9}2 s := numbers[1:4]3 fmt.Println(s) // [1, 2, 3]4 fmt.Println(cap(s)) // 9

對於一個 array 來說,cap(a) == len(a) 在上面的程式碼片段中,s 的容量是 9,因為這個slice 從索引 1 開始,而底層 array 有 8 個元素(2 到 9)。完整的 slice 表示式允許修改這種預設行為,以如下程式碼為例:

1 numbers := [10]int{0,1,2,3,4,5,6,7,8,9}2 s := numbers[1:4:5]3 fmt.Println(s) // [1, 2, 3]4 fmt.Println(cap(s)) // 4

完整的 slice 表示式具有以下的形式:

1 input[low:high:max]

索引 low 和索引 high 的含義和工作方式與簡略表示式相同。唯一的區別是 max 將結果slice 的容量設定為 max-low。

1 numbers := [10]int{0,1,2,3,4,5,6,7,8,9}2 s := numbers[2:4:6]3 fmt.Println(s) // [2, 3]4 fmt.Println(cap(s)) // 4

當 slice 的輸入運算元是一個 slice 時,結果 slice 的容量取決於輸入運算元,而不是它的指向的底層 array:

1 numbers := [10]int{0, 1, 2, 3, 4, 5, 6, 7, 8, 9}2 fmt.Println(cap(numbers)) // 103 s1 := numbers[1:4]4 fmt.Println(s1) // [1, 2, 3]5 fmt.Println(cap(s1)) // 96 s2 := numbers[1:4:5]7 fmt.Println(s2) // [1, 2, 3]8 fmt.Println(cap(s2)) // 49 s3 := s2[:]10 fmt.Println(s3) // [1, 2, 3]11 fmt.Println(cap(s3)) // 4

這個例子中 s3 的容量不能超過 s2 的容量 (4),即使它指向的 array 有 10 個元素,而且s1,s2,s3 都是從 1 開始的。

當輸入運算元是 slice 時,完整表示式的索引 hight 不能超過其 cap(input)

1 numbers := [10]int{0,1,2,3,4,5,6,7,8,9}2 s1 := numbers[0:1]3 fmt.Println(s1) // [0]4 fmt.Println(len(s1)) // 15 fmt.Println(cap(s1)) // 106 s2 := numbers[0:5]7 fmt.Println(s2) // [0, 1, 2, 3, 4]8 fmt.Println(cap(s1)) // 10

另外,對於它的 max 取值,有兩個額外的規則

1 high <= max2 max <= cap(input)3 numbers := [10]int{0,1,2,3,4,5,6,7,8,9}4 s1 := numbers[0:1]5 s2 := numbers[0:5:11] // invalid slice index 11 (out of bounds for 10-element array)6 fmt.Println(s1, s2)

在完整的 slice 表示式中低索引(low)是可以省略的

1 numbers := [10]int{0, 1, 2, 3, 4, 5, 6, 7, 8, 9}2 s := numbers[:4:6]3 fmt.Println(s) // [0, 1, 2, 3]4 fmt.Println(cap(s)) // 6

然而,不能省略略高索引(hight)

1 numbers := [10]int{0, 1, 2, 3, 4, 5, 6, 7, 8, 9}2 s1 := numbers[:4:6]3 s2 := s1[::5]4 fmt.Println(s2)5 fmt.Println(cap(s2))

否則,程式碼會在編譯時報錯middle index required in 3-index slice

原文連結:

via: https://medium.com/golangspec/slice-expressions-in-go-963368c20765作者:Michał Łowicki 譯者:bizky 校對:polaris1119



相關文章