在Go切片中,为什么s [lo:hi]在元素hi-1处结束?

时间:2014-11-11 04:38:50

标签: go slice

根据Tour of Go,在Go切片s中,表达式s[lo:hi]会计算从lohi-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]"看起来很直观"阅读" as:"从位置0到位置3和#34;的切片,等于[0,10,20,30]。但当然,它实际上相当于[0 10 20]。

所以我的问题是:评估为hi-1而不是简单hi的上限值的设计原理是什么?感觉不直观,但我必须有一些理由让我失踪,我很好奇这可能是什么。

提前致谢。

2 个答案:

答案 0 :(得分:10)

这完全是一个惯例问题,当然还有其他方法(例如,Matlab使用第一个索引为1的数组)。选择真正归结为您想要的属性。事实证明,使用0索引数组,其中切片是包容性的(即,从a到b的切片包括元素a并且排除元素b)具有一些非常好的属性,因此它是一种非常常见的选择。以下是一些优点。

0索引数组和包容性排他切片的优点

(注意我使用的是非Go术语,所以我将以C或Java谈论它们的方式讨论数组。数组是Go调用切片,切片是子数组(即, “从索引1到索引4的切片”))

  • 指针算术工作。如果您使用的是C语言,那么数组实际上只是指向数组中第一个元素的指针。因此,如果使用0索引数组,则可以说索引 i 中的元素只是数组指针加上 i 所指向的元素。例如,如果我们有数组[3 2 1],数组的地址为10(假设每个值占用一个字节的内存),那么第一个元素的地址是10 + 0 = 10,第二个的地址是10 + 1 = 11,依此类推。简而言之,它使数学变得简单。
  • 切片的长度也是切片的地方。也就是说,对于数组arrarr[0:len(arr)]本身就是arr。这在实践中很方便。例如,如果我调用n, _ := r.Read(arr)(其中n是读入arr的字节数),那么我可以arr[:n]来获取{{1}的切片对应于实际写入arr的数据。
  • 指数不重叠。这意味着,如果我有arrarr[0:i]arr[i:j]arr[j:k],则这些切片会完全覆盖arr[k:len(arr)]本身。您可能不会经常发现自己将数组划分为这样的子切片,但它具有许多相关的优点。例如,请考虑以下代码,以基于非连续整数拆分数组:

    arr

(这段代码显然不能很好地处理一些边缘情况,但你明白了)

如果我们尝试使用包容性切片来编写等效函数,那将会更加复杂。

如果有人再想一想,请随时编辑此答案并添加。

答案 1 :(得分:3)

  

The Go Programming Language Specification

     

Slice types

     

Slice expressions

     

对于字符串,数组,指向数组的指针或切片a,主要的   表达

a[low : high]
     

构造子字符串或切片。指数低和高选择哪个   操作数a的元素出现在结果中。结果有指数   从0开始,长度等于high - low

     

为方便起见,可省略任何索引。失踪低   index默认为零;缺少的高索引默认为长度   切片的操作数

     

对于数组或字符串,如果0 <=低&lt; = high&lt; =,则索引在范围内   len(a),否则他们超出范围。对于切片,上部索引   bound是切片容量上限(a)而不是长度。一个常数   index必须是非负的,并且可以通过int类型的值表示;   对于数组或常量字符串,常量索引也必须在   范围。如果两个指数都是常数,则它们必须满足低<=高。如果   指数在运行时超出范围,发生运行时恐慌。

对于q := p[m:n]qp的一个切片,从索引m开始,长度为n-m个元素。