mirror of
https://github.com/gopl-zh/gopl-zh.github.com.git
synced 2024-12-26 23:06:20 +00:00
75 lines
3.0 KiB
Markdown
75 lines
3.0 KiB
Markdown
### 2.4.1. 元组赋值
|
||
|
||
元组赋值是另一种形式的赋值语句,它允许同时更新多个变量的值。在赋值之前,赋值语句右边的所有表达式将会先进行求值,然后再统一更新左边对应变量的值。这对于处理有些同时出现在元组赋值语句左右两边的变量很有帮助,例如我们可以这样交换两个变量的值:
|
||
|
||
```Go
|
||
x, y = y, x
|
||
|
||
a[i], a[j] = a[j], a[i]
|
||
```
|
||
|
||
或者是计算两个整数值的的最大公约数(GCD)(译注:GCD不是那个敏感字,而是greatest common divisor的缩写,欧几里德的GCD是最早的非平凡算法):
|
||
|
||
```Go
|
||
func gcd(x, y int) int {
|
||
for y != 0 {
|
||
x, y = y, x%y
|
||
}
|
||
return x
|
||
}
|
||
```
|
||
|
||
或者是计算斐波纳契数列(Fibonacci)的第N个数:
|
||
|
||
```Go
|
||
func fib(n int) int {
|
||
x, y := 0, 1
|
||
for i := 0; i < n; i++ {
|
||
x, y = y, x+y
|
||
}
|
||
return x
|
||
}
|
||
```
|
||
|
||
元组赋值也可以使一系列琐碎赋值更加紧凑(译注: 特别是在for循环的初始化部分),
|
||
|
||
```Go
|
||
i, j, k = 2, 3, 5
|
||
```
|
||
|
||
但如果表达式太复杂的话,应该尽量避免过度使用元组赋值;因为每个变量单独赋值语句的写法可读性会更好。
|
||
|
||
有些表达式会产生多个值,比如调用一个有多个返回值的函数。当这样一个函数调用出现在元组赋值右边的表达式中时(译注:右边不能再有其它表达式),左边变量的数目必须和右边一致。
|
||
|
||
```Go
|
||
f, err = os.Open("foo.txt") // function call returns two values
|
||
```
|
||
|
||
通常,这类函数会用额外的返回值来表达某种错误类型,例如os.Open是用额外的返回值返回一个error类型的错误,还有一些是用来返回布尔值,通常被称为ok。在稍后我们将看到的三个操作都是类似的用法。如果map查找(§4.3)、类型断言(§7.10)或通道接收(§8.4.2)出现在赋值语句的右边,它们都可能会产生两个结果,有一个额外的布尔结果表示操作是否成功:
|
||
|
||
```Go
|
||
v, ok = m[key] // map lookup
|
||
v, ok = x.(T) // type assertion
|
||
v, ok = <-ch // channel receive
|
||
```
|
||
|
||
译注:map查找(§4.3)、类型断言(§7.10)或通道接收(§8.4.2)出现在赋值语句的右边时,并不一定是产生两个结果,也可能只产生一个结果。对于值产生一个结果的情形,map查找失败时会返回零值,类型断言失败时会发送运行时panic异常,通道接收失败时会返回零值(阻塞不算是失败)。例如下面的例子:
|
||
|
||
```Go
|
||
v = m[key] // map查找,失败时返回零值
|
||
v = x.(T) // type断言,失败时panic异常
|
||
v = <-ch // 管道接收,失败时返回零值(阻塞不算是失败)
|
||
|
||
_, ok = m[key] // map返回2个值
|
||
_, ok = mm[""], false // map返回1个值
|
||
_ = mm[""] // map返回1个值
|
||
```
|
||
|
||
和变量声明一样,我们可以用下划线空白标识符`_`来丢弃不需要的值。
|
||
|
||
```Go
|
||
_, err = io.Copy(dst, src) // 丢弃字节数
|
||
_, ok = x.(T) // 只检测类型,忽略具体值
|
||
```
|
||
|