Skip to content

Commit 230dd60

Browse files
tkftimholynalimilan
authored andcommitted
Support init keyword in sum/prod/maximum/minimum (JuliaLang#36188)
Co-authored-by: Tim Holy <[email protected]> Co-authored-by: Milan Bouchet-Valat <[email protected]>
1 parent c32bdf3 commit 230dd60

File tree

6 files changed

+175
-38
lines changed

6 files changed

+175
-38
lines changed

NEWS.md

+1
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,7 @@ Standard library changes
4747
* The function `isapprox(x,y)` now accepts the `norm` keyword argument also for numeric (i.e., non-array) arguments `x` and `y` ([#35883]).
4848
* `view`, `@view`, and `@views` now work on `AbstractString`s, returning a `SubString` when appropriate ([#35879]).
4949
* All `AbstractUnitRange{<:Integer}`s now work with `SubString`, `view`, `@view` and `@views` on strings ([#35879]).
50+
* `sum`, `prod`, `maximum`, and `minimum` now support `init` keyword argument ([#36188], [#35839]).
5051

5152
#### LinearAlgebra
5253
* New method `LinearAlgebra.issuccess(::CholeskyPivoted)` for checking whether pivoted Cholesky factorization was successful ([#36002]).

base/reduce.jl

+117-22
Original file line numberDiff line numberDiff line change
@@ -45,7 +45,7 @@ function mapfoldl_impl(f::F, op::OP, nt, itr) where {F,OP}
4545
end
4646

4747
function foldl_impl(op::OP, nt, itr) where {OP}
48-
v = _foldl_impl(op, get(nt, :init, _InitialValue()), itr)
48+
v = _foldl_impl(op, nt, itr)
4949
v isa _InitialValue && return reduce_empty_iter(op, itr)
5050
return v
5151
end
@@ -157,7 +157,7 @@ Like [`mapreduce`](@ref), but with guaranteed left associativity, as in [`foldl`
157157
If provided, the keyword argument `init` will be used exactly once. In general, it will be
158158
necessary to provide `init` to work with empty collections.
159159
"""
160-
mapfoldl(f, op, itr; kw...) = mapfoldl_impl(f, op, kw.data, itr)
160+
mapfoldl(f, op, itr; init=_InitialValue()) = mapfoldl_impl(f, op, init, itr)
161161

162162
"""
163163
foldl(op, itr; [init])
@@ -200,7 +200,7 @@ Like [`mapreduce`](@ref), but with guaranteed right associativity, as in [`foldr
200200
provided, the keyword argument `init` will be used exactly once. In general, it will be
201201
necessary to provide `init` to work with empty collections.
202202
"""
203-
mapfoldr(f, op, itr; kw...) = mapfoldr_impl(f, op, kw.data, itr)
203+
mapfoldr(f, op, itr; init=_InitialValue()) = mapfoldr_impl(f, op, init, itr)
204204

205205

206206
"""
@@ -462,14 +462,21 @@ reduce(op, a::Number) = a # Do we want this?
462462
## sum
463463

464464
"""
465-
sum(f, itr)
465+
sum(f, itr; [init])
466466
467467
Sum the results of calling function `f` on each element of `itr`.
468468
469469
The return type is `Int` for signed integers of less than system word size, and
470470
`UInt` for unsigned integers of less than system word size. For all other
471471
arguments, a common return type is found to which all arguments are promoted.
472472
473+
The value returned for empty `itr` can be specified by `init`. It must be
474+
the additive identity (i.e. zero) as it is unspecified whether `init` is used
475+
for non-empty collections.
476+
477+
!!! compat "Julia 1.6"
478+
Keyword argument `init` requires Julia 1.6 or later.
479+
473480
# Examples
474481
```jldoctest
475482
julia> sum(abs2, [2; 3; 4])
@@ -491,60 +498,88 @@ In the former case, the integers are widened to system word size and therefore
491498
the result is 128. In the latter case, no such widening happens and integer
492499
overflow results in -128.
493500
"""
494-
sum(f, a) = mapreduce(f, add_sum, a)
501+
sum(f, a; kw...) = mapreduce(f, add_sum, a; kw...)
495502

496503
"""
497-
sum(itr)
504+
sum(itr; [init])
498505
499506
Returns the sum of all elements in a collection.
500507
501508
The return type is `Int` for signed integers of less than system word size, and
502509
`UInt` for unsigned integers of less than system word size. For all other
503510
arguments, a common return type is found to which all arguments are promoted.
504511
512+
The value returned for empty `itr` can be specified by `init`. It must be
513+
the additive identity (i.e. zero) as it is unspecified whether `init` is used
514+
for non-empty collections.
515+
516+
!!! compat "Julia 1.6"
517+
Keyword argument `init` requires Julia 1.6 or later.
518+
505519
# Examples
506520
```jldoctest
507521
julia> sum(1:20)
508522
210
523+
524+
julia> sum(1:20; init = 0.0)
525+
210.0
509526
```
510527
"""
511-
sum(a) = sum(identity, a)
512-
sum(a::AbstractArray{Bool}) = count(a)
528+
sum(a; kw...) = sum(identity, a; kw...)
529+
sum(a::AbstractArray{Bool}; kw...) =
530+
kw.data === NamedTuple() ? count(a) : reduce(add_sum, a; kw...)
513531

514532
## prod
515533
"""
516-
prod(f, itr)
534+
prod(f, itr; [init])
517535
518536
Returns the product of `f` applied to each element of `itr`.
519537
520538
The return type is `Int` for signed integers of less than system word size, and
521539
`UInt` for unsigned integers of less than system word size. For all other
522540
arguments, a common return type is found to which all arguments are promoted.
523541
542+
The value returned for empty `itr` can be specified by `init`. It must be the
543+
multiplicative identity (i.e. one) as it is unspecified whether `init` is used
544+
for non-empty collections.
545+
546+
!!! compat "Julia 1.6"
547+
Keyword argument `init` requires Julia 1.6 or later.
548+
524549
# Examples
525550
```jldoctest
526551
julia> prod(abs2, [2; 3; 4])
527552
576
528553
```
529554
"""
530-
prod(f, a) = mapreduce(f, mul_prod, a)
555+
prod(f, a; kw...) = mapreduce(f, mul_prod, a; kw...)
531556

532557
"""
533-
prod(itr)
558+
prod(itr; [init])
534559
535560
Returns the product of all elements of a collection.
536561
537562
The return type is `Int` for signed integers of less than system word size, and
538563
`UInt` for unsigned integers of less than system word size. For all other
539564
arguments, a common return type is found to which all arguments are promoted.
540565
566+
The value returned for empty `itr` can be specified by `init`. It must be the
567+
multiplicative identity (i.e. one) as it is unspecified whether `init` is used
568+
for non-empty collections.
569+
570+
!!! compat "Julia 1.6"
571+
Keyword argument `init` requires Julia 1.6 or later.
572+
541573
# Examples
542574
```jldoctest
543-
julia> prod(1:20)
544-
2432902008176640000
575+
julia> prod(1:5)
576+
120
577+
578+
julia> prod(1:5; init = 1.0)
579+
120.0
545580
```
546581
"""
547-
prod(a) = mapreduce(identity, mul_prod, a)
582+
prod(a; kw...) = mapreduce(identity, mul_prod, a; kw...)
548583

549584
## maximum & minimum
550585
_fast(::typeof(min),x,y) = min(x,y)
@@ -610,62 +645,122 @@ function mapreduce_impl(f, op::Union{typeof(max), typeof(min)},
610645
end
611646

612647
"""
613-
maximum(f, itr)
648+
maximum(f, itr; [init])
614649
615650
Returns the largest result of calling function `f` on each element of `itr`.
616651
652+
The value returned for empty `itr` can be specified by `init`. It must be
653+
a neutral element for `max` (i.e. which is less than or equal to any
654+
other element) as it is unspecified whether `init` is used
655+
for non-empty collections.
656+
657+
!!! compat "Julia 1.6"
658+
Keyword argument `init` requires Julia 1.6 or later.
659+
617660
# Examples
618661
```jldoctest
619662
julia> maximum(length, ["Julion", "Julia", "Jule"])
620663
6
664+
665+
julia> maximum(length, []; init=-1)
666+
-1
667+
668+
julia> maximum(sin, Real[]; init=-1.0) # good, since output of sin is >= -1
669+
-1.0
621670
```
622671
"""
623-
maximum(f, a) = mapreduce(f, max, a)
672+
maximum(f, a; kw...) = mapreduce(f, max, a; kw...)
624673

625674
"""
626-
minimum(f, itr)
675+
minimum(f, itr; [init])
627676
628677
Returns the smallest result of calling function `f` on each element of `itr`.
629678
679+
The value returned for empty `itr` can be specified by `init`. It must be
680+
a neutral element for `min` (i.e. which is greater than or equal to any
681+
other element) as it is unspecified whether `init` is used
682+
for non-empty collections.
683+
684+
!!! compat "Julia 1.6"
685+
Keyword argument `init` requires Julia 1.6 or later.
686+
630687
# Examples
631688
```jldoctest
632689
julia> minimum(length, ["Julion", "Julia", "Jule"])
633690
4
691+
692+
julia> minimum(length, []; init=typemax(Int64))
693+
9223372036854775807
694+
695+
julia> minimum(sin, Real[]; init=1.0) # good, since output of sin is <= 1
696+
1.0
634697
```
635698
"""
636-
minimum(f, a) = mapreduce(f, min, a)
699+
minimum(f, a; kw...) = mapreduce(f, min, a; kw...)
637700

638701
"""
639-
maximum(itr)
702+
maximum(itr; [init])
640703
641704
Returns the largest element in a collection.
642705
706+
The value returned for empty `itr` can be specified by `init`. It must be
707+
a neutral element for `max` (i.e. which is less than or equal to any
708+
other element) as it is unspecified whether `init` is used
709+
for non-empty collections.
710+
711+
!!! compat "Julia 1.6"
712+
Keyword argument `init` requires Julia 1.6 or later.
713+
643714
# Examples
644715
```jldoctest
645716
julia> maximum(-20.5:10)
646717
9.5
647718
648719
julia> maximum([1,2,3])
649720
3
721+
722+
julia> maximum(())
723+
ERROR: ArgumentError: reducing over an empty collection is not allowed
724+
Stacktrace:
725+
[...]
726+
727+
julia> maximum((); init=-Inf)
728+
-Inf
650729
```
651730
"""
652-
maximum(a) = mapreduce(identity, max, a)
731+
maximum(a; kw...) = mapreduce(identity, max, a; kw...)
653732

654733
"""
655-
minimum(itr)
734+
minimum(itr; [init])
656735
657736
Returns the smallest element in a collection.
658737
738+
The value returned for empty `itr` can be specified by `init`. It must be
739+
a neutral element for `min` (i.e. which is greater than or equal to any
740+
other element) as it is unspecified whether `init` is used
741+
for non-empty collections.
742+
743+
!!! compat "Julia 1.6"
744+
Keyword argument `init` requires Julia 1.6 or later.
745+
659746
# Examples
660747
```jldoctest
661748
julia> minimum(-20.5:10)
662749
-20.5
663750
664751
julia> minimum([1,2,3])
665752
1
753+
754+
julia> minimum([])
755+
ERROR: ArgumentError: reducing over an empty collection is not allowed
756+
Stacktrace:
757+
[...]
758+
759+
julia> minimum([]; init=Inf)
760+
Inf
666761
```
667762
"""
668-
minimum(a) = mapreduce(identity, min, a)
763+
minimum(a; kw...) = mapreduce(identity, min, a; kw...)
669764

670765
## all & any
671766

base/reducedim.jl

+14-14
Original file line numberDiff line numberDiff line change
@@ -307,21 +307,21 @@ julia> mapreduce(isodd, |, a, dims=1)
307307
1 1 1 1
308308
```
309309
"""
310-
mapreduce(f, op, A::AbstractArrayOrBroadcasted; dims=:, kw...) =
311-
_mapreduce_dim(f, op, kw.data, A, dims)
310+
mapreduce(f, op, A::AbstractArrayOrBroadcasted; dims=:, init=_InitialValue()) =
311+
_mapreduce_dim(f, op, init, A, dims)
312312
mapreduce(f, op, A::AbstractArrayOrBroadcasted...; kw...) =
313313
reduce(op, map(f, A...); kw...)
314314

315-
_mapreduce_dim(f, op, nt::NamedTuple{(:init,)}, A::AbstractArrayOrBroadcasted, ::Colon) =
316-
mapfoldl(f, op, A; nt...)
315+
_mapreduce_dim(f, op, nt, A::AbstractArrayOrBroadcasted, ::Colon) =
316+
mapfoldl_impl(f, op, nt, A)
317317

318-
_mapreduce_dim(f, op, ::NamedTuple{()}, A::AbstractArrayOrBroadcasted, ::Colon) =
318+
_mapreduce_dim(f, op, ::_InitialValue, A::AbstractArrayOrBroadcasted, ::Colon) =
319319
_mapreduce(f, op, IndexStyle(A), A)
320320

321-
_mapreduce_dim(f, op, nt::NamedTuple{(:init,)}, A::AbstractArrayOrBroadcasted, dims) =
322-
mapreducedim!(f, op, reducedim_initarray(A, dims, nt.init), A)
321+
_mapreduce_dim(f, op, nt, A::AbstractArrayOrBroadcasted, dims) =
322+
mapreducedim!(f, op, reducedim_initarray(A, dims, nt), A)
323323

324-
_mapreduce_dim(f, op, ::NamedTuple{()}, A::AbstractArrayOrBroadcasted, dims) =
324+
_mapreduce_dim(f, op, ::_InitialValue, A::AbstractArrayOrBroadcasted, dims) =
325325
mapreducedim!(f, op, reducedim_init(f, op, A, dims), A)
326326

327327
"""
@@ -717,12 +717,12 @@ for (fname, _fname, op) in [(:sum, :_sum, :add_sum), (:prod, :_prod,
717717
(:maximum, :_maximum, :max), (:minimum, :_minimum, :min)]
718718
@eval begin
719719
# User-facing methods with keyword arguments
720-
@inline ($fname)(a::AbstractArray; dims=:) = ($_fname)(a, dims)
721-
@inline ($fname)(f, a::AbstractArray; dims=:) = ($_fname)(f, a, dims)
720+
@inline ($fname)(a::AbstractArray; dims=:, kw...) = ($_fname)(a, dims; kw...)
721+
@inline ($fname)(f, a::AbstractArray; dims=:, kw...) = ($_fname)(f, a, dims; kw...)
722722

723723
# Underlying implementations using dispatch
724-
($_fname)(a, ::Colon) = ($_fname)(identity, a, :)
725-
($_fname)(f, a, ::Colon) = mapreduce(f, $op, a)
724+
($_fname)(a, ::Colon; kw...) = ($_fname)(identity, a, :; kw...)
725+
($_fname)(f, a, ::Colon; kw...) = mapreduce(f, $op, a; kw...)
726726
end
727727
end
728728

@@ -743,8 +743,8 @@ for (fname, op) in [(:sum, :add_sum), (:prod, :mul_prod),
743743
mapreducedim!(f, $(op), initarray!(r, $(op), init, A), A)
744744
$(fname!)(r::AbstractArray, A::AbstractArray; init::Bool=true) = $(fname!)(identity, r, A; init=init)
745745

746-
$(_fname)(A, dims) = $(_fname)(identity, A, dims)
747-
$(_fname)(f, A, dims) = mapreduce(f, $(op), A, dims=dims)
746+
$(_fname)(A, dims; kw...) = $(_fname)(identity, A, dims; kw...)
747+
$(_fname)(f, A, dims; kw...) = mapreduce(f, $(op), A; dims=dims, kw...)
748748
end
749749
end
750750

base/reflection.jl

+4-2
Original file line numberDiff line numberDiff line change
@@ -1221,10 +1221,12 @@ See also [`applicable`](@ref).
12211221
julia> hasmethod(length, Tuple{Array})
12221222
true
12231223
1224-
julia> hasmethod(sum, Tuple{Function, Array}, (:dims,))
1224+
julia> f(; oranges=0) = oranges;
1225+
1226+
julia> hasmethod(f, Tuple{}, (:oranges,))
12251227
true
12261228
1227-
julia> hasmethod(sum, Tuple{Function, Array}, (:apples, :bananas))
1229+
julia> hasmethod(f, Tuple{}, (:apples, :bananas))
12281230
false
12291231
12301232
julia> g(; xs...) = 4;

0 commit comments

Comments
 (0)