proofread of chapters enum, foreach, switch, arithmetique by stephane Goujet

This commit is contained in:
Raphaël JAKSE 2015-04-21 09:01:52 +02:00
parent 64a8424110
commit 5059d96e91
7 changed files with 178 additions and 148 deletions

View File

@ -97,19 +97,19 @@
write("Second nombre : ");
readf(" %s", &second);
int result;
int resultat;
if (operation == 1) {
result = premier + second;
resultat = premier + second;
} else if (operation == 2) {
result = premier - second;
resultat = premier - second;
} else if (operation == 3) {
result = premier * second;
resultat = premier * second;
} else if (operation == 4) {
result = premier / second;
resultat = premier / second;
} else {
writeln(
@ -118,7 +118,7 @@
break;
}
writeln(" Résultat : ", result);
writeln(" Résultat : ", resultat);
}
}
>>>]

View File

@ -2,6 +2,7 @@
title = "Les énumérations ([c enum])"
translator = "Raphaël Jakse"
partAs = correction
proofreader = "Stéphane Goujet"
]
[code=d <<<
@ -56,10 +57,10 @@
double second;
double resultat;
write(" premier operand? ");
write("Premier opérande ? ");
readf(" %s", &premier);
write("Second operand? ");
write("Second opérande ? ");
readf(" %s", &second);
switch (operation) {
@ -85,7 +86,7 @@
"ERREUR: Cette ligne n'aurait jamais dû être atteinte.");
}
writeln(" resultat: ", resultat);
writeln(" resultat : ", resultat);
}
}
>>>]

View File

