Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
42 changes: 31 additions & 11 deletions base/reduce.jl
Original file line number Diff line number Diff line change
Expand Up @@ -345,8 +345,17 @@ reduce_empty(::typeof(+), ::Type{Bool}) = zero(Int)
reduce_empty(::typeof(*), ::Type{Union{}}) = _empty_reduce_error(*, Union{})
reduce_empty(::typeof(*), ::Type{T}) where {T} = one(T)
reduce_empty(::typeof(*), ::Type{<:AbstractChar}) = ""
reduce_empty(::typeof(&), ::Type{Bool}) = true
reduce_empty(::typeof(|), ::Type{Bool}) = false

reduce_empty(::typeof(&), ::Type{Union{}}) = _empty_reduce_error(&, Union{})
reduce_empty(::typeof(&), ::Type{T}) where T<:Integer = -1 % T
reduce_empty(::typeof(|), ::Type{Union{}}) = _empty_reduce_error(|, Union{})
reduce_empty(::typeof(xor), ::Type{Union{}}) = _empty_reduce_error(xor, Union{})
reduce_empty(::Union{typeof(|),typeof(xor)}, ::Type{T}) where T<:Integer = zero(T)

reduce_empty(::typeof(min), ::Type{Union{}}) = _empty_reduce_error(min, Union{})
reduce_empty(::typeof(min), ::Type{T}) where T = typemax(T)
reduce_empty(::typeof(max), ::Type{Union{}}) = _empty_reduce_error(max, Union{})
reduce_empty(::typeof(max), ::Type{T}) where T = typemin(T)

reduce_empty(::typeof(add_sum), ::Type{Union{}}) = _empty_reduce_error(add_sum, Union{})
reduce_empty(::typeof(add_sum), ::Type{T}) where {T} = reduce_empty(+, T)
Expand Down Expand Up @@ -460,7 +469,7 @@ initial value `init` must be a neutral element for `op` that will be returned fo
collections. It is unspecified whether `init` is used for non-empty collections.

For empty collections, providing `init` will be necessary, except for some special cases
(e.g. when `op` is one of `+`, `*`, `max`, `min`, `&`, `|`) when Julia can determine the
(e.g. when `op` is one of `+`, `*`, `max`, `min`, `&`, `|`, `xor`) when Julia can determine the
neutral element of `op`.

Reductions for certain commonly-used operators may have special implementations, and
Expand Down Expand Up @@ -739,7 +748,9 @@ Return the largest element in a collection.
The value returned for empty `itr` can be specified by `init`. It must be
a neutral element for `max` (i.e. which is less than or equal to any
other element) as it is unspecified whether `init` is used
for non-empty collections.
for non-empty collections. If `init` is not given for an empty collection
with element type `T`, then `maximum` returns `typemin(T)`, which must
therefore be defined.

!!! compat "Julia 1.6"
Keyword argument `init` requires Julia 1.6 or later.
Expand All @@ -752,12 +763,14 @@ julia> maximum(-20.5:10)
julia> maximum([1,2,3])
3

julia> maximum(())
ERROR: MethodError: reducing over an empty collection is not allowed; consider supplying `init` to the reducer
Stacktrace:
julia> maximum([])
ERROR: MethodError: no method matching typemin(::Type{Any})
[...]

julia> maximum((); init=-Inf)
julia> maximum([]; init=-Inf)
-Inf

julia> maximum(Float64[])
-Inf
```
"""
Expand All @@ -771,7 +784,9 @@ Return the smallest element in a collection.
The value returned for empty `itr` can be specified by `init`. It must be
a neutral element for `min` (i.e. which is greater than or equal to any
other element) as it is unspecified whether `init` is used
for non-empty collections.
for non-empty collections. If `init` is not given for an empty collection
with element type `T`, then `minimum` returns `typemax(T)`, which must
therefore be defined.

!!! compat "Julia 1.6"
Keyword argument `init` requires Julia 1.6 or later.
Expand All @@ -785,12 +800,14 @@ julia> minimum([1,2,3])
1

julia> minimum([])
ERROR: MethodError: reducing over an empty collection is not allowed; consider supplying `init` to the reducer
Stacktrace:
ERROR: MethodError: no method matching typemax(::Type{Any})
[...]

julia> minimum([]; init=Inf)
Inf

julia> minimum(Float64[])
Inf
```
"""
minimum(a; kw...) = mapreduce(identity, min, a; kw...)
Expand Down Expand Up @@ -872,6 +889,9 @@ function _extrema_rf(x::NTuple{2,T}, y::NTuple{2,T}) where {T<:IEEEFloat}
z1, z2
end

mapreduce_empty(f::ExtremaMap, ::typeof(_extrema_rf), T) =
(mapreduce_empty(f.f, min, T), mapreduce_empty(f.f, max, T))

## findmax, findmin, argmax & argmin

"""
Expand Down
30 changes: 25 additions & 5 deletions test/reduce.jl
Original file line number Diff line number Diff line change
Expand Up @@ -248,9 +248,9 @@ prod2(itr) = invoke(prod, Tuple{Any}, itr)

# maximum & minimum & extrema

@test_throws "reducing over an empty" maximum(Int[])
@test_throws "reducing over an empty" minimum(Int[])
@test_throws "reducing over an empty" extrema(Int[])
@test_throws MethodError maximum(Vector{Int}[])
@test_throws MethodError minimum(String[])
@test_throws MethodError extrema(String[])

@test maximum(Int[]; init=-1) == -1
@test minimum(Int[]; init=-1) == -1
Expand Down Expand Up @@ -626,8 +626,6 @@ test18695(r) = sum( t^2 for t in r )

# test neutral element not picked incorrectly for &, |
@test @inferred(foldl(&, Int[1])) === 1
@test_throws ["reducing over an empty",
"consider supplying `init`"] foldl(&, Int[])

# prod on Chars
@test prod(Char[]) == ""
Expand Down Expand Up @@ -705,3 +703,25 @@ let a = NamedTuple(Symbol(:x,i) => i for i in 1:33),
b = (a...,)
@test fold_alloc(a) == fold_alloc(b) == 0
end

@testset "reduce over empty Integer lists with &" begin
for T in (Bool, Int8, UInt16, Int32, UInt64, Int128, BigInt)
x = reduce(&, T[])
@test x isa T && x == ~zero(T)
end
end

@testset "extrema of empty Real lists" begin
for T in (Bool, Int8, UInt16, Int32, UInt64, Int128, Float16, Float32, Float64, BigFloat)
x = extrema(T[])
@test x isa Tuple{T,T} && x == (typemax(T), typemin(T))
end
end

@testset "reducing over empty tuples and vectors of type Union{}" begin
for f in (+, *, &, |, xor, min, max, Base.add_sum, Base.mul_prod)
s = "reducing with $f over an empty collection of element type Union{} is not allowed"
@test_throws s reduce(f, ())
@test_throws s reduce(f, Union{}[])
end
end
5 changes: 2 additions & 3 deletions test/reducedim.jl
Original file line number Diff line number Diff line change
Expand Up @@ -182,9 +182,8 @@ end
A = Matrix{Int}(undef, 0,1)
@test sum(A) === 0
@test prod(A) === 1
@test_throws ["reducing over an empty",
"consider supplying `init`"] minimum(A)
@test_throws "consider supplying `init`" maximum(A)
@test minimum(A) === typemax(Int)
@test maximum(A) === typemin(Int)

@test isequal(sum(A, dims=1), zeros(Int, 1, 1))
@test isequal(sum(A, dims=2), zeros(Int, 0, 1))
Expand Down