This is because UnitRange
(such as 1:5
) and Vector
(such as collect(1:5)
) do not define their “iterator state” in the same way.
The documentation for the iterators interface explains that
for i in iter
# body
end
gets translated into
next = iterate(iter)
while next !== nothing
(i, state) = next
# body
next = iterate(iter, state)
end
In other words, the first call to iterate
is performed with only one argument and returns an (element, state)
tuple. The state
from each call is passed as the second argument in the next call to iterate
. But the iterate
method for each type can decide exactly how the state is represented.
Let’s look at what happens in both cases you mention:
function test_iter(collection)
@show next = iterate(collection)
while next !== nothing
(i, state) = next
@show next = iterate(collection, state)
end
end
julia> test_iter(1:5)
next = iterate(collection) = (1, 1)
next = iterate(collection, state) = (2, 2)
next = iterate(collection, state) = (3, 3)
next = iterate(collection, state) = (4, 4)
next = iterate(collection, state) = (5, 5)
next = iterate(collection, state) = nothing
julia> test_iter(collect(1:5))
next = iterate(collection) = (1, 2)
next = iterate(collection, state) = (2, 3)
next = iterate(collection, state) = (3, 4)
next = iterate(collection, state) = (4, 5)
next = iterate(collection, state) = (5, 6)
next = iterate(collection, state) = nothing
In both cases, the state
s are consecutive indices. But states in the Vector
case are shifted by 1 w.r.t. states in the UnitRange
case. This explains both your observations: the state associated to the first element (i.e. the implicit state that is omitted in the first call to iterate
) in a UnitRange
is 0, whereas the state associated to the first element in a vector is 1.
Does this make sense?
EDIT: it looks like I posted almost simultaneously to mbauman
. Keeping this answer anyway in case it would add something to his excellent answer…