Skip to content

Commit

Permalink
Added `empty
Browse files Browse the repository at this point in the history
 * `empty` returns an `Associative` with no keys, replacing `similar`.
 * Also `empty(::Tuple) = ()` and `empty(::Array{T}) = T[]`.
  • Loading branch information
Andy Ferris committed Nov 22, 2017
1 parent ea34968 commit 2dca269
Show file tree
Hide file tree
Showing 14 changed files with 87 additions and 28 deletions.
7 changes: 7 additions & 0 deletions NEWS.md
Original file line number Diff line number Diff line change
Expand Up @@ -1292,6 +1292,13 @@ Deprecated or removed

* `EnvHash` has been renamed to `EnvDict` ([#24167]).

* Introduced the `empty` function, the functional pair to `empty!` which returns a new,
empty container ([#24390]).

* `similar(::Associative)` has been deprecated in favor of `empty(::Associative)`, and
`similar(::Associative, ::Pair{K, V})` has been deprecated in favour of
`empty(::Associative, K, V)` ([#24390]).

Command-line option changes
---------------------------

Expand Down
18 changes: 18 additions & 0 deletions base/abstractarray.jl
Original file line number Diff line number Diff line change
Expand Up @@ -566,6 +566,24 @@ indices of `A`.
similar(f, shape::Tuple) = f(to_shape(shape))
similar(f, dims::DimOrInd...) = similar(f, dims)

"""
empty(v::AbstractVector, [eltype])
Create an empty vector similar to `v`, optionally changing the `eltype`.
# Examples
```jldoctest
julia> empty([1.0, 2.0, 3.0])
0-element Array{Float64,1}
julia> empty([1.0, 2.0, 3.0], String)
0-element Array{String,1}
```
"""
empty(a::AbstractVector) = empty(a, eltype(a))
empty(a::AbstractVector, ::Type{T}) where {T} = Vector{T}()

## from general iterable to any array

function copy!(dest::AbstractArray, src)
Expand Down
21 changes: 18 additions & 3 deletions base/associative.jl
Original file line number Diff line number Diff line change
Expand Up @@ -136,8 +136,23 @@ This includes arrays, where the keys are the array indices.
"""
pairs(collection) = Generator(=>, keys(collection), values(collection))

"""
empty(a::Associative, [index_type=keytype(a)], [value_type=valtype(a)])
Create an empty `Associative` container which can accept indices of type `index_type` and
values of type `value_type`. The second and third arguments are optional and default to the
input's `keytype` and `valtype`, respectively. (If only one of the two types is specified,
it is assumed to be the `value_type`, and the `index_type` we default to `keytype(a)`).
Custom `Associative` subtypes may choose which specific associative type is best suited to
return for the given index and value types, by specializing on the three-argument signature.
The default is to return an empty `Dict`.
"""
empty(a::Associative) = empty(a, keytype(a), valtype(a))
empty(a::Associative, ::Type{V}) where {V} = empty(a, keytype(a), V) # Note: this is the form which makes sense for `Vector`.

function copy(a::Associative)
b = similar(a)
b = empty(a)
for (k,v) in a
b[k] = v
end
Expand Down Expand Up @@ -403,7 +418,7 @@ Dict{Int64,String} with 1 entry:
"""
function filter(f, d::Associative)
# don't just do filter!(f, copy(d)): avoid making a whole copy of d
df = similar(d)
df = empty(d)
try
for (k, v) in d
if f(k => v)
Expand Down Expand Up @@ -512,7 +527,7 @@ mutable struct ObjectIdDict <: Associative{Any,Any}
ObjectIdDict(o::ObjectIdDict) = new(copy(o.ht))
end

similar(d::ObjectIdDict) = ObjectIdDict()
empty(d::ObjectIdDict, ::Type{Any}, ::Type{Any}) = ObjectIdDict()

function rehash!(t::ObjectIdDict, newsz = length(t.ht))
t.ht = ccall(:jl_idtable_rehash, Any, (Any, Csize_t), t.ht, newsz)
Expand Down
2 changes: 1 addition & 1 deletion base/deepcopy.jl
Original file line number Diff line number Diff line change
Expand Up @@ -105,7 +105,7 @@ function deepcopy_internal(x::Dict, stackdict::ObjectIdDict)
return (stackdict[x] = copy(x))
end

dest = similar(x)
dest = empty(x)
stackdict[x] = dest
for (k, v) in x
dest[deepcopy_internal(k, stackdict)] = deepcopy_internal(v, stackdict)
Expand Down
5 changes: 4 additions & 1 deletion base/deprecated.jl
Original file line number Diff line number Diff line change
Expand Up @@ -2210,7 +2210,6 @@ end
@deprecate chol!(x::Number, uplo) chol(x) false
end


# issue #16307
@deprecate finalizer(o, f::Function) finalizer(f, o)
# This misses other callables but they are very rare in the wild
Expand All @@ -2221,6 +2220,10 @@ end
finalizer(f::Ptr{Void}, o::Ptr{Void}) = invoke(finalizer, Tuple{Ptr{Void}, Any}, f, o)
finalizer(f::Ptr{Void}, o::Function) = invoke(finalizer, Tuple{Ptr{Void}, Any}, f, o)

# issue #24019
@deprecate similar(a::Associative) empty(a)
@deprecate similar(a::Associative, ::Type{Pair{K,V}}) where {K, V} empty(a, K, V)

# END 0.7 deprecations

# BEGIN 1.0 deprecations
Expand Down
19 changes: 8 additions & 11 deletions base/dict.jl
Original file line number Diff line number Diff line change
Expand Up @@ -107,6 +107,9 @@ mutable struct Dict{K,V} <: Associative{K,V}
new(copy(d.slots), copy(d.keys), copy(d.vals), d.ndel, d.count, d.age,
d.idxfloor, d.maxprobe)
end
function Dict{K, V}(slots, keys, vals, ndel, count, age, idxfloor, maxprobe) where {K, V}
new(slots, keys, vals, ndel, count, age, idxfloor, maxprobe)
end
end
function Dict{K,V}(kv) where V where K
h = Dict{K,V}()
Expand Down Expand Up @@ -166,7 +169,7 @@ end
# this is a special case due to (1) allowing both Pairs and Tuples as elements,
# and (2) Pair being invariant. a bit annoying.
function grow_to!(dest::Associative, itr)
out = grow_to!(similar(dest, Pair{Union{},Union{}}), itr, start(itr))
out = grow_to!(empty(dest, Union{}, Union{}), itr, start(itr))
return isempty(out) ? dest : out
end

Expand All @@ -176,7 +179,7 @@ function grow_to!(dest::Associative{K,V}, itr, st) where V where K
if isa(k,K) && isa(v,V)
dest[k] = v
else
new = similar(dest, Pair{typejoin(K,typeof(k)), typejoin(V,typeof(v))})
new = empty(dest, typejoin(K,typeof(k)), typejoin(V,typeof(v)))
copy!(new, dest)
new[k] = v
return grow_to!(new, itr, st)
Expand All @@ -185,8 +188,7 @@ function grow_to!(dest::Associative{K,V}, itr, st) where V where K
return dest
end

similar(d::Dict{K,V}) where {K,V} = Dict{K,V}()
similar(d::Dict, ::Type{Pair{K,V}}) where {K,V} = Dict{K,V}()
empty(a::Associative, ::Type{K}, ::Type{V}) where {K, V} = Dict{K, V}()

# conversion between Dict types
function convert(::Type{Dict{K,V}},d::Associative) where V where K
Expand Down Expand Up @@ -809,12 +811,7 @@ next(::ImmutableDict{K,V}, t) where {K,V} = (Pair{K,V}(t.key, t.value), t.parent
done(::ImmutableDict, t) = !isdefined(t, :parent)
length(t::ImmutableDict) = count(x->true, t)
isempty(t::ImmutableDict) = done(t, start(t))
function similar(t::ImmutableDict)
while isdefined(t, :parent)
t = t.parent
end
return t
end
empty(::ImmutableDict, ::Type{K}, ::Type{V}) where {K, V} = ImmutableDict{K,V}()

_similar_for(c::Dict, ::Type{P}, itr, isz) where {P<:Pair} = similar(c, P)
_similar_for(c::Dict, ::Type{Pair{K,V}}, itr, isz) where {K, V} = empty(c, K, V)
_similar_for(c::Associative, T, itr, isz) = throw(ArgumentError("for Associatives, similar requires an element type of Pair;\n if calling map, consider a comprehension instead"))
2 changes: 0 additions & 2 deletions base/env.jl
Original file line number Diff line number Diff line change
Expand Up @@ -73,8 +73,6 @@ variables.
"""
const ENV = EnvDict()

similar(::EnvDict) = Dict{String,String}()

getindex(::EnvDict, k::AbstractString) = access_env(k->throw(KeyError(k)), k)
get(::EnvDict, k::AbstractString, def) = access_env(k->def, k)
get(f::Callable, ::EnvDict, k::AbstractString) = access_env(k->f(), k)
Expand Down
1 change: 1 addition & 0 deletions base/exports.jl
Original file line number Diff line number Diff line change
Expand Up @@ -651,6 +651,7 @@ export
deleteat!,
eltype,
empty!,
empty,
endof,
filter!,
filter,
Expand Down
4 changes: 2 additions & 2 deletions base/pkg/query.jl
Original file line number Diff line number Diff line change
Expand Up @@ -42,9 +42,9 @@ end

# Specialized copy for the avail argument below because the deepcopy is slow
function availcopy(avail)
new_avail = similar(avail)
new_avail = empty(avail)
for (pkg, vers_avail) in avail
new_vers_avail = similar(vers_avail)
new_vers_avail = empty(vers_avail)
for (version, pkg_avail) in vers_avail
new_vers_avail[version] = copy(pkg_avail)
end
Expand Down
7 changes: 7 additions & 0 deletions base/tuple.jl
Original file line number Diff line number Diff line change
Expand Up @@ -328,3 +328,10 @@ any(x::Tuple{}) = false
any(x::Tuple{Bool}) = x[1]
any(x::Tuple{Bool, Bool}) = x[1]|x[2]
any(x::Tuple{Bool, Bool, Bool}) = x[1]|x[2]|x[3]

"""
empty(x::Tuple)
Returns an empty tuple, `()`.
"""
empty(x::Tuple) = ()
3 changes: 1 addition & 2 deletions base/weakkeydict.jl
Original file line number Diff line number Diff line change
Expand Up @@ -68,8 +68,7 @@ function WeakKeyDict(kv)
end
end

similar(d::WeakKeyDict{K,V}) where {K,V} = WeakKeyDict{K,V}()
similar(d::WeakKeyDict, ::Type{Pair{K,V}}) where {K,V} = WeakKeyDict{K,V}()
empty(d::WeakKeyDict, ::Type{K}, ::Type{V}) where {K, V} = WeakKeyDict{K, V}()

# conversion between Dict types
function convert(::Type{WeakKeyDict{K,V}},d::Associative) where V where K
Expand Down
14 changes: 13 additions & 1 deletion test/abstractarray.jl
Original file line number Diff line number Diff line change
Expand Up @@ -870,10 +870,22 @@ end
Base.convert(::Type{Array{T,n}}, a::Array{T,n}) where {T<:Number,n} = a
Base.convert(::Type{Array{T,n}}, a::Array) where {T<:Number,n} =
copy!(Array{T,n}(size(a)), a)
@test isa(similar(Dict(:a=>1, :b=>2.0), Pair{Union{},Union{}}), Dict{Union{}, Union{}})
@test isa(empty(Dict(:a=>1, :b=>2.0), Union{}, Union{}), Dict{Union{}, Union{}})
end

@testset "zero-dimensional copy" begin
Z = Array{Int}(); Z[] = 17
@test Z == collect(Z) == copy(Z)
end

@testset "empty" begin
@test isempty([])
v = [1, 2, 3]
v2 = empty(v)
v3 = empty(v, Float64)
@test !isempty(v)
empty!(v)
@test isempty(v)
@test isempty(v2::Vector{Int})
@test isempty(v3::Vector{Float64})
end
10 changes: 5 additions & 5 deletions test/dict.jl
Original file line number Diff line number Diff line change
Expand Up @@ -389,7 +389,7 @@ end
a[1] = a
a[a] = 2

sa = similar(a)
sa = empty(a)
@test isempty(sa)
@test isa(sa, ObjectIdDict)

Expand Down Expand Up @@ -515,8 +515,8 @@ import Base.ImmutableDict
@test get(d, k1, :default) === :default
@test d1["key1"] === v1
@test d4["key1"] === v2
@test similar(d3) === d
@test similar(d) === d
@test empty(d3) === d
@test empty(d) === d

@test_throws KeyError d[k1]
@test_throws KeyError d1["key2"]
Expand Down Expand Up @@ -657,8 +657,8 @@ Dict(1 => rand(2,3), 'c' => "asdf") # just make sure this does not trigger a dep
@test !isempty(wkd)

wkd = empty!(wkd)
@test wkd == similar(wkd)
@test typeof(wkd) == typeof(similar(wkd))
@test wkd == empty(wkd)
@test typeof(wkd) == typeof(empty(wkd))
@test length(wkd) == 0
@test isempty(wkd)
@test isa(wkd, WeakKeyDict)
Expand Down
2 changes: 2 additions & 0 deletions test/tuple.jl
Original file line number Diff line number Diff line change
Expand Up @@ -83,6 +83,8 @@ end
@test Tuple{Int,Vararg{Any}}.ninitialized == 1
@test Tuple{Any,Any,Vararg{Any}}.ninitialized == 2
end

@test empty((1, 2.0, "c")) === ()
end

@testset "size" begin
Expand Down

0 comments on commit 2dca269

Please sign in to comment.