Chapitre fonction membres, merci Hassan Azi

This commit is contained in:
Raphaël JAKSE 2015-04-15 19:10:37 +02:00
parent 602c9d2402
commit 44c7b43e47
3 changed files with 525 additions and 2 deletions

View File

@ -0,0 +1,165 @@
[set
title = "Fonctions membres"
partAs = correction
translator = "Raphaël Jakse"
]
# Des valeurs intermédiaires potentiellement négatives rfinent [c decrement()] légèrement plus compliquéée que [c increment()]~ :
[code=d <<<
struct MomentDuJour
{
// ...
void decrement(in Duree duree)
{
auto minutesARetrancher = duree.minute % 60;
auto heuresARetrancher = duree.minute / 60;
minute -= minutesARetrancher;
if (minute < 0) {
minute += 60;
++heuresARetrancher;
}
heure -= heuresARetrancher;
if (heure < 0) {
heure = 24 - (-heure % 24);
}
}
// ...
}
>>>]
Pour voir à quel point c'est plus facile avec des fonction membres [c toString()], jetons une fois de plus un œil à la surcharge d'[c info()] correspondant à la structure [c Reunion]~ :
[code=d <<<
void info(in Reunion reunion)
{
info(reunion.debut);
write('-');
info(reunion.fin);
writef(" Réunion \"%s\" avec %s participants",
reunion.sujet,
reunion.nombreParticipants);
}
>>>]
En nous servant de la fonction [c MomentDuJour.toString] déjà définie, l'implémentation de [c Reunion.toString] devient triviale~ :
[code=d <<<
string toString()
{
return format("%s-%s Réunion \"%s\" avec %s participants",
debut, fin, sujet, nombreParticipants);
}
>>>]
Voici le programme entier~ :
[code=d <<<
import std.stdio;
import std.string;
struct Duree
{
int minute;
}
struct MomentDuJour
{
int heure;
int minute;
string toString()
{
return format("%02s:%02s", heure, minute);
}
void increment(in Duree duree)
{
minute += duree.minute;
heure += minute / 60;
minute %= 60;
heure %= 24;
}
}
struct Reunion
{
string sujet;
int nombreParticipants;
MomentDuJour debut;
MomentDuJour fin;
string toString()
{
return format("%s-%s Réunion \"%s\" with %s attfinees",
debut, fin, sujet, nombreParticipants);
}
}
struct Repas
{
MomentDuJour moment;
string adresse;
string toString()
{
MomentDuJour fin = moment;
fin.increment(Duree(90));
return format("%s-%s Repas, Adresse: %s",
moment, fin, adresse);
}
}
struct PlanningJournalier
{
Reunion amReunion;
Repas repas;
Reunion pmReunion;
string toString()
{
return format("%s\n%s\n%s",
amReunion,
repas,
pmReunion);
}
}
void main()
{
auto ReunionVelo = Reunion("Vélo", 4,
MomentDuJour(10, 30),
MomentDuJour(11, 45));
auto repas = Repas(MomentDuJour(12, 30), "İstanbul");
auto reunionBudget = Reunion("Budget", 8,
MomentDuJour(15, 30),
MomentDuJour(17, 30));
auto emploiDuTempsAujourdhui = PlanningJournalier(ReunionVelo,
repas,
reunionBudget);
writeln(emploiDuTempsAujourdhui);
writeln();
}
>>>]
La sortie du programme est la même que la version précédente qui utilisait les surcharges de la fonction [c info()]~ :
[output <<<
10:30-11:45 Réunion "Vélo" avec 4 participants
12:30-14:00 Repas, Adresse : İstanbul
15:30-17:30 Réunion "Budget" avec 8 participants
>>>]

View File

