Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add rightif(predicate, focus) #32

Merged
merged 2 commits into from
Jul 12, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion src/DataTools.jl
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
module DataTools

export averaging, inc1, modifying, oncol
export averaging, inc1, modifying, oncol, rightif

using InitialValues: InitialValues
using Setfield: @lens, Lens, PropertyLens, modify, set
Expand Down
70 changes: 70 additions & 0 deletions src/reductions.jl
Original file line number Diff line number Diff line change
Expand Up @@ -69,3 +69,73 @@ InitialValues.@def_monoid merge_state
Transducers.Completing(::typeof(merge_state)) = merge_state # TODO: remove this

const averaging = reducingfunction(Map(singleton_average), merge_state)

"""
rightif(predicate, [focus = identity]) -> op::Function

Return a binary function that keeps the first argument unless
`predicate` evaluates to `true`.

This is equivalent to

```julia
(l, r) -> predicate(focus(l), focus(r)) ? r : l
```

# Examples
```jldoctest
julia> using DataTools, Transducers

julia> table = 1:100 |> Map(x -> (k = gcd(x, 42), v = x));

julia> table |> Take(5) |> collect # preview
5-element Array{NamedTuple{(:k, :v),Tuple{Int64,Int64}},1}:
(k = 1, v = 1)
(k = 2, v = 2)
(k = 3, v = 3)
(k = 2, v = 4)
(k = 1, v = 5)

julia> foldl(rightif(<), Map(x -> x.k), table) # maximum
42

julia> foldl(rightif(>), Map(x -> x.k), table) # minimum
1

julia> foldl(rightif(<, x -> x.k), table) # first maximum
(k = 42, v = 42)

julia> foldl(rightif(<=, x -> x.k), table) # last maximum
(k = 42, v = 84)

julia> foldl(rightif(>, x -> x.k), table) # first minimum
(k = 1, v = 1)

julia> foldl(rightif(>=, x -> x.k), table) # last minimum
(k = 1, v = 97)

julia> table |> Scan(rightif(<, x -> x.k)) |> Take(5) |> collect
5-element Array{NamedTuple{(:k, :v),Tuple{Int64,Int64}},1}:
(k = 1, v = 1)
(k = 2, v = 2)
(k = 3, v = 3)
(k = 3, v = 3)
(k = 3, v = 3)
```
"""
rightif(predicate, focus = identity) = RightIf(predicate, focus)

struct RightIf{P,F} <: _Function
predicate::P
focus::F
end

RightIf(predicate::P, ::Type{F}) where {P,F} = RightIf{P,Type{F}}(predicate, F)

@inline (f::RightIf)(l, r) = f.predicate(f.focus(l), f.focus(r)) ? r : l

const InitRightIf{P,F} = InitialValues.GenericInitialValue{RightIf{P,F}}
(::RightIf)(::InitRightIf, x) = x
(::RightIf)(x, ::InitRightIf) = x
(::RightIf)(x::InitRightIf, ::InitRightIf) = x
InitialValues.hasinitialvalue(::Type{<:RightIf}) = true
9 changes: 9 additions & 0 deletions src/utils.jl
Original file line number Diff line number Diff line change
Expand Up @@ -14,3 +14,12 @@ end
const GenericInitializer = Union{typeof(Transducers.Init),Transducers.InitOf}

const InitializerFor{OP} = Union{GenericInitializer,InitialValues.GenericInitialValue{OP}}

# Just like `Function` but for defining some common methods.
abstract type _Function <: Function end

# Avoid `Function` fallbacks:
@nospecialize
Base.show(io::IO, ::MIME"text/plain", f::_Function) = show(io, f)
Base.print(io::IO, f::_Function) = show(io, f)
@specialize
17 changes: 17 additions & 0 deletions test/test_rightif.jl
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
module TestRightIf

using DataTools
using Test
using Transducers: Map, Take

reduce_bs1(args...; kw...) = reduce(args...; basesize = 1, kw...)

@testset for fold in [foldl, reduce_bs1, reduce]
foldl = nothing
table = 43:100 |> Map(x -> (k = gcd(x, 42), v = x))
@test fold(rightif(<), Map(x -> x.k), table) == 42
@test fold(rightif(>), Map(x -> x.k), table) == 1
@test fold(rightif(<, x -> x.k), table) == (k = 42, v = 84)
end

end # module