Skip to content

Commit 04dd4e7

Browse files
committed
CartesianRange constructor and eachindex
1 parent 4c0d0c6 commit 04dd4e7

File tree

5 files changed

+69
-3
lines changed

5 files changed

+69
-3
lines changed

NEWS.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -388,6 +388,11 @@ Library improvements
388388
This supersedes the old behavior of reinterpret on Arrays. As a result, reinterpreting
389389
arrays with different alignment requirements (removed in 0.6) is once again allowed ([#23750]).
390390

391+
* `CartesianRange` changes ([#24715]):
392+
- Inherits from `AbstractArray`
393+
- Constructor taking an array
394+
- `eachindex` returns the linear indices into a reshaped array, as `sub2ind` alternative
395+
391396
Compiler/Runtime improvements
392397
-----------------------------
393398

base/abstractarray.jl

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -811,6 +811,17 @@ if all inputs have fast linear indexing, a [`CartesianRange`](@ref)
811811
otherwise).
812812
If the arrays have different sizes and/or dimensionalities, `eachindex` returns an
813813
iterable that spans the largest range along each dimension.
814+
815+
For a CartesianRange, this returns a reshaped range of the linear indices into
816+
the range, e.g.:
817+
818+
```jldoctest
819+
julia> eachindex(CartesianRange((1:2,1:3)))
820+
2×3 reshape(::Base.OneTo{Int64}, 2, 3) with eltype Int64:
821+
1 3 5
822+
2 4 6
823+
```
824+
814825
"""
815826
eachindex(A::AbstractArray) = (@_inline_meta(); eachindex(IndexStyle(A), A))
816827

base/multidimensional.jl

Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -172,6 +172,11 @@ module IteratorsMD
172172
Consequently these can be useful for writing algorithms that
173173
work in arbitrary dimensions.
174174
175+
CartesianRange(A::AbstractArray) -> R
176+
177+
As a convenience, constructing a CartesianRange from an array makes a
178+
range of its indices.
179+
175180
# Examples
176181
```jldoctest
177182
julia> foreach(println, CartesianRange((2, 2, 2)))
@@ -183,7 +188,47 @@ module IteratorsMD
183188
CartesianIndex(2, 1, 2)
184189
CartesianIndex(1, 2, 2)
185190
CartesianIndex(2, 2, 2)
191+
192+
julia> CartesianRange(ones(2,3))
193+
2×3 CartesianRange{2,Tuple{Base.OneTo{Int64},Base.OneTo{Int64}}}:
194+
CartesianIndex(1, 1) CartesianIndex(1, 2) CartesianIndex(1, 3)
195+
CartesianIndex(2, 1) CartesianIndex(2, 2) CartesianIndex(2, 3)
186196
```
197+
198+
## Conversion between linear and cartesian indices
199+
200+
Linear index to cartesian index conversion exploits the fact that a
201+
`CartesianRange` is an `AbstractArray` and can be indexed linearly:
202+
203+
```jldoctest subarray
204+
julia> cartesian = CartesianRange(1:3,1:2)
205+
3×2 CartesianRange{2,Tuple{UnitRange{Int64},UnitRange{Int64}}}:
206+
CartesianIndex(1, 1) CartesianIndex(1, 2)
207+
CartesianIndex(2, 1) CartesianIndex(2, 2)
208+
CartesianIndex(3, 1) CartesianIndex(3, 2)
209+
210+
julia> cartesian[4]
211+
CartesianIndex(1, 2)
212+
```
213+
214+
For cartesian to linear index conversion, [`eachindex`](@ref) returns a
215+
reshaped version of the linear indices when called on a `CartesianRange`:
216+
217+
```jldoctest subarray
218+
julia> linear = eachindex(cartesian)
219+
3×2 reshape(::Base.OneTo{Int64}, 3, 2) with eltype Int64:
220+
1 4
221+
2 5
222+
3 6
223+
224+
julia> linear[1,2]
225+
4
226+
227+
julia> linear[cartesian[4]]
228+
4
229+
```
230+
231+
187232
"""
188233
struct CartesianRange{N,R<:NTuple{N,AbstractUnitRange{Int}}} <: AbstractArray{CartesianIndex{N},N}
189234
indices::R
@@ -204,6 +249,8 @@ module IteratorsMD
204249
CartesianRange(inds::NTuple{N,Union{<:Integer,AbstractUnitRange{<:Integer}}}) where {N} =
205250
CartesianRange(map(i->first(i):last(i), inds))
206251

252+
CartesianRange(A::AbstractArray) = CartesianRange(indices(A))
253+
207254
convert(::Type{Tuple{}}, R::CartesianRange{0}) = ()
208255
convert(::Type{NTuple{N,AbstractUnitRange{Int}}}, R::CartesianRange{N}) where {N} =
209256
R.indices
@@ -225,6 +272,7 @@ module IteratorsMD
225272
# AbstractArray implementation
226273
Base.IndexStyle(::Type{CartesianRange{N,R}}) where {N,R} = IndexCartesian()
227274
@inline Base.getindex(iter::CartesianRange{N,R}, I::Vararg{Int, N}) where {N,R} = CartesianIndex(first.(iter.indices) .- 1 .+ I)
275+
Base.eachindex(iter::CartesianRange) = reshape(linearindices(iter), size(iter))
228276

229277
ndims(R::CartesianRange) = ndims(typeof(R))
230278
ndims(::Type{CartesianRange{N}}) where {N} = N

doc/src/devdocs/subarrays.md

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -22,9 +22,8 @@ computation (such as interpolation), and the type under discussion here, `SubArr
2222
For these types, the underlying information is more naturally described in terms of
2323
cartesian indexes.
2424

25-
You can manually convert from a cartesian index to a linear index with `sub2ind`, and vice versa
26-
using `ind2sub`. `getindex` and `setindex!` functions for `AbstractArray` types may include similar
27-
operations.
25+
The `getindex` and `setindex!` functions for `AbstractArray` types may include automatic conversion
26+
between indexing types. For explicit conversion, [`CartesianRange`](@ref) can be used.
2827

2928
While converting from a cartesian index to a linear index is fast (it's just multiplication and
3029
addition), converting from a linear index to a cartesian index is very slow: it relies on the

test/abstractarray.jl

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -894,4 +894,7 @@ end
894894
j = (i_lin-i) ÷ length(xrng) + 1
895895
@test CR[i_lin] == CartesianIndex(xrng[i],yrng[j])
896896
end
897+
898+
@test CartesianRange(ones(2,3)) == CartesianRange((2,3))
899+
@test eachindex(CartesianRange((2,3))) == [1 3 5; 2 4 6]
897900
end

0 commit comments

Comments
 (0)