Como Ordenar Arrays e Slices em Zig

Introdução

Ordenar dados é uma das operações mais comuns em programação. Zig oferece funções de ordenação eficientes na biblioteca padrão através do módulo std.mem, permitindo ordenar qualquer tipo de dado com funções de comparação customizadas.

Nesta receita, você aprenderá a ordenar arrays de números, strings e structs.

Pré-requisitos

Ordenar Array de Números

A forma mais simples de ordenar:

const std = @import("std");

pub fn main() !void {
    var numeros = [_]i32{ 42, 17, 8, 95, 3, 61, 28, 54, 12, 76 };

    std.debug.print("Original:  ", .{});
    for (numeros) |n| std.debug.print("{d} ", .{n});
    std.debug.print("\n", .{});

    // Ordenar em ordem crescente
    std.mem.sort(i32, &numeros, {}, std.sort.asc(i32));

    std.debug.print("Crescente: ", .{});
    for (numeros) |n| std.debug.print("{d} ", .{n});
    std.debug.print("\n", .{});

    // Ordenar em ordem decrescente
    std.mem.sort(i32, &numeros, {}, std.sort.desc(i32));

    std.debug.print("Decresc.:  ", .{});
    for (numeros) |n| std.debug.print("{d} ", .{n});
    std.debug.print("\n", .{});
}

Saída esperada

Original:  42 17 8 95 3 61 28 54 12 76
Crescente: 3 8 12 17 28 42 54 61 76 95
Decresc.:  95 76 61 54 42 28 17 12 8 3

Ordenar com Função de Comparação Customizada

Para lógica de ordenação personalizada:

const std = @import("std");

pub fn main() !void {
    // Ordenar por valor absoluto
    var numeros = [_]i32{ -5, 3, -1, 4, -2, 0, 7, -6 };

    std.mem.sort(i32, &numeros, {}, struct {
        fn compare(_: void, a: i32, b: i32) bool {
            const abs_a = if (a < 0) -a else a;
            const abs_b = if (b < 0) -b else b;
            return abs_a < abs_b;
        }
    }.compare);

    std.debug.print("Por valor absoluto: ", .{});
    for (numeros) |n| std.debug.print("{d} ", .{n});
    std.debug.print("\n", .{});

    // Ordenar pares antes de ímpares
    var nums2 = [_]i32{ 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 };

    std.mem.sort(i32, &nums2, {}, struct {
        fn compare(_: void, a: i32, b: i32) bool {
            const a_par = @mod(a, 2) == 0;
            const b_par = @mod(b, 2) == 0;
            if (a_par and !b_par) return true;
            if (!a_par and b_par) return false;
            return a < b;
        }
    }.compare);

    std.debug.print("Pares primeiro:     ", .{});
    for (nums2) |n| std.debug.print("{d} ", .{n});
    std.debug.print("\n", .{});
}

Ordenar Strings

Ordenar um array de strings lexicograficamente:

const std = @import("std");

pub fn main() !void {
    var frutas = [_][]const u8{
        "uva",
        "banana",
        "abacaxi",
        "manga",
        "cereja",
        "laranja",
    };

    std.mem.sort([]const u8, &frutas, {}, struct {
        fn compare(_: void, a: []const u8, b: []const u8) bool {
            return std.mem.order(u8, a, b) == .lt;
        }
    }.compare);

    std.debug.print("Frutas ordenadas:\n", .{});
    for (frutas, 1..) |fruta, i| {
        std.debug.print("  {d}. {s}\n", .{ i, fruta });
    }
}

Ordenar Structs

Ordenar uma lista de structs por campo:

const std = @import("std");

const Aluno = struct {
    nome: []const u8,
    nota: f64,
    idade: u32,
};

pub fn main() !void {
    var alunos = [_]Aluno{
        .{ .nome = "Maria", .nota = 9.5, .idade = 22 },
        .{ .nome = "João", .nota = 7.8, .idade = 20 },
        .{ .nome = "Ana", .nota = 9.5, .idade = 21 },
        .{ .nome = "Pedro", .nota = 8.2, .idade = 23 },
        .{ .nome = "Lucas", .nota = 6.9, .idade = 19 },
    };

    // Ordenar por nota (decrescente)
    std.mem.sort(Aluno, &alunos, {}, struct {
        fn compare(_: void, a: Aluno, b: Aluno) bool {
            return a.nota > b.nota;
        }
    }.compare);

    std.debug.print("Ranking por nota:\n", .{});
    for (alunos, 1..) |aluno, i| {
        std.debug.print("  {d}. {s} - nota {d:.1}\n", .{ i, aluno.nome, aluno.nota });
    }

    // Ordenar por nome
    std.mem.sort(Aluno, &alunos, {}, struct {
        fn compare(_: void, a: Aluno, b: Aluno) bool {
            return std.mem.order(u8, a.nome, b.nome) == .lt;
        }
    }.compare);

    std.debug.print("\nPor nome:\n", .{});
    for (alunos) |aluno| {
        std.debug.print("  {s} (nota {d:.1}, {d} anos)\n", .{ aluno.nome, aluno.nota, aluno.idade });
    }
}

Verificar se Array Está Ordenado

const std = @import("std");

fn estaOrdenado(comptime T: type, items: []const T) bool {
    if (items.len <= 1) return true;
    for (items[0 .. items.len - 1], items[1..]) |a, b| {
        if (a > b) return false;
    }
    return true;
}

pub fn main() !void {
    const ord = [_]i32{ 1, 2, 3, 4, 5 };
    const desord = [_]i32{ 3, 1, 4, 1, 5 };

    std.debug.print("[1,2,3,4,5] ordenado? {}\n", .{estaOrdenado(i32, &ord)});
    std.debug.print("[3,1,4,1,5] ordenado? {}\n", .{estaOrdenado(i32, &desord)});
}

Ordenar ArrayList

const std = @import("std");

pub fn main() !void {
    var gpa = std.heap.GeneralPurposeAllocator(.{}){};
    defer _ = gpa.deinit();
    const allocator = gpa.allocator();

    var lista = std.ArrayList(f64).init(allocator);
    defer lista.deinit();

    try lista.appendSlice(&[_]f64{ 3.14, 1.41, 2.71, 0.57, 1.73 });

    // Ordenar os items do ArrayList
    std.mem.sort(f64, lista.items, {}, std.sort.asc(f64));

    std.debug.print("Ordenado: ", .{});
    for (lista.items) |v| {
        std.debug.print("{d:.2} ", .{v});
    }
    std.debug.print("\n", .{});
}

Dicas e Boas Práticas

  1. A ordenação é estável: std.mem.sort preserva a ordem relativa de elementos iguais.

  2. Use std.sort.asc/desc: Para tipos primitivos, essas funções prontas são mais limpas.

  3. Contexto via parâmetro: O terceiro parâmetro de sort passa contexto para a função de comparação. Use {} quando não precisa de contexto.

  4. Slices são referências: A ordenação modifica o array/slice original in-place.

Receitas Relacionadas

Tutoriais Relacionados

Continue aprendendo Zig

Explore mais tutoriais e artigos em português para dominar a linguagem Zig.