Simultaneous variable assignment in Go different from individual variable assignment

huangapple go评论82阅读模式
英文:

Simultaneous variable assignment in Go different from individual variable assignment

问题

我之前的印象是,尽管语法上有所不同,但下面的函数a和函数b在逻辑上是等价的。然而,它们并不等价,我不理解它们之间的区别。

在我看来,它们都是在进行多变量赋值:

  • 将变量z的值设为x的值,
  • 将变量x的值设为y的值,
  • 将变量y的值设为x+y的值。

有人能帮助我澄清关于多变量赋值和函数a与函数b之间的逻辑差异的误解吗?

package main

import "fmt"

func a() (int, int, int) {
    x := 1
    y := 2
    z := 3

    z = x
    x = y
    y = x + y

    return x, y, z
}

func b() (int, int, int) {
    x := 1
    y := 2
    z := 3

    z, x, y = x, y, x+y

    return x, y, z
}

func main() {
    fmt.Println(a()) // 输出 2 4 1
    fmt.Println(b()) // 输出 2 3 1
}

以上是要翻译的内容。

英文:

I was under the impression that despite the differences in syntax, function a and function b below were logically equivalent. However, they are not and I do not understand the difference between them.

It seems to me that they are both assigning:

  • the value of x to the variable z,
  • the value of y to the variable x, and
  • the value of x+y to the variable y.

Could anyone help clear up my misunderstanding regarding the multiple variable assignment and the logical difference between function a and function b?

package main

import "fmt"

func a() (int, int, int) {
    x:=1
    y:=2
    z:=3

    z = x
    x = y
    y = x+y

    return x, y, z
}

func b() (int, int, int) {
    x:=1
    y:=2
    z:=3

    z, x, y = x, y, x+y

    return x, y, z
}

func main() {
    fmt.Println(a()) // prints 2 4 1
    fmt.Println(b()) // prints 2 3 1
}

答案1

得分: 5

赋值可以被看作是一个“原子”操作。也就是说,有用的思考方式是将等号左边的所有值“冻结”,直到所有操作完成。

考虑以下程序:

package main

import "fmt"

func swap() (int, int) {
    x := 1
    y := 2
    x, y = y, x
    return x, y
}

func main() {
    fmt.Println(swap()) // 输出 2 1
}

如果没有这种“冻结”行为,你会得到对于 xy 都是 2 的结果,这可能不是你从代码中期望的结果。这种“冻结”行为的语义也可能更容易理解,相比采用“级联”方法。

英文:

Assignment can be thought of as an "atomic" operation. That is, it's useful to think that all values on the left hand side of the = are "frozen" until all of the operations are finished.

Consider the following program:

package main

import "fmt"

func swap() (int, int) {
	x := 1
	y := 2
	x, y = y, x
	return x, y
}

func main() {
	fmt.Println(swap()) // prints 2 1
}

Without this "freezing" behaviour, you would get 2 for both x and y, which is probably not what you'd expect from the code. It's also probably easier to reason about the semantics of this "freezing" behaviour than if the "cascading" approach were taken.

答案2

得分: 2

《Go编程语言规范》

赋值

左边的操作数数量必须等于右边的表达式数量,每个表达式必须是单值的,右边的第n个表达式被赋值给左边的第n个操作数:

one, two, three = '一', '二', '三'

空白标识符提供了一种忽略赋值右侧值的方法:

_ = x       // 计算x但忽略它
x, _ = f()  // 计算f()但忽略第二个结果值

赋值分为两个阶段。首先,按照通常的顺序计算左边的索引表达式和指针间接引用(包括选择器中的隐式指针间接引用)的操作数以及右边的表达式。其次,按照从左到右的顺序执行赋值操作。

元组赋值是一个两阶段的赋值过程。首先,按照通常的顺序计算左边的索引表达式和指针间接引用(包括选择器中的隐式指针间接引用)的操作数以及右边的表达式。其次,按照从左到右的顺序执行赋值操作。

例如,

package main

import "fmt"

func a() (int, int, int) {
    x := 1
    y := 2
    z := 3

    // 阶段1
    tx := x
    ty := y

    // 阶段2
    z = tx
    x = ty
    y = tx + ty

    return x, y, z
}

func b() (int, int, int) {
    x := 1
    y := 2
    z := 3

    z, x, y = x, y, x+y

    return x, y, z
}

func main() {
    fmt.Println(a()) 
    fmt.Println(b()) 
}

输出:

2 3 1
2 3 1
英文:

> The Go Programming Language Specification
>
> Assignments
>
> the number of operands on the left must equal the number of
> expressions on the right, each of which must be single-valued, and the
> nth expression on the right is assigned to the nth operand on the
> left:
>
> one, two, three = '一', '二', '三'
>
> The blank identifier provides a way to ignore right-hand side values
> in an assignment:
>
> _ = x // evaluate x but ignore it
> x, _ = f() // evaluate f() but ignore second result value
>
> The assignment proceeds in two phases. First, the operands of index
> expressions and pointer indirections (including implicit pointer
> indirections in selectors) on the left and the expressions on the
> right are all evaluated in the usual order. Second, the assignments
> are carried out in left-to-right order.

Tuple assignments are two phase assignment. First, the operands of index expressions and pointer indirections (including implicit pointer indirections in selectors) on the left and the expressions on the right are all evaluated in the usual order. Second, the assignments are carried out in left-to-right order.

For example,

package main

import "fmt"

func a() (int, int, int) {
	x := 1
	y := 2
	z := 3

	// phase 1
	tx := x
	ty := y

	// phase 2
	z = tx
	x = ty
	y = tx + ty

	return x, y, z
}

func b() (int, int, int) {
	x := 1
	y := 2
	z := 3

	z, x, y = x, y, x+y

	return x, y, z
}

func main() {
	fmt.Println(a()) 
	fmt.Println(b()) 
}

Output:

2 3 1
2 3 1

答案3

得分: 2

简单的答案是因为它是一个完整的语句,并且在评估x+y时,变量y的值尚未更新为2。也就是说,在任何赋值之前,右侧的表达式会先进行评估。而在另一种情况下,每一步都是逐个进行的,所以当然y的值已经更新为2,你会得到四。

这是一个有趣的学术问题,但在实际生活中,这是糟糕的代码,请不要在真实的程序中编写类似的内容。

英文:

The simple answer is because it's all one statement and the value of y hasn't been updated to 2 at the point when x+y is evaluated. IE the expression on the right hand side is evaluated prior to any assignment. In the other case everything happens on step at a time so ofc y's value has been updated to 2 and you get four.

Interesting problem for academic purposes, terrible code in real life so please don't write anything like that in a real program.

huangapple
  • 本文由 发表于 2016年3月12日 06:59:06
  • 转载请务必保留本文链接:https://go.coder-hub.com/35951423.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定