This Julia package supports the creation of array types with
"unconventional" indices, i.e., when the indices may not start at 1.
With this package, each custom array type can have a corresponding
axes
range type, consequently providing a means for consistency
in allocation by similar
.
See https://docs.julialang.org/en/v1/devdocs/offset-arrays/ for more information about defining and using array types with non-1 indices.
Currently this package defines two AbstractUnitRange
types:
-
ZeroRange
, whereZeroRange(n)
is the equivalent of0:n-1
, except that Julia's type system knows that the lower bound is 0. (This is analogous toBase
'sOneTo
type.) This is useful for defining arrays that are indexed starting with 0. -
URange
, a parallel toUnitRange
, for defining arbitrary range indices.
This package has a somewhat atypical usage: you should include
files
from this repository at the source level. The reason is that this
package's range types should be private to the module that needs
them; consequently you don't want to define a module in the global
namespace.
Instead, suppose you're defining an array type that supports arbitrary indices. In broad terms, your module might look like this:
module MyArrayType
using CustomUnitRanges: filename_for_urange
include(filename_for_urange)
struct MyArray{T,N} <: AbstractArray{T,N}
...
end
Base.axes(A::MyArray) = Base.Slice.(map(URange, #=starting indices=#, #=ending indices=#))
...
end
Here,
using CustomUnitRanges: filename_for_urange
brings a non-exported string, filename_for_urange
, into the scope of
MyArrayType
. The key line is the include(filename_for_urange)
statement, which will load (at source-level) the code for the URange
type into your MyArrayType
module. We chose "URange.jl"
because
here we want arbitrary indices; had we wanted zero-based indices, we
would have chosen "ZeroRange.jl"
instead. Second, note that the
output of axes
is a Slice
containing a URange
type. More specifically, it's
creating a tuple of slices with MyArrayType.URange
---there is no "global"
URange
type, so the indices-tuple is therefore specific to this
package.
The important result is that two packages, defining MyArray
and
OtherArray
, can independently exploit URange
. If MyArrayType
includes the specialization
function Base.similar(f::Union{Type,Function}, shape::Tuple{URange,Vararg{URange}}
MyArray(f(map(length, shape)), #=something for the offset=#)
end
and similarly for OtherArrayType
. Then, if A
is a MyArray
and
B
is an OtherArray
,
similar(Array{Int}, axes(A))
will create anotherMyArray
similar(Array{Int}, axes(B))
will create anotherOtherArray
despite the fact that they both use URange
.