Saltar al contenido

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.

10-generators.zolo
Playground
// 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.

11-async-coroutines.zolo
Playground
// 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.

Buscar en Zolo

9 resultados

enespt-br