3 septembre 2023

Boucles : while et for

Nous avons souvent besoin d’effectuer des actions similaires plusieurs fois de suite.

Par exemple, lorsque nous devons extraire des marchandises d’une liste les unes Ă  la suite des autres. Ou exĂ©cutez simplement le mĂȘme code pour chaque numĂ©ro de 1 Ă  10.

Les boucles permettent de rĂ©pĂ©ter plusieurs fois la mĂȘme partie du code.

Les boucles for
of et for
in

Une petite annonce pour les lecteurs avertis.

Cet article ne couvre que les boucles de base : while, do..while et for(..;..;..).

Si vous ĂȘtes venu Ă  cet article Ă  la recherche d’autres types de boucles, voici les pointeurs :

  • Voir for
in pour boucler sur les propriĂ©tĂ©s de l’objet.
  • Voir for
of et iterables pour boucler sur des tableaux et des objets itĂ©rables.

Sinon, lisez la suite.

La boucle “while”

La boucle while a la syntaxe suivante :

while (condition) {
  // code
  // appelé "loop body" ("corps de boucle")
}

Tant que la condition est vraie, le code du corps de la boucle est exécuté.

Par exemple, la boucle ci-dessous affiche i tant que i < 3 :

let i = 0;
while (i < 3) { // affiche 0, puis 1, puis 2
  alert( i );
  i++;
}

Une unique exĂ©cution du corps de la boucle est appelĂ©e une itĂ©ration. La boucle dans l’exemple ci-dessus fait trois itĂ©rations.

S’il n’y avait pas d’i++ dans l’exemple ci-dessus, la boucle se rĂ©pĂšterait (en thĂ©orie) pour toujours. En pratique, le navigateur fournit des moyens d’arrĂȘter ces boucles, et pour JavaScript cĂŽtĂ© serveur, nous pouvons tuer le processus.

Toute expression ou variable peut ĂȘtre une condition de boucle, pas seulement une comparaison. Ils sont Ă©valuĂ©s et convertis en un boolĂ©en par while.

Par exemple, le moyen le plus court d’écrire while (i != 0) pourrait ĂȘtre while (i) :

let i = 3;
while (i) { // quand i devient 0, la condition devient fausse et la boucle s'arrĂȘte
  alert( i );
  i--;
}
Les accolades ne sont pas requis pour un corps Ă  une seule ligne

Si le corps de la boucle a une seule déclaration, nous pouvons omettre les accolades {
} :

let i = 3;
while (i) alert(i--);

La boucle “do
while”

La vĂ©rification de la condition peut ĂȘtre dĂ©placĂ©e sous le corps de la boucle en utilisant la syntaxe do..while :

do {
  // corps de la boucle
} while (condition);

La boucle exĂ©cute d’abord le corps, puis vĂ©rifie la condition et, tant que c’est vrai, l’exĂ©cute encore et encore.

Par exemple :

let i = 0;
do {
  alert( i );
  i++;
} while (i < 3);

Cette forme de syntaxe est rarement utilisĂ©e, sauf lorsque vous souhaitez que le corps de la boucle s’exĂ©cute au moins une fois, quelle que soit la condition. Habituellement, l’autre forme est prĂ©fĂ©rĂ©e : while(
) {
}.

La boucle “for”

La boucle for est plus complexe, mais c’est aussi la boucle la plus utilisĂ©e.

Cela ressemble Ă  ceci :

for (début; condition; étape) {
  // ... corps de la boucle ...
}

Apprenons la signification de ces parties par l’exemple. La boucle ci-dessous exĂ©cute alert(i) pour i en partant de 0 jusqu’à 3 (mais non compris) :

for (let i = 0; i < 3; i++) { // affiche 0, puis 1, puis 2
  alert(i);
}

Examinons la déclaration for partie par partie :

