Skip to content

Commit a2ce230

Browse files
committed
make map and collect more general and uniform by adding iterator traits
make HasEltype and HasLength the default this is a possible fix for #15342 type-accumulating Dict constructor some tests for type accumulation in `map` and `Dict`
1 parent 2103b17 commit a2ce230

21 files changed

+243
-220
lines changed

base/abstractarray.jl

+5-147
Original file line numberDiff line numberDiff line change
@@ -1086,34 +1086,6 @@ foreach(f) = (f(); nothing)
10861086
foreach(f, itr) = (for x in itr; f(x); end; nothing)
10871087
foreach(f, itrs...) = (for z in zip(itrs...); f(z...); end; nothing)
10881088

1089-
# generic map on any iterator
1090-
function map(f, iters...)
1091-
result = []
1092-
len = length(iters)
1093-
states = [start(iters[idx]) for idx in 1:len]
1094-
nxtvals = cell(len)
1095-
cont = true
1096-
for idx in 1:len
1097-
if done(iters[idx], states[idx])
1098-
cont = false
1099-
break
1100-
end
1101-
end
1102-
while cont
1103-
for idx in 1:len
1104-
nxtvals[idx],states[idx] = next(iters[idx], states[idx])
1105-
end
1106-
push!(result, f(nxtvals...))
1107-
for idx in 1:len
1108-
if done(iters[idx], states[idx])
1109-
cont = false
1110-
break
1111-
end
1112-
end
1113-
end
1114-
result
1115-
end
1116-
11171089
## map over arrays ##
11181090

11191091
## transform any set of dimensions
@@ -1174,39 +1146,6 @@ function mapslices(f, A::AbstractArray, dims::AbstractVector)
11741146
return R
11751147
end
11761148

1177-
1178-
# using promote_type
1179-
function promote_to!{T,F}(f::F, offs, dest::AbstractArray{T}, A::AbstractArray)
1180-
# map to dest array, checking the type of each result. if a result does not
1181-
# match, do a type promotion and re-dispatch.
1182-
for i = offs:length(A) #Fixme iter
1183-
@inbounds Ai = A[i]
1184-
el = f(Ai)
1185-
S = typeof(el)
1186-
if S === T || S <: T
1187-
@inbounds dest[i] = el::T
1188-
else
1189-
R = promote_type(T, S)
1190-
if R !== T
1191-
new = similar(dest, R)
1192-
copy!(new,1, dest,1, i-1)
1193-
new[i] = el
1194-
return promote_to!(f, i+1, new, A)
1195-
end
1196-
@inbounds dest[i] = el
1197-
end
1198-
end
1199-
return dest
1200-
end
1201-
1202-
function map_promote(f, A::AbstractArray)
1203-
if isempty(A); return similar(A, Bottom); end
1204-
first = f(A[1])
1205-
dest = similar(A, typeof(first))
1206-
dest[1] = first
1207-
return promote_to!(f, 2, dest, A)
1208-
end
1209-
12101149
# These are needed because map(eltype, As) is not inferrable
12111150
promote_eltype_op(::Any) = (@_pure_meta; Bottom)
12121151
promote_eltype_op{T}(op, ::AbstractArray{T}) = (@_pure_meta; promote_op(op, T))
@@ -1225,39 +1164,11 @@ function map!{F}(f::F, dest::AbstractArray, A::AbstractArray)
12251164
return dest
12261165
end
12271166

1228-
function map_to!{T,F}(f::F, offs, st, dest::AbstractArray{T}, A)
1229-
# map to dest array, checking the type of each result. if a result does not
1230-
# match, widen the result type and re-dispatch.
1231-
i = offs
1232-
while !done(A, st)
1233-
@inbounds Ai, st = next(A, st)
1234-
el = f(Ai)
1235-
S = typeof(el)
1236-
if S === T || S <: T
1237-
@inbounds dest[i] = el::T
1238-
i += 1
1239-
else
1240-
R = typejoin(T, S)
1241-
new = similar(dest, R)
1242-
copy!(new,1, dest,1, i-1)
1243-
@inbounds new[i] = el
1244-
return map_to!(f, i+1, st, new, A)
1245-
end
1246-
end
1247-
return dest
1248-
end
1167+
# map on collections
1168+
map(f, A::Union{AbstractArray,AbstractSet,Associative}) = collect_similar(A, Generator(f,A))
12491169

