Skip to content

Commit

Permalink
make collect and map more generic
Browse files Browse the repository at this point in the history
this is a possible fix for #15342

type-accumulating Dict constructor
  • Loading branch information
JeffBezanson committed Mar 24, 2016
1 parent 29b08a1 commit 9785457
Show file tree
Hide file tree
Showing 7 changed files with 85 additions and 64 deletions.
10 changes: 6 additions & 4 deletions base/abstractarray.jl
Original file line number Diff line number Diff line change
Expand Up @@ -1164,7 +1164,11 @@ function map!{F}(f::F, dest::AbstractArray, A::AbstractArray)
return dest
end

map{F}(f::F, A::AbstractArray) = collect(Generator(f,A))
# map on collections
map(f, A::Union{AbstractArray,AbstractSet,Associative}) = collect_similar(A, Generator(f,A))

# default to returning an Array for `map` on general iterators
map(f, A) = collect(Generator(f,A))

## 2 argument
function map!{F}(f::F, dest::AbstractArray, A::AbstractArray, B::AbstractArray)
Expand All @@ -1189,9 +1193,7 @@ end

map!{F}(f::F, dest::AbstractArray, As::AbstractArray...) = map_n!(f, dest, As)

spread(f) = (args)->f(args...)

map(f, iters...) = collect(Generator(spread(f),zip(iters...)))
map(f, iters...) = collect(Generator(f, iters...))

