Golang基础学习-array与slice | 青训营笔记

96 阅读3分钟

这是我参与「第五届青训营」笔记创作活动的第1天

今天是1月16日,昨天的课程上对golang的基础语法进行了学习。其中除了golang的协程特性之外,我最喜欢的语法特性就是golang对于array(数组)与slice(切片)的设计。

golang中的array是一种相对“静态”的数据类型,由中括号中加数字后面接类型表示这是一个相应数字大小的存储相应类型的数组。

const N = 10
var intArray [N]int        // 长度为10的整形数组类型的变量
var int2DArray [N][N+1]int // 长度为10的存储长度为11的整形数组类型的数组

其与C++类似的地方是,在推断类型时不同长度的数组并不认为是同一种类型。而与C++不同的的是,这样的数组类型是可以作为函数参数的,并且会做类型检查,长度不同的数组是无法通过编译的;同时直接用数组类型作为参数时与其他值类型一样是以副本的形式传入函数内部的,意味着函数内部改变数组元素不会影响函数外的作为参数的数组内容。(还有一个区别就是C++中的数组多是栈上分配的甚至是static区编译时分配载入的,而golang中的数组要根据逃逸分析计算其存在位置)

func CannotChange(param [11]int) {
    param[10] = 1
}
CannotChange(intArray) // cannot use intArray 
                       // (variable of type [10]int) 
                       // as type [11]int in argument to CannotChange
CannotChange(int2DArray[0]) // Ok
int2DArray[0] // [11]int{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}

当想要函数调用可以改变数组类型时,有两种选择一种是将参数的数组类型改成数组的指针类型,但是这样仍然受制于数组长度与类型相关的问题;第二种方法就是使用golang的slice。

slice整体上十分类似于Python的list,在取范围切片时都是左闭右开的(但是golang的slice是不支持负数索引的;个人认为Python中的负索引虽然有时比较方便,但是会使一些编程错误难以在早期发现)。slice可以是数组的范围切片,也可以是另一个切片的范围切片。

func CanChange(param []int) { 
    if len(param) > 0 {
        param[0] = len(param)
    }
}
CanChange(intArray[:]) // 整个数组作为范围
intArray // [10]int{10, 0, 0, 0, 0, 0, 0, 0, 0, 0}
intArray[:] // []int{10, 0, 0, 0, 0, 0, 0, 0, 0, 0}

slice与Python中的list的主要的区别是slice仍是以相应array为底层的数据结构,所以只能为一种大类服务(基本类型或实现了相应接口方法的数据类型)而list是不限制类型的(当然可以用any也就是interface{}做模仿),并且slice由于依赖于底层的array只能做到“半inplace”,这也就意味着如果两个slice出自于同一个array或slice对象的两个不同范围,就需要注意他们的操作是否会产生overlap并且这种overlap会不会带来你不想要的副作用。

sliceL := intArray[:1]
sliceL // []int{10}
sliceR := intArray[1:N - 1]
// len(sliceR) == 8 
sliceR // []int{0, 0, 0, 0, 0, 0, 0, 0}
sliceL = append(sliceL, 1, 2)
sliceL // []int{10, 1, 2}
sliceR // []int{1, 2, 0, 0, 0, 0, 0, 0}
       // 左侧的扩展带来了overlaping
sliceR2 := append(sliceR, 3, 4)
sliceR2 // []int{1, 2, 0, 0, 0, 0, 0, 0, 3, 4}
// len(sliceR2) == 10
intArray // [10]int{10, 1, 2, 0, 0, 0, 0, 0, 0, 0}
         // 扩展超界,变化被做到新分配的数组中了
sliceR = append(sliceR, 5)
sliceR // []int{1, 2, 0, 0, 0, 0, 0, 0, 5}
intArray // [10]int{10, 1, 2, 0, 0, 0, 0, 0, 0, 5}
         // 扩展未超界,inplace了

这里代码的测试主要是使用一个Go的开源repl,叫做gore,可以用来做一些简单代码逻辑的验证,对初学者还是有一些帮助的。