Skip to content
This repository was archived by the owner on Sep 1, 2020. It is now read-only.
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
22 changes: 22 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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))), ...`.
Expand Down
49 changes: 49 additions & 0 deletions src/Iterators.jl
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,8 @@ export
subsets,
iterate,
takenth,
peekiter,
peek,
@itr

# iteratorsize is new in 0.5, declare it here for older versions. However,
Expand Down Expand Up @@ -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
Expand Down
28 changes: 28 additions & 0 deletions test/runtests.jl
Original file line number Diff line number Diff line change
Expand Up @@ -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
## ====

Expand Down