1250-
function map(f, A::AbstractArray)
1251-
if isempty(A)
1252-
return isa(f,Type) ? similar(A,f) : similar(A)
1253-
end
1254-
st = start(A)
1255-
A1, st = next(A, st)
1256-
first = f(A1)
1257-
dest = similar(A, typeof(first))
1258-
dest[1] = first
1259-
return map_to!(f, 2, st, dest, A)
1260-
end
1170+
# default to returning an Array for `map` on general iterators
1171+
map(f, A) = collect(Generator(f,A))
12611172

12621173
## 2 argument
12631174
function map!{F}(f::F, dest::AbstractArray, A::AbstractArray, B::AbstractArray)
@@ -1267,34 +1178,6 @@ function map!{F}(f::F, dest::AbstractArray, A::AbstractArray, B::AbstractArray)
12671178
return dest
12681179
end
12691180

1270-
function map_to!{T,F}(f::F, offs, dest::AbstractArray{T}, A::AbstractArray, B::AbstractArray)
1271-
for i = offs:length(A) #Fixme iter
1272-
@inbounds Ai, Bi = A[i], B[i]
1273-
el = f(Ai, Bi)
1274-
S = typeof(el)
1275-
if (S !== T) && !(S <: T)
1276-
R = typejoin(T, S)
1277-
new = similar(dest, R)
1278-
copy!(new,1, dest,1, i-1)
1279-
@inbounds new[i] = el
1280-
return map_to!(f, i+1, new, A, B)
1281-
end
1282-
@inbounds dest[i] = el::T
1283-
end
1284-
return dest
1285-
end
1286-
1287-
function map(f, A::AbstractArray, B::AbstractArray)
1288-
shp = promote_shape(size(A),size(B))
1289-
if prod(shp) == 0
1290-
return similar(A, promote_type(eltype(A),eltype(B)), shp)
1291-
end
1292-
first = f(A[1], B[1])
1293-
dest = similar(A, typeof(first), shp)
1294-
dest[1] = first
1295-
return map_to!(f, 2, dest, A, B)
1296-
end
1297-
12981181
## N argument
12991182

13001183
ith_all(i, ::Tuple{}) = ()
@@ -1310,32 +1193,7 @@ end
13101193

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

1313-
function map_to_n!{T,F}(f::F, offs, dest::AbstractArray{T}, As)
1314-
for i = offs:length(As[1])
1315-
el = f(ith_all(i, As)...)
1316-
S = typeof(el)
1317-
if (S !== T) && !(S <: T)
1318-
R = typejoin(T, S)
1319-
new = similar(dest, R)
1320-
copy!(new,1, dest,1, i-1)
1321-
@inbounds new[i] = el
1322-
return map_to_n!(f, i+1, new, As)
1323-
end
1324-
@inbounds dest[i] = el::T
1325-
end
1326-
return dest
1327-
end
1328-
1329-
function map(f, As::AbstractArray...)
1330-
shape = mapreduce(size, promote_shape, As)
1331-
if prod(shape) == 0
1332-
return similar(As[1], promote_eltype(As...), shape)
1333-
end
1334-
first = f(map(a->a[1], As)...)
1335-
dest = similar(As[1], typeof(first), shape)
1336-
dest[1] = first
1337-
return map_to_n!(f, 2, dest, As)
1338-
end
1196+
map(f, iters...) = collect(Generator(f, iters...))
13391197

13401198
# multi-item push!, unshift! (built on top of type-specific 1-item version)
13411199
# (note: must not cause a dispatch loop when 1-item case is not defined)

base/array.jl

