Push! seems to be incorrect in this situation

function Append(vectorOfVector, vector)
    vector[1] = 0
    push!(vectorOfVector,vector)
    vector[1] = 1
    push!(vectorOfVector,vector)
end

vectorOfVectors = Vector{Vector{Int64}}()
vector = Vector{Int64}(undef,1)
Append(vectorOfVectors, vector)
vectorOfVectors

My Julia version is 1.2.0. When I try to push vectors to a vector container by the function above, the output are not as expected:

2-element Array{Array{Int64,1},1}:
 [1]
 [1]

Should not the variable vectorOfVectors turns out to be [[0],[1]] ?

1 Like

Welcome @ke_xu !

You may have a look into this proposal for your function:

function Append!(vectorOfVector, vector)
    vector[1] = 0
    push!(vectorOfVector,deepcopy(vector))
    vector[1] = 1
    push!(vectorOfVector,deepcopy(vector))
end

And you can find an explanation here (it’s better than what I could explain with my own words):
https://docs.julialang.org/en/v1.2/manual/arrays/

at the beginning:

In Julia, all arguments to functions are passed by sharing (i.e. by pointers). Some technical computing languages pass arrays by value, and while this prevents accidental modification by callees of a value in the caller, it makes avoiding unwanted copying of arrays difficult. By convention, a function name ending with a ! indicates that it will mutate or destroy the value of one or more of its arguments (compare, for example, sort and sort! ). Callees must make explicit copies to ensure that they don’t modify inputs that they don’t intend to change. Many non- mutating functions are implemented by calling a function of the same name with an added ! at the end on an explicit copy of the input, and returning that copy.

julia> function Append!(vectorOfVector, vector)
           vector[1] = 0
           push!(vectorOfVector,deepcopy(vector))
               vector[1] = 1
           push!(vectorOfVector,deepcopy(vector))
       end
Append! (generic function with 1 method)

julia> vectorOfVectors = Vector{Vector{Int64}}()
0-element Array{Array{Int64,1},1}

julia> vector = Vector{Int64}(undef,1)
1-element Array{Int64,1}:
 0

julia> Append!(vectorOfVectors, vector)
2-element Array{Array{Int64,1},1}:
 [0]
 [1]
1 Like

Hi oheil,
Thanks for your replying. I tested your code and it works well.
But I don’t really understand why making a copy of vector can make change. If use vector directly (not copy(vector)), it should be [0] and [1] before being pushed in the two times respectively. So the two push! functions equal push!(vectorOfVector, [0]) and push!(vectorOfVector, [1]). But how the second-time push! function changed the first value of vectorOfVector?

The line

push!(vectorOfVector,vector)

pushes just a pointer to vector into the container.
Than the line

vector[1] = 1

changes the value at index 1 of your one and only existing vector.

In other words:

In those four lines:

vector = Vector{Int64}(undef,1)
vector[1] = 0
push!(vectorOfVector,vector)
push!(vectorOfVector,vector)

we only have a single instance/memory allocation of vector. Every change on a value at some index of vector will be visible in vector itself, at index 1 of vectorOfVector and at index 2 of vectorOfVector, because its all pointing to the same memory.

So the two push! functions equal push!(vectorOfVector, [0]) and push!(vectorOfVector, [1]).

And thats not true. [0] and [1] above are two different vectors/arrays pointing to different locations in memory.

2 Likes

Thank you very much.
Variable vectorOfVector just contained the pointers to value.

This is only true because in this case “value” is a vector/array.
See e.g.:

julia> vectorOfAnything = Vector{Any}()
0-element Array{Any,1}

julia> vector = Vector{Int64}(undef,1)
1-element Array{Int64,1}:
 292839264

julia> vector[1]=0
0

julia> push!(vectorOfAnything,vector)
1-element Array{Any,1}:
 [0]

julia> x=0
0

julia> push!(vectorOfAnything,x)
2-element Array{Any,1}:
  [0]
 0

julia> push!(vectorOfAnything,vector)
3-element Array{Any,1}:
  [0]
 0
  [0]

julia> x=1
1

julia> push!(vectorOfAnything,x)
4-element Array{Any,1}:
  [0]
 0
  [0]
 1

julia> vector[1]=1
1

julia> x=0
0

julia> vectorOfAnything
4-element Array{Any,1}:
  [1]
 0
  [1]
 1

julia> 

You can think of it as always being true. Doing x= something wouldn’t change the value in the array even in the case where the value is an array. In fact, an assignment can never have an observable effect in some other object.

It will never be wrong to think of all objects being passed via pointer (modulo C interop). Some objects just happen to be immutable.

2 Likes

:thinking: This needs some time to digest :laughing: