Create initialized arrays of structs


#1

Dearests,

today I found a disturbing (yet documented … ) behavior of julia. Let’s see:

struct T a::Vector{Int}; end
T(N) = T(zeros(Int,N))

Now aiming at a Vector of initialized Ts I (mistakenly) did a

julia> x=fill(T(3),2)
2-element Array{T,1}:
 T([0, 0, 0])
 T([0, 0, 0])

The problem is that now the 2 elements in the vector are indeed the same:

julia> x[1].a[1] = 1;
julia> x
2-element Array{T,1}:
 T([1, 0, 0])
 T([1, 0, 0])

No big deal, a comprehension would have produced my intended result:

julia> y = [T(3) for i=1:2];
julia> y[1].a[1] = 1;
julia> y
2-element Array{T,1}:
 T([1, 0, 0])
 T([0, 0, 0])

hooray! the elements are now different. Of course, reading the docs is always a good idea: ?fill
“[…] If x is an object reference, all elements will refer to the same object. fill(Foo(), dims) will return an array filled with the result of evaluating
Foo() once.”

Now the two questions:

  1. What is the rationale behind having fill returning “all elements referring to the same object”
  2. What is the julian way to do what I wanted, beside the comprehension? For vectors comprehensions are ok but with arrays with more elaborated layouts it is a bit of a pain.

Cheers

Andrea


#2

What is the rationale behind having fill returning “all elements referring to the same object”

What else would you want fill(x, n) to do? Call copy(x)? Or perhaps deepcopy(x)? Julia never silently copies your objects in any other circumstance, so it would be very unexpected for it to do so here.

What is the julian way to do what I wanted, beside the comprehension? For vectors comprehensions are ok but with arrays with more elaborated layouts it is a bit of a pain.

With multiple loop variables I don’t think it’s too bad:

julia> [i + j + k for i in 1:2, j in 1:3, k in 1:5]
2×3×5 Array{Int64,3}:
[:, :, 1] =
 3  4  5
 4  5  6

[:, :, 2] =
 4  5  6
 5  6  7

[:, :, 3] =
 5  6  7
 6  7  8

[:, :, 4] =
 6  7  8
 7  8  9

[:, :, 5] =
 7  8   9
 8  9  10

Or, if you don’t like the way the comprehension syntax works, you could wrap it in a function. Here’s one idea:

julia> myfill(f::Function, dims...) = [f(I.I...) for I in CartesianRange(Base.OneTo.(dims))]
myfill (generic function with 1 method)

julia> myfill((i, j, k)-> i + j + k, 2, 3, 4)
2×3×4 Array{Int64,3}:
[:, :, 1] =
 3  4  5
 4  5  6

[:, :, 2] =
 4  5  6
 5  6  7

[:, :, 3] =
 5  6  7
 6  7  8

[:, :, 4] =
 6  7  8
 7  8  9

now you can even use the do-block syntax:

julia> myfill(2, 3, 4) do i, j, k
         i + j + k
       end
2×3×4 Array{Int64,3}:
[:, :, 1] =
 3  4  5
 4  5  6

[:, :, 2] =
 4  5  6
 5  6  7

[:, :, 3] =
 5  6  7
 6  7  8

[:, :, 4] =
 6  7  8
 7  8  9

#3

What about other shapes, as e.g. Diagonal? Is there any “general” way of
iterating over the stored values (think about banded and triangular
matrices etc) as fill! does?
thanks!
A/