# multi-item push!, unshift! (built on top of type-specific 1-item version)
# (note: must not cause a dispatch loop when 1-item case is not defined)
Expand Down
85 changes: 43 additions & 42 deletions base/array.jl
Original file line number Diff line number Diff line change
Expand Up @@ -199,39 +199,63 @@ convert{T,n,S}(::Type{Array{T,n}}, x::AbstractArray{S,n}) = copy!(Array(T, size(

promote_rule{T,n,S}(::Type{Array{T,n}}, ::Type{Array{S,n}}) = Array{promote_type(T,S),n}

## copying iterators to containers

# make a collection similar to `c` and appropriate for collecting `itr`
_similar_for(c::AbstractArray, T, itr, ::SizeUnknown) = similar(c, T, 0)
_similar_for(c::AbstractArray, T, itr, ::HasLength) = similar(c, T, Int(length(itr)::Integer))
_similar_for(c::AbstractArray, T, itr, ::HasShape) = similar(c, T, convert(Dims,size(itr)))
_similar_for(c, T, itr, isz) = similar(c, T)

"""
collect(element_type, collection)
Return an array of type `Array{element_type,1}` of all items in a collection.
"""
collect{T}(::Type{T}, itr) = _collect_t(T, itr, iteratorsize(itr))
collect{T}(::Type{T}, itr) = collect(Generator(T, itr))

"""
collect(collection)
Return an array of all items in a collection. For associative collections, returns Pair{KeyType, ValType}.
"""
collect(itr) = _collect(itr, iteratoreltype(itr), iteratorsize(itr))
collect(itr) = _collect(1:1 #= Array =#, itr, iteratoreltype(itr), iteratorsize(itr))

_collect(itr, ::HasEltype, isz) = _collect_t(eltype(itr), itr, isz)
collect_similar(cont, itr) = _collect(cont, itr, iteratoreltype(itr), iteratorsize(itr))

_collect_t(T::Type, itr, ::HasLength) = copy!(Array(T,Int(length(itr)::Integer)), itr)
_collect_t(T::Type, itr, ::HasShape) = copy!(Array(T,convert(Dims,size(itr))), itr)
function _collect_t(T::Type, itr, ::SizeUnknown)
a = Array(T,0)
_collect(cont, itr, ::HasEltype, isz::Union{HasLength,HasShape}) =
copy!(_similar_for(cont, eltype(itr), itr, isz), itr)

function _collect(cont, itr, ::HasEltype, isz::SizeUnknown)
a = _similar_for(cont, eltype(itr), itr, isz)
for x in itr
push!(a,x)
end
return a
end

_collect(itr, ::EltypeUnknown, ::HasLength) = _collect_shaped(itr, (Int(length(itr)),))
_collect(itr, ::EltypeUnknown, ::HasShape) = _collect_shaped(itr, convert(Dims,size(itr)))
_collect(c, itr, ::EltypeUnknown, isz::SizeUnknown) = grow_to!(_similar_for(c, Union{}, itr, isz), itr)

function _collect(c, itr, ::EltypeUnknown, isz::Union{HasLength,HasShape})
st = start(itr)
if done(itr,st)
return _similar_for(c, Union{}, itr, isz)
end
v1, st = next(itr, st)
collect_to_with_first!(_similar_for(c, typeof(v1), itr, isz), v1, itr, st)
end

function collect_to_with_first!(dest::AbstractArray, v1, itr, st)
dest[1] = v1
return collect_to!(dest, itr, 2, st)
end

_default_container(itr, elty, sz) = Array(elty, sz)
_default_container(itr::AbstractArray, elty, sz) = similar(itr, elty, sz)
function collect_to_with_first!(dest, v1, itr, st)
push!(dest, v1)
return grow_to!(dest, itr, st)
end

function collect_to!{T}(itr, offs, st, dest::AbstractArray{T})
function collect_to!{T}(dest::AbstractArray{T}, itr, offs, st)
# collect to dest array, checking the type of each result. if a result does not
# match, widen the result type and re-dispatch.
i = offs
Expand All @@ -246,52 +270,29 @@ function collect_to!{T}(itr, offs, st, dest::AbstractArray{T})
new = similar(dest, R)
copy!(new,1, dest,1, i-1)
@inbounds new[i] = el
return collect_to!(itr, i+1, st, new)
return collect_to!(new, itr, i+1, st)
end
end
return dest
end

function _collect_shaped(itr, sz)
if prod(sz) == 0
return _default_container(itr, Union{}, sz)
end
st = start(itr)
v1, st = next(itr, st)
dest = _default_container(itr, typeof(v1), sz)
dest[1] = v1
return collect_to!(itr, 2, st, dest)
end

function grow_to!{T}(itr, st, dest::AbstractArray{T})
function grow_to!(dest, itr, st = start(itr))
T = eltype(dest)
while !done(itr, st)
el, st = next(itr, st)
S = typeof(el)
if S === T || S <: T
push!(dest, el::T)
else
R = typejoin(T, S)
n = length(dest)
new = similar(dest, R, n+1)
copy!(new,1, dest,1, n)
@inbounds new[n+1] = el
return grow_to!(itr, st, new)
new = similar(dest, typejoin(T, S))
copy!(new, dest)
push!(new, el)
return grow_to!(new, itr, st)
end
end
return dest
end

function _collect(itr, ::EltypeUnknown, ::SizeUnknown)
st = start(itr)
if done(itr,st)
return _default_container(itr, Union{}, 0)
end
v1, st = next(itr, st)
dest = _default_container(itr, typeof(v1), 1)
dest[1] = v1
return grow_to!(itr, st, dest)
end

## Iteration ##
start(A::Array) = 1
next(a::Array,i) = (a[i],i+1)
Expand Down
45 changes: 29 additions & 16 deletions base/dict.jl
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,6 @@

# generic operations on associative collections

abstract Associative{K,V}

const secret_table_token = :__c782dbf1cf4d6a2e5e3865d7e95634f2e09b5902__

haskey(d::Associative, k) = in(k,keys(d))
Expand Down Expand Up @@ -225,6 +223,16 @@ function merge!(d::Associative, others::Associative...)
end
return d
end

# very similar to `merge!`, but accepts any iterable and extends code
# that would otherwise only use `copy!` with arrays.
function copy!(dest::Union{Associative,AbstractSet}, src)
for x in src
push!(dest, x)
end
return dest
end

keytype{K,V}(::Type{Associative{K,V}}) = K
keytype(a::Associative) = keytype(typeof(a))
keytype{A<:Associative}(::Type{A}) = keytype(supertype(A))
Expand Down Expand Up @@ -449,19 +457,6 @@ copy(d::Dict) = Dict(d)

const AnyDict = Dict{Any,Any}

# TODO: this can probably be simplified using `eltype` as a THT (Tim Holy trait)
Dict{K,V}(kv::Tuple{Vararg{Tuple{K,V}}}) = Dict{K,V}(kv)
Dict{K }(kv::Tuple{Vararg{Tuple{K,Any}}}) = Dict{K,Any}(kv)
Dict{V }(kv::Tuple{Vararg{Tuple{Any,V}}}) = Dict{Any,V}(kv)
Dict{K,V}(kv::Tuple{Vararg{Pair{K,V}}}) = Dict{K,V}(kv)
Dict{K }(kv::Tuple{Vararg{Pair{K}}}) = Dict{K,Any}(kv)
Dict{V }(kv::Tuple{Vararg{Pair{TypeVar(:K),V}}}) = Dict{Any,V}(kv)
Dict( kv::Tuple{Vararg{Pair}}) = Dict{Any,Any}(kv)

Dict{K,V}(kv::AbstractArray{Tuple{K,V}}) = Dict{K,V}(kv)
Dict{K,V}(kv::AbstractArray{Pair{K,V}}) = Dict{K,V}(kv)
Dict{K,V}(kv::Associative{K,V}) = Dict{K,V}(kv)

Dict{K,V}(ps::Pair{K,V}...) = Dict{K,V}(ps)
Dict{K }(ps::Pair{K}...,) = Dict{K,Any}(ps)
Dict{V }(ps::Pair{TypeVar(:K),V}...,) = Dict{Any,V}(ps)
Expand All @@ -482,9 +477,27 @@ end

dict_with_eltype{K,V}(kv, ::Type{Tuple{K,V}}) = Dict{K,V}(kv)
dict_with_eltype{K,V}(kv, ::Type{Pair{K,V}}) = Dict{K,V}(kv)
dict_with_eltype(kv, t) = Dict{Any,Any}(kv)
dict_with_eltype(kv, t) = grow_to!(Dict{Union{},Union{}}(), kv)

# 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!{K,V}(dest::Associative{K,V}, itr, st = start(itr))
while !done(itr, st)
(k,v), st = next(itr, st)
if isa(k,K) && isa(v,V)
dest[k] = v
else
new = similar(dest, Pair{typejoin(K,typeof(k)), typejoin(V,typeof(v))})
copy!(new, dest)
new[k] = v
return grow_to!(new, itr, st)
end
end
return dest
end

similar{K,V}(d::Dict{K,V}) = Dict{K,V}()
similar{K,V}(d::Dict, ::Type{Pair{K,V}}) = Dict{K,V}()

# conversion between Dict types
function convert{K,V}(::Type{Dict{K,V}},d::Associative)
Expand Down
3 changes: 3 additions & 0 deletions base/essentials.jl
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,9 @@ typealias Callable Union{Function,DataType}

const Bottom = Union{}

abstract AbstractSet{T}
abstract Associative{K,V}

# The real @inline macro is not available until after array.jl, so this
# internal macro splices the meta Expr directly into the function body.
macro _inline_meta()
Expand Down
2 changes: 0 additions & 2 deletions base/intset.jl
Original file line number Diff line number Diff line change
@@ -1,7 +1,5 @@
# This file is a part of Julia. License is MIT: http://julialang.org/license

abstract AbstractSet{T}

type IntSet <: AbstractSet{Int}
bits::Array{UInt32,1}
limit::Int
Expand Down
1 change: 1 addition & 0 deletions base/set.jl
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ Set(itr) = Set{eltype(itr)}(itr)

eltype{T}(::Type{Set{T}}) = T
similar{T}(s::Set{T}) = Set{T}()
similar(s::Set, T::Type) = Set{T}()

function show(io::IO, s::Set)
print(io,"Set")
Expand Down
3 changes: 3 additions & 0 deletions test/functional.jl
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,9 @@ let z = zip(1:2, 3:4, 5:6)
@test eltype(z) == Tuple{Int,Int,Int}
end

# typed `collect`
@test collect(Float64, Filter(isodd, [1,2,3,4]))[1] === 1.0

# enumerate (issue #6284)
let b = IOBuffer("1\n2\n3\n"), a = []
for (i,x) in enumerate(eachline(b))
Expand Down

0 comments on commit 9785457

Please sign in to comment.