12
12
const SmallUnsigned = Union{UInt8,UInt16,UInt32}
13
13
end
14
14
15
- # Certain reductions like sum and prod may wish to promote the items being reduced over to
16
- # an appropriate size. Note we need x + zero(x) because some types like Bool have their sum
17
- # lie in a larger type.
18
- promote_sys_size (T:: Type ) = T
19
- promote_sys_size (:: Type{<:SmallSigned} ) = Int
20
- promote_sys_size (:: Type{<:SmallUnsigned} ) = UInt
21
-
22
- promote_sys_size_add (x) = convert (promote_sys_size (typeof (x + zero (x))), x)
23
- promote_sys_size_mul (x) = convert (promote_sys_size (typeof (x * one (x))), x)
24
- const _PromoteSysSizeFunction = Union{typeof (promote_sys_size_add),
25
- typeof (promote_sys_size_mul)}
15
+ """
16
+ Base.add_sum(x,y)
17
+
18
+ The reduction operator used in `sum`. The main difference from [`+`](@ref) is that small
19
+ integers are promoted to `Int`/`UInt`.
20
+ """
21
+ add_sum (x,y) = x + y
22
+ add_sum (x:: SmallSigned ,y:: SmallSigned ) = Int (x) + Int (y)
23
+ add_sum (x:: SmallUnsigned ,y:: SmallUnsigned ) = UInt (x) + UInt (y)
24
+
25
+ """
26
+ Base.mul_prod(x,y)
27
+
28
+ The reduction operator used in `prod`. The main difference from [`*`](@ref) is that small
29
+ integers are promoted to `Int`/`UInt`.
30
+ """
31
+ mul_prod (x,y) = x * y
32
+ mul_prod (x:: SmallSigned ,y:: SmallSigned ) = Int (x) * Int (y)
33
+ mul_prod (x:: SmallUnsigned ,y:: SmallUnsigned ) = UInt (x) * UInt (y)
26
34
27
35
# # foldl && mapfoldl
28
36
@@ -64,7 +72,7 @@ function mapfoldl(f, op, itr)
64
72
return Base. mapreduce_empty_iter (f, op, itr, iteratoreltype (itr))
65
73
end
66
74
(x, i) = next (itr, i)
67
- v0 = f ( x)
75
+ v0 = mapreduce_first (f, op, x)
68
76
mapfoldl_impl (f, op, v0, itr, i)
69
77
end
70
78
@@ -133,7 +141,7 @@ function mapfoldr(f, op, itr)
133
141
if isempty (itr)
134
142
return Base. mapreduce_empty_iter (f, op, itr, iteratoreltype (itr))
135
143
end
136
- return mapfoldr_impl (f, op, f ( itr[i]), itr, i- 1 )
144
+ return mapfoldr_impl (f, op, mapreduce_first (f, op, itr[i]), itr, i- 1 )
137
145
end
138
146
139
147
"""
@@ -174,7 +182,7 @@ foldr(op, itr) = mapfoldr(identity, op, itr)
174
182
@noinline function mapreduce_impl (f, op, A:: AbstractArray , ifirst:: Integer , ilast:: Integer , blksize:: Int )
175
183
if ifirst == ilast
176
184
@inbounds a1 = A[ifirst]
177
- return f ( a1)
185
+ return mapreduce_first (f, op, a1)
178
186
elseif ifirst + blksize > ilast
179
187
# sequential portion
180
188
@inbounds a1 = A[ifirst]
@@ -238,34 +246,88 @@ pairwise_blocksize(::typeof(abs2), ::typeof(+)) = 4096
238
246
239
247
# handling empty arrays
240
248
_empty_reduce_error () = throw (ArgumentError (" reducing over an empty collection is not allowed" ))
249
+
250
+ """
251
+ Base.reduce_empty(op, T)
252
+
253
+ The value to be returned when calling [`reduce`](@ref), [`foldl`](@ref) or [`foldr`](@ref)
254
+ with reduction `op` over an empty array with element type of `T`.
255
+
256
+ If not defined, this will throw an `ArgumentError`.
257
+ """
241
258
reduce_empty (op, T) = _empty_reduce_error ()
242
259
reduce_empty (:: typeof (+ ), T) = zero (T)
260
+ reduce_empty (:: typeof (+ ), :: Type{Bool} ) = zero (Int)
243
261
reduce_empty (:: typeof (* ), T) = one (T)
262
+ reduce_empty (:: typeof (* ), :: Type{Char} ) = " "
244
263
reduce_empty (:: typeof (& ), :: Type{Bool} ) = true
245
264
reduce_empty (:: typeof (| ), :: Type{Bool} ) = false
246
265
266
+ reduce_empty (:: typeof (add_sum), T) = reduce_empty (+ , T)
267
+ reduce_empty (:: typeof (add_sum), :: Type{T} ) where {T<: SmallSigned } = zero (Int)
268
+ reduce_empty (:: typeof (add_sum), :: Type{T} ) where {T<: SmallUnsigned } = zero (UInt)
269
+ reduce_empty (:: typeof (mul_prod), T) = reduce_empty (* , T)
270
+ reduce_empty (:: typeof (mul_prod), :: Type{T} ) where {T<: SmallSigned } = one (Int)
271
+ reduce_empty (:: typeof (mul_prod), :: Type{T} ) where {T<: SmallUnsigned } = one (UInt)
272
+
273
+ """
274
+ Base.mapreduce_empty(f, op, T)
275
+
276
+ The value to be returned when calling [`mapreduce`](@ref), [`mapfoldl`](@ref`) or
277
+ [`mapfoldr`](@ref) with map `f` and reduction `op` over an empty array with element type
278
+ of `T`.
279
+
280
+ If not defined, this will throw an `ArgumentError`.
281
+ """
247
282
mapreduce_empty (f, op, T) = _empty_reduce_error ()
248
283
mapreduce_empty (:: typeof (identity), op, T) = reduce_empty (op, T)
249
- mapreduce_empty (f:: _PromoteSysSizeFunction , op, T) =
250
- f (mapreduce_empty (identity, op, T))
251
- mapreduce_empty (:: typeof (abs), :: typeof (+ ), T) = abs (zero (T))
252
- mapreduce_empty (:: typeof (abs2), :: typeof (+ ), T) = abs2 (zero (T))
253
- mapreduce_empty (:: typeof (abs), :: Union{typeof(scalarmax), typeof(max)} , T) =
254
- abs (zero (T))
255
- mapreduce_empty (:: typeof (abs2), :: Union{typeof(scalarmax), typeof(max)} , T) =
256
- abs2 (zero (T))
257
-
258
- # Allow mapreduce_empty to “see through” promote_sys_size
259
- let ComposedFunction = typename (typeof (identity ∘ identity)). wrapper
260
- global mapreduce_empty (f:: ComposedFunction{<:_PromoteSysSizeFunction} , op, T) =
261
- f. f (mapreduce_empty (f. g, op, T))
262
- end
284
+ mapreduce_empty (:: typeof (abs), op, T) = abs (reduce_empty (op, T))
285
+ mapreduce_empty (:: typeof (abs2), op, T) = abs2 (reduce_empty (op, T))
286
+
287
+ mapreduce_empty (f:: typeof (abs), :: Union{typeof(scalarmax), typeof(max)} , T) = abs (zero (T))
288
+ mapreduce_empty (f:: typeof (abs2), :: Union{typeof(scalarmax), typeof(max)} , T) = abs2 (zero (T))
263
289
264
290
mapreduce_empty_iter (f, op, itr, :: HasEltype ) = mapreduce_empty (f, op, eltype (itr))
265
291
mapreduce_empty_iter (f, op:: typeof (& ), itr, :: EltypeUnknown ) = true
266
292
mapreduce_empty_iter (f, op:: typeof (| ), itr, :: EltypeUnknown ) = false
267
293
mapreduce_empty_iter (f, op, itr, :: EltypeUnknown ) = _empty_reduce_error ()
268
294
295
+ # handling of single-element iterators
296
+ """
297
+ Base.reduce_first(op, x)
298
+
299
+ The value to be returned when calling [`reduce`](@ref), [`foldl`](@ref`) or
300
+ [`foldr`](@ref) with reduction `op` over an iterator which contains a single element
301
+ `x`. This value may also used to initialise the recursion, so that `reduce(op, [x, y])`
302
+ may call `op(reduce_first(op, x), y)`.
303
+
304
+ The default is `x` for most types. The main purpose is to ensure type stability, so
305
+ additional methods should only be defined for cases where `op` gives a result with
306
+ different types than its inputs.
307
+ """
308
+ reduce_first (op, x) = x
309
+ reduce_first (:: typeof (+ ), x:: Bool ) = Int (x)
310
+ reduce_first (:: typeof (* ), x:: Char ) = string (x)
311
+
312
+ reduce_first (:: typeof (add_sum), x) = reduce_first (+ , x)
313
+ reduce_first (:: typeof (add_sum), x:: SmallSigned ) = Int (x)
314
+ reduce_first (:: typeof (add_sum), x:: SmallUnsigned ) = UInt (x)
315
+ reduce_first (:: typeof (mul_prod), x) = reduce_first (* , x)
316
+ reduce_first (:: typeof (mul_prod), x:: SmallSigned ) = Int (x)
317
+ reduce_first (:: typeof (mul_prod), x:: SmallUnsigned ) = UInt (x)
318
+
319
+ """
320
+ Base.mapreduce_first(f, op, x)
321
+
322
+ The value to be returned when calling [`mapreduce`](@ref), [`mapfoldl`](@ref`) or
323
+ [`mapfoldr`](@ref) with map `f` and reduction `op` over an iterator which contains a
324
+ single element `x`. This value may also used to initialise the recursion, so that
325
+ `mapreduce(f, op, [x, y])` may call `op(reduce_first(op, f, x), f(y))`.
326
+
327
+ The default is `reduce_first(op, f(x))`.
328
+ """
329
+ mapreduce_first (f, op, x) = reduce_first (op, f (x))
330
+
269
331
_mapreduce (f, op, A:: AbstractArray ) = _mapreduce (f, op, IndexStyle (A), A)
270
332
271
333
function _mapreduce (f, op, :: IndexLinear , A:: AbstractArray{T} ) where T
@@ -275,7 +337,7 @@ function _mapreduce(f, op, ::IndexLinear, A::AbstractArray{T}) where T
275
337
return mapreduce_empty (f, op, T)
276
338
elseif n == 1
277
339
@inbounds a1 = A[inds[1 ]]
278
- return f ( a1)
340
+ return mapreduce_first (f, op, a1)
279
341
elseif n < 16 # process short array here, avoid mapreduce_impl() compilation
280
342
@inbounds i = inds[1 ]
281
343
@inbounds a1 = A[i]
294
356
_mapreduce (f, op, :: IndexCartesian , A:: AbstractArray ) = mapfoldl (f, op, A)
295
357
296
358
mapreduce (f, op, A:: AbstractArray ) = _mapreduce (f, op, IndexStyle (A), A)
297
- mapreduce (f, op, a:: Number ) = f ( a)
359
+ mapreduce (f, op, a:: Number ) = mapreduce_first (f, op, a)
298
360
299
361
"""
300
362
reduce(op, v0, itr)
@@ -372,7 +434,7 @@ In the former case, the integers are widened to system word size and therefore
372
434
the result is 128. In the latter case, no such widening happens and integer
373
435
overflow results in -128.
374
436
"""
375
- sum (f:: Callable , a) = mapreduce (promote_sys_size_add ∘ f, + , a)
437
+ sum (f, a) = mapreduce (f, add_sum , a)
376
438
377
439
"""
378
440
sum(itr)
@@ -388,7 +450,7 @@ julia> sum(1:20)
388
450
210
389
451
```
390
452
"""
391
- sum (a) = mapreduce (promote_sys_size_add, + , a)
453
+ sum (a) = sum (identity , a)
392
454
sum (a:: AbstractArray{Bool} ) = count (a)
393
455
394
456
# # prod
@@ -406,7 +468,7 @@ julia> prod(abs2, [2; 3; 4])
406
468
576
407
469
```
408
470
"""
409
- prod (f:: Callable , a) = mapreduce (promote_sys_size_mul ∘ f, * , a)
471
+ prod (f, a) = mapreduce (f, mul_prod , a)
410
472
411
473
"""
412
474
prod(itr)
@@ -422,7 +484,7 @@ julia> prod(1:20)
422
484
2432902008176640000
423
485
```
424
486
"""
425
- prod (a) = mapreduce (promote_sys_size_mul, * , a)
487
+ prod (a) = mapreduce (identity, mul_prod , a)
426
488
427
489
# # maximum & minimum
428
490
@@ -433,7 +495,7 @@ function mapreduce_impl(f, op::Union{typeof(scalarmax),
433
495
A:: AbstractArray , first:: Int , last:: Int )
434
496
# locate the first non NaN number
435
497
@inbounds a1 = A[first]
436
- v = f ( a1)
498
+ v = mapreduce_first (f, op, a1)
437
499
i = first + 1
438
500
while (v == v) && (i <= last)
439
501
@inbounds ai = A[i]
0 commit comments