Skip to content

Commit dc593ad

Browse files
committed
make map and collect more general and uniform by adding iterator traits
1 parent d8b5679 commit dc593ad

File tree

9 files changed

+163
-194
lines changed

9 files changed

+163
-194
lines changed

base/abstractarray.jl

Lines changed: 3 additions & 147 deletions
Original file line numberDiff line numberDiff line change
@@ -1087,34 +1087,6 @@ foreach(f) = (f(); nothing)
10871087
foreach(f, itr) = (for x in itr; f(x); end; nothing)
10881088
foreach(f, itrs...) = (for z in zip(itrs...); f(z...); end; nothing)
10891089

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

11201092
## transform any set of dimensions
@@ -1175,39 +1147,6 @@ function mapslices(f, A::AbstractArray, dims::AbstractVector)
11751147
return R
11761148
end
11771149

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

1229-
function map_to!{T,F}(f::F, offs, st, dest::AbstractArray{T}, A)
1230-
# map to dest array, checking the type of each result. if a result does not
1231-
# match, widen the result type and re-dispatch.
1232-
i = offs
1233-
while !done(A, st)
1234-
@inbounds Ai, st = next(A, st)
1235-
el = f(Ai)
1236-
S = typeof(el)
1237-
if S === T || S <: T
1238-
@inbounds dest[i] = el::T
1239-
i += 1
1240-
else
1241-
R = typejoin(T, S)
1242-
new = similar(dest, R)
1243-
copy!(new,1, dest,1, i-1)
1244-
@inbounds new[i] = el
1245-
return map_to!(f, i+1, st, new, A)
1246-
end
1247-
end
1248-
return dest
1249-
end
1250-
1251-
function map(f, A::AbstractArray)
1252-
if isempty(A)
1253-
return isa(f,Type) ? similar(A,f) : similar(A)
1254-
end
1255-
st = start(A)
1256-
A1, st = next(A, st)
1257-
first = f(A1)
1258-
dest = similar(A, typeof(first))
1259-
dest[1] = first
1260-
return map_to!(f, 2, st, dest, A)
1261-
end
1168+
map{F}(f::F, A::AbstractArray) = collect(Generator(f,A))
12621169

12631170
## 2 argument
12641171
function map!{F}(f::F, dest::AbstractArray, A::AbstractArray, B::AbstractArray)
@@ -1268,34 +1175,6 @@ function map!{F}(f::F, dest::AbstractArray, A::AbstractArray, B::AbstractArray)
12681175
return dest
12691176
end
12701177

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

13011180
ith_all(i, ::Tuple{}) = ()
@@ -1311,32 +1190,9 @@ end
13111190

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

1314-
function map_to_n!{T,F}(f::F, offs, dest::AbstractArray{T}, As)
1315-
for i = offs:length(As[1])
1316-
el = f(ith_all(i, As)...)
1317-
S = typeof(el)
1318-
if (S !== T) && !(S <: T)
1319-
R = typejoin(T, S)
1320-
new = similar(dest, R)
1321-
copy!(new,1, dest,1, i-1)
1322-
@inbounds new[i] = el
1323-
return map_to_n!(f, i+1, new, As)
1324-
end
1325-
@inbounds dest[i] = el::T
1326-
end
1327-
return dest
1328-
end
1193+
spread(f) = (args)->f(args...)
13291194

1330-
function map(f, As::AbstractArray...)
1331-
shape = mapreduce(size, promote_shape, As)
1332-
if prod(shape) == 0
1333-
return similar(As[1], promote_eltype(As...), shape)
1334-
end
1335-
first = f(map(a->a[1], As)...)
1336-
dest = similar(As[1], typeof(first), shape)
1337-
dest[1] = first
1338-
return map_to_n!(f, 2, dest, As)
1339-
end
1195+
map(f, iters...) = collect(Generator(spread(f),zip(iters...)))
13401196

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

base/array.jl

Lines changed: 81 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -204,30 +204,93 @@ promote_rule{T,n,S}(::Type{Array{T,n}}, ::Type{Array{S,n}}) = Array{promote_type
204204
205205
Return an array of type `Array{element_type,1}` of all items in a collection.
206206
"""
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
207+
collect{T}(::Type{T}, itr) = _collect_t(T, itr, iteratorsize(itr))
224208

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

232295
## Iteration ##
233296
start(A::Array) = 1

base/dict.jl

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,9 @@
44

55
abstract Associative{K,V}
66

7+
iteratorsize{T<:Associative}(::Type{T}) = HasLength()
8+
iteratoreltype{T<:Associative}(::Type{T}) = HasEltype()
9+
710
const secret_table_token = :__c782dbf1cf4d6a2e5e3865d7e95634f2e09b5902__
811

912
haskey(d::Associative, k) = in(k,keys(d))

base/generator.jl

Lines changed: 38 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,11 +11,48 @@ immutable Generator{I,F}
1111
iter::I
1212
end
1313

14+
Generator{T,I}(::Type{T}, iter::I) = Generator{I,Type{T}}(T, iter)
15+
1416
start(g::Generator) = start(g.iter)
1517
done(g::Generator, s) = done(g.iter, s)
1618
function next(g::Generator, s)
1719
v, s2 = next(g.iter, s)
1820
g.f(v), s2
1921
end
2022

21-
collect(g::Generator) = map(g.f, g.iter)
23+
## iterator traits
24+
25+
abstract IteratorSize
26+
immutable SizeUnknown <: IteratorSize end
27+
immutable HasLength <: IteratorSize end
28+
immutable HasShape <: IteratorSize end
29+
30+
iteratorsize(x) = iteratorsize(typeof(x))
31+
iteratorsize(::Type) = SizeUnknown()
32+
33+
and_iteratorsize{T}(isz::T, ::T) = isz
34+
and_iteratorsize(::HasLength, ::HasShape) = HasLength()
35+
and_iteratorsize(::HasShape, ::HasLength) = HasLength()
36+
and_iteratorsize(a, b) = SizeUnknown()
37+
38+
abstract IteratorEltype
39+
immutable EltypeUnknown <: IteratorEltype end
40+
immutable HasEltype <: IteratorEltype end
41+
42+
iteratoreltype(x) = iteratoreltype(typeof(x))
43+
iteratoreltype(::Type) = EltypeUnknown()
44+
45+
and_iteratoreltype{T}(iel::T, ::T) = iel
46+
and_iteratoreltype(a, b) = EltypeUnknown()
47+
48+
iteratorsize{T<:AbstractArray}(::Type{T}) = HasShape()
49+
iteratorsize{T<:AbstractString}(::Type{T}) = HasLength()
50+
iteratorsize{T<:Tuple}(::Type{T}) = HasLength()
51+
iteratorsize{I,F}(::Type{Generator{I,F}}) = iteratorsize(I)
52+
length(g::Generator) = length(g.iter)
53+
size(g::Generator) = size(g.iter)
54+
55+
iteratoreltype{T<:AbstractArray}(::Type{T}) = HasEltype()
56+
iteratoreltype{T<:AbstractString}(::Type{T}) = HasEltype()
57+
iteratoreltype{I,T}(::Type{Generator{I,Type{T}}}) = HasEltype()
58+
eltype{I,T}(::Type{Generator{I,Type{T}}}) = T

0 commit comments

Comments
 (0)