diff --git a/base/abstractarray.jl b/base/abstractarray.jl index d4d430a5a1811..f3fd279ab84d2 100644 --- a/base/abstractarray.jl +++ b/base/abstractarray.jl @@ -18,14 +18,20 @@ convert(::Type{AbstractArray{T}}, a::AbstractArray) where {T} = AbstractArray{T} convert(::Type{AbstractArray{T,N}}, a::AbstractArray{<:Any,N}) where {T,N} = AbstractArray{T,N}(a)::AbstractArray{T,N} """ - size(A::AbstractArray, [dim]) + size(A, [dim]) -Return a tuple containing the dimensions of `A`. Optionally you can specify a -dimension to just get the length of that dimension. +Return a tuple containing the dimensions of an `AbstractArray` (or a multidimensional iterator) `A`. +Optionally you may specify a dimension to just get the length of that dimension. + +Non-`AbstractArray` arguments `A` to the two-argument `size` must satisfy +`Base.IteratorSize(A) isa Base.HasShape`. Note that `size` may not be defined for arrays with non-standard indices, in which case [`axes`](@ref) may be useful. See the manual chapter on [arrays with custom indices](@ref man-custom-indices). +!!! compat "Julia 1.13" + `size(A, d)` where `A` is not an `AbstractArray` requires at least Julia 1.13. + See also: [`length`](@ref), [`ndims`](@ref), [`eachindex`](@ref), [`sizeof`](@ref). # Examples @@ -39,12 +45,24 @@ julia> size(A, 2) 3 ``` """ -size(t::AbstractArray{T,N}, d) where {T,N} = d::Integer <= N ? size(t)[d] : 1 +function size(A, d) + @inline + _size(IteratorSize(A), size(A), d) +end +function _size(::HasShape, s::Tuple, d::Integer) + @inline + d_Int = Int(d)::Int + d_Int <= length(s) ? s[d_Int] : 1 +end """ axes(A, d) -Return the valid range of indices for array `A` along dimension `d`. +Return the valid range of indices for an `AbstractArray` (or a mutidimensional iterator) +`A` along dimension `d`. + +Non-`AbstractArray` arguments `A` to the two-argument `axes` must satisfy +`Base.IteratorSize(A) isa Base.HasShape`. See also [`size`](@ref), and the manual chapter on [arrays with custom indices](@ref man-custom-indices). @@ -66,15 +84,23 @@ Each of the indices has to be an `AbstractUnitRange{<:Integer}`, but at the same a type that uses custom indices. So, for example, if you need a subset, use generalized indexing constructs like `begin`/`end` or [`firstindex`](@ref)/[`lastindex`](@ref): +!!! compat "Julia 1.13" + `axes(A, d)` where `A` is not an `AbstractArray` requires at least Julia 1.13. + ```julia ix = axes(v, 1) ix[2:end] # will work for eg Vector, but may fail in general ix[(begin+1):end] # works for generalized indexes ``` """ -function axes(A::AbstractArray{T,N}, d) where {T,N} +function axes(A, d) + @inline + _axes(IteratorSize(A), axes(A), d) +end +function _axes(::HasShape, ax::Tuple, d::Integer) @inline - d::Integer <= N ? axes(A)[d] : OneTo(1) + d_Int = Int(d)::Int + d_Int <= length(ax) ? ax[d_Int] : OneTo(1) end """ diff --git a/test/broadcast.jl b/test/broadcast.jl index a751d9d381ce6..38a43cd873ab1 100644 --- a/test/broadcast.jl +++ b/test/broadcast.jl @@ -1221,3 +1221,11 @@ end @test bc[1] == bc[CartesianIndex(1)] == bc[1, CartesianIndex()] @test a .+ [1 2] == a.a .+ [1 2] end + +@testset "axes/size for a specific broadcasted axis" begin + b = Broadcast.broadcasted(+, ones(2), ones(2,3)) + @test axes(b,1) == 1:2 + @test axes(b,2) == 1:3 + @test size(b,1) == 2 + @test size(b,2) == 3 +end