Archives Wild Code School
C#

2. Les boucles

Quête 17 sur 46 · 3 images archivées

Après cette quête, tu sauras itérer de plusieurs manières et seras capable de maîtriser le flux d'exécution de manière plus fine

Objectifs

  • Comprendre la notion d'itération
  • Écrire des conditions tant que
  • Écrire des conditions bornée
  • Contrôler le flux d'exécution des boucles avec break et continue

Ton spectacle de danse classique

Et 1, 2, 3, quaaaatre ... Cela fait maintenant quelque temps que tu t'es mis.e à la danse classique et, que tu le veuilles ou non, une représentation t'attend dans deux jours. Tu te sens fin prêt à enfiler ton tutu et assurer le spectacle, mais ton prof n'a pas l'air du même avis. Il aimerait que tu travailles jour et nuit pour que tu maîtrises bien la répétition des mouvements et la technicité qui leur est associée. Tu aimerais partir pour rentrer chez toi, mais ce prof (diabolique) t'assure avec véhémence que tu as encore besoin de beaucoup d'entraînement avant de pouvoir élancer ton corps avec légèreté et présence.

Ton prof te prend entre quatre yeux à la fin de l'entraînement pour te faire réviser - encore une fois - les bases de la danse et du rythme. Tu répètes, répètes, répètes et répètes encore une fois. La prof te fait danser les phases encore et encore ...

A force de répéter des mouvements, ton esprit divague vers des questions existentielles concernant la nature des répétitions, et ce qui les constitue fondamentalement.

Tu commences alors à construire dans ta tête un programme fait de répétitions et, pour te préparer au spectacle que tu vas donner avec ton équipe de danse, tu veux réviser les fondamentaux théoriques. Ce qui t'amène ici à lire ce présent document.

Une itération

Tu te souviens de ta prof qui te répétait sans cesse : "Encore une itération du pas, et cela quatre fois !". Cette phrase résonne dans ta tête alors que le trac commence à monter.

Mais au fait, c'est quoi, une itération ?

En mathématiques, une itération est le fait de répéter plusieurs fois un processus. Eh bien en programmation, le principe est exactement le même : une itération est le fait de répéter plusieurs fois la même opération. Tu as sûrement déjà entendu et utilisé le mot réitérer plusieurs fois dans ta vie. C'est un mot dont un des synonymes est répéter.

Attention, cependant : en programmation, une itération n'est pas à proprement parler une répétition. Il ne suffit pas de copier-coller un même morceau de code plusieurs fois pour réaliser une ou plusieurs itérations. Je sais, cela semble bizarre, mais il ne faut pas se mélanger les pinceaux : en programmation, des itérations sont réalisées dans des blocs particuliers adaptés à la répétition. Ecrire plusieurs fois le même code dans une fiche de code ne peut pas être appelé une itération. De plus, avoir deux fois le même code qui réalise la même tâche est une mauvaise pratique en programmation. Si tu vois du copier-coller quelque part, ce n'est pas une itération mais une répétition.

Je suppose que tu as déjà abordé le sujet des itérables, ou du moins des conteneurs ?

Oui, les itérables sont des conteneurs. Un conteneur est une suite de valeurs du même type, stockées de manière organisée.

Exactement ! Et si tu as compris le principe d'itération, tu devrais aussi comprendre pourquoi on peut appeler un conteneur un itérable. Puisqu'un conteneur est une suite de valeurs du même type, il est possible de répéter plusieurs fois la même action sur toutes les valeurs du conteneur. Dans ce cas-là, on réalise chaque opérant en itérant dans le conteneur. Ainsi, une même opération sera réalisée une fois sur chaque élément du conteneur.

Ainsi, une itération est une seule et même opération, réalisée plusieurs fois à la suite. C'est aussi simple que cela. Par exemple, en pseudo-code :

shell
Pour chaque pasDeDanse dans la suiteDeMouvements
    Exécuter(pasDeDanse)
FinPour

Le pseudo code ci-dessus te permet de danser chaque pas de danse compris dans une suite de mouvements, en itérant pas par pas dans la suite de mouvements.

Et alors, comment je fais pour en écrire en C# ?

Un bloc itératif

Un ensemble de mouvements est réalisé plusieurs fois de suite à chaque partie des musiques sur lesquelles tu déhanches ton corps. Une suite est décomposée mouvement par mouvement. Tu dois répéter plusieurs fois cette même suite pour satisfaire ton prof et le public qui te regardera. Tu dois bien distinguer quels mouvements appartiennent à quelles itérations.

