Generadores y Corrutinas
En esta página
Un generador se declara con fn* y usa yield para producir un valor
y suspender la ejecución. Llamar al handle devuelto avanza hasta el siguiente
yield; cuando el cuerpo termina, devuelve nil. Como la ejecución es
perezosa, las secuencias infinitas son posibles:
Contador finito, Fibonacci infinito, range_step parametrizado y generador de cadenas.
// Feature: Generators — functions that produce values on demand
// Syntax: `fn* name(...)` + `yield <value>` in the body.
// When to use: infinite sequences, lazy evaluation, streams,
// pipelines where we don't want to materialize everything in memory.
//
// Each call to the generator returns the next `yield`. When the
// function ends, it returns `nil`.
// Finite counter.
fn* counter(limit: int) {
var i = 0
while i < limit {
yield i
i += 1
}
}
let gen = counter(5)
print(gen()) // 0
print(gen()) // 1
print(gen()) // 2
print(gen()) // 3
print(gen()) // 4
print(gen()) // nil (exhausted)
// INFINITE sequence — possible because it's lazy.
fn* fibonacci() {
var a = 0
var b = 1
loop {
yield a
let temp = a + b
a = b
b = temp
}
}
let fib = fibonacci()
for i in 0..10 {
print("fib({i}) = {fib()}")
}
// Parameterized generator.
fn* range_step(start: int, stop: int, step: int) {
var i = start
while i < stop {
yield i
i += step
}
}
print("Evens 0-10:")
let evens = range_step(0, 10, 2)
var v = evens()
while v != nil {
print(" {v}")
v = evens()
}
// String generator — any type works.
fn* statuses() {
yield "loading"
yield "processing"
yield "done"
}
let st = statuses()
var s = st()
while s != nil {
print("status: {s}")
s = st()
}
async fn es azúcar sintáctico para una corrutina basada en el mismo
mecanismo. El handle se avanza de la misma manera que un generador, lo que
hace que la cooperación entre tareas sea explícita — tú controlas cuándo cada
"tarea" cede el procesador:
produce_numbers, productor/consumidor e intercalación de dos tareas cooperativas.
// Feature: async fns / coroutines (built on generators)
// Syntax: `async fn name()` produces a coroutine; uses `yield`
// to suspend and hand control back.
// When to use: cooperative tasks, step-wise simulation of I/O,
// lightweight state machines.
//
// In Zolo, `async fn` is sugar for a coroutine — you advance it
// by calling the returned handle, just like a generator.
async fn produce_numbers() {
yield 10
yield 20
yield 30
}
let producer = produce_numbers()
print(producer()) // 10
print(producer()) // 20
print(producer()) // 30
// `fn*` also works for stream-style asynchronous sources.
fn* async_data() {
yield "loading..."
yield "processing..."
yield "done!"
}
let status = async_data()
var s = status()
while s != nil {
print("Status: {s}")
s = status()
}
// Producer/consumer: the consumer "pulls" items.
fn* producer_items(items: [str]) {
for item in items {
yield item
}
}
let gen = producer_items(["apple", "banana", "cherry"])
var item = gen()
while item != nil {
print("Consumed: {item}")
item = gen()
}
// Multiple "tasks" cooperating: we advance them in turn.
fn* task_a() {
yield "a1"
yield "a2"
yield "a3"
}
fn* task_b() {
yield "b1"
yield "b2"
}
let a = task_a()
let b = task_b()
print(a()) // a1
print(b()) // b1
print(a()) // a2
print(b()) // b2
print(a()) // a3
print(b()) // nil
Desafío
Escribe un generador fn* naturals() que produzca 1, 2, 3, … infinitamente.
Usa while v != nil para imprimir solo los 10 primeros.
Consulta también