From 1895fe4ded6289c91ac14254cc72481431c09c62 Mon Sep 17 00:00:00 2001 From: Andrei Zhabinski Date: Fri, 10 Jun 2016 02:11:45 +0300 Subject: [PATCH] peekiter --- README.md | 22 ++++++++++++++++++++++ src/Iterators.jl | 49 ++++++++++++++++++++++++++++++++++++++++++++++++ test/runtests.jl | 28 +++++++++++++++++++++++++++ 3 files changed, 99 insertions(+) diff --git a/README.md b/README.md index 058adb1..1375210 100644 --- a/README.md +++ b/README.md @@ -222,6 +222,28 @@ Install this package with `Pkg.add("Iterators")` i = [2,3] ``` +- **peekiter**(xs) + + Add possibility to peek head element of an iterator without updating the state. + + Example: + ```julia + it = peekiter(["face", "foo", "bar", "book", "baz", "zzz"]) + s = start(it) + @show peek(it, s) + @show peek(it, s) + x, s = next(it, s) + @show x + @show peek(it, s) + ``` + + ``` + peek(it,s) = Nullable("face") + peek(it,s) = Nullable("face") # no change + x = "face" + peek(it,s) = Nullable("foo") + ``` + - **iterate**(f, x) Iterate over successive applications of `f`, as in `f(x), f(f(x)), f(f(f(x))), ...`. diff --git a/src/Iterators.jl b/src/Iterators.jl index cc907aa..a18fc9f 100644 --- a/src/Iterators.jl +++ b/src/Iterators.jl @@ -17,6 +17,8 @@ export subsets, iterate, takenth, + peekiter, + peek, @itr # iteratorsize is new in 0.5, declare it here for older versions. However, @@ -714,6 +716,53 @@ start(it::Iterate) = it.seed next(it::Iterate, state) = (state, it.f(state)) @compat done(it::Iterate, state) = (state==Union{}) +# peekiter(iter): possibility to peek the head of an iterator + +immutable PeekIter{I} + it::I +end + +peekiter(itr) = PeekIter(itr) + +eltype{I}(::Type{PeekIter{I}}) = eltype(I) +iteratorsize{I}(::Type{PeekIter{I}}) = iteratorsize(I) +iteratoreltype{I}(::Type{PeekIter{I}}) = iteratoreltype(I) +length(f::PeekIter) = length(f.it) +size(f::PeekIter) = size(f.it) + +function start{I}(f::PeekIter{I}) + s = start(f.it) + if done(f.it, s) + val = Nullable{eltype(I)}() + else + el, s = next(f.it, s) + val = Nullable{eltype(I)}(el) + end + return s, val +end + +function next(f::PeekIter, state) + s, val = state + # done() should prevent condition `isnull(val) && done(state)` + !isnull(val) && done(f.it, s) && return get(val), (s, Nullable{typeof(val)}()) + el, s = next(f.it, s) + return get(val), (s, Nullable(el), done(f.it, s)) +end + +@inline function done(f::PeekIter, state) + s, val = state + return done(f.it, s) && isnull(val) +end + +peek{I}(f::PeekIter{I}, state) = done(f, state) ? Nullable{eltype(I)}() : state[2] + + +start{T}(r::PeekIter{UnitRange{T}}) = start(r.it) +next{T}(r::PeekIter{UnitRange{T}}, i) = next(r.it, i) +done{T}(r::PeekIter{UnitRange{T}}, i) = done(r.it, i) +peek{T}(r::PeekIter{UnitRange{T}}, i) = done(r.it, i) ? Nullable{T}() : Nullable{T}(next(r.it, i)[1]) + + using Base.Meta ## @itr macro for auto-inlining in for loops diff --git a/test/runtests.jl b/test/runtests.jl index 6df1b09..e8090c7 100644 --- a/test/runtests.jl +++ b/test/runtests.jl @@ -224,6 +224,34 @@ test_groupby( @test collect(takenth(10:20, 1)) == collect(10:20) +# peekiter +# -------- +result = Int[] +for x in peekiter(1:10) push!(result, x) end +@test result == collect(1:10) + +result = Int[] +for x in peekiter([]) push!(result, x) end +@test result == [] + +result = Int[] +for x in peekiter(1:10) push!(result, x) end +@test result == collect(1:10) + +it = peekiter([:a, :b, :c]) +s = start(it) +@test get(peek(it, s)) == :a + +it = peekiter([]) +s = start(it) +@test isnull(peek(it, s)) + +it = peekiter(1:10) +s = start(it) +x, s = next(it, s) +@test get(peek(it, s)) == 2 + + ## @itr ## ====