It's easy to think about it also only on the paper (e.g. at the job interview) and you don't need to debug or disassemble code to bytecode for understanding.
I also think it hasn't anything to do with the implementation of swap function in C++. These are unrelated things.
What you only need to know is that the right side is completely evaluated first and then the values from the right side of the expression are assigned to the values on the left side in the order from the left to the right. Sophros answered it right way I only expand the idea further and in more detail.
Imagine the first case. We have:
a = [2,1,0]
a[0], a[a[0]] = a[a[0]], a[0]
When we start to execute this code, the right side evaluates first, so we will have
a[0], a[a[0]] = a[a[0]], a[0] # a[a[0]] == 0, a[0] == 2, a == [2, 1, 0]
On the right side, we have tuple (0, 2)
and a
is still [2, 1, 0]
Next, we start to assign to the left side of the expression from the left, so to the a[0]
we assign the first item from the tuple, which is 0
. Now we have
a[0], a[a[0]] = (0, 2) # a[0] == 0, a == [0, 1, 0]
And now we execute the last part of the assignment, which is to a[a[0]]
assign 2
. But a[0]
is now 0
, so after reduction we assign to a[0]
value 2
. Therefore values after the last assignment are
a[0], a[a[0]] = (0, 2) # a[a[0]] == 2, a == [2, 1, 0]
Which seems, that nothing changed and values didn't swap, but as is apparent from above a
was [2,1,0]
, then [0,1,0]
and lastly again [2,1,0]
. So it seems, nothing changed and swap doesn't work.
And now the second case, where we only change the order of variables in the expression:
a = [2,1,0]
a[a[0]], a[0] = a[0], a[a[0]]
When we start to execute this code, the right side evaluates first, so we will have
a[a[0]], a[0] = a[0], a[a[0]] # a[0] == 2, a[a[0]] == 0, a == [2, 1, 0]
On the right side, we have tuple (2, 0)
and a
is still [2, 1, 0]
Next, we start to assign to the left side of the expression from the left, so to the a[a[0]]
we assign the first item from the tuple, which is 2
. a[0]
is 2
, so after reduction, we assign to a[2]
value 2
. Now we have
a[a[0]], a[0] = (2, 0) # a[a[0]] == 2, a == [2, 1, 2]
And now we execute the last part of the assignment, which is to a[0]
assign 0
. Therefore values after the last assignment are
a[a[0]], a[0] = (2, 0) # a[0] == 0, a == [0, 1, 2]
Now this works as expected.
So it is necessary also think about the order when you have dependent variables in your swap expression. As dependent variables I mean that in the first case we have on the left side a[0], a[a[0]]
that means a[0]
change it's value and a[a[0]]
use this changed value, which leads to unwanted behavior.
Finally, regardless of the programming language, it's better not to use dependent variables (array index for another array index etc.), when you want to swap their values.