Kodewick
开始学 →
第 6 课,共 24 课目录

数组和切片

第 6 课,共 24 课 · 第 2 部分 · 数据 · 约 15 分钟

一个变量放一个值。购物清单、一个班的分数、一周的温度,都是一串值。Go 用切片放一串值:

prog.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 显示切片时,元素放在方括号里,用空格隔开。

讲切片之前,先看看它的底子:数组。

数组:长度固定

数组的长度写在方括号里,定下来就不能变。没给值的元素是零值:

prog.go
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 是两种不同的类型,不能互相赋值。

数组赋值的时候,整份复制。改副本,原来的不变:

prog.go
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}

数组用得不多,因为长度不能变。平时用的是切片。

切片:能变长的一串

切片的类型是 []int、[]string,方括号里不写长度。读元素、改元素和数组一样,用下标:

prog.go
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:

prog.go
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 在切片后面加元素,然后返回加好的切片。要把结果赋回给原来的变量:

prog.go
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。前一个数不写是从头开始,后一个不写是到末尾:

prog.go
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 是那个元素。不要下标,就写成 _:

prog.go
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 的切片,五个元素都是零值,再按下标填:

prog.go
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 个后面)。

练一练

写好代码,点检查。提示随便看,实在想不出来再看答案。

练习 1

购物车总价

写好 total 函数:把切片里的价格加起来,返回总数。main 用它算两个购物车,应该显示 50 和 107。

要显示
50
107
exercise1.go
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}
输出
点「运行」或按 Ctrl+Enter
练习 2

挑出及格的分数

写好 passing 函数:返回一个新切片,只放不低于 60 的分数,顺序不变。应该显示 [72 90 65] 和 [88]。

要显示
[72 90 65]
[88]
exercise2.go
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}
输出
点「运行」或按 Ctrl+Enter
练习 3

修好它:最后一样东西

last 应该返回切片的最后一个元素,可是程序一运行就 panic。改好它,让它显示 鸡蛋 和 苹果。

要显示
鸡蛋
苹果
exercise3.go
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}
输出
点「运行」或按 Ctrl+Enter

这一课学会了

  • 数组 [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 个零值的切片。
这一课学完了吗?点一下标记。进度存在这个浏览器里,随时可以撤销。