diff --git a/NEWS.md b/NEWS.md index 58444878ba494..e7cf33f0b1c46 100644 --- a/NEWS.md +++ b/NEWS.md @@ -61,6 +61,7 @@ Standard library changes * `nextfloat(::BigFloat)` and `prevfloat(::BigFloat)` now returns a value with the same precision as their argument, which means that (in particular) `nextfloat(prevfloat(x)) == x` whereas previously this could result in a completely different value with a different precision ([#31310]) +* `mapreduce` now accept multiple iterators, similar to `map` ([#31532]). #### LinearAlgebra @@ -108,6 +109,7 @@ Deprecated or removed [#21598]: https://github.com/JuliaLang/julia/issues/21598 +[#22922]: https://github.com/JuliaLang/julia/issues/22922 [#24980]: https://github.com/JuliaLang/julia/issues/24980 [#28850]: https://github.com/JuliaLang/julia/issues/28850 [#29777]: https://github.com/JuliaLang/julia/issues/29777 @@ -117,16 +119,27 @@ Deprecated or removed [#30200]: https://github.com/JuliaLang/julia/issues/30200 [#30298]: https://github.com/JuliaLang/julia/issues/30298 [#30323]: https://github.com/JuliaLang/julia/issues/30323 -[#30349]: https://github.com/JuliaLang/julia/issues/30349 [#30372]: https://github.com/JuliaLang/julia/issues/30372 [#30382]: https://github.com/JuliaLang/julia/issues/30382 [#30577]: https://github.com/JuliaLang/julia/issues/30577 [#30583]: https://github.com/JuliaLang/julia/issues/30583 [#30584]: https://github.com/JuliaLang/julia/issues/30584 [#30593]: https://github.com/JuliaLang/julia/issues/30593 +[#30604]: https://github.com/JuliaLang/julia/issues/30604 [#30618]: https://github.com/JuliaLang/julia/issues/30618 [#30670]: https://github.com/JuliaLang/julia/issues/30670 [#30712]: https://github.com/JuliaLang/julia/issues/30712 [#30724]: https://github.com/JuliaLang/julia/issues/30724 [#30915]: https://github.com/JuliaLang/julia/issues/30915 [#30919]: https://github.com/JuliaLang/julia/issues/30919 +[#30938]: https://github.com/JuliaLang/julia/issues/30938 +[#31008]: https://github.com/JuliaLang/julia/issues/31008 +[#31009]: https://github.com/JuliaLang/julia/issues/31009 +[#31125]: https://github.com/JuliaLang/julia/issues/31125 +[#31211]: https://github.com/JuliaLang/julia/issues/31211 +[#31230]: https://github.com/JuliaLang/julia/issues/31230 +[#31235]: https://github.com/JuliaLang/julia/issues/31235 +[#31310]: https://github.com/JuliaLang/julia/issues/31310 +[#31441]: https://github.com/JuliaLang/julia/issues/31441 +[#31451]: https://github.com/JuliaLang/julia/issues/31451 +[#31532]: https://github.com/JuliaLang/julia/issues/31532 diff --git a/base/reduce.jl b/base/reduce.jl index 4149b4c69e4e0..0eeb6ced0be54 100644 --- a/base/reduce.jl +++ b/base/reduce.jl @@ -179,9 +179,9 @@ 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; [init]) + mapreduce(f, op, itrs...; [init]) -Apply function `f` to each element in `itr`, and then reduce the result using the binary +Apply function `f` to each element(s) in `itrs`, and then reduce the result using the binary function `op`. If provided, `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. In general, it will be necessary to provide `init` to work with empty collections. @@ -191,6 +191,9 @@ In general, it will be necessary to provide `init` to work with empty collection intermediate collection needs to be created. See documentation for [`reduce`](@ref) and [`map`](@ref). +!!! compat "Julia 1.2" + `mapreduce` with multiple iterators requires Julia 1.2 or later. + # Examples ```jldoctest julia> mapreduce(x->x^2, +, [1:3;]) # == 1 + 4 + 9 @@ -203,6 +206,7 @@ implementations may reuse the return value of `f` for elements that appear multi guaranteed left or right associativity and invocation of `f` for every value. """ mapreduce(f, op, itr; kw...) = mapfoldl(f, op, itr; kw...) +mapreduce(f, op, itrs...; kw...) = reduce(op, Generator(f, itrs...); kw...) # Note: sum_seq usually uses four or more accumulators after partial # unrolling, so each accumulator gets at most 256 numbers diff --git a/base/reducedim.jl b/base/reducedim.jl index 7b005aeff4f10..44a72a9a25b9d 100644 --- a/base/reducedim.jl +++ b/base/reducedim.jl @@ -278,11 +278,14 @@ reducedim!(op, R::AbstractArray{RT}, A::AbstractArray) where {RT} = mapreducedim!(identity, op, R, A) """ - mapreduce(f, op, A::AbstractArray; dims=:, [init]) + mapreduce(f, op, A::AbstractArray...; dims=:, [init]) Evaluates to the same as `reduce(op, map(f, A); dims=dims, init=init)`, but is generally faster because the intermediate array is avoided. +!!! compat "Julia 1.2" + `mapreduce` with multiple iterators requires Julia 1.2 or later. + # Examples ```jldoctest julia> a = reshape(Vector(1:16), (4,4)) @@ -302,6 +305,7 @@ julia> mapreduce(isodd, |, a, dims=1) ``` """ mapreduce(f, op, A::AbstractArray; dims=:, kw...) = _mapreduce_dim(f, op, kw.data, A, dims) +mapreduce(f, op, A::AbstractArray...; kw...) = reduce(op, map(f, A...); kw...) _mapreduce_dim(f, op, nt::NamedTuple{(:init,)}, A::AbstractArray, ::Colon) = mapfoldl(f, op, A; nt...) diff --git a/test/reduce.jl b/test/reduce.jl index 190bbe60aedfa..cc4ce66d2d7d4 100644 --- a/test/reduce.jl +++ b/test/reduce.jl @@ -43,6 +43,27 @@ using .Main.OffsetArrays @test mapreduce(-, +, [-10 -9 -3]) == ((10 + 9) + 3) @test mapreduce((x)->x[1:3], (x,y)->"($x+$y)", ["abcd", "efgh", "01234"]) == "((abc+efg)+012)" +# mapreduce with multiple iterators +@test mapreduce(*, +, (i for i in 2:3), (i for i in 4:5)) == 23 +@test mapreduce(*, +, (i for i in 2:3), (i for i in 4:5); init = 2) == 25 +@test mapreduce(*, (x,y)->"($x+$y)", ["a", "b", "c"], ["d", "e", "f"]) == "((ad+be)+cf)" +@test mapreduce(*, (x,y)->"($x+$y)", ["a", "b", "c"], ["d", "e", "f"]; init = "gh") == + "(((gh+ad)+be)+cf)" + +@test mapreduce(*, +, [2, 3], [4, 5]) == 23 +@test mapreduce(*, +, [2, 3], [4, 5]; init = 2) == 25 +@test mapreduce(*, +, [2, 3], [4, 5]; dims = 1) == [23] +@test mapreduce(*, +, [2, 3], [4, 5]; dims = 1, init = 2) == [25] +@test mapreduce(*, +, [2, 3], [4, 5]; dims = 2) == [8, 15] +@test mapreduce(*, +, [2, 3], [4, 5]; dims = 2, init = 2) == [10, 17] + +@test mapreduce(*, +, [2 3; 4 5], [6 7; 8 9]) == 110 +@test mapreduce(*, +, [2 3; 4 5], [6 7; 8 9]; init = 2) == 112 +@test mapreduce(*, +, [2 3; 4 5], [6 7; 8 9]; dims = 1) == [44 66] +@test mapreduce(*, +, [2 3; 4 5], [6 7; 8 9]; dims = 1, init = 2) == [46 68] +@test mapreduce(*, +, [2 3; 4 5], [6 7; 8 9]; dims = 2) == reshape([33, 77], :, 1) +@test mapreduce(*, +, [2 3; 4 5], [6 7; 8 9]; dims = 2, init = 2) == reshape([35, 79], :, 1) + # mapreduce() for 1- 2- and n-sized blocks (PR #19325) @test mapreduce(-, +, [-10]) == 10 @test mapreduce(abs2, +, [-9, -3]) == 81 + 9