Iterator em Zig
O padrão Iterator fornece uma maneira de acessar os elementos de uma coleção sequencialmente sem expor sua representação interna. Em Zig, o protocolo de iteração é simples e convencionado: uma struct com um método next() que retorna ?T (optional do tipo do elemento). Quando retorna null, a iteração acabou.
Quando Usar
- Percorrer coleções customizadas
- Processamento lazy (sob demanda) de sequências
- Composição de transformações sem alocação intermediária
- Gerar sequências infinitas ou muito grandes
Protocolo de Iteração em Zig
const std = @import("std");
// Qualquer struct com next() -> ?T funciona com while
const Contador = struct {
atual: u32,
maximo: u32,
pub fn init(maximo: u32) Contador {
return .{ .atual = 0, .maximo = maximo };
}
pub fn next(self: *Contador) ?u32 {
if (self.atual >= self.maximo) return null;
const valor = self.atual;
self.atual += 1;
return valor;
}
};
pub fn main() void {
var contador = Contador.init(5);
// Protocolo while com capture
while (contador.next()) |valor| {
std.debug.print("{d} ", .{valor}); // 0 1 2 3 4
}
std.debug.print("\n", .{});
}
Iteradores da Biblioteca Padrão
const std = @import("std");
pub fn main() void {
// Split iterator
var split = std.mem.splitSequence(u8, "Zig,é,incrível", ",");
while (split.next()) |parte| {
std.debug.print("'{s}' ", .{parte});
}
std.debug.print("\n", .{});
// Tokenize (ignora delimitadores consecutivos)
var tokens = std.mem.tokenizeAny(u8, " Zig é incrível ", " ");
while (tokens.next()) |token| {
std.debug.print("'{s}' ", .{token});
}
std.debug.print("\n", .{});
// Window iterator
const dados = "ABCDEFGH";
var window = std.mem.window(u8, dados, 3, 1);
while (window.next()) |w| {
std.debug.print("{s} ", .{w}); // ABC BCD CDE DEF EFG FGH
}
std.debug.print("\n", .{});
}
Iterador Customizado com Estado
const std = @import("std");
const Fibonacci = struct {
a: u64 = 0,
b: u64 = 1,
limite: ?u64 = null,
pub fn init() Fibonacci {
return .{};
}
pub fn comLimite(limite: u64) Fibonacci {
return .{ .limite = limite };
}
pub fn next(self: *Fibonacci) ?u64 {
if (self.limite) |lim| {
if (self.a > lim) return null;
}
const valor = self.a;
const proximo = self.a +| self.b; // saturating add
self.a = self.b;
self.b = proximo;
return valor;
}
};
pub fn main() void {
var fib = Fibonacci.comLimite(100);
while (fib.next()) |n| {
std.debug.print("{d} ", .{n}); // 0 1 1 2 3 5 8 13 21 34 55 89
}
std.debug.print("\n", .{});
}
Composição de Iteradores
const std = @import("std");
fn MapIterator(comptime Src: type, comptime Dst: type) type {
return struct {
const Self = @This();
fonte: *Src,
transformar: *const fn (std.meta.Elem(Src)) Dst,
pub fn next(self: *Self) ?Dst {
const val = self.fonte.next() orelse return null;
return self.transformar(val);
}
};
}
fn FilterIterator(comptime Src: type) type {
return struct {
const Self = @This();
const T = std.meta.Elem(Src);
fonte: *Src,
predicado: *const fn (T) bool,
pub fn next(self: *Self) ?T {
while (self.fonte.next()) |val| {
if (self.predicado(val)) return val;
}
return null;
}
};
}
Iterador sobre Diretório
const std = @import("std");
pub fn main() !void {
var dir = try std.fs.cwd().openDir(".", .{ .iterate = true });
defer dir.close();
var iter = dir.iterate();
while (try iter.next()) |entrada| {
const tipo: []const u8 = switch (entrada.kind) {
.file => "ARQ",
.directory => "DIR",
else => "???",
};
std.debug.print("[{s}] {s}\n", .{ tipo, entrada.name });
}
}
Quando Evitar
- Arrays simples — use
fordireto:for (array) |item| { ... } - Quando todo o conteúdo precisa estar em memória de qualquer forma
- Operações que precisam de acesso aleatório (índice)
Veja Também
- Pipeline — Composição de estágios de processamento
- Arrays e Slices — Iteração sobre arrays nativos
- Strings — Iteradores de string e Unicode
- Operações de I/O — Readers como iteradores
- Biblioteca Padrão — Iteradores da std