gopl-zh.github.com/ch2/ch2-04-1.md

3.0 KiB
Raw Blame History

2.4.1. 元组赋值

元组赋值是另一种形式的赋值语句,它允许同时更新多个变量的值。在赋值之前,赋值语句右边的所有表达式将会先进行求值,然后再统一更新左边对应变量的值。这对于处理有些同时出现在元组赋值语句左右两边的变量很有帮助,例如我们可以这样交换两个变量的值:

x, y = y, x

a[i], a[j] = a[j], a[i]

或者是计算两个整数值的的最大公约数GCD译注GCD不是那个敏感字而是greatest common divisor的缩写欧几里德的GCD是最早的非平凡算法

func gcd(x, y int) int {
	for y != 0 {
		x, y = y, x%y
	}
	return x
}

或者是计算斐波纳契数列Fibonacci的第N个数

func fib(n int) int {
	x, y := 0, 1
	for i := 0; i < n; i++ {
		x, y = y, x+y
	}
	return x
}

元组赋值也可以使一系列琐碎赋值更加紧凑(译注: 特别是在for循环的初始化部分

i, j, k = 2, 3, 5

但如果表达式太复杂的话,应该尽量避免过度使用元组赋值;因为每个变量单独赋值语句的写法可读性会更好。

有些表达式会产生多个值,比如调用一个有多个返回值的函数。当这样一个函数调用出现在元组赋值右边的表达式中时(译注:右边不能再有其它表达式),左边变量的数目必须和右边一致。

f, err = os.Open("foo.txt") // function call returns two values

通常这类函数会用额外的返回值来表达某种错误类型例如os.Open是用额外的返回值返回一个error类型的错误还有一些是用来返回布尔值通常被称为ok。在稍后我们将看到的三个操作都是类似的用法。如果map查找§4.3、类型断言§7.10或通道接收§8.4.2)出现在赋值语句的右边,它们都可能会产生两个结果,有一个额外的布尔结果表示操作是否成功:

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异常通道接收失败时会返回零值阻塞不算是失败。例如下面的例子

v = m[key]                // map查找失败时返回零值
v = x.(T)                 // type断言失败时panic异常
v = <-ch                  // 管道接收,失败时返回零值(阻塞不算是失败)

_, ok = m[key]            // map返回2个值
_, ok = mm[""], false     // map返回1个值
_ = mm[""]                // map返回1个值

和变量声明一样,我们可以用下划线空白标识符_来丢弃不需要的值。

_, err = io.Copy(dst, src) // 丢弃字节数
_, ok = x.(T)              // 只检测类型,忽略具体值