Arrays of arrays.

127 views
Skip to first unread message

Aleksandr Mikheev

unread,
Nov 29, 2015, 5:51:28 AM11/29/15
to julia-users
Hi all. Once again I have some questions about Julia.

I know that there is a possibility to create a list of arrays. For exmple:

s = fill(Array(Int64,1),4)

And then I can do something like this:

s[1] = [1; 2]
s
[1] = [s[1]; 5]

By parity of reasoning I did this:

s = fill(Array(Int64,1),4,4,4)

And it worked fine. But in both cases I had initial elements in s (like when I construct arrays with Array{Int64}(m,n)):

julia> s = fill(Array(Int64,1),3,3,3)
3x3x3 Array{Array{Int64,1},3}:
[:, :, 1] =
 
[2221816832]  [2221816832]  [2221816832]
 
[2221816832]  [2221816832]  [2221816832]
 
[2221816832]  [2221816832]  [2221816832]


[:, :, 2] =
 
[2221816832]  [2221816832]  [2221816832]
 
[2221816832]  [2221816832]  [2221816832]
 
[2221816832]  [2221816832]  [2221816832]


[:, :, 3] =
 
[2221816832]  [2221816832]  [2221816832]
 
[2221816832]  [2221816832]  [2221816832]
 
[2221816832]  [2221816832]  [2221816832]


Is there something I could do to prevent this? I know I could easily fix it by:


for i = 1:3
for j = 1:3
for k = 1:3
s
[i,j,k] = []
end
end
end

But I guess this is a weird solution.

Thank you in advance!

Milan Bouchet-Valat

unread,
Nov 29, 2015, 6:06:47 AM11/29/15
to julia...@googlegroups.com
I'm not sure exactly what kind of list of arrays you're trying to
create. If you want an array of empty arrays, the best solution is the
for loops you show above, or a shorter comprehension form:
[[] for i in 1:3, j in 1:3, k=1:3]

On the contrary, be careful with fill() as it doesn't make a copy of
its first argument. That is, it will create an array in which all
elements hold a reference to a single common object. So check that,
call e.g.:
s[1,1][1] = 0
and see how all the elements in the array are affected.


Finally, if your goal is to fill the array later with arrays, you can
simply create an empty array of arrays:
x = Array{Array{Int,1}}(4, 4, 4)

and fill it manually later:
x[1,1] = [1]

as long as you don't try to access undefined elements.


Regards

Kristoffer Carlsson

unread,
Nov 29, 2015, 6:36:03 AM11/29/15
to julia-users
I guess the simplest would be:

[Int[] for i = 1:3, j=1:3, k=1:3]


And to repeat what Milan already said, you don't want fill! because then all your arrays point to the same memory location.

James Gilbert

unread,
Nov 29, 2015, 6:56:10 AM11/29/15
to julia-users
This code:

  fill(Array(Int64,1),3,3,3)

Constructs a 3D array of length 3 in each dimension where each element is a reference to the same single element array containing a single Int64.  The Int64 is uninitialised, so it has the value of whatever that piece of memory happens to hold.  You should have used curly braces to specify the type (rather than parentheses, which constructs an Array object):

  fill(Array{Int64,1},3,3,3)

but like Milan says, I don't think that can be what you want, because you are making a 3D array where each element is a reference to the same array.  What do you actually want each element of your array to hold?  To create a 3D array filled with zero integers, all you need to do is:

julia> a = fill(0,3,3,3)
3x3x3 Array{Int64,3}:
[:, :, 1] =
 0  0  0
 0  0  0
 0  0  0

[:, :, 2] =
 0  0  0
 0  0  0
 0  0  0

[:, :, 3] =
 0  0  0
 0  0  0
 0  0  0

Or you can be be specific about the type if you need to:

julia> a = fill(Int16(0),3,3,3)
3x3x3 Array{Int16,3}:
[:, :, 1] =
 0  0  0
 0  0  0
 0  0  0

[:, :, 2] =
 0  0  0
 0  0  0
 0  0  0

[:, :, 3] =
 0  0  0
 0  0  0
 0  0  0

Integers are immutable types, so are passed by copying, not reference, and so each element has its own value.

James Gilbert

unread,
Nov 29, 2015, 7:15:28 AM11/29/15
to julia-users
Sorry, this:

  fill(Array{Int64,1},3,3,3)

creates a 3D array of types.  So that's just as useless.

Dan

unread,
Nov 29, 2015, 7:21:29 AM11/29/15
to julia-users
Since this situation comes up once in a while, it might be nice to have a copyfill which does a copy on each cell. In code:

function copyfill!{T}(a::Array{T}, x)
    xT = convert(T, x)
    for i in eachindex(a)
        @inbounds a[i] = copy(xT)
    end
    return a
end

copyfill(v, dims::Dims)       = copyfill!(Array(typeof(v), dims), v)
copyfill(v, dims::Integer...) = copyfill!(Array(typeof(v), dims...), v)

And then we have:

julia> m = copyfill(Int[1],3,3)
3x3 Array{Array{Int64,1},2}:
 [1]  [1]  [1]
 [1]  [1]  [1]
 [1]  [1]  [1]

julia> push!(m[1,1],2)
2-element Array{Int64,1}:
 1
 2

julia> m
3x3 Array{Array{Int64,1},2}:
 [1,2]  [1]  [1]
 [1]    [1]  [1]
 [1]    [1]  [1]

As some would expect. Is there a +1 on this?

Aleksandr Mikheev

unread,
Nov 29, 2015, 8:06:16 AM11/29/15
to julia-users
Thank you, guys. I guess this
[Int[] for i = 1:3, j=1:3, k=1:3]
is exactly what I need. At the same time, now I understand that I should read more about creating and filling arrays. :) Once again, thanks.

Milan Bouchet-Valat

unread,
Nov 29, 2015, 8:49:19 AM11/29/15
to julia...@googlegroups.com
This has been discussed in depth here:
https://github.com/JuliaLang/julia/pull/8759

One issue is that sometimes copy() is enough, but sometimes you want
deepcopy() instead (when the object itself contains references). Thus,
a more general solution would be to take as first argument a function
returning the value:
https://groups.google.com/d/msg/julia-users/L5fXkHPduBo/UNoq6NTQOnEJ

But as Stefan noted the comprehension form is quite good too. Maybe the
docs should point to it after the warning about shared references.


Regards

Dan

unread,
Nov 29, 2015, 9:11:15 AM11/29/15
to julia-users
comprehension is indeed fine (and fast). should remember to read/search the docs and issues more (as usual).
fill(function,dims) is also good.

wondering, if it might be productive to make fill warn or even err when there is ambiguity and make versions:
`shallowfill`,`copyfill`,`deepcopyfill`. all this is unnecessary, but might help for readability purposes.
Reply all
Reply to author
Forward
0 new messages