For

         Los bucles for son ideales para los casos donde el número de iteraciones es conocido de antemano. como cuando procesamos un array o queremos hacer algo un número fijo de veces.

En Zig creamos un bucle for de esta manera:


for (secuencia_de_elementos) | elemento | {

     // Código ejecutado para cada elemento perteneciente a la secuencia procesada

} 


También podemos añadir un rango para obtener el índice de cada elemento:


for (secuencia_de_elementos, 0..) | elemento, índice | {

     // ejecutado para cada elemento de la secuencia procesada

} 


Igual que podemos combinar un array con un rango, podemos combinar varios arrays. Imagina que en tu juego el jugador llega a la base espacial donde disponen de los mejores modelos de combate shmup, y quieres presentarle los datos por pantalla. Ah, lo que pasa es que el terminal de la base funciona con una API un tanto “incómoda” para los terrestres: los datos de la misma nave vienen en diferentes arrays: uno contiene el modelo, otro el poder de ataque, otro velocidad. Por suerte cuentas con los bucles for de Zig:

for_spaceships.zig

const std = @import("std");

const print = std.debug.print;

pub fn main() void {

    const a_spaceship = [_][]const u8{ "Fighter", "VViper", "Silver Hawk", "Saint Dragon", "Arrowhead" };

    const a_attack = [_]u16{

        200, // disparo sencillo - puede ser doble

        450, // con powerups

        400, // disparo + misiles + láser

        375, // disparo frontal con powerups

        600, // láser potente +  cápsula Force

    };

    const a_speed = [_]u16{

        150, // lento

        300, // rápido con power ups

        275, // velocidad media

        200, // mecha lento por la cola articulada

        180, // lento

    };

    print("Modelos de combate más populares: \n", .{});

    print("-------------------------------------------------\n", .{});

    print("#\t| MODELO\t| ATAQUE\t| VELOCIDAD\t \n", .{});

    print("-------------------------------------------------\n", .{});

    for (a_spaceship, a_attack, a_speed, 0..) // bucle para listar los arrays combinados

    | // captura de datos

        s_model, // modelo de nave

        n_pow, // puntos de ataque

        n_speed, // velocidad nave

        n_i, // indice

    | {

        print("{}\t| {s}\t| {}\t\t| {}\t \n", // imprimimos los datos

            .{ n_i + 1, s_model, n_pow, n_speed });

    }

}

$ zig run for_spaceships.zig

Modelos de combate más populares:

-------------------------------------------------

#      | MODELO        | ATAQUE        | VELOCIDAD      

-------------------------------------------------

1      | Fighter       | 200           | 150    

2      | VViper        | 450           | 300    

3      | Silver Hawk   | 400           | 275    

4      | Saint Dragon  | 375           | 200    

5      | Arrowhead     | 600           | 180  

¡Excelente! Con esta información combinada el jugador podrá seguir su aventura espacial. En un solo bucle for combinamos los nombres, la potencia y la velocidad de las naves del catálogo y además un índice automático. En este caso los elementos se recorren en paralelo: una posición de cada array forma un conjunto junto a la misma posición de los demás arrays.

        Ahora, después de listar las naves, el jugador entra en la opción: “Power-ups para la nave”. Pero aquí nos encontramos con un problema: la base de datos está hecha un desastre. Uff:

Los power-ups están mezclados - tienen distintas propiedades: ataque, defensa y puntos de curación pero aquellos campos que no aplican para un tipo están puestos a null - esos hay que imprimirlos en la lista con el valor 0.

for_powerups.zig

const std = @import("std");

const print = std.debug.print;