Un bloc itératif contient une suite d'instructions réalisées plusieurs fois. Un bloc est entouré de deux apostrophes et précédé par un mot-clé précisant la nature du bloc. Grâce au mot-clé précédant le bloc, on sait quand et comment le code va s'exécuter à l'intérieur du bloc en question.

Il existe plusieurs types de blocs itératifs. Mais au lieu de parler de blocs itératifs, qui est une expression lourde, je vais parler de boucles. Une boucle est une structure de contrôle, c'est-à-dire qu'elle a la responsabilité de s'occuper du flux d'exécution d'un programme. Par conséquent, une condition est une structure de contrôle tout autant qu'une boucle. Une structure de contrôle redirige du code à un endroit ou l'autre, en fonction d'une condition.

Il existe deux types de boucles principaux:

  • Les boucles while

  • les boucles for

Ces deux types possèdent quelques déclinaisons, que nous allons étudier dans la quête courante.

Et on peut tout faire avec seulement deux structures de contrôle itératives ? J'aurais imaginé ça plus compliqué ...

while

La boucle while effectue une seule et même opération, si et seulement si une expression conditionnelle renvoie vrai. Par exemple :

shell
Tant que (laMusiqueEstIntense)
    JeTourneSurMoiMeme()
FinTantQue

Dans le pseudo code ci-dessus, on réalise une itération de l'opération tant que la musique est intense. L'opération est de tourner sur soi-même. En français, on peut représenter ça par: Tant que la musique est intense, je tourne sur moi-même.

C'est bien drôle, mais en C#, ça donne quoi ?

Trêve de plaisanteries, le mot-clé précédant un bloc itératif de type while est ... while. La syntaxe est la même que pour une condition : la boucle while s'exécute tant qu'une condition est satisfaite. La condition à satisfaire suit le mot-clé while entre parenthèses :

cs
while (theMusicIsIntense)
{
    ITurnAround();
}

Le code ci-dessus est la traduction du pseudo code précédent. Ce code est un peu simpliste, donc pas besoin de le détailler. En revanche, le code qui suit nécessite un peu plus de détails :

csharp
string[] moves = { "jump", "turn around", "done" };
bool movesAreDone = false;
int iterator = 0;
while (!movesAreDone)
{
    if (moves[iterator] == "done") 
    {
        movesAreDone = true;
    }
    else
    {
        iterator = i + 1;
        Console.WriteLine("I " + moves[iterator]);
    }
}
Console.WriteLine("Your movement set is done after" + iterator + " moves !");
  1. Un tableau moves contenant des pas de danse est déclaré. A ce tableau, on affecte une liste de mouvements

  2. Une variable booléenne movesAreDone est déclarée, puis on l'instancie en lui affectant la valeur false

  3. Une variable de type entier iterator est déclarée et on l'instancie en lui affectant la valeur 0.

  4. Tant que la variable movesAreDone vaut false:

    1. Si la variable du tableau moves à l'index iterator est égale à done, on affecte true à movesAreDone

    2. Sinon, on incrémente iterator de 1 pour que la prochaine valeur testée du tableau soit la suivante et on affiche le pas de danse que tu dois faire

  5. La boucle s'arrête lorsque tous les mouvements ont été réalisés

  6. "Your movement set is done after {valeur de iterator} moves !" est affiché pour te donner le nombre de mouvements de la suite de pas de danse

Le nombre d'itérations de la boucle ne changera qu'en fonction de la taille de l'ensemble de mouvements. Plus le tableau grandit, plus le nombre d'itérations augmente, et inversement. Le nombre d'opérations réalisées par rapport à la taille d'une entrée s'appelle la complexité algorithmique, notion que nous abstrayons pour le moment, mais qui est très utile à connaître pour savoir évaluer la vitesse des algorithmes que l'on écrit.

D'ailleurs, cette boucle a un problème : s’il n'y a pas "done" dans le tableau, que se passera- t-il ?

iterator sera affecté à une valeur plus grande que le nombre d'éléments du tableau ?

C'est cela. Par conséquent, on aura une erreur qui arrêtera le programme, car la zone mémoire à laquelle on cherche à accéder avec iterator n'a pas été allouée. Il s'agit d'une erreur de segmentation.

On ne connaît pas le nombre d'itérations à l'avance, à moins que l'on connaisse le contenu du tableau moves. C'est pour ces cas-là que l'on utilise une boucle while : lorsqu'on ne connaît pas le nombre d'itérations à l'avance.

Tu m'as parlé d'un deuxième type de boucle, non ?

↗
La boucle WHILE

Tutoriel

cs.utah.edu
↗
La boucle WHILE

Vidéo

youtube.com

for