@ -1,6 +1,362 @@
[set
title = "Fonction membres"
partAs = chapitre
translator = Hassan Azi
]
[fixme En attente d'écriture.]
Bien que ce chapitre se concentre principalement sur les structures, une grande partie des informations détaillées ci-après peuvent être également appliquées aux classes également.
Au cours de ce chapitre, nous couvrirons les méthodes des structures, notamment la méthode spéciale [c toString()] qui est utilisée afin de représenter les objets sous forme d'une chaîne de caractères, ou [c string].
Lorsqu'une structure ou une classe est définie, on définit aussi souvent un certain nombre de fonctions avec elle. Nous avions déjà vu de exemples dans le chapitres précédents: [c ajouterDuree()] et la surcharge de [c info()] ont été écrites spécialement pour être utilisées avec le type [c TempsDuJour]. En quelque sorte, ces deux fonctions forment l'[i interface] de [c TempsDuJour].
[c ajouterDuree()] et [c info()] avaient pris pour premier paramètre un objet [c TempsDuJour] sur lequel elles allaient opérer. En plus de cela, comme c'est le cas pour toutes les fonctions jusque-là étudiées, nos deux fonctions avaient été définies au [i niveau du module] (''module level''), en dehors de toute autre portée.
Le fait qu'un ensemble de fonctions définisse l'interface d'une structure est un concept très répandu. Pour cette raison, les fonctions ayant une relation proche à un certain type peuvent être définies au sein de celui-ci.
[ = Définir des méthodes
Les fonctions définies à l'intérieur des accolades d'une [c struct] sont dites des [i méthodes]~ :
[code=d <<<
struct UneStructure
{
void methode(/* les paramètres de la fonction */)
{
// ... la définition de la fonction ...
}
// ... les autres membres de la structure ...
}
>>>]
Les méthodes peuvent être accédées de la même façon que les champs, en les séparant du nom de l'objet par un point:
[code=d <<<
[i objet.methode(arguments));
>>>]
Nous avions déjà utilisé les méthodes quand on avait pris soin de spécifier [c stdin] and [c stdout] lors des opérations d'entrée et de sortie~ :
[code=d <<<
stdin.readf(" %s", &nombre);
stdout.writeln(nombre);
>>>]
Dans ce code, nous avons effectué des appels aux méthodes [c readf()] et [c writeln()] qui opèrent respectivement sur les objets [c stdin] et [c stdout].
Définissions maintenant [c info()] en tant que méthode. Auparavant, nous l'avions définie ainsi~ :
[code=d <<<
void info(in TempsDuJour temps)
{
writef("%02s:%02s", temps.heure, temps.minute);
}
>>>]
Afin de transformer [c info()] en une méthode, il faudra non seulement déplacer sa définition à l'intérieur de la structure, mais aussi lui faire subir deux modifications~ :
[code=d <<<
struct TempsDuJour
{
int heure;
int minute;
void info() // (1)
{
writef("%02s:%02s", heure, minute); // (2)
}
}
>>>]
# La méthode ne prend plus l'objet comme paramètre.
# Suite à ceci, ele accède aux champs directement par [c heure] et [c minute].
La raison pour cela est que les méthodes sont toujours appelées sur un objet existant. Ce dernier est implicitement disponible pour la méthode~ :
[code=d <<<
auto temps = TempsDuJour(10, 30);
temps.info();
>>>]
La méthode [c info()] est appelée sur l'objet [c temps] ci-avant. Les variables [c heure] et [c minute] qui sont indiquées à l'intérieur de la définition de la fonction correspondent aux champs de l'objet [c temps], plus précisément, [c temps.heure] et [c temps.minute].
Ici, l'appel à la méthode est presque l'équivalent de l'appel à une fonction normale~ :
[code=d <<<
temps.info(); // méthode
info(temps); // fonction habituelle (la définition précédente)
>>>]
Dès qu'une méthode est appelée sur un objet, les champs de celui-ci sont implicitement accessibles pour la fonction~ :
[code=d <<<
auto matin = TempsDuJour(10, 0);
auto apresmidi = TempsDuJour(22, 0);
matin.info();
write('-');
apresmidi.info();
writeln();
>>>]
Appelée sur [c matin], les variables [c heure] et [c minute] utilisées à l'intérieur de la méthode ne sont autre que [c matin.heure] et [c matin.minute]. De même, en appelant [c apresmidi.info()], elles font référence à [c apresmidi.heure] et [c apresmidi.minute]~ :
[output <<<
10:00-22:00
>>>]
[ = [c toString()] pour les représentations sous formes de chaînes de caractères
Dans le chapitre précédent, nous venons de parler des limitations de la fonction [c info()]. Mais malgré tout ce que nous venons de voir, la fonction garde au moins un autre inconvénient: Bien qu'elle affiche le temps dans un format lisible, le programmeur devra toujours prendre soin d'expliciter le caractère [c '-'] ainsi que le caractère de fin de ligne.
Il serait plus pratique si les objets [c TempsDuJour] pouvaient être utilisés comme des types fondamentaux dans le code ci-après~ :
[code=d <<<
writefln("%s-%s", matin, apresmidi);
>>>]
En plus d'avoir réduit quatre lignes de code en seulement une, cela aurait aussi pour effet de pouvoir écrire l'objet à n'importe quel flux~ :
[code=d <<<
auto fichier = File("temps_information", "w");
fichier.writefln("%s-%s", matin, apresmidi);
>>>]
La méthode [c toString()] des types définis par l'utilisateur est un peu spéciale : elle est automatiquement appelée pour produire la représentation [c string] des objets. [c toString()] doit impérativement retourner la représentation [c string] de l'objet en question.
Avant de plonger dans les détails, étudions d'abord la définition de la fonction [c toString()]~ :
[code=d <<<
import std.stdio;
struct TempsDuJour
{
int heure;
int minute;
string toString()
{
return "a_faire";
}
}
void main()
{
auto matin = TempsDuJour(10, 0);
auto apresmidi = TempsDuJour(22, 0);
writefln("%s-%s", matin, apresmidi);
}
>>>]
Pour le moment, [c toString()] ne produit pas grand chose, mais l'exécution du code ci-avant montre qu'elle a effectivement été appelée à deux reprises par [c writefln()]~ :
[output <<<
a_faire-a_faire
>>>]
Remarquez aussi que [c info()] est désormais redondante vu que [c toString()] offre la même fonctionnalité.
La façon la plus simple d'implémenter [c toString()] serait d'appeler la fonction [c format()], celle-ci est disponible dans le module [c std.string]. Le fonctionnement de [c format()] est similaire à celui des fonctions d'affichage formaté comme [c writef()]. La seule différence réside dans le fait qu'au lieu d'afficher directement les variables, [c format()] retourne le résultat sous la forme d'une [c string].
[c toString()] peut donc directement retourner le résultat de [c format()]~ :
[code=d <<<
import std.string;
// ...
struct TempsDuJour
{
// ...
string toString()
{
return format("%02s:%02s", heure, minute);
}
}
>>>]
Remarquez que [c toString()] retourne uniquement la représentation textuelle de l'objet [i this]. [c writefln()] se charge d'afficher le reste: elle appelle la méthode [c toString()] pour chacun des deux objets tout en prenant soin d'afficher le caractère [c '-'] entre les deux appels, puis termine la ligne~ :
[output <<<
10:00-22:00
>>>]
La définition de [c toString()] qui a été expliquée ci-avant ne prend aucun paramètre; elle produit simplement une [c string] et la retourne. Cependant, il existe une autre façon de définir [c toString()], celle-ci prend en paramètre un [c delegate]. Nous étudierons cette définition plus tard dans le chapitre sur les pointeurs de fonctions, les delegates et les lambdas.
]
[ = Exemple: la méthode [c increment())]
Définissons une méthode qui ajoute une certaine durée aux objets [c TempsDuJour].
Avant d'aller plus loin, corrigeons d'abord un défaut de conception qui était là tout le temps. Dans le chapitre sur des structures, nous avions vu que l'addition de deux objets [c TempsDuJour] dans [c ajouterDuree] n'avait pas de sens~ :
[code=d <<<
TempsDuJour ajouterDuree(in TempsDuJour start,
in TempsDuJour duree) // absurde
{
// ...
}
>>>]
Il serait plus naturel d'ajouter une [i durée] à un point dans le temps. En ajoutant par exemple la durée d'un voyage à son temps de départ, on obtiendrait le temps d'arrivée.
D'un autre côté, soustraire deux points dans le temps est une opération naturelle, et dans ce cas le résultat serait une [i durée].
Le programme ci-après définit une structure [c Duree] d'une précision allant à la minute, ainsi qu'une fonction [c ajouterDuree()] qui l'utilise:
[code=d <<<
struct Duree
{
int minute;
}
TempsDuJour ajouterDuree(in TempsDuJour debut,
in Duree duree)
{
// Commençons par faire une copie de start
TempsDuJour resultat = debut;
// Ajoutons-y la durée
resultat.minute += duree.minute;
// Puis prenons soin de régler les débordements
resultat.heure += resultat.minute / 60;
resultat.minute %= 60;
resultat.heure %= 24;
return resultat;
}
unittest
{
// Un test banal
assert(ajouterDuree(TempsDuJour(10, 30), Duree(10))
== TempsDuJour(10, 40));
// Un temps à minuit
assert(ajouterDuree(TempsDuJour(23, 9), Duree(51))
== TempsDuJour(0, 0));
// Un temps dans le jour suivant
assert(ajouterDuree(TempsDuJour(17, 45), Duree(8 * 60))
== TempsDuJour(1, 45));
}
>>>]
Maintenant, redéfinissons cette fonction sous la forme d'une méthode. Jusqu'à présent, [c ajouterDuree()] retournait un nouvel objet à chaque appel. Définissons donc une méthode [c incrementer()] qui modifiera directement l'objet [i this]~ :
[code=d <<<
struct Duree
{
int minute;
}
struct TempsDuJour
{
int heure;
int minute;
string toString()
{
return format("%02s:%02s", heure, minute);
}
void $(HILITE incrementer)(in Duree duree)
{
minute += duree.minute;
heure += minute / 60;
minute %= 60;
heure %= 24;
}
unittest
{
auto temps = TempsDuJour(10, 30);
// Un test banal
temps$(HILITE .incrementer)(Duree(10));
assert(temps == TempsDuJour(10, 40));
// 15 heures plus tard devra être dans le jour suivant
temps$(HILITE .incrementer)(Duree(15 * 60));
assert(temps == TempsDuJour(1, 40));
// 22 heures et 20 minutes devra être à minuit
temps$(HILITE .incrementer)(Duree(22 * 60 + 20));
assert(temps == TempsDuJour(0, 0));
}
}
>>>]
[c incrementer()] incrémente la valeur de l'objet dépendamment de la durée qu'on lui passe. Plus tard, nous étudierons comment la surcharge d'opérateur ([i operator overloading]) va nous permettre d'ajouter une durée à l'aide de l'opérateur [c +=]~ :
[code=d <<<
temps += Duree(10); // sera expliqué dans un chapitre ultérieur
>>>]
Notez également que les blocs de tests unitaires [c unittest] peuvent être écrits à l'intérieur des définitions des [c struct] aussi, c'est surtout le cas lorsque l'on souhaite tester ses méthodes. Mais il reste tout de même possible de déplacer de tels blocs [c unittest] en dehors du corps de la structure:
[code=d <<<
struct TempsDuJour
{
// ... définition de la structure ...
}
unittest
{
// ... tests de la structure ...
}
>>>]
]
]
[ = Exercices
# Ajoutez une méthode [c decrementer()] à [c TempsDuJour] qui en retrancherait la durée de temps spécifiée. Tout comme pour [c incrementer()], elle devrait repasser au jour précédent s'il n'y a pas assez de temps dans le jour actuel. Par exemple, soustraire 10 minutes de 00:05 devra donner pour résultat 23:55.
En d'autres termes, implémentez [c decrementer()] de façon à ne pas échouer les tests unitaires suivant~ :
[code=d <<<
struct TempsDuJour
{
// ...
void decrementer(in Duree duree)
{
// ... veuillez implémenter cette fonction ...
}
unittest
{
auto temps = TempsDuJour(10, 30);
// Un test banal
temps.decrementer(Duree(12));
assert(temps == TempsDuJour(10, 18));
// 3 jours and 11 heures plus tôt
temps.decrementer(Duree(3 * 24 * 60 + 11 * 60));
assert(temps == TempsDuJour(23, 18));
// 23 heures and 18 minutes plus tôt devra être à minuit
temps.decrementer(Duree(23 * 60 + 18));
assert(temps == TempsDuJour(0, 0));
// 1 minute plus tôt
temps.decrementer(Duree(1));
assert(temps == TempsDuJour(23, 59));
}
}
>>>]
# Convertissez également les surcharges de [c infos()] en méthodes [c toString()] des structures [c Reunion], [c Repas] et [c MomentDeLaJournee]. (Consulter [[doc:corrections/surcharge_fonctions | les solutions du chapitre sur les surcharges de fonctions]] pour voir comment les différentes implémentations de [c info()]).
Vous remarquerez qu'en plus de rendre leurs structures respectives plus pratiques, les implémentations des méthodes [c toString()] ne prendront chacune qu'une ligne de code.
[[part:corrections/fonctions_membres | … Les solutions]]
]

View File

@ -26,7 +26,9 @@
Merci à sclytrac (forum D) pour ses corrections.
Merci à Stéphane Goujet pour ses relectures attentives et pointilleuses qui ont permise d'élever la qualité et la cohérence de la traduction.
Merci à Hassan Azi pour sa traduction du chapitre sur les fonctions membres.
Merci à Olivier Pisano pour une traduction d'excellente qualité de quelques chapitres.
Merci à Stéphane Goujet pour ses relectures attentives et pointilleuses qui ont permise d'élever la qualité et la cohérence de la traduction.
]