Go-Slice

要点

  • 一个区间:[low:high]
  • 两个概念:length, capability
  • 三个函数:make(), append(), copy()

概念

Array是在声明的时候都确定了长度,之后不可更改。Slice和数组类似,也是表示一个有序元素,但这个序列的长度可变。

在Go中,Slice序列中各元素的数据类型必须一致,因为在声明slice的时候即要指定类型。

可以在Python中找到Slice,和Go的Slice基本上是一致的语义和用法。Slice中文通常被译为切片,大抵是因为可以从一个序列中存取指定的某一部分。下面是Collins词典中对于slice的解释,有助于了解该词的含义:

  • N-COUNT (指食物切下的)片,薄片 A slice of bread, meat, fruit, or other food is a thin piece that has been cut from a larger piece. 语法信息

  • VERB 将…切成薄片 If you slice bread, meat, fruit, or other food, you cut it into thin pieces.

在第一次接触Python的slice的时候,对于这个slice或切片,觉得很是怪异,因为在C/C++、Java中还没有对应的术语。——虽然substr()的功用和slice有些相似,但显然slice的用法更加简洁和灵活,更显脚本语言的本色。

以上都是废话,下面给出示例。

声明&赋值

package main
import "fmt"

/*
D:\examples>go run helloworld.go
values[0]=1
values[1]=2
values[2]=3
values[3]=4
values[4]=5

D:\examples>
*/
func main() {
    var values []int // (1)

    values = make([]int, 5) // (2)
    for i, _ := range values {
        values[i] = i + 1
    }

    for i, item := range values {
        fmt.Printf("values[%d]=%d\n", i, item)
    }
}

要点:

  • (1)声明:和数组类似,但是不用指定长度。在这个时候,slice的长度为0,所以还不直接放数据。
  • (2)分配存储空间:调用内置make()函数为slice分配存储空间。
  • 接下来的存取则和普通的数组语法一致。

可以看到,slice和数组非常类似的,即内部存储是有序的数组形式,访问也是索引方式,仅仅在于其动态长度特性。如此,slice又和C++ STL的vector类似,不用指定长度。vector可以声明最大capability,可以动态改变长度。此为,slice和Java的ArrayList也是类似的。所以,通俗的讲,slice也可看做列表。——Go有一个list package,后面会提到。

用Array给slice赋值

package main
import "fmt"

/*
D:\examples>go run helloworld.go
the_slice[0]=3
the_slice[1]=4
the_slice[2]=5

D:\examples>
*/
func main() {
    the_array := [5]int {1, 2, 3, 4, 5} 
    the_slice := the_array[2:5]

    for i, item := range the_slice {
        fmt.Printf("the_slice[%d]=%d\n", i, item)
    }
}

要点:
- 直接用[low:high]这种方式定义一个slice
- 这里的[low:high],用数学集合的方式来讲,就是[low, high),即左闭右开。
- 这种创建slice的方式,不需要make()函数。

[low:high]

Go的[low:high]和Python的语法一致。要点:
- [:]等价于[0:len(array)]
- [:n]等价于[0:n]
- [n:]等价于[n:len(array)]

注:别挑战low小于0或high大于len(array)的这种异常。

package main
import "fmt"

/*
D:\examples>go run helloworld.go
[:] elements:
the_slice[0]=1
the_slice[1]=2
the_slice[2]=3
the_slice[3]=4
the_slice[4]=5
[2:] elements:
the_slice[0]=3
the_slice[1]=4
the_slice[2]=5
[:4] elements:
the_slice[0]=1
the_slice[1]=2
the_slice[2]=3
the_slice[3]=4
[:4] elements:
the_slice[0]=1
the_slice[1]=2
the_slice[2]=3
the_slice[3]=4
[1:4] elements:
the_slice[0]=2
the_slice[1]=3
the_slice[2]=4
panic: runtime error: slice bounds out of range

goroutine 1 [running]:
panic(0x49cba0, 0xc04200a040)
        C:/Go/src/runtime/panic.go:500 +0x1af
main.main()
        D:/examples/helloworld.go:29 +0x191
exit status 2

D:\examples>
*/
func main() {
    the_array := [5]int {1, 2, 3, 4, 5} 
    the_slice := the_array[:]

    debug_slice(the_slice, "[:] elements:")
    debug_slice(the_array[2:], "[2:] elements:")
    debug_slice(the_array[:4], "[:4] elements:")
    debug_slice(the_array[:4], "[:4] elements:")
    debug_slice(the_array[1:4], "[1:4] elements:")

    // invalid slice index -1 (index must be non-negative)
    //debug_slice(the_array[-1:40], "[-1:40] elements:")

    //invalid slice index 40 (out of bounds for 5-element array)
    //debug_slice(the_array[0:40], "[:40] elements:")

    //Runtime Error
    the_slice2 := make([]int, 5)
    debug_slice(the_slice2[0:40], "[:40] elements:")
}

func debug_slice(the_slice []int, msg string) {
    fmt.Println(msg)
    for i, item := range the_slice {
        fmt.Printf("the_slice[%d]=%d\n", i, item)
    }
}

length & capability

先给出示例代码:

package main
import "fmt"

/*
D:\examples>go run helloworld.go
default values of slice:
the_slice[0]=0
the_slice[1]=0
the_slice[2]=0
the_slice[3]=0
the_slice[4]=0

D:\examples>
*/
func main() {
    var the_slice []int;

    the_slice = make([]int, 5, 10)

    debug_slice(the_slice, "default values of slice:")
}

