Julia is a modern language designed for technical computing. In the
spirit of other scientific languages such as Fortran, MATLAB/Octave,
Mathematica, and R, Julia uses 1-based indexing for arrays. Meaning that
the first element of an one-dimensional array a
is accessed with
a[1]
. This is in contrast to popular languages such as C and Python
which use 0-based indexing and that though they came to be used in
sciences they weren’t specifically designed for them.
But like Fortran, Julia though it defaults to 1-based, it allows you to specify arbitrary indices for the arrays. The documentation is very good, showing how to generalize the code, bound check and do loops but is more oriented to developers. Meaning even if they’re are good techniques it is not something someone that wants to implement a simple algorithm will find straightforward to apply.
For this the OffsetArrays.jl package may be used. This
package can be utilized to provide arbitrary indices in similar vein to
Fortan. Specifically it exports the following OffsetArray
function.
help?> OffsetArray
OffsetArray(A, indices...)
That function returns an AbstractArray
sharing element type and size
with supplied array A
but uses axes inferred from the supplied
indices
argument.
Let’s define an one-dimensional array a
.
julia> a = collect(1:5)
5-element Array{Int64,1}:
1
2
3
4
5
Elements in a loop can be accessed counting from 1
to length(a)
.
julia> i=1;
julia> a[i]
1
In order to change the loop from 0
to length(a)-1
, the following can
be run.
julia> using OffsetArrays
julia> a = OffsetArray(a, 0:(length(a) - 1))
5-element OffsetArray(::Array{Int64,1}, 0:4) with eltype Int64 with indices 0:4:
1
2
3
4
5
Then
julia> i=0;
julia> a[i]
1
For simpler usage the following function can be defined
function zero_based(a)
return OffsetArray(a, 0:(length(a) - 1))
end
Then
julia> a = collect(1:5);
julia> a = zero_based(a)
5-element OffsetArray(::Array{Int64,1}, 0:4) with eltype Int64 with indices 0:4:
1
2
3
4
5
Another usage is when we wish to access elements of a region referred
from an array, using the indices corresponding to the originating
array. For an example, let’s define a two-dimensional array A
.
julia> A = reshape(1:36, 6, 6)
6×6 reshape(::UnitRange{Int64}, 6, 6) with eltype Int64:
1 7 13 19 25 31
2 8 14 20 26 32
3 9 15 21 27 33
4 10 16 22 28 34
5 11 17 23 29 35
6 12 18 24 30 36
A region may be referred as follows.
julia> B = A[2:5, 2:5]
4×4 Array{Int64,2}:
8 14 20 26
9 15 21 27
10 16 22 28
11 17 23 29
Now as things are the elements of A
and B
do not correspond.
julia> A[2, 2]
8
julia> B[1, 1]
8
To make indexing consistent the following can be run.
julia> B = OffsetArray(A[2:5, 2:5], 2:5, 2:5)
4×4 OffsetArray(::Array{Int64,2}, 2:5, 2:5) with eltype Int64 with indices 2:5×2:5:
8 14 20 26
9 15 21 27
10 16 22 28
11 17 23 29
Then
julia> A[2, 2]
8
julia> B[2, 2]
8
For simpler usage the following function can be defined
function refer_region(A, x, y)
return OffsetArray(A[x, y], x, y)
end
Then
julia> A = reshape(1:36, 6, 6);
julia> B = refer_region(A, 2:5, 2:5)
4×4 OffsetArray(::Array{Int64,2}, 2:5, 2:5) with eltype Int64 with indices 2:5×2:5:
8 14 20 26
9 15 21 27
10 16 22 28
11 17 23 29
Those are some trivial examples to showcase arbitrary indices. Since Julia defaults to 1 some codes and packages may see this as absolute. Meaning that custom indices may create issues and trigger errors, even in base Julia. It should be noted that the last example was taken from Holy’s post on Julia blog which has about similar theme to this post but is more extended and detailed.