programmez-en-d/enum.whata

239 lines
9.2 KiB
Plaintext
Raw Permalink Blame History

This file contains invisible Unicode characters

This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

[set
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 constantes nommées.
[ = 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 entiers et les opérations arithmétiques]~ :
[code=d <<<
if (operation == 1) {
resultat = premier + second;
} else if (operation == 2) {
resultat = premier - second;
} else if (operation == 3) {
resultat = premier * second;
} else if (operation == 4) {
resultat = premier / second;
}
>>>]
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é.
[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) {
resultat = premier + second;
} else if (operation == Operation.soustraire) {
resultat = premier - second;
} else if (operation == Operation.multiplier) {
resultat = premier * second;
} else if (operation == Operation.diviser) {
resultat = premier / second;
}
>>>]
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 }
>>>]
]
[ = La syntaxe d'[c enum]
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~ :
[code=d <<<
enum NomDuType : type_de_base { NomDeValeur_1, NomDeValeur_2, /* etc. */ }
>>>]
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 entre accolades. Voici quelques exemples~ :
[code=d <<<
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 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 <<<
PileOuFace resultat; // initialisé à la valeur par défaut
auto tq = PileOuFace.pile; // type inféré
>>>]
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 == PileOuFace.pile) {
// ...
}
>>>]
]
[ = Valeurs réelles et types de base
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 PileOuFace] ont les valeurs 0 et 1~ :
[code=d <<<
writeln("pile vaut 0 : ", (PileOuFace.pile == 0));
writeln("face vaut 1 : ", (PileOuFace.face == 1));
>>>]
La sortie~ :
[output <<<
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é 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~ :
[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~ :
[code=d <<<
enum ConstanteNaturelle : double { pi = 3.14, e = 2.72 }
enum UnitéDeTempérature : string { C = "Celsius", F = "Fahrenheit" }
>>>]
]
[ = Des valeurs énumérées sans type énuméré
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~ :
[code=d <<<
enum secondesParJour = 60 * 60 * 24;
>>>]
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;
>>>]
Comme il n'y a pas de type énuméré auquel se référer, de telles constantes nommées peuvent être simplement utilisées dans le code par leur nom :
[code=d <<<
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]~ :
[code=d <<<
enum nomDeFichier = "list.txt";
>>>]
]
[ = 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~ :
[code=d <<<
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~ :
[output <<<
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]~ :
[code=d <<<
foreach (couleur; Couleur.min .. Couleur.max) {
writefln("%s : %d", couleur, couleur);
}
>>>]
La sortie~ :
[output <<<
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~ :
[code=d <<<
Couleur couleur = 1; // ← ERREUR de compilation
>>>]
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 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 <<<
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.
]
[ = Exercice
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 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]]
]