JuliaCollections / IterTools.jl

Common functional iterator patterns
Other
152 stars 28 forks source link

A proposal for `takeuntil` #78

Open xukai92 opened 4 years ago

xukai92 commented 4 years ago

The current takewhile is very convient to use. But for cases in which I also want to take the last value which fails the condition, it cannot do what I want. Hence I'm proposing to add a function called takeuntil, which baiscally do something similar but also include the value which first makes the function false. This is the code snippet for what I propose.

struct TakeUntil{I}
    cond::Function
    xs::I
end

"""
    takeuntil(cond, xs)
An iterator that yields values from the iterator `xs` as long as the
predicate `cond` is true. Unlike `takewhile`, it also take the last 
value for which the predicate `cond` is false.
'''jldoctest
julia> collect(takeuntil(x-> x^2 < 10, 1:100))
3-element Array{Int64,1}:
 1
 2
 3
 4
'''
"""
takeuntil(cond, xs) = TakeUntil(cond, xs)

function Base.iterate(it::TakeUntil, state=(false, nothing))
    is_cond, state_xs = state
    is_cond && return nothing
    (val, state_xs) = 
        @ifsomething (state_xs === nothing ? iterate(it.xs) : iterate(it.xs, state_xs))
    val, (it.cond(val), state_xs)
end

Base.IteratorSize(it::TakeUntil) = Base.SizeUnknown()
Base.eltype(::Type{TakeUntil{I}}) where {I} = eltype(I)
IteratorEltype(::Type{TakeUntil{I}}) where {I} = IteratorEltype(I)

If this looks good, I can fire a PR for this.