diff --git a/README.md b/README.md index 0bda19e..ad2cc0f 100644 --- a/README.md +++ b/README.md @@ -210,3 +210,16 @@ julia> arraysT = map(A->of_eltype(Float64, A), arrays) This construct is inferrable (type-stable), so it can be a useful means to "coerce" arrays to a common type. This can sometimes solve type-stability problems without requiring that one copy the data. + +### mappedarrayreduce + +This package provides a "lazy" `mapreduce` operation in the form of the function `mappedarrayreduce`, where the `map` is evaluated as a `MappedArray` and is not materialized. This therefore might be more performant than a standard `mapreduce`. + +Note that `mappedarrayreduce` follows the same signature as `mapreduce`, and does not accept an inverse function. + +An example of its usage: + +```julia +julia> mappedarrayreduce(x -> x^2, +, 1:3) # == 1^2 + 2^2 + 3^2 +14 +``` \ No newline at end of file diff --git a/src/MappedArrays.jl b/src/MappedArrays.jl index 3e7f28f..dc83d44 100644 --- a/src/MappedArrays.jl +++ b/src/MappedArrays.jl @@ -2,7 +2,7 @@ module MappedArrays using Base: @propagate_inbounds -export AbstractMappedArray, MappedArray, ReadonlyMappedArray, mappedarray, of_eltype +export AbstractMappedArray, MappedArray, ReadonlyMappedArray, mappedarray, of_eltype, mappedarrayreduce abstract type AbstractMappedArray{T,N} <: AbstractArray{T,N} end abstract type AbstractMultiMappedArray{T,N} <: AbstractMappedArray{T,N} end @@ -261,4 +261,25 @@ eltypes(A::AbstractArray) = Tuple{eltype(A)} ## Deprecations @deprecate mappedarray(f_finv::Tuple{Any,Any}, args::AbstractArray...) mappedarray(f_finv[1], f_finv[2], args...) + +# mapreduce + +""" + mappedarrayreduce(f, op, A...; kw...) + +Perform a "lazy" `mapreduce` without allocating an intermediate array. This might +be more performant than a standard `mapreduce`. Functionally this is equivalent to +`reduce(op, mappedarray(f, A...); kw...)`. + +# Examples +```jldoctest +julia> mappedarrayreduce(x -> x^2, +, 1:10) # == 1^2 + 2^2 + 3^2 +385 +``` +""" +mappedarrayreduce(f, op, A...; kw...) = reduce(op, mappedarray(f, A...); kw...) +mappedarrayreduce(f, finv::Function, op::Function, A...; kw...) = error( + "mappedarrayreduce does not support an inverse function, "* + "please use the signature mappedarrayreduce(f, op, A...; kw...)") + end # module diff --git a/test/runtests.jl b/test/runtests.jl index 5e593a0..a30ee05 100644 --- a/test/runtests.jl +++ b/test/runtests.jl @@ -177,3 +177,25 @@ end str = String(take!(io)) @test occursin("x1 + x2", str) end + +@testset "mapreduce" begin + for T in [Int, Float64] + x = rand(T, 10); y = similar(x); + + f = x->x^2; op = + + @test mapreduce(f, op, x) == mappedarrayreduce(f, op, x) + @test mapreduce(f, op, x, init = zero(T)) == mappedarrayreduce(f, op, x, init = zero(T)) + @test mapreduce(f, op, x, init = zero(T), dims = 1) == mappedarrayreduce(f, op, x, init = zero(T), dims = 1) + @test mapreduce(f, op, x, init = zero(T), dims = :) == mappedarrayreduce(f, op, x, init = zero(T), dims = :) + + @test_throws Exception mappedarrayreduce(x->x^2, sqrt, op, x) + + if VERSION >= v"1.2" + f = ==; op = + + @test mapreduce(f, op, x, y) == mappedarrayreduce(f, op, x, y) + @test mapreduce(f, op, x, y, init = 0) == mappedarrayreduce(f, op, x, y, init = 0) + @test mapreduce(f, op, x, y, init = 0, dims = 1) == mappedarrayreduce(f, op, x, y, init = 0, dims = 1) + @test mapreduce(f, op, x, y, init = 0, dims = :) == mappedarrayreduce(f, op, x, y, init = 0, dims = :) + end + end +end