60. Exercices▲
60-1. Le programme Hello world - Correction▲
import
std.stdio;
void
main
(
)
{
writeln
(
"Autre chose... :p"
);
}
import
std.stdio;
void
main
(
)
{
writeln
(
"Une ligne..."
);
writeln
(
"Une autre ligne..."
);
}
- Le programme suivant ne peut pas être compilé parce qu'il manque le point-virgule à la fin de la ligne du writeln :
import
std.stdio;
void
main
(
)
{
writeln
(
"Hello world!"
)
}
60-2. writeln et write - Correction▲
-
Une méthode est d'utiliser un autre paramètre entre les deux :
Sélectionnezwriteln
(
"Hello world!"
," "
,"Hello fish!"
); - write peut elle aussi prendre plusieurs paramètres :
write
(
"one"
, " two"
, " three"
);
60-3. Types Fondamentaux - Correction▲
On peut utiliser d'autres types à la place de int :
import
std.stdio;
void
main
(
)
{
writeln
(
"Type : "
, short
.stringof);
writeln
(
"Taille en octets : "
, short
.sizeof);
writeln
(
"Valeur minimale : "
, short
.min);
writeln
(
"Valeur maximale : "
, short
.max);
writeln
(
);
writeln
(
"Type : "
, ulong
.stringof);
writeln
(
"Taille en octets : "
, ulong
.sizeof);
writeln
(
"Valeur minimale : "
, ulong
.min);
writeln
(
"Valeur maximale : "
, ulong
.max);
}
60-4. Affectation et ordre d'évaluation - Correction▲
Les valeurs de a, b et c sont affichées à droite de chaque opération. La valeur qui change est à chaque opération surlignée
|
À la fin, les valeurs de a et de b ont été échangées.
60-5. Variable - Correction▲
import
std.stdio;
void
main
(
)
{
int
montant =
20
;
double
taux =
2
.11
;
writeln
(
"J'ai échangé "
, montant,
" euros au taux "
, taux);
}
60-6. Entrée standard et flux de sortie - Correction▲
import
std.stdio;
void
main
(
)
{
writeln
(
1
, ","
, 2
);
stdout.writeln
(
3
, ","
, 4
);
}
60-7. Expressions logiques - Correction▲
- Parce que le compilateur reconnaît 10 < valeur comme une expression, il s'attend à avoir une virgule après elle pour l'accepter comme un argument valable de writeln. Ajouter des parenthèses autour de l'expression ne marche pas non plus parce que, cette fois, une parenthèse fermante serait attendue après la même expression.
- Grouper l'expression de cette manière : (10 < value) < 20 supprime l'erreur de compilation, parce que dans ce cas, 10 < value est évaluée et son résultat est utilisé avec < 20.
Nous savons que la valeur d'une expression logique comme 10 < value est soit fausse, soit vraie. Dans des expressions entières, false et true sont convertis en 0 et 1 respectivement (nous verrons les conversions de types automatiques dans un chapitre suivant). De ce fait, l'expression complète est l'équivalent de 0 < 20 ou 1 < 20 , qui sont vraies toutes les deux. -
L'expression « plus grand que la valeur basse et plus petit que la valeur haute » peut être écrite de cette façon :
Sélectionnezwriteln
(
"Est entre : "
,(
valeur>
10
)&&
(
valeur<
20
)); -
Il y a un vélo pour tout le monde peut être codée comme nombrePersonnes <= nombreVelos ou nombreVelos >= nombrePersonnes. Le reste de l'expression logique peut directement être traduit en code depuis l'énoncé :
Sélectionnezwriteln
(
"Nous allons à la plage : "
,((
distance<
10
)&&
(
nombreVelos>=
nombrePersonnes))||
((
nombrePersonnes<=
5
)&&
existeUneVoiture&&
existeUnPermis) ); - Notez le placement de l'opérateur || pour faciliter la lecture en séparant les deux conditions principales.
60-8. Lire depuis l'entrée - Correction▲
Quand les caractères ne peuvent pas être convertis au type voulu, stdin devient inutilisable. Par exemple, en entrant « abc » quand un entier est attendu rendrait stdin inutilisable.
60-9. L'instruction if - Correction▲
-
L'instruction writeln("Laver l'assiette") est indentée comme si elle était dans le bloc else. Cependant, du fait que le bloc de ce else n'est pas écrit avec des accolades, seule l'instruction writeln("Manger la tarte") est considérée comme étant dans la portée de ce else.
Comme les caractères blancs ne sont pas pris en compte dans le langage D, l'instruction « assiette » est en fait une instruction indépendante située au niveau de main et exécutée de façon inconditionnelle. Cela embrouille le lecteur à cause de l'indentation plus grande que normale. Si l'instruction « assiette » doit vraiment être dans le bloc else, il doit y avoir des accolades autour de ce bloc :Sélectionnezimport
std.stdio;void
main
(
){
bool ilyaDeLaLimonade=
true
;if
(
ilyaDeLaLimonade){
writeln
(
"Boire de la limonade"
);writeln
(
"Laver le verre"
);}
else
{
writeln
(
"Manger la tarte"
);writeln
(
"Laver l'assiette"
);}
}
-
On peut trouver plusieurs solutions pour les conditions de ce jeu. Je vais montrer deux exemples. Dans la première, on fait une traduction directe depuis l'énoncé :
Sélectionnezimport
std.stdio;void
main
(
){
write
(
"Quelle est la valeur du dé ? "
);int
dé;readf
(
" %s"
,&
dé);if
(
dé==
1
){
writeln
(
"Vous avez gagné"
);}
else
if
(
dé==
2
){
writeln
(
"Vous avez gagné"
);}
else
if
(
dé==
3
){
writeln
(
"Vous avez gagné"
);}
else
if
(
dé==
4
){
writeln
(
"J'ai gagné"
);}
else
if
(
dé==
5
){
writeln
(
"J'ai gagné"
);}
else
if
(
dé==
6
){
writeln
(
"J'ai gagné"
);}
else
{
writeln
(
"ERREUR : "
, dé," est invalide."
);}
}
-
Malheureusement, ce programme comporte beaucoup de répétitions. On peut arriver au même résultat d'une autre manière. En voici une :
Sélectionnezimport
std.stdio;void
main
(
){
write
(
"Quelle est la valeur du dé ? "
);int
dé;readf
(
" %s"
,&
dé);if
((
dé==
1
)||
(
dé==
2
)||
(
dé==
3
)){
writeln
(
"Vous avez gagné"
);}
else
if
((
dé==
4
)||
(
dé==
5
)||
(
dé==
6
)){
writeln
(
"J'ai gagné"
);}
else
{
writeln
(
"ERREUR : "
, dé," est invalide"
);}
}
- Les précédentes manières de faire ne peuvent pas être utilisées dans ce cas. Il n'est pas pratique de taper 1000 valeurs différentes dans un programme et s'attendre à ce qu'elles soient toutes correctes ou lisibles. Pour cette raison, il est mieux de déterminer si la valeur du dé est à l'intérieur d'un intervalle ou non :
if
((
dé >=
1
) &&
(
dé <=
500
))
60-10. La boucle while - Correction▲
-
À cause de la valeur initiale de nombre qui est 0, l'expression logique de la boucle while est fausse depuis le tout début, et cela empêche de rentrer dans le corps de la boucle. Une solution est d'utiliser une valeur initiale qui permettra à la condition du while d'être vraie au tout début :
Sélectionnezint
number=
3
; - Toutes les variables dans le programme suivant sont initialisées par défaut à 0. Cela permet d'entrer dans les deux boucles au moins une fois :
import
std.stdio;
void
main
(
)
{
int
nombreSecret;
while
((
nombreSecret <
1
) ||
(
nombreSecret >
10
)) {
write
(
"Veuillez entrer un nombre entre 1 et 10 : "
);
readf
(
" %s"
, &
nombreSecret);
}
int
deviner;
while
(
deviner !=
nombreSecret) {
write
(
"Deviner le nombre secret : "
);
readf
(
" %s"
, &
deviner);
}
writeln
(
"C'est le bon nombre !"
);
}
60-11. Nombres entiers et opérations arithmétiques - Correction▲
-
On peut utiliser l'opérateur / pour la division et l'opérateur % pour le reste :
Sélectionnezimport
std.stdio;void
main
(
){
int
premier;write
(
"Veuillez entrer le premier nombre : "
);readf
(
" %s"
,&
premier);int
second;write
(
"Veuillez entrer le second nombre : "
);readf
(
" %s"
,&
second);int
quotient=
premier/
second;int
reste=
premier%
second;writeln
(
premier," = "
, second," * "
, quotient," + "
, reste);}
-
On peut déterminer si le reste est 0 ou non avec une instruction if :
Sélectionnezimport
std.stdio;void
main
(
){
int
premier;write
(
"Veuillez entrer le premier nombre : "
);readf
(
" %s"
,&
premier);int
second;write
(
"Veuillez entrer le second nombre : "
);readf
(
" %s"
,&
second);int
quotient=
premier/
second;int
reste=
premier%
second;// Nous ne pouvons pas appeler writeln tout de suite avant de déterminer si
// le reste est 0 ou non. On doit terminer la ligne plus loin
// avec writeln.
write
(
premier," = "
, second," * "
, quotient);// Le reste doit être affiché seulement si non nul.
if
(
reste!=
0
){
write
(
" + "
, reste);}
// On peut maintenant terminer la ligne.
writeln
(
);}
-
Code :
Sélectionnezimport
std.stdio;void
main
(
){
while
(
true
){
write
(
"0: Quitter, 1: Ajouter, 2: Soustraire, 3: Multiplier,"
," 4: Diviser - Veuillez entrer l'opération : "
);int
operation;readf
(
" %s"
,&
operation);// Vérifions qu'une opération valide a été entrée
if
((
operation<
0
)||
(
operation>
4
)){
writeln
(
"Je ne connais pas cette opération"
);continue
;}
if
(
operation==
0
){
writeln
(
"Au revoir !"
);break
;}
// Si nous sommes ici, nous savons que nous avons affaire à l'une
// des 4 opérations. Maintenant, il est temps de demander
// deux entiers à l'utilisateur:
int
premier;int
second;write
(
" Premier nombre : "
);readf
(
" %s"
,&
premier);write
(
"Second nombre : "
);readf
(
" %s"
,&
second);int
resultat;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;}
else
{
writeln
(
"erreur ! "
,"Cette condition ne devrait jamais avoir eu lieu."
);break
;}
writeln
(
" Résultat : "
, resultat);}
}
- Code :
import
std.stdio;
void
main
(
)
{
int
valeur =
1
;
while
(
valeur <=
10
) {
if
(
valeur !=
7
) {
writeln
(
valeur);
}
++
valeur;
}
}
60-12. Types à virgule flottante - Correction▲
-
Il suffit de remplacer les trois int par trois double.
Sélectionnezdouble
first;double
second;// ...
double
result; - Le programme suivant laisse entrevoir comment il deviendrait compliqué si plus de cinq variables étaient nécessaires :
import
std.stdio;
void
main
(
)
{
double
valeur_1;
double
valeur_2;
double
valeur_3;
double
valeur_4;
double
valeur_5;
write
(
"Value 1: "
);
readf
(
" %s"
, &
valeur_1);
write
(
"Value 2: "
);
readf
(
" %s"
, &
valeur_2);
write
(
"Value 3: "
);
readf
(
" %s"
, &
valeur_3);
write
(
"Value 4: "
);
readf
(
" %s"
, &
valeur_4);
write
(
"Value 5: "
);
readf
(
" %s"
, &
valeur_5);
writeln
(
"Le double des valeurs :"
);
writeln
(
valeur_1 *
2
);
writeln
(
valeur_2 *
2
);
writeln
(
valeur_3 *
2
);
writeln
(
valeur_4 *
2
);
writeln
(
valeur_5 *
2
);
writeln
(
"Le cinquième des valeurs :"
);
writeln
(
valeur_1 /
5
);
writeln
(
valeur_2 /
5
);
writeln
(
valeur_3 /
5
);
writeln
(
valeur_4 /
5
);
writeln
(
valeur_5 /
5
);
}
60-13. Tableaux - Correction▲
-
Code :
Sélectionnezimport
std.stdio;void
main
(
){
write
(
"Combien de valeurs seront entrées ? "
);int
nombre;readf
(
" %s"
,&
nombre);double
[] valeurs; valeurs.length=
nombre;// Le compteur est souvent appelé 'i'
int
i;while
(
i<
nombre){
write
(
"Valeur "
, i," : "
);readf
(
" %s"
,&
valeurs[i]);++
i;}
writeln
(
"Dans l'ordre:"
); valeurs.sort; i=
0
;while
(
i<
nombre){
write
(
valeurs[i]," "
);++
i;}
writeln
(
);writeln
(
"Dans l'ordre inverse:"
); valeurs.reverse; i=
0
;while
(
i<
nombre){
write
(
valeurs[i]," "
);++
i;}
writeln
(
);}
-
Les explications sont fournies dans les commentaires :
Sélectionnezimport
std.stdio;void
main
(
){
// On utilise des tableaux dynamiques parce qu'on ne connaît pas
// le nombre de valeurs qui vont être lues depuis l'entrée
int
[] pairs;int
[] impairs;writeln
(
"Merci d'entrer des entiers (-1 pour finir) :"
);while
(
true
){
// Lecture de la valeur
int
valeur;readf
(
" %s"
,&
valeur);// La valeur spéciale de -1 casse la boucle
if
(
valeur==
-
1
){
break
;}
// Ajout au tableau correspondant, selon
// si la valeur est paire ou impaire. C'est un nombre
// pair s'il n'y a pas de reste quand on le divise par 2.
if
((
valeur%
2
)==
0
){
pairs~=
valeur;}
else
{
impairs~=
valeur;}
}
// Les tableaux impairs et pairs sont triés séparément
impairs.sort; pairs.sort;// Les deux tableaux sont ensuite concaténés dans un nouveau tableau
int
[] resultat; resultat=
impairs~
pairs;writeln
(
"D'abord les impairs, ensuite les pairs, triés :"
);// Afficher les tableaux dans une boucle
int
i;while
(
i<
resultat.length){
write
(
resultat[i]," "
);++
i;}
writeln
(
);}
- Il y a trois erreurs (bogues) dans ce programme. Les deux premières concernent à la boucle while : les deux conditions de boucle utilisent l'opérateur <= au lieu de l'opérateur <. Du coup, le programme utilise des indices invalides et essaie d'accéder à des éléments qui ne sont pas dans le tableau.
Comme il est plus bénéfique pour vous de déboguer la troisième erreur vous-même, il serait souhaitable que vous lanciez d'abord le programme après avoir corrigé les deux premiers bogues avant de lire le paragraphe suivant. Vous remarquerez que le programme n'affichera pas les résultats. Pourquoi ? …
… La valeur de i est 5 quand la première boucle while se termine et cette valeur entraîne que l'expression logique de la deuxième boucle est fausse, ce qui en fait empêche l'exécution de la deuxième boucle. La solution est de remettre i à 0 avant la seconde boucle while, par exemple avec l'instruction i = 0.
60-14. Tranches et autres fonctionnalités des tableaux - Correction ▲
Itérer sur les éléments d'une tranche depuis le début de la tranche est une idée intéressante. Cette méthode est aussi la base des intervalles de Phobos que nous verrons dans un chapitre ultérieur.
import
std.stdio;
void
main
(
)
{
double
[] tableau =
[ 1
, 20
, 2
, 30
, 7
, 11
];
double
[] tranche =
tableau; // On démarre avec une tranche
// qui donne accès à l'ensemble
// des éléments du tableau
while
(
tranche.length) {
// Tant qu'il y a au moins
// un élément dans cette tranche
if
(
tranche[0
] >
10
) {
// On travaille toujours sur le premier
tranche[0
] /=
2
; // élément de la tranche dans ces expressions
}
tranche =
tranche[1
.. $]; // On raccourcit la tranche par
// son début
}
writeln
(
tableau); // Les éléments du tableau
// ont été mis à jour
}
60-15. Chaînes de caractères - Correction ▲
- Même si certaines fonctions des modules de Phobos sont faciles à utiliser avec les chaînes, les documentations des bibliothèques sont souvent laconiques comparées aux tutoriels. Vous pouvez en particulier trouver les intervalles (ranges) de Phobos confus, à ce moment du cours. Nous verrons les intervalles de Phobos dans un chapitre ultérieur.
-
Beaucoup d'autres fonctions peuvent aussi être chaînées :
Sélectionnezimport
std.stdio;import
std.string;void
main
(
){
write
(
"Prénom : "
); string prenom=
capitalize
(
chomp
(
readln
(
)));write
(
"Nom : "
); string nom=
capitalize
(
chomp
(
readln
(
))); string nomComplet=
prenom~
" "
~
nom;writeln
(
nomComplet);}
- Ce programme utilise deux indices pour créer une tranche :
import
std.stdio;
import
std.string;
void
main
(
)
{
write
(
"Veuillez entrer une ligne : "
);
string line =
chomp
(
readln
(
));
sizediff_t premier_e =
indexOf
(
line, 'e'
);
if
(
premier_e ==
-
1
) {
writeln
(
"Il n'y a pas de lettre e dans cette ligne."
);
}
else
{
sizediff_t dernier_e =
lastIndexOf
(
line, 'e'
);
writeln
(
line[premier_e .. dernier_e +
1
]);
}
}
60-16. Rediriger l'entrée standard et les flux de sortie - Correction▲
Rediriger l'entrée standard et la sortie des programmes est souvent utilisé, spécialement dans les consoles des systèmes de type Unix. Certains programmes sont conçus pour fonctionner correctement quand ils sont combinés avec d'autres programmes.
Par exemple, un fichier nommé essai.d peut être cherché dans une arborescence en combinant find avec grep :
find |
grep essai.d
find affiche le nom de tous les fichiers sur sa sortie. grep reçoit cette sortie dans son entrée et affiche les lignes qui contiennent essai.d sur sa propre sortie.
60-17. Fichiers - Correction▲
import
std.stdio;
import
std.string;
void
main
(
)
{
write
(
"Veuillez saisir le nom du fichier à lire : "
);
string nomFichierEntree =
chomp
(
readln
(
));
File fichierEntree =
File
(
nomFichierEntree, "r"
);
string nomFichierSortie =
nomFichierEntree ~
".out"
;
File fichierSortie =
File
(
nomFichierSortie, "w"
);
while
(!
fichierEntree.eof
(
)) {
string ligne =
chomp
(
fichierEntree.readln
(
));
if
(
ligne.length !=
0
) {
fichierSortie.writeln
(
ligne);
}
}
writeln
(
nomFichierSortie, " a été créé."
);
}
60-18. auto et typeof - Correction▲
On peut utiliser typeof() pour déterminer le type du littéral et .stringof pour avoir le nom de ce type en tant que string :
import
std.stdio;
void
main
(
)
{
writeln
(
typeof
(
1
.2
).stringof);
}
La sortie :
double
60-19. La boucle for - Correction▲
-
Code :
Sélectionnezimport
std.stdio;void
main
(
){
for
(
int
ligne=
0
; ligne!=
9
;++
ligne){
for
(
int
colonne=
0
; colonne!=
9
;++
colonne){
write
(
ligne,','
, colonne,' '
);}
writeln
(
);}
}
-
Triangle :
Sélectionnezimport
std.stdio;void
main
(
){
for
(
int
ligne=
0
; ligne!=
9
;++
ligne){
int
longueur=
ligne+
1
;for
(
int
i=
0
; i!=
longueur;++
i){
write
(
'*'
);}
writeln
(
);}
}
-
Parallélogramme :
Sélectionnezimport
std.stdio;void
main
(
){
for
(
int
ligne=
0
; ligne!=
9
;++
ligne){
for
(
int
i=
0
; i!=
ligne;++
i){
write
(
' '
);}
writeln
(
"********"
);}
}
- Pouvez-vous produire la forme du diamant ?
|
60-20. L'opérateur ternaire « ? »: - Correction▲
Même s'il pourrait être plus sensé d'utiliser une instruction if-else dans cet exercice, le programme suivant utilise deux opérateurs ?: :
import
std.stdio;
void
main
(
)
{
write
(
"Veuillez entrer la valeur nette : "
);
int
valeur;
readf
(
" %s"
, &
valeur);
writeln
(
valeur <
0
? -
valeur : valeur,
"€ "
,
valeur <
0
? "perdus"
: "gagnés"
);
}
60-21. Littéraux - Correction▲
-
Le problème ici est que la valeur à droite de l'affectation est trop grande pour être contenue dans un int. Selon les règles sur les littéraux entiers, son type est long. Pour cette raison, il ne peut pas être contenu dans la variable à gauche du signe égal. Il y a au moins deux solutions.
Une première solution est de laisser le compilateur déduire le type de la variable avec le mot-clé auto :Sélectionnezauto
quantite=
10_000_000_000
; -
Le type de quantite est déduit comme long.
Une autre solution est de spécifier explicitement le type de la variable en tant que long :Sélectionnezlong
quantite=
10_000_000_000
; -
Nous pouvons utiliser le caractère spécial '\r' qui ramène le curseur d'affichage au début de la ligne.
Sélectionnezimport
std.stdio;void
main
(
){
for
(
int
compteur=
0
; ;++
compteur){
write
(
"\rNombre: "
, compteur);}
}
-
La sortie de ce programme peut être erratique à cause de ses interactions avec le tampon de sortie. Le programme suivant force l'affichage du contenu du tampon de sortie avec la fonction flush et attend une milliseconde après chaque write :
Sélectionnezimport
std.stdio;import
core.thread;void
main
(
){
for
(
int
compteur=
0
; ;++
compteur){
write
(
"\rNombre : "
, compteur); stdout.flush
(
); Thread.sleep
(
Thread.sleep
(
10
.msecs));}
}
Forcer l'affichage du tampon de sortie n'est normalement pas nécessaire, car c'est fait automatiquement avant d'aller sur une nouvelle ligne (par exemple par writeln, ou avant de lire depuis stdin).
60-22. Sortie formatée - Correction▲
-
Nous avons déjà vu que c'est trivial avec les indicateurs de format :
Sélectionnezimport
std.stdio;void
main
(
){
writeln
(
"(Entrez 0 pour quitter le programme.)"
);while
(
true
){
write
(
"Veuillez entrer un nombre : "
);long
nombre;readf
(
" %s"
,&
nombre);if
(
nombre==
0
){
break
;}
writefln
(
"%1$d <=> %1$#x"
, nombre);}
}
- En se souvenant que le caractère % doit apparaître deux fois dans la chaîne de formatage pour être affiché une fois :
import
std.stdio;
void
main
(
)
{
write
(
"Veuillez entrer la valeur en pourcentage : "
);
double
pourcentage;
readf
(
" %s"
, &
pourcentage);
writefln
(
"%.2f%%"
, pourcentage);
}
60-23. Entrée formatée - Correction▲
Il suffit d'utiliser une chaîne de formatage où les parties de date sont remplacées par %s :
import
std.stdio;
void
main
(
)
{
int
annee;
int
mois;
int
jour;
readf
(
"%s.%s.%s"
, &
annee , &
mois , &
jour);
writeln
(
"Mois : "
, mois);
}
60-24. La boucle do-while - Correction▲
Ce programme n'est pas directement lié à la boucle do-while puisque tout problème qui peut être résolu avec une boucle do-while peut également être résolu par les autres types de boucle.
Le programme peut deviner le nombre auquel l'utilisateur pense en réduisant l'intervalle candidat des deux côtés selon les réponses de l'utilisateur. Par exemple, si sa première tentative est 50 et que l'utilisateur répond que le nombre secret est plus grand, le programme sait alors que le nombre doit être dans l'intervalle [51, 100]. Si le programme tente alors un autre nombre dans le milieu de cet intervalle, cette fois on saura que le nombre est soit dans l'intervalle [51, 75], soit dans l'intervalle [76, 100].
Quand la taille de l'intervalle est 1, le programme est sûr que le nombre qui est dedans est le nombre que l'utilisateur a choisi.
60-25. La boucle foreach - Correction▲
Pour obtenir un tableau associatif qui marche dans le sens opposé de noms, les types des clés et des valeurs doivent être échangés. Le nouveau tableau associatif doit être de type 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 valeurs :
import
std.stdio;
void
main
(
)
{
string[int
] noms =
[ 1
:"un"
, 7
:"sept"
, 20
:"vingt"
];
int
[string] valeurs;
foreach
(
cle, valeur; noms) {
valeurs[valeur] =
cle;
}
writeln
(
valeurs["vingt"
]);
}
60-26. Tableaux associatifs - Correction▲
-
La propriété .keys retourne une tranche (c.-à-d. un tableau dynamique) qui inclut toutes les clés du tableau associatif. Itérer sur cette tranche et supprimer les éléments de chaque clé en appelant .remove résultera en un tableau associatif vide :
Sélectionnezimport
std.stdio;void
main
(
){
string[int
] noms=
[1
:"un"
,10
:"dix"
,100
:"cent"
, ];writeln
(
"Taille initiale : "
, noms.length);int
[] clés=
noms.keys;for
(
int
i=
0
; i!=
clés.length;++
i){
writefln
(
"suppression de l'élément %s"
, clés[i]); noms.remove
(
clés[i]);}
writeln
(
"Taille finale : "
, noms.length);}
Cette solution peut être lente, surtout pour des tableaux larges. Les méthodes suivantes vident le tableau en une seule étape.
-
Une autre solution est d'assigner un tableau vide :
Sélectionnezstring[
int
] AAvide; noms=
AAvide; -
Comme la valeur initiale d'un tableau est de toute manière un tableau vide, la méthode suivante aura le même effet :
Sélectionneznoms
=
noms.init; -
Le but est de stocker de multiples notes par étudiant. Comme plusieurs notes peuvent être stockées dans un tableau dynamique, un tableau associatif qui associe des chaînes à des int[] fonctionne. Les notes peuvent être ajoutées aux tableaux dynamiques qui sont stockés dans le tableau associatif :
Sélectionnezimport
std.stdio;void
main
(
){
/* Le type de clé de ce tableau associatif est string et
* le type de valeur est int[], c.-à-d. un tableau d'int.
* le tableau associatif est défini avec un espace en plus
* pour aider à distinguer le type de valeur : */
int
[] [string] notes;/* Le tableau d'int qui correspond à "pierre" est
* utilisé pour ajouter la nouvelle note à ce tableau : */
notes["pierre"
]~=
18
; notes["pierre"
]~=
17
;/* afficher les notes de "pierre": */
writeln
(
notes["pierre"
]);}
- Les notes peuvent aussi être assignées d'un coup via un tableau littéral :
import
std.stdio;
void
main
(
)
{
int
[][string] notes;
notes["pierre"
] =
[ 18
, 17
, 19
];
writeln
(
notes["pierre"
]);
}
60-27. switch et case - Correction▲
import
std.stdio;
import
std.string;
void
main
(
)
{
string op;
double
premier;
double
second;
write
(
"Veuillez entrer l'opération :"
);
op =
chomp
(
readln
(
));
write
(
"Veuillez entrer deux valeurs séparées par un espace :"
);
readf
(
" %s %s"
, &
premier, &
second);
double
resultat;
final
switch
(
op) {
case
"ajouter"
:
resultat =
premier +
second;
break
;
case
"soustraire"
:
resultat =
premier -
second;
break
;
case
"multiplier"
:
resultat =
premier *
second;
break
;
case
"diviser"
:
resultat =
premier /
second;
break
;
}
writeln
(
resultat);
}
-
En utilisant les valeurs distinctes :
Sélectionnezfinal
switch
(
op){
case
"ajouter"
,"+"
: resultat=
premier+
second;break
;case
"soustraire"
,"-"
: resultat=
premier-
second;break
;case
"multiplier"
,"*"
: resultat=
premier*
second;break
;case
"diviser"
,"/"
: resultat=
premier/
second;break
;}
- Comme la section default est nécessaire pour lever l'exception, on ne peut plus utiliser le final switch. Voici les parties modifiées du programme :
// ...
switch
(
op) {
// ...
default
:
throw
new
Exception
(
"Opération invalide : "
~
op);
}
// ...
60-28. Les énumérations (enum) - Correction▲
import
std.stdio;
import
std.conv;
enum
Operation {
sortir, ajouter, soustraire, multiplier, diviser }
void
main
(
)
{
// Afficher les opérations prises en charge
write
(
"Opérations - "
);
for
(
Operation operation;
operation <=
Operation.max;
++
operation) {
writef
(
"%d:%s "
, operation, operation);
}
writeln
(
);
// Boucle infinie jusqu'à ce que l'utilisateur veuille sortir
while
(
true
) {
write
(
"Opération ? "
);
// L'entrée doit être lue dans le type réel (int) de l'énumération
int
codeOperation;
readf
(
" %s"
, &
codeOperation);
/*
À partir d'ici, on utilise les valeurs de l'énumération
plutôt que les constantes magiques. Le code de l'opération
qui a été lu doit donc être converti vers sa valeur énumérée
correspondante.
(Les conversions de types seront traitées plus en détail dans un
chapitre ultérieur.)
*/
Operation operation =
cast
(
Operation)codeOperation;
if
((
operation <
Operation.min) ||
(
operation >
Operation.max)) {
writeln
(
"ERREUR : Opération invalide"
);
continue
;
}
if
(
operation ==
Operation.sortir) {
writeln
(
"Au revoir !"
);
break
;
}
double
premier;
double
second;
double
resultat;
write
(
"Premier opérande ? "
);
readf
(
" %s"
, &
premier);
write
(
"Second opérande ? "
);
readf
(
" %s"
, &
second);
switch
(
operation) {
case
Operation.ajouter:
resultat =
premier +
second;
break
;
case
Operation.soustraire:
resultat =
premier -
second;
break
;
case
Operation.multiplier:
resultat =
premier *
second;
break
;
case
Operation.diviser:
resultat =
premier /
second;
break
;
default
:
throw
new
Exception
(
"ERREUR: Cette ligne n'aurait jamais dû être atteinte."
);
}
writeln
(
" resultat : "
, resultat);
}
}
60-29. Les fonctions - Correction▲
import
std.stdio;
void
afficherMenu
(
string[] elements, int
premierNombre)
{
foreach
(
i, element; elements) {
writeln
(
' '
, i +
premierNombre, ' '
, element);
}
}
void
main
(
)
{
string[] elements =
[ "Noir"
, "Rouge"
, "Vert"
, "Bleu"
, "Blanc"
];
afficherMenu
(
elements, 1
);
}
- Voici quelques idées :
void
placerPoint
(
Toile toile, int
ligne, int
colonne, dchar
point)
{
toile[ligne][colonne] =
point;
}
60-30. Paramètre des fonctions - Correction▲
Parce que les paramètres de cette fonction sont du genre de ceux qui sont copiés depuis les arguments (types valeur), ce sont ces copies qui sont échangées à l'intérieur de la fonction.
Pour faire en sorte que la fonction échange les arguments, les deux paramètres doivent être passés par référence :
void
echanger
(
ref int
premier, ref int
second)
{
immutable int
temp =
premier;
premier =
second;
second =
temp;
}
Avec ce changement, les variables dans main() sont échangées :
2
1
Même si ce n'est pas lié au problème original, notez également que temp est spécifié comme immutable, car il n'a pas à être modifié dans la fonction après son initialisation.
60-31. Environnement du Programme - Correction▲
import
std.stdio;
import
std.conv;
int
main
(
string[] args)
{
if
(
args.length !=
4
) {
stderr.writeln
(
"ERREUR ! Usage : \n "
, args[0
],
" un_nombre operateur un_autre_nombre"
);
return
1
;
}
immutable premier =
to!
double
(
args[1
]);
string op =
args[2
];
immutable second =
to!
double
(
args[3
]);
switch
(
op) {
case
"+"
:
writeln
(
premier +
second);
break
;
case
"-"
:
writeln
(
premier -
second);
break
;
case
"x"
:
writeln
(
premier *
second);
break
;
case
"/"
:
writeln
(
premier /
second);
break
;
default
:
throw
new
Exception
(
"Opérateur invalide : "
~
op);
}
return
0
;
}
import
std.stdio;
import
std.process;
void
main
(
)
{
write
(
"Veuillez saisir la ligne de commande à exécuter : "
);
string ligneDeCommande =
readln
(
);
writeln
(
"La sortie : "
, executeShell
(
ligneDeCommande));
}
60-32. assert et enforce - Correction▲
-
Vous remarquerez que le programme termine normalement quand on saisit 06:09 et 1:2. Cependant, vous pourrez remarquer que le temps de départ ne correspond pas à ce qui a été saisi par l'utilisateur :
Sélectionnez1
heure et2
minutes après09
:06
donne10
:08
. -
Comme vous pouvez le voir, même si le moment qui a été saisi est 06:09, la sortie contient 09:06. Cette erreur sera attrapée à l'aide d'une assertion dans le problème suivant.
-
L'erreur d'assertion après la saisie de 06:09 et 15:2 nous amène à la ligne suivante :
Sélectionnezstring
momentVersChaine
(
in
int
heure,in
int
minute){
assert
((
heure>=
0
)&&
(
heure<=
23
));// ...
}
-
Pour que cette assertion échoue, cette fonction doit être appelée avec une heure incorrecte.
Les deux seuls appels à momentVersChaine() dans le programme ne semblent pas avoir de problème :Sélectionnezwritefln
(
"%s heures et %s minutes après %s donne %s."
, DuréeHeures, duréeMinutes,momentVersChaine
(
heuresDepart, minutesDepart),momentVersChaine
(
endHour, endMinute)); -
Une enquête un peu plus poussée devrait révéler la vraie cause du bogue : les variables heure et minute sont échangées lors de la lecture du moment de départ :
SélectionnezlireMoment
(
"Moment de départ"
, minutesDepart, heuresDepart); -
Cet erreur de programmation provoque l'interprétation de l'heure comme 09:06 et l'incrémente par la durée 15:2 causant une valeur horaire invalide.
Une correction évidente est de passer les variables heure et minute dans le bon ordre :SélectionnezlireMoment
(
"Start time"
, heuresDepart, minutesDepart); -
La sortie :
SélectionnezMoment de départ ?
(
HH:MM)06
:09
Durée ?(
HH:MM)15
:2
15
heures et2
minutes après06
:09
donne21
:11
. -
Il s'agit de la même assertion :
Sélectionnezassert
((
heure>=
0
)&&
(
heure<=
23
)); -
La raison est que ajouterMoment() peut produire des heures qui sont plus grandes que 23. Ajouter un modulo à la fin garantirait une des sorties de la fonction :
Sélectionnezvoid
ajouterDurée
(
in
int
heuresDepart,in
int
minutesDepart,in
int
DuréeHeures,in
int
duréeMinutes,out
int
heuresResultat,out
int
minutesResultat){
heuresResultat=
heuresDepart+
DuréeHeures; minutesResultat=
minutesDepart+
duréeMinutes;if
(
minutesResultat>
59
){
++
heuresResultat;}
heuresResultat%=
24
;}
-
Notez que la fonction a d'autres problèmes. Par exemple, minutesResultat peut être supérieure à 59. La fonction suivante calcule la valeur des minutes correctement et s'assure que la sortie de la fonction vérifie les spécifications :
Sélectionnezvoid
ajouterDurée
(
in
int
heuresDepart,in
int
minutesDepart,in
int
DuréeHeures,in
int
duréeMinutes,out
int
heuresResultat,out
int
minutesResultat){
heuresResultat=
heuresDepart+
DuréeHeures; minutesResultat=
minutesDepart+
duréeMinutes; heuresResultat+=
minutesResultat/
60
; heuresResultat%=
24
; minutesResultat%=
60
;assert
((
heuresResultat>=
0
)&&
(
heuresResultat<=
23
));assert
((
minutesResultat>=
0
)&&
(
minutesResultat<=
59
));}
- Bonne chance.
60-33. Tests Unitaires - Correction▲
La première chose à faire est de compiler et exécuter le programme pour s'assurer que les tests fonctionnent et échouent :
$
dmd deneme.d -ofdeneme -w -unittest // ou: gdc -Wall -funittest deneme.d -o deneme
$
./deneme
core.exception.AssertError@deneme
(
11
): unittest failure
Le numéro de la ligne 11 indique que le premier test a échoué.
Pour des raisons pédagogiques, écrivons une implémentation incorrecte qui passe le premier test par accident. La fonction suivante retourne simplement une copie de l'entrée :
dstring toFront
(
dstring str, in
dchar
lettre)
{
dstring resultat;
foreach
(
c; str) {
resultat ~=
c;
}
return
resultat;
}
unittest
{
immutable str =
"hello"
d;
assert
(
toFront
(
str, 'h'
) ==
"hello"
);
assert
(
toFront
(
str, 'o'
) ==
"ohell"
);
assert
(
toFront
(
str, 'l'
) ==
"llheo"
);
}
void
main
(
)
{}
Le premier test passe, mais le deuxième échoue :
$
./deneme
core.exception.AssertError@deneme.d
(
17
): unittest failure
Voici une implémentation correcte qui passe tous les tests :
dstring toFront
(
dstring str, in
dchar
lettre)
{
dchar
[] debut;
dchar
[] fin;
foreach
(
c; str) {
if
(
c ==
lettre) {
debut ~=
c;
}
else
{
fin ~=
c;
}
}
return
(
debut ~
fin).idup;
}
unittest
{
immutable str =
"hello"
d;
assert
(
toFront
(
str, 'h'
) ==
"hello"
);
assert
(
toFront
(
str, 'o'
) ==
"ohell"
);
assert
(
toFront
(
str, 'l'
) ==
"llheo"
);
}
void
main
(
)
{}
Les tests passent :
$
./deneme
$
Cette fonction peut maintenant être modifiée de différentes manières avec l'assurance que ses tests devront passer. Les deux implémentations suivantes sont très différentes de la première, mais sont toutes deux correctes vis-à-vis des tests.
Une implémentation qui utilise std.algorithm.partition :
import
std.algorithm;
dstring toFront
(
dstring str, in
dchar
lettre)
{
dchar
[] result =
str.dup;
partition!(
c =>
c ==
lettre, SwapStrategy.stable)(
result);
return
result.idup;
}
unittest
{
immutable str =
"hello"
d;
assert
(
toFront
(
str, 'h'
) ==
"hello"
);
assert
(
toFront
(
str, 'o'
) ==
"ohell"
);
assert
(
toFront
(
str, 'l'
) ==
"llheo"
);
}
void
main
(
)
{}
Note : la syntaxe => qui apparaît dans le programme ci-avant crée une fonction lambda. Nous verrons les fonctions lambda dans des chapitres ultérieurs.
L'implémentation suivante compte combien de fois la lettre spéciale apparaît dans la chaîne. Cette information est ensuite envoyée à une autre fonction nommée repete pour produire la première partie du résultat. Notez que repete a elle-même un jeu de tests unitaires :
dstring repete
(
size_t compte, dchar
lettre)
{
dstring resultat;
foreach
(
i; 0
..compte) {
resultat ~=
lettre;
}
return
resultat;
}
unittest
{
assert
(
repete
(
3
, 'z'
) ==
"zzz"
);
assert
(
repete
(
10
, 'é'
) ==
"éééééééééé"
);
}
dstring toFront
(
dstring str, in
dchar
lettre)
{
size_t nombreLettreSpeciale;
dstring fin;
foreach
(
c; str) {
if
(
c ==
lettre) {
++
nombreLettreSpeciale;
}
else
{
fin ~=
c;
}
}
return
repete
(
nombreLettreSpeciale, lettre) ~
fin;
}
unittest
{
immutable str =
"hello"
d;
assert
(
toFront
(
str, 'h'
) ==
"hello"
);
assert
(
toFront
(
str, 'o'
) ==
"ohell"
);
assert
(
toFront
(
str, 'l'
) ==
"llheo"
);
}
void
main
(
)
{}
60-34. Programmation par contrat - Correction▲
Le bloc unittest peut être implémenté simplement en copiant les vérifications qui ont déjà été écrites dans la fonction main. Le seul ajout ci-dessous est le test pour le cas dans lequel la seconde équipe gagne :
int
ajouterPoints
(
in
int
buts1,
in
int
buts2,
ref int
points1,
ref int
points2)
in
{
assert
(
buts1 >=
0
);
assert
(
buts2 >=
0
);
assert
(
points1 >=
0
);
assert
(
points2 >=
0
);
}
out
(
resultat)
{
assert
((
resultat >=
0
) &&
(
resultat <=
2
));
}
body
{
int
gagnante;
if
(
buts1 >
buts2) {
points1 +=
3
;
gagnante =
1
;
}
else
if
(
buts1 <
buts2) {
points2 +=
3
;
gagnante =
2
;
}
else
{
++
points1;
++
points2;
gagnante =
0
;
}
return
gagnante;
}
unittest
{
int
points1 =
10
;
int
points2 =
7
;
int
gagnante;
// La première équipe gagne
gagnante =
ajouterPoints
(
3
, 1
, points1, points2);
assert
(
points1 ==
13
);
assert
(
points2 ==
7
);
assert
(
gagnante ==
1
);
// Nul
gagnante =
ajouterPoints
(
2
, 2
, points1, points2);
assert
(
points1 ==
14
);
assert
(
points2 ==
8
);
assert
(
gagnante ==
0
);
// La seconde équipe gagne
gagnante =
ajouterPoints
(
0
, 1
, points1, points2);
assert
(
points1 ==
14
);
assert
(
points2 ==
11
);
assert
(
gagnante ==
2
);
}
void
main
(
)
{
// ...
}
60-35. Structures - Correction▲
-
Un des choix de conception les plus aisés est d'utiliser deux membres dchar :
Sélectionnezstruct
Carte{
dchar
couleur;dchar
valeur;}
-
Il s'agit simplement d'afficher les deux membres côte à côte :
Sélectionnezvoid
afficherCarte
(
in
Carte carte){
write
(
carte.couleur, carte.valeur);}
-
En supposant qu'il y ait déjà une fonction appelée nouvelleCouleur(), nouveauJeu() peut être implémentée en appelant cette fonction pour chaque couleur :
SélectionnezCarte[]
nouveauJeu
(
)out
(
resultat){
assert
(
resultat.length==
52
);}
body
{
Carte[] jeu; jeu~=
nouvelleCouleur
(
'♠'
); jeu~=
nouvelleCouleur
(
'♡'
); jeu~=
nouvelleCouleur
(
'♢'
); jeu~=
nouvelleCouleur
(
'♣'
);return
jeu;}
-
Le reste du travail peut être accompli par la fonction nouvelleCouleur() suivante, qui construit la couleur en combinant le caractère de la couleur avec chaque valeur d'une chaîne :
SélectionnezCarte[]
nouvelleCouleur
(
in
dchar
couleur)in
{
assert
((
couleur==
'♠'
)||
(
couleur==
'♡'
)||
(
couleur==
'♢'
)||
(
couleur==
'♣'
));}
out
(
resultat){
assert
(
resultat.length==
13
);}
body
{
Carte[] cartesCouleur;foreach
(
valeur;"234567890VDRA"
){
cartesCouleur~=
Carte
(
couleur, valeur);}
return
cartesCouleur;}
-
On peut noter que ces fonctions mettent en œuvre la programmation par contrat afin de réduire les risques d'erreurs de programmation.
-
Si l'on échange deux éléments pris au hasard, cela suffit à ce que le jeu soit de plus en plus mélangé à chaque itération. Bien qu'il soit possible de tomber par hasard deux fois sur le même élément, échanger un élément avec lui-même n'a pas d'effet néfaste hormis le fait de ne pas obtenir un jeu plus mélangé après l'itération en question qu'avant son exécution.
Sélectionnezvoid
melanger
(
Carte[] jeu,in
int
repetitions){
/* Note : un meilleur algorithme consisterait à parcourir
* le jeu du début à la fin, en échangeant l'élément
* courant avec un tiré aléatoirement parmi les
* éléments situés entre l'élément courant et la fin.
*
* Il serait encore préférable d'appeler randomShuffle, du
* module std.algorithm.module, qui implémente déjà cet
* algorithme. Reportez-vous au commentaire dans main()
* pour voir comment randomShuffle() s'utilise.
*/
foreach
(
i;0
.. repetitions){
// Choisit deux éléments au hasard
immutable premier=
uniform
(
0
, jeu.length); immutable second=
uniform
(
0
, jeu.length);swap
(
jeu[premier], jeu[second]);}
}
-
La fonction ci-dessus appelle std.algorithm.swap qui échange tout simplement les valeurs de ses deux paramètres ref. En pratique, c'est l'équivalent de la fonction suivante :
Sélectionnezvoid
mySwap
(
ref Carte gauche, ref Carte droite){
immutable temporaire=
gauche; gauche=
droite; droite=
temporaire;}
Voici le programme au complet :
import
std.stdio;
import
std.random;
import
std.algorithm;
struct
Carte
{
dchar
couleur;
dchar
valeur;
}
void
afficherCarte
(
in
Carte carte)
{
write
(
carte.couleur, carte.valeur);
}
Carte[] nouvelleCouleur
(
in
dchar
couleur)
in
{
assert
((
couleur ==
'♠'
) ||
(
couleur ==
'♡'
) ||
(
couleur ==
'♢'
) ||
(
couleur ==
'♣'
));
}
out
(
resultat)
{
assert
(
resultat.length ==
13
);
}
body
{
Carte[] cartesCouleur;
foreach
(
valeur; "234567890JQKA"
) {
cartesCouleur ~=
Carte
(
couleur, valeur);
}
return
cartesCouleur;
}
Carte[] nouveauJeu
(
)
out
(
resultat)
{
assert
(
resultat.length ==
52
);
}
body
{
Carte[] jeu;
jeu ~=
nouvelleCouleur
(
'♠'
);
jeu ~=
nouvelleCouleur
(
'♡'
);
jeu ~=
nouvelleCouleur
(
'♢'
);
jeu ~=
nouvelleCouleur
(
'♣'
);
return
jeu;
}
void
melanger
(
Carte[] jeu, in
int
repetitions)
{
/* Note : un meilleur algorithme consisterait à parcourir
* le jeu du début à la fin, en échangeant l'élément
* courant avec un tiré aléatoirement parmi les
* éléments situés entre l'élément courant et la fin.
*
* Il serait encore préférable d'appeler randomShuffle, du
* module std.algorithm.module, qui implémente déjà cet
* algorithme. Reportez-vous au commentaire dans main()
* pour voir comment randomShuffle() s'utilise.
*/
foreach
(
i; 0
.. repetitions) {
// Choisit deux éléments au hasard
immutable premier =
uniform
(
0
, jeu.length);
immutable second =
uniform
(
0
, jeu.length);
swap
(
jeu[premier], jeu[second]);
}
}
void
main
(
)
{
Carte[] jeu =
nouveauJeu
(
);
melanger
(
jeu, 100
);
/* Note : au lieu d'appeler melanger(), il serait préférable
* d'appeler randomShuffle comme ceci :
*
* randomShuffle(jeu);
*/
foreach
(
carte; jeu) {
afficherCarte
(
carte);
write
(
' '
);
}
writeln
(
);
}
60-36. Nombre variable de paramètres - Correction▲
Pour que la fonction calculer puisse accepter un nombre variable de paramètres, elle doit inclure une tranche de Calcul suivie de … :
double
[] calculer
(
in
Calcul[] calculs ...)
{
double
[] resultats;
foreach
(
calcul; calculs) {
final
switch
(
calcul.op) {
case
Operation.addition:
resultats ~=
calcul.premier +
calcul.second;
break
;
case
Operation.soustraction:
resultats ~=
calcul.premier -
calcul.second;
break
;
case
Operation.multiplication:
resultats ~=
calcul.premier *
calcul.second;
break
;
case
Operation.division:
resultats ~=
calcul.premier /
calcul.second;
break
;
}
}
return
resultats;
}
Voici le programme complet :
import
std.stdio;
enum
Operation {
addition, soustraction, multiplication, division }
struct
Calcul {
Operation op;
double
premier;
double
second;
}
double
[] calculer
(
in
Calcul[] calculs ...)
{
double
[] resultats;
foreach
(
calcul; calculs) {
final
switch
(
calcul.op) {
case
Operation.addition:
resultats ~=
calcul.premier +
calcul.second;
break
;
case
Operation.soustraction:
resultats ~=
calcul.premier -
calcul.second;
break
;
case
Operation.multiplication:
resultats ~=
calcul.premier *
calcul.second;
break
;
case
Operation.division:
resultats ~=
calcul.premier /
calcul.second;
break
;
}
}
return
resultats;
}
void
main
(
) {
writeln
(
calculer
(
Calcul
(
Operation.addition, 1
.1
, 2
.2
),
Calcul
(
Operation.soustraction, 3
.3
, 4
.4
),
Calcul
(
Operation.multiplication, 5
.5
, 6
.6
),
Calcul
(
Operation.division, 7
.7
, 8
.8
)));
}
60-37. Surcharge de fonctions - Correction▲
Les deux surcharges suivantes utilisent les surcharges d'info() existantes :
void
info
(
in
Repas repas)
{
info
(
repas.moment);
write
(
'-'
);
info
(
ajouterDuree
(
repas.moment, MomentDeLaJournee
(
1
, 30
)));
write
(
" Repas, adresse : "
, repas.adresse);
}
void
info
(
planningDeLaJournee planning)
{
info
(
planning.reunionMatin);
writeln
(
);
info
(
planning.repas);
writeln
(
);
info
(
planning.reunionApresMidi);
}
Voici le programme complet, qui utilise tous ces types :
import
std.stdio;
struct
MomentDeLaJournee
{
int
heure;
int
minute;
}
void
info
(
in
MomentDeLaJournee moment)
{
writef
(
"%02s:%02s"
, moment.heure, moment.minute);
}
MomentDeLaJournee ajouterDuree
(
in
MomentDeLaJournee debut,
in
MomentDeLaJournee duree)
{
MomentDeLaJournee resultat;
resultat.minute =
debut.minute +
duree.minute;
resultat.heure =
debut.heure +
duree.heure;
resultat.heure +=
resultat.minute /
60
;
resultat.minute %=
60
;
resultat.heure %=
24
;
return
resultat;
}
struct
Reunion
{
string sujet;
size_t nombreDeParticipants;
MomentDeLaJournee debut;
MomentDeLaJournee fin;
}
void
info
(
in
Reunion reunion)
{
info
(
reunion.debut);
write
(
'-'
);
info
(
reunion.fin);
writef
(
" Réunion \"%s\" avec %s personnes"
, reunion.sujet,
reunion.nombreDeParticipants);
}
struct
Repas
{
MomentDeLaJournee moment;
string adresse;
}
void
info
(
in
Repas repas)
{
info
(
repas.moment);
write
(
'-'
);
info
(
ajouterDuree
(
repas.moment, MomentDeLaJournee
(
1
, 30
)));
write
(
" Repas, adresse : "
, repas.adresse);
}
struct
planningDeLaJournee
{
Reunion reunionMatin;
Repas repas;
Reunion reunionApresMidi;
}
void
info
(
planningDeLaJournee planning)
{
info
(
planning.reunionMatin);
writeln
(
);
info
(
planning.repas);
writeln
(
);
info
(
planning.reunionApresMidi);
}
void
main
(
)
{
immutable reunionCyclisme =
Reunion
(
"Cyclisme"
, 4
,
MomentDeLaJournee
(
10
, 30
),
MomentDeLaJournee
(
11
, 45
));
immutable dejeuner =
Repas
(
MomentDeLaJournee
(
12
, 30
), "Istamboul"
);
immutable reunionBudget =
Reunion
(
"Budget"
, 8
,
MomentDeLaJournee
(
15
, 30
),
MomentDeLaJournee
(
17
, 30
));
immutable planningAujourdhui =
planningDeLaJournee
(
reunionCyclisme,
dejeuner,
reunionBudget);
info
(
planningAujourdhui);
writeln
(
);
}
La fonction main() peut aussi n'être écrite qu'avec des objets littéraux :
void
main
(
)
{
info
(
planningDeLaJournee
(
Reunion
(
"Cyclisme"
, 4
,
MomentDeLaJournee
(
10
, 30
),
MomentDeLaJournee
(
11
, 45
)),
Repas
(
MomentDeLaJournee
(
12
, 30
), "Istamboul"
),
Reunion
(
"Budget"
, 8
,
MomentDeLaJournee
(
15
, 30
),
MomentDeLaJournee
(
17
, 30
))));
writeln
(
);
}