You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
While these behave equivalently now (conversion currently performs coercion), developers are encouraged to "future-proof" their code by choosing the behavior appropriate for each usage.
106
106
107
+
108
+
## Wrapping other offset array types
109
+
110
+
An `OffsetArray` may wrap any subtype of `AbstractArray`, including ones that do not use `1`-based indexing. Such arrays however need to satisfy the fundamental axiom of idempotent indexing for things to work correctly. In other words, an axis of an offset array needs to have the same values as its own axis. This property is built into `OffsetArray`s if the parent uses 1-based indexing, but it's up to the user to ensure the correctness in case a type is to be wrapped that uses offset indices.
111
+
112
+
We demonstrate this through an example by creating a custom 0-based range type that we wrap in an `OffsetArray`:
julia> function Base.show(io::IO, A::ZeroBasedRange)
140
+
show(io, A.a)
141
+
print(io, " with indices $(axes(A,1))")
142
+
end;
143
+
```
144
+
145
+
This definition of a `ZeroBasedRange` appears to have the correct indices, for example:
146
+
147
+
```jldoctest zerobasedrange
148
+
julia> z = ZeroBasedRange(1:4)
149
+
1:4 with indices 0:3
150
+
151
+
julia> z[0]
152
+
1
153
+
154
+
julia> z[3]
155
+
4
156
+
```
157
+
158
+
However this does not use idempotent indexing, as the axis of a `ZeroBasedRange` is not its own axis.
159
+
160
+
```jldoctest zerobasedrange
161
+
julia> axes(z, 1)
162
+
0:3
163
+
164
+
julia> axes(axes(z, 1), 1)
165
+
Base.OneTo(4)
166
+
```
167
+
168
+
This will lead to complications in certain functions --- for example `LinearIndices` --- that tend to implictly assume idempotent indexing. In this case the `LinearIndices` of `z` will not match its axis.
ERROR: BoundsError: attempt to access 4-element UnitRange{Int64} at index [5]
189
+
[...]
190
+
```
191
+
192
+
The `Array` conversion errors despite `zo` having 1-based indices. The function `axes(zo, 1)` hints at the underlying problem --- the values and the indices of the axis are different. We may check that the axis of `zo` is not its own axis:
In this case the bug may be fixed by defining the `axes` of a `ZeroBasedRange` to be idempotent, for example using the `OffsetArrays.IdentityUnitRange` wrapper:
With this new definition, the values and indices of the axis are identical, which makes indexing idempotent. The conversion to an `Array` works as expected now:
212
+
213
+
```jldoctest zerobasedrange
214
+
julia> Array(zo)
215
+
4-element Vector{Int64}:
216
+
1
217
+
2
218
+
3
219
+
4
220
+
```
221
+
107
222
## Caveats
108
223
109
224
Because `IdOffsetRange` behaves quite differently to the normal `UnitRange` type, there are some
@@ -115,13 +230,13 @@ One such cases is `getindex`:
115
230
julia> Ao = zeros(-3:3, -3:3); Ao[:] .= 1:49;
116
231
117
232
julia> Ao[-3:0, :] |> axes # the first dimension does not preserve offsets
In this example we had to define the action of `to_indices` as the type `ZeroBasedIndexing` did not have a familiar hierarchy. Things are even simpler if we subtype `AbstractUnitRange`, in which case we need to define `first` and `length` for the custom range to be able to use it as an axis:
0 commit comments