diff --git a/NEWS.md b/NEWS.md index 7c7a08e3d4f93..3357ca3340030 100644 --- a/NEWS.md +++ b/NEWS.md @@ -713,6 +713,10 @@ Library improvements * Added an optimized method of `vecdot` for taking the Frobenius inner product of sparse matrices. ([#27470]) + * The initial element `v0` in `reduce(op, v0, itr)` has been replaced with an `init` + optional keyword argument, as in `reduce(op, itr; init=v0)`. Similarly for `foldl`, + `foldr`, `mapreduce`, `mapfoldl` and `mapfoldr`. ([#27711]) + Compiler/Runtime improvements ----------------------------- diff --git a/base/Enums.jl b/base/Enums.jl index a6cc1db4772e4..021c8c4bea561 100644 --- a/base/Enums.jl +++ b/base/Enums.jl @@ -20,7 +20,7 @@ function membershiptest(expr, values) if length(values) == hi - lo + 1 :($lo <= $expr <= $hi) elseif length(values) < 20 - foldl((x1,x2)->:($x1 || ($expr == $x2)), :($expr == $(values[1])), values[2:end]) + foldl((x1,x2)->:($x1 || ($expr == $x2)), values[2:end]; init=:($expr == $(values[1]))) else :($expr in $(Set(values))) end diff --git a/base/abstractset.jl b/base/abstractset.jl index 49182e805b46b..bf20a1c1ad00b 100644 --- a/base/abstractset.jl +++ b/base/abstractset.jl @@ -59,7 +59,7 @@ julia> a Set([7, 4, 3, 5, 1]) ``` """ -union!(s::AbstractSet, sets...) = foldl(union!, s, sets) +union!(s::AbstractSet, sets...) = foldl(union!, sets; init=s) max_values(::Type) = typemax(Int) max_values(T::Type{<:Union{Nothing,BitIntegerSmall}}) = 1 << (8*sizeof(T)) @@ -109,7 +109,7 @@ const ∩ = intersect Intersect all passed in sets and overwrite `s` with the result. Maintain order with arrays. """ -intersect!(s::AbstractSet, itrs...) = foldl(intersect!, s, itrs) +intersect!(s::AbstractSet, itrs...) = foldl(intersect!, itrs; init=s) intersect!(s::AbstractSet, s2::AbstractSet) = filter!(_in(s2), s) intersect!(s::AbstractSet, itr) = intersect!(s, union!(emptymutable(s, eltype(itr)), itr)) @@ -147,8 +147,8 @@ julia> a Set([4]) ``` """ -setdiff!(s::AbstractSet, itrs...) = foldl(setdiff!, s, itrs) -setdiff!(s::AbstractSet, itr) = foldl(delete!, s, itr) +setdiff!(s::AbstractSet, itrs...) = foldl(setdiff!, itrs; init=s) +setdiff!(s::AbstractSet, itr) = foldl(delete!, itr; init=s) """ @@ -185,7 +185,7 @@ Construct the symmetric difference of the passed in sets, and overwrite `s` with When `s` is an array, the order is maintained. Note that in this case the multiplicity of elements matters. """ -symdiff!(s::AbstractSet, itrs...) = foldl(symdiff!, s, itrs) +symdiff!(s::AbstractSet, itrs...) = foldl(symdiff!, itrs; init=s) function symdiff!(s::AbstractSet, itr) for x in itr diff --git a/base/array.jl b/base/array.jl index 7655ba1530eb9..4c11a63004c4c 100644 --- a/base/array.jl +++ b/base/array.jl @@ -2364,7 +2364,7 @@ _shrink_filter!(keep) = _unique_filter!(∈, pop!, keep) function _grow!(pred!, v::AbstractVector, itrs) filter!(pred!, v) # uniquify v - foldl(v, itrs) do v, itr + foldl(itrs; init=v) do v, itr mapfilter(pred!, push!, itr, v) end end diff --git a/base/bitset.jl b/base/bitset.jl index 25850add41b64..9e07ac8cffcaa 100644 --- a/base/bitset.jl +++ b/base/bitset.jl @@ -29,7 +29,7 @@ very large integers), use [`Set`](@ref) instead. BitSet(itr) = union!(BitSet(), itr) # Special implementation for BitSet, which lacks a fast `length` method. -union!(s::BitSet, itr) = foldl(push!, s, itr) +union!(s::BitSet, itr) = foldl(push!, itr; init=s) @inline intoffset(s::BitSet) = s.offset << 6 @@ -274,7 +274,7 @@ intersect!(s1::BitSet, s2::BitSet) = _matched_map!(&, s1, s2) setdiff!(s1::BitSet, s2::BitSet) = _matched_map!((p, q) -> p & ~q, s1, s2) -symdiff!(s::BitSet, ns) = foldl(int_symdiff!, s, ns) +symdiff!(s::BitSet, ns) = foldl(int_symdiff!, ns; init=s) function int_symdiff!(s::BitSet, n::Integer) n0 = _check_bitset_bounds(n) @@ -309,7 +309,7 @@ function last(s::BitSet) idx == -1 ? _throw_bitset_notempty_error() : idx + intoffset(s) end -length(s::BitSet) = bitcount(s.bits) # = mapreduce(count_ones, +, 0, s.bits) +length(s::BitSet) = bitcount(s.bits) # = mapreduce(count_ones, +, s.bits; init=0) function show(io::IO, s::BitSet) print(io, "BitSet([") diff --git a/base/compiler/ssair/passes.jl b/base/compiler/ssair/passes.jl index b960715441b01..8f477f13da129 100644 --- a/base/compiler/ssair/passes.jl +++ b/base/compiler/ssair/passes.jl @@ -71,7 +71,7 @@ end function compute_value_for_block(ir::IRCode, domtree::DomTree, allblocks, du, phinodes, fidx, curblock) curblock = find_curblock(domtree, allblocks, curblock) - def = reduce(max, 0, stmt for stmt in du.defs if block_for_inst(ir.cfg, stmt) == curblock) + def = reduce(max, stmt for stmt in du.defs if block_for_inst(ir.cfg, stmt) == curblock; init=0) def == 0 ? phinodes[curblock] : val_for_def_expr(ir, def, fidx) end @@ -680,7 +680,7 @@ function getfield_elim_pass!(ir::IRCode, domtree) # not to include any intermediaries that have dead uses. As a result, missing uses will only ever # show up in the nuses_total count. nleaves = length(defuse.uses) + length(defuse.defs) + length(defuse.ccall_preserve_uses) - nuses_total = compact.used_ssas[idx] + mapreduce(idx->compact.used_ssas[idx], +, 0, intermediaries) - length(intermediaries) + nuses_total = compact.used_ssas[idx] + mapreduce(idx->compact.used_ssas[idx], +, intermediaries; init=0) - length(intermediaries) nleaves == nuses_total || continue # Find the type for this allocation defexpr = ir[SSAValue(idx)] diff --git a/base/compiler/tfuncs.jl b/base/compiler/tfuncs.jl index 13279e27326fe..8f1f1cdfcb9d8 100644 --- a/base/compiler/tfuncs.jl +++ b/base/compiler/tfuncs.jl @@ -616,7 +616,7 @@ function getfield_tfunc(@nospecialize(s00), @nospecialize(name)) return rewrap_unionall(unwrapva(s.types[1]), s00) end # union together types of all fields - return reduce(tmerge, Bottom, map(t -> rewrap_unionall(unwrapva(t), s00), s.types)) + return reduce(tmerge, map(t -> rewrap_unionall(unwrapva(t), s00), s.types); init=Bottom) end fld = name.val if isa(fld,Symbol) @@ -688,8 +688,8 @@ function fieldtype_tfunc(@nospecialize(s0), @nospecialize(name)) if !(Int <: name || Symbol <: name) return Bottom end - return reduce(tmerge, Bottom, - Any[ fieldtype_tfunc(s0, Const(i)) for i = 1:length(ftypes) ]) + return reduce(tmerge, Any[ fieldtype_tfunc(s0, Const(i)) for i = 1:length(ftypes) ]; + init=Bottom) end fld = name.val diff --git a/base/compiler/typeutils.jl b/base/compiler/typeutils.jl index fde23e230836f..44acfa81da6cd 100644 --- a/base/compiler/typeutils.jl +++ b/base/compiler/typeutils.jl @@ -85,7 +85,7 @@ _typename(a::DataType) = Const(a.name) function tuple_tail_elem(@nospecialize(init), ct) # FIXME: this is broken: it violates subtyping relations and creates invalid types with free typevars tmerge_maybe_vararg(@nospecialize(a), @nospecialize(b)) = tmerge(a, tvar_extent(unwrapva(b))) - return Vararg{widenconst(foldl(tmerge_maybe_vararg, init, ct))} + return Vararg{widenconst(foldl(tmerge_maybe_vararg, ct; init=init))} end function countunionsplit(atypes) diff --git a/base/deprecated.jl b/base/deprecated.jl index 8c93747d6090d..b0a1ef7dfed12 100644 --- a/base/deprecated.jl +++ b/base/deprecated.jl @@ -1723,6 +1723,14 @@ end @deprecate_moved eigs "Arpack" @deprecate_moved svds "Arpack" +# PR #27711 +@deprecate reduce(op, v0, itr) reduce(op, itr; init=v0) +@deprecate foldl(op, v0, itr) foldl(op, itr; init=v0) +@deprecate foldr(op, v0, itr) foldr(op, itr; init=v0) +@deprecate mapreduce(f, op, v0, itr) mapreduce(f, op, itr; init=v0) +@deprecate mapfoldl(f, op, v0, itr) mapfoldl(f, op, itr; init=v0) +@deprecate mapfoldr(f, op, v0, itr) mapfoldr(f, op, itr; init=v0) + # END 0.7 deprecations # BEGIN 1.0 deprecations diff --git a/base/gmp.jl b/base/gmp.jl index be9b729cd8f4c..b5bd32631a8c1 100644 --- a/base/gmp.jl +++ b/base/gmp.jl @@ -553,7 +553,7 @@ function gcdx(a::BigInt, b::BigInt) g, s, t end -sum(arr::AbstractArray{BigInt}) = foldl(MPZ.add!, BigInt(0), arr) +sum(arr::AbstractArray{BigInt}) = foldl(MPZ.add!, arr; init=BigInt(0)) # note: a similar implementation for `prod` won't be efficient: # 1) the time complexity of the allocations is negligible compared to the multiplications # 2) assuming arr contains similarly sized BigInts, the multiplications are much more diff --git a/base/intfuncs.jl b/base/intfuncs.jl index 51281eaf834e3..d3ba2356cfb1f 100644 --- a/base/intfuncs.jl +++ b/base/intfuncs.jl @@ -79,7 +79,7 @@ lcm(a::Integer, b::Integer) = lcm(promote(a,b)...) gcd(a::Integer, b::Integer...) = gcd(a, gcd(b...)) lcm(a::Integer, b::Integer...) = lcm(a, lcm(b...)) -lcm(abc::AbstractArray{<:Integer}) = reduce(lcm,one(eltype(abc)),abc) +lcm(abc::AbstractArray{<:Integer}) = reduce(lcm, abc; init=one(eltype(abc))) function gcd(abc::AbstractArray{<:Integer}) a = zero(eltype(abc)) diff --git a/base/reduce.jl b/base/reduce.jl index 989e34c5d95b0..8568562f8d155 100644 --- a/base/reduce.jl +++ b/base/reduce.jl @@ -34,12 +34,13 @@ mul_prod(x::SmallUnsigned,y::SmallUnsigned) = UInt(x) * UInt(y) ## foldl && mapfoldl -@noinline function mapfoldl_impl(f, op, v0, itr, i...) - # Unroll the while loop once; if v0 is known, the call to op may +@noinline function mapfoldl_impl(f, op, nt::NamedTuple{(:init,)}, itr, i...) + init = nt.init + # Unroll the while loop once; if init is known, the call to op may # be evaluated at compile time y = iterate(itr, i...) - y === nothing && return v0 - v = op(v0, f(y[1])) + y === nothing && return init + v = op(init, f(y[1])) while true y = iterate(itr, y[2]) y === nothing && break @@ -48,70 +49,55 @@ mul_prod(x::SmallUnsigned,y::SmallUnsigned) = UInt(x) * UInt(y) return v end -""" - mapfoldl(f, op, v0, itr) - -Like [`mapreduce`](@ref), but with guaranteed left associativity, as in [`foldl`](@ref). -`v0` will be used exactly once. -""" -mapfoldl(f, op, v0, itr) = mapfoldl_impl(f, op, v0, itr) - -""" - mapfoldl(f, op, itr) - -Like `mapfoldl(f, op, v0, itr)`, but using the first element of `itr` to generate `v0`. -Specifically, `mapfoldl(f, op, itr)` produces the same result as -`mapfoldl(f, op, f(first(itr)), drop(itr, 1))`. -In general, this cannot be used with empty collections (see [`reduce(op, itr)`](@ref)). -""" -function mapfoldl(f, op, itr) +function mapfoldl_impl(f, op, nt::NamedTuple{()}, itr) y = iterate(itr) if y === nothing return Base.mapreduce_empty_iter(f, op, itr, IteratorEltype(itr)) end (x, i) = y - v0 = mapreduce_first(f, op, x) - mapfoldl_impl(f, op, v0, itr, i) + init = mapreduce_first(f, op, x) + mapfoldl_impl(f, op, (init=init,), itr, i) end -""" - foldl(op, v0, itr) -Like [`reduce`](@ref), but with guaranteed left associativity. `v0` will be used -exactly once. +""" + mapfoldl(f, op, itr; [init]) -# Examples -```jldoctest -julia> foldl(=>, 0, 1:4) -(((0=>1)=>2)=>3) => 4 -``` +Like [`mapreduce`](@ref), but with guaranteed left associativity, as in [`foldl`](@ref). +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. """ -foldl(op, v0, itr) = mapfoldl(identity, op, v0, itr) +mapfoldl(f, op, itr; kw...) = mapfoldl_impl(f, op, kw.data, itr) """ - foldl(op, itr) + foldl(op, itr; [init]) -Like `foldl(op, v0, itr)`, but using the first element of `itr` as `v0`. In general, this -cannot be used with empty collections (see [`reduce(op, itr)`](@ref)). +Like [`reduce`](@ref), but with guaranteed left associativity. 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. # Examples ```jldoctest julia> foldl(=>, 1:4) ((1=>2)=>3) => 4 + +julia> foldl(=>, 1:4; init=0) +(((0=>1)=>2)=>3) => 4 ``` """ -foldl(op, itr) = mapfoldl(identity, op, itr) +foldl(op, itr; kw...) = mapfoldl(identity, op, itr; kw...) ## foldr & mapfoldr -function mapfoldr_impl(f, op, v0, itr, i::Integer) - # Unroll the while loop once; if v0 is known, the call to op may +function mapfoldr_impl(f, op, nt::NamedTuple{(:init,)}, itr, i::Integer) + init = nt.init + # Unroll the while loop once; if init is known, the call to op may # be evaluated at compile time if isempty(itr) || i == 0 - return v0 + return init else x = itr[i] - v = op(f(x), v0) + v = op(f(x), init) while i > 1 x = itr[i -= 1] v = op(f(x), v) @@ -120,57 +106,40 @@ function mapfoldr_impl(f, op, v0, itr, i::Integer) end end -""" - mapfoldr(f, op, v0, itr) - -Like [`mapreduce`](@ref), but with guaranteed right associativity, as in [`foldr`](@ref). -`v0` will be used exactly once. -""" -mapfoldr(f, op, v0, itr) = mapfoldr_impl(f, op, v0, itr, lastindex(itr)) - -""" - mapfoldr(f, op, itr) - -Like `mapfoldr(f, op, v0, itr)`, but using the first element of `itr` to generate `v0`. -Specifically, `mapfoldr(f, op, itr)` produces the same result as -`mapfoldr(f, op, f(last(itr)), take(itr, length(itr)-1))`. -In general, this cannot be used with empty collections (see [`reduce(op, itr)`](@ref)). -""" -function mapfoldr(f, op, itr) - i = lastindex(itr) +function mapfoldr_impl(f, op, ::NamedTuple{()}, itr, i::Integer) if isempty(itr) return Base.mapreduce_empty_iter(f, op, itr, IteratorEltype(itr)) end - return mapfoldr_impl(f, op, mapreduce_first(f, op, itr[i]), itr, i-1) + return mapfoldr_impl(f, op, (init=mapreduce_first(f, op, itr[i]),), itr, i-1) end """ - foldr(op, v0, itr) + mapfoldr(f, op, itr; [init]) -Like [`reduce`](@ref), but with guaranteed right associativity. `v0` will be used -exactly once. - -# Examples -```jldoctest -julia> foldr(=>, 0, 1:4) -1 => (2=>(3=>(4=>0))) -``` +Like [`mapreduce`](@ref), but with guaranteed right associativity, as in [`foldr`](@ref). 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. """ -foldr(op, v0, itr) = mapfoldr(identity, op, v0, itr) +mapfoldr(f, op, itr; kw...) = mapfoldr_impl(f, op, kw.data, itr, lastindex(itr)) + """ - foldr(op, itr) + foldr(op, itr; [init]) -Like `foldr(op, v0, itr)`, but using the last element of `itr` as `v0`. In general, this -cannot be used with empty collections (see [`reduce(op, itr)`](@ref)). +Like [`reduce`](@ref), but with guaranteed right associativity. 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. # Examples ```jldoctest julia> foldr(=>, 1:4) 1 => (2=>(3=>4)) + +julia> foldr(=>, 1:4; init=0) +1 => (2=>(3=>(4=>0))) ``` """ -foldr(op, itr) = mapfoldr(identity, op, itr) +foldr(op, itr; kw...) = mapfoldr(identity, op, itr; kw...) ## reduce & mapreduce @@ -208,23 +177,17 @@ mapreduce_impl(f, op, A::AbstractArray, ifirst::Integer, ilast::Integer) = mapreduce_impl(f, op, A, ifirst, ilast, pairwise_blocksize(f, op)) """ - mapreduce(f, op, itr) - -Like `mapreduce(f, op, v0, itr)`. In general, this cannot be used with empty collections -(see `reduce(op, itr)`). -""" -mapreduce(f, op, itr) = mapfoldl(f, op, itr) - -""" - mapreduce(f, op, v0, itr) + mapreduce(f, op, itr; [init]) Apply function `f` to each element in `itr`, and then reduce the result using the binary -function `op`. `v0` must be a neutral element for `op` that will be returned for empty -collections. It is unspecified whether `v0` is used for non-empty collections. +function `op`. If provided, `init` must be a neutral element for `op` that will be returne +for empty collections. It is unspecified whether `init` is used for non-empty collections. +In general, it will be necessary to provide `init` to work with empty collections. -[`mapreduce`](@ref) is functionally equivalent to calling `reduce(op, v0, -map(f, itr))`, but will in general execute faster since no intermediate collection needs to -be created. See documentation for [`reduce`](@ref) and [`map`](@ref). +[`mapreduce`](@ref) is functionally equivalent to calling +`reduce(op, map(f, itr); init=init)`, but will in general execute faster since no +intermediate collection needs to be created. See documentation for [`reduce`](@ref) and +[`map`](@ref). # Examples ```jldoctest @@ -237,7 +200,7 @@ implementations may reuse the return value of `f` for elements that appear multi `itr`. Use [`mapfoldl`](@ref) or [`mapfoldr`](@ref) instead for guaranteed left or right associativity and invocation of `f` for every value. """ -mapreduce(f, op, v0, itr) = mapfoldl(f, op, v0, itr) +mapreduce(f, op, itr; kw...) = mapfoldl(f, op, itr; kw...) # Note: sum_seq usually uses four or more accumulators after partial # unrolling, so each accumulator gets at most 256 numbers @@ -361,11 +324,15 @@ mapreduce(f, op, a::Number) = mapreduce_first(f, op, a) _mapreduce(f, op, ::IndexCartesian, A::AbstractArray) = mapfoldl(f, op, A) """ - reduce(op, v0, itr) + reduce(op, itr; [init]) -Reduce the given collection `itr` with the given binary operator `op`. `v0` must be a -neutral element for `op` that will be returned for empty collections. It is unspecified -whether `v0` is used for non-empty collections. +Reduce the given collection `itr` with the given binary operator `op`. If provided, the +initial value `init` must be a neutral element for `op` that will be returned for empty +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 +neutral element of `op`. Reductions for certain commonly-used operators may have special implementations, and should be used instead: `maximum(itr)`, `minimum(itr)`, `sum(itr)`, `prod(itr)`, @@ -380,29 +347,18 @@ Some operations accumulate error. Parallelism will be easier if the reduction ca executed in groups. Future versions of Julia might change the algorithm. Note that the elements are not reordered if you use an ordered collection. -# Examples -```jldoctest -julia> reduce(*, 1, [2; 3; 4]) -24 -``` -""" -reduce(op, v0, itr) = mapreduce(identity, op, v0, itr) - -""" - reduce(op, itr) - -Like `reduce(op, v0, itr)`. This cannot be used with empty collections, except for some -special cases (e.g. when `op` is one of `+`, `*`, `max`, `min`, `&`, `|`) when Julia can -determine the neutral element of `op`. - # Examples ```jldoctest julia> reduce(*, [2; 3; 4]) 24 + +julia> reduce(*, [2; 3; 4]; init=-1) +-24 ``` """ -reduce(op, itr) = mapreduce(identity, op, itr) -reduce(op, a::Number) = a +reduce(op, itr; kw...) = mapreduce(identity, op, itr; kw...) + +reduce(op, a::Number) = a # Do we want this? ###### Specific reduction functions ###### diff --git a/base/reducedim.jl b/base/reducedim.jl index c8d5e720b6f89..a06d6a33b6d84 100644 --- a/base/reducedim.jl +++ b/base/reducedim.jl @@ -76,8 +76,8 @@ for (Op, initval) in ((:(typeof(&)), true), (:(typeof(|)), false)) end # reducedim_initarray is called by -reducedim_initarray(A::AbstractArray, region, v0, ::Type{R}) where {R} = fill!(similar(A,R,reduced_indices(A,region)), v0) -reducedim_initarray(A::AbstractArray, region, v0::T) where {T} = reducedim_initarray(A, region, v0, T) +reducedim_initarray(A::AbstractArray, region, init, ::Type{R}) where {R} = fill!(similar(A,R,reduced_indices(A,region)), init) +reducedim_initarray(A::AbstractArray, region, init::T) where {T} = reducedim_initarray(A, region, init, T) function reducedim_initarray0(A::AbstractArray{T}, region, f, ops) where T ri = reduced_indices0(A, region) @@ -248,9 +248,9 @@ reducedim!(op, R::AbstractArray{RT}, A::AbstractArray) where {RT} = mapreducedim!(identity, op, R, A) """ - mapreduce(f, op[, v0], A::AbstractArray; dims) + mapreduce(f, op, A::AbstractArray; dims=:, [init]) -Evaluates to the same as `reduce(op, f(v0), map(f, A); dims)`, but is generally +Evaluates to the same as `reduce(op, map(f, A); dims=dims, init=init)`, but is generally faster because the intermediate array is avoided. # Examples @@ -271,30 +271,28 @@ julia> mapreduce(isodd, |, true, a, dims=1) true true true true ``` """ -mapreduce(f, op, v0, A::AbstractArray; dims=:) = _mapreduce_dim(f, op, v0, A, dims) +mapreduce(f, op, A::AbstractArray; dims=:, kw...) = _mapreduce_dim(f, op, kw.data, A, dims) -mapreduce(f, op, A::AbstractArray; dims=:) = _mapreduce_dim(f, op, A, dims) +_mapreduce_dim(f, op, nt::NamedTuple{(:init,)}, A::AbstractArray, ::Colon) = mapfoldl(f, op, A; nt...) -_mapreduce_dim(f, op, v0, A::AbstractArray, ::Colon) = mapfoldl(f, op, v0, A) +_mapreduce_dim(f, op, ::NamedTuple{()}, A::AbstractArray, ::Colon) = _mapreduce(f, op, IndexStyle(A), A) -_mapreduce_dim(f, op, A::AbstractArray, ::Colon) = _mapreduce(f, op, IndexStyle(A), A) +_mapreduce_dim(f, op, nt::NamedTuple{(:init,)}, A::AbstractArray, dims) = + mapreducedim!(f, op, reducedim_initarray(A, dims, nt.init), A) -_mapreduce_dim(f, op, v0, A::AbstractArray, dims) = - mapreducedim!(f, op, reducedim_initarray(A, dims, v0), A) - -_mapreduce_dim(f, op, A::AbstractArray, dims) = +_mapreduce_dim(f, op, ::NamedTuple{()}, A::AbstractArray, dims) = mapreducedim!(f, op, reducedim_init(f, op, A, dims), A) """ - reduce(f[, v0], A; dims) + reduce(f, A; dims=:, [init]) Reduce 2-argument function `f` along dimensions of `A`. `dims` is a vector specifying the -dimensions to reduce, and `v0` is the initial value to use in the reductions. For `+`, `*`, -`max` and `min` the `v0` argument is optional. +dimensions to reduce, and the keyword argument `init` is the initial value to use in the +reductions. For `+`, `*`, `max` and `min` the `init` argument is optional. The associativity of the reduction is implementation-dependent; if you need a particular -associativity, e.g. left-to-right, you should write your own loop. See documentation for -[`reduce`](@ref). +associativity, e.g. left-to-right, you should write your own loop or consider using +[`foldl`](@ref) or [`foldr`](@ref). See documentation for [`reduce`](@ref). # Examples ```jldoctest @@ -317,15 +315,7 @@ julia> reduce(max, a, dims=1) 4 8 12 16 ``` """ -reduce(op, v0, A::AbstractArray; dims=:) = _reduce_dim(op, v0, A, dims) - -_reduce_dim(op, v0, A, dims) = mapreduce(identity, op, v0, A, dims=dims) -_reduce_dim(op, v0, A, ::Colon) = mapreduce(identity, op, v0, A) - -reduce(op, A::AbstractArray; dims=:) = _reduce_dim(op, A, dims) - -_reduce_dim(op, A, dims) = mapreduce(identity, op, A, dims=dims) -_reduce_dim(op, A, ::Colon) = mapreduce(identity, op, A) +reduce(op, A::AbstractArray; kw...) = mapreduce(identity, op, A; kw...) ##### Specific reduction functions ##### """ diff --git a/base/strings/unicode.jl b/base/strings/unicode.jl index 39eb6b015ff03..6f6636d7ef985 100644 --- a/base/strings/unicode.jl +++ b/base/strings/unicode.jl @@ -234,7 +234,7 @@ julia> textwidth("March") 5 ``` """ -textwidth(s::AbstractString) = mapreduce(textwidth, +, 0, s) +textwidth(s::AbstractString) = mapreduce(textwidth, +, s; init=0) lowercase(c::T) where {T<:AbstractChar} = isascii(c) ? ('A' <= c <= 'Z' ? c + 0x20 : c) : T(ccall(:utf8proc_tolower, UInt32, (UInt32,), c)) diff --git a/doc/src/base/collections.md b/doc/src/base/collections.md index 4a64aeaa82a2b..471b74ca4de42 100644 --- a/doc/src/base/collections.md +++ b/doc/src/base/collections.md @@ -82,11 +82,8 @@ Base.indexin Base.unique Base.unique! Base.allunique -Base.reduce(::Any, ::Any, ::Any) Base.reduce(::Any, ::Any) -Base.foldl(::Any, ::Any, ::Any) Base.foldl(::Any, ::Any) -Base.foldr(::Any, ::Any, ::Any) Base.foldr(::Any, ::Any) Base.maximum Base.maximum! @@ -116,11 +113,8 @@ Base.all(::Any, ::Any) Base.foreach Base.map Base.map! -Base.mapreduce(::Any, ::Any, ::Any, ::Any) Base.mapreduce(::Any, ::Any, ::Any) -Base.mapfoldl(::Any, ::Any, ::Any, ::Any) Base.mapfoldl(::Any, ::Any, ::Any) -Base.mapfoldr(::Any, ::Any, ::Any, ::Any) Base.mapfoldr(::Any, ::Any, ::Any) Base.first Base.last diff --git a/stdlib/LinearAlgebra/src/factorization.jl b/stdlib/LinearAlgebra/src/factorization.jl index 0e022481ed99c..c41a55f65647c 100644 --- a/stdlib/LinearAlgebra/src/factorization.jl +++ b/stdlib/LinearAlgebra/src/factorization.jl @@ -49,7 +49,7 @@ convert(::Type{T}, f::Factorization) where {T<:AbstractArray} = T(f) Factorization{T}(F::Factorization{T}) where {T} = F inv(F::Factorization{T}) where {T} = (n = size(F, 1); ldiv!(F, Matrix{T}(I, n, n))) -Base.hash(F::Factorization, h::UInt) = mapreduce(f -> hash(getfield(F, f)), hash, h, 1:nfields(F)) +Base.hash(F::Factorization, h::UInt) = mapreduce(f -> hash(getfield(F, f)), hash, 1:nfields(F); init=h) Base.:(==)( F::T, G::T) where {T<:Factorization} = all(f -> getfield(F, f) == getfield(G, f), 1:nfields(F)) Base.isequal(F::T, G::T) where {T<:Factorization} = all(f -> isequal(getfield(F, f), getfield(G, f)), 1:nfields(F))::Bool diff --git a/stdlib/LinearAlgebra/src/generic.jl b/stdlib/LinearAlgebra/src/generic.jl index bed2605cb75a2..d0dc58607597a 100644 --- a/stdlib/LinearAlgebra/src/generic.jl +++ b/stdlib/LinearAlgebra/src/generic.jl @@ -1340,7 +1340,7 @@ Complex{Float64} promote_leaf_eltypes(x::Union{AbstractArray{T},Tuple{Vararg{T}}}) where {T<:Number} = T promote_leaf_eltypes(x::Union{AbstractArray{T},Tuple{Vararg{T}}}) where {T<:NumberArray} = eltype(T) promote_leaf_eltypes(x::T) where {T} = T -promote_leaf_eltypes(x::Union{AbstractArray,Tuple}) = mapreduce(promote_leaf_eltypes, promote_type, Bool, x) +promote_leaf_eltypes(x::Union{AbstractArray,Tuple}) = mapreduce(promote_leaf_eltypes, promote_type, x; init=Bool) # isapprox: approximate equality of arrays [like isapprox(Number,Number)] # Supports nested arrays; e.g., for `a = [[1,2, [3,4]], 5.0, [6im, [7.0, 8.0]]]` diff --git a/stdlib/Markdown/src/render/plain.jl b/stdlib/Markdown/src/render/plain.jl index 9ea3515e7d96d..3bc0bbca7e693 100644 --- a/stdlib/Markdown/src/render/plain.jl +++ b/stdlib/Markdown/src/render/plain.jl @@ -22,7 +22,7 @@ end function plain(io::IO, code::Code) # If the code includes a fenced block this will break parsing, # so it must be enclosed by a longer ````-sequence. - n = mapreduce(m -> length(m.match), max, 2, eachmatch(r"^`+"m, code.code)) + 1 + n = mapreduce(m -> length(m.match), max, eachmatch(r"^`+"m, code.code); init=2) + 1 println(io, "`" ^ n, code.language) println(io, code.code) println(io, "`" ^ n) diff --git a/stdlib/OldPkg/src/reqs.jl b/stdlib/OldPkg/src/reqs.jl index f9d70789cfb31..a2404bf314814 100644 --- a/stdlib/OldPkg/src/reqs.jl +++ b/stdlib/OldPkg/src/reqs.jl @@ -135,7 +135,7 @@ function add(lines::Vector{Line}, pkg::AbstractString, versions::VersionSet=Vers return true end length(v) == 1 && v[1] == intersect(v[1],versions) && return copy(lines) - versions = reduce(intersect, versions, v) + versions = reduce(intersect, v; init=versions) push!(filtered, Requirement(pkg, versions)) end diff --git a/stdlib/Random/src/DSFMT.jl b/stdlib/Random/src/DSFMT.jl index 87525904b02c2..398b0b536606f 100644 --- a/stdlib/Random/src/DSFMT.jl +++ b/stdlib/Random/src/DSFMT.jl @@ -136,7 +136,7 @@ function sqrmod!(f::GF2X, m::GF2X)::GF2X x2i = GF2X(1) GF2X[copy(mulxmod!(mulxmod!(x2i, m, d+1), m, d+1)) for i=1:d] end - foldl(GF2X(0), filter(i->coeff(f, i), 0:degree(f))) do g, i + foldl(filter(i->coeff(f, i), 0:degree(f)); init=GF2X(0)) do g, i i <= d÷2 ? # optimization for "simple" squares setcoeff!(g, 2i) : xor!(g, sqrs[i]) @@ -146,7 +146,7 @@ end # compute X^e mod m function powxmod(e::BigInt, m::GF2X)::GF2X e < 0 && throw(DomainError("e must be >= 0")) - foldl(GF2X(1), Base.ndigits0z(e, 2)-1:-1:0) do f, i + foldl(Base.ndigits0z(e, 2)-1:-1:0; init=GF2X(1)) do f, i MPZ.tstbit(e, i) ? mulxmod!(sqrmod!(f, m), m) : sqrmod!(f, m) diff --git a/stdlib/Random/src/RNGs.jl b/stdlib/Random/src/RNGs.jl index c5b06f9dfc4d7..3e54b56082e9a 100644 --- a/stdlib/Random/src/RNGs.jl +++ b/stdlib/Random/src/RNGs.jl @@ -160,7 +160,7 @@ copy(src::MersenneTwister) = r1.idxF == r2.idxF && r1.idxI == r2.idxI hash(r::MersenneTwister, h::UInt) = - foldr(hash, h, (r.seed, r.state, r.vals, r.ints, r.idxF, r.idxI)) + foldr(hash, (r.seed, r.state, r.vals, r.ints, r.idxF, r.idxI); init=h) function fillcache_zeros!(r::MersenneTwister) # the use of this function is not strictly necessary, but it makes diff --git a/test/reduce.jl b/test/reduce.jl index 67d750df3aa8a..7329ad6088110 100644 --- a/test/reduce.jl +++ b/test/reduce.jl @@ -6,36 +6,36 @@ using Random @test foldl(+, Int64[]) === Int64(0) # In reference to issues #7465/#20144 (PR #20160) @test foldl(+, Int16[]) === Int16(0) # In reference to issues #21536 @test foldl(-, 1:5) == -13 -@test foldl(-, 10, 1:5) == -5 +@test foldl(-, 1:5; init=10) == -5 @test Base.mapfoldl(abs2, -, 2:5) == -46 -@test Base.mapfoldl(abs2, -, 10, 2:5) == -44 +@test Base.mapfoldl(abs2, -, 2:5; init=10) == -44 @test Base.mapfoldl(abs2, /, 2:5) ≈ 1/900 -@test Base.mapfoldl(abs2, /, 10, 2:5) ≈ 1/1440 +@test Base.mapfoldl(abs2, /, 2:5; init=10) ≈ 1/1440 -@test Base.mapfoldl((x)-> x ⊻ true, &, true, [true false true false false]) == false @test Base.mapfoldl((x)-> x ⊻ true, &, [true false true false false]) == false +@test Base.mapfoldl((x)-> x ⊻ true, &, [true false true false false]; init=true) == false @test Base.mapfoldl((x)-> x ⊻ true, |, [true false true false false]) == true -@test Base.mapfoldl((x)-> x ⊻ true, |, false, [true false true false false]) == true +@test Base.mapfoldl((x)-> x ⊻ true, |, [true false true false false]; init=false) == true @test foldr(+, Int64[]) === Int64(0) # In reference to issue #20144 (PR #20160) @test foldr(+, Int16[]) === Int16(0) # In reference to issues #21536 @test foldr(-, 1:5) == 3 -@test foldr(-, 10, 1:5) == -7 +@test foldr(-, 1:5; init=10) == -7 @test foldr(+, [1]) == 1 # Issue #21493 @test Base.mapfoldr(abs2, -, 2:5) == -14 -@test Base.mapfoldr(abs2, -, 10, 2:5) == -4 +@test Base.mapfoldr(abs2, -, 2:5; init=10) == -4 # reduce @test reduce(+, Int64[]) === Int64(0) # In reference to issue #20144 (PR #20160) @test reduce(+, Int16[]) === Int16(0) # In reference to issues #21536 @test reduce((x,y)->"($x+$y)", 9:11) == "((9+10)+11)" @test reduce(max, [8 6 7 5 3 0 9]) == 9 -@test reduce(+, 1000, 1:5) == (1000 + 1 + 2 + 3 + 4 + 5) -@test reduce(+,1) == 1 +@test reduce(+, 1:5; init=1000) == (1000 + 1 + 2 + 3 + 4 + 5) +@test reduce(+, 1) == 1 # mapreduce @test mapreduce(-, +, [-10 -9 -3]) == ((10 + 9) + 3) @@ -118,12 +118,12 @@ sum2(itr) = invoke(sum, Tuple{Any}, itr) plus(x,y) = x + y sum3(A) = reduce(plus, A) sum4(itr) = invoke(reduce, Tuple{Function, Any}, plus, itr) -sum5(A) = reduce(plus, 0, A) -sum6(itr) = invoke(reduce, Tuple{Function, Int, Any}, plus, 0, itr) +sum5(A) = reduce(plus, A; init=0) +sum6(itr) = invoke(Core.kwfunc(reduce), Tuple{NamedTuple{(:init,), Tuple{Int}}, typeof(reduce), Function, Any}, (init=0,), reduce, plus, itr) sum7(A) = mapreduce(x->x, plus, A) sum8(itr) = invoke(mapreduce, Tuple{Function, Function, Any}, x->x, plus, itr) -sum9(A) = mapreduce(x->x, plus, 0, A) -sum10(itr) = invoke(mapreduce, Tuple{Function, Function, Int, Any}, x->x,plus,0,itr) +sum9(A) = mapreduce(x->x, plus, A; init=0) +sum10(itr) = invoke(Core.kwfunc(mapreduce), Tuple{NamedTuple{(:init,),Tuple{Int}}, typeof(mapreduce), Function, Function, Any}, (init=0,), mapreduce, x->x, plus, itr) for f in (sum2, sum5, sum6, sum9, sum10) @test sum(z) == f(z) @test sum(Int[]) == f(Int[]) == 0 diff --git a/test/reducedim.jl b/test/reducedim.jl index 2c9f622cb4905..88da2c60547af 100644 --- a/test/reducedim.jl +++ b/test/reducedim.jl @@ -111,15 +111,15 @@ end @test sum(Union{Float32, Float64}[1.0], dims=1) == [1.0] @test prod(Union{Float32, Float64}[1.0], dims=1) == [1.0] -@test reduce((a,b) -> a|b, false, [true false; false false], dims=1) == [true false] -let R = reduce((a,b) -> a+b, 0.0, [1 2; 3 4], dims=2) +@test reduce((a,b) -> a|b, [true false; false false], dims=1, init=false) == [true false] +let R = reduce((a,b) -> a+b, [1 2; 3 4], dims=2, init=0.0) @test eltype(R) == Float64 @test R ≈ [3,7] end -@test reduce((a,b) -> a+b, 0, [1 2; 3 4], dims=1) == [4 6] +@test reduce((a,b) -> a+b, [1 2; 3 4], dims=1, init=0) == [4 6] # inferred return types -@test typeof(@inferred(reduce(+, 0.0, ones(3,3,3), dims=1))) == Array{Float64, 3} +@test typeof(@inferred(reduce(+, ones(3,3,3), dims=1, init=0.0))) == Array{Float64, 3} @testset "empty cases" begin A = Matrix{Int}(undef, 0,1)