← Volver al listado de tecnologías

Funciones en Zig

Por: Artiko
zigfuncionesparametrosclosures

Funciones

Funciones Básicas

const std = @import("std");

// Función simple
fn saludar() void {
    std.debug.print("¡Hola!\n", .{});
}

// Con parámetros y retorno
fn sumar(a: i32, b: i32) i32 {
    return a + b;
}

// Múltiples parámetros del mismo tipo
fn maximo(a: i32, b: i32) i32 {
    return if (a > b) a else b;
}

pub fn main() void {
    saludar();
    const resultado = sumar(10, 20);
    std.debug.print("Suma: {d}\n", .{resultado});
    std.debug.print("Máximo: {d}\n", .{maximo(5, 8)});
}

Funciones Públicas y Privadas

// Pública - accesible desde otros módulos
pub fn funcionPublica() void {}

// Privada - solo accesible en este archivo
fn funcionPrivada() void {}

// Export para C ABI
export fn funcionC() callconv(.C) void {}

Parámetros por Valor y Referencia

const std = @import("std");

// Por valor (copia)
fn duplicar(n: i32) i32 {
    return n * 2;
}

// Por referencia constante (solo lectura)
fn sumarArray(arr: []const i32) i32 {
    var suma: i32 = 0;
    for (arr) |n| suma += n;
    return suma;
}

// Por referencia mutable
fn incrementarTodos(arr: []i32) void {
    for (arr) |*n| {
        n.* += 1;
    }
}

// Puntero mutable
fn intercambiar(a: *i32, b: *i32) void {
    const temp = a.*;
    a.* = b.*;
    b.* = temp;
}

pub fn main() void {
    var numeros = [_]i32{ 1, 2, 3 };
    incrementarTodos(&numeros);
    std.debug.print("Suma: {d}\n", .{sumarArray(&numeros)}); // 9

    var x: i32 = 5;
    var y: i32 = 10;
    intercambiar(&x, &y);
    std.debug.print("x={d}, y={d}\n", .{ x, y }); // x=10, y=5
}

Retornos Múltiples con Structs

const std = @import("std");

const DivisionResultado = struct {
    cociente: i32,
    resto: i32,
};

fn dividirConResto(dividendo: i32, divisor: i32) DivisionResultado {
    return .{
        .cociente = @divTrunc(dividendo, divisor),
        .resto = @rem(dividendo, divisor),
    };
}

pub fn main() void {
    const resultado = dividirConResto(17, 5);
    std.debug.print("17 / 5 = {d} resto {d}\n", .{
        resultado.cociente,
        resultado.resto
    });
}

Funciones con Errores

const std = @import("std");

const MathError = error{
    DivisionPorCero,
    Overflow,
};

fn dividirSeguro(a: i32, b: i32) MathError!i32 {
    if (b == 0) return MathError.DivisionPorCero;
    return @divTrunc(a, b);
}

pub fn main() void {
    // Usando try (propaga el error)
    const r1 = dividirSeguro(10, 2) catch |err| {
        std.debug.print("Error: {}\n", .{err});
        return;
    };
    std.debug.print("10/2 = {d}\n", .{r1});

    // Usando catch con valor por defecto
    const r2 = dividirSeguro(10, 0) catch 0;
    std.debug.print("10/0 = {d}\n", .{r2});
}

Funciones Genéricas con Comptime

const std = @import("std");

fn maximoGenerico(comptime T: type, a: T, b: T) T {
    return if (a > b) a else b;
}

fn llenarArray(comptime T: type, arr: []T, valor: T) void {
    for (arr) |*elem| {
        elem.* = valor;
    }
}

pub fn main() void {
    std.debug.print("Max i32: {d}\n", .{maximoGenerico(i32, 5, 10)});
    std.debug.print("Max f64: {d}\n", .{maximoGenerico(f64, 3.14, 2.71)});

    var arr: [5]u8 = undefined;
    llenarArray(u8, &arr, 42);
}

Funciones como Valores

