Simultaneous variable assignment in Go different from individual variable assignment - go

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
}

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.

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.

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

Related

Go language simultaneous assignment through un-buffered channel: how does it work?

I'm learning Go and saw this program at go.dev/tour/concurrency/2
package main
import "fmt"
func sum(s []int, c chan int) {
sum := 0
for _, v := range s {
sum += v
}
c <- sum // send sum to c
}
func main() {
s := []int{7, 2, 8, -9, 4, 0}
c := make(chan int)
go sum(s[:len(s)/2], c)
go sum(s[len(s)/2:], c)
x, y := <-c, <-c // receive from c
fmt.Println(x, y, x+y)
}
I recall that in python the execution flow of a, b = b, a should be like:
Create a temporary tuple of (b, a)
Assign each value in (b, a) to variables in (a, b) with a loop
The method doesn't seem to apply to this occasion in GO. So what actually happens in that a, b = b, a sentence?
a, b = b, a is an assignment, more specifically a tuple assignment.
It is detailed in the Spec: Assignments:
A tuple assignment assigns the individual elements of a multi-valued operation to a list of variables.
[...]
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.
a, b = b, a // exchange a and b
So first the (current) values of b and a are read, then they are assigned to a, b, effectively swapping their values.
This:
x, y := <-c, <-c
Is a short variable declaration, it is equivalent to a regular variable declaration with initializer expressions but no types:
var x, y = <-c, <-c
This will receive from c, twice, and assign the received values to x and y.

value vs. pointer: is `*x=y` equivalent to `x=&y`?

say y := []int
say x := *[]int
Question 1: is *x=y equivalent to x=&y?
I found that, when y is updated, say y=y[1:], *x=y gave me correct updated y, whereas x=&y still gave me the old y.
Question 2: Why it is like this?
No, they're not the same, and they do what they say. *x = y modifies *x by assigning y to it (there are two different []int, one at *x and one at y). x = &y modifies x by assigning &y to it (x points to the same []int named by y).

Assigning values to multiple variables in Go

I recently came up across this:
func main() {
x, y := 0, 1
x, y = y, x+y
fmt.Println(y)
}
What I thought was that:
x, y = y, x+y
Is identical to:
x = y
y = x+y
Which would result to final values x = 1, y = 2
However the final values I get is x = 1, y = 1
Why is that?
Thanks.
This is how it's specified:
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.
Assignment first evaluates all expressions on the right side and then assigns the results to the variables on the left side.
Your
x, y = y, x+y
is basically equivalent to this
tmp1 := y
tmp2 := x+y
x = tmp1
y = tmp2
You can even use this fact to swap 2 variables in one line, like this:
a, b = b, a

How the shorthand of declaration & initialization are evaluated in go lang?

The shorthand for declaration and initialization in go is
var a, b, c = 1 , 2, 3
Equivalent to following way of declaration and initialization (as per specs)
a:=1
b:=2
c:=3
var a int
var b int
var c int
a=1
b=2
c=3
But I am not getting the answer for the problem found in following code:
package main
import "fmt"
func main() {
var a int = 0
var b int = 1
fmt.Println("init a ",a)
fmt.Println("init b ",b)
a, b = b, a+b
fmt.Println("printing a after `a, b = b, a+b`",a)
fmt.Println("printing b after `a, b = b, a+b`",b)
}
Output should be:
printing a after 'a, b = b, a+b' 1
printing b after 'a, b = b, a+b' 2
Since the value of b is evaluated with a + b i.e 1+1 = 2. But its giving 1.
Here is the playground links of both the working code where you can observe the difference.
a,b = b, a+b
a=b, b=a+b
I know I am missing something to understand, basically how the shorthand expression are evaluated especially when the same variable is involved in the expression.
But where is the proper documentation to refer. Could anyone help on this?
See here
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.
Based on that a+b (0+1) is evaluated first. Then it's assigned. Thus you get the result of a = 1 and b = 1

Is there a function that takes two values, lets f(x,y) == f(y,x), and the output is otherwise unique?

I am wondering if there is a way to generate a key based on the relationship between two entities in a way that the key for relationship a->b is the same as the key for relationship b->a.
Desirably this would be a hash function which takes either relationship member but generates the same output regardless of the order the members are presented in.
Obviously you could do this with numbers (e.g. add(2,3) is equivalent to add(3,2)). The problem for me is that I do not want add(1,4) to equal add(2,3). Obviously any hash function has overlap but I mean a weak sense of uniqueness.
My naive (and performance undesirable) thought is:
function orderIndifferentHash(string val1, string val2)
{
return stringMerge(hash(val1), hash(val2));
/* String merge will 'add' each character (with wrapping).
The pre-hash is to lengthen strings to at least 32 characters */
}
In your function orderIndifferentHash you could first order val1 and val2 by some criteria and then apply any hash function you like to get the result.
function orderIndifferentHash( val1, val2 ) {
if( val1 < val2 ) {
first = val1
second = val2
}
else {
first = val2
second = val1
}
hashInput = concat( first, second )
return someHash( hashInput )
// or as an alternative:
// return concat( someHash( first ), someHash( second ) )
}
With numbers, one way to achieve that is for two numbers x and y take the x-th prime and y-th prime and calculate the product of these primes. That way you will guarantee the uniqueness of the product for each distinct pair of x and y and independence from the argument order. Of course, in order to do that with any practically meaningful efficiency you'll need to keep a prime table for all possible values of x and y. If x and y are chosen from relatively small range, this will work. But if range is large, the table itself becomes prohibitively impractical, and you'll have no other choice but to accept some probability of collision (like keep a reasonably sized table of N primes and select the x%N-th prime for the given value of x).
Alternative solution, already mentioned in the other answers is to build a perfect hash function that works on your x and y values and then simply concatenate the hashes for x and y. The order independence is achieved by pre-sorting x and y. Of course, building a perfect hash is only possible for a set of arguments from a reasonably small range.
Something tells me that the primes-based approach will give you the shortest possible hash that satisfies the required conditions. No, not true.
You you are after:
Some function f(x, y) such that
f(x, y) == f(y, x)
f(x, y) != f(a, b) => (x == a and y == b) or (x == b and y == a)
There are going to be absolutely loads of these - off hand the one I can think of is "sorted concatenation":
Sort (x, y) by any ordering
Apply a hash function u(a) to x and y individually (where u(a) == u(b) implies a == b, and the length of u(a) is constant)
Concatenate u(x) and u(y).
In this case:
If x == y then then the two hashes are trivially the same, so without loss of generality x < y, hence:
f(y, x) = u(x) + u(y) = f(x, y)
Also, if f(x, y) == f(a, b), this means that either:
u(x) == u(a) and u(y) == u(b) => x == a and y == b, or
u(y) == u(a) and u(x) == u(b) => y == a and x == b
Short version:
Sort x and y, and then apply any hash function where the resulting hash length is constant.
Suppose you have any hash h(x,y). Then define f(x,y) = h(x,y) + h(y,x). Now you have a symmetric hash.
(If you do a trivial multiplicative "hash" then 1+3 and 2+2 might hash to the same value, but even something like h(x,y) = x*y*y will avoid that--just make sure there's some nonlinearity in at least one argument of the hash function.)

Resources