在Go切片中,为什么s [lo:hi]在元素hi-1处结束? [英] In a Go slice, why does s[lo:hi] end at element hi-1?

查看:115
本文介绍了在Go切片中,为什么s [lo:hi]在元素hi-1处结束?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

根据 Go of Go ,在Go分片 s ,表达式 s [lo:hi] 将从 lo 通过 hi-1 (含):

  package main 

importfmt

func main(){

p:= [] int {0,//切片位置0
10,//切片位置1
20,//切片位置2
30,//切片位置3
40,//切片位置4
50} //切片位置5

fmt.Println(p [0:3])// => [0 10 20]
}

在上面的代码示例中,p [0: 3]似乎直观地被读为:从位置0到位置3的切片,等同于[0,10,20,30]。但当然,它实际上相当于[0 10 20]。



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



在此先感谢。

解决方案

这完全是约定的问题,当然还有其他方法可以做到这一点(例如,Matlab使用的数组的第一个索引是1)。这个选择真的归结于你想要的属性。事实证明,使用包含切片的0索引数组是独占的(也就是说,从a到b的切片包含元素a并排除元素b)具有一些非常好的属性,因此这是一个非常普遍的选择。这里有几个优点。
$ b

0索引数组和独占切片的优点


$ b $ (请注意,我使用non-Go术语,所以我将以C或Java会谈论它们的方式讨论数组。数组就是Go所谓的切片,而切片则是子数组(即,从索引1到索引4的切片))


  • 指针运算。如果你使用C这样的语言,数组实际上只是指向数组中第一个元素的指针。因此,如果使用0索引数组,那么可以说索引 i 处的元素只是数组指针加上 i 指向的元素。例如,如果我们有数组[3 2 1],数组地址为10(并假定每个值占用一个字节的内存),则第一个元素的地址为10 + 0 = 10,第二个地址是10 + 1 = 11,依此类推。总之,它使得数学变得简单。

  • 切片的长度也是切片的位置。也就是说,对于数组 arr arr [0:len(arr)] 只是 arr 本身。这在实践中派上用场。例如,如果我调用 n,_:= r.Read(arr)(其中 n 是字节读入 arr ),然后我可以执行 arr [:n] 来获得 arr 与实际写入 arr 的数据相对应。

  • 索引不重叠。这意味着如果我有 arr [0:i] arr [i:j] arr [j:k] arr [k:len(arr)] ,这些切片完全覆盖 arr 本身。你可能不会经常发现自己将数组分割成像这样的子片,但它有很多相关的优点。例如,考虑以下代码根据非连续整数拆分数组:

      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])
    }

    li>



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



如果我们尝试使用包含包含的切片来编写等价函数,那么它会更加复杂。



如果任何人都可以想到更多,请随时编辑此答案并添加它们。


According to the Tour of Go, in a Go slice s, the expression s[lo:hi] evaluates to a slice of the elements from lo through hi-1, inclusive:

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]
}    

In my code example above, "p[0:3]" would seem to intuitively "read" as: "the slice from position 0 to position 3", equating to [0, 10, 20, 30]. But of course, it actually equates to [0 10 20].

So my question is: what is the design rationale for the upper value evaluating to hi-1 rather than simply hi? It feels unintuitive, but there must be some reason for it that I'm missing, and I'm curious what that might be.

Thanks in advance.

解决方案

This is completely a matter of convention, and there are certainly other ways to do it (for example, Matlab uses arrays whose first index is 1). The choice really comes down to what properties you want. As it turns out, using 0-indexed arrays where slicing is inclusive-exclusive (that is, a slice from a to b includes element a and excludes element b) has some really nice properties, and thus it's a very common choice. Here are a few advantages.

Advantages of 0-indexed arrays and inclusive-exclusive slicing

(note that I'm using non-Go terminology, so I'll talk about arrays in the way that C or Java would talk about them. Arrays are what Go calls slices, and slices are sub-arrays (ie, "the slice from index 1 to index 4"))

  • Pointer arithmetic works. If you're in a language like C, arrays are really just pointers to the first element in the array. Thus, if you use 0-indexed arrays, then you can say that the element at index i is just the element pointed at by the array pointer plus i. For example, if we have the array [3 2 1] with the address of the array being 10 (and assuming that each value takes up one byte of memory), then the address of the first element is 10 + 0 = 10, the address of the second is 10 + 1 = 11, and so on. In short, it makes the math simple.
  • The length of a slice is also the place to slice it. That is, for an array arr, arr[0:len(arr)] is just arr itself. This comes in handy a lot in practice. For example, if I call n, _ := r.Read(arr) (where n is the number of bytes read into arr), then I can just do arr[:n] to get the slice of arr corresponding to the data that was actually written into arr.
  • Indices don't overlap. This means that if I have arr[0:i], arr[i:j], arr[j:k], arr[k:len(arr)], these slices fully cover arr itself. You may not often find yourself partitioning an array into sub-slices like this, but it has a number of related advantages. For example, consider the following code to split an array based on non-consecutive integers:

    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])
    }
    

(this code obviously doesn't handle some edge cases well, but you get the idea)

If we were to try to write the equivalent function using inclusive-inclusive slicing, it would be significantly more complicated.

If anyone can think of any more, please feel free to edit this answer and add them.

这篇关于在Go切片中,为什么s [lo:hi]在元素hi-1处结束?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