La boucle for est une boucle bornée. Une boucle bornée est une boucle où l'on connaît le nombre d'itérations à l'avance, avant l'exécution de celle-ci. C'est une boucle qui itère à partir d'un début, jusqu'à une fin. Par exemple, en pseudo code:

shell
Pour i allant de 1 à 10 par pas de 1
    Ecrire(i)
FinPour

Cette boucle affiche successivement à l'écran tous les entiers naturels appartenant à l'intervalle [1; 10].

Comment j'écris ça en C# ?

La syntaxe de la boucle for est légèrement plus complexe que les autres. Etant donné que c'est une boucle bornée (qui itère sur un ensemble de valeurs fini), il faut le spécifier dans ce qui précède la déclaration du bloc. Ainsi, une boucle bornée a:

  1. Une valeur de départ

  2. Une condition tant que

  3. Le pas avec lequel on incrémente la valeur de départ à chaque itération

Bon, un peu de code pour montrer tout ça :

cs
for (int i=0; i < 12; i++)
{
    Console.WriteLine("Number " + i);
}

La boucle ci-dessus affiche à l'écran des entiers allant de 0 à 11, chacun séparé par un saut de ligne. Décortiquons ce qui précède le bloc :

  1. Le mot-clé for qui spécifie que l'on utilise une boucle bornée

  2. L'instanciation d'une variable i à 0 <= valeur de départ

  3. Une expression booléenne qui asserte que i est strictement inférieur à 12 <= condition tant que

  4. i++: équivalent de i = i + 1. Plus concis que l'affectation complète. A chaque tour de boucle, cette affectation est réalisée. <= pas avec lequel on incrémente à chaque tour de boucle

Tu sais à peu près tout ce qu'il faut savoir sur la boucle for maintenant.

Mais attends, tu m'as dit qu'il existait des déclinaisons des deux types de boucles ?

↗
La boucle FOR

Lire jusque "Infinite For loop"

tutorialsteacher.com
↗
La boucle FOR

Regarder à partir de 1:54

youtube.com

foreach

Une troisième boucle est très utile (et utilisée) en programmation. Il s'agit d'une boucle bornée, mais qui itère sur un conteneur. Je m'explique : dans la boucle for standard que l'on a vue à l'étape précédente, il fallait que tu spécifies une valeur de départ, une condition "tant que" et un pas d'incrémentation. La syntaxe est assez lourde, surtout si tu connais la taille de ton conteneur. Et comme tu peux le voir, ce n'est pas très facile à lire. Il y a une manière plus simple et concise d'itérer sur un ensemble d'éléments, il s'agit de la boucle foreach.

En pseudo code:

shell
Pour chaque élément dans un tableau
    Ecrire(élément)
FinPour

La boucle ci-dessus affiche tous les éléments d'un tableau. A chaque itération correspond l'élément suivant dans le conteneur, et la boucle s'arrête lorsqu'elle rencontre la fin du tableau. Plutôt pratique, non ?

Cool, et comment j'écris ça ?

csharp
string[] moves = { "jump", "turn around", "turn around", "jump" };
foreach (String moveToDo in moves)
{
    Console.WriteLine("I " + moveToDo);
}

Cette boucle est adaptée au cas où tu dois itérer sur un conteneur. Ici, on itère sur un tableau contenant des pas de danse. De plus, pas besoin de "done" à l'intérieur du tableau pour signifier la fin de ton ensemble de mouvements. Par conséquent, la syntaxe de la boucle est beaucoup plus simple et lisible que celle d'une boucle for classique, pour la même opération. Voici l'équivalent du code précédent avec une boucle for classique :

csharp
string[] moves = { "jump", "turn around", "turn around", "jump" };
for (int i=0; i < moves.Length; i++)
{
   Console.WriteLine("I " + moves[i]);
}

Tu avoueras quand même que la version foreach est un tantinet plus agréable à lire et qu'elle convient bien mieux au cas où l'on veut itérer sur un conteneur.

↗
La boucle FOREACH
geeksforgeeks.org

break & continue

Dans certains cas, il y a des exceptions dans tes suites de pas de danse. Des fois, tu exécutes trois fois une suite de pas, mais à la dernière, tu t'arrêtes à l'avant-dernier pas pour embrayer sur une autre suite de pas. Parfois, tu ne dois pas réaliser un pas de danse si c'est la troisième suite. Deux mots-clés te permettent de contrôler le flux d'exécution d'une boucle pendant son exécution. Il s'agit des deux mots-clés break et continue.

