Alexander Kurz
Alexander Kurz

Reputation: 308

Why does updating a global vector with for-loop and with list comprehension in Julia give different results?

I pasted the full MWE below. Since it is a bit long, I first give a break-down.

Task: I have a global vector vec that is going to be randomly updated in a loop. I have a global vector vecvec (vector of vectors) that is supposed to store the history of the vecs.

Attempted Solution (does not work): The function make_vec() randomly updates vec. I put this in a loop as follows.

for i in 1:5
    make_vec()     
    println(vec)
    push!(vecvec,vec)
end

Running this (or rather the MWE below), one can see from the printed output, that the global variable vec is updated by make_vec() in each round of the loop as intended. But:

Question 1: Why then are all vectors in the collected history vecvec the same?

I can guess an answer to the question, namely that push! does not push the actual vector vec but only a reference to vec. Thus, when I inspect vecvec after completion of the loop, all entries are identical to the last update of vec.

But this raises another question.

Question 2: In the MWE below, I add two alternative implementations of make_vec(). In particular, make_vec2() (make_vec3() is similar) is the same as make_vec() but replaces the for-loop with list comprehension. Why does replacing make_vec() in the loop by make_vec2() give a different result (in fact the desired result)?

vec = [1,2,3]
vecvec = []

function make_vec() 
    for i in 1:3
        vec[i] = rand(0:9)
    end
end

function make2_vec()
    global vec = [rand(0:9) for i=1:3]
end

function make3_vec()
    local vec3 = [1,2,3]
    for i in 1:3
        vec3[i] = rand(0:9)
    end
    global vec = vec3
end

    
for i in 1:5
    make_vec()     # make_vec2 and make_vec3 would give the desired result, but not make_vec
    println(vec)
    push!(vecvec,vec)
end

print(vecvec)

I run this in a Jupyter notebook with kernel 1.8.3.

Upvotes: 2

Views: 134

Answers (1)

Bogumił Kamiński
Bogumił Kamiński

Reputation: 69949

In

    for i in 1:3
        vec[i] = rand(0:9)
    end

you do not change the value (array in this case) that vec is bound to. Instead you only change the elements stored in the array. This is typically called in-place update.

On the other hand:

    global vec = [rand(0:9) for i=1:3]

allocates a new vector and binds this new value to vec variable. This means that instead of updating values in some container you replace the container.

The same with:

    local vec3 = [1,2,3]
    for i in 1:3
        vec3[i] = rand(0:9)
    end
    global vec = vec3

The local vec3 = [1,2,3] allocates new container and global vec = vec3 binds this new container to vec variable.

Is this clear?


As a side note - it is better do not use vec as variable name as vec is a function name that is defined in Base Julia.

Upvotes: 5

Related Questions