Why Have 0-Dimensional Arrays?

question

#1

Hi.

I apologize if this has been asked before, but I’m a bit confused by 0-dimensional arrays.

You can access the elements of a 1D array via the syntax x[k], of a 2D array via x[k,l] or x[k], and so on. But a 0D array is nothing more than a scalar, or as implemented, a 1D array of constant length 1.

Wouldn’t it make more sense for 0D arrays to be equivalent to their elements types, i.e. Array{Float64,0} ≡ Float64? Is there a reason for allowing 0D arrays?


#2

One use of 0-dimensional arrays is to be able to pass scalars to a function by reference. For example:

   function f(a)
      a=1.0
      nothing
   end

does nothing at all. However,

   function f!(a)
      a[] = 1.0
      nothing
   end

changes the value passed in by the caller.

Since we are on this topic, I don’t know the correct syntax to create a 0-dimensional array using the named-type constructor. Neither a = Array{Float64,0}() nor a = Array{Float64,0}(1.0) seems to work for me. The only one I know is a = zeros()


#3

That makes a lot of sense. I had been wondering how to handle returning a scalar without allocating memory for it or using a Vector of length 1. Thanks!

fill(val,())

seems to work. :slight_smile:


#4

FYI, Array{Float64}() works:

julia> x = Array{Float64}()
0-dimensional Array{Float64,0}:
1.4822e-323

julia> x[] = 100.0
100.0

julia> x[]
100.0

#5

Huh, never knew this. Is there a performance disadvantage to this (since it has to de-reference)?


#6

I have used 0-subscript arrays in only one place in my code (“last_checkpoint_time”, a scalar that keeps track of the last wallclock time that the program state was checkpointed. It is passed to the checkpointing function. The checkpointing function may or may not want to change its value, so I need to pass it by reference). Since I use 0-subscript arrays so rarely, I never bothered to check the performance.


#7

In response to your original question, I would like to add that there is no specific “reason” for these arrays, rather, they are just a consequence of certain invariants, eg

prod(size(array)) == length(array)

If you wanted any other kind of behavior, you would have to special case it.

Using 0 rank arrays as containers is a use case that goes back to Common Lisp. In Julia, however, I would use a wrapper type, for semantic clarity.