Avant de commencer, j'aimerais que tu réfléchisses quelques minutes à la question suivante. Avec tout ce que tu as appris sur les conditions et les boucles, comment écrirais-tu une boucle infinie ? C'est-à-dire une boucle avec un nombre infini d'itérations ?

while (true) ?

Exactement ! while(true) est une boucle qui s'exécutera à l'infini, car le résultat de l'expression booléenne qui est évaluée en tant que condition de cette boucle sera toujours vrai. Les boucles infinies sont surtout utilisées dans le jeu vidéo, pour créer ce que l'on appelle une boucle de jeu.

Mais il existe un problème. Tu sais comment écrire une boucle infinie, mais sais-tu comment l'arrêter ?

Fermer l'application ? Appuyer sur CTRL-C ?

Oui, ces méthodes marchent, mais parce qu'il y a un événement système qui vient modifier le cours des choses. Et en faisant ça, ton programme s'arrête, tout simplement : il perd son environnement mémoire. Dans certains cas, lorsque l'on sort d'une boucle infinie, on veut réaliser d'autres opérations : détruire des objets, sauvegarder la partie et j'en passe ...

Afin de sortir d'une structure de contrôle itérative, un mot-clé est mis à ta disposition et il s'agit du mot-clé break. Si tu places break à l'intérieur d'une boucle, la boucle s'arrêtera et l'exécution continuera comme si de rien n'était.

Exemple:

cs
while (true)
{
    if (ShowIsOver())
    {
        break;
    }
    Dance();
}

Le mot-clé continue termine une seule itération, tandis que break termine la boucle tout entière. En clair, continue passe à l'itération suivante sans effectuer les opérations le succédant. Ainsi :

cs

for (int doneMoveSetsCount=0; doneMoveSetsCount < completeDance.Length; doneMoveSetsCount++)
{
    string[] moveSet = completeDance[doneMoveSetsCount];
    foreach (string move in moveSet)
    {
        if ((doneMoveSetsCount % 5 == 0) && move == "turn around")
        {
            Console.WriteLine("I jump");
        }
        else
        {
            Console.WriteLine("I " + move);
        }
    }
}

Attention, ces mots-clés sont très utiles, mais en abuser complexifie énormément un programme. Ces mots-clés doivent être utilisés avec parcimonie et de manière justifiée. Si tu peux éviter de les utiliser, évite. Par contre, si le cas te force ou s'il serait beaucoup trop compliqué de faire différemment, alors tu as le droit d'utiliser un des deux.

↗
Break & continue

Lire la réponse acceptée

stackoverflow.com

Don't repeat yourself !

Il existe, dans le consensus des programmeurs à l’échelle du monde, quelques principes à suivre afin d'écrire du bon code. Les mettre en pratique ne donne pas forcément du code propre, mais y contribue très fortement. Il est important de connaître...les importants.

Un principe de qualité logicielle ultra-important concerne la lisibilité et la maintenabilité du code. Les boucles sont un bon moyen de commencer à le mettre en pratique. Il s'agit du concept DRY pour Don't Repeat Yourself. En effet, il est très mal vu de se répéter inlassablement (copier-coller le même code plusieurs fois à des endroits différents), car cela altère la lisibilité du code, en plus d'impacter les futurs programmeurs qui n'imagineront pas que le même bug est répété plusieurs fois à des endroits différents du code.

Donc, utilise les boucles pour fournir du code lisible et unique. Souviens-toi que copier-coller (du code) est bien souvent une mauvaise pratique qui crée des bugs et de l'incompréhension dans l'équipe. Les boucles sont un allié précieux pour te permettre de commencer à écrire du code propre ! Alors, fais-en bon usage.

↗
Wikipédia sur le DRY

Wikipédia

en.m.wikipedia.org
↗
Article sur le DRY

Article

deviq.com

Challenge

Compose ta chorégraphie

Grâce à ce programme, tu vas pouvoir composer tes propres chorégraphies et même t'en souvenir !

  1. Crée un tableau moves de taille 32

  2. Une boucle itère en attendant que l'utilisateur rentre un pas de danse de type string. La boucle s'arrête lorsque la taille maximum est atteinte ou que la chaîne de caractères "done" est spécifiée.

    1. Ajouter dans le tableau moves chaque pas entré si il est différent de "done"
  3. L'utilisateur choisit ensuite le nombre d'itérations de la suite de pas

  4. La suite de pas est répétée autant de fois à l'écran que le nombre d'itérations entré par l'utilisateur

Critères de validation

  • Un lien vers ta solution sur GitHub
  • La solution itère autant de fois que spécifié par l'utilisateur
  • Chaque pas de danse est bien affiché à l'écran à chaque itération