pub fn main() void {

    const a_powerups = [_]?[]const u8{

        "Turbo Boost", // aumenta velocidad

        "Láser doble", // ataque

        null, // DADO DE BAJA !!!

        null, // DATOS CORRUPTOS !!!

        "Campo escudo", // solo defensa

        "Kit emergen", // recupera energía

        null, // Nombre no conocido pero vale

        "OverdriveCore", // mega powerup

        "Borrado",

        "Borrado",

        // ...así son todos los que siguen

    };

    const a_pow_attack = [_]?u16{

        null, // Turbo Boost no da ataque

        80, // Láser doble

        null,

        777,

        null, // Campo protector no da ataque

        null, // Kit de emergencia no es ofensivo

        null,

        180, // Overdrive Core

        null,

        null,

        // ...

    };

    const a_pow_def = [_]?u16{

        5, // Turbo Boost da algo de defensa

        null, // Láser doble no da defensa

        null,

        777,

        40, // Campo protector

        null, // Kit de emergencia

        null, // ?

        20, // Overdrive Core

        null,

        null,

        // ...

    };

    const a_pow_heal = [_]?u16{

        null, // no cura

        null, // no cura

        null,

        777,

        null, // Campo protector no cura

        100, // Kit de emergencia ¡repara!

        200, // ? esto tenía que ser algo de reparar

        null, // Overdrive Core

        null,

        null,

        // ...

    };

    print("Power ups para la nave: \n", .{});

    print("======================================================\n", .{});

    print("#\t| NOMBRE\t\t| ATQ.\t| DEF.\t| ENE.\t \n", .{});

    print("======================================================\n", .{});

    var n_i: usize = 0;

    loop: switch (n_i) {

        else => {

            for (a_powerups[n_i..], 

                 a_pow_attack[n_i..], 

                 a_pow_def[n_i..], 

                 a_pow_heal[n_i..], 

                 n_i..) // bucle para listar los arrays combinados

                | // captura de datos

                  s_name, // modelo de nave

                  n_att, // puntos de ataque

                  n_def, // puntos defensa

                  n_energy, // puntos energía

                  n_ix, // indice

                | {

                // el indice 2 dado de baja -> no se lista

                if (n_ix == 2) continue;

                // base de datos borrada

                // salir después del registro 8

                if (n_ix > 7) break;

                // si los datos son corruptos los campos = 777

                if (n_att == 777) {

                    n_i = n_ix + 1;

                    continue :loop n_i;

                }

                // manejo de algunos nombres nulos

                const s_name_pow: []const u8 = s_name orelse "DESCONOCIDO";

                print("{}\t| {s}\t\t| {} \t| {}\t| {}\t  \n", //

                    .{

                        n_ix + 1, // índice

                        s_name_pow, // nombre

                        n_att orelse 0, // puntos ataque

                        n_def orelse 0, // puntos defensa

                        n_energy orelse 0, // energía

                    });

            }

        },

    }

}

$ zig run for_powerups.zig

Power ups para la nave:

======================================================

#       | NOMBRE                | ATQ.  | DEF.  | ENE.  

======================================================

1       | Turbo Boost           | 0     | 5     | 0      

2       | Láser doble           | 80    | 0     | 0      

5       | Campo escudo          | 0     | 40    | 0      

6       | Kit emergen           | 0     | 0     | 100    

7       | DESCONOCIDO           | 0     | 0     | 200    

8       | OverdriveCore         | 180   | 20    | 0  

Al usar break y continue dentro de los bucles for podemos manejar valores nulos, salir del bucle, saltar hasta una etiqueta, volver al inicio del bucle y, como ves, procesar los datos como mejor nos convenga.  

Si te fijas bien, hay un detalle que no habíamos visto hasta ahora: la palabra clave orelse, que sirve para sustituir los valores opcionales (potencialmente nulos) por valores por defecto:

  // manejo de algunos nombres nulos

  const s_name_pow: []const u8 = s_name orelse "DESCONOCIDO";

Esto significa que cuando s_name es null se asigna la cadena “DESCONOCIDO” a la constante s_name_pow, y cuando vale null se asina el valor que contiene - que será una cadena válida.

También cuando imprimimos los valores opcionales:

  n_att orelse 0, // puntos ataque

  n_def orelse 0, // puntos defensa

  n_energy orelse 0, // energía

Si estas variables tienen el valor null, pasamos el valor 0 como argumento al print. De esta forma, podemos imprimir valores potencialmente nulos como enteros normales sin necesidad de usar {?}:

      // sin orelse (fallaría si hay nulls)

  print("{}\t| {s}\t\t| {?} \t| {?}\t| {?}\t  \n"

      // con orelse

  print("{}\t| {s}\t\t| {} \t| {}\t| {}\t  \n"

While
Resumen del capítulo
© 2025 Zen of Zig