partie
début let i = 0 Exécute une fois en entrant dans la boucle.
condition i < 3 VĂ©rifiĂ© avant chaque itĂ©ration de la boucle, en cas d’échec, la boucle s’arrĂȘte.
corps alert(i) Exécute encore et encore tant que la condition est vraie
étape i++ Exécute aprÚs le corps à chaque itération

L’algorithme de boucle gĂ©nĂ©ral fonctionne comme ceci :

Exécuter le début
→ (si condition → exĂ©cuter le corps et exĂ©cuter l'Ă©tape)
→ (si condition → exĂ©cuter le corps et exĂ©cuter l'Ă©tape)
→ (si condition → exĂ©cuter le corps et exĂ©cuter l'Ă©tape)
→ ...

C’est-Ă -dire que begin est exĂ©cutĂ© une fois, puis itĂ©rĂ© : aprĂšs chaque test de condition, body et step sont exĂ©cutĂ©s.

Si vous dĂ©butez dans les boucles, il pourrait ĂȘtre utile de revenir Ă  l’exemple et de reproduire comment elle s’exĂ©cute pas Ă  pas sur une feuille de papier.

Voici ce qui se passe exactement dans notre cas :

// for (let i = 0; i < 3; i++) alert(i)

// exécute début
let i = 0
// si condition → exĂ©cuter le corps et exĂ©cuter l'Ă©tape
if (i < 3) { alert(i); i++ }
// si condition → exĂ©cuter le corps et exĂ©cuter l'Ă©tape
if (i < 3) { alert(i); i++ }
// si condition → exĂ©cuter le corps et exĂ©cuter l'Ă©tape
if (i < 3) { alert(i); i++ }
// ... fini, parce que maintenant i == 3
Déclaration de variable en ligne

Ici, la variable “counter” i est dĂ©clarĂ©e directement dans la boucle. Cela s’appelle une dĂ©claration de variable “en ligne”. De telles variables ne sont visibles que dans la boucle.

for (let i = 0; i < 3; i++) {
  alert(i); // 0, 1, 2
}
alert(i); // erreur, pas de variable

Au lieu de définir une variable, nous pouvons en utiliser une existante :

let i = 0;

for (i = 0; i < 3; i++) { // utiliser une variable existante
  alert(i); // 0, 1, 2
}

alert(i); // 3, visible, car déclaré en dehors de la boucle

Sauter des parties

Toute partie de for peut ĂȘtre ignorĂ©e.

Par exemple, nous pouvons omettre le dĂ©but si nous n’avons rien Ă  faire au dĂ©but de la boucle.

Comme ici :

let i = 0; // nous avons i déjà déclaré et assigné

for (; i < 3; i++) { // pas besoin de "début"
  alert( i ); // 0, 1, 2
}

Nous pouvons également supprimer la partie étape :

let i = 0;

for (; i < 3;) {
  alert( i++ );
}

La boucle est devenue identique Ă  while (i < 3).

Nous pouvons tout supprimer, créant ainsi une boucle infinie :

for (;;) {
  // répÚte sans limites
}

Veuillez noter que les deux les points-virgules ; de for doivent ĂȘtre prĂ©sents, sinon ce serait une erreur de syntaxe.

Briser la boucle

Normalement, la boucle sort quand la condition devient fausse.

Mais nous pouvons forcer la sortie à tout moment. Il y a une directive spéciale appelée break pour cela.

Par exemple, la boucle ci-dessous demande Ă  l’utilisateur une sĂ©rie de chiffres, mais “se casse” quand aucun numĂ©ro n’est entrĂ© :

let sum = 0;

while (true) {

  let value = +prompt("Entrez un nombre", '');

  if (!value) break; // (*)

  sum += value;

}
alert( 'Sum: ' + sum );

La directive break est activĂ©e sur la ligne (*) si l’utilisateur entre une ligne vide ou annule l’entrĂ©e. Il arrĂȘte la boucle immĂ©diatement, en passant le contrĂŽle Ă  la premiĂšre ligne aprĂšs la boucle. À savoir, alert.

La combinaison “boucle infinie + break au besoin” est idĂ©ale pour les situations oĂč la condition doit ĂȘtre vĂ©rifiĂ©e non pas au dĂ©but / Ă  la fin de la boucle, mais au milieu, voire Ă  plusieurs endroits du corps.

Continuer jusqu'à la prochaine itération

La directive continue est une “version plus lĂ©gĂšre” de break. Cela n’arrĂȘte pas toute la boucle. Au lieu de cela, elle arrĂȘte l’itĂ©ration en cours et force la boucle Ă  en dĂ©marrer une nouvelle (si la condition le permet).

Nous pouvons l’utiliser si nous avons terminĂ© l’itĂ©ration en cours et aimerions passer Ă  la suivante.

La boucle ci-dessous utilise continue pour ne produire que des valeurs impaires :

for (let i = 0; i < 10; i++) {

  // si vrai, saute le reste du corps
  if (i % 2 == 0) continue;

  alert(i); // 1, ensuite 3, 5, 7, 9
}

Pour les valeurs paires de i, la directive continue arrĂȘte l’exĂ©cution du corps en passant le contrĂŽle Ă  la prochaine itĂ©ration de for (avec le nombre suivant). Donc, l’alert n’est appelĂ©e que pour les valeurs impaires.

La directive continue aide Ă  rĂ©duire le niveau d’imbrication

Une boucle affichant des valeurs impaires pourrait ressembler Ă  ceci :

for (let i = 0; i < 10; i++) {

  if (i % 2) {
    alert( i );
  }

}

D’un point de vue technique, c’est identique à l’exemple du dessus. Certes, nous pouvons simplement envelopper le code dans un bloc if au lieu de continue.

Mais comme effet secondaire, nous avons obtenu un niveau d’imbrication supplĂ©mentaire (l’appel de l’alert Ă  l’intĂ©rieur des accolades). Si le code Ă  l’intĂ©rieur du if est plus long que quelques lignes, la lisibilitĂ© globale peut en ĂȘtre rĂ©duite.

Pas de break/continue à droite de ‘?’

Veuillez noter que les constructions de syntaxe qui ne sont pas des expressions ne peuvent pas ĂȘtre utilisĂ©es avec l’opĂ©rateur ternaire ?. Tout particuliĂšrement les directives telles que break/continue ne sont pas autorisĂ©es.

Par exemple, si nous prenons ce code :

if (i > 5) {
  alert(i);
} else {
  continue;
}


 Et le réécrivons Ă  l’aide d’un point d’interrogation :

(i > 5) ? alert(i) : continue; // continue n'est pas autorisé ici


 Ensuite cesse de fonctionner : il y a une erreur de syntaxe.

C’est une autre raison pour ne pas utiliser l’opĂ©rateur point d’interrogation ? au lieu de if.

Des labels pour break/continue

Parfois, nous devons sortir de plusieurs boucles imbriquĂ©es en mĂȘme temps.

Par exemple, dans le code ci-dessous, nous bouclons sur i et j pour demander les coordonnées (i, j) de (0,0) à (2,2) :

for (let i = 0; i < 3; i++) {

  for (let j = 0; j < 3; j++) {

    let input = prompt(`Value at coords (${i},${j})`, '');

    // Et si nous voulons sortir d'ici Ă  Done (ci-dessous) ?
  }
}

alert('Done!');

Nous avons besoin d’un moyen d’arrĂȘter le processus si l’utilisateur annule la saisie.

Le break ordinaire aprĂšs input ne ferait que briser la boucle intĂ©rieure. Ce n’est pas suffisant – les labels viennent Ă  la rescousse.

Une label est un identifiant avec deux points avant une boucle :

labelName: for (...) {
  ...
}

L’instruction break <labelName> dans la boucle interrompt tout le bloc de code relatif au label.

Comme ici :

outer: for (let i = 0; i < 3; i++) {

  for (let j = 0; j < 3; j++) {

    let input = prompt(`Value at coords (${i},${j})`, '');

    // si une chaßne est vide ou annulée, alors rompre les deux boucles
    if (!input) break outer; // (*)

    // faire quelque chose avec la valeur 

  }
}

alert('Done!');

Dans le code ci-dessus, break outer regarde vers le haut le label outer et sort de cette boucle.

Donc, le contrĂŽle va directement de (*) Ă  alert('Done!').

Nous pouvons également déplacer le label sur une ligne séparée :

outer:
for (let i = 0; i < 3; i++) { ... }

La directive continue peut Ă©galement ĂȘtre utilisĂ©e avec un label. Dans ce cas, l’exĂ©cution passe Ă  l’itĂ©ration suivante de la boucle labellisĂ©e.

Les labels ne permettent pas de “sauter” n’importe oĂč

Les labels ne nous permettent pas de sauter dans un endroit arbitraire du code.

Par exemple, il est impossible de faire ceci :

break label;  // saute au label ci-dessous (ne fonctionne pas)

label: for (...)

Une directive break doit ĂȘtre Ă  l’intĂ©rieur d’un bloc de code. Techniquement, tout bloc de code Ă©tiquetĂ© fera l’affaire, par exemple :

label: {
  // ...
  break label; // works
  // ...
}


 Bien que 99,9% du temps les break utilisĂ©s sont Ă  l’intĂ©rieur de boucles, comme nous l’avons vu dans les exemples ci-dessus.

Un continue n’est possible que depuis l’intĂ©rieur d’une boucle.

Résumé

Nous avons couvert 3 types de boucles :

  • while – La condition est vĂ©rifiĂ©e avant chaque itĂ©ration.
  • do..while – La condition est vĂ©rifiĂ©e aprĂšs chaque itĂ©ration.
  • for (;;) – La condition est vĂ©rifiĂ©e avant chaque itĂ©ration, des paramĂštres supplĂ©mentaires sont disponibles.

Pour crĂ©er une boucle “infinie”, on utilise gĂ©nĂ©ralement la construction while(true). Une telle boucle, comme toute autre, peut ĂȘtre stoppĂ©e avec la directive break.

Si nous ne voulons rien faire avec l’itĂ©ration actuelle et que nous souhaitons avancer jusqu’à la suivante, la directive continue nous permet de faire cela.

break/continue accepte les labels prĂ©cĂ©dents la boucle. Un label est le seul moyen de break/continue pour Ă©chapper Ă  l’imbrication et accĂ©der en dehors de la boucle.

Exercices

importance: 3

Quelle est la derniÚre valeur affichée par ce code ? Pourquoi ?

let i = 3;

while (i) {
  alert( i-- );
}

La réponse : 1.

let i = 3;

while (i) {
  alert( i-- );
}

Chaque itĂ©ration de boucle diminue i de 1. La vĂ©rification while(i) arrĂȘte la boucle lorsque i = 0.

Par consĂ©quent, les Ă©tapes de la boucle forment la sĂ©quence suivante (“boucle dĂ©composĂ©e”) :

let i = 3;

alert(i--); // affiche 3, diminue i Ă  2

alert(i--) // affiche 2, diminue i Ă  1

alert(i--) // affiche 1, diminue i Ă  0

// terminé, la vérification while(i) termine la boucle
importance: 4

A votre avis, quelles sont les valeurs affichées pour chaque boucle ? Notez-les puis comparer avec la réponse.

Les deux boucles affichent-elles les mĂȘmes valeurs dans l’alert ou pas ?

  1. Le préfixe sous forme ++i :

    let i = 0;
    while (++i < 5) alert( i );
  2. Le postfixe sous forme i++ :

    let i = 0;
    while (i++ < 5) alert( i );

L’exercice montre comment les formes postfix/prefix peuvent conduire Ă  des rĂ©sultats diffĂ©rents lorsqu’ils sont utilisĂ©s dans des comparaisons.

  1. De 1 Ă  4

    let i = 0;
    while (++i < 5) alert( i );

    La premiĂšre valeur est i=1, parce que ++i incrĂ©mente d’abord i puis renvoie la nouvelle valeur. La premiĂšre comparaison est donc 1 < 5 et alert indique 1.

    Ensuite, viennent 2,3,4
 – les valeurs apparaissent les unes aprĂšs les autres. La comparaison utilise toujours la valeur incrĂ©mentĂ©e, car ++ est avant la variable.

    Enfin, i=4 est incrĂ©mentĂ© Ă  5, la comparaison while(5 < 5) Ă©choue et la boucle s’arrĂȘte. Donc 5 n’est pas affichĂ©.

  2. De 1 Ă  5

    let i = 0;
    while (i++ < 5) alert( i );

    La premiĂšre valeur est encore i=1. La forme postfixĂ©e de i++ incrĂ©mente i puis renvoie l’ancienne valeur, la comparaison i++ < 5 utilisera donc i=0 (contrairement Ă  ++i < 5).

    Mais l’appel d’alert est sĂ©parĂ©. C’est une autre instruction qui s’exĂ©cute aprĂšs l’incrĂ©mentation et la comparaison. Donc, on obtient i=1.

    Ensuite viennent 2,3,4


    ArrĂȘtons-nous sur i=4. Le prĂ©fixe sous forme ++i l’incrĂ©menterait et utiliserait 5 dans la comparaison. Mais ici nous avons la forme postfixĂ©e i++. Donc, i augmente Ă  5, mais renvoie l’ancienne valeur. Par consĂ©quent, la comparaison est en rĂ©alitĂ© while(4 < 5) – true, et le contrĂŽle continue Ă  alert.

    La valeur i=5 est la derniĂšre, car Ă  l’étape suivante while(5 <5) est faux.

importance: 4

Pour chaque boucle, notez les valeurs qui vont s’afficher. Ensuite, comparez avec la rĂ©ponse.

Les deux boucles alert les mĂȘmes valeurs ou pas ?

  1. La forme postfix :

    for (let i = 0; i < 5; i++) alert( i );
  2. La forme préfix :

    for (let i = 0; i < 5; ++i) alert( i );

La réponse: de 0 à 4 dans les deux cas.

for (let i = 0; i < 5; ++i) alert( i );

for (let i = 0; i < 5; i++) alert( i );

Cela peut ĂȘtre facilement dĂ©duit de l’algorithme de for :

  1. Exécute une fois i = 0 avant tout (début).
  2. VĂ©rifie l’état i < 5
  3. Si true – execute le corps de la boucle alert(i), et ensuite i++

L’incrĂ©ment i++ est sĂ©parĂ© de la vĂ©rification de condition (2). C’est juste une autre dĂ©claration.

La valeur renvoyĂ©e par l’incrĂ©mentation n’est pas utilisĂ©e ici, il n’y a donc pas de diffĂ©rence entre i++ et ++i.

importance: 5

Utilisez la boucle for pour afficher les nombres pairs de 2 Ă  10.

Exécuter la démo

for (let i = 2; i <= 10; i++) {
  if (i % 2 == 0) {
    alert( i );
  }
}

Ici nous utilisons l’opĂ©rateur “modulo” % pour obtenir le reste et vĂ©rifier si c’est pair ou pas.

importance: 5

Réécrivez le code en modifiant la boucle for en while sans modifier son comportement (la sortie doit rester la mĂȘme).

for (let i = 0; i < 3; i++) {
  alert( `number ${i}!` );
}
let i = 0;
while (i < 3) {
  alert( `number ${i}!` );
  i++;
}
importance: 5

Ecrivez une boucle qui demande un nombre supérieur à 100. Si le visiteur saisit un autre numéro, demandez-lui de le saisir à nouveau.

La boucle doit demander un numĂ©ro jusqu’à ce que le visiteur saisisse un nombre supĂ©rieur Ă  100 ou annule l’entrĂ©e/entre une ligne vide.

Ici, nous pouvons supposer que le visiteur ne saisit que des chiffres. Il n’est pas nĂ©cessaire de mettre en Ɠuvre un traitement spĂ©cial pour une entrĂ©e non numĂ©rique dans cette tĂąche.

Exécuter la démo

let num;

do {
  num = prompt("Enter a number greater than 100?", 0);
} while (num <= 100 && num);

La boucle do..while se répÚte tant que les deux vérifications sont vrai :

  1. La vĂ©rification de num <= 100 – c’est-Ă -dire que la valeur entrĂ©e n’est toujours pas supĂ©rieure Ă  100.
  2. La vĂ©rification que && num est false lorsque num est null ou une chaĂźne vide. Ensuite, la boucle while s’arrĂȘte aussi.

P.S. Si num est null, alors num <= 100 est true. Par consĂ©quent, sans la seconde vĂ©rification, la boucle ne s’arrĂȘterait pas si l’utilisateur cliquait sur CANCEL. Les deux vĂ©rifications sont obligatoires.

importance: 3

Un nombre entier supĂ©rieur Ă  1 est appelĂ© un Nombre premier s’il ne peut ĂȘtre divisĂ© sans reste par rien d’autre que 1 et lui-mĂȘme.

En d’autres termes, n > 1 est un nombre premier s’il ne peut ĂȘtre divisĂ© de maniĂšre Ă©gale par autre chose que 1 et n.

Par exemple, 5 est un nombre premier, car il ne peut pas ĂȘtre divisĂ© sans reste par 2, 3 et 4.

Écrivez un code qui produit les nombres premiers dans l’intervall e 2 à n.

Pour n = 10, le résultat sera 2,3,5,7.

P.S. Le code devrait fonctionner pour n’importe quel n et aucune valeur fixe ne doit ĂȘtre codĂ© en dur.

Il existe de nombreux algorithmes pour cette tĂąche.

Utilisons une boucle imbriquée :

Pour chaque i dans l'intervalle {
  vérifier si i a un diviseur de 1..i
  si oui => la valeur n'est pas un nombre premier
  si non => la valeur est un nombre premier, affichez-le
}

Un code utilisant un label :

let n = 10;

nextPrime:
for (let i = 2; i <= n; i++) { // Pour chaque i...

  for (let j = 2; j < i; j++) { // cherche un diviseur ..
    if (i % j == 0) continue nextPrime; // pas un premier, on passe au prochain i
  }

  alert( i ); // un premier
}

Il y a beaucoup d’espace pour l’optimiser. Par exemple, nous pourrions rechercher les diviseurs de 2 Ă  la racine carrĂ©e de i. Quoi qu’il en soit, si nous voulons ĂȘtre vraiment efficaces pour les grands intervalles, nous devons changer d’approche et nous baser sur des mathĂ©matiques avancĂ©es et des algorithmes complexes comme Crible quadratique, Crible algĂ©brique etc.

Carte du tutoriel

Commentaires

lire ceci avant de commenter

  • Si vous avez des amĂ©liorations Ă  suggĂ©rer, merci de soumettre une issue GitHub ou une pull request au lieu de commenter.
  • Si vous ne comprenez pas quelque chose dans l'article, merci de prĂ©ciser.
  • Pour insĂ©rer quelques bouts de code, utilisez la balise <code>, pour plusieurs lignes – enveloppez-les avec la balise <pre>, pour plus de 10 lignes - utilisez une sandbox (plnkr, jsbin, codepen
)