Search code examples
arraysjuliapushglobalread-eval-print-loop

Update and store a global Julia array with !push


I'm a bit puzzled about the way that Julia 1.0.3 treats global variables. Is there a way to use !push to update a global array?

While playing in the REPL, I want to update a global variable, then push! the result to a global array to store it.

var = [1]
res = []

for i in 1:5
  global var
  global res
  push!(var,i)
  print(string(var,"\n"))
  push!(res,var)
end

However, the values stored in res are as follows:

 [1, 1, 2, 3, 4, 5]
 [1, 1, 2, 3, 4, 5]
 [1, 1, 2, 3, 4, 5]
 [1, 1, 2, 3, 4, 5]
 [1, 1, 2, 3, 4, 5]

Whereas I would expect this:

[1, 1]
[1, 1, 2]
[1, 1, 2, 3]
[1, 1, 2, 3, 4]
[1, 1, 2, 3, 4, 5]

Particularly puzzling since behaviour seems as expected with variables, instead of arrays:

var = 1
res = []

for i in 1:5
  global var
  global res
  var = var + i
  print(string(var,"\n"))
  push!(res, var)
end

Which gives expected result:

  2
  4
  7
 11
 16

I am clearly missing something.


Solution

  • You're pushing the same var array to every spot in the res array. For example:

    julia> var = [1]
    1-element Array{Int64,1}:
     1
    
    julia> res = [var, var]
    2-element Array{Array{Int64,1},1}:
     [1]
     [1]
    
    julia> var[1] = 2
    2
    
    julia> res
    2-element Array{Array{Int64,1},1}:
     [2]
     [2]
    

    Both elements in the res array are var itself. So if you modify var (with push! or indexed assignment or somesuch), then no matter how you access it you'll see those modifications.

    This doesn't occur with numbers because you cannot modify numbers themselves. You can change which number is stored in an array, but you cannot change the number 1 to represent 2 everywhere that 1 had previously been used — that's the equivalent of what's happening here.

    To fix this, you'll often want to just create your var array inside the for loop (instead of outside it). But in this case, since you're iteratively adding things to var and want to save that intermediate state, you can use copy:

    julia> for i in 1:5
             global var
             global res
             push!(var,i)
             print(string(var,"\n"))
             push!(res,copy(var))
           end
    Any[1]
    Any[1, 2]
    Any[1, 2, 3]
    Any[1, 2, 3, 4]
    Any[1, 2, 3, 4, 5]
    
    julia> res
    5-element Array{Any,1}:
     Any[1]
     Any[1, 2]
     Any[1, 2, 3]
     Any[1, 2, 3, 4]
     Any[1, 2, 3, 4, 5]