Julia updates variable

Viewed 96

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]

2 Answers

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.

Related