golang 交换值运算顺序的探究|Go主题月

797 阅读3分钟

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 Codeacode

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 种猜想:

  1. 逐项赋值,从左到右
  2. 逐项赋值,从右到左
  3. 统一赋值,从左到右
  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*,所以没有导致数组越界,而猜想二引入不了临时变量而导致了数组越界。