+82-18
Original file line numberDiff line numberDiff line change
@@ -199,35 +199,99 @@ convert{T,n,S}(::Type{Array{T,n}}, x::AbstractArray{S,n}) = copy!(Array(T, size(
199199

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

202+
## copying iterators to containers
203+
204+
# make a collection similar to `c` and appropriate for collecting `itr`
205+
_similar_for(c::AbstractArray, T, itr, ::SizeUnknown) = similar(c, T, 0)
206+
_similar_for(c::AbstractArray, T, itr, ::HasLength) = similar(c, T, Int(length(itr)::Integer))
207+
_similar_for(c::AbstractArray, T, itr, ::HasShape) = similar(c, T, convert(Dims,size(itr)))
208+
_similar_for(c, T, itr, isz) = similar(c, T)
209+
202210
"""
203211
collect(element_type, collection)
204212
205213
Return an array of type `Array{element_type,1}` of all items in a collection.
206214
"""
207-
function collect{T}(::Type{T}, itr)
208-
if applicable(length, itr)
209-
# when length() isn't defined this branch might pollute the
210-
# type of the other.
211-
a = Array(T,length(itr)::Integer)
212-
i = 0
213-
for x in itr
214-
a[i+=1] = x
215-
end
216-
else
217-
a = Array(T,0)
218-
for x in itr
219-
push!(a,x)
220-
end
221-
end
222-
return a
223-
end
215+
collect{T}(::Type{T}, itr) = collect(Generator(T, itr))
224216

225217
"""
226218
collect(collection)
227219
228220
Return an array of all items in a collection. For associative collections, returns Pair{KeyType, ValType}.
229221
"""
230-
collect(itr) = collect(eltype(itr), itr)
222+
collect(itr) = _collect(1:1 #= Array =#, itr, iteratoreltype(itr), iteratorsize(itr))
223+
224+
collect_similar(cont, itr) = _collect(cont, itr, iteratoreltype(itr), iteratorsize(itr))
225+
226+
_collect(cont, itr, ::HasEltype, isz::Union{HasLength,HasShape}) =
227+
copy!(_similar_for(cont, eltype(itr), itr, isz), itr)
228+
229+
function _collect(cont, itr, ::HasEltype, isz::SizeUnknown)
230+
a = _similar_for(cont, eltype(itr), itr, isz)
231+
for x in itr
232+
push!(a,x)
233+
end
234+
return a
235+
end
236+
237+
_collect(c, itr, ::EltypeUnknown, isz::SizeUnknown) = grow_to!(_similar_for(c, Union{}, itr, isz), itr)
238+
239+
function _collect(c, itr, ::EltypeUnknown, isz::Union{HasLength,HasShape})
240+
st = start(itr)
241+
if done(itr,st)
242+
return _similar_for(c, Union{}, itr, isz)
243+
end
244+
v1, st = next(itr, st)
245+
collect_to_with_first!(_similar_for(c, typeof(v1), itr, isz), v1, itr, st)
246+
end
247+
248+
function collect_to_with_first!(dest::AbstractArray, v1, itr, st)
249+
dest[1] = v1
250+
return collect_to!(dest, itr, 2, st)
251+
end
252+
253+
function collect_to_with_first!(dest, v1, itr, st)
254+
push!(dest, v1)
255+
return grow_to!(dest, itr, st)
256+
end
257+
258+
function collect_to!{T}(dest::AbstractArray{T}, itr, offs, st)
259+
# collect to dest array, checking the type of each result. if a result does not
260+
# match, widen the result type and re-dispatch.
261+
i = offs
262+
while !done(itr, st)
263+
el, st = next(itr, st)
264+
S = typeof(el)
265+
if S === T || S <: T
266+
@inbounds dest[i] = el::T
267+
i += 1
268+
else
269+
R = typejoin(T, S)
270+
new = similar(dest, R)
271+
copy!(new,1, dest,1, i-1)
272+
@inbounds new[i] = el
273+
return collect_to!(new, itr, i+1, st)
274+
end
275+
end
276+
return dest
277+
end
278+
279+
function grow_to!(dest, itr, st = start(itr))
280+
T = eltype(dest)
281+
while !done(itr, st)
282+
el, st = next(itr, st)
283+
S = typeof(el)
284+
if S === T || S <: T
285+
push!(dest, el::T)
286+
else
287+
new = similar(dest, typejoin(T, S))
288+
copy!(new, dest)
289+
push!(new, el)
290+
return grow_to!(new, itr, st)
291+
end
292+
end
293+
return dest
294+
end
231295

232296
## Iteration ##
233297
start(A::Array) = 1

base/channels.jl

+2
Original file line numberDiff line numberDiff line change
@@ -95,3 +95,5 @@ function done(c::Channel, state::Ref)
9595
end
9696
end
9797
next{T}(c::Channel{T}, state) = (v=get(state[]); state[]=nothing; (v, state))
98+
99+
iteratorsize{C<:Channel}(::Type{C}) = SizeUnknown()

base/dict.jl

+29-16
Original file line numberDiff line numberDiff line change
@@ -2,8 +2,6 @@
22

33
# generic operations on associative collections
44

5-
abstract Associative{K,V}
6-
75
const secret_table_token = :__c782dbf1cf4d6a2e5e3865d7e95634f2e09b5902__
86

97
haskey(d::Associative, k) = in(k,keys(d))
@@ -225,6 +223,16 @@ function merge!(d::Associative, others::Associative...)
225223
end
226224
return d
227225
end
226+
227+
# very similar to `merge!`, but accepts any iterable and extends code
228+
# that would otherwise only use `copy!` with arrays.
229+
function copy!(dest::Union{Associative,AbstractSet}, src)
230+
for x in src
231+
push!(dest, x)
232+
end
233+
return dest
234+
end
235+
228236
keytype{K,V}(::Type{Associative{K,V}}) = K
229237
keytype(a::Associative) = keytype(typeof(a))
230238
keytype{A<:Associative}(::Type{A}) = keytype(supertype(A))
@@ -449,19 +457,6 @@ copy(d::Dict) = Dict(d)
449457

450458
const AnyDict = Dict{Any,Any}
451459

452-
# TODO: this can probably be simplified using `eltype` as a THT (Tim Holy trait)
453-
Dict{K,V}(kv::Tuple{Vararg{Tuple{K,V}}}) = Dict{K,V}(kv)
454-
Dict{K }(kv::Tuple{Vararg{Tuple{K,Any}}}) = Dict{K,Any}(kv)
455-
Dict{V }(kv::Tuple{Vararg{Tuple{Any,V}}}) = Dict{Any,V}(kv)
456-
Dict{K,V}(kv::Tuple{Vararg{Pair{K,V}}}) = Dict{K,V}(kv)
457-
Dict{K }(kv::Tuple{Vararg{Pair{K}}}) = Dict{K,Any}(kv)
458-
Dict{V }(kv::Tuple{Vararg{Pair{TypeVar(:K),V}}}) = Dict{Any,V}(kv)
459-
Dict( kv::Tuple{Vararg{Pair}}) = Dict{Any,Any}(kv)
460-
461-
Dict{K,V}(kv::AbstractArray{Tuple{K,V}}) = Dict{K,V}(kv)
462-
Dict{K,V}(kv::AbstractArray{Pair{K,V}}) = Dict{K,V}(kv)
463-
Dict{K,V}(kv::Associative{K,V}) = Dict{K,V}(kv)
464-
465460
Dict{K,V}(ps::Pair{K,V}...) = Dict{K,V}(ps)
466461
Dict{K }(ps::Pair{K}...,) = Dict{K,Any}(ps)
467462
Dict{V }(ps::Pair{TypeVar(:K),V}...,) = Dict{Any,V}(ps)
@@ -482,9 +477,27 @@ end
482477

483478
dict_with_eltype{K,V}(kv, ::Type{Tuple{K,V}}) = Dict{K,V}(kv)
484479
dict_with_eltype{K,V}(kv, ::Type{Pair{K,V}}) = Dict{K,V}(kv)
485-
dict_with_eltype(kv, t) = Dict{Any,Any}(kv)
480+
dict_with_eltype(kv, t) = grow_to!(Dict{Union{},Union{}}(), kv)
481+
482+
# this is a special case due to (1) allowing both Pairs and Tuples as elements,
483+
# and (2) Pair being invariant. a bit annoying.
484+
function grow_to!{K,V}(dest::Associative{K,V}, itr, st = start(itr))
485+
while !done(itr, st)
486+
(k,v), st = next(itr, st)
487+
if isa(k,K) && isa(v,V)
488+
dest[k] = v
489+
else
490+
new = similar(dest, Pair{typejoin(K,typeof(k)), typejoin(V,typeof(v))})
491+
copy!(new, dest)
492+
new[k] = v
493+
return grow_to!(new, itr, st)
494+
end
495+
end
496+
return dest
497+
end
486498

487499
similar{K,V}(d::Dict{K,V}) = Dict{K,V}()
500+
similar{K,V}(d::Dict, ::Type{Pair{K,V}}) = Dict{K,V}()
488501

489502
# conversion between Dict types
490503
function convert{K,V}(::Type{Dict{K,V}},d::Associative)

base/essentials.jl

+3
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,9 @@ typealias Callable Union{Function,DataType}
66

77
const Bottom = Union{}
88

9+
abstract AbstractSet{T}
10+
abstract Associative{K,V}
11+
912
# The real @inline macro is not available until after array.jl, so this
1013
# internal macro splices the meta Expr directly into the function body.
1114
macro _inline_meta()

0 commit comments

Comments
 (0)