Anonymous iterators?

i was reading in the Discourse Slack when someone asked about some syntax equivalent to the yield keyword in python. in Julia, the syntax ´for i in iter´ is lowered to iterate(iter::IterType) and iterate(iter::IterType,state), from the docs:

for i in iter   # or  "for i = iter"
    # body
end

is translated into:

next = iterate(iter)
while next !== nothing
    (i, state) = next
    # body
    next = iterate(iter, state)
end

so the interface requires the existence of a type IterType and the definition of iterate over that type.
The yield keyword does something weird that probably involves internal state, but is very convenient to do, and only requires the definition of a function. compare this (from Stack Overflow):

def squares(n):
    mylist = range(n)
   for i in mylist:
        yield i*i

to this (from Julia Docs):

struct Squares
       count::Int
end
Base.iterate(S::Squares, state=1) = state > S.count ? nothing : (state*state, state+1)

so, in the context of Julia, how can an anonymous iterator can be done?

The stop condition in the python code is hitting other code that isn’t yield, whereas the stop condition of julia code is returning nothing. so an anonymous function has to have this in mind.

One idea, as all functions are of an specific type, is create a macro that registers the function as an iterator, but i lack the skills to do that right now :yum:

How about this:

squares(n) = Channel{Int}() do c
    for i in 1:n
        push!(c, i^2)
    end
end

and then

julia> gen = squares(5)
Channel{Int64}(sz_max:0,sz_curr:1)

julia> collect(gen)
5-element Array{Int64,1}:
  1
  4
  9
 16
 25

Julia has the same Iterator constructor that python, so this is possible too:

squares  = n->  (i^2 for i in 1:n)
5 Likes

See also https://github.com/BenLauwens/ResumableFunctions.jl
for a way to define iterators with a @yield style

2 Likes