Skip to content

Commit ac1cf76

Browse files
committed
Don't overload * for linearindexing type computations
This also generalizes `eachindex(A, B, C...)`.
1 parent 85e3fe0 commit ac1cf76

File tree

4 files changed

+31
-9
lines changed

4 files changed

+31
-9
lines changed

base/abstractarray.jl

+10-5
Original file line numberDiff line numberDiff line change
@@ -113,10 +113,10 @@ linearindexing{T<:AbstractArray}(::Type{T}) = LinearSlow()
113113
linearindexing{T<:Array}(::Type{T}) = LinearFast()
114114
linearindexing{T<:Range}(::Type{T}) = LinearFast()
115115

116-
*(::LinearFast, ::LinearFast) = LinearFast()
117-
*(::LinearSlow, ::LinearFast) = LinearSlow()
118-
*(::LinearFast, ::LinearSlow) = LinearSlow()
119-
*(::LinearSlow, ::LinearSlow) = LinearSlow()
116+
linearindexing(A::AbstractArray, B::AbstractArray) = linearindexing(linearindexing(A), linearindexing(B))
117+
linearindexing(A::AbstractArray, B::AbstractArray...) = linearindexing(linearindexing(A), linearindexing(B...))
118+
linearindexing(::LinearFast, ::LinearFast) = LinearFast()
119+
linearindexing(::LinearIndexing, ::LinearIndexing) = LinearSlow()
120120

121121
# The real @inline macro is not available this early in the bootstrap, so this
122122
# internal macro splices the meta Expr directly into the function body.
@@ -385,9 +385,14 @@ eachindex(::LinearFast, A::AbstractArray) = 1:length(A)
385385

386386
function eachindex(A::AbstractArray, B::AbstractArray)
387387
@_inline_meta
388-
eachindex(linearindexing(A)*linearindexing(B), A, B)
388+
eachindex(linearindexing(A,B), A, B)
389+
end
390+
function eachindex(A::AbstractArray, B::AbstractArray...)
391+
@_inline_meta
392+
eachindex(linearindexing(A,B...), A, B...)
389393
end
390394
eachindex(::LinearFast, A::AbstractArray, B::AbstractArray) = 1:max(length(A),length(B))
395+
eachindex(::LinearFast, A::AbstractArray, B::AbstractArray...) = 1:max(length(A), map(length, B)...)
391396

392397
isempty(a::AbstractArray) = (length(a) == 0)
393398

base/multidimensional.jl

+7-3
Original file line numberDiff line numberDiff line change
@@ -94,10 +94,14 @@ ndims(R::CartesianRange) = length(R.start)
9494
:($meta; CartesianRange(CartesianIndex{$N}($(startargs...)), CartesianIndex{$N}($(stopargs...))))
9595
end
9696

97-
@generated function eachindex{S,T,M,N}(::LinearSlow, A::AbstractArray{S,M}, B::AbstractArray{T,N})
98-
K = max(M,N)
97+
@generated function eachindex(::LinearSlow, A::AbstractArray, B::AbstractArray...)
98+
K = max(ndims(A), map(ndims, B)...)
9999
startargs = fill(1, K)
100-
stopargs = [:(max(size(A,$i),size(B,$i))) for i=1:K]
100+
stopargs = Array(Expr, K)
101+
for i = 1:K
102+
Bargs = [:(size(B[$j],$i)) for j = 1:length(B)]
103+
stopargs[i] = :(max(size(A,$i),$(Bargs...)))
104+
end
101105
meta = Expr(:meta, :inline)
102106
:($meta; CartesianRange(CartesianIndex{$K}($(startargs...)), CartesianIndex{$K}($(stopargs...))))
103107
end

doc/stdlib/arrays.rst

+8-1
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,7 @@ Basic functions
3131

3232
Returns the number of elements in A
3333

34-
.. function:: eachindex(A)
34+
.. function:: eachindex(A...)
3535

3636
Creates an iterable object for visiting each index of an AbstractArray ``A`` in an efficient manner. For array types that have opted into fast linear indexing (like ``Array``), this is simply the range ``1:length(A)``. For other array types, this returns a specialized Cartesian range to efficiently index into the array with indices specified for every dimension. Example for a sparse 2-d array::
3737

@@ -59,6 +59,13 @@ Basic functions
5959
(iter.I_1,iter.I_2) = (2,3)
6060
A[iter] = 0.8090413606455655
6161

62+
If you supply more than one ``AbstractArray`` argument, ``eachindex``
63+
will create an iterable object that is fast for all arguments (a
64+
``UnitRange`` if all inputs have fast linear indexing, a
65+
CartesianRange otherwise). If the arrays have different sizes and/or
66+
dimensionalities, ``eachindex`` returns an interable that spans the
67+
largest range along each dimension.
68+
6269
.. function:: Base.linearindexing(A)
6370

6471
``linearindexing`` defines how an AbstractArray most efficiently accesses its elements. If ``Base.linearindexing(A)`` returns ``Base.LinearFast()``, this means that linear indexing with only one index is an efficient operation. If it instead returns ``Base.LinearSlow()`` (by default), this means that the array intrinsically accesses its elements with indices specified for every dimension. Since converting a linear index to multiple indexing subscripts is typically very expensive, this provides a traits-based mechanism to enable efficient generic code for all array types.

test/arrayops.jl

+6
Original file line numberDiff line numberDiff line change
@@ -1105,6 +1105,12 @@ R = CartesianRange((0,3))
11051105
R = CartesianRange((3,0))
11061106
@test done(R, start(R)) == true
11071107

1108+
@test eachindex(Base.LinearSlow(),zeros(3),zeros(2,2),zeros(2,2,2),zeros(2,2)) == CartesianRange((3,2,2))
1109+
@test eachindex(Base.LinearFast(),zeros(3),zeros(2,2),zeros(2,2,2),zeros(2,2)) == 1:8
1110+
@test eachindex(zeros(3),sub(zeros(3,3),1:2,1:2),zeros(2,2,2),zeros(2,2)) == CartesianRange((3,2,2))
1111+
@test eachindex(zeros(3),zeros(2,2),zeros(2,2,2),zeros(2,2)) == 1:8
1112+
1113+
11081114
#rotates
11091115

11101116
a = [1 0 0; 0 0 0]

0 commit comments

Comments
 (0)