Les boucles permettent d'exécuter plusieurs fois le même code. Sans elles, il faudrait copier-coller chaque instruction autant de fois que nécessaire.
Tant que (while)
La boucle while exécute un bloc de code tant qu'une
condition est vraie :
let compteur = 1
while (compteur <= 5) {
console.log("Tour numéro " + compteur)
compteur = compteur + 1
}
// Affiche : Tour numéro 1, Tour numéro 2, ... Tour numéro 5
À chaque tour, on incrémente le compteur de 1. Quand il atteint 6, la condition compteur <= 5
devient fausse et la boucle s'arrête.
Pour (for)
La boucle for est une version plus compacte de while quand on connaît à l'avance le
nombre d'itérations. Elle regroupe la déclaration du compteur, la condition d'arrêt et l'incrémentation sur une
seule ligne :
for (let compteur = 1; compteur <= 5; compteur++) {
console.log("Tour " + compteur)
}
// Affiche : Tour 1, ... Tour 5
compteur++ est un raccourci pour compteur = compteur + 1.
Pour chaque (forEach)
Quand on veut parcourir les éléments d'un
tableau un par un, on utilise forEach :
let courses = ["Pommes", "Pain", "Lait"]
courses.forEach(function(article) {
console.log("Acheter : " + article)
})
Pour chaque élément du tableau, la fonction passée en paramètre est exécutée. C'est le callback dont on parlera
dans le chapitre sur les
fonctions. Le forEach est très courant dans le développement web : parcourir
une liste de clients, d'articles, de commandes...
Exemple :
Un site e-commerce veut envoyer un email de confirmation à tous les clients qui ont passé commande aujourd'hui. On récupère la liste des commandes du jour, et pour chaque commande, on envoie un email au client.
La boucle infinie
Si la condition d'arrêt n'est jamais atteinte, la boucle tourne indéfiniment. Le programme se bloque, consomme toutes les ressources de la machine, et il faut le forcer à s'arrêter.
// DANGER : boucle infinie !
let compteur = 1
while (compteur > 0) {
compteur = compteur + 1
}
// compteur ne sera jamais <= 0, la boucle ne s'arrête jamaisN'exécute pas cet exemple dans ta console, ça bloquerait ton onglet. La boucle infinie est un bug fréquent. L'essentiel, c'est de toujours s'assurer que la condition d'arrêt finira par être atteinte.
Sortir d'une boucle : break et continue
La condition d'arrêt n'est pas le seul moyen de reprendre la main. Deux mots-clés permettent d'intervenir en plein milieu d'un tour.
break
Le break arrête la boucle immédiatement et passe à la suite du programme.
On l'utilise par exemple pour une recherche, dès qu'on a trouvé ce qu'on cherchait, il n'y a plus rien à parcourir.
Prenons un client qui saisit un code promo au moment de payer. Il faut vérifier qu'il figure bien dans la liste des
codes valides.
let codesValides = ["BIENVENUE10", "NOEL20", "ETE15", "BLACKFRIDAY30"]
let codeSaisi = "ETE15"
let codeAccepte = false
for (let i = 0; i < codesValides.length; i++) {
if (codesValides[i] === codeSaisi) {
codeAccepte = true
break // trouvé, inutile de regarder les suivants
}
}
console.log(codeAccepte) // true
Sans le break, le programme continuerait à comparer le code aux suivants alors que la réponse est déjà
connue. Sur quatre codes personne ne le remarque, sur un catalogue de plusieurs centaines de milliers de références
la différence devient très concrète.
continue
Le continue abandonne le tour en cours et passe directement au suivant, sans arrêter la boucle. Il sert
à écarter les cas qui n'ont rien à faire dans le traitement.
Imaginons la note moyenne d'un produit, sachant que tous les acheteurs n'ont pas laissé d'avis.
let notes = [5, null, 4, 3, null, 5]
let total = 0
let nombreDAvis = 0
for (let i = 0; i < notes.length; i++) {
if (notes[i] === null) {
continue // cet acheteur n'a pas laissé d'avis, il ne compte pas
}
total = total + notes[i]
nombreDAvis = nombreDAvis + 1
}
console.log(total / nombreDAvis) // 4.25Map, filter et reduce, une autre façon de penser
Tout ce qu'on vient de voir décrit comment parcourir une liste, avec un compteur, une condition d'arrêt et des instructions à répéter. Il existe une autre approche, où l'on décrit plutôt le résultat attendu et où la mécanique du parcours devient invisible. Elle repose sur trois opérations :
- map : transforme chaque élément de la liste, indépendamment des autres, et produit une liste de même taille.
- filter : ne garde que les éléments qui remplissent une condition, et produit donc une liste plus courte.
- reduce : condense toute la liste en une seule valeur, un total, une moyenne, un maximum.
Prenons une opération commerciale, une remise de 10 % sur les articles à plus de 10 euros, et calculons le montant remisé. Les trois opérations s'enchaînent, chacune travaillant sur le résultat de la précédente.
let panier = [12, 30, 8]
// filter : seuls les articles à plus de 10 euros sont concernés
let eligibles = panier.filter(function(prix) {
return prix > 10
})
console.log(eligibles) // [12, 30]
// map : on applique la remise de 10% à chacun d'eux
let remises = eligibles.map(function(prix) {
return prix * 0.9
})
console.log(remises) // [10.8, 27]
// reduce : on additionne pour obtenir le montant remisé
let total = remises.reduce(function(somme, prix) {
return somme + prix
}, 0)
console.log(total) // 37.8
Tout ça, tu pourrais l'écrire avec un for et un compteur, le résultat serait identique. L'intérêt est
ailleurs. Le nom de l'opération raconte l'intention, là où une boucle t'oblige à lire tout le bloc pour la deviner.
Mais la vraie portée de ces notions dépasse largement le confort de lecture. Puisqu'un map traite chaque
élément sans se soucier des autres, rien n'oblige à les traiter l'un après l'autre, ni même sur la même machine. On
peut découper la liste en mille morceaux, les distribuer à mille ordinateurs, puis rassembler les résultats avec un
reduce. C'est exactement le modèle que Google a formalisé en 2004 sous le nom de MapReduce pour indexer le web, et
qui a donné naissance à tout l'écosystème du big data.
Une boucle classique, elle, est prisonnière de son ordre. Le tour numéro 2 suit le tour numéro 1, sur un seul
processeur. C'est toute la différence entre décrire une intention, qu'une machine peut réorganiser comme elle veut,
et dicter une marche à suivre, qu'elle doit exécuter au pied de la lettre.
Boucles et conditions : la base des algorithmes
Chaque langage propose ses propres variantes de boucles, avec des noms et des syntaxes différents : for...of
en JavaScript, foreach en PHP, les compréhensions de liste en Python, les
Stream en Java... Ne t'inquiète pas de tous les retenir : ce sont toujours les mêmes concepts
(parcourir, transformer, s'arrêter) habillés différemment.
Maintenant que tu comprends les concepts de boucles et de conditions, tu vas commencer à les voir partout dans ta
vie quotidienne. Quand l'équipe marketing demandera "envoie un email de rappel à tous les clients dont l'abonnement expire ce mois-ci",
tu reconnaîtras intuitivement une boucle et une condition.
À elles deux, ces notions suffisent déjà à écrire la
plupart des algorithmes.
C'est tout l'intérêt de ces deux chapitres, la traduction en code n'est plus qu'une formalité de syntaxe. Le vrai
travail consiste à découper un besoin en une suite d'étapes sans ambiguïté.
Reste une question : comment éviter que ces étapes ne s'empilent en un bloc illisible ? C'est le rôle des
fonctions, le sujet du chapitre suivant.