@ -2,26 +2,27 @@
title = "La boucle [c foreach]"
partAs = correction
translator = "Raphaël Jakse"
proofreader = "Stéphane Goujet"
]
Pour obtenir un tableau associatif qui marche dans le sens opposé de [c noms], les types des clés et des valeurs doivent être échangés. Le nouveau tableau associatif doit être de type [c int~[string~]].
En itérant sur les clés et les valeurs du tableau associatif original et en utilisant les clés comme les valeurs et les valeurs comme les clés, on peuple le tableau [c valeurs] :
En itérant sur les clés et les valeurs du tableau associatif original et en utilisant les clés comme les valeurs et les valeurs comme les clés, on peuple le tableau [c valeurs]~ :
[code=d <<<
import std.stdio;
void main()
{
string[int] noms = [ 1:"one", 7:"seven", 20:"twenty" ];
string[int] noms = [ 1:"un", 7:"sept", 20:"vingt" ];
int[string] valeurs;
foreach (key, valeur; noms) {
valeurs[valeur] = key;
foreach (cle, valeur; noms) {
valeurs[valeur] = cle;
}
writeln(valeurs["twenty"]);
writeln(valeurs["vingt"]);
}
>>>]

View File

@ -2,6 +2,7 @@
title = "[c switch] et [c case]"
partAs = correction
translator = "Raphaël Jakse"
proofreader = "Stéphane Goujet"
]
# [
@ -68,7 +69,7 @@
}
>>>]
]
# [ Comme la section [c default] est nécessaire pour lever l'exception, on ne peut plus utiliser le [c final switch]. Voici les parties modifiées du programme :
# [ Comme la section [c default] est nécessaire pour lever l'exception, on ne peut plus utiliser le [c final switch]. Voici les parties modifiées du programme~ :
[code=d <<<
// ...

View File

@ -2,13 +2,14 @@
title = "Les énumérations ([c enum])"
partAs = chapitre
translator = "Raphaël Jakse"
proofreader = "Stéphane Goujet"
]
[c enum] est la fonctionnalité qui permet de définir des valeurs de constantes nommées.
[c enum] est la fonctionnalité qui permet de définir des valeurs constantes nommées.
[ = Les Effets des constantes magiques sur la qualité du code
[ = Les effets des constantes magiques sur la qualité du code
Le code suivant apparaît dans les solutions des exercices du [[part:arithmetique | chapitre sur les opérations entières et arithmétiques] :
Le code suivant apparaît dans les solutions des exercices du [[part:arithmetique | chapitre sur les entiers et les opérations arithmétiques]~ :
[code=d <<<
if (operation == 1) {
@ -25,11 +26,11 @@
}
>>>]
Les littéraux entiers 1, 2, 3 et 4 dans ce bout de code sont appelés constantes magiques. Il n'est pas facile de déterminer à quoi correspond chacun de ces littéraux dans le programme. Le code de chaque bloc doit être examiné pour voir que 1 désigne l'ajouterition, 2 la soustraction, etc. Cette tâche est relativement aisée dans le code ci-avant parce que chaque bloc ne contient qu'une ligne. Il serait considérablement plus difficile de déchiffrer le sens des constantes magiques dans la plupart des autres programmes.
Les littéraux entiers 1, 2, 3 et 4 dans ce bout de code sont appelés constantes magiques. Il n'est pas facile de déterminer à quoi correspond chacun de ces littéraux dans le programme. Le code de chaque bloc doit être examiné pour voir que 1 désigne l'addition, 2 la soustraction, etc. Cette tâche est relativement aisée dans cet exemple parce que chaque bloc ne contient qu'une ligne. Il serait considérablement plus difficile de déchiffrer le sens des constantes magiques dans la plupart des autres programmes.
Les constantes magiques doivent être évitées parce qu'elle limitent les deux qualités les plus importantes des programmes : lisibilité et maintenabilité.
Les constantes magiques doivent être évitées parce qu'elle limitent les deux qualités les plus importantes des programmes~ : lisibilité et maintenabilité.
[c enum] permet de donner des noms à de telles constantes et donc de rendre le code plus lisible et maintainable. Chaque condition est immédiatement compréhensible quand les valeurs énumérées suivantes sont utilisées :
[c enum] permet de donner des noms à de telles constantes et donc de rendre le code plus lisible et maintainable. Chaque condition est immédiatement compréhensible quand les valeurs énumérées suivantes sont utilisées~ :
[code=d <<<
if (operation == Operation.ajouter) {
@ -46,7 +47,7 @@
}
>>>]
Le type énuméré [c Operation] ci-avant qui rend inutile le recours aux constantes magiques 1, 2, 3 et 4 peut être défini comme ceci :
Le type énuméré [c Operation] ci-dessus qui rend inutile le recours aux constantes magiques 1, 2, 3 et 4 peut être défini comme ceci~ :
[code=d <<<
enum Operation { ajouter = 1, soustraire, multiplier, diviser }
@ -55,13 +56,13 @@
[ = La syntaxe d'[c enum]
La forme la plus simple de la définition d'une énumération est la suivante :
La forme la plus simple de la définition d'une énumération est la suivante~ :
[code=d <<<
enum NomDuType { NomDeValeur_1, NomDeValeur_2, /* etc. */ }
>>>]
Il est parfois nécessaire de spécifier également le vrai type (le type de base) des valeurs :
Il est parfois nécessaire de spécifier également le vrai type (le type de base) des valeurs~ :
[code=d <<<
enum NomDuType : type_de_base { NomDeValeur_1, NomDeValeur_2, /* etc. */ }
@ -69,61 +70,61 @@
Nous verrons comment ceci peut être utilisé dans la section suivante.
[c NomDuType] définit la signification commune des valeurs. Toutes les valeurs d'un type énuméré sont listées entres accolades. Voici quelques exemples :
[c NomDuType] définit la signification commune des valeurs. Toutes les valeurs d'un type énuméré sont listées entres accolades. Voici quelques exemples~ :
[code=d <<<
enum TetesOuQueues { tete, queue }
enum Couleur { piques, ceurs, carreaux, trèfles }
enum PileOuFace { pile, face }
enum Couleur { pique, cœur, carreau, trèfle }
enum Voyageur { ordinaire, enfant, etudiant, senior }
>>>]
Chaque ensemble de valeur fait alors partie d'un type distinct. Par exemple, [c tetes] et [c queues] deviennent des valeurs du type [c TetesOuQueues]. Le nouveau type peut être utilisé comme n'importe quel type lors de la définition de variables :
Chaque ensemble de valeur fait alors partie d'un type distinct. Par exemple, [c pile] et [c face] deviennent des valeurs du type [c PileOuFace]. Le nouveau type peut être utilisé comme n'importe quel type lors de la définition de variables~ :
[code=d <<<
TetesOuQueues resultat; // initialisé à la valeur par défaut
auto tq = TetesOuQueues.tetes; // type inféré
PileOuFace resultat; // initialisé à la valeur par défaut
auto tq = PileOuFace.pile; // type inféré
>>>]
Comme nous l'avons vu dans les codes ci-avant, les valeurs des types énumérés sont toujours spécifiés par le nom de leur type :
Comme nous l'avons vu dans les codes précédents, les valeurs des types énumérés sont toujours spécifiées au moyen du nom de leur type~ :
[code=d <<<
if (resultat == TetesOuQueues.tetes) {
if (resultat == PileOuFace.pile) {
// ...
}
>>>]
]
[ = Valeurs réelles et types de base
Les valeurs des type énumérés sont normalement implémentés, en interne, par des valeurs [c int]. Dans d'autres termes, même si elles apparaissent comme des valeurs nommées comme [c tetes] et [c queues] dans le code, elles sont en réalité des valeurs [c int]. (Note : il est possible de choisir un autre type que [c int] lorsque c'est nécessaire).
Les valeurs des type énumérés sont normalement implémentées, en interne, par des valeurs [c int]. Autrement dit, même si elles apparaissent comme des valeurs nommées comme [c pile] et [c face] dans le code, elles sont en réalité des valeurs [c int]. (Note~ : il est possible de choisir un autre type que [c int] lorsque c'est nécessaire.)
Sauf dans le cas où c'est explicitement indiqué par le programmeur, les valeurs [c int] commencent par 0 et sont incrémentées de 1 pour chaque valeur énumérée. Par exemple, les deux valeurs de [c TetesOuQueues] ont les valeurs 0 et 1 :
Sauf dans le cas où c'est explicitement indiqué par le programmeur, les valeurs [c int] commencent par 0 et sont incrémentées de 1 pour chaque valeur énumérée. Par exemple, les deux valeurs de [c PileOuFace] ont les valeurs 0 et 1~ :
[code=d <<<
writeln("tetes vaut 0 : ", (TetesOuQueues.tetes == 0));
writeln("queues vaut 1 : ", (TetesOuQueues.queues == 1));
writeln("pile vaut 0 : ", (PileOuFace.pile == 0));
writeln("face vaut 1 : ", (PileOuFace.face == 1));
>>>]
La sortie :
La sortie~ :
[output <<<
tetes vaut 0 : true
queues vaut 1 : true
pile vaut 0 : true
face vaut 1 : true
>>>]
Il est possible de réinitialiser manuellement les valeurs à n'importe quel endroit. Cela a été le cas quand on a donnée la valeur 1 à [c Operation.ajouter]. L'exemple suivant réinitialise les valeurs deux fois :
Il est possible de réinitialiser manuellement les valeurs à n'importe quel endroit. Cela a été le cas quand on a donné la valeur 1 à [c Operation.ajouter]. L'exemple suivant réinitialise les valeurs deux fois~ :
[code=d <<<
enum Test { a, b, c, ç = 100, d, e, f = 222, g, ğ }
writefln("%d %d %d", Test.b, Test.ç, Test.ğ);
>>>]
La sortie :
La sortie~ :
[output <<<
1 100 224
>>>]
Si [c int] n'est pas adapté en tant que type de base des valeurs énumérées, le type de base peut être indiqué de façon explicite après le nom de l'énumération :
Si [c int] n'est pas adapté en tant que type de base des valeurs énumérées, le type de base peut être indiqué de façon explicite après le nom de l'énumération~ :
[code=d <<<
enum ConstanteNaturelle : double { pi = 3.14, e = 2.72 }
@ -131,17 +132,17 @@
>>>]
]
[ = Les valeurs énumérées ne sont pas d'un type énuméré
[ = Des valeurs énumérées sans type énuméré
Nous avons vu qu'il été important d'éviter les constantes magiques et qu'il vaut mieux se servir des énumérations.
Nous avons vu qu'il était important d'éviter les constantes magiques et qu'il valait mieux se servir des énumérations.
Cependant, parfois, il peut ne pas être naturel d'utiliser les noms des types énumérés pour simplement utiliser des constantes nommées. Supposons que l'on ait besoin de représenter le nombre de secondes par jours. Il ne devrait pas être nécessaire de définir également un type énuméré pour cette constante. Tout ce dont on a besoin est une valeur constante à laquelle on peut se référer par son nom. Dans de tels cas, le type de l'énumération et les accolades ne sont pas écrits :
Cependant, parfois, il peut ne pas être naturel d'utiliser les noms des types énumérés pour simplement utiliser des constantes nommées. Supposons que l'on ait besoin de représenter le nombre de secondes par jours. Il ne devrait pas être nécessaire de définir également un type énuméré pour cette constante. Tout ce dont on a besoin est une valeur constante à laquelle on peut se référer par son nom. Dans de tels cas, le type de l'énumération et les accolades ne sont pas écrits~ :
[code=d <<<
enum secondesParJour = 60 * 60 * 24;
>>>]
Le type de la valeur peut être spécifiée explicitement, ce qui est nécessaire si le type ne peut pas être inféré depuis la valeur :
Le type de la valeur peut être spécifié explicitement, ce qui est nécessaire si le type ne peut pas être inféré depuis la valeur~ :
[code=d <<<
enum int secondesParJour = 60 * 60 * 24;
@ -153,7 +154,7 @@
nombreTotalDeSecondes = nombreDeJours * secondesParJour;
>>>]
[c enum] peut également être utilisé pour définir des constantes nommées d'autres types. Par exemple, le type de la constante suivante est [c string] :
[c enum] peut également être utilisé pour définir des constantes nommées d'autres types. Par exemple, le type de la constante suivante est [c string]~ :
[code=d <<<
enum nomDeFichier = "list.txt";
@ -163,26 +164,26 @@
[ = Propriétés
Les propriétés [c .min] et [c .max] sont les valeurs minimale et maximale du type énuméré. Quand les valeurs du type énuméré sont consécutives, elles peuvent être itérées dans une boucle [c for] entre ces limites :
Les propriétés [c .min] et [c .max] sont les valeurs minimale et maximale du type énuméré. Quand les valeurs du type énuméré sont consécutives, elles peuvent être itérées dans une boucle [c for] entre ces limites~ :
[code=d <<<
enum Couleur { piques, cœurs, carreaux, trèfles }
enum Couleur { pique, cœur, carreau, trèfle }
for (auto couleur = Couleur.min; couleur <= Couleur.max; ++couleur) {
writefln("%s : %d", couleur, couleur);
}
>>>]
Les indicateurs de format [c "%s"] et [c "%d"] produisent des sorties différentes :
Les indicateurs de format [c "%s"] et [c "%d"] produisent des sorties différentes~ :
[output <<<
piques : 0
cœurs : 1
carreaux : 2
trèfles : 3
pique : 0
cœur : 1
carreau : 2
trèfle : 3
>>>]
Notez qu'une boucle [c foreach] sur cet intervalle ne considérerait pas la valeur [c .max] :
Notez qu'une boucle [c foreach] sur cet intervalle ne considérerait pas la valeur [c .max]~ :
[code=d <<<
foreach (couleur; Couleur.min .. Couleur.max) {
@ -190,34 +191,34 @@
}
>>>]
La sortie :
La sortie~ :
[output <<<
piques : 0
cœurs : 1
carreaux : 2
← il manque les trèfles
pique : 0
cœur : 1
carreau : 2
← il manque le trèfle
>>>]
]
[ = Conversion depuis le type de base
Comme nous l'avons vu dans la sortie formatée ci-dessus, une valeur énumérée peut être automatiquement convertie vers son type de base (par ex. vers [c int]). La conversion inverse n'est pas automatique :
Comme nous l'avons vu dans la sortie formatée ci-dessus, une valeur énumérée peut être automatiquement convertie vers son type de base (par ex. vers [c int]). La conversion inverse n'est pas automatique~ :
[code=d <<<
Couleur couleur = 1; // ← ERREUR de compilation
>>>]
La raison à cela est d'éviter de se retrouver avec des valeurs énumérées invalides :
La raison à cela est d'éviter de se retrouver avec des valeurs énumérées invalides~ :
[code=d <<<
couleur = 100; // ← cela serait une valeur énumérée invalide
>>>]
Les valeurs qui sont connues pour correspondre à des valeurs énumérées valides d'un type énuméré particulier peuvent quand même être converties vers ce type par un ''cast'' explicite :
Les valeurs que l'on sait pouvoir correspondre à des valeurs énumérées valides d'un type énuméré particulier peuvent quand même être converties vers ce type par un ''cast'' explicite~ :
[code=d <<<
suit = cast(Suit)1; // devient cœurs
couleur = cast(Couleur)1; // devient cœur
>>>]
Il est à la charge du programmeur de s'assurer de la validité des valeurs quand un ''cast'' explicite est utilisé. Nous verrons les conversions de type et les ''casts'' dans des chapitres ultérieurs.
@ -226,12 +227,12 @@
[ = Exercice
Modifiez le programme de calculatrice des exercices du [[part:arithmetique | chapitre sur les opérations entières et arithmétiques]] en faisant choisir à l'utilisateur l'opération arithmétique dans un menu.
Modifiez le programme de calculatrice des exercices du [[part:arithmetique | chapitre sur les entiers et les opérations arithmétiques]] en faisant choisir à l'utilisateur l'opération arithmétique dans un menu.
Ce programme doit différer du précédent par au moins ces points :
- Utilisez les valeurs énumérées, pas des constantes magiques.
- Utilisez [c double] à la place d'[c int]
- Utilisez une instruction switch à la place de la chaîne "if, else if, else".
Ce programme doit différer du précédent par au moins ces points~ :
- utilisez des valeurs énumérées, pas des constantes magiques~ ;
- utilisez [c double] à la place d'[c int]~ ;
- utilisez une instruction switch à la place de la chaîne «~ if, else if, else~ ».
[[part:corrections/enum | … La solution]]
]

View File

@ -2,11 +2,12 @@
title = "Boucle [c foreach]"
partAs = chapitre
translator = "Raphaël Jakse"
proofreader = "Stéphane Goujet"
]
Une des structures de contrôle les plus communes du D est la boucle [c foreach]. Elle est utilisée pour appliquer la même opération à tous les éléments d'un conteneur (ou d'un intervalle).
Les opérations qui sont appliquées aux éléments d'un conteneur sont très répandues en programmation. Nous avons vu dans le [[part:for | chapitre sur la boucle [c for]] que l'on peut accéder aux éléments d'un tableau dans une boucle [c for] par une valeur d'indice qui est incrémentée à chaque itération :
Les opérations qui sont appliquées aux éléments d'un conteneur sont très répandues en programmation. Nous avons vu dans le [[part:for | chapitre sur la boucle [c for]] que l'on peut accéder aux éléments d'un tableau dans une boucle [c for] par une valeur d'indice qui est incrémentée à chaque itération~ :
[code=d <<<
for (int i = 0; i != tableau.length; ++i) {
@ -14,14 +15,14 @@ Les opérations qui sont appliquées aux éléments d'un conteneur sont très r
}
>>>]
Voici les étapes d'une itération sur tous les éléments :
Voici les étapes d'une itération sur tous les éléments~ :
- Définir une variable compteur, souvent nommée [c i]
- Itérer la boucle jusqu'à la valeur de la propriété [c .length] du tableau
- incrémenter [c i]
- Accéder à l'élément
- définir une variable compteur, souvent nommée [c i]~ ;
- itérer la boucle jusqu'à la valeur de la propriété [c .length] du tableau~ ;
- incrémenter [c i]~ ;
- accéder à l'élément.
[c foreach] a essentiellement le même comportement mais simplifie le code en gérant ces étapes automatiquement :
[c foreach] a essentiellement le même comportement mais simplifie le code en gérant ces étapes automatiquement~ :
[code=d <<<
foreach (element; tableau) {
@ -29,16 +30,16 @@ Voici les étapes d'une itération sur tous les éléments :
}
>>>]
Une partie du pouvoir de [c foreach] vient du fait qu'elle peut être utilisée de la même manière indépendamment du type du conteneur. Comme nous l'avons vu dans le chapitre précédent, une manière d'itérer sur les valeurs d'un tableau associatif dans une boucle for est d'utiliser la propriété [c .valeurs] du tableau :
Une partie du pouvoir de [c foreach] vient du fait qu'elle peut être utilisée de la même manière indépendamment du type du conteneur. Comme nous l'avons vu dans le chapitre précédent, une manière d'itérer sur les valeurs d'un tableau associatif dans une boucle for est d'utiliser la propriété [c .values] du tableau~ :
[code=d <<<
auto valeurs = aa.valeurs;
auto valeurs = aa.values;
for (int i = 0; i != valeurs.length; ++i) {
writeln(valeurs[i]);
}
>>>]
[c foreach] ne nécessite rien de particulier pour les tableaux associatifs ; elle est utilisée de la même façon qu'avec les tableaux :
[c foreach] ne nécessite rien de particulier pour les tableaux associatifs~ ; elle est utilisée de la même façon qu'avec les tableaux~ :
[code=d <<<
foreach (valeur; aa) {
@ -48,7 +49,7 @@ Une partie du pouvoir de [c foreach] vient du fait qu'elle peut être utilisée
[ = La syntaxe de [c foreach]
[c foreach] consiste en trois sections :
[c foreach] consiste en trois sections~ :
[code=d <<<
foreach (noms ; conteneur_ou_intervalle) {
@ -56,21 +57,21 @@ Une partie du pouvoir de [c foreach] vient du fait qu'elle peut être utilisée
}
>>>]
- [c conteneur_ou_intervalle] indique où sont les éléments.
- [c // opérations] indique les opérations à appliquer à chaque élément.
- [c conteneur_ou_intervalle] indique où sont les éléments~ ;
- [c // opérations] indique les opérations à appliquer à chaque élément~ ;
- [c noms] indique le nom de l'élément et potentiellement d'autres variables dépendant du type du conteneur ou de l'intervalle. Même si le choix de [c noms] appartient au programmeur, le nombre et les types de ces noms dépend du type du conteneur.
]
[ = [c continue] et [c break]
Ces mots-clés ont le même sens que celui qu'ils ont avec la boucle [c for] : [c continue] mène à l'itération suivante au lieu de finir celle qui est en cours et [c break] sort de la boucle.
Ces mots-clés ont le même sens que celui qu'ils ont avec la boucle [c for]~ : [c continue] mène à l'itération suivante au lieu de finir celle qui est en cours et [c break] sort de la boucle.
]
[ = [c foreach] avec les tableaux
Quand il y a un seul nom dans la section [c noms], c'est la valeur de l'élément à chaque itération :
Quand il y a un seul nom dans la section [c noms], c'est la valeur de l'élément à chaque itération~ :
[code=d <<<
foreach (element; tableau) {
@ -78,7 +79,7 @@ Une partie du pouvoir de [c foreach] vient du fait qu'elle peut être utilisée
}
>>>]
Quand deux noms sont indiqués dans la section [c noms], il y a respectivement un compteur automatique et la valeur de l'élément :
Quand deux noms sont indiqués dans la section [c noms], il y a respectivement un compteur automatique et la valeur de l'élément~ :
[code=d <<<
foreach (i, element; tableau) {
@ -92,7 +93,7 @@ Une partie du pouvoir de [c foreach] vient du fait qu'elle peut être utilisée
[ = [c foreach] avec les chaînes et [c std.range.stride]
Comme les chaînes sont des tableaux de caractères, [c foreach] fonctionne de la même manière qu'avec les tableaux : un nom unique est le caractère, deux noms sont le compteur et le caractère :
Comme les chaînes sont des tableaux de caractères, [c foreach] fonctionne de la même manière qu'avec les tableaux : un nom unique est le caractère, deux noms sont le compteur et le caractère~ :
[code=d <<<
foreach (c; "salut") {
@ -104,7 +105,7 @@ Une partie du pouvoir de [c foreach] vient du fait qu'elle peut être utilisée
}
>>>]
Cependant, étant des unités de code UTF, [c char] et [c wchar] itèrent sur les unités de codes, par sur les points de code Unicode :
Cependant, étant des unités de stockage UTF, [c char] et [c wchar] itèrent sur les unités de stockage, par sur les points de code Unicode~ :
[code=d <<<
foreach (i, code; "abcçd") {
@ -112,7 +113,7 @@ Une partie du pouvoir de [c foreach] vient du fait qu'elle peut être utilisée
}
>>>]
On accède aux deux unités de code UTF-8 qui forment [c ç] par des éléments séparés :
On accède aux deux unités de stockage UTF-8 qui forment [c ç] par des éléments séparés~ :
[output <<<
0: a
@ -123,7 +124,7 @@ Une partie du pouvoir de [c foreach] vient du fait qu'elle peut être utilisée
5: d
>>>]
Une manière d'itérer sur les caractères Unicode des chaînes dans une boucle [c foreach] est [c stride] du module [c std.range]. [c stride] présente la chaîne comme un conteneur constitué de caractères Unicode. Il prend la taille de son pas en second paramètre :
Une manière d'itérer sur les caractères Unicode des chaînes dans une boucle [c foreach] est la fonction [c stride] du module [c std.range]. [c stride] présente la chaîne comme un conteneur constitué de caractères Unicode. Il prend la taille de son pas en second paramètre~ :
[code=d <<<
import std.range;
@ -135,7 +136,7 @@ Une partie du pouvoir de [c foreach] vient du fait qu'elle peut être utilisée
}
>>>]
Selon le type de caractère de la chaîne, [c stride] présente toujours ses éléments comme des caractères Unicode :
Quel que soit le type de caractère de la chaîne, [c stride] présente toujours ses éléments comme des caractères Unicode~ :
[output <<<
a
@ -145,30 +146,30 @@ Une partie du pouvoir de [c foreach] vient du fait qu'elle peut être utilisée
d
>>>]
J'expliquerais ci-après pourquoi cette boucle ne pourrait pas inclure de compteur automatique.
J'expliquerai plus loin pourquoi cette boucle ne peut pas inclure de compteur automatique.
]
[ = [c foreach] avec les tableaux associatifs
Un seul nom indique la valeur, deux noms indiquent la clé et la valeur :
Un seul nom indique la valeur, deux noms indiquent la clé et la valeur~ :
[code=d <<<
foreach (valeur; aa) {
foreach (valeur; ta) {
writeln(valeur);
}
>>>]
[code=d <<<
foreach (key, valeur; aa) {
writeln(key, ": ", valeur);
foreach (clé, valeur; ta) {
writeln(clé, ": ", valeur);
}
>>>]
Les tableaux associatifs peuvent également donner leurs clés et leurs valeurs comme des [* intervalles]. Nous verrons les intervalles dans un chapitre ultérieur. [c .byKey()] et [c .byValue()] retournent des objets intervalles efficaces qui sont aussi utiles dans d'autres contextes. [c .byValue()] n'a pas grand intérêt dans les boucles [c foreach] par rapport aux itérations classiques décrites ci-avant. En revanche, [c .byKey()] est la seule manière efficace d'itérer seulement sur les clés d'un tableau associatif :
Les tableaux associatifs peuvent également donner leurs clés et leurs valeurs comme des [* intervalles]. Nous verrons les intervalles dans un chapitre ultérieur. [c .byKey()] et [c .byValue()] retournent des objets intervalle efficaces qui sont aussi utiles dans d'autres contextes. [c .byValue()] n'a pas grand intérêt dans les boucles [c foreach] par rapport aux itérations classiques que l'on a déjà décrites. En revanche, [c .byKey()] est la seule manière efficace d'itérer seulement sur les clés d'un tableau associatif~ :
[code=d <<<
foreach (clé; aa.byKey()) {
foreach (clé; ta.byKey()) {
writeln(clé);
}
>>>]
@ -176,7 +177,7 @@ Une partie du pouvoir de [c foreach] vient du fait qu'elle peut être utilisée
]
[ = [c foreach] avec les intervalles de nombres
Nous avons vu les intervalles de nombres dans le chapitre "Tranches (tranches) et autres fonctionnalités des tableaux". Il est possible d'indiquer un intervalle de nombre dans la section [c conteneur_ou_intervalle] :
Nous avons vu les intervalles de nombres dans le chapitre [[part:tranches | «~ Tranches (slices) et autres fonctionnalités des tableaux~ »]. Il est possible d'indiquer un intervalle de nombre dans la section [c conteneur_ou_intervalle]~ :
[code=d <<<
foreach (nombre; 10..15) {
@ -184,7 +185,7 @@ Une partie du pouvoir de [c foreach] vient du fait qu'elle peut être utilisée
}
>>>]
[p Rappel : | 10 est inclus dans l'intervalle mais pas 15.]
[p Rappel~ : | 10 est inclus dans l'intervalle mais pas 15.]
]
@ -198,7 +199,7 @@ Une partie du pouvoir de [c foreach] vient du fait qu'elle peut être utilisée
[ = Le compteur n'est automatique que pour les tableaux
Le compteur automatique est donné seulement quand on itère sur les tableaux. Quand un compteur est nécessaire lors d'une itération sur d'autres types de conteneurs, le compteur peut être définit et incrémenté de façon explicite :
Le compteur automatique est fourni seulement quand on itère sur les tableaux. Quand un compteur est nécessaire lors d'une itération sur d'autres types de conteneurs, le compteur peut être défini et incrémenté de façon explicite~ :
[code=d <<<
int i;
@ -208,7 +209,7 @@ Une partie du pouvoir de [c foreach] vient du fait qu'elle peut être utilisée
}
>>>]
Une telle variable est aussi nécessaire quand on compte une condition spécifique. Par exemple, le code suivant compte seulement les valeurs qui sont divisibles par 10 :
Une telle variable est aussi nécessaire quand on compte une condition spécifique. Par exemple, le code suivant compte seulement les valeurs qui sont divisibles par 10~ :
[code=d <<<
import std.stdio;
@ -227,7 +228,7 @@ Une partie du pouvoir de [c foreach] vient du fait qu'elle peut être utilisée
write(' ');
}
writeln(": ", nombre);
writeln(" : ", nombre);
}
}
>>>]
@ -235,21 +236,21 @@ Une partie du pouvoir de [c foreach] vient du fait qu'elle peut être utilisée
La sortie :
[output <<<
: 1
1: 0
: 15
2: 10
: 3
: 5
3: 20
4: 30
: 1
1 : 0
: 15
2 : 10
: 3
: 5
3 : 20
4 : 30
>>>]
]
[ = La copie de l'élément, pas l'élément lui-même
La boucle [c foreach] fournit normalement une copie de l'élément, pas l'élément qui est stocké dans le conteneur. Ceci peut être la cause de bogues.
Pour voir un exemple ce ceci, jetons un œil sur le programme suivant qui essaie de doubler les valeurs des élément d'un tableau :
Pour voir un exemple de ceci, jetons un œil sur le programme suivant qui essaie de doubler les valeurs des élément d'un tableau :
[code=d <<<
import std.stdio;
@ -268,14 +269,14 @@ Une partie du pouvoir de [c foreach] vient du fait qu'elle peut être utilisée
}
>>>]
La sortie du programme indique que l'affectation faite à chaque élément à l'intérieur du corps de [c foreach] n'a aucun effet sur les éléments du conteneur :
La sortie du programme montre que l'affectation faite à chaque élément à l'intérieur du corps de [c foreach] n'a aucun effet sur les éléments du conteneur~ :
[code=d <<<
Avant : [1.2, 3.4, 5.6]
Après : [1.2, 3.4, 5.6]
>>>]
Ceci s'explique par le fait que [c nombre] n'est pas un élément du tableau, mais une copie d'élément. Quand on a besoin de modifier les éléments eux-même, le nom doit être défini comme une référence à l'élément par le mot-clé [c ref] :
Ceci s'explique par le fait que [c nombre] n'est pas un élément du tableau, mais une copie d'élément. Quand on a besoin de modifier les éléments eux-même, le nom doit être défini comme une référence à l'élément par le mot-clé [c ref]~ :
[code=d <<<
foreach (ref nombre; nombres) {
@ -283,7 +284,7 @@ Une partie du pouvoir de [c foreach] vient du fait qu'elle peut être utilisée
}
>>>]
La nouvelle sortie montre que maintenant, les affectations modifient les éléments du tableau :
La nouvelle sortie montre que maintenant, les affectations modifient les éléments du tableau~ :
[code=d <<<
Avant : [1.2, 3.4, 5.6]
@ -296,23 +297,46 @@ Une partie du pouvoir de [c foreach] vient du fait qu'elle peut être utilisée
[ = L'intégrité du conteneur doit être préservée
Même s'il est correct de modifier les éléments du conteneur à travers des variables [c ref], la structure du conteneur ne doit pas changer. Par exemple, les éléments ne doivent pas être supprimés du ou ajouté au conteneur pendant une boucle [c foreach].
Même s'il est correct de modifier les éléments du conteneur à travers des variables [c ref], la structure du conteneur ne doit pas changer. Par exemple, les éléments ne doivent pas être supprimés ou ajoutés au conteneur pendant une boucle [c foreach].
De telles modifications peuvent perturber le fonctionnement interne de l'itération de la boucle et mettre le programme dans un état incohérent.
]
[ = ``foreach_reverse`` to iterate in the reverse direction
``foreach_reverse`` fonctionne de la même manière que ``foreach`` mais itère dans la direction inverse~ :
[code=d <<<
auto conteneur = [ 1, 2, 3 ];
foreach_reverse (element; conteneur) {
writefln("%s ", element);
}
>>>]
La sortie~ :
[output <<<
3
2
1
>>>]
L'utilisation de [c foreach_reverse] n'est pas répandue parce que la fonction ``retro()`` fait la même chose. Nous verrons cette fonction dans un chapitre suivant.
]
[ = Exercice
Nous savons que les tableaux associatifs proposent une relation de type clés-valeurs. Cette relation est unidirectionnelle : on accède aux valeurs par les clés mais ce n'est pas vrai dans l'autre sens.
Nous savons que les tableaux associatifs proposent une relation de type clés-valeurs. Cette relation est unidirectionnelle~ : on accède aux valeurs par les clés mais ce n'est pas vrai dans l'autre sens.
Supposons que l'on aie ce tableau associatif :
Supposons que l'on ait ce tableau associatif~ :
[code=d <<<
string[int] noms = [ 1:"un", 7:"sept", 20:"vingt" ];
>>>]
Utilisez ce tableau associatif et une boucle [c foreach] pour remplir un tableau associatif nommé [c valeurs]. Ce nouveau tableau associatif doit donner les valeurs qui correspondent aux noms. Par exemple, la ligne suivante devrait afficher ``20`` :
Utilisez ce tableau associatif et une boucle [c foreach] pour remplir un tableau associatif nommé [c valeurs]. Ce nouveau tableau associatif doit donner les valeurs qui correspondent aux noms. Par exemple, la ligne suivante devrait afficher ``20``~ :
[code=d <<<
writeln(valeurs["vingt"]);

View File

@ -2,11 +2,12 @@
title = "[c switch] et [c case]"
partAs = chapitre
translator = "Raphaël Jakse"
proofreader = "Stéphane Goujet"
]
[c switch] est une structure qui permet de comparer la valeur d'une expression avec de multiples valeurs spéciales. Elle est similaire à une chaîne "if, else if, else" mais fonctionne un peu différemment. [c case] est utilisé pour spécifier les valeurs à comparer avec l'expression du [c switch]. [c case] fait partie de la structure [c switch] et n'est pas une structure en lui-même.
[c switch] est une structure qui permet de comparer la valeur d'une expression avec de multiples valeurs spéciales. Elle est similaire à une chaîne «~ if, else if, else~ » mais fonctionne un peu différemment. [c case] est utilisé pour spécifier les valeurs à comparer avec l'expression du [c switch]. [c case] fait partie de la structure [c switch] et n'est pas une structure en lui-même.
[c switch] prend une expression entre parenthèses, compare la valeur de cette expression aux valeurs des [c case] et exécute les opérations du case qui est égal à la valeur de l'expression. Sa syntaxe consiste en un bloc [c switch] qui contient une ou plusieur section [c case] et une section [c default] :
[c switch] prend une expression entre parenthèses, compare la valeur de cette expression aux valeurs des [c case] et exécute les opérations du [c case] qui est égal à la valeur de l'expression. Sa syntaxe consiste en un bloc [c switch] qui contient une ou plusieurs sections [c case] et une section [c default]~ :
[code=d <<<
switch (expression) {
@ -24,13 +25,13 @@
// ... autres cases ...
default:
// opérations tà exécuter si l'expression ne correspond à aucun case
// opérations à exécuter si l'expression ne correspond à aucun case
// ...
break;
}
>>>]
Même si elle est utilisée dans des vérifications conditionnelles, l'expression que le [c switch] prend n'est pas utilisée comme une expression logique. Elle n'est pas évaluée comme "si cette condition est vraie" comme dans la structure [c if]. La valeur de l'expression du [c switch] est utilisée dans des comparaisons d'égalité avec les valeurs des [c case]. C'est similaire à une chaîne "if, else if, else" qui n'a que des opérations d'égalité :
Même si elle est utilisée dans des vérifications conditionnelles, l'expression que le [c switch] prend n'est pas utilisée comme une expression logique. Elle n'est pas évaluée comme «~ si cette condition est vraie~ » comme dans la structure [c if]. La valeur de l'expression du [c switch] est utilisée dans des comparaisons d'égalité avec les valeurs des [c case]. C'est similaire à une chaîne «~ if, else if, else~ » qui n'a que des opérations d'égalité~ :
[code=d <<<
auto valeur = expression;
@ -52,15 +53,15 @@ Même si elle est utilisée dans des vérifications conditionnelles, l'expressio
}
>>>]
Cependant, la chaîne "if, else if, else" ci-avant n'est pas l'équivalent de la structure [c switch]. Les raisons à cela seront expliquées dans les sections suivantes.
Cependant, la chaîne «~ if, else if, else~ » ci-dessus n'est pas l'équivalent de la structure [c switch]. Les raisons à cela seront expliquées dans les sections suivantes.
Si une valeur [c case] correspond à la valeur de l'expression du [c switch], alors les opérations qui sont sous ce [c case] sont exécutées. Si aucune valeur ne correspond, alors les opérations qui sont en dessous de [c default] sont exécutées. Les opérations sont exécutées jusqu'à ce qu'une instruction [c break] ou [c goto] est rencontrée.
[ = L'instruction [c goto]
Certains usages de [c goto] sont déconseillés dans la plupart des langages de programmation en général. Cependant, [c goto] est utile dans les structures [c switch] dans quelques rares cas. L'instruction [c goto] sera vue plus en détail dans un chapitre ultérieur.
Certains usages de [c goto] sont en général déconseillés dans la plupart des langages de programmation. Cependant, [c goto] est utile dans les structures [c switch] dans quelques rares cas. L'instruction [c goto] sera vue plus en détail dans un chapitre ultérieur.
[c case] n'introduit pas un nouveau bloc comme le fait la structure [c if]. Une fois que les opérations dans un bloc [c if] ou [c else] sont finies, l'évaluation totale de la structure [c if] est également terminée. Ce n'est pas le cas avec les sections [c case] ; une fois qu'un [c case] correspondant est trouvé, l'exécution du programme saute vers ce [c case] et exécute les opérations sous ce [c case]. Quand ceci est nécessaire dans certains cas rare, [c goto] fait sauter l'exécution du programme vers le [c case] suivant :
[c case] n'introduit pas un nouveau bloc comme le fait la structure [c if]. Une fois que les opérations dans un bloc [c if] ou [c else] sont finies, l'évaluation totale de la structure [c if] est également terminée. Ce n'est pas le cas avec les sections [c case]~ ; une fois qu'un [c case] correspondant est trouvé, l'exécution du programme saute vers ce [c case] et exécute les opérations sous ce [c case]. Quand ceci est nécessaire dans certains cas rares, [c goto] fait sauter l'exécution du programme vers le [c case] suivant~ :
[code=d <<<
switch (valeur) {
@ -78,27 +79,27 @@ Si une valeur [c case] correspond à la valeur de l'expression du [c switch], al
}
>>>]
Si la valeur est 5, l'exécution continue sous la ligne [c case 5:] et le programme affiche "``cinq``". Ensuite, l'instruction [c goto case] fait continuer l'exécution au [c case] suivant, et "``quatre``" est également affiché :
Si la valeur est 5, l'exécution continue sous la ligne [c case 5:] et le programme affiche «~ ``cinq``~ ». Ensuite, l'instruction [c goto case] fait continuer l'exécution au [c case] suivant, et «~ ``quatre``~ » est également affiché~ :
[output <<<
cinq
quatre
>>>]
[c goto] peut apparaître de trois manières dans les sections [c case] :
[c goto] peut apparaître de trois manières dans les sections [c case]~ :
- [c goto case] fait continuer l'exécution au [c case] suivant.
- [c goto default] fait continuer l'exécution à la section [c default].
- [c goto case] fait continuer l'exécution au [c case] suivant~ ;
- [c goto default] fait continuer l'exécution à la section [c default]~ ;
- [c goto case [i expression]] fait continuer l'exécution au [c case] qui correspond à cette expression.
Le programme suivant montre ces trois utilisations en utilisant une boucle foreach :
Le programme suivant montre ces trois utilisations en utilisant une boucle [c foreach]~ :
[code=d <<<
import std.stdio;
void main()
{
foreach (valer; [ 1, 2, 3, 10, 20 ]) {
foreach (valeur; [ 1, 2, 3, 10, 20 ]) {
writefln("--- valeur : %s ---", valeur);
switch (valeur) {
@ -146,7 +147,7 @@ Si une valeur [c case] correspond à la valeur de l'expression du [c switch], al
>>>]
]
[ = Le type de l'expression doit être entière, une chaîne ou un booléen
[ = Le type de l'expression doit être un type entier, une chaîne ou un booléen
Tous les types peuvent être utilisés dans des comparaisons d'égalité dans les structures [c if]. Par contre, le type des expressions du [c switch] est limité aux entiers, aux chaînes et à [c bool].
@ -162,7 +163,7 @@ Si une valeur [c case] correspond à la valeur de l'expression du [c switch], al
resultat = premier - second;
break;
case "multiply":
case "multiplier":
resultat = premier * second;
break;
@ -175,14 +176,14 @@ Si une valeur [c case] correspond à la valeur de l'expression du [c switch], al
}
>>>]
[p Note : | Le code ci-avant lève une exception quand l'opération n'est pas reconnue par le programme. Nous verrons les exceptions dans un chapitre ultérieur.]
[p Note : | le code ci-dessus lève une exception quand l'opération n'est pas reconnue par le programme. Nous verrons les exceptions dans un chapitre ultérieur.]
Même s'il est possible d'utiliser des expression booléennes, comme [c bool] n'a que deux valeurs, utiliser une structure [c if] ou l'opérateur ternaire ([c ?:]) avec ce type est peut-être plus adapté.
]
[ = Les valeurs [c case] doivent être connues lors de la compilation
Avec les structures [c if], les valeurs qui sont des deux côtés de l'opérateur d'égalité peuvent être calculée lors de l'exécution. Par exemple, l'instruction [c if] suivante peut utiliser deux valeurs, une qui est calculée par le programme et une qui est entrée par l'utilisateur :
Avec les structures [c if], les valeurs qui sont des deux côtés de l'opérateur d'égalité peuvent être calculée lors de l'exécution. Par exemple, l'instruction [c if] suivante peut utiliser deux valeurs, une qui est calculée par le programme et une qui est entrée par l'utilisateur~ :
[code=d <<<
if (tentative == nombre) {
@ -195,7 +196,7 @@ Si une valeur [c case] correspond à la valeur de l'expression du [c switch], al
]
[ = Intervalles de valeurs
Les intervalles de valeurs peuvent être indiqués pas des [c ..] entre les [c case]s :
Les intervalles de valeurs peuvent être indiqués par des [c ..] entre les [c case]~ :
[code=d <<<
switch (valeurDuDé) {
@ -213,18 +214,18 @@ Si une valeur [c case] correspond à la valeur de l'expression du [c switch], al
default:
/* Le programme ne devrait jamais arriver ici parce que les cases
* ci-avant couvrent l'intervalle entier des valeurs possibles d'un dé
* (voir l'instruction final switch ci-après) */
* précédents couvrent l'intervalle complet des valeurs possibles d'un dé
* (voir l'instruction "final switch" un peu plus loin) */
break;
}
>>>]
Le code ci-avant détermine que le jeu se fini par match nul quand la valeur du dé est 2, 3, 4 ou 5.
Le code qui précède détermine que le jeu se fini par match nul quand la valeur du dé est 2, 3, 4 ou 5.
]
[ = Valeurs distinctes
Supposons que c'est match nul pour les valeurs 2 et 4, plutôt que les valeurs qui sont dans l'intervalle [m ~[2, 5~]]. Les valeurs distinctes d'un [c case] sont séparées par des virgules :
Supposons que le match nul est obtenu seulement pour les valeurs 2 et 4, plutôt que les valeurs qui sont dans l'intervalle [m ~[2, 5~]]. Les valeurs distinctes d'un [c case] sont séparées par des virgules~ :
[code=d <<<
case 2, 4:
@ -235,9 +236,9 @@ Si une valeur [c case] correspond à la valeur de l'expression du [c switch], al
]
[ = L'instruction [c final switch]
Cette structure fonctionne de la même manière qu'un [switch] classique, avec les différences suivantes :
Cette structure fonctionne de la même manière qu'un [switch] classique, avec les différences suivantes~ :
- Il ne peut pas y avoir de section [c default]. Notez que cette section n'a aucun sens dans les cas où les sections [c case] couvrent l'intervalle entier de valeurs, comme dans le cas des six valeurs du dé ci-avant.
- Il ne peut pas y avoir de section [c default]. Notez que cette section n'a aucun sens dans les cas où les sections [c case] couvrent l'intervalle complet de valeurs, comme dans le cas des six valeurs du dé.
- Les intervalles de valeurs ne peuvent pas être utilisés avec les [c case] (on peut cependant utiliser les valeurs distinctes).
- Si l'expression est un type [c enum], toutes les valeurs du type doivent être couvertes par les [c case] (nous verrons les types [c enum] dans le chapitre suivant).
@ -264,10 +265,10 @@ Si une valeur [c case] correspond à la valeur de l'expression du [c switch], al
[c switch] est adapté quand on compare la valeur d'une expression avec un ensemble de valeurs connues lors de la compilation.
Quand il n'y a que deux valeurs à comparer, une instruction [c if] peut avoir plus de sens. Par exemple, pour vérifier si ce sont des têtes ou des queues :
Quand il n'y a que deux valeurs à comparer, une instruction [c if] peut avoir plus de sens. Par exemple, pour vérifier si on obtient pile ou face~ :
[code=d <<<
if (resultatTetesOuQueues == queues) {
if (resultatPileOuFace == pile) {
// ...
} else {
@ -275,13 +276,14 @@ Si une valeur [c case] correspond à la valeur de l'expression du [c switch], al
}
>>>]
En règle généal, [c switch] est plus adapté quand il y a plus de deux valeurs à comparer.
En règle générale, [c switch] est plus adapté quand il y a plus de deux valeurs à comparer.
]
[ = Exercices
# [ Écrivez un programme de calculatrice qui prend en charge les opérations arithmétiques. Le programme lit l'opération comme une chaîne et ensuite deux valeurs de type [c double] depuis l'entrée. La calculatrice doit afficher le résultat de l'opération. Par exemple, quand l'opération et les valeurs sont "ajouter" et "5 7", le programme devrait afficher 12.
# [ Écrivez un programme de calculatrice qui prend en charge les opérations arithmétiques. Le programme lit une première ligne avec l'opération comme une chaîne et ensuite une seconde ligne avec deux valeurs de type [c double] depuis l'entrée. La calculatrice doit afficher le résultat de l'opération. Par exemple, quand l'opération et les valeurs sont «~ ajouter~ » et «~ 5 7~ », le programme devrait afficher 12.
[comment <<< Tel que l'énoncé était rédigé, je comprenais que l'opération et les 2 valeurs étaient à la suite sur la même ligne. >>>]
L'entrée peut être lue comme dans le code suivant :
L'entrée peut être lue comme dans le code suivant~ :
[code=d <<<
string op;
@ -294,7 +296,7 @@ Si une valeur [c case] correspond à la valeur de l'expression du [c switch], al
readf(" %s %s", &premier, &second);
>>>]
]
# Améliorez la calculatrice pour prendre en charge les opérateurs du type [c +] en plus des mots du type "ajouter".
# Améliorez la calculatrice pour prendre en charge les opérateurs du type [c +] en plus des mots du type «~ ajouter~ ».
# Levez une exception pour les opérations inconnues.