Les bases de la programmation - Partie 4
Après avoir vu les fonctions conditionnelles dans la partie 3, et avant d'attaquer un nouveau programme complet tel que le MasterMind, il est désormais nécessaire d'attaquer les boucles. Extrêmement pratiques, et utilisées à longueur de temps en programmation, elles sont un point clé de la base de la programmation.
Comme les boucles utilisent les conditions pour fonctionner, il est nécessaire d'avoir bien compris l'article précédent avant d'attaquer cette partie. Sans être complexe, elle nécessite un tout petit peu plus, notamment, pour être sur de ne pas faire de boucles infinies qui pourraient bloquer votre navigateur. Rassurez vous, tous les navigateurs modernes intègrent un détecteur de boucle infinie, et il vous proposera de bloquer le programme, ce qu'il faudra faire.
Le principe des boucles est d'exécuter et de répéter un bloc d'actions, tant qu'une condition se vérifie.
Les deux types de boucles les plus connues sont la boucle for et la boucle while. La grande différence entre les deux, est que la boucle for contient dans son appel, l'initialisation de la variable utilisée pour la condition, la condition à tester, et la modification de la variable à chaque nouvelle boucle, alors que la boucle while ne contient que la condition. L'initialisation et très souvent, la modification aussi, sont à gérer "manuellement" en dehors de la fonction while.
La fonction for permet des boucles plus rapides et faciles à mettre en oeuvre, avec une utilisation très linéaire. La boucle while est plus permissive, laissant au développeur un plus grand champ d'action, mais le laissant aussi plus facilement des erreurs et lancer une boucle infinie.
Lors que l'on écrit une boucle dans un programme, la première chose à s'assurer, est qu'elle dipose d'une sortie, mais malgré tout, une erreur est toujours possible.
Jusqu'à maintenant, nous utilisions la fonction alert pour afficher des résultats. Mais avec les boucles, nous pouvons avoir de nombreux résultats successifs. Devoir à chaque fois cliquer sur OK pour fermer l'alerte peut rapidement devenir pénible, nous allons donc utiliser la console pour afficher les résultats que l'on souhaite.
Affichons donc la console
- sur Firefox : Menu Outil > Développement Web > Console web (cmd+option+K)
- sur Safari (en ayant débloquer le menu Développement dans les préférences > Avancé) : Menu Développement > Afficher la console Javascript (cmd+option+C)
La boucle for
Commençons et écrivons notre première boucle. Pour ce premier bout de code nous allons écrire un petit programme, qui ajoute 5 fois la même lettre à une chaine vide.
monTexte = ''; for (i = 0; i < 5; i++) {monTexte += 'a'; console.log(monTexte);}
Vous devriez voir 5 lignes indiquant
a aa aaa aaaa aaaaa
Dans la fonction for, nous voyons 3 instructions :
- i=0; indique l'initialisation de la variable i à 0, cette action est effectuée une seule fois, avant de vérifier la condition, et d'exécuter le contenu de la boucle
- i<5; indique la condition de sortie de la boucle, elle est exécutée avant d'exécuter le contenu de la boucle
- i++; indique la modification de i, à chaque nouvelle boucle. Elle est exécutée à la fin de la boucle, avant la vérification de la condition de la prochain boucle.
Regardons comment réagit la boucle lorsque nous la modifions.
monTexte = ''; for (i = 2; i < 5; i++) {monTexte += 'a'; console.log(monTexte);}
Ici nous commençons l'incrémentation de i à 2, en allant jusqu'à 5, nous aurons donc 3 étapes, et finalement, dans la console, le résultat
a aa aaa
Boucles vides et infinies
Maintenant, que ce passe-t-il si nous commençons notre boucle à 6 ? Que pensez vous qu'il va se passer ? Rien ? Une boucle infinie ? Je vous propose de le tester
monTexte = ''; for (i = 6; i < 5; i++) {monTexte += 'a'; console.log(monTexte);}
Tout simplement, il ne va rien se passer, car au début de la première boucle, i est initialisé à 6. Lors de la première boucle, le for vérifie que la variable i est bien inférieure à 5 afin d'exécuter son contenu, la condition n'est donc pas remplie, la sortie de la boucle est immédiate.
Pour réaliser une boucle infinie, il faut que la condition se vérifie indéfiniment toujours. Par exemple en décrémentant i plutôt qu'en l'incrémentant. Ainsi i en partant de 0, au lieu d'aller vers le 5, va s'en éloigner en s'enfonçant dans des valeurs négatives.
monTexte = ''; for (i = 0; i < 5; i--) {monTexte += 'a'; console.log(monTexte);}
Attention, si vous lancez ce code, notre navigateur va afficher énormément de lignes dans la console avant de s'arrêter. Le seule intérêt est de connaitre la limite avant laquelle le navigateur stoppe toute action.
La boucle while
Regardons maintenant comment réaliser les mêmes actions avec la boucle while
monTexte = ''; i = 0; while (i < 5) {monTexte += 'a'; console.log(monTexte); i++;}
Comme vous pouvez le voir, nous retrouvons les mêmes éléments, à savoir l'initialisation de i, la condition, et la modification de la valeur i. Elles sont cependant présentées différemment. A noter que la modification se trouve bien dans les actions effectuées dans la boucle, sinon elle ne sera jamais effectuée, et vous vous retrouverez une nouvelle fois dans une boucle infinie.
L'intérêt de la boucle while est que l'on peut se passer de la variable i, et vérifier la condition sur la longueur de la variable monTexte.
monTexte = ''; while (monTexte.length < 5) {monTexte += 'a'; console.log(monTexte);}
La boucle while permet aussi de faire des vérifications plus complexes, avec par exemples 2 variables.
monTexte = ''; i = 4; j = 154; while (i < 12 && j > 132) {i++; j--; console.log('i = '+i+' - j = '+j);}
Ici nous avons deux conditions qui doivent être remplies. Vous pouvez aussi tester en mettant un "or" à la place du "and". Vous verrez que vous aurez beaucoup plus de lignes de résultat, puisque j mettra quelques boucles de plus à ne plus être vérifiée.
Le parcours de liste
La boucle for est la championne du parcours de liste, en effet, elle est extrêmement facile à mettre en oeuvre pour en obtenir les données. Regardons comment afficher dans la console le contenu d'une liste.
maListe = new Array('pomme', 'poire', 'peche', 'banane', 'prune'); for (i = 0; i<5; i++) {console.log(maListe[i]);}
Que se passe-t-il si nous ne connaissons pas la longueur de notre liste ? Il faut que la condition d'adapte en toute circonstance. Au lieu d'indiquer une valeur de 5, finalement très arbitraire, pourquoi ne pas utiliser la valeur de la longueur de la chaine telle que nous l'avions vu dans l'article 2.
maListe = new Array('pomme', 'poire', 'peche', 'banane', 'prune'); for (i = 0; i<maListe.length; i++) {console.log(maListe[i]);}
Interruption de boucles
Conservons notre liste, et voyons comment nous pouvons interrompre la boucle. Imaginons que nous souhaitions que la boucle s'arrête si l'on rencontre un fruit ne commençant pas par la lettre p.
maListe = new Array('pomme', 'poire', 'peche', 'banane', 'prune'); for (i = 0; i<maListe.length; i++) {if (maListe[i].substring(0,1) != 'p')}break;console.log(maListe[i]);
Le programme affiche bien les 3 premiers fruits, puis s'arrête en arrivant sur "banane".
Dès le break est appelé, peut importe ce qu'il se passe ensuite, cela ne sera jamais exécuté.
Plus subtile, nous souhaitons n'afficher que les fruits qui commencent par p, mais sans s'arrêter sur un fruit ne remplissant pas cette condition.
maListe = new Array('pomme', 'poire', 'peche', 'banane', 'prune'); for (i = 0; i<maListe.length; i++) {if (maListe[i].substring(0,1) != 'p')}continue;console.log(maListe[i]);
Le programme nous affiche les 4 fruits commençant par p.
La commande continue indique au programme qu'il ne faut pas continuer l'exécution de la boucle courante, en passant directement à la suivante.
A noter que nous pourrions légèrement optimiser ce code en effectuant
maListe = new Array('pomme', 'poire', 'peche', 'banane', 'prune'); for (i = 0; i<maListe.length; i++) {if (maListe[i].substring(0,1) == 'p')}console.log(maListe[i]);
Ici nous faisons l'inverse, nous n'affichons la console que si le fruit commence par un p. Le résultat est le même, mais la condition et son utilisation sont inversées.
Cependant, avec ce code là, je n'aurai pas pu vous présenter la fonction continue.
Des boucles imbriquées
Comme nous avions pu faire des tests dans d'autres tests dans l'article 3, nous pouvons aussi imbriquer les boucles les unes dans les autres. Il faut cependant faire attention que les variables et les conditions ne se mélangent pas ... sauf si cela est souhaité.
for (i = 0; i < 2; i++) {for (j = 4; j < 6; j++) {}console.log('i = '+i+' - j = '+j);}
Vous devriez avoir le résultat
i = 0 - j = 4 i = 0 - j = 5 i = 1 - j = 4 i = 1 - j = 5
J'ai volontairement fait un nombre très limité de boucles, car comme vous pouvez le voir, c'est multiplicatif. Vous pouvez le remarquer aussi, nous utilisons une autre variable pour la seconde boucle, afin d'éviter que les deux ne se confondent.
Très important, la seconde boucle s'exécute complètement à chaque itération de la première. L'ordre d'imbrication dans lequel sont placées les boucles est donc très important.
Regardons un cas un peu plus concret d'utilisation d'une boucle dans une boucle.
fruits = new Array('pomme', 'poire', 'peche', 'banane', 'prune'); preparations = new Array('tranches', 'sirop', 'compote'); for (i = 0; i < fruits.length; i++) {for (j = 0; j < preparations.length; j++) {}console.log (preparations[j] + ' de ' + fruits[i]);}
L'imbrication de boucles est la solution la plus simple pour gérer les matrices, regardons cela rapidement.
neo = [[1,2],[3,4,5],[6,7]]; for (i = 0; i < neo.length; i++) {for (j = 0; j < neo[i].length; j++) {}console.log('neo['+i+']['+j+'] = '+ neo[i][j]);}
Nous affichons l'ensemble des valeurs, et malgré le fait que la matrice ne soit pas parfaitement carrée, ni rectangulaire, nous adaptons la seconde boucle à la taille de la ligne en cours.
Pourquoi i et j ?
Vous vous demandez pourquoi je m'obstine à utiliser la variable i, puis la variable j, dans tous les exemples ?
C'est simplement historique, les premiers languages de programmation avaient des variables fixes à utiliser, il n'était pas possible de définir ses propres variables. Et les numériques étaient définis de i à n. C'est donc par habitude que cette manière de faire est restée pour l'ensemble de la communauté de développeurs. Mais cela n'est absolument pas une contrainte, vous pouvez, du moins avec Javascript, utiliser les noms de variables que vous souhaitez.
Comme indiqué en introduction, le prochain article nous permettra d'attaquer un nouveau programme. Après le MasterMind, il s'agira encore d'un jeu, car le côté ludique facilite l'apprentissage, et permet d'apprendre à gérer les interactions et nécessite un petit peu de réflexion pour définir lorsqu'un jeu est gagné ou perdu.