L'exemple présenté ici reprend le projet peinture en l'écrivant avec des fonctions. Ce nouvel exemple se trouve dans le répertoire Exemple-Sous-Programme/PeintureAvecFonctions.
Rappelons que le projet Peinture permet de calculer la surface à peindre d'une pièce rectangulaire, connaissant les dimensions de la pièce (longueur, largeur), le nombre de fenêtres et le nombres de portes.
Les dimensions des fenêtres, des portes et la hauteur des murs sont des constantes (LargeurFenetre=1.4 , HauteurFenetre=1.2, LargeurPorte=0.9, HauteurPorte=2.1, HauteurMur=2.5).
La dernière version (répertoire Exemple-Sous-Programme/Peinture) nous avait servi à introduire les procédures. Nous n'avions alors aucune notion de variables locales, ni de paramètres. Cette version du projet avait donc été écrite en utilisant uniquement des variables globales et des procédures sans paramètres.
Pour calculer la surface à peindre à l'aide de procédures, nous avions procédé par programmation descendante en découpant le problème en quatre sous-problèmes:
Nous avions associé une procédure à chacun de ces traitements. Voici le code de ces procédures et voici l'appel de ces procédures dans la procédure évènementielle associée au bouton Calculer.
La nouvelle version que nous allons présenter ici n'utilise au contraire aucune variable globale et effectue des calculs par l'intermédiaire de fonctions.
Voici le code de cette même procédure évènementielle dans la nouvelle version:
procedure TForm1.BT_CalculClick(Sender: TObject); var LargeurPiece, LongueurPiece, Surface_A_Peindre : double; NFenetre, NPorte : Integer; begin LireNombre (LargeurPiece, ZT_Largeur); LireNombre (LongueurPiece, ZT_Longueur); LireEntier (NFenetre, ZT_NF); LireEntier (NPorte, ZT_NP); Surface_A_Peindre := SurfaceDesMurs(LargeurPiece,LongueurPiece) - SurfaceDesFenetres(NFenetre) - SurfaceDesPortes(Nporte); AfficherNombre (Surface_A_Peindre,ZT_SP); end;
Le calcul de la surface à peindre se fait à présent en une seule affectation. Plus précisément, il est représenté par l'expression en jaune qui appelle trois fonctions:
Voici le code de ces trois fonctions.
L'entête d'une fonction commence nécessairement par le mot clé function. Ce dernier est suivi du nom de la fonction, puis optionnellement, de la liste des paramètres. Enfin, l'entête se termine par le type du résultat produit par la fonction (en jaune dans notre exemple). Dans notre cas, il s'agit du type double, car le résultat produit est un nombre à priori non entier.
Une fonction se distingue d'une procédure par le fait qu'elle retourne un résultat. Pour comprendre ce que signifie "retourner un résultat", il faut voir comment l'ordinateur exécute une expression contenant des appels de fonction. Nous reviendrons là-dessus un peu plus loin.
Dans notre exemple, chaque fonction ne contient qu'une seule instruction. Ce n'est pas vrai en général. Le corps d'une fonction peut contenir plusieurs instructions. Comment savoir alors qu'elle est le résultat de la fonction ? En Pascal, le résultat d'une fonction est déterminée par la dernière exécution d'une affectation de la forme:
Nom de la fonction := expression ;
Il faut donc nécessairement qu'une affectation de ce type soit présente dans le corps de la fonction afin que le résultat retourné par celle-ci soit défini.
Vous constaterez que dans notre exemple chaque fonction contient bien une instruction de ce type.
Voyons maintenant ce que signifie "retourner un résultat". Pour cela nous allons voir comment est évaluée l'expression contenant les appels de fonctions:
SurfaceDesMurs(LargeurPiece,LongueurPiece) - SurfaceDesFenetres(NFenetre) - SurfaceDesPortes(Nporte);
L'expression est évaluée de gauche à droite. L'ordinateur va donc commencer par évaluer l'appel de la fonction SurfaceDesMurs. Supposons que LargeurPiece = 3,6 et LongueurPiece = 4,3.
Comme dans les appels de procédures, les paramètres formels sont remplacés par les paramètres effectifs. Donc ici, LaP va prendre la valeur 3,6 et LoP, la valeur 4,3. Puis le corps de la fonction est exécuté avec ces valeurs. La fonction effectue donc le calcul suivant:
SurfaceDesMurs := 2 * (3,6 + 4,3) * HauteurMur;
Avec une hauteur de mur de 2,50m, cela fait 39,5m2.
A la fin de l'exécution de la fonction, ce résultat va "retourner" dans l'expression qui l'a appelé. Tout ce passe comme si la valeur 39,5 remplacait à présent l'appel de fonction dans cette expression. C'est à dire qu'il faut à présent évaluer l'expression:
39,5 - SurfaceDesFenetres(NFenetre) - SurfaceDesPortes(Nporte);
Mais l'évaluation de l'expression n'est pas terminée. L'ordinateur va à présent exécuter l'appel de la fonction SurfaceDesFenetres. Supposons que le nombre de fenêtres soit égal à trois.
nf, le paramètre formel de cette fonction, va donc prendre la valeur 3 et la fonction va faire le calcul suivant:
SurfaceDesFenetres := 3 * LargeurFenetre * HauteurFenetre;
Avec une largeur de fenêtre de 1,4m et une hauteur de fenêtre de 1,2m, cela donne 5,04m2.
Celle valeur retourne dans l'expression d'appel et nous obtenons:
39,5 - 5,04 - SurfaceDesPortes(Nporte);
Ici, l'ordinateur va d'abord effectuer la différence 39,5 - 5,04. Cela fait 34,46. Nous nous retrouvons donc avec l'expression:
34,46 - SurfaceDesPortes(Nporte);
Il reste donc à évaluer l'appel de la fonction SurfaceDesPortes. Supposons deux portes. Le paramètre formel np prend la valeur 2 et la fonction effectue le calcul suivant:
SurfaceDesPortes := 2 * LargeurPorte * HauteurPorte;
Avec une largeur de porte de 0,9m et une hauteur de porte de 2,1m, cela fait 3,78m2. En ramenant ce résultat dans l'expression de départ nous obtenons:
34,46 - 3,78;
ce qui fait 30,68m2. Vous pouvez vérifier que c'est bien le résultat affiché par le programme: