Generators
A generator is a function that produces a sequence of values one at a time. Values are computed only when a loop asks for the next one, so a generator can describe a huge or even infinite sequence without building it in memory.
Writing a generator
Section titled “Writing a generator”Any function that contains yield is a generator function. Calling it doesn’t run the body; it returns a generator object. A for loop then runs the body up to each yield, takes the yielded value, and resumes from there on the next iteration. The loop ends when the function returns.
fn countdown(n) while n > 0 do yield n n -= 1 endend
for x in countdown(3) do println xend# 3# 2# 1
println typeof(countdown(3)) # generatoryield with no value yields null. return ends the sequence early.
fn first_two() yield "a" yield "b" return yield "never"end
for item, i in first_two() do println "${i}: ${item}" # 0: a, 1: bendAs in any for loop, a second variable receives the index.
Infinite sequences
Section titled “Infinite sequences”Because values are produced on demand, a generator can loop forever. The consumer decides when to stop:
fn fibonacci() a, b = 0, 1 while true do yield a a, b = b, a + b endend
for f in fibonacci() do break when f > 50 print "${f} "endprintln "" # 0 1 1 2 3 5 8 13 21 34Filtering and transforming
Section titled “Filtering and transforming”Generators compose like pipelines. Each stage pulls values from the previous one:
fn naturals() n = 1 while true do yield n n += 1 endend
fn squares_of_odds(source) for n in source do next when n % 2 == 0 yield n * n endend
for s in squares_of_odds(naturals()) do break when s > 100 print "${s} "endprintln "" # 1 9 25 49 81Generator objects
Section titled “Generator objects”A generator object can be stored and iterated later. Each call creates an independent generator with its own position. Once a generator has run to the end it stays exhausted; iterate a fresh one to start over.
fn letters() yield "x" yield "y"end
gen = letters()for l in gen do print lendfor l in gen do print l # nothing: gen is exhaustedendprintln "" # xy
for l in letters() do print lendprintln "" # xyTo collect a generator’s values into a list, push them in a loop:
fn range_step(start, stop, step) i = start while i < stop do yield i i += step endend
values = []for v in range_step(0, 20, 5) do values.push(v)endprintln values # [0, 5, 10, 15]Struct methods and lambdas can be generators too:
struct Countdown fn new(from) @from = from end
fn ticks() n = @from while n > 0 do yield n n -= 1 end endend
for t in Countdown.new(3).ticks() do println tend
squares = do (n) for i in [1 to n] do yield i * i endendfor s in squares(3) do println send