Changing an element inside array of arrays

Hello!
Can anyone explain a strange result of changing one element inside an array of arrays?

julia> b1=fill(zeros(4),4)
4-element Array{Array{Float64,1},1}:
[0.0, 0.0, 0.0, 0.0]
[0.0, 0.0, 0.0, 0.0]
[0.0, 0.0, 0.0, 0.0]
[0.0, 0.0, 0.0, 0.0]

julia> b1[1][1]=100.0
100.0

julia> b1
4-element Array{Array{Float64,1},1}:
[100.0, 0.0, 0.0, 0.0]
[100.0, 0.0, 0.0, 0.0]
[100.0, 0.0, 0.0, 0.0]
[100.0, 0.0, 0.0, 0.0]

What you actually do is:

julia> z=zeros(4)
4-element Array{Float64,1}:
 0.0
 0.0
 0.0
 0.0

julia> b1=[z,z,z,z]
4-element Array{Array{Float64,1},1}:
 [0.0, 0.0, 0.0, 0.0]
 [0.0, 0.0, 0.0, 0.0]
 [0.0, 0.0, 0.0, 0.0]
 [0.0, 0.0, 0.0, 0.0]

Now doing

julia> z[1]=100.0
100.0

julia> b1
4-element Array{Array{Float64,1},1}:
 [100.0, 0.0, 0.0, 0.0]
 [100.0, 0.0, 0.0, 0.0]
 [100.0, 0.0, 0.0, 0.0]
 [100.0, 0.0, 0.0, 0.0]

Is not so surprising anymore.

What you want, I would create with:

julia> b2 = [ zeros(4) for i in 1:4 ]
4-element Array{Array{Float64,1},1}:
 [0.0, 0.0, 0.0, 0.0]
 [0.0, 0.0, 0.0, 0.0]
 [0.0, 0.0, 0.0, 0.0]
 [0.0, 0.0, 0.0, 0.0]

julia> b2[1][1]=100.0
100.0

julia> b2
4-element Array{Array{Float64,1},1}:
 [100.0, 0.0, 0.0, 0.0]
 [0.0, 0.0, 0.0, 0.0]
 [0.0, 0.0, 0.0, 0.0]
 [0.0, 0.0, 0.0, 0.0]

but there are probably more elegant ways.

Thank you for the decision.
Yet it’s still unclear how the equal arrays can produce different results.

julia> b1=fill(zeros(4),4)
4-element Array{Array{Float64,1},1}:
[0.0, 0.0, 0.0, 0.0]
[0.0, 0.0, 0.0, 0.0]
[0.0, 0.0, 0.0, 0.0]
[0.0, 0.0, 0.0, 0.0]

julia> b2=[zeros(4) for i in 1:4]
4-element Array{Array{Float64,1},1}:
[0.0, 0.0, 0.0, 0.0]
[0.0, 0.0, 0.0, 0.0]
[0.0, 0.0, 0.0, 0.0]
[0.0, 0.0, 0.0, 0.0]

julia> isequal(b1,b2)
true

julia> b1==b2
true

julia> b1===b2
false
1 Like

isequal (or also == FWIW) only compares the value. It ignores many other properties of the object being compared.

As examples.

julia> a = [1, 2, 3]; b = 1:3
1:3

julia> isequal(a, b)
true

julia> push!(a, 1)
4-element Array{Int64,1}:
 1
 2
 3
 1

julia> push!(b, 1)
ERROR: MethodError: no method matching resize!(::UnitRange{Int64}, ::Int64)
Closest candidates are:
  resize!(::Array{T,1} where T, ::Integer) at array.jl:1016
  resize!(::BitArray{1}, ::Integer) at bitarray.jl:773
Stacktrace:
 [1] _append!(::UnitRange{Int64}, ::Base.HasLength, ::Tuple{Int64}) at ./array.jl:920
 [2] append!(::UnitRange{Int64}, ::Tuple{Int64}) at ./array.jl:914
 [3] push!(::UnitRange{Int64}, ::Int64) at ./array.jl:915
 [4] top-level scope at REPL[4]:1

julia> isequal(0x1, 1)
true

julia> -0x1
0xff

julia> -1
-1

OTOH, is (or ===) checks if the two objects are the same one. There is not a general way to compare if the two objects “behaves identically” since that’s not really a well defined concept anyway…

1 Like

In the fill case there is only one array referenced 4 times. Equality relates to the values in the object, not the memory layout.

Thanks a lot for the explanations.
To check it I can compare elements inside both arrays.

julia> b1[1]===b1[2]
true

julia> b2[1]===b2[2]
false

1 Like