From ff809d956a63157f757ca9d1d4af06b537aedb35 Mon Sep 17 00:00:00 2001 From: Unknown Date: Tue, 4 Nov 2014 23:48:07 -0500 Subject: [PATCH] 07.2 --- README.md | 2 +- eBook/07.2.md | 137 ++++++++++++++++++++++++--------------------- eBook/07.3.md | 4 +- eBook/directory.md | 1 + 4 files changed, 78 insertions(+), 66 deletions(-) diff --git a/README.md b/README.md index 1c6ef81..d9f0a95 100644 --- a/README.md +++ b/README.md @@ -9,7 +9,7 @@ ## 翻译进度 -7.1 [声明和初始化](eBook/07.1.md) +7.2 [For-range 结构](eBook/07.2.md) ## 支持本书 diff --git a/eBook/07.2.md b/eBook/07.2.md index 4f62eb3..a0de80d 100644 --- a/eBook/07.2.md +++ b/eBook/07.2.md @@ -1,52 +1,53 @@ # 7.2 切片 -164 - ## 7.2.1 概念 -slice是对数组一个连续片段的引用(该数组我们称之为相关数组,通常是匿名的),所以slice是一个引用类型(因此更类似于C/C++中的数组类型,或者Python中的list类型)。这个片段可以是整个数组,或者是由起始和终止索引标识的一些项的子集。需要注意的是,终止索引标识的项不包括在slice内。Slice提供了一个相关数组的动态窗口。 -Slice是可索引的,并且可以由len()方法获取长度。 +slice 是对数组一个连续片段的引用(该数组我们称之为相关数组,通常是匿名的),所以 slice 是一个引用类型(因此更类似于 C/C++ 中的数组类型,或者 Python 中的 list 类型)。这个片段可以是整个数组,或者是由起始和终止索引标识的一些项的子集。需要注意的是,终止索引标识的项不包括在 slice 内。 Slice 提供了一个相关数组的动态窗口。 -给定项的slice索引可能比相关数组的相同元素的索引小。和数组不同的是,slice的长度可以在运行时修改,最小为0最大为相关数组的长度:slice是一个`长度可变的数组`。 +Slice 是可索引的,并且可以由 len() 方法获取长度。 -slice提供了计算容量的方法cap()可以测量slice最长可以达到多少:它等于slice的长度 + 数组除slice之外的长度。如果s是一个slice,cap就是从s[0]到数组末尾的数组长度。slice的长度永远不会超过它的容量,所以对于slice s来说该不等式永远成立: 0 <= len(s) <= cap(s) +给定项的 slice 索引可能比相关数组的相同元素的索引小。和数组不同的是,slice 的长度可以在运行时修改,最小为 0 最大为相关数组的长度:slice 是一个 **长度可变的数组**。 -多个slice如果表示同一个数组的片段,它们可以共享数据;因此一个slice和相关数组的其他slice是共享存储的,相反,不同的数组总是代表不同的存储。数组实际上是slice的构建块。 +slice 提供了计算容量的方法 cap() 可以测量 slice 最长可以达到多少:它等于 slice 的长度 + 数组除 slice 之外的长度。如果 s 是一个 slice,cap 就是从 s[0] 到数组末尾的数组长度。slice 的长度永远不会超过它的容量,所以对于 slice s 来说该不等式永远成立: 0 <= len(s) <= cap(s) -`优点: ` 因为slice是引用,所以它们不需要使用额外的内存并且比使用数组更有效率,所以在Go代码中slice比数组更常用。 +多个 slice 如果表示同一个数组的片段,它们可以共享数据;因此一个 slice 和相关数组的其他 slice 是共享存储的,相反,不同的数组总是代表不同的存储。数组实际上是 slice 的构建块。 -声明slice的格式是: var identifier []type 不需要说明长度 +**优点** 因为 slice 是引用,所以它们不需要使用额外的内存并且比使用数组更有效率,所以在 Go 代码中 slice 比数组更常用。 -一个slice在未初始化之前默认为nil,长度为0。 +声明 slice 的格式是: `var identifier []type` 不需要说明长度 -slice的初始化格式是: var slice1 []type = arr1[start:end] +一个 slice 在未初始化之前默认为 nil,长度为 0。 -这表示slice1是由数组arr1从start索引到end-1索引之间的元素构成的子集(切分数组,start:end被称为slice表达式)。所以slice1[0]就等于arr1[start]。这可以在arr1被填充前就定义好。 +slice 的初始化格式是:`var slice1 []type = arr1[start:end]` -如果某个人写: var slice1 []type = arr1[:]那么slice1就等于完整的arr1数组(所以这种表示方式是arr1[0:len(arr1)]的一种缩写)。另外一种表述方式是:slice1 = &arr1。 +这表示 slice1 是由数组 arr1 从 start 索引到 end-1 索引之间的元素构成的子集(切分数组,start:end 被称为 slice 表达式)。所以 slice1[0] 就等于 arr1[start]。这可以在 arr1 被填充前就定义好。 -arr1[2:]和arr1[2:len(arr1)]相同,都包含了数组从第二个到最后的所有元素。 +如果某个人写:`var slice1 []type = arr1[:]` 那么 slice1 就等于完整的 arr1 数组(所以这种表示方式是 `arr1[0:len(arr1)]` 的一种缩写)。另外一种表述方式是:`slice1 = &arr1`。 -arr1[:3]和arr1[0:3]相同,包含了从第一个到第三个元素(不包括第三个)。 +arr1[2:] 和 arr1[2:len(arr1)] 相同,都包含了数组从第二个到最后的所有元素。 -如果你想去掉slice1的最后一个元素,只要 slice1 = slice1[:len(slice1)-1] +arr1[:3] 和 arr1[0:3] 相同,包含了从第一个到第三个元素(不包括第三个)。 -一个由数组第1,2,3个元素组成的分片可以这么生成: s := [3]int{1,2,3} 或者 s := [...]int{1,2,3}[:]甚至更简单的s := []int{1,2,3}。 +如果你想去掉 slice1 的最后一个元素,只要 `slice1 = slice1[:len(slice1)-1]`。 -s2 := s[:]是用slice组成的slice,拥有相同的元素,但是仍然指向相同的相关数组。 +一个由数组第 1,2,3 个元素组成的分片可以这么生成:`s := [3]int{1,2,3}` 或者 `s := [...]int{1,2,3}[:]` 甚至更简单的 `s := []int{1,2,3}`。 -一个slice s可以这样扩展到它的大小上限:s = s[:cap(s)],如果再扩大的话就会导致运行时错误(参见7.7)。 +`s2 := s[:]` 是用 slice 组成的 slice,拥有相同的元素,但是仍然指向相同的相关数组。 -对于每一个slice(包括string),以下状态总是成立的: +一个 slice s 可以这样扩展到它的大小上限:`s = s[:cap(s)]`,如果再扩大的话就会导致运行时错误(参见第 7.7 节)。 + +对于每一个 slice(包括 string),以下状态总是成立的: s == s[:i] + s[i:] // i是一个整数且: 0 <= i <= len(s) len(s) < cap(s) -Slice也可以用类似数组的方式初始化: var x = []int{2, 3, 5, 7, 11}。这样就创建了一个长度为5的数组并且创建了一个相关slice。 +Slice 也可以用类似数组的方式初始化:`var x = []int{2, 3, 5, 7, 11}`。这样就创建了一个长度为5的数组并且创建了一个相关 slice。 -slice在内存中的组织方式实际上是一个有3个域的结构体:指向相关数组的指针,slice长度以及slice容量。下图给出了一个长度为2,容量为4的slice。 +slice 在内存中的组织方式实际上是一个有 3 个域的结构体:指向相关数组的指针,slice 长度以及 slice 容量。下图给出了一个长度为 2,容量为 4 的 slice。 -y[0] = 3且y[1] = 5。slice y[0:4]由元素3, 5, 7和11组成。 ![](images/7.2_fig7.2.png?raw=true) +`y[0] = 3` 且 `y[1] = 5`。slice y[0:4]由 元素 3, 5, 7 和 11 组成。 + +![](images/7.2_fig7.2.png?raw=true) 示例 7.7 [array_slices.go](exmaples/chapter_7/array_slices.go) @@ -97,14 +98,15 @@ y[0] = 3且y[1] = 5。slice y[0:4]由元素3, 5, 7和11组成。 ![](images/7. The length of slice1 is 4 The capacity of slice1 is 4 -如果s2是一个slice,你可以将s2向后移动一位s2 = s2[1:],但是末尾没有移动。slice只能向后移动,s2 = s2[-1:]会导致编译错误。slice不能被重新分片以获取数组的前一个元素。 +如果 s2 是一个 slice,你可以将 s2 向后移动一位 `s2 = s2[1:]`,但是末尾没有移动。slice 只能向后移动,`s2 = s2[-1:]` 会导致编译错误。slice 不能被重新分片以获取数组的前一个元素。 -`注意`绝对不要用指针指向slice。slice本身已经是一个引用类型,所以它本身就是一个指针!! +**注意** 绝对不要用指针指向 slice。slice 本身已经是一个引用类型,所以它本身就是一个指针!! -问题7.2: 给定slice b:= []byte{'g', 'o', 'l', 'a', 'n', 'g'},那么b[1:4],b[:2],b[2:]和b[:]分别是什么? +问题7.2: 给定 `slice b:= []byte{'g', 'o', 'l', 'a', 'n', 'g'}`,那么 b[1:4],b[:2],b[2:] 和 b[:] 分别是什么? -## 7.2.2 将slice传递给函数 -如果你有一个函数需要对数组做操作,你可能总是需要把参数声明为slice。当你调用该函数时,把数组分片,创建为一个slice引用并传递给该函数。这里有一个计算数组元素和的方法: +## 7.2.2 将 slice 传递给函数 + +如果你有一个函数需要对数组做操作,你可能总是需要把参数声明为 slice。当你调用该函数时,把数组分片,创建为一个 slice 引用并传递给该函数。这里有一个计算数组元素和的方法: func sum(a []int) int { s := 0 @@ -119,25 +121,26 @@ y[0] = 3且y[1] = 5。slice y[0:4]由元素3, 5, 7和11组成。 ![](images/7. sum(arr[:]) } -## 7.2.3 用make()创建一个slice -当相关数组还没有定义时,我们可以使用make()方法来创建一个slice同时创建好相关数组: var slice1 []type = make([]type, len) +## 7.2.3 用 make() 创建一个 slice -也可以简写为 slice1 := make([]type, len),这里len是数组的长度并且也是slice的初始长度。 +当相关数组还没有定义时,我们可以使用 make() 方法来创建一个 slice 同时创建好相关数组:`var slice1 []type = make([]type, len)` -所以定义s2 := make([]int, 10),那么cap(s2) == len(s2) == 10 +也可以简写为 `slice1 := make([]type, len)`,这里 `len` 是数组的长度并且也是 `slice` 的初始长度。 -make接受2个参数:元素的类型以及slice的元素个数。 +所以定义 `s2 := make([]int, 10)`,那么 `cap(s2) == len(s2) == 10` -如果你想创建一个slice1,它不占用整个数组,而只是占用以len为个数个项,那么只要: slice1 := make([]type, len, cap) +make 接受 2 个参数:元素的类型以及 slice 的元素个数。 -make的使用方式是: func make([]T, len, cap) 其中cap是可选参数。 +如果你想创建一个 slice1,它不占用整个数组,而只是占用以 len 为个数个项,那么只要:`slice1 := make([]type, len, cap)` -所以下面两种方法可以生成相同的slice: +make 的使用方式是:`func make([]T, len, cap)` 其中 cap 是可选参数。 + +所以下面两种方法可以生成相同的 slice: make([]int, 50, 100) new([100]int)[0:50] -下图描述了使用make方法生成的slice的内存结构:![](images/7.2_fig7.2.1.png?raw=true) +下图描述了使用 make 方法生成的 slice 的内存结构:![](images/7.2_fig7.2.1.png?raw=true) 示例 7.8 [make_slice.go](exmaples/chapter_7/make_slice.go) @@ -173,25 +176,28 @@ make的使用方式是: func make([]T, len, cap) 其中cap是可选参数。 The length of slice1 is 10 The capacity of slice1 is 10 -因为字符串是纯粹不可变的字节数组,它们也可以被切分成slice。 +因为字符串是纯粹不可变的字节数组,它们也可以被切分成 slice。 -练习 7.4: fobinacci_funcarray.go: 为练习7.3写一个新的版本,主函数调用一个使用序列个数作为参数的函数,该函数返回一个大小为序列个数的Fibonacci slice。 +练习 7.4: fobinacci_funcarray.go: 为练习 7.3 写一个新的版本,主函数调用一个使用序列个数作为参数的函数,该函数返回一个大小为序列个数的 Fibonacci slice。 + +## 7.2.4 new() 和 make() 的区别 -## 7.2.4 new()和make()的区别 看起来二者没有什么区别,都在堆上分配内存,但是它们的行为不同,适用于不同的类型。 -new(T)为每个新的类型T分配一片内存,初始化为0并且返回内存地址,类型*T:这种方法`返回一个指向类型为T,值为0的地址的指针`,它适用于值类型如数组和结构体(参见第十章);它相当于&T{} +new(T) 为每个新的类型T分配一片内存,初始化为 0 并且返回内存地址,类型 *T:这种方法 **返回一个指向类型为 T,值为 0 的地址的指针**,它适用于值类型如数组和结构体(参见第 10 章);它相当于 `&T{}`。 -make(T)`返回一个类型为T的初始值`,它只适用于3种内建的引用类型:slice, map和channel(参见第8章,第13章) +make(T) **返回一个类型为 T 的初始值**,它只适用于3种内建的引用类型:slice, map 和 channel(参见第 8 章,第 13 章) -换言之,new方法分配内存,make方法初始化;下图给出了区别:![](images/7.3_fig7.3.png?raw=true) +换言之,new 方法分配内存,make 方法初始化;下图给出了区别: -在图7.3的第一幅图中: +![](images/7.3_fig7.3.png?raw=true) + +在图 7.3 的第一幅图中: var p *[]int = new([]int) // *p == nil; with len and cap 0 p := new([]int) -在第二幅图中, p := make([]int, 0) ,slice已经被初始化,但是指向一个空的数组。 +在第二幅图中, `p := make([]int, 0)` ,slice 已经被初始化,但是指向一个空的数组。 这两种方式实用性都不高,下面的方法: @@ -201,35 +207,37 @@ make(T)`返回一个类型为T的初始值`,它只适用于3种内建的引用 v := make([]int, 10, 50) -这样分配一个有50个int值的数组,并且创建了一个长度为10,容量为50的slice v,该slice指向数组的前10个元素。 +这样分配一个有 50 个 int 值的数组,并且创建了一个长度为 10,容量为 50 的 slice v,该 slice 指向数组的前 10 个元素。 -问题 7.3:给定s := make([]byte, 5),len(s)和cap(s)分别是多少?s = s[2:4],len(s)和cap(s)又分别是多少? -问题 7.4:假设s1 := []byte{'p', 'o', 'e', 'm'}且s2 := d[2:],s2的值是多少?如果我们执行s2[1] == 't',s1和s2现在的值又分配是多少? +问题 7.3:给定 `s := make([]byte, 5)`,len(s) 和 cap(s) 分别是多少?`s = s[2:4]`,len(s) 和 cap(s) 又分别是多少? +问题 7.4:假设 `s1 := []byte{'p', 'o', 'e', 'm'}` 且 `s2 := d[2:]`,s2 的值是多少?如果我们执行 `s2[1] == 't'`,s1 和 s2 现在的值又分配是多少? -## 7.2.5 多维slice -和数组一样,slice通常也是一维的,但是也可以由一维组合成高维。通过分片的分片(或者slice的数组),长度可以任意动态变化,所以Go语言的多维slice可以任意切分。而且,内层的slice必须单独分配(通过make方法) +## 7.2.5 多维 slice -## 7.2.6 bytes包 -bytes的slice十分常见,Go语言有一个bytes包专门用来解决这种类型的操作方法。 +和数组一样,slice 通常也是一维的,但是也可以由一维组合成高维。通过分片的分片(或者 slice 的数组),长度可以任意动态变化,所以 Go 语言的多维 slice 可以任意切分。而且,内层的 slice 必须单独分配(通过 make 方法) -bytes包和字符串包十分类似(参见4.7)。而且它还包含一个十分有用的类型Buffer: +## 7.2.6 bytes 包 + +bytes 的 slice 十分常见,Go 语言有一个 bytes 包专门用来解决这种类型的操作方法。 + +bytes 包和字符串包十分类似(参见第 4.7 节)。而且它还包含一个十分有用的类型 Buffer: import "bytes" type Buffer struct { ... } -这是一个bytes的定长buffer,提供Read和Write方法,因为读写不知道长度的bytes最好使用buffer。 +这是一个 bytes 的定长 buffer,提供 Read 和 Write 方法,因为读写不知道长度的 bytes 最好使用 buffer。 -Buffer可以这样定义: var buffer bytes.Buffer +Buffer 可以这样定义:`var buffer bytes.Buffer` -或者new出一个指针: var r *bytes.Buffer = new(bytes.Buffer) +或者 new 出一个指针:`var r *bytes.Buffer = new(bytes.Buffer)` -或者通过函数: func NewBuffer(buf []byte) *Buffer,这就用创建了一个Buffer对象并且用buf初始化好了;NewBuffer最好用在从buf读取的时候使用。 +或者通过函数:`func NewBuffer(buf []byte) *Buffer`,这就用创建了一个 Buffer 对象并且用 buf 初始化好了;NewBuffer 最好用在从 buf 读取的时候使用。 -通过buffer串联字符串:类似于Java的StringBuilder类。 +通过 buffer 串联字符串:类似于 Java 的 StringBuilder 类。 -创建一个Buffer,通过buffer.WriteString(s)方法将每个string s追加到后面,最后再通过buffer.String()方法转换为string,下面是代码段: +创建一个 Buffer,通过 buffer.WriteString(s) 方法将每个 string s 追加到后面,最后再通过 buffer.String() 方法转换为 string,下面是代码段: var buffer bytes.Buffer for { @@ -241,14 +249,15 @@ Buffer可以这样定义: var buffer bytes.Buffer } fmt.Print(buffer.String(), "\n") -这种实现方式比使用`+=`要更节省内存和CPU,尤其是要串联的字符串数目特别多的时候。 +这种实现方式比使用 `+=` 要更节省内存和 CPU,尤其是要串联的字符串数目特别多的时候。 练习: -练习 7.5: 给定slice sl,将a []byte数组追加到sl后面。写一个函数Append(slice, data []byte) []byte,该函数在sl不能存储更多数据的时候自动扩容。 -练习 7.6: 把一个缓存buf分片成两个slice:第一个是前n个bytes,后一个是剩余的,用一行代码实现。 +练习 7.5: 给定 slice sl,将 a []byte 数组追加到 sl 后面。写一个函数 `Append(slice, data []byte) []byte`,该函数在 sl 不能存储更多数据的时候自动扩容。 +练习 7.6: 把一个缓存 buf 分片成两个 slice:第一个是前 n 个 bytes,后一个是剩余的,用一行代码实现。 + +## 链接 -##链接 - [目录](directory.md) - 上一节:[声明和初始化](07.1.md) -- 下一节:[For range构建方法](07.3.md) \ No newline at end of file +- 下一节:[For-range 结构](07.3.md) \ No newline at end of file diff --git a/eBook/07.3.md b/eBook/07.3.md index 088982c..30ea4a3 100644 --- a/eBook/07.3.md +++ b/eBook/07.3.md @@ -1,4 +1,6 @@ -#7.3 For range构建方法 +# 7.3 For-range 结构 + +172 这种构建方法可以应用与数组和slice: for ix, value := range slice1 { diff --git a/eBook/directory.md b/eBook/directory.md index d78bb08..3e32c2c 100644 --- a/eBook/directory.md +++ b/eBook/directory.md @@ -61,6 +61,7 @@ - 第7章:[数组与切片](07.0.md) - 7.1 [声明和初始化](07.1.md) - 7.2 [切片](07.2.md) + - 7.3 [For-range 结构](07.3.md) - 第8章:Maps - 第9章:包(package) - 第10章:结构(struct)与方法(method)