ZIG - Iterazioni - while


L'istruzione oggetto di questo paragrafo, while, è comune in molti linguaggi di programmazione. Essa introduce una iterazione che si ripete fintanto che è verificata una condizione booleana. In Zig comunque assume sfaccettature non comuni.
La sintassi del caso più semplice è la seguente:

while (condizione booleana) {
  istruzioni
}

La condizione, come sempre, deve essere messa tra parentesi. Il fulcro di tutto è proprio la condizione, finchè essa è verificata, come detto, il gruppo di istruzioni, una o più, viene eseguito.

Esempio 9.1

const std = @import("std");
pub fn main() !void {
  var i: i32 = 0;
  while (i < 5) {
    std.debug.print("{}\n", .{i});
    i += 1;
  }
}

Qui vediamo all'opera la prima e più semplice applicazione del nosto while. La stampa prosegue fintanto che la variabile "i" risulta minore di 5. Ovviamente bisogna prevedere un incremento della "i" stessa altrimenti l'iterazione si ripete all'infinito. A proposito di incremento, Zig ci mette anche a disposizione una forma di incremento in stile C, forse anche più leggibile:

Esempio 9.2

const std = @import("std");
pub fn main() !void {
  var i: i32 = 0;
  while (i < 5) : (i += 1) {
    std.debug.print("{}\n", .{i});
  }
}

while (i < 5) : (i += 1) questa elegante istruzione vi permette di specificare in modo chiaro ed evidente l'incremento, che può essere anche diverso da 1, ovviamente, dell'elemento scelto.

Se proprio vi serve un loop infinito potete ricorrere a

while (true) {
 istruzioni
}

Come in altri linguaggi sono presenti le due classiche istruzioni per uscire dall'iterazione o per saltarne un ciclo: si tratta di break e continue rispettivamente.

Esempio 9.3

const std = @import("std");
pub fn main() !void {
  var i: i32 = 0;
  while (i < 5) : (i += 1) {
    std.debug.print("{}\n", .{i});
    if (i == 3) {
      break;
    }
  }
}

e con continue:

Esempio 9.4

const std = @import("std");
pub fn main() !void {
  var i: i32 = 0;
  while (i < 5) : (i += 1) {
    if (i == 3) {
      continue;
    }
    std.debug.print("{}\n", .{i});
  }
}

che forniscono questo output, rispettivamente

0
1
2
3
0
1
2
4

nel primo caso siamo usciti dopo aver incontrato il valore 3 che invece nel secondo caso è stato saltato. La possibilità di gestire l'incremento del contatore è molto utile e ne riparleremo nel prossimo paragrafo dove descriveremo l'istruzione for. All'interno di while potremmo anche gestire strutture più complesse di if... else, con varie ramificazioni e questo potrà essere utile, tra i tanti casi possibili, anche nella gestione degli errori.
Il seguente esempio ci fornisce un uso alternativo di while. Esso si basa su una sintassi un po' particolare:

Esempio 9.5

const std = @import("std");
pub fn main() !void {
  var x1: ?i32 = 5;
  while (x1) |value| {
    std.debug.print("{}\n", .{value});
    x1 = null;
 }
}

var x1: ?i32 = 5; vuol dire che la variabile x1 può essere null
while (x1) |value| significa semplicemente "finchè c'è un valore in x1 mettilo in value". Vedremo altri esempi del genere, ad esempio quando parleremo del loop con l'istruzione for.
x1 = null; termina il while dopo avere stampato, una volta sola in questo caso, il valore 5.

Al contrario di if, while non può essere usato come espressione per riversare un valore in una variabile o in una costante. Tuttavia le cose possono anche non risultare così stringenti in questo senso, esiste una eccezione e ne parleremo nel prossimo paragrafo perchè riguarda anche for.