From ed1cfc91779a6bd158523214bccd210fc89496d1 Mon Sep 17 00:00:00 2001 From: Tim Holy Date: Mon, 11 May 2020 06:25:35 -0500 Subject: [PATCH 01/25] Support `init` keyword in `maximum`/`minimum` --- base/reduce.jl | 52 +++++++++++++++++++++++++++++++++++++---------- base/reducedim.jl | 28 ++++++++++++------------- test/reduce.jl | 3 +++ test/reducedim.jl | 5 +++++ 4 files changed, 63 insertions(+), 25 deletions(-) diff --git a/base/reduce.jl b/base/reduce.jl index 414bac5099f78..5c3fe4c2e1ff3 100644 --- a/base/reduce.jl +++ b/base/reduce.jl @@ -45,7 +45,7 @@ function mapfoldl_impl(f::F, op::OP, nt, itr) where {F,OP} end function foldl_impl(op::OP, nt, itr) where {OP} - v = _foldl_impl(op, get(nt, :init, _InitialValue()), itr) + v = _foldl_impl(op, nt, itr) v isa _InitialValue && return reduce_empty_iter(op, itr) return v end @@ -157,7 +157,7 @@ Like [`mapreduce`](@ref), but with guaranteed left associativity, as in [`foldl` If provided, the keyword argument `init` will be used exactly once. In general, it will be necessary to provide `init` to work with empty collections. """ -mapfoldl(f, op, itr; kw...) = mapfoldl_impl(f, op, kw.data, itr) +mapfoldl(f, op, itr; init=_InitialValue()) = mapfoldl_impl(f, op, init, itr) """ foldl(op, itr; [init]) @@ -200,7 +200,7 @@ Like [`mapreduce`](@ref), but with guaranteed right associativity, as in [`foldr provided, the keyword argument `init` will be used exactly once. In general, it will be necessary to provide `init` to work with empty collections. """ -mapfoldr(f, op, itr; kw...) = mapfoldr_impl(f, op, kw.data, itr) +mapfoldr(f, op, itr; init=_InitialValue()) = mapfoldr_impl(f, op, init, itr) """ @@ -606,35 +606,47 @@ function mapreduce_impl(f, op::Union{typeof(max), typeof(min)}, end """ - maximum(f, itr) + maximum(f, itr; [init]) Returns the largest result of calling function `f` on each element of `itr`. +If provided, `init` must be a neutral element for `max` that will be returned +for empty collections. # Examples ```jldoctest julia> maximum(length, ["Julion", "Julia", "Jule"]) 6 + +julia> maximum(length, []; init=-1) +-1 ``` """ -maximum(f, a) = mapreduce(f, max, a) +maximum(f, a; kw...) = mapreduce(f, max, a; kw...) """ - minimum(f, itr) + minimum(f, itr; [init]) Returns the smallest result of calling function `f` on each element of `itr`. +If provided, `init` must be a neutral element for `min` that will be returned +for empty collections. # Examples ```jldoctest julia> minimum(length, ["Julion", "Julia", "Jule"]) 4 + +julia> minimum(length, []; init=-1) +-1 ``` """ -minimum(f, a) = mapreduce(f, min, a) +minimum(f, a; kw...) = mapreduce(f, min, a; kw...) """ - maximum(itr) + maximum(itr; [init]) Returns the largest element in a collection. +If provided, `init` must be a neutral element for `max` that will be returned +for empty collections. # Examples ```jldoctest @@ -643,14 +655,24 @@ julia> maximum(-20.5:10) julia> maximum([1,2,3]) 3 + +julia> maximum(()) +ERROR: ArgumentError: reducing over an empty collection is not allowed +Stacktrace: +[...] + +julia> maximum((); init=-1) +-1 ``` """ -maximum(a) = mapreduce(identity, max, a) +maximum(a; kw...) = mapreduce(identity, max, a; kw...) """ - minimum(itr) + minimum(itr; [init]) Returns the smallest element in a collection. +If provided, `init` must be a neutral element for `min` that will be returned +for empty collections. # Examples ```jldoctest @@ -659,9 +681,17 @@ julia> minimum(-20.5:10) julia> minimum([1,2,3]) 1 + +julia> minimum([]) +ERROR: ArgumentError: reducing over an empty collection is not allowed +Stacktrace: +[...] + +julia> minimum([]; init=-1) +-1 ``` """ -minimum(a) = mapreduce(identity, min, a) +minimum(a; kw...) = mapreduce(identity, min, a; kw...) ## all & any diff --git a/base/reducedim.jl b/base/reducedim.jl index d1e5001492fc4..140e004cc5457 100644 --- a/base/reducedim.jl +++ b/base/reducedim.jl @@ -307,21 +307,21 @@ julia> mapreduce(isodd, |, a, dims=1) 1 1 1 1 ``` """ -mapreduce(f, op, A::AbstractArrayOrBroadcasted; dims=:, kw...) = - _mapreduce_dim(f, op, kw.data, A, dims) +mapreduce(f, op, A::AbstractArrayOrBroadcasted; dims=:, init=_InitialValue()) = + _mapreduce_dim(f, op, init, A, dims) mapreduce(f, op, A::AbstractArrayOrBroadcasted...; kw...) = reduce(op, map(f, A...); kw...) -_mapreduce_dim(f, op, nt::NamedTuple{(:init,)}, A::AbstractArrayOrBroadcasted, ::Colon) = - mapfoldl(f, op, A; nt...) +_mapreduce_dim(f, op, nt, A::AbstractArrayOrBroadcasted, ::Colon) = + mapfoldl_impl(f, op, nt, A) -_mapreduce_dim(f, op, ::NamedTuple{()}, A::AbstractArrayOrBroadcasted, ::Colon) = +_mapreduce_dim(f, op, ::_InitialValue, A::AbstractArrayOrBroadcasted, ::Colon) = _mapreduce(f, op, IndexStyle(A), A) -_mapreduce_dim(f, op, nt::NamedTuple{(:init,)}, A::AbstractArrayOrBroadcasted, dims) = - mapreducedim!(f, op, reducedim_initarray(A, dims, nt.init), A) +_mapreduce_dim(f, op, nt, A::AbstractArrayOrBroadcasted, dims) = + mapreducedim!(f, op, reducedim_initarray(A, dims, nt), A) -_mapreduce_dim(f, op, ::NamedTuple{()}, A::AbstractArrayOrBroadcasted, dims) = +_mapreduce_dim(f, op, ::_InitialValue, A::AbstractArrayOrBroadcasted, dims) = mapreducedim!(f, op, reducedim_init(f, op, A, dims), A) """ @@ -717,12 +717,12 @@ for (fname, _fname, op) in [(:sum, :_sum, :add_sum), (:prod, :_prod, (:maximum, :_maximum, :max), (:minimum, :_minimum, :min)] @eval begin # User-facing methods with keyword arguments - @inline ($fname)(a::AbstractArray; dims=:) = ($_fname)(a, dims) - @inline ($fname)(f, a::AbstractArray; dims=:) = ($_fname)(f, a, dims) + @inline ($fname)(a::AbstractArray; dims=:, kw...) = ($_fname)(a, dims; kw...) + @inline ($fname)(f, a::AbstractArray; dims=:, kw...) = ($_fname)(f, a, dims; kw...) # Underlying implementations using dispatch - ($_fname)(a, ::Colon) = ($_fname)(identity, a, :) - ($_fname)(f, a, ::Colon) = mapreduce(f, $op, a) + ($_fname)(a, ::Colon; kw...) = ($_fname)(identity, a, :; kw...) + ($_fname)(f, a, ::Colon; kw...) = mapreduce(f, $op, a; kw...) end end @@ -743,8 +743,8 @@ for (fname, op) in [(:sum, :add_sum), (:prod, :mul_prod), mapreducedim!(f, $(op), initarray!(r, $(op), init, A), A) $(fname!)(r::AbstractArray, A::AbstractArray; init::Bool=true) = $(fname!)(identity, r, A; init=init) - $(_fname)(A, dims) = $(_fname)(identity, A, dims) - $(_fname)(f, A, dims) = mapreduce(f, $(op), A, dims=dims) + $(_fname)(A, dims; kw...) = $(_fname)(identity, A, dims; kw...) + $(_fname)(f, A, dims; kw...) = mapreduce(f, $(op), A; dims=dims, kw...) end end diff --git a/test/reduce.jl b/test/reduce.jl index 69b8b1911e7ea..6c787bec82ac7 100644 --- a/test/reduce.jl +++ b/test/reduce.jl @@ -208,6 +208,9 @@ prod2(itr) = invoke(prod, Tuple{Any}, itr) @test_throws ArgumentError maximum(Int[]) @test_throws ArgumentError minimum(Int[]) +@test maximum(Int[]; init=-1) == -1 +@test minimum(Int[]; init=-1) == -1 + @test maximum(5) == 5 @test minimum(5) == 5 @test extrema(5) == (5, 5) diff --git a/test/reducedim.jl b/test/reducedim.jl index 3f59ae6e2570a..e51a075496f1c 100644 --- a/test/reducedim.jl +++ b/test/reducedim.jl @@ -75,6 +75,11 @@ safe_minabs(A::Array{T}, region) where {T} = safe_mapslices(minimum, abs.(A), re @test @inferred(count(!, Breduc, dims=region)) ≈ safe_count(.!Breduc, region) end +# Combining dims and init +A = Array{Int}(undef, 0, 3) +@test_throws ArgumentError maximum(A; dims=1) +@test maximum(A; dims=1, init=-1) == reshape([-1,-1,-1], 1, 3) + # Test reduction along first dimension; this is special-cased for # size(A, 1) >= 16 Breduc = rand(64, 3) From a9fd8b8a8792a197b7063b6e4e9f37b93c097062 Mon Sep 17 00:00:00 2001 From: Takafumi Arakaki Date: Sun, 7 Jun 2020 17:47:37 -0700 Subject: [PATCH 02/25] Add `init` keyword argument to `sum` and `prod` --- base/reduce.jl | 48 +++++++++++++++++++++++++++++++++++++++--------- 1 file changed, 39 insertions(+), 9 deletions(-) diff --git a/base/reduce.jl b/base/reduce.jl index 6d19fa50e3037..85b5fb70cdfd0 100644 --- a/base/reduce.jl +++ b/base/reduce.jl @@ -462,7 +462,7 @@ reduce(op, a::Number) = a # Do we want this? ## sum """ - sum(f, itr) + sum(f, itr; init) Sum the results of calling function `f` on each element of `itr`. @@ -470,6 +470,13 @@ The return type is `Int` for signed integers of less than system word size, and `UInt` for unsigned integers of less than system word size. For all other arguments, a common return type is found to which all arguments are promoted. +The value returned for empty `itr` can be specified by `init` which must be the +additive identity. It is unspecified whether `init` is used for non-empty +collections. + +!!! compat "Julia 1.6" + Keyword argument `init` requires Julia 1.6 or later. + # Examples ```jldoctest julia> sum(abs2, [2; 3; 4]) @@ -491,10 +498,10 @@ In the former case, the integers are widened to system word size and therefore the result is 128. In the latter case, no such widening happens and integer overflow results in -128. """ -sum(f, a) = mapreduce(f, add_sum, a) +sum(f, a; kw...) = mapreduce(f, add_sum, a; kw...) """ - sum(itr) + sum(itr; init) Returns the sum of all elements in a collection. @@ -502,18 +509,27 @@ The return type is `Int` for signed integers of less than system word size, and `UInt` for unsigned integers of less than system word size. For all other arguments, a common return type is found to which all arguments are promoted. +The value returned for empty `itr` can be specified by `init` which must be the +additive identity. It is unspecified whether `init` is used for non-empty +collections. + +!!! compat "Julia 1.6" + Keyword argument `init` requires Julia 1.6 or later. + # Examples ```jldoctest julia> sum(1:20) 210 ``` """ -sum(a) = sum(identity, a) -sum(a::AbstractArray{Bool}) = count(a) +sum(a; kw...) = sum(identity, a; kw...) +sum(a::AbstractArray{Bool}; kw...) = count(a) +# Note: It is OK to ignore `init` to `sum(::AbstractArray{Bool})` +# because it is unspecified if the value of `init` is used or not. ## prod """ - prod(f, itr) + prod(f, itr; init) Returns the product of `f` applied to each element of `itr`. @@ -521,16 +537,23 @@ The return type is `Int` for signed integers of less than system word size, and `UInt` for unsigned integers of less than system word size. For all other arguments, a common return type is found to which all arguments are promoted. +The value returned for empty `itr` can be specified by `init` which must be the +multiplicative identity. It is unspecified whether `init` is used for non-empty +collections. + +!!! compat "Julia 1.6" + Keyword argument `init` requires Julia 1.6 or later. + # Examples ```jldoctest julia> prod(abs2, [2; 3; 4]) 576 ``` """ -prod(f, a) = mapreduce(f, mul_prod, a) +prod(f, a; kw...) = mapreduce(f, mul_prod, a; kw...) """ - prod(itr) + prod(itr; init) Returns the product of all elements of a collection. @@ -538,13 +561,20 @@ The return type is `Int` for signed integers of less than system word size, and `UInt` for unsigned integers of less than system word size. For all other arguments, a common return type is found to which all arguments are promoted. +The value returned for empty `itr` can be specified by `init` which must be the +multiplicative identity. It is unspecified whether `init` is used for non-empty +collections. + +!!! compat "Julia 1.6" + Keyword argument `init` requires Julia 1.6 or later. + # Examples ```jldoctest julia> prod(1:20) 2432902008176640000 ``` """ -prod(a) = mapreduce(identity, mul_prod, a) +prod(a; kw...) = mapreduce(identity, mul_prod, a; kw...) ## maximum & minimum _fast(::typeof(min),x,y) = min(x,y) From 72d01c02114d3ef67eef9e4ddc8334d8f8be654e Mon Sep 17 00:00:00 2001 From: Takafumi Arakaki Date: Sun, 7 Jun 2020 18:34:27 -0700 Subject: [PATCH 03/25] Test sum and prod --- test/reduce.jl | 29 +++++++++++++++++++++++++++++ 1 file changed, 29 insertions(+) diff --git a/test/reduce.jl b/test/reduce.jl index 474a52e2e32fd..abedbdf960906 100644 --- a/test/reduce.jl +++ b/test/reduce.jl @@ -4,6 +4,9 @@ using Random isdefined(Main, :OffsetArrays) || @eval Main include("testhelpers/OffsetArrays.jl") using .Main.OffsetArrays +==ₜ(::Any, ::Any) = false +==ₜ(a::T, b::T) where {T} = isequal(a, b) + # fold(l|r) & mapfold(l|r) @test foldl(+, Int64[]) === Int64(0) # In reference to issues #7465/#20144 (PR #20160) @test foldl(+, Int16[]) === Int16(0) # In reference to issues #21536 @@ -172,6 +175,19 @@ for f in (sum3, sum4, sum7, sum8) end @test typeof(sum(Int8[])) == typeof(sum(Int8[1])) == typeof(sum(Int8[1 7])) +@testset "`sum` of empty collections with `init`" begin + @testset for init in [0, 0.0] + @test sum([]; init = init) === init + @test sum((x for x in [123] if false); init = init) === init + @test sum(nothing, []; init = init) === init + @test sum(nothing, (x for x in [123] if false); init = init) === init + @test sum(Array{Any,3}(undef, 3, 2, 0); dims = 1, init = init) ==ₜ + zeros(typeof(init), 1, 2, 0) + @test sum(nothing, Array{Any,3}(undef, 3, 2, 0); dims = 1, init = init) ==ₜ + zeros(typeof(init), 1, 2, 0) + end +end + # check sum(abs, ...) for support of empty collections @testset "sum(abs, [])" begin @test @inferred(sum(abs, Float64[])) === 0.0 @@ -199,6 +215,19 @@ end @test typeof(prod(Array(trues(10)))) == Bool +@testset "`prod` of empty collections with `init`" begin + @testset for init in [1, 1.0, ""] + @test prod([]; init = init) === init + @test prod((x for x in [123] if false); init = init) === init + @test prod(nothing, []; init = init) === init + @test prod(nothing, (x for x in [123] if false); init = init) === init + @test prod(Array{Any,3}(undef, 3, 2, 0); dims = 1, init = init) ==ₜ + ones(typeof(init), 1, 2, 0) + @test prod(nothing, Array{Any,3}(undef, 3, 2, 0); dims = 1, init = init) ==ₜ + ones(typeof(init), 1, 2, 0) + end +end + # check type-stability prod2(itr) = invoke(prod, Tuple{Any}, itr) @test prod(Int[]) === prod2(Int[]) === 1 From c3e66a57b5a3287dd6c0d2627b798aa9229f5c97 Mon Sep 17 00:00:00 2001 From: Takafumi Arakaki Date: Sun, 7 Jun 2020 18:44:04 -0700 Subject: [PATCH 04/25] Add compat annotations to `minimum` and `maximum` --- base/reduce.jl | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/base/reduce.jl b/base/reduce.jl index 95feba25334f4..fd7b306fcab06 100644 --- a/base/reduce.jl +++ b/base/reduce.jl @@ -646,6 +646,9 @@ Returns the largest result of calling function `f` on each element of `itr`. If provided, `init` must be a neutral element for `max` that will be returned for empty collections. +!!! compat "Julia 1.6" + Keyword argument `init` requires Julia 1.6 or later. + # Examples ```jldoctest julia> maximum(length, ["Julion", "Julia", "Jule"]) @@ -664,6 +667,9 @@ Returns the smallest result of calling function `f` on each element of `itr`. If provided, `init` must be a neutral element for `min` that will be returned for empty collections. +!!! compat "Julia 1.6" + Keyword argument `init` requires Julia 1.6 or later. + # Examples ```jldoctest julia> minimum(length, ["Julion", "Julia", "Jule"]) From 31feb691a554995382898a233caae5125cb564d5 Mon Sep 17 00:00:00 2001 From: Takafumi Arakaki Date: Sun, 7 Jun 2020 18:44:53 -0700 Subject: [PATCH 05/25] Tweak docstring signature style --- base/reduce.jl | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/base/reduce.jl b/base/reduce.jl index fd7b306fcab06..3e97db3e1afba 100644 --- a/base/reduce.jl +++ b/base/reduce.jl @@ -462,7 +462,7 @@ reduce(op, a::Number) = a # Do we want this? ## sum """ - sum(f, itr; init) + sum(f, itr; [init]) Sum the results of calling function `f` on each element of `itr`. @@ -501,7 +501,7 @@ overflow results in -128. sum(f, a; kw...) = mapreduce(f, add_sum, a; kw...) """ - sum(itr; init) + sum(itr; [init]) Returns the sum of all elements in a collection. @@ -529,7 +529,7 @@ sum(a::AbstractArray{Bool}; kw...) = count(a) ## prod """ - prod(f, itr; init) + prod(f, itr; [init]) Returns the product of `f` applied to each element of `itr`. @@ -553,7 +553,7 @@ julia> prod(abs2, [2; 3; 4]) prod(f, a; kw...) = mapreduce(f, mul_prod, a; kw...) """ - prod(itr; init) + prod(itr; [init]) Returns the product of all elements of a collection. From 32e0c99b6f21f84a75549cf2d527a1db3d35b286 Mon Sep 17 00:00:00 2001 From: Takafumi Arakaki Date: Sun, 7 Jun 2020 18:47:21 -0700 Subject: [PATCH 06/25] Fix reflection doctest --- base/reflection.jl | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/base/reflection.jl b/base/reflection.jl index 3b3e745559593..a5883382e9353 100644 --- a/base/reflection.jl +++ b/base/reflection.jl @@ -1218,10 +1218,12 @@ See also [`applicable`](@ref). julia> hasmethod(length, Tuple{Array}) true -julia> hasmethod(sum, Tuple{Function, Array}, (:dims,)) +julia> f(; orange) = orange; + +julia> hasmethod(f, Tuple{}, (:orange,)) true -julia> hasmethod(sum, Tuple{Function, Array}, (:apples, :bananas)) +julia> hasmethod(f, Tuple{}, (:apples, :bananas)) false julia> g(; xs...) = 4; From b8a686b5a94ab262ceed70ba74b5f646f21dfb75 Mon Sep 17 00:00:00 2001 From: Takafumi Arakaki Date: Sun, 7 Jun 2020 18:56:08 -0700 Subject: [PATCH 07/25] Add a NEWS item --- NEWS.md | 1 + 1 file changed, 1 insertion(+) diff --git a/NEWS.md b/NEWS.md index 71a2d66d7946c..4582f2f991a36 100644 --- a/NEWS.md +++ b/NEWS.md @@ -47,6 +47,7 @@ Standard library changes * The function `isapprox(x,y)` now accepts the `norm` keyword argument also for numeric (i.e., non-array) arguments `x` and `y` ([#35883]). * `view`, `@view`, and `@views` now work on `AbstractString`s, returning a `SubString` when appropriate ([#35879]). * All `AbstractUnitRange{<:Integer}`s now work with `SubString`, `view`, `@view` and `@views` on strings ([#35879]). +* `sum`, `prod`, `maximum`, and `minimum` now supports `init` keyword argument ([#36188], [#35839]). #### LinearAlgebra * New method `LinearAlgebra.issuccess(::CholeskyPivoted)` for checking whether pivoted Cholesky factorization was successful ([#36002]). From 749e2ce459fda5e627699c9e2874662e0a2bd849 Mon Sep 17 00:00:00 2001 From: Takafumi Arakaki Date: Sun, 7 Jun 2020 20:02:32 -0700 Subject: [PATCH 08/25] Fix `sum(::AbstractArray{Bool}; dims)` --- base/reduce.jl | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/base/reduce.jl b/base/reduce.jl index 3e97db3e1afba..0c0b89f5577a9 100644 --- a/base/reduce.jl +++ b/base/reduce.jl @@ -523,9 +523,8 @@ julia> sum(1:20) ``` """ sum(a; kw...) = sum(identity, a; kw...) -sum(a::AbstractArray{Bool}; kw...) = count(a) -# Note: It is OK to ignore `init` to `sum(::AbstractArray{Bool})` -# because it is unspecified if the value of `init` is used or not. +sum(a::AbstractArray{Bool}; kw...) = + kw === NamedTuple() ? count(a) : reduce(add_sum, a; kw...) ## prod """ From bf551a78e1870bab12aa5ccae9e096df075723a7 Mon Sep 17 00:00:00 2001 From: Takafumi Arakaki Date: Sun, 7 Jun 2020 21:34:37 -0700 Subject: [PATCH 09/25] Fix a typo --- NEWS.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/NEWS.md b/NEWS.md index 4582f2f991a36..11851e8356474 100644 --- a/NEWS.md +++ b/NEWS.md @@ -47,7 +47,7 @@ Standard library changes * The function `isapprox(x,y)` now accepts the `norm` keyword argument also for numeric (i.e., non-array) arguments `x` and `y` ([#35883]). * `view`, `@view`, and `@views` now work on `AbstractString`s, returning a `SubString` when appropriate ([#35879]). * All `AbstractUnitRange{<:Integer}`s now work with `SubString`, `view`, `@view` and `@views` on strings ([#35879]). -* `sum`, `prod`, `maximum`, and `minimum` now supports `init` keyword argument ([#36188], [#35839]). +* `sum`, `prod`, `maximum`, and `minimum` now support `init` keyword argument ([#36188], [#35839]). #### LinearAlgebra * New method `LinearAlgebra.issuccess(::CholeskyPivoted)` for checking whether pivoted Cholesky factorization was successful ([#36002]). From e1173cb5588cb249bfc2bcc4c0eaa7004ceff314 Mon Sep 17 00:00:00 2001 From: Takafumi Arakaki Date: Sun, 7 Jun 2020 23:14:03 -0700 Subject: [PATCH 10/25] Fix sum(::AbstractArray{Bool}) optimization path --- base/reduce.jl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/base/reduce.jl b/base/reduce.jl index 0c0b89f5577a9..0f571742a48a3 100644 --- a/base/reduce.jl +++ b/base/reduce.jl @@ -524,7 +524,7 @@ julia> sum(1:20) """ sum(a; kw...) = sum(identity, a; kw...) sum(a::AbstractArray{Bool}; kw...) = - kw === NamedTuple() ? count(a) : reduce(add_sum, a; kw...) + kw.data === NamedTuple() ? count(a) : reduce(add_sum, a; kw...) ## prod """ From 791f93ce011ffa4aa27cc5c18b93587475c12b67 Mon Sep 17 00:00:00 2001 From: Takafumi Arakaki Date: Mon, 8 Jun 2020 13:09:41 -0700 Subject: [PATCH 11/25] Apply suggestions from code review Co-authored-by: Milan Bouchet-Valat Co-authored-by: Tim Holy --- base/reduce.jl | 11 ++++++----- base/reflection.jl | 4 ++-- 2 files changed, 8 insertions(+), 7 deletions(-) diff --git a/base/reduce.jl b/base/reduce.jl index 0f571742a48a3..d5afedaf34061 100644 --- a/base/reduce.jl +++ b/base/reduce.jl @@ -470,9 +470,9 @@ The return type is `Int` for signed integers of less than system word size, and `UInt` for unsigned integers of less than system word size. For all other arguments, a common return type is found to which all arguments are promoted. -The value returned for empty `itr` can be specified by `init` which must be the -additive identity. It is unspecified whether `init` is used for non-empty -collections. +The value returned for empty `itr` can be specified by `init`. It must be +the additive identity (i.e. zero) as it is unspecified whether `init` is used +for non-empty collections. !!! compat "Julia 1.6" Keyword argument `init` requires Julia 1.6 or later. @@ -642,8 +642,9 @@ end maximum(f, itr; [init]) Returns the largest result of calling function `f` on each element of `itr`. -If provided, `init` must be a neutral element for `max` that will be returned -for empty collections. + +If provided, `init` must be a neutral element for `max` (i.e. which is less +than any other element) that will be returned for empty collections. !!! compat "Julia 1.6" Keyword argument `init` requires Julia 1.6 or later. diff --git a/base/reflection.jl b/base/reflection.jl index a5883382e9353..ca37d7f6250ec 100644 --- a/base/reflection.jl +++ b/base/reflection.jl @@ -1218,9 +1218,9 @@ See also [`applicable`](@ref). julia> hasmethod(length, Tuple{Array}) true -julia> f(; orange) = orange; +julia> f(; oranges=0) = oranges; -julia> hasmethod(f, Tuple{}, (:orange,)) +julia> hasmethod(f, Tuple{}, (:oranges,)) true julia> hasmethod(f, Tuple{}, (:apples, :bananas)) From 2b84499f40e0a6b1439f16ebf6ddbd94d537ed70 Mon Sep 17 00:00:00 2001 From: Takafumi Arakaki Date: Mon, 8 Jun 2020 13:33:47 -0700 Subject: [PATCH 12/25] Reflect the same changes to other docstrings --- base/reduce.jl | 25 +++++++++++++------------ 1 file changed, 13 insertions(+), 12 deletions(-) diff --git a/base/reduce.jl b/base/reduce.jl index d5afedaf34061..831beaa8cbc67 100644 --- a/base/reduce.jl +++ b/base/reduce.jl @@ -509,9 +509,9 @@ The return type is `Int` for signed integers of less than system word size, and `UInt` for unsigned integers of less than system word size. For all other arguments, a common return type is found to which all arguments are promoted. -The value returned for empty `itr` can be specified by `init` which must be the -additive identity. It is unspecified whether `init` is used for non-empty -collections. +The value returned for empty `itr` can be specified by `init`. It must be +the additive identity (i.e. zero) as it is unspecified whether `init` is used +for non-empty collections. !!! compat "Julia 1.6" Keyword argument `init` requires Julia 1.6 or later. @@ -536,9 +536,9 @@ The return type is `Int` for signed integers of less than system word size, and `UInt` for unsigned integers of less than system word size. For all other arguments, a common return type is found to which all arguments are promoted. -The value returned for empty `itr` can be specified by `init` which must be the -multiplicative identity. It is unspecified whether `init` is used for non-empty -collections. +The value returned for empty `itr` can be specified by `init`. It must be the +multiplicative identity (i.e. one) as it is unspecified whether `init` is used +for non-empty collections. !!! compat "Julia 1.6" Keyword argument `init` requires Julia 1.6 or later. @@ -560,9 +560,9 @@ The return type is `Int` for signed integers of less than system word size, and `UInt` for unsigned integers of less than system word size. For all other arguments, a common return type is found to which all arguments are promoted. -The value returned for empty `itr` can be specified by `init` which must be the -multiplicative identity. It is unspecified whether `init` is used for non-empty -collections. +The value returned for empty `itr` can be specified by `init`. It must be the +multiplicative identity (i.e. one) as it is unspecified whether `init` is used +for non-empty collections. !!! compat "Julia 1.6" Keyword argument `init` requires Julia 1.6 or later. @@ -644,7 +644,7 @@ end Returns the largest result of calling function `f` on each element of `itr`. If provided, `init` must be a neutral element for `max` (i.e. which is less -than any other element) that will be returned for empty collections. +than or equal to any other element) that will be returned for empty collections. !!! compat "Julia 1.6" Keyword argument `init` requires Julia 1.6 or later. @@ -664,8 +664,9 @@ maximum(f, a; kw...) = mapreduce(f, max, a; kw...) minimum(f, itr; [init]) Returns the smallest result of calling function `f` on each element of `itr`. -If provided, `init` must be a neutral element for `min` that will be returned -for empty collections. + +If provided, `init` must be a neutral element for `min` (i.e. which is greater +than or equal to any other element) that will be returned for empty collections. !!! compat "Julia 1.6" Keyword argument `init` requires Julia 1.6 or later. From 2122e0e538284f94f15a3bf1be29bd0ad7abc945 Mon Sep 17 00:00:00 2001 From: Takafumi Arakaki Date: Mon, 8 Jun 2020 13:51:10 -0700 Subject: [PATCH 13/25] Mention how `nothing` is used in the test --- test/reduce.jl | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/test/reduce.jl b/test/reduce.jl index abedbdf960906..1150229155fa5 100644 --- a/test/reduce.jl +++ b/test/reduce.jl @@ -186,6 +186,8 @@ end @test sum(nothing, Array{Any,3}(undef, 3, 2, 0); dims = 1, init = init) ==ₜ zeros(typeof(init), 1, 2, 0) end + # Note: We are using `nothing` in place of the callable to make + # sure that it's not actually called. end # check sum(abs, ...) for support of empty collections @@ -226,6 +228,8 @@ end @test prod(nothing, Array{Any,3}(undef, 3, 2, 0); dims = 1, init = init) ==ₜ ones(typeof(init), 1, 2, 0) end + # Note: We are using `nothing` in place of the callable to make + # sure that it's not actually called. end # check type-stability From 6918213ef2081430e7b6f586b1013aee6641523e Mon Sep 17 00:00:00 2001 From: Takafumi Arakaki Date: Mon, 8 Jun 2020 13:51:25 -0700 Subject: [PATCH 14/25] Demonstrate `init` for `sum` and `prod` --- base/reduce.jl | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/base/reduce.jl b/base/reduce.jl index 831beaa8cbc67..b3c2626baed12 100644 --- a/base/reduce.jl +++ b/base/reduce.jl @@ -520,6 +520,9 @@ for non-empty collections. ```jldoctest julia> sum(1:20) 210 + +julia> sum(1:20; init = 0.0) +210.0 ``` """ sum(a; kw...) = sum(identity, a; kw...) @@ -569,8 +572,11 @@ for non-empty collections. # Examples ```jldoctest -julia> prod(1:20) -2432902008176640000 +julia> prod(1:5) +120 + +julia> prod(1:5; init = 1.0) +120.0 ``` """ prod(a; kw...) = mapreduce(identity, mul_prod, a; kw...) From ea2cc8bae4388ac4bc63594a42aa660b4b395197 Mon Sep 17 00:00:00 2001 From: Takafumi Arakaki Date: Mon, 8 Jun 2020 14:06:31 -0700 Subject: [PATCH 15/25] Reflect the same changes to other docstrings (2) --- base/reduce.jl | 16 ++++++++++++---- 1 file changed, 12 insertions(+), 4 deletions(-) diff --git a/base/reduce.jl b/base/reduce.jl index b3c2626baed12..66a184fa35812 100644 --- a/base/reduce.jl +++ b/base/reduce.jl @@ -692,8 +692,12 @@ minimum(f, a; kw...) = mapreduce(f, min, a; kw...) maximum(itr; [init]) Returns the largest element in a collection. -If provided, `init` must be a neutral element for `max` that will be returned -for empty collections. + +If provided, `init` must be a neutral element for `max` (i.e. which is less +than or equal to any other element) that will be returned for empty collections. + +!!! compat "Julia 1.6" + Keyword argument `init` requires Julia 1.6 or later. # Examples ```jldoctest @@ -718,8 +722,12 @@ maximum(a; kw...) = mapreduce(identity, max, a; kw...) minimum(itr; [init]) Returns the smallest element in a collection. -If provided, `init` must be a neutral element for `min` that will be returned -for empty collections. + +If provided, `init` must be a neutral element for `min` (i.e. which is greater +than or equal to any other element) that will be returned for empty collections. + +!!! compat "Julia 1.6" + Keyword argument `init` requires Julia 1.6 or later. # Examples ```jldoctest From 32d7f93f29d8926c7f534c0127e621b26d2affd9 Mon Sep 17 00:00:00 2001 From: Takafumi Arakaki Date: Mon, 8 Jun 2020 14:08:01 -0700 Subject: [PATCH 16/25] Use `function noncallable end` --- test/reduce.jl | 18 ++++++++---------- 1 file changed, 8 insertions(+), 10 deletions(-) diff --git a/test/reduce.jl b/test/reduce.jl index 1150229155fa5..562f9ab9e0209 100644 --- a/test/reduce.jl +++ b/test/reduce.jl @@ -176,18 +176,17 @@ end @test typeof(sum(Int8[])) == typeof(sum(Int8[1])) == typeof(sum(Int8[1 7])) @testset "`sum` of empty collections with `init`" begin + function noncallable end # should not be called @testset for init in [0, 0.0] @test sum([]; init = init) === init @test sum((x for x in [123] if false); init = init) === init - @test sum(nothing, []; init = init) === init - @test sum(nothing, (x for x in [123] if false); init = init) === init + @test sum(noncallable, []; init = init) === init + @test sum(noncallable, (x for x in [123] if false); init = init) === init @test sum(Array{Any,3}(undef, 3, 2, 0); dims = 1, init = init) ==ₜ zeros(typeof(init), 1, 2, 0) - @test sum(nothing, Array{Any,3}(undef, 3, 2, 0); dims = 1, init = init) ==ₜ + @test sum(noncallable, Array{Any,3}(undef, 3, 2, 0); dims = 1, init = init) ==ₜ zeros(typeof(init), 1, 2, 0) end - # Note: We are using `nothing` in place of the callable to make - # sure that it's not actually called. end # check sum(abs, ...) for support of empty collections @@ -218,18 +217,17 @@ end @test typeof(prod(Array(trues(10)))) == Bool @testset "`prod` of empty collections with `init`" begin + function noncallable end # should not be called @testset for init in [1, 1.0, ""] @test prod([]; init = init) === init @test prod((x for x in [123] if false); init = init) === init - @test prod(nothing, []; init = init) === init - @test prod(nothing, (x for x in [123] if false); init = init) === init + @test prod(noncallable, []; init = init) === init + @test prod(noncallable, (x for x in [123] if false); init = init) === init @test prod(Array{Any,3}(undef, 3, 2, 0); dims = 1, init = init) ==ₜ ones(typeof(init), 1, 2, 0) - @test prod(nothing, Array{Any,3}(undef, 3, 2, 0); dims = 1, init = init) ==ₜ + @test prod(noncallable, Array{Any,3}(undef, 3, 2, 0); dims = 1, init = init) ==ₜ ones(typeof(init), 1, 2, 0) end - # Note: We are using `nothing` in place of the callable to make - # sure that it's not actually called. end # check type-stability From 3e7b09cfaf0a2019f895407f56e881adb5d2b5b1 Mon Sep 17 00:00:00 2001 From: Takafumi Arakaki Date: Mon, 8 Jun 2020 14:16:30 -0700 Subject: [PATCH 17/25] Use init=Inf for minimum docstring --- base/reduce.jl | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/base/reduce.jl b/base/reduce.jl index 66a184fa35812..8b3e941bcf357 100644 --- a/base/reduce.jl +++ b/base/reduce.jl @@ -682,8 +682,8 @@ than or equal to any other element) that will be returned for empty collections. julia> minimum(length, ["Julion", "Julia", "Jule"]) 4 -julia> minimum(length, []; init=-1) --1 +julia> minimum(length, []; init=Inf) +Inf ``` """ minimum(f, a; kw...) = mapreduce(f, min, a; kw...) @@ -742,8 +742,8 @@ ERROR: ArgumentError: reducing over an empty collection is not allowed Stacktrace: [...] -julia> minimum([]; init=-1) --1 +julia> minimum([]; init=Inf) +Inf ``` """ minimum(a; kw...) = mapreduce(identity, min, a; kw...) From 0791d495d129c9c3e85b6a960b27e0e6ecff276b Mon Sep 17 00:00:00 2001 From: Takafumi Arakaki Date: Tue, 9 Jun 2020 10:41:47 -0700 Subject: [PATCH 18/25] Apply suggestions from code review Co-authored-by: Milan Bouchet-Valat --- base/reduce.jl | 32 ++++++++++++++++++++------------ 1 file changed, 20 insertions(+), 12 deletions(-) diff --git a/base/reduce.jl b/base/reduce.jl index 8b3e941bcf357..a76902e38d121 100644 --- a/base/reduce.jl +++ b/base/reduce.jl @@ -649,8 +649,10 @@ end Returns the largest result of calling function `f` on each element of `itr`. -If provided, `init` must be a neutral element for `max` (i.e. which is less -than or equal to any other element) that will be returned for empty collections. +The value returned for empty `itr` can be specified by `init`. It must be the +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. !!! compat "Julia 1.6" Keyword argument `init` requires Julia 1.6 or later. @@ -660,8 +662,8 @@ than or equal to any other element) that will be returned for empty collections. julia> maximum(length, ["Julion", "Julia", "Jule"]) 6 -julia> maximum(length, []; init=-1) --1 +julia> maximum(length, []; init=-Inf) +-Inf ``` """ maximum(f, a; kw...) = mapreduce(f, max, a; kw...) @@ -671,8 +673,10 @@ maximum(f, a; kw...) = mapreduce(f, max, a; kw...) Returns the smallest result of calling function `f` on each element of `itr`. -If provided, `init` must be a neutral element for `min` (i.e. which is greater -than or equal to any other element) that will be returned for empty collections. +The value returned for empty `itr` can be specified by `init`. It must be the +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. !!! compat "Julia 1.6" Keyword argument `init` requires Julia 1.6 or later. @@ -693,8 +697,10 @@ minimum(f, a; kw...) = mapreduce(f, min, a; kw...) Returns the largest element in a collection. -If provided, `init` must be a neutral element for `max` (i.e. which is less -than or equal to any other element) that will be returned for empty collections. +The value returned for empty `itr` can be specified by `init`. It must be the +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. !!! compat "Julia 1.6" Keyword argument `init` requires Julia 1.6 or later. @@ -712,8 +718,8 @@ ERROR: ArgumentError: reducing over an empty collection is not allowed Stacktrace: [...] -julia> maximum((); init=-1) --1 +julia> maximum((); init=-Inf) +-Inf ``` """ maximum(a; kw...) = mapreduce(identity, max, a; kw...) @@ -723,8 +729,10 @@ maximum(a; kw...) = mapreduce(identity, max, a; kw...) Returns the smallest element in a collection. -If provided, `init` must be a neutral element for `min` (i.e. which is greater -than or equal to any other element) that will be returned for empty collections. +The value returned for empty `itr` can be specified by `init`. It must be the +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. !!! compat "Julia 1.6" Keyword argument `init` requires Julia 1.6 or later. From 923bbd8741b457e17df33176ee272eb2fb580598 Mon Sep 17 00:00:00 2001 From: Takafumi Arakaki Date: Tue, 9 Jun 2020 15:33:56 -0700 Subject: [PATCH 19/25] Revert: maximum(length, []; init=-1) --- base/reduce.jl | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/base/reduce.jl b/base/reduce.jl index a76902e38d121..d9780ed566412 100644 --- a/base/reduce.jl +++ b/base/reduce.jl @@ -662,8 +662,8 @@ for non-empty collections. julia> maximum(length, ["Julion", "Julia", "Jule"]) 6 -julia> maximum(length, []; init=-Inf) --Inf +julia> maximum(length, []; init=-1) +-1 ``` """ maximum(f, a; kw...) = mapreduce(f, max, a; kw...) From 0050d4f7cdd548ddf52783b7ac5994a32fb79483 Mon Sep 17 00:00:00 2001 From: Takafumi Arakaki Date: Tue, 9 Jun 2020 15:36:23 -0700 Subject: [PATCH 20/25] Use typemax(Int64) for minimum(length, []; init=typemax(Int64)) --- base/reduce.jl | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/base/reduce.jl b/base/reduce.jl index d9780ed566412..c1f7070506eab 100644 --- a/base/reduce.jl +++ b/base/reduce.jl @@ -686,8 +686,8 @@ for non-empty collections. julia> minimum(length, ["Julion", "Julia", "Jule"]) 4 -julia> minimum(length, []; init=Inf) -Inf +julia> minimum(length, []; init=typemax(Int64)) +9223372036854775807 ``` """ minimum(f, a; kw...) = mapreduce(f, min, a; kw...) From 6951b2c5cb52fdcb8cd692667ee050840e483a2a Mon Sep 17 00:00:00 2001 From: Takafumi Arakaki Date: Tue, 9 Jun 2020 15:41:13 -0700 Subject: [PATCH 21/25] Yet another example: minimum(tanh, Real[]; init=1.0) --- base/reduce.jl | 3 +++ 1 file changed, 3 insertions(+) diff --git a/base/reduce.jl b/base/reduce.jl index c1f7070506eab..aa7057661eedd 100644 --- a/base/reduce.jl +++ b/base/reduce.jl @@ -688,6 +688,9 @@ julia> minimum(length, ["Julion", "Julia", "Jule"]) julia> minimum(length, []; init=typemax(Int64)) 9223372036854775807 + +julia> minimum(tanh, Real[]; init=1.0) +1.0 ``` """ minimum(f, a; kw...) = mapreduce(f, min, a; kw...) From 04c0cc93262f05796a9619b8067246f0d465894a Mon Sep 17 00:00:00 2001 From: Takafumi Arakaki Date: Tue, 9 Jun 2020 15:43:56 -0700 Subject: [PATCH 22/25] Yet another example: maximum(tanh, Real[]; init=-1.0) --- base/reduce.jl | 3 +++ 1 file changed, 3 insertions(+) diff --git a/base/reduce.jl b/base/reduce.jl index aa7057661eedd..b42cb729edfb5 100644 --- a/base/reduce.jl +++ b/base/reduce.jl @@ -664,6 +664,9 @@ julia> maximum(length, ["Julion", "Julia", "Jule"]) julia> maximum(length, []; init=-1) -1 + +julia> maximum(tanh, Real[]; init=-1.0) +-1.0 ``` """ maximum(f, a; kw...) = mapreduce(f, max, a; kw...) From 5d952ecdc412d43e6d6e3848f318e6860ce30c69 Mon Sep 17 00:00:00 2001 From: Takafumi Arakaki Date: Wed, 10 Jun 2020 00:50:33 -0700 Subject: [PATCH 23/25] Apply suggestions from code review Co-authored-by: Milan Bouchet-Valat --- base/reduce.jl | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/base/reduce.jl b/base/reduce.jl index b42cb729edfb5..d893e9168ef81 100644 --- a/base/reduce.jl +++ b/base/reduce.jl @@ -649,7 +649,7 @@ end Returns the largest result of calling function `f` on each element of `itr`. -The value returned for empty `itr` can be specified by `init`. It must be the +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. @@ -676,7 +676,7 @@ maximum(f, a; kw...) = mapreduce(f, max, a; kw...) Returns the smallest result of calling function `f` on each element of `itr`. -The value returned for empty `itr` can be specified by `init`. It must be the +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. @@ -703,7 +703,7 @@ minimum(f, a; kw...) = mapreduce(f, min, a; kw...) Returns the largest element in a collection. -The value returned for empty `itr` can be specified by `init`. It must be the +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. @@ -735,7 +735,7 @@ maximum(a; kw...) = mapreduce(identity, max, a; kw...) Returns the smallest element in a collection. -The value returned for empty `itr` can be specified by `init`. It must be the +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. From 42a015593b37f199ba314eeb6603133542625fdc Mon Sep 17 00:00:00 2001 From: Takafumi Arakaki Date: Wed, 10 Jun 2020 00:52:33 -0700 Subject: [PATCH 24/25] A short note on the output bound of tanh --- base/reduce.jl | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/base/reduce.jl b/base/reduce.jl index d893e9168ef81..b002a7fef0d4e 100644 --- a/base/reduce.jl +++ b/base/reduce.jl @@ -665,7 +665,7 @@ julia> maximum(length, ["Julion", "Julia", "Jule"]) julia> maximum(length, []; init=-1) -1 -julia> maximum(tanh, Real[]; init=-1.0) +julia> maximum(tanh, Real[]; init=-1.0) # good, since output of tanh is >= -1 -1.0 ``` """ @@ -692,7 +692,7 @@ julia> minimum(length, ["Julion", "Julia", "Jule"]) julia> minimum(length, []; init=typemax(Int64)) 9223372036854775807 -julia> minimum(tanh, Real[]; init=1.0) +julia> minimum(tanh, Real[]; init=1.0) # good, since output of tanh is <= 1 1.0 ``` """ From 254a7cbff535ebc7799dc1e2a39d069f526b4a2c Mon Sep 17 00:00:00 2001 From: Takafumi Arakaki Date: Wed, 10 Jun 2020 22:18:33 -0700 Subject: [PATCH 25/25] Use sin instead of tanh --- base/reduce.jl | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/base/reduce.jl b/base/reduce.jl index b002a7fef0d4e..697f9de713a27 100644 --- a/base/reduce.jl +++ b/base/reduce.jl @@ -665,7 +665,7 @@ julia> maximum(length, ["Julion", "Julia", "Jule"]) julia> maximum(length, []; init=-1) -1 -julia> maximum(tanh, Real[]; init=-1.0) # good, since output of tanh is >= -1 +julia> maximum(sin, Real[]; init=-1.0) # good, since output of sin is >= -1 -1.0 ``` """ @@ -692,7 +692,7 @@ julia> minimum(length, ["Julion", "Julia", "Jule"]) julia> minimum(length, []; init=typemax(Int64)) 9223372036854775807 -julia> minimum(tanh, Real[]; init=1.0) # good, since output of tanh is <= 1 +julia> minimum(sin, Real[]; init=1.0) # good, since output of sin is <= 1 1.0 ``` """