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'sOneTotype.) 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=#))
...
endHere,
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=#)
endand similarly for OtherArrayType. Then, if A is a MyArray and
B is an OtherArray,
similar(Array{Int}, axes(A))will create anotherMyArraysimilar(Array{Int}, axes(B))will create anotherOtherArray
despite the fact that they both use URange.