const std = @import("std");

fn sumar(a: i32, b: i32) i32 {
    return a + b;
}

fn restar(a: i32, b: i32) i32 {
    return a - b;
}

fn aplicar(f: *const fn (i32, i32) i32, a: i32, b: i32) i32 {
    return f(a, b);
}

pub fn main() void {
    const resultado1 = aplicar(sumar, 10, 5);
    const resultado2 = aplicar(restar, 10, 5);

    std.debug.print("Sumar: {d}, Restar: {d}\n", .{ resultado1, resultado2 });
}

Recursión

const std = @import("std");

fn fibonacci(n: u32) u32 {
    if (n <= 1) return n;
    return fibonacci(n - 1) + fibonacci(n - 2);
}

// Versión con tail call optimization
fn fibonacciTail(n: u32, a: u32, b: u32) u32 {
    if (n == 0) return a;
    if (n == 1) return b;
    return @call(.always_tail, fibonacciTail, .{ n - 1, b, a + b });
}

pub fn main() void {
    std.debug.print("Fib(10): {d}\n", .{fibonacci(10)});
    std.debug.print("FibTail(10): {d}\n", .{fibonacciTail(10, 0, 1)});
}

Testing de Funciones

const std = @import("std");
const testing = std.testing;

fn sumar(a: i32, b: i32) i32 {
    return a + b;
}

fn multiplicar(a: i32, b: i32) i32 {
    return a * b;
}

const MathError = error{DivisionPorCero};

fn dividir(a: i32, b: i32) MathError!i32 {
    if (b == 0) return MathError.DivisionPorCero;
    return @divTrunc(a, b);
}

fn factorial(n: u32) u32 {
    if (n <= 1) return 1;
    return n * factorial(n - 1);
}

fn maximoGenerico(comptime T: type, a: T, b: T) T {
    return if (a > b) a else b;
}

fn intercambiar(a: *i32, b: *i32) void {
    const temp = a.*;
    a.* = b.*;
    b.* = temp;
}

test "funciones básicas" {
    try testing.expectEqual(@as(i32, 15), sumar(10, 5));
    try testing.expectEqual(@as(i32, 0), sumar(-5, 5));
    try testing.expectEqual(@as(i32, 50), multiplicar(10, 5));
}

test "función con errores - caso exitoso" {
    const resultado = try dividir(10, 2);
    try testing.expectEqual(@as(i32, 5), resultado);
}

test "función con errores - división por cero" {
    const resultado = dividir(10, 0);
    try testing.expectError(MathError.DivisionPorCero, resultado);
}

test "función recursiva" {
    try testing.expectEqual(@as(u32, 1), factorial(0));
    try testing.expectEqual(@as(u32, 1), factorial(1));
    try testing.expectEqual(@as(u32, 120), factorial(5));
}

test "función genérica" {
    try testing.expectEqual(@as(i32, 10), maximoGenerico(i32, 5, 10));
    try testing.expectEqual(@as(f64, 3.14), maximoGenerico(f64, 2.71, 3.14));
    try testing.expectEqual(@as(u8, 255), maximoGenerico(u8, 100, 255));
}

test "función con punteros" {
    var a: i32 = 5;
    var b: i32 = 10;

    intercambiar(&a, &b);

    try testing.expectEqual(@as(i32, 10), a);
    try testing.expectEqual(@as(i32, 5), b);
}

test "función con slice mutable" {
    var arr = [_]i32{ 1, 2, 3 };

    for (&arr) |*n| {
        n.* *= 2;
    }

    try testing.expectEqual(@as(i32, 2), arr[0]);
    try testing.expectEqual(@as(i32, 4), arr[1]);
    try testing.expectEqual(@as(i32, 6), arr[2]);
}

Ejercicios

  1. Crea una función que calcule la potencia de un número (recursiva e iterativa)
  2. Implementa una función genérica que encuentre el mínimo en un slice
  3. Crea una función que valide un email y retorne un error si es inválido

Resumen