Generators e Sequências Infinitas
Nesta página
Um generator é declarado com fn*. Cada chamada à instância retorna o próximo
yield; quando a função termina, retorna nil. Como a execução é suspensa entre
yields, generators podem ser infinitos sem consumir memória:
Counter finito, Fibonacci infinito com loop/yield, e range parametrizado com step — todos consumidos manualmente.
// Feature: Generators (`fn*` + `yield`)
// Syntax: `fn* name(args) { yield value }`. Each call to the instance
// returns the next `yield`; once finished, returns `nil`.
// When to use: infinite sequences, custom lazy values, sources that do
// not fit in memory, simple state machines.
// Finite counter.
fn* counter(limit: int) {
var i = 0
while i < limit {
yield i
i += 1
}
}
let gen = counter(3)
print(gen()) // 0
print(gen()) // 1
print(gen()) // 2
print(gen()) // nil
// Infinite generator — possible because it is 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..7 {
print("fib({i}) = {fib()}")
}
// expected:
// fib(0) = 0
// fib(1) = 1
// fib(2) = 1
// fib(3) = 2
// fib(4) = 3
// fib(5) = 5
// fib(6) = 8
// Generator parameterized with a step.
fn* step_range(start: int, stop: int, step: int) {
var i = start
while i < stop {
yield i
i += step
}
}
let evens = step_range(0, 10, 2)
var v = evens()
while v != nil {
print(" even {v}")
v = evens()
}
O Zolo também suporta ranges infinitos literais (0..) e operadores adicionais
como scan, windows, sum e Iter::from_fn. A regra de ouro é sempre
acoplar um take(n) antes de qualquer consumidor terminal:
Quadrados de pares, soma com .sum(), Fibonacci via Iter::from_fn, janelas deslizantes, scan, zip de infinitos e Iter::repeat_val.
use std::Iter
// Lazy infinite iterators — examples
//
// Key rule: infinite ranges (0..) must always be paired with
// a terminator like take() before collect() or each().
// 1. First 5 squares of even numbers starting from 0
let squares = 0..
|> .map(|x| x * x)
|> .filter(|x| x % 2 == 0)
|> .take(5)
|> .collect()
print(squares)
// [0, 4, 16, 36, 64]
// 2. Sum of first 10 natural numbers (0..9)
let total = 0..
|> .take(10)
|> .sum()
print(total)
// 45
// 3. Fibonacci sequence via Iter.from_fn
fn fib_iter() {
var a = 0
var b = 1
return Iter::from_fn(|| {
let val = a
let next = a + b
a = b
b = next
return val
})
}
let fibs = fib_iter()
|> .take(10)
|> .collect()
print(fibs)
// [0, 1, 1, 2, 3, 5, 8, 13, 21, 34]
// 4. Sliding windows of size 3 over first 6 naturals
let windows = 0..
|> .take(6)
|> .windows(3)
|> .collect()
print(windows)
// [[0, 1, 2], [1, 2, 3], [2, 3, 4], [3, 4, 5]]
// 5. Scan (running sum)
let running = 1..
|> .take(5)
|> .scan(0, |acc, x| acc + x)
|> .collect()
print(running)
// [1, 3, 6, 10, 15]
// 6. Zip two infinite ranges
let pairs = Iter::zip(0.., 1..)
|> .take(4)
|> .collect()
print(pairs)
// [[0, 1], [1, 2], [2, 3], [3, 4]]
// 7. Infinite repeat
let ones = Iter::repeat_val(1)
|> .take(5)
|> .sum()
print(ones)
// 5
// 8. for loop with infinite range (use break to exit)
for i in 0.. {
if i >= 5 { break }
print(i)
}
// 0, 1, 2, 3, 4
Pipelines longos que combinam pipe, tap e funções nomeadas traduzem transformações complexas em código legível passo a passo:
Pipeline de strings com tap; pipeline numérico data-first com map_arr/filter_arr/sum_arr; inspeção passo a passo com variáveis.
// Feature: Long pipelines — full composition
// Syntax: combines `|>`, `&.`, and utility functions.
// When to use: small ETL, collection transformations, normalization,
// any case where the pipeline tells the story of the data flowing.
fn shout(s: str) -> str {
return s.to_upper()
}
fn add_bang(s: str) -> str {
return s + "!"
}
fn pad_dashes(s: str) -> str {
return "-- " + s + " --"
}
fn log_str(s: str) -> str {
print(" [step] {s}")
return s
}
// String pipeline with tap between steps — the classic shape.
let banner = "hello"
|> shout() &. log_str()
// HELLO
|> add_bang() &. log_str()
// HELLO!
|> pad_dashes()
// -- HELLO! --
print(banner)
// expected:
// [step] HELLO
// [step] HELLO!
// -- HELLO! --
// Long numeric pipeline: 1..10 -> *3 -> evens only -> sum.
fn triple(x: int) -> int {
return x * 3
}
fn is_even(x: int) -> bool {
return x % 2 == 0
}
fn map_arr(arr: [int], f: fn(int) -> int) -> [int] {
var out: [int] = []
for x in arr { out.push(f(x)) }
return out
}
fn filter_arr(arr: [int], pred: fn(int) -> bool) -> [int] {
var out: [int] = []
for x in arr {
if pred(x) { out.push(x) }
}
return out
}
fn sum_arr(arr: [int]) -> int {
var total = 0
for x in arr { total += x }
return total
}
// Data-first pipeline without tap (chaining tap on arrays in sequence
// has a runtime gotcha — break out the intermediate step into a
// variable when you want to inspect arrays).
let result = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
|> map_arr(triple)
|> filter_arr(is_even)
|> sum_arr()
print("total = {result}")
// expected: total = 90
// To inspect steps in an array pipeline, prefer variables and direct
// `print(arr)` calls (interpolating an array inside a string only
// shows the handle).
let tripled = map_arr([1, 2, 3, 4, 5], triple)
print(" [tripled]")
print(tripled)
let only_even = filter_arr(tripled, is_even)
print(" [even]")
print(only_even)
let total = sum_arr(only_even)
print(" [total] {total}")
// expected:
// [tripled]
// [3, 6, 9, 12, 15]
// [even]
// [6, 12]
// [total] 18
Desafio
Escreva um generator fn* powers(base: int) que produza potências crescentes de
base (1, base, base², base³, …) e use-o para imprimir as primeiras 5 potências
de 2.
Veja também