golang 交换值运算顺序的探究
笔力较差,文章晦涩,望多指正!
序言
在 golang 的语法体系中,提供了 = 简单地值地交换,即进行多右值匹配多左值的赋值运算操作。
比如常见的:
a, b = b, a
a, b, c = c, b, a
a[i], a[j] = a[j], a[i]
但当我们的右值更为复杂时,它的赋值执行顺序究竟又是怎样的呢?
a, b = calx(b), caly(a)
问题:
- 它是逐项计算、赋值,还是全部计算完、再统一进行赋值呢?
- 它是先计算
cal1(b),还是先计算cal2(a)呢(从左到右,还是从右到左)?
实际中,遇到的问题:
这份问题代码,后面我们统称为 Annoy Code 或 acode
package main
import "fmt"
func main() {
var (
first int
array = []int{1, 2, 3, 4, 5}
)
for len(array) != 0 {
first, array = array[0], append(array[:0], array[1:]...)
// 取出第一个值,并从数组中移除:
// 实际生产中请使用:first, array = array[0], array[1:]
// 即可得到预期结果。
// 这里仅是为了还原问题。
fmt.Println(first, array)
}
}
预期输出结果:
1 [2 3 4 5]
2 [3 4 5]
3 [4 5]
4 [5]
5 []
实际输出:
2 [2 3 4 5]
3 [3 4 5]
4 [4 5]
5 [5]
5 []
结论
- 全部计算完、再统一进行赋值
- 从右到左
a, b = calx(b), caly(a)
// 等价于
func sway(){
tmp1 := caly(a)
tmp2 := calx(b)
return tmp2, tmp1
}
a, b = swap()
猜想与验证
目前有 4 种猜想:
- 逐项赋值,从左到右
- 逐项赋值,从右到左
- 统一赋值,从左到右
- 统一赋值,从右到左
验证:逐项赋值
如果是逐项赋值的话,也就意味着在交换值中每一项右值都先运算完,然后赋值给左值。
若为猜想一,从左到右,可将 acode 拆解为:
package main
import "fmt"
func main() {
var (
first int
array = []int{1, 2, 3, 4, 5}
)
for len(array) != 0 {
first = array[0]
array = append(array[:0], array[1:]...)
fmt.Println(first, array)
}
}
输出:
1 [2 3 4 5]
2 [3 4 5]
3 [4 5]
4 [5]
5 []
显然与 acode 输出不一致,猜想不成立。
若为猜想二,从右到左,可将 acode 拆解为:
package main
import "fmt"
func main() {
var (
first int
array = []int{1, 2, 3, 4, 5}
)
for len(array) != 0 {
array = append(array[:0], array[1:]...)
first = array[0]
fmt.Println(first, array)
}
}
输出:
2 [2 3 4 5]
3 [3 4 5]
4 [4 5]
5 [5]
panic: runtime error: index out of range [0] with length 0
...
报错,猜想不成立。
验证:统一赋值
如果是逐项赋值的话,也就意味着在交换值中先计算全部右值,然后向左赋值。
若为猜想一,从左到右,可将 acode 拆解为:
package main
import "fmt"
func getFirst(array []int) (int, []int) {
first := array[0]
tmpArray := append(array[:0], array[1:]...)
return first, tmpArray
}
func main() {
var (
first int
array = []int{1, 2, 3, 4, 5}
)
for len(array) != 0 {
first, array = getFirst(array)
fmt.Println(first, array)
}
}
输出:
1 [2 3 4 5]
2 [3 4 5]
3 [4 5]
4 [5]
5 []
显然与 acode 输出不一致,猜想不成立。
若为猜想二,从右到左,可将 acode 拆解为:
package main
import "fmt"
func getFirst(array []int) (int, []int) {
tmpArray := append(array[:0], array[1:]...)
first := array[0]
return first, tmpArray
}
func main() {
var (
first int
array = []int{1, 2, 3, 4, 5}
)
for len(array) != 0 {
first, array = getFirst(array)
fmt.Println(first, array)
}
}
输出:
2 [2 3 4 5]
3 [3 4 5]
4 [4 5]
5 [5]
5 []
报错,但输出与 acode 一致,猜想成立。
- 同为从右到左的猜想,猜想四中引入了临时变量tmp*,所以没有导致数组越界,而猜想二引入不了临时变量而导致了数组越界。