From 2dca269cb9e8128bb0ef91c64805c15863447d5a 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 | 5 ++++- 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(+), 28 deletions(-) diff --git a/NEWS.md b/NEWS.md index e76ddd4aa4eb3..6f06512b2e180 100644 --- a/NEWS.md +++ b/NEWS.md @@ -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 --------------------------- diff --git a/base/abstractarray.jl b/base/abstractarray.jl index 70d1fd4828d4e..7ca2c3cd75776 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 3e04c6adacf01..2d4e451e06def 100644 --- a/base/associative.jl +++ b/base/associative.jl @@ -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 @@ -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) @@ -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) 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 f05f28b10c117..821a0d2fbcc92 100644 --- a/base/deprecated.jl +++ b/base/deprecated.jl @@ -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 @@ -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 diff --git a/base/dict.jl b/base/dict.jl index 1f8be1c375aee..d519b6ac35b98 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 @@ -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")) diff --git a/base/env.jl b/base/env.jl index 718c500257665..4fa664c428add 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 889910e51cdfc..2f483fb0a9567 100644 --- a/base/exports.jl +++ b/base/exports.jl @@ -651,6 +651,7 @@ export deleteat!, eltype, empty!, + empty, endof, filter!, filter, diff --git a/base/pkg/query.jl b/base/pkg/query.jl index 0233d159186a6..2507fa36741e1 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 d211cf16028ce..0ed5f865995e6 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 6e852a90c5b22..1d39c1e9c8a4d 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}(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 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