-
Notifications
You must be signed in to change notification settings - Fork 29
Description
Is there a way to slice/index arbitrary iterator in Julia? Can you add a function ("slice") that can do that? It's like Python's itertools.islice and it does
silce(finite_iterator, indices) == collect(finite_iterator)[indices]
but it also works with infinite iterator and indices, provided that indices are strictly increasing (otherwise you need to cache the whole history). I actually coded up a quick implementation:
struct Slice{I, S}
iter::I
slice::S
end
const slice = Slice
mutable struct SliceState
index::Int
iter_state
slice_state
iter_value
end
Base.start(it::Slice) =
SliceState(0, start(it.iter), start(it.slice), nothing)
function Base.done(it::Slice, state)
done(it.slice, state.slice_state) && return true
local x
i = state.index
n, ss = next(it.slice, state.slice_state)
state.slice_state = ss
si = state.iter_state
@assert i < n
while i < n
if done(it.iter, si)
return true
end
x, si = next(it.iter, si)
i += 1
end
state.iter_state = si
state.iter_value = x
state.index = i
return false
end
Base.next(it::Slice, state) = (state.iter_value, state)
Base.iteratorsize(::Type{<: Slice}) = Base.SizeUnknown()
using Base.Test
# @show collect(slice(1:100, 1:2:5))
@test collect(slice(1:100, 1:2:5)) == collect(1:2:5)
@test collect(slice(1:5, 1:2:10)) == collect(1:2:5)
@test collect(slice(1:10, [1, 3, 7, 11])) == [1, 3, 7]Side notes: Besides obvious lack of documentation and good error message (rather than @assert), the most smelly part of the code is the mutable SliceState and the calls to the next of the underlying iterators. But it seems that this is the usual way to solve this kind of problem for Julia <= 0.6. I guess it's going to be easier with the new iteration protocol for Julia 1.0.
Can IterTools.jl and/or Base already do it? Am I missing something? If not, do you think it is a good API to add in IterTools.jl? I can make a PR with a bit more refined code if you like.