数组和切片
一个变量放一个值。购物清单、一个班的分数、一周的温度,都是一串值。Go 用切片放一串值:
1package main2 3import "fmt"4 5func main() {6 list := []string{"牛奶", "面包", "鸡蛋"}7 fmt.Println(list)8 fmt.Println("一共", len(list), "样")9 fmt.Println("第一样是", list[0])10}
[牛奶 面包 鸡蛋] 一共 3 样 第一样是 牛奶
[]string 是「一串字符串」,花括号里是一开始放进去的元素。len(list) 是元素的个数,list[0] 是第一个元素:下标从 0 开始。fmt.Println 显示切片时,元素放在方括号里,用空格隔开。
讲切片之前,先看看它的底子:数组。
数组:长度固定
数组的长度写在方括号里,定下来就不能变。没给值的元素是零值:
1package main2 3import "fmt"4 5func main() {6 var temps [3]int7 fmt.Println(temps)8 temps[0] = 189 temps[2] = 2510 fmt.Println(temps, len(temps))11 12 scores := [4]int{90, 75, 88, 62}13 fmt.Println(scores[1])14}
[0 0 0] [18 0 25] 3 75
长度是类型的一部分:[3]int 和 [4]int 是两种不同的类型,不能互相赋值。
数组赋值的时候,整份复制。改副本,原来的不变:
1package main2 3import "fmt"4 5func main() {6 a := [3]int{1, 2, 3}7 b := a8 b[0] = 1009 fmt.Println("a:", a)10 fmt.Println("b:", b)11}
a: [1 2 3] b: [100 2 3]
点单步,看 b := a 这一步:变量面板里出现了第二排格子,a 和 b 各有各的。
这段程序显示什么?
1package main2 3import "fmt"4 5func main() {6 week := [7]int{}7 week[6] = 18 copyOf := week9 copyOf[6] = 210 fmt.Println(week[6], copyOf[6], len(copyOf))11}
copyOf := week 把七个元素整份复制了一遍,所以改 copyOf[6] 不影响 week[6],还是 1。长度是 7:下标从 0 到 6。
数组用得不多,因为长度不能变。平时用的是切片。
切片:能变长的一串
切片的类型是 []int、[]string,方括号里不写长度。读元素、改元素和数组一样,用下标:
1package main2 3import "fmt"4 5func main() {6 prices := []int{12, 30, 8}7 prices[1] = 288 fmt.Println(prices)9 fmt.Println("最后一个是", prices[len(prices)-1])10}
[12 28 8] 最后一个是 8
最后一个元素的下标是 len(prices)-1,因为下标从 0 开始。
下标越界
下标超出范围,程序会停下来,这叫 panic:
1package main2 3import "fmt"4 5func main() {6 prices := []int{12, 30, 8}7 fmt.Println(prices[2])8 fmt.Println(prices[3])9}
8
panic: runtime error: index out of range [3] with length 3
goroutine 1 [running]:
main.main(...)
/tmp/sandbox/prog.go:8 +0x1d
exit status 2发生了什么: 下标 3 越界了:只有 3 个元素,下标只能是 0~2。
index out of range [3] with length 3:要第 3 号元素,可是只有 3 个元素(0、1、2 号)。下面的 goroutine 1 [running]: 是出事的地方:main.main(...) 函数,prog.go 第 8 行。最后的 exit status 2 说明程序是出错停下的。Go 不会悄悄给你一个乱七八糟的值,而是马上停下来告诉你。
append:往后加
append 在切片后面加元素,然后返回加好的切片。要把结果赋回给原来的变量:
1package main2 3import "fmt"4 5func main() {6 var list []string7 fmt.Println(list, len(list))8 list = append(list, "牛奶")9 list = append(list, "面包", "鸡蛋")10 fmt.Println(list, len(list))11}
[] 0 [牛奶 面包 鸡蛋] 3
var list []string 是一个空切片(零值是 nil,长度 0),append 照样能往里加。一次可以加好几个。点单步,看 list 的格子一次次变多。
切一段
s[1:3] 取下标 1 到 3 的那一段:包括 1,不包括 3。前一个数不写是从头开始,后一个不写是到末尾:
1package main2 3import "fmt"4 5func main() {6 days := []string{"一", "二", "三", "四", "五"}7 fmt.Println(days[1:3])8 fmt.Println(days[:2])9 fmt.Println(days[3:])10 fmt.Println(len(days[1:4]))11}
[二 三] [一 二] [四 五] 3
s[a:b] 有 b-a 个元素。切出来的这一段和原来的切片共用同一块存储,第 7 课细讲。
用 range 走一遍
for i, v := range s 一个个走过切片:i 是下标,v 是那个元素。不要下标,就写成 _:
1package main2 3import "fmt"4 5func main() {6 scores := []int{90, 75, 88, 62}7 for i, v := range scores {8 fmt.Println(i, v)9 }10 11 total := 012 for _, v := range scores {13 total += v14 }15 fmt.Println("总分", total)16}
0 90 1 75 2 88 3 62 总分 315
make:先建好再填
知道要放几个,可以用 make([]int, 5) 先建一个长度 5 的切片,五个元素都是零值,再按下标填:
1package main2 3import "fmt"4 5func main() {6 squares := make([]int, 5)7 fmt.Println(squares)8 for i := range squares {9 squares[i] = i * i10 }11 fmt.Println(squares)12}
[0 0 0 0 0] [0 1 4 9 16]
for i := range squares 只写一个变量,就只拿下标。make 建的切片已经有 5 个元素了,所以用下标填,不用 append(用 append 会加在这 5 个后面)。
练一练
写好代码,点检查。提示随便看,实在想不出来再看答案。
购物车总价
✓写好 total 函数:把切片里的价格加起来,返回总数。main 用它算两个购物车,应该显示 50 和 107。
50 107
1package main2 3import "fmt"4 5func total(prices []int) int {6 return 07}8 9func main() {10 fmt.Println(total([]int{12, 30, 8}))11 fmt.Println(total([]int{99, 5, 2, 1}))12}
挑出及格的分数
✓写好 passing 函数:返回一个新切片,只放不低于 60 的分数,顺序不变。应该显示 [72 90 65] 和 [88]。
[72 90 65] [88]
1package main2 3import "fmt"4 5func passing(scores []int) []int {6 var result []int7 return result8}9 10func main() {11 fmt.Println(passing([]int{72, 45, 90, 65, 59}))12 fmt.Println(passing([]int{30, 88, 12}))13}
修好它:最后一样东西
✓last 应该返回切片的最后一个元素,可是程序一运行就 panic。改好它,让它显示 鸡蛋 和 苹果。
鸡蛋 苹果
1package main2 3import "fmt"4 5func last(items []string) string {6 return items[len(items)]7}8 9func main() {10 fmt.Println(last([]string{"牛奶", "面包", "鸡蛋"}))11 fmt.Println(last([]string{"苹果"}))12}
这一课学会了
- 数组
[3]int长度固定,长度是类型的一部分;赋值时整份复制。 - 切片
[]int{…}能变长;len(s)是元素个数,下标从 0 到len(s)-1。 - 下标越界会 panic:
index out of range [3] with length 3,exit status 2。 s = append(s, v)往后加元素,结果要赋回去。s[a:b]取一段:包括a,不包括b。for i, v := range s走一遍;不要的用_。make([]int, 5)建一个有 5 个零值的切片。