I was just wondering, why Julia outputs the following
a = [1,2,3]
b = a
b.+=[5,5,5]
Then the variable a is updated as well, so after the code is run a=[6,7,8].
Why is this? If the last line is b+=[5,5,5], a isn't updated and stays a=[1,2,3]
I was just wondering, why Julia outputs the following
a = [1,2,3]
b = a
b.+=[5,5,5]
Then the variable a is updated as well, so after the code is run a=[6,7,8].
Why is this? If the last line is b+=[5,5,5], a isn't updated and stays a=[1,2,3]
b = a does not create a copy, but simply points the label b to the object referenced by a. When you do b = something_else, you just reassign the label b to point to something else. However once you change elements within b (i.e. not the binding), you are changing a as well, because they both point to the same underlying object. You don't even need += to see this, just regular assignments will do:
julia> a = [1,2,3];
julia> b = a;
julia> b = 5;
julia> a
3-element Vector{Int64}:
1
2
3
julia> b = a;
julia> b[1] = 10;
julia> a
3-element Vector{Int64}:
10
2
3
Now the difference between .+= and += is that b += [5, 5, 5] means b = b + [5, 5, 5], i.e. you're essentially doing b = [6, 7, 8], thereby replacing the binding of b. If you do b .+= [5, 5, 5], you are broadcasting the assignment elementwise, that is you are changing each element within b, but not the object (the vector holding your integers) which b points to, which in that case is still the same as a.
In Julia when you write:
a = [1, 2, 3]
you make a binding between name a and value [1, 2, 3]. This is an assignment operation.
Similarly when you write:
b = a
Julia makes a binding of name b to the same value (no copying is performed).
So after these first two operations names a and b are bound to the same value [1, 2, 3] (exactly the same object in memory).
Now the difference between:
b .+= [5, 5, 5]
and
b += [5, 5, 5]
is that .+= is a broadcasting assignment operation which is in-place. In-place means that the object that b name is bound to is updated. No new memory is allocated.
On the other hand += is a standard assignment and in this case when you perform the addition of [1, 2, 3] (that b name points to) and [5, 5, 5] a new vector is allocated and bound to name b. However, this does not affect the original vector [1, 2, 3] that is still stored in memory. Since a name is bound to the [1, 2, 3] vector it is unaffected.
In summary: .+= is an in place operation, while += allocates a new vector because addition of two vectors with + (non broadcasted) creates a new vector.
Finally let me highlight that the left hand side of the broadcasting assignment must be a container that allows for in-place update (technically a copyto! function is called).
Therefore we have the following:
julia> a = (1, 2, 3)
(1, 2, 3)
julia> b = a
(1, 2, 3)
julia> b .+= (5, 5, 5)
ERROR: MethodError: no method matching copyto!(::Tuple{Int64, Int64, Int64}, ::Base.Broadcast.Broadcasted{Base.Broadcast.Style{Tuple}, Tuple{Base.OneTo{Int64}}, typeof(+), Tuple{Tuple{Int64, Int64, Int64}, Tuple{Int64, Int64, Int64}}})
julia> b += (5, 5, 5)
ERROR: MethodError: no method matching +(::Tuple{Int64, Int64, Int64}, ::Tuple{Int64, Int64, Int64})
julia> b = b .+ (5, 5, 5)
(6, 7, 8)
julia> a
(1, 2, 3)
The behavior you see is caused by the fact that Tuples are immutable and also the + operation is undefined for them, but as you can see in the last line you can broadcasst + to add tuples, but this addition creates a new tuple.