根据Tour of Go ,在 Go slice s
中,表达式 s[lo:hi]
求值为从 lo
到 hi 的元素 slice -1
,含:
package main
import "fmt"
func main() {
p := []int{0, // slice position 0
10, // slice position 1
20, // slice position 2
30, // slice position 3
40, // slice position 4
50} // slice position 5
fmt.Println(p[0:3]) // => [0 10 20]
}
在我上面的代码示例中,“p[0:3]”似乎直观地“读”为:“从位置 0 到位置 3 的 slice ”,等于 [0, 10, 20, 30]。但当然,它实际上等于 [0 10 20]。
所以我的问题是:将上限值评估为 hi-1
而不仅仅是 hi
的设计原理是什么?感觉不直观,但我想念它一定是有原因的,我很好奇这可能是什么。
提前致谢。
最佳答案
这完全是一个约定俗成的问题,当然还有其他方法可以做到(例如,Matlab 使用第一个索引为 1 的数组)。选择实际上取决于您想要什么属性。事实证明,使用 0-indexed 数组,其中 slice 是独占的(即,从 a 到 b 的 slice 包括元素 a 并排除元素 b)具有一些非常好的属性,因此它是一个非常常见的选择。这里有一些优点。
0索引数组和包容-独占 slice 的优势
(请注意,我使用的是非 Go 术语,因此我将按照 C 或 Java 谈论数组的方式来讨论数组。数组是 Go 所谓的 slice , slice 是子数组(即, "从索引 1 到索引 4 的 slice "))
- 指针算术有效。如果您使用像 C 这样的语言,那么数组实际上只是指向数组中第一个元素的指针。因此,如果您使用索引为 0 的数组,那么您可以说索引 i 处的元素只是数组指针所指向的元素加上 i。例如,如果我们有数组 [3 2 1],数组的地址是 10(假设每个值占用一个字节的内存),那么第一个元素的地址是 10 + 0 = 10,则第二个的地址是 10 + 1 = 11,依此类推。简而言之,它使数学变得简单。
- slice 的长度也是 slice 的地方。也就是说,对于数组
arr
,arr[0:len(arr)]
只是arr
本身。这在实践中非常有用。例如,如果我调用n, _ := r.Read(arr)
(其中n
是读入arr
的字节数) ,那么我可以只做arr[:n]
来获取与实际写入arr
的数据相对应的arr
slice 。 指数不重叠。这意味着如果我有
arr[0:i]
,arr[i:j]
,arr[j:k]
,arr[k:len(arr)]
,这些 slice 完全覆盖了arr
本身。您可能不会经常发现自己像这样将数组划分为子 slice ,但它有许多相关的优点。例如,考虑以下代码,根据不连续的整数拆分数组:func consecutiveSlices(ints []int) [][]int { ret := make([][]int, 0) i, j := 0, 1 for j < len(ints) { if ints[j] != ints[j-1] + 1 { ret = append(ret, ints[i:j]) i = j } } ret = append(ret, ints[i:j]) }
(这段代码显然不能很好地处理一些边缘情况,但你明白了)
如果我们尝试使用 inclusive-inclusive slice 来编写等效函数,那会明显更复杂。
如果有人能想到更多,请随时编辑此答案并添加它们。
关于go - 在 Go slice 中,为什么 s[lo :hi] end at element hi-1?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/26857582/