Section 6.2

Parcourir par le rang

La boucle for comptée trouve enfin son emploi — et le piège du cran de trop se voit à l'exécution.

Quand le rang lui-même vous intéresse, for … in ne suffit plus : il donne les valeurs, jamais leur position. C’est le moment de reprendre la boucle comptée de la section 4.2.

@entry(demarrer)
object MonProgramme {
    demarrer() {
        var mesures := [12, 19, 7];

        for (var i := 0; i < mesures.length; i := i + 1) {
            Console.print(text: $"mesure {i + 1} sur {mesures.length} : {mesures[i]}");
        }
    }
}

Les trois clauses, appliquées à un tableau

for (var i := 0; i < mesures.length; i := i + 1) {

Chaque clause dit maintenant quelque chose de précis :

  • var i := 0 — on part du premier rang, qui est zéro ;
  • i < mesures.length — on continue tant qu’on n’a pas dépassé le dernier ;
  • i := i + 1 — on avance d’un rang.

La 4.2 présentait ces trois clauses sur une suite de nombres qui ne servait à rien d’autre qu’à compter. Voici ce à quoi elles servent vraiment : i n’est plus un nombre, c’est un rang, et mesures[i] est l’élément qui s’y trouve.

Remarquez que l’affichage se sert des deux : {i + 1} pour parler au lecteur, qui compte à partir de 1, et mesures[i] pour atteindre l’élément, qui se compte à partir de 0. Le décalage est là, tout entier, sur une seule ligne.

< et non <=

C’est la faute la plus courante de tout ce cours, et elle vaut qu’on s’y arrête.

Trois éléments, rangs 0, 1 et 2. La condition i < 3 laisse passer 0, 1 et 2, puis s’arrête : exactement les trois rangs qui existent. Écrivez i <= mesures.length et la boucle fait un tour de plus, avec i valant 3 — un rang qui n’existe pas.

Le résultat n’est pas un tableau mal parcouru, c’est un programme qui s’arrête :

12
19
7
uncaught error — IndexOutOfRangeError : index 3 out of range for an array of length 3

Les trois lignes s’affichent, puis tout tombe. Voilà pourquoi la 4.3 disait que la faute la plus classique du for compté est de s’arrêter un cran trop tôt ou trop tard : voici le cran de trop, et ce qu’il coûte.

À l’envers, sans compter

Remonter un tableau est un besoin si courant qu’il ne demande pas de boucle comptée :

for mesure in reverse mesures {
    Console.print(text: $"{mesure}");
}

Le mot reverse se glisse entre in et ce qu’on parcourt, et la boucle part du dernier élément. Tout ce que la sous-section précédente vous a fait éviter — l’indice de départ, la condition, le sens de l’avancement — disparaît avec lui. Un tableau vide ne fait aucun tour, dans un sens comme dans l’autre.

reverse n’est pas un mot réservé pour autant. Il ne prend ce sens qu’ici, juste après le in d’un for ; ailleurs il reste un nom comme un autre, et rien ne vous empêche d’appeler une variable reverse.

Lequel choisir

La règle de la 4.3 tient toujours, et cette sous-section ne fait qu’en montrer l’exception :

  • for … in dès que vous parcourez des valeurs, et for … in reverse dès que vous les parcourez à l’envers. Pas de rang à écrire, donc pas de rang à rater — la boucle ne peut pas sortir des bornes.
  • for compté quand le rang lui-même vous sert : pour l’afficher, pour comparer un élément à son voisin, pour n’en parcourir qu’une partie.

En cas de doute, for … in. Le rang qu’on n’écrit pas est un rang qu’on ne peut pas fausser — et depuis que reverse existe, remonter n’est plus une raison d’en écrire un.

À vous

  • Remplacez < par <= et exécutez. Trois lignes, puis l’erreur.
  • Parcourez à l’envers à la main — var i := mesures.length - 1, condition i >= 0, avancement i := i - 1 — puis récrivez la même chose avec for mesure in reverse mesures. Trois clauses contre un mot, pour le même résultat.
  • N’affichez qu’un élément sur deux, en avançant de 2 au lieu de 1.
  • Réécrivez la boucle en for mesure in mesures et voyez ce que vous perdez : plus moyen d’écrire « mesure 2 sur 3 ».