func debug_slice(the_slice []int, msg string) {
    fmt.Println(msg)
    for i, item := range the_slice {
        fmt.Printf("the_slice[%d]=%d\n", i, item)
    }
}

在make()的时候,5表示长度为5个元素,此即length的概念;10表示整个容器的长度,即capability的概念。在这里,slice可以放10个元素,但目前只放了5个元素。make()的时候会自动初始化这5个元素为缺省值(???)。

在 <> Ch4 Composite Types, P88 有如下的描述:

make([]T, len)
make([]T, len, capability) // same as make([]T capability)[:len]

也就是说, 对于第一种情形,make直接创建长度为len的数组,并返回这个数组的元素。对于第二种情形,make会创建长度为capability的数组,但仅仅返回前面的len个元素。显然,返回的slice的长度就是len。

因为slice是依存于数组数据结构而存在,虽然第二种情形下slice的长度为len,但在添加新的元素的时候,只要不超过capability,就无须动态创建新的数组,而直接把要添加的元素放在slice已有元素的后面。

slice的length和capability的概念,和C++ STL vector对应的含义是一致的。——python的slice/list貌似没用这个概念。。。不确定。。。

当capability不至于放新的原始的时候,slice对象就会重新创建一个数组,并把原来的数据拷贝到新的数组里。

可以认为,capability对于功能无影响,但对于程序的性能会有影响。如果设置不当,会因为频繁的数据拷贝而降低性能。

TODO: 暂不清楚Go是否有OO的ctor的概念,如果有的话,用slice存放OO对象+ctor打印,会比较好的说明capability的作用。

对于以上解读,再给出一个例子:

package main
import "fmt"

/*
D:\examples>go run helloworld.go
default value of slice:
the_slice[0]=0
the_slice[1]=0
the_slice[2]=0
slice:
the_slice[0]=1
the_slice[1]=2
the_slice[2]=3
the_slice[3]=4
the_slice[4]=5
the_slice[5]=6

D:\examples>
*/
func main() {
    the_array := [6]int {1,2,3,4,5,6}
    the_slice := make([]int, 3, 5)

    debug_slice(the_slice, "default value of slice:")

    the_slice = the_array[:]
    debug_slice(the_slice, "slice:")
}

func debug_slice(the_slice []int, msg string) {
    fmt.Println(msg)
    for i, item := range the_slice {
        fmt.Printf("the_slice[%d]=%d\n", i, item)
    }
}

append

append()函数是在slice后面添加元素。同前面对capability的描述,append()的时候可能涉及到数组的动态创建。

package main
import "fmt"

/*
D:\examples>go run helloworld.go
slice1: 1       2       3
slice2: 1       2       3       4       5
slice1: 1       2       3       4       5       6
slice2: 1       2       3       4       5

D:\examples>
*/
func main() {
    the_slice1 := []int {1,2,3}
    the_slice2 := append(the_slice1, 4, 5)

    debug_slice(the_slice1, "slice1:")
    debug_slice(the_slice2, "slice2:")

    the_slice1 = append(the_slice1, 4, 5, 6)
    debug_slice(the_slice1, "slice1:")
    debug_slice(the_slice2, "slice2:")

    //the_slice1.append undefined (type []int has no field or method append)
    //the_slice1.append(7)
}

func debug_slice(the_slice []int, msg string) {
    fmt.Print(msg, "\t")
    for _, item := range the_slice {
        fmt.Print(item, "\t")
    }
    fmt.Println()
}

不清楚,为什么不支持.append()这种语法,而是append(slice, elements …)。

copy

copy()函数是把一个slice的“所有”元素拷贝到另外一个slice中。——是拷贝,而不是追加(append)。需要注意的是:如果目标slice的长度小于源slice的长度,那么就不会拷贝源slice的所有原始。

package main
import "fmt"

/*
D:\examples>go run helloworld.go
slice1: 1       2       3
slice2: 0       0
slice3: 10      0       0       0
slice1: 1       2       3
slice2: 1       2
slice3: 1       2       3       0

D:\examples>
*/
func main() {
    slice1 := []int {1,2,3}
    slice2 := make([]int, 2)
    slice3 := make([]int, 4)

    slice3[0] = 10
    debug_slice(slice1, "slice1:")
    debug_slice(slice2, "slice2:")
    debug_slice(slice3, "slice3:")

    copy(slice2, slice1)
    copy(slice3, slice1)
    debug_slice(slice1, "slice1:")
    debug_slice(slice2, "slice2:")
    debug_slice(slice3, "slice3:")
}

func debug_slice(the_slice []int, msg string) {
    fmt.Print(msg, "\t")
    for _, item := range the_slice {
        fmt.Print(item, "\t")
    }
    fmt.Println()
}

附 append documentation

func append

func append(slice []Type, elems ...Type) []Type

The append built-in function appends elements to the end of a slice. If it has sufficient capacity, the destination is resliced to accommodate the new elements. If it does not, a new underlying array will be allocated. Append returns the updated slice. It is therefore necessary to store the result of append, often in the variable holding the slice itself:

slice = append(slice, elem1, elem2)
slice = append(slice, anotherSlice...)

As a special case, it is legal to append a string to a byte slice, like this:

slice = append([]byte("hello "), "world"...)

附 copy documentation

func copy

func copy(dst, src []Type) int

The copy built-in function copies elements from a source slice into a destination slice. (As a special case, it also will copy bytes from a string to a slice of bytes.) The source and destination may overlap. Copy returns the number of elements copied, which will be the minimum of len(src) and len(dst).

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值