Skip to content

Commit

Permalink
CartesianRange constructor and eachindex
Browse files Browse the repository at this point in the history
  • Loading branch information
barche committed Nov 29, 2017
1 parent 4c0d0c6 commit 4a6a566
Show file tree
Hide file tree
Showing 5 changed files with 67 additions and 3 deletions.
5 changes: 5 additions & 0 deletions NEWS.md
Original file line number Diff line number Diff line change
Expand Up @@ -388,6 +388,11 @@ Library improvements
This supersedes the old behavior of reinterpret on Arrays. As a result, reinterpreting
arrays with different alignment requirements (removed in 0.6) is once again allowed ([#23750]).

* `CartesianRange` changes ([#24715]):
- Inherits from `AbstractArray`
- Constructor taking an array
- `eachindex` returns the linear indices into a reshaped array, as `sub2ind` alternative

Compiler/Runtime improvements
-----------------------------

Expand Down
11 changes: 11 additions & 0 deletions base/abstractarray.jl
Original file line number Diff line number Diff line change
Expand Up @@ -811,6 +811,17 @@ if all inputs have fast linear indexing, a [`CartesianRange`](@ref)
otherwise).
If the arrays have different sizes and/or dimensionalities, `eachindex` returns an
iterable that spans the largest range along each dimension.
For a CartesianRange, this returns a reshaped range of the linear indices into
the range, e.g.:
```jldoctest
julia> eachindex(CartesianRange((1:2,1:3)))
2×3 reshape(::Base.OneTo{Int64}, 2, 3) with eltype Int64:
1 3 5
2 4 6
```
"""
eachindex(A::AbstractArray) = (@_inline_meta(); eachindex(IndexStyle(A), A))

Expand Down
46 changes: 46 additions & 0 deletions base/multidimensional.jl
Original file line number Diff line number Diff line change
Expand Up @@ -172,6 +172,11 @@ module IteratorsMD
Consequently these can be useful for writing algorithms that
work in arbitrary dimensions.
CartesianRange(A::AbstractArray) -> R
As a convenience, constructing a CartesianRange from an array makes a
range of its indices.
# Examples
```jldoctest
julia> foreach(println, CartesianRange((2, 2, 2)))
Expand All @@ -183,6 +188,44 @@ module IteratorsMD
CartesianIndex(2, 1, 2)
CartesianIndex(1, 2, 2)
CartesianIndex(2, 2, 2)
julia> CartesianRange(ones(2,3))
2×3 CartesianRange{2,Tuple{Base.OneTo{Int64},Base.OneTo{Int64}}}:
CartesianIndex(1, 1) CartesianIndex(1, 2) CartesianIndex(1, 3)
CartesianIndex(2, 1) CartesianIndex(2, 2) CartesianIndex(2, 3)
```
## Conversion between linear and cartesian indices
Linear index to cartesian index conversion exploits the fact that a
`CartesianRange` is an `AbstractArray` and can be indexed linearly:
```jldoctest subarray
julia> cartesian = CartesianRange(1:3,1:2)
3×2 CartesianRange{2,Tuple{UnitRange{Int64},UnitRange{Int64}}}:
CartesianIndex(1, 1) CartesianIndex(1, 2)
CartesianIndex(2, 1) CartesianIndex(2, 2)
CartesianIndex(3, 1) CartesianIndex(3, 2)
julia> cartesian[4]
CartesianIndex(1, 2)
```
For cartesian to linear index conversion, [`eachindex`](@ref) returns a
reshaped version of the linear indices when called on a `CartesianRange`:
```jldoctest subarray
julia> linear = eachindex(cartesian)
3×2 reshape(::Base.OneTo{Int64}, 3, 2) with eltype Int64:
1 4
2 5
3 6
julia> linear[1,2]
4
julia> linear[cartesian[4]]
4
```
"""
struct CartesianRange{N,R<:NTuple{N,AbstractUnitRange{Int}}} <: AbstractArray{CartesianIndex{N},N}
Expand All @@ -204,6 +247,8 @@ module IteratorsMD
CartesianRange(inds::NTuple{N,Union{<:Integer,AbstractUnitRange{<:Integer}}}) where {N} =
CartesianRange(map(i->first(i):last(i), inds))

CartesianRange(A::AbstractArray) = CartesianRange(indices(A))

convert(::Type{Tuple{}}, R::CartesianRange{0}) = ()
convert(::Type{NTuple{N,AbstractUnitRange{Int}}}, R::CartesianRange{N}) where {N} =
R.indices
Expand All @@ -225,6 +270,7 @@ module IteratorsMD
# AbstractArray implementation
Base.IndexStyle(::Type{CartesianRange{N,R}}) where {N,R} = IndexCartesian()
@inline Base.getindex(iter::CartesianRange{N,R}, I::Vararg{Int, N}) where {N,R} = CartesianIndex(first.(iter.indices) .- 1 .+ I)
Base.eachindex(iter::CartesianRange) = reshape(linearindices(iter), size(iter))

ndims(R::CartesianRange) = ndims(typeof(R))
ndims(::Type{CartesianRange{N}}) where {N} = N
Expand Down
5 changes: 2 additions & 3 deletions doc/src/devdocs/subarrays.md
Original file line number Diff line number Diff line change
Expand Up @@ -22,9 +22,8 @@ computation (such as interpolation), and the type under discussion here, `SubArr
For these types, the underlying information is more naturally described in terms of
cartesian indexes.

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

While converting from a cartesian index to a linear index is fast (it's just multiplication and
addition), converting from a linear index to a cartesian index is very slow: it relies on the
Expand Down
3 changes: 3 additions & 0 deletions test/abstractarray.jl
Original file line number Diff line number Diff line change
Expand Up @@ -894,4 +894,7 @@ end
j = (i_lin-i) ÷ length(xrng) + 1
@test CR[i_lin] == CartesianIndex(xrng[i],yrng[j])
end

@test CartesianRange(ones(2,3)) == CartesianRange((2,3))
@test eachindex(CartesianRange((2,3))) == [1 3 5; 2 4 6]
end

0 comments on commit 4a6a566

Please sign in to comment.