From 20af397357f181291d3267163536e945383948af Mon Sep 17 00:00:00 2001 From: Andy Ferris Date: Mon, 9 Oct 2017 07:24:00 +1000 Subject: [PATCH] Added `empty * `empty` returns an `Associative` with no keys, replacing `similar`. * Also `empty(::Tuple) = ()` and `empty(::Array{T}) = T[]`. --- NEWS.md | 7 +++++++ base/abstractarray.jl | 18 ++++++++++++++++++ base/associative.jl | 21 ++++++++++++++++++--- base/deepcopy.jl | 2 +- base/deprecated.jl | 4 ++++ base/dict.jl | 19 ++++++++----------- base/env.jl | 2 -- base/exports.jl | 1 + base/pkg/query.jl | 4 ++-- base/tuple.jl | 7 +++++++ base/weakkeydict.jl | 3 +-- test/abstractarray.jl | 14 +++++++++++++- test/dict.jl | 10 +++++----- test/tuple.jl | 2 ++ 14 files changed, 87 insertions(+), 27 deletions(-) diff --git a/NEWS.md b/NEWS.md index a4189e1e67afe..cd29a3341ccdc 100644 --- a/NEWS.md +++ b/NEWS.md @@ -1339,6 +1339,13 @@ Deprecated or removed * `linspace` and `logspace` now require an explicit number of elements to be supplied rather than defaulting to `50`. + * 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 --------------------------- diff --git a/base/abstractarray.jl b/base/abstractarray.jl index 721dfe78d3baf..b5c3ab79c6170 100644 --- a/base/abstractarray.jl +++ b/base/abstractarray.jl @@ -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) diff --git a/base/associative.jl b/base/associative.jl index a55b1c82cfb7c..f3719ba65eaac 100644 --- a/base/associative.jl +++ b/base/associative.jl @@ -138,8 +138,23 @@ pairs(collection) = Generator(=>, keys(collection), values(collection)) pairs(a::Associative) = a +""" + 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 @@ -418,7 +433,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 pair in d if f(pair) @@ -527,7 +542,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) diff --git a/base/deepcopy.jl b/base/deepcopy.jl index 7ee72dc6926ce..2f22a32679697 100644 --- a/base/deepcopy.jl +++ b/base/deepcopy.jl @@ -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) diff --git a/base/deprecated.jl b/base/deprecated.jl index d789c5cd47dd9..44e83e07e72c7 100644 --- a/base/deprecated.jl +++ b/base/deprecated.jl @@ -2172,6 +2172,10 @@ end @deprecate merge!(repo::LibGit2.GitRepo, args...; kwargs...) LibGit2.merge!(repo, args...; kwargs...) +# 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 diff --git a/base/dict.jl b/base/dict.jl index 50371473e427f..bd3ac41287e55 100644 --- a/base/dict.jl +++ b/base/dict.jl @@ -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}() @@ -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 @@ -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) @@ -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 @@ -794,12 +796,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")) diff --git a/base/env.jl b/base/env.jl index f998b009fc10e..e7849373e308f 100644 --- a/base/env.jl +++ b/base/env.jl @@ -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) diff --git a/base/exports.jl b/base/exports.jl index 8213ecce7b584..31af9f6ce7813 100644 --- a/base/exports.jl +++ b/base/exports.jl @@ -649,6 +649,7 @@ export deleteat!, eltype, empty!, + empty, endof, filter!, filter, diff --git a/base/pkg/query.jl b/base/pkg/query.jl index b24ec3f6469af..69893315aafc5 100644 --- a/base/pkg/query.jl +++ b/base/pkg/query.jl @@ -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 diff --git a/base/tuple.jl b/base/tuple.jl index 962a7f1b654a9..4a8e26ca29b70 100644 --- a/base/tuple.jl +++ b/base/tuple.jl @@ -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) = () diff --git a/base/weakkeydict.jl b/base/weakkeydict.jl index 030302e380687..f07d2f29f5ea0 100644 --- a/base/weakkeydict.jl +++ b/base/weakkeydict.jl @@ -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 diff --git a/test/abstractarray.jl b/test/abstractarray.jl index ef8f72d391cb5..23f0747410efd 100644 --- a/test/abstractarray.jl +++ b/test/abstractarray.jl @@ -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}(uninitialized, 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,0}(uninitialized); 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 diff --git a/test/dict.jl b/test/dict.jl index aa0d21c32e1c8..7212022e51ea4 100644 --- a/test/dict.jl +++ b/test/dict.jl @@ -389,7 +389,7 @@ end a[1] = a a[a] = 2 - sa = similar(a) + sa = empty(a) @test isempty(sa) @test isa(sa, ObjectIdDict) @@ -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"] @@ -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) diff --git a/test/tuple.jl b/test/tuple.jl index 438fe0e0d47ca..7fda7483c9572 100644 --- a/test/tuple.jl +++ b/test/tuple.jl @@ -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