3. Conditions et boucles
Sections
Réglages de lecture
Aucune voix pour cette langue dans ce navigateur.
Plus de voix
Code et termes techniques en anglais
Désactivé : la voix de la page lit tout, sans pause entre deux voix.
Aucune voix pour cette langue dans ce navigateur.
Plus de voix
D’autres voix : Microsoft Edge propose des voix naturelles en ligne, et Windows (Paramètres › Heure et langue › Voix), macOS et Android en installent d’autres.
Code : les exemples examples/l03_*.cs, les extraits refusés compile_fail/l03_*.cs et les solutions exercises/l03_*.cs.
Jusqu’ici, les programmes exécutaient chaque ligne une fois, de haut en bas. Cette leçon change ça : une condition n’exécute certaines lignes que dans certains cas, et une boucle exécute des lignes plusieurs fois.
Les comparaisons et bool
Section intitulée « Les comparaisons et bool »Une comparaison pose une question dont la réponse est un bool : true (vrai) ou false (faux).
int fret = 12;
// Une comparaison donne un bool : true ou falseConsole.WriteLine(fret == 12);Console.WriteLine(fret != 12);Console.WriteLine(fret > 5 && fret < 10); // && : les deux doivent être vraiesConsole.WriteLine(fret < 1 || fret > 11); // || : au moins une doit être vraieConsole.WriteLine(!(fret > 5)); // ! : le contraireTrueFalseFalseTrueFalse| Opérateur | Sens | Opérateur | Sens | |
|---|---|---|---|---|
== |
égal à | && |
et | |
!= |
différent de | || |
ou | |
<, <= |
inférieur, ou égal | ! |
non | |
>, >= |
supérieur, ou égal |
Attention à ==, deux signes égal, qui compare, et à =, un seul signe, qui affecte. Les pages sur les opérateurs de comparaison et les opérateurs logiques booléens donnent toutes les règles. Le et et le ou s’arrêtent dès qu’ils connaissent la réponse : dans fret > 5 && fret < 10, quand la première comparaison est false, la seconde n’est même pas calculée.
if, else if, else
Section intitulée « if, else if, else »if exécute un bloc, les lignes entre accolades, seulement quand sa condition est true :
// if exécute un bloc seulement quand la condition est vraieif (fret == 0){ Console.WriteLine("Open string");}else if (fret == 12){ Console.WriteLine("One octave above the open string");}else{ Console.WriteLine("Somewhere else on the neck");}
// Les chaînes sont comparées par leur contenustring tuning = "E A D G B E";if (tuning == "E A D G B E"){ Console.WriteLine("Standard tuning");}
// Une variable déclarée dans un bloc n'existe que dans ce blocif (fret > 7){ int distance = fret - 7; Console.WriteLine($"{distance} frets above the 7th");}One octave above the open stringStandard tuning5 frets above the 7thLes conditions sont testées dans l’ordre, et seul le premier bloc dont la condition est true s’exécute. else attrape tous les autres cas. else if et else sont tous deux facultatifs.
La condition doit être un bool. Deux erreurs classiques sont refusées. Écrire un seul signe égal au lieu de deux :
int fret = 5;if (fret = 12){ Console.WriteLine("Octave");}l03_assign_in_if.cs(2,5): error CS0029: Cannot implicitly convert type 'int' to 'bool'l03_assign_in_if.cs(1,5): warning CS0219: The variable 'fret' is assigned but its value is never usedfret = 12 est une affectation, dont la valeur est l’int 12, pas un bool. Dans certains autres langages, ça compile et ça modifie fret en silence. Une chaîne n’est pas une condition non plus :
string answer = "yes";if (answer)l03_string_condition.cs(2,5): error CS0029: Cannot implicitly convert type 'string' to 'bool'Écris if (answer == "yes").
Une variable déclarée dans un bloc n’existe que dans ce bloc, et dans les blocs qu’il contient. Cette zone est sa portée :
int fret = 9;if (fret > 7){ int distance = fret - 7;}Console.WriteLine(distance);l03_out_of_scope.cs(6,19): error CS0103: The name 'distance' does not exist in the current contextPour utiliser distance après le if, déclare-la avant le if.
Quand une valeur est comparée à une liste de valeurs possibles, un switch se lit plus facilement qu’une suite de else if.
int semitone = 7;
// L'instruction switch : un case par valeur, chaque case se termine par breakswitch (semitone){ case 0: Console.WriteLine("Unison"); break; case 7: Console.WriteLine("Perfect fifth"); break; case 12: Console.WriteLine("Octave"); break; default: Console.WriteLine("Another interval"); break;}Perfect fifthL’instruction switch saute au case qui correspond, ou à default quand aucun ne correspond. Chaque section doit se terminer par break (ou return, leçon 4). En C et en JavaScript, un break oublié laisse le programme tomber dans le cas suivant ; C# le refuse :
switch (semitone){ case 7: Console.WriteLine("Perfect fifth"); case 12: Console.WriteLine("Octave"); break;}l03_fall_through.cs(4,5): error CS0163: Control cannot fall through from one case label ('case 7:') to anotherL’expression switch
Section intitulée « L’expression switch »Souvent, chaque cas ne fait que calculer une valeur. L’expression switch le dit en moins de lignes : la valeur, switch, puis une branche par cas, motif => résultat, séparées par des virgules.
// L'expression switch calcule une valeur : une branche par motif, _ correspond à tout le restestring name = semitone switch{ 0 => "C", 1 => "C#", 2 => "D", 3 => "D#", 4 => "E", 5 => "F", 6 => "F#", 7 => "G", 8 => "G#", 9 => "A", 10 => "A#", 11 => "B", _ => "not a semitone between 0 and 11",};Console.WriteLine($"Semitone {semitone} above C is {name}");
// Les motifs peuvent comparer : la première branche qui correspond l'emporteforeach (int fret in new[] { 0, 3, 7, 12, 17, 30 }){ string zone = fret switch { 0 => "open string", < 5 => "first position", < 12 => "middle of the neck", 12 => "octave", <= 24 => "high on the neck", _ => "no such fret", }; Console.WriteLine($"Fret {fret}: {zone}");}Semitone 7 above C is GFret 0: open stringFret 3: first positionFret 7: middle of the neckFret 12: octaveFret 17: high on the neckFret 30: no such fret- Les branches sont essayées de haut en bas, et la première qui correspond donne la valeur. La case 3 correspond à la branche
< 5, inférieur à 5, avant que la branche inférieur à 12 ne soit essayée. - Un motif (pattern) peut être une valeur, comme
12, ou une comparaison, comme< 5, inférieur à 5. La page sur les motifs montre les autres :and,or,not, et bien d’autres. _, le discard (l’élément ignoré), correspond à tout : c’est ledefaultd’une expression switch.- La boucle
foreach (int fret in new[] { … })exécute le bloc une fois pour chaque nombre ; les boucles arrivent juste après.
Sans _, une valeur qu’aucune branche ne reconnaît n’a rien à produire. Le compilateur avertit, et le programme échoue quand ça arrive :
int stringNumber = 7;
// Pas de branche pour 7, ni de branche _ : le compilateur avertit, et le programme échoue à l'exécutionstring open = stringNumber switch{ 1 => "E4", 2 => "B3", 3 => "G3", 4 => "D3", 5 => "A2", 6 => "E2",};Console.WriteLine(open);l03_switch_warning.cs(4,28): warning CS8509: The switch expression does not handle all possible values of its input type (it is not exhaustive). For example, the pattern '0' is not covered.Unhandled exception. System.Runtime.CompilerServices.SwitchExpressionException: Non-exhaustive switch expression failed to match its input.Unmatched value was 7. at <PrivateImplementationDetails>.ThrowSwitchExpressionException(Object unmatchedValue) at Program.<Main>$(String[] args) in C:\Users\spare\source\repos\learn\code\csharp-beginner\examples\l03_switch_warning.cs:line 4Un avertissement, contrairement à une erreur, n’empêche pas la compilation : le programme s’exécute, et ici il s’arrête sur une exception, une erreur qui se produit pendant l’exécution. Les lignes qui commencent par at forment la pile d’appels (stack trace) : où en était le programme. La dernière indique la ligne 4 du fichier. La leçon 8 porte sur les exceptions.
Deux choses à savoir sur les avertissements. D’abord, lis-les : celui-ci annonçait le plantage. Ensuite, dotnet run ne les affiche que quand il compile ; si tu exécutes à nouveau le même fichier inchangé, il ne compile pas, et l’avertissement ne s’affiche plus. dotnet clean l03_switch_warning.cs oublie le programme compilé, et l’exécution suivante affiche à nouveau l’avertissement. Le check.sh du cours le fait avant chaque exécution.
Les boucles
Section intitulée « Les boucles »Une boucle répète un bloc. C# a quatre instructions d’itération :
// while : répète tant que la condition est vraieint countdown = 3;while (countdown > 0){ Console.WriteLine($"{countdown}..."); countdown--; // comme countdown = countdown - 1}Console.WriteLine("Play!");
// for : départ ; condition ; pasfor (int fret = 0; fret <= 12; fret += 3){ Console.Write($"{fret} ");}Console.WriteLine();
// foreach : chaque élément d'une collection, ici chaque caractère d'une chaîneforeach (char letter in "EADGBE"){ Console.Write($"[{letter}]");}Console.WriteLine();
// break quitte la boucle, continue passe au tour suivantfor (int i = 1; i <= 10; i++){ if (i % 2 == 0) { continue; // saute les nombres pairs } if (i > 7) { break; // s'arrête après 7 } Console.Write($"{i} ");}Console.WriteLine();
// do-while : le bloc s'exécute au moins une fois, la condition est testée aprèsint tries = 0;do{ tries++; Console.WriteLine($"Try {tries}");} while (tries < 2);3...2...1...Play!0 3 6 9 12[E][A][D][G][B][E]1 3 5 7Try 1Try 2| Boucle | À utiliser quand | Teste la condition |
|---|---|---|
while (condition) |
tu ne sais pas à l’avance combien de tours | avant chaque tour |
do { … } while (condition); |
le bloc doit s’exécuter au moins une fois | après chaque tour |
for (start; condition; step) |
tu comptes : de 0 à 12, de 3 en 3 | avant chaque tour |
foreach (type item in collection) |
tu parcours chaque élément d’une collection | — |
countdown--retire 1,i++ajoute 1, etfret += 3ajoute 3 : des formes courtes decountdown = countdown - 1et defret = fret + 3.- Un
fora trois parties séparées par des points-virgules : ce qu’il faut faire une fois au début (int fret = 0), la condition testée avant chaque tour (fret <= 12), et ce qu’il faut faire après chaque tour (fret += 3). Sa variablefretn’existe que dans la boucle. foreachprend chaque élément à tour de rôle. Une chaîne est une collection dechar; les tableaux et les listes, de la leçon 4, sont aussi des collections.continuesaute le reste du bloc et commence le tour suivant ;breakquitte la boucle immédiatement.
Un while dont la condition ne devient jamais false tourne sans fin. Si un programme semble bloqué, appuie sur Ctrl+C dans le terminal pour l’arrêter.
Des boucles dans des boucles : le manche
Section intitulée « Des boucles dans des boucles : le manche »Une boucle peut contenir une autre boucle. Pour chaque corde, la boucle intérieure parcourt chaque case. Ce programme affiche les notes des cinq premières cases d’une guitare en accordage standard, le Tuning.Default de Guitar Alchemist :
// Les cinq premières cases d'une guitare en accordage standard (E2 A2 D3 G3 B3 E4, Tuning.Default dans GA).// Chaque note est un nombre de demi-tons : C vaut 0, C# vaut 1, ... B vaut 11, et le C suivant revient à 12.for (int stringNumber = 6; stringNumber >= 1; stringNumber--){ // Demi-tons de la corde à vide au-dessus de C int open = stringNumber switch { 6 => 4, // E 5 => 9, // A 4 => 2, // D 3 => 7, // G 2 => 11, // B _ => 4, // E (corde 1) };
Console.Write($"String {stringNumber}:"); for (int fret = 0; fret <= 5; fret++) { int semitone = (open + fret) % 12; // % 12 ramène 12 à 0 : l'octave string name = semitone switch { 0 => "C", 1 => "C#", 2 => "D", 3 => "D#", 4 => "E", 5 => "F", 6 => "F#", 7 => "G", 8 => "G#", 9 => "A", 10 => "A#", _ => "B", }; Console.Write($" {name,-2}"); } Console.WriteLine();}String 6: E F F# G G# AString 5: A A# B C C# DString 4: D D# E F F# GString 3: G G# A A# B CString 2: B C C# D D# EString 1: E F F# G G# ALes guitaristes numérotent les cordes de la plus aiguë, la corde 1, à la plus grave, la corde 6 ; la boucle extérieure compte à rebours pour que la corde 6 s’affiche en premier. Sur la corde de si (B), la case 1 donne (11 + 1) % 12, c’est-à-dire 0 : C, un do. L’opérateur % fait tourner les nombres comme les heures sur une horloge, et c’est exactement ainsi que les noms de notes reviennent à chaque octave. Le cours de théorie musicale va beaucoup plus loin avec la même idée.
Les mêmes notes sur le manche : chaque ligne est une corde, la corde 6 en haut comme dans la sortie, et chaque colonne est une case. La case 0 est la corde à vide, à gauche du sillet, la barre d’où partent les cordes ; le C mis en évidence est la case 1 de la corde de si.
Lire jusqu’à la bonne réponse
Section intitulée « Lire jusqu’à la bonne réponse »Une boucle while peut redemander une saisie jusqu’à obtenir ce qu’il lui faut :
// Devine la case : la boucle lit des réponses jusqu'à la bonneint secret = 7;int tries = 0;bool found = false;
while (!found){ Console.Write("Which fret? "); string? line = Console.ReadLine(); if (line == null) { Console.WriteLine("No more input."); break; } if (!int.TryParse(line, out int guess)) { Console.WriteLine($"\"{line}\" is not a number."); continue; }
tries++; if (guess < secret) { Console.WriteLine("Higher."); } else if (guess > secret) { Console.WriteLine("Lower."); } else { found = true; Console.WriteLine($"Found in {tries} tries."); }}En tapant 12, five, 5 et 7 :
Which fret? 12Lower.Which fret? five"five" is not a number.Which fret? 5Higher.Which fret? 7Found in 3 tries.continue revient à la question sans compter la mauvaise réponse comme un essai. Quand l’entrée se termine, ReadLine renvoie null et break quitte la boucle : sans ça, un programme dont l’entrée vient d’un fichier, comme dans la CI, poserait la question à l’infini. Au clavier, Ctrl+Z puis Entrée sous Windows, ou Ctrl+D sous Linux et macOS, termine l’entrée.
Trouver une erreur avec le débogueur
Section intitulée « Trouver une erreur avec le débogueur »Quand un programme donne un résultat faux, il faut voir ce qu’il fait, ligne par ligne. Le moyen le plus simple est d’ajouter des appels à Console.WriteLine qui affichent les variables, de relancer, puis de les retirer. Ça marche partout.
Un débogueur fait mieux : il met le programme en pause sur une ligne que tu choisis, un point d’arrêt, montre la valeur de chaque variable, et te laisse exécuter une ligne à la fois. Les trois éditeurs de la leçon 1 en ont un :
| Action | Visual Studio Code | Visual Studio | Rider |
|---|---|---|---|
| Poser ou retirer un point d’arrêt | F9, ou clic à gauche du numéro de ligne | F9 | clic à gauche de la ligne |
| Démarrer avec le débogueur | F5 | F5 | l’icône en forme d’insecte |
| Exécuter la ligne suivante | F10 (pas à pas principal) | F10 | step over |
| Entrer dans une méthode appelée sur cette ligne | F11 (pas à pas détaillé) | F11 | step into |
| Continuer jusqu’au point d’arrêt suivant | F5 | F5 | resume |
Essaie sur le programme du manche : pose un point d’arrêt sur la ligne int semitone = (open + fret) % 12;, démarre avec le débogueur, et regarde stringNumber, open et fret dans le panneau Variables ou Locals. Chaque fois que tu continues, le programme s’arrête à nouveau à la case suivante. Tu peux aussi taper une expression comme (open + fret) % 12 dans le panneau Watch (espion) pour voir sa valeur.
Les touches de ce tableau sont les raccourcis par défaut de chaque éditeur sous Windows et Linux ; sous macOS, le jeu de raccourcis de Rider est différent. Voir le débogage dans VS Code, le débogueur de Visual Studio pour débutants, et le débogage dans Rider. À vérifier : je n’ai pas encore débogué ces exemples dans chacun des trois éditeurs, ni comme applications basées sur un fichier, ni dans un projet créé avec dotnet new console.
À retenir
Section intitulée « À retenir »- Une comparaison donne un
bool; et (&&), ou (||) et non (!) les combinent. Deux signes égal comparent, un seul signe égal affecte. if,else ifetelseexécutent le premier bloc dont la condition esttrue; une variable déclarée dans un bloc n’existe que dans ce bloc.- Une instruction
switchdemande unbreakà la fin de chaque cas ; une expressionswitchcalcule une valeur, essaie ses branches de haut en bas, et devrait se terminer par_. whilerépète tant qu’une condition est vraie,forcompte,foreachparcourt chaque élément d’une collection ;breakquitte la boucle etcontinuepasse au tour suivant.- Un avertissement n’empêche pas la compilation, mais annonce souvent un problème ; une exception non gérée arrête le programme et affiche où elle s’est produite.
- Un débogueur s’arrête sur les points d’arrêt et montre les variables, une ligne à la fois.
Exercices
Section intitulée « Exercices »- Affiche les nombres de 1 à 15 sur une ligne, mais affiche
Fizzà la place des multiples de 3,Buzzà la place des multiples de 5, etFizzBuzzpour les multiples des deux. Indice : une expression switch peut regarder deux valeurs à la fois, écrites(a, b) switch.
Solution
exercises/l03_ex_fizzbuzz.cs :
// Exercice 1 : FizzBuzz de 1 à 15, avec une expression switch sur deux restesfor (int i = 1; i <= 15; i++){ string text = (i % 3, i % 5) switch { (0, 0) => "FizzBuzz", (0, _) => "Fizz", (_, 0) => "Buzz", _ => i.ToString(), }; Console.Write($"{text} ");}Console.WriteLine();1 2 Fizz 4 Buzz Fizz 7 8 Fizz Buzz 11 Fizz 13 14 FizzBuzz(i % 3, i % 5) regroupe les deux restes dans un tuple, et chaque branche teste les deux. (0, _) veut dire « divisible par 3, quel que soit le second reste ». L’ordre compte : (0, 0) doit venir en premier, sinon 15 afficherait Fizz. Avec if, tu écrirais d’abord if (i % 3 == 0 && i % 5 == 0), puis else if (i % 3 == 0), et ainsi de suite.
- Demande un nom de note (
C,C#, …B) et affiche les 13 notes de la gamme chromatique à partir de cette note, jusqu’à la même note une octave plus haut. Affiche un message si la note est inconnue.
Solution
exercises/l03_ex_chromatic.cs :
// Exercice 2 : lire un nom de note et afficher les 13 notes de la gamme chromatique à partir d'elle, jusqu'à son octaveConsole.Write("Starting note? ");string? input = Console.ReadLine();
int start = input switch{ "C" => 0, "C#" => 1, "D" => 2, "D#" => 3, "E" => 4, "F" => 5, "F#" => 6, "G" => 7, "G#" => 8, "A" => 9, "A#" => 10, "B" => 11, _ => -1,};
if (start == -1){ Console.WriteLine($"Unknown note: {input}");}else{ for (int step = 0; step <= 12; step++) { string name = ((start + step) % 12) switch { 0 => "C", 1 => "C#", 2 => "D", 3 => "D#", 4 => "E", 5 => "F", 6 => "F#", 7 => "G", 8 => "G#", 9 => "A", 10 => "A#", _ => "B", }; Console.Write($"{name} "); } Console.WriteLine();}Avec A :
Starting note? AA A# B C C# D D# E F F# G G# ALa première expression switch transforme le nom en nombre, avec -1 pour dire « inconnue » : une expression switch fonctionne aussi sur des chaînes. La boucle tourne 13 fois, step de 0 à 12, et % 12 ramène au début les nombres au-dessus de 11. Taper deux fois les mêmes noms de notes n’est pas idéal : la leçon 4 les range une seule fois, dans un tableau.
- Lis des temps d’entraînement en minutes, un par ligne, jusqu’à une ligne vide ou la fin de l’entrée. Ignore les lignes qui ne sont pas des nombres entiers ou qui sont négatives, en le signalant. Affiche ensuite le nombre de jours valides, le total en minutes, et le total en heures et minutes.
Solution
// Exercice 3 : additionner les minutes d'entraînement tapées une par ligne ; une ligne vide termine l'entréeint total = 0;int days = 0;while (true){ string? line = Console.ReadLine(); if (line == null || line == "") { break; } if (!int.TryParse(line, out int minutes) || minutes < 0) { Console.WriteLine($"Skipped: {line}"); continue; } total += minutes; days++;}Console.WriteLine($"{days} days, {total} minutes, {total / 60} h {total % 60} min");Avec les lignes 30, 45, forty, -5, 60, une ligne vide, puis 90 (input/l03_ex_total.txt), la sortie est :
Skipped: fortySkipped: -53 days, 135 minutes, 2 h 15 minwhile (true) ne s’arrête jamais tout seul : c’est le break à l’intérieur qui décide. Le 90 après la ligne vide n’est jamais lu. Dans !int.TryParse(line, out int minutes) || minutes < 0, le || ne calcule minutes < 0 que quand TryParse a réussi, donc minutes contient toujours la valeur lue au moment où on la compare.
- Instructions de sélection :
if,elseetswitch, l’expressionswitch, les motifs - Instructions d’itération :
for,foreach,doetwhile, instructions de saut :breaketcontinue - Opérateurs de comparaison, opérateurs logiques booléens
- Avertissement du compilateur CS8509,
SwitchExpressionException - Déboguer du C# dans VS Code, visite guidée du débogueur de Visual Studio, Rider : déboguer du code