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:
- “A partir del registro 8 ya no vale ningún registro”,
- “Hay datos corruptos - suelen llevar 777 en todos los campos, es un valor que no tiene ningún otro item”.
- Hay una nota interna que dice “El tercer registro está dado de baja del sistema- ya no se puede adquirir.”
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" |