CustomUnitRanges
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.
What's in this package
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.
Usage
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
.