Skip to content

Commit

Permalink
Use similar in empty (#49958)
Browse files Browse the repository at this point in the history
After this,
```julia
julia> S = StructArray{Complex{Int}}(([1,2], [3,4]))
2-element StructArray(::Vector{Int64}, ::Vector{Int64}) with eltype Complex{Int64}:
 1 + 3im
 2 + 4im

julia> empty(S)
0-element StructArray(::Vector{Int64}, ::Vector{Int64}) with eltype Complex{Int64}
```
instead of
```julia
julia> empty(S)
Complex{Int64}[]
```
This behavior matches the docstring now: "Create an empty vector similar
to `v`". Often, `similar` will fall back to creating a `Vector`, so the
current behavior will be preserved.

~I'm unsure about what test to add for this, so suggestions are
welcome.~ Test added for a `StructArray` through a new test helper
  • Loading branch information
jishnub authored Jan 14, 2024
1 parent c3836e1 commit cd4f44d
Show file tree
Hide file tree
Showing 3 changed files with 53 additions and 1 deletion.
2 changes: 1 addition & 1 deletion base/abstractarray.jl
Original file line number Diff line number Diff line change
Expand Up @@ -881,7 +881,7 @@ julia> empty([1.0, 2.0, 3.0], String)
String[]
```
"""
empty(a::AbstractVector{T}, ::Type{U}=T) where {T,U} = Vector{U}()
empty(a::AbstractVector{T}, ::Type{U}=T) where {T,U} = similar(a, U, 0)

# like empty, but should return a mutable collection, a Vector by default
emptymutable(a::AbstractVector{T}, ::Type{U}=T) where {T,U} = Vector{U}()
Expand Down
13 changes: 13 additions & 0 deletions test/abstractarray.jl
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,9 @@ using Random, LinearAlgebra
isdefined(Main, :InfiniteArrays) || @eval Main include("testhelpers/InfiniteArrays.jl")
using .Main.InfiniteArrays

isdefined(Main, :StructArrays) || @eval Main include("testhelpers/StructArrays.jl")
using .Main.StructArrays

A = rand(5,4,3)
@testset "Bounds checking" begin
@test checkbounds(Bool, A, 1, 1, 1) == true
Expand Down Expand Up @@ -1019,6 +1022,16 @@ end
@test isempty(v)
@test isempty(v2::Vector{Int})
@test isempty(v3::Vector{Float64})

S = StructArrays.StructArray{Complex{Int}}((v, v))
for T in (Complex{Int}, ComplexF64)
S0 = empty(S, T)
@test S0 isa StructArrays.StructArray{T}
@test length(S0) == 0
end
S0 = empty(S, String)
@test S0 isa Vector{String}
@test length(S0) == 0
end

@testset "CartesianIndices" begin
Expand Down
39 changes: 39 additions & 0 deletions test/testhelpers/StructArrays.jl
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
module StructArrays

struct StructArray{T,N,C <: Tuple{Vararg{AbstractArray{<:Any,N}}}} <: AbstractArray{T,N}
components :: C

function StructArray{T,N,C}(components::C) where {T,N,C}
fieldcount(T) == length(components) || throw(ArgumentError("number of components incompatible with eltype"))
allequal(axes.(components)) || throw(ArgumentError("component arrays must have the same axes"))
new{T,N,C}(components)
end
end

function StructArray{T}(components::Tuple{Vararg{AbstractArray{<:Any,N}}}) where {T,N}
StructArray{T,N,typeof(components)}(components)
end

Base.size(S::StructArray) = size(S.components[1])
Base.axes(S::StructArray) = axes(S.components[1])
function Base.getindex(S::StructArray{T,N}, inds::Vararg{Int,N}) where {T,N}
vals = map(x -> x[inds...], S.components)
T(vals...)
end
function Base.setindex!(S::StructArray{T,N}, val, inds::Vararg{Int,N}) where {T,N}
vals = getfield.(Ref(convert(T, val)), fieldnames(T))
for (A,v) in zip(S.components, vals)
A[inds...] = v
end
S
end

isnonemptystructtype(::Type{T}) where {T} = isstructtype(T) && fieldcount(T) != 0

function Base.similar(S::StructArray, ::Type{T}, dims::Tuple{Int, Vararg{Int}}) where {T}
isnonemptystructtype(T) || return similar(S.components[1], T, dims)
arrs = similar.(S.components, fieldtypes(T), Ref(dims))
StructArray{T}(arrs)
end

end

0 comments on commit cd4f44d

Please sign in to comment.