Go 切片表达式

Go 提供了通过索引访问数组和切片中单个元素的能力。这些类型被视为有序序列。

除了访问切片的特定元素之外,还可以使用基于索引的操作对切片进行其他操作。在本节中,你将学习如何使用切片表达式来执行这些操作。

简单切片表达式

在 Go 中,我们可以使用切片表达式执行一系列操作。首先来看一下常用的简单切片表达式语法,用于构造子切片:

s[low:high]

代码解析
索引 lowhigh 用于选择切片 s 中哪些元素会出现在结果中。下面我们看一个例子来创建子切片 s1

s := []int{2, 3, 5, 7, 11} // len: 5 cap: 5
s1 := s[1:3]
fmt.Println(s1) // [3 5]

代码解析
注意,索引 low 对应的元素 s[1] = 3 会被包含在新的子切片 s1 中,而索引 high 对应的元素 s[3] = 7 不会被包含。简单来说,high 是切片元素的范围边界。

新创建的子切片 s1 的长度 len 等于索引范围 high - low -> 3 - 1 = 2,容量 cap 等于 cap(s) - low -> 5 - 1 = 4

fmt.Println("len:", len(s1), "cap:", cap(s1)) // len: 2 cap: 4

为了方便,可以省略任意一个索引。缺省的 low 默认为 0,缺省的 high 默认为切片的总长度:

fmt.Println(s[2:]) // [5 7 11]     — 等同于 s[2:len(s)]
fmt.Println(s[:3]) // [2 3 5]      — 等同于 s[0:3]
fmt.Println(s[:])  // [2 3 5 7 11] — 等同于 s[0:len(s)]

由于字符串也是字符序列,我们可以使用相同的语法从字符串构造子串:

star := "Polaris"
s2 := star[0:5] + " Bear"
fmt.Println(s2) // Polar Bear

planets := []string{"Mercury", "Venus", "Earth", "Mars", "Jupiter", "Saturn", "Uranus", "Neptune"}
s3 := planets[5][0:5] + "day"
fmt.Println(s3) // Saturday

简单切片表达式的细节

在使用简单切片表达式获取数组或字符串的子切片时,索引 lowhigh 必须满足范围:

0 <= low <= high <= len(s)

否则会出现运行时错误。例如:

star := "Polaris"
fmt.Println(star[-1])  // 无效参数:索引 -1
fmt.Println(star[0:8]) // 运行时错误:slice bounds out of range [:8] with length 7

当从切片获取子切片时,结果切片的容量 cap 等于基础切片的容量减去 low(即 cap(s) - low),而不是基础切片的长度:

teams := []string{"Bayern Munich", "Real Madrid", "Manchester City"}
t := teams[1:2]
fmt.Println(t, "| len:", len(t), "cap:", cap(t)) // [Real Madrid] | len: 1 cap: 2
fmt.Println(t[1:3]) // 运行时错误:slice bounds out of range [:3] with capacity 2

简单切片表达式支持数组、切片、字符串,以及指向数组/切片的指针:

consts := new([3]float64)
*consts = [3]float64{3.1416, 2.718, 1.618} // 指向数组
fmt.Println(consts[1:3])                   // [2.718 1.618]

animals := new([]string)
*animals = []string{"cat", "dog", "fish"} // 指向切片
fmt.Println((*animals)[1:3])              // ["dog" "fish"]

当切片为 nil 时,获取子切片的结果仍然是 nil:

var n []int       // nil 切片
fmt.Println(n[:]) // []

获取子切片的子切片

一个重要细节是,子切片会共享其“父切片”的底层数组。例子如下:

var nums [5]int
nums = [5]int{1, 2, 3, 4, 5}
n1 := nums[0:4] // n1 的底层数组是 nums,n1[2] == nums[2]
n2 := n1[0:3]   // n2 的底层数组是 n1,n2[2] == n1[2]
n2[2] = 343     // n2[2] == n1[2] == nums[2]

因此,n2[2] = 343 会更新三个切片的相应元素:

fmt.Println(nums) // [1 2 343 4 5]
fmt.Println(n1)   // [1 2 343 4]
fmt.Println(n2)   // [1 2 343]

完整切片表达式

完整切片表达式相比简单切片表达式,多了一个额外索引 max

s[low:high:max]

它的作用是构造一个长度与简单切片表达式相同,但容量可以通过 max 控制的子切片:

s := []int{2, 3, 5, 7, 11}
s4 := s[1:3:4]
fmt.Println(s4, "| len:", len(s4), "cap:", cap(s4)) // [3 5] | len: 2 cap: 3

与简单切片表达式类似,len = high - low,但容量 cap = max - low -> 4 - 1 = 3

完整切片表达式仅支持数组、切片或指向数组的指针,不支持字符串,否则会报错:

star := "Polaris"
fmt.Println(star[1:3:2]) // 错误:3-index slice of string

完整切片表达式的索引规则

完整切片表达式索引必须满足:

0 <= low <= high <= max <= cap(s)

否则会出错:

s := []int{2, 3, 5, 7, 11}
fmt.Println(s[1:3:2]) // 错误:2 < 3
fmt.Println(s[1:3:6]) // 运行时错误:slice bounds out of range [::6] with capacity 5

在完整切片表达式中,只有 low 可以省略,缺省为 0;但 highmax 不允许省略:

fmt.Println(s[:3:5]) // [2 3 5]
fmt.Println(s[1::5]) // 错误:必须提供中间索引
fmt.Println(s[1:3:]) // 错误:必须提供最后索引

总结

本节你学习了 Go 中的切片表达式,主要内容包括:

  • 简单切片表达式及其索引 lowhigh

  • 如何通过简单切片表达式获取子切片和子串;

  • 子切片共享父切片的底层数组;

  • 完整切片表达式及其索引 lowhighmax

  • 完整切片表达式不支持字符串,只能用于切片、数组或指向数组的指针。

评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值