Jusqu'ici, nous avons étudié la structure des programmes Pascal ainsi que les déclarations de constantes et variables. Ces notions sont fondamentales mais nous n'avons pas encore vu une miette (jusqu'à maintenant) des fameuses instructions en Pascal dont nous parlions au début du guide.
Dans le langage Pascal, les instructions doivent être regroupés en blocs nommés procédures et fonctions, similaires aux blocs déjà connus de vous comme le bloc uses ou le bloc var. Un aspect de la philpsophie du langage Pascal est en effet que toute tâche complexe peut être découpée en tâches élémentaires. La tâche complexe, c'est l'application, tandis que les tâches élémentaires, ce seront les procédures et les fonctions. Chaque procédure ou fonction effectuera un travail particulier et bien ciblé (par exemple, répondre au clic sur un bouton).
Vous voyez maintenant l'intérêt d'étudier les procédures et les fonctions : il n'y a qu'après cela que nous pourrons réellement commencer à programmer en Pascal. Il serait en effet impossible d'utiliser seulement le fichier-projet.
Note à ceux qui connaissent : La syntaxe des procédures et des fonctions va être présentée ici dans sa forme de base. Les diverses variantes (conventions d'appel, paramètres variables ou constants, surcharges, paramètres ouverts) seront présentées en temps opportun mais bien plus tard dans le guide. L'objectif de ce guide n'est pas de recopier l'aide en ligne de Delphi (qui ne vaut rien au plan pédagogique) qui donne d'un coup toutes les options, mais plutôt de proposer une méthode progressive qui laisse le temps de s'habituer à une version de base pour y greffer petit à petit d'autres possibilités.
Nous allons étudier dans un premier temps les procédures, puis les fonctions, mais sachez dés à présent que les deux notions, du point de vue syntactique, sont assez proches, et on se permettra de parler de fonction avant de les avoir vues car les fonctions sont en quelque sorte des procédures évoluées. La fin du chapitre sera consacrée à une longue manipulation avec Delphi pour vous permettre de vous exercer tout de suite. Entre temps, hélas, il faudra freiner votre envie de tester vos essais, car les connaissances nécessaires ne sont pas encore tout-à-fait disponibles, mais c'est tout proche.
Les procédures sont en fait constituées par un ou deux blocs de texte pascal, dont le premier est optionnel et permet de déclarer la procédure. Nous reviendrons plus loin sur ce premier bloc qui n'a pas toujours d'utilité. Le second bloc, lui, est obligatoire et constitue le corps de la procédure (on dit également son implémentation). Les deux endroits possibles pour écrire ce bloc sont :
...
{$R *.RES}
{ <-- Ici }
begin
Application.Initialize;
...
Ce bloc doit être écrit à l'extérieur des blocs de déclaration comme uses, var et const. Par contre, il sera possible d'écrire une procédure à l'intérieur d'une autre procédure. Ceci ne sera pas souvent utile, mais je tâcherai de vous trouver un exemple pertinent dans la suite du guide.
Passons maintenant à l'aspect pratique : la syntaxe du bloc.
(Cette présentation sous forme de spécifications est affreuse, j'en conviens volontier, mais c'est à mon sens le moins mauvais moyen
de commencer)
Le bloc débute par le mot Pascal réservé procedure qui définit
clairement le bloc comme étant une procédure. Le second mot est un identificateur qui donne le nom de la procédure. Ce
nom sera utilisé dans l'instruction qui commandera l'exécution de la procédure.
Vient ensuite, éventuellement, une liste de paramètres entre parenthèses. Les paramètres sont des données intervenant dans la procédure, mais dont vous ne pouvez connaître la valeur : les paramètres seront renseignés à chaque exécution de la procédure. Considérons par exemple que vous avez une procédure qui dessine un cercle : les paramètres seront alors la position du centre et le rayon. Vous ne connaissez pas la valeur de ces paramètres mais vous allez quand même les employer dans la procédure.
La liste de paramètres, si elle est présente, est constituée d'au moins un paramètre, mais peut en contenir plus. Chaque paramètre est séparé des autres par un point-virgule. La liste ne comporte pas de point-virgule à son début ni à sa fin. Voici la syntaxe d'une liste de paramètres :
Chaque paramètre est de la forme suivante :
Si plusieurs paramètres sont du même type, il est possible de les regrouper comme suit :
Mais c'en est assez de toutes ces spécifications : voici des exemples (juste pour la première ligne).
procedure Explosion1;
procedure Explosion2 (Force: Integer);
procedure Explosion3 (Force, Vitesse: Integer);
procedure Explosion4 (Force, Vitesse: Integer; Son: Boolean);
procedure Explosion5 (Force, Vitesse: Integer; Son: Boolean;
Forme: TFormeExplosion);
Ces 5 exemples, outre leur coté un peu sordide, montrent les différentes possibilités qui existent pour les paramètres : aucun, un seul, plusieurs, avec des regroupements, de différents types. Le 5ième exemple montre qu'on peut couper cette première ligne en plusieurs plus courtes à des fins de présentation, sans toutefois tomber dans l'excés.
Passons maintenant à la deuxième ligne : les déclarations locales. Ce sont des déclarations de constantes (blocs const) de variables (blocs var) ou d'autres procédures. L'ensemble de ces déclarations locales ne seront accessibles, contrairement aux déclarations normales, qu'à l'intérieur de la procédure. On parlera alors de variables, de constantes et de procédures locales.
Exemple :
procedure Cercle (X, Y: Integer; Rayon: Word);
var
Angle: Single;
begin
end;
Dans l'exemple ci-dessus, une seule déclaration locale a été faite : celle d'une variable 'Angle' de type 'single'. Cette variable, qui servira pendant le tracé du cercle, ne doit pas être déclarée à l'extérieur de la procédure dans un bloc var car cette variable n'a d'utilité qu'à l'intérieur de la procédure. Voici cependant un extrait de code qui déclarerait 'Angle' à l'extérieur de la procédure :
var
Angle: Single;
procedure Cercle (X, Y: Integer; Rayon: Word);
begin
end;
Lorsqu'une variable (resp. une constante) est déclarée hors de toute procédure (ou fonction), elle est appelée variable (resp.
constante) globale. Lorsque, par contre, elle est déclarée à l'intérieur d'une procédure ou d'une fonction, on l'appelle
variable (resp. constante) locale.
Il faut éviter au maximum les variables globales (mais pas les constantes) : elles sont non seulement contraires à la philosophie du
langage Pascal mais monopolisent également davantage de ressources que les variables locales car restent en mémoire en permanence
tandis que les variables locales n'existent en mémoire que pendant que la procédure est exécutée. Pensez pour vous convaince que
l'effort en vaut la peine à ces programmes que vous lancez sur votre ordinateur et qui en ralentissent énormément le fonctionnement.
Passons maintenant au reste de la procédure : les mots Pascal réservés begin et end (sans le point-virgule final) délimitent le début et la fin des instructions contenues dans la procédure. L'ensemble des trois (begin, instructions et end) constitue ce qu'on appelle un bloc d'instructions (souvenez-vous de ce terme, il sera utilisé plus tard pour désigner ces trois éléments).
Le terme instructions désigne une suite d'instructions, terminées chacune par un point-virgule. Les puristes vous diront que la dernière instruction ne doit pas avoir de point-virgule final (et ils ont raison) mais ce dernier point-virgule avant le mot réservé end est toutefois autorisé et est en pratique très répandu, jusqu'à être présent dans l'aide en ligne de Delphi. Vous pourrez donc vous permettre d'écrire ce dernier point-virgule sans aucun remords.
Quant aux instructions proprement dites, elles seront vues progressivement dans la suite du guide, car elles peuvent prendre bon nombre de formes et requièrent des connaissances spécifiques.
Les procédures ne sont pas acceptées dans l'interface des unités, pourtant, seule l'interface est accessible depuis l'extérieur. Comment accèder alors à ces procédures (et fonctions) depuis l'extérieur ? De même, une procédure ne peut être utilisée que par la partie de l'unité qui la suit, comment dans ce cas faire si deux procédures doivent s'appeler mutuellement ?
la réponse à ces deux questions est dans le premier bloc optionnel dont nous parlions au début de la partie. Ce bloc, c'est la déclaration de la procédure. Cette déclaration est constituée par la première ligne du deuxième bloc (jusqu'au premier point-virgule inclus), avec quelques exceptions que nous verrons quand l'occasion se présentera. La déclaration d'une procédure (resp. d'une fonction) précède toujours cette procedure (resp. cette fonction) puisque la déclaration doit se trouver dans l'interface de l'unité. La procédure (resp. la fonction) est utilisable tout de suite après sa déclaration. Du fait de la présence de la déclaration dans l'interface, la procédure (resp. la fonction) devient accessible depuis l'extérieur de l'unité.
Voici un exemple complet (sans instructions dans la procédure) :
unit test;
interface
procedure Cercle (X, Y: Integer; Rayon: Word);
implementation
procedure Cercle (X, Y: Integer; Rayon: Word);
var
Angle: Single;
begin
end;
end.
Comme on l'a dit au début du chapitre, les fonctions sont assez proches des procédures. En fait, une fonction est une procédure avec une possibilité de plus : celle de renvoyer un résultat final. La syntaxe change alors un peu, mais seule la première ligne change et devient :
'type_resultat' indique, dans la définition ci-dessus, le type du résultat de la fonction, c'est-à-dire le type d'une variable utilisable dans chaque fonction, bien que non déclarée : result. result est en effet utilisable au même titre qu'une variable, mis à part cette particularité unique de n'être pas déclaré. Ceci amène une restricton : l'identificateur 'result' est réservé à Delphi, vous n'y avez pas droit. La valeur de result après que la dernière instruction de la fonction ait été exécutée devient le résultat de la fonction. Vous ne voyez peut-être pas encore bien comment cela peut marcher : c'est un peu normal, lisez la suite et vous comprendrez tout.
Pour fixer le résultat d'une fonction, il va nous falloir donner une valeur à une variable (et cette variable sera 'result'). Cela se fait au moyen (je vous le donne en mille...) d'une instruction. Cette instruction a un nom : c'est une affectation. Sa syntaxe est la suivante :
Attention : le point-virgule final a ici été omis car les instructions sont séparées par ces point-virgules : ces derniers sont considérés comme ne faisant pas partie des instructions.
Voici un exemple qui donne un résultat (fixe) à une fonction :
function TauxEuro: Single;
begin
Result := 6.55957;
end;
Parmi les choses à remarquer dans le listing ci-dessus, le mot clé function qui débute la fonction. Le type de résultat a été fixé à 'single' car le taux de l'euro n'est pas un nombre entier. La variable 'Result', accessible puisque l'on est dans une fonction, est par conséquent de type 'single'.
La seule instruction de la fonction est une affectation : elle fixe la valeur de 'Result' au taux de l'euro, qui est un nombre à virgule.
Cet exemple n'est hélas pas très heureux (mais je suis bien trop fatigué pour vous en chercher un autre), car on aurait pu se servir d'une constante, ce qui serait revenu au même :
const
TauxEuro = 6.55957;
Mais le terme 'valeur' dans la structure de l'affectation peut être beaucoup de choses, parmi lesquelles :
Voici un exemple illustrant cela :
unit test;
interface
function AireDisque(Rayon: Single): Single;
implementation
function AireDisque(Rayon: Single): Single;
begin
Result := PI * Rayon * Rayon;
end;
end.
Ce petit exemple illustre diverses choses :
Remarque : La fonction PI renvoie un nombre de type 'extended' pour donner un nombre maximal de décimales de PI. C'est le type le plus large du calcul, donc le résultat des multiplication est de type 'extended'. Pourtant, 'result' est de type 'single', qui est beaucoup moins large que 'extended'. Une conversion implicite a eu lieu pendant l'affectation : ce qui ne pouvait pas être stocké dans 'result' a tout simplement été perdu, mais ce n'est pas grave : qu'aurions-nous fait d'un aire avec 20 décimales ?
Nous venons d'apprendre à écrire des procédures et des fonctions, et même à donner un résultat aux fonctions. Mais ceci ne sert à rien si nous ne savons pas faire appel à ces procédures et fonctions (les exécuter). L'appel d'une procédure ou d'une fonction est de la forme :
'nom_de_procedure' est le nom de la procédure ou de la fonction que l'on veut exécuter. Si cette procédure ou cette fonction a des paramètres, il faudra fournir des valeurs de type correct et dans l'ordre de déclaration des paramètres. Les valeurs des paramètres sont données entre parenthèses, et les valeurs sont séparées entre elles par des virgules (et non pas par des points-virgules). Ces valeurs peuvent être directement données (un nombre, une chaîne de caractères), être les valeurs de constantes, de variables, de paramètres, ou de calculs dont le type de résultat convient.
Dans le cas d'une procédure, l'appel (l'exécution) d'une procédure constitue une instruction complète. Par contre, dans le cas d'une fonction, l'appel peut être inséré dans une instruction : le bloc d'appel avec la syntaxe décrite ci-dessus se comportera comme une constante dont le type est celui du résultat de la fonction. Les exemples ci-dessous illustrerons cela.
Exemple :
function VolumeCylindre1(Hauteur: Single): Single;
begin
Result := AireDisque(1) * Hauteur;
end;
Cette nouvelle fonction calcule le volume d'un cylindre de rayon 1. Le seul paramètre est la hauteur du cylindre. Plutôt que de calculer l'aire du disque de base dans la fonction, on utilise celle qui a été écrite précédemment, et qui a besoin d'un paramètre de type single. On lui donne cet unique paramètre, entre parenthèses : c'est la valeur 1. Vous voyez en passant que 'result' est encore calculé à partie d'une opération, qui fait elle-même appel à une autre fonction : 'AireDisque'.
Comme vous pouvez le constater, cette fonction a un intérêt assez limité, puisque le rayon du cylindre est fixé. On va donc en écrire une plus générale à laquelle on va ajouter un paramètre nommé 'RayonBase' de type single. Il restera à faire intervenir ce paramètre dans le calcul :
function VolumeCylindre(RayonBase, Hauteur: Single): Single;
begin
Result := AireDisque(RayonBase) * Hauteur;
end;
l'exemple ci-dessus est déjà plus pertinent : la valeur du paramètre 'RayonBase' de la fonction 'VolumeCylindre' est transmise en tant que valeur pour le paramètre 'Rayon' de la fonction 'AireDisque'. La fonction AireDisque est exécutée et renvoie l'aire du disque, que l'on multiplie par la hauteur pour obtenir le volume.
Exercice 1 : (voir la solution)Pour fonctionner, la deuxième fonction doit pouvoir avoir accès à la première. Il y a plusieurs moyens que vous connaissez déjà :
forward;
('forward' signifie littéralement 'plus loin')function AireDisque(Rayon: Single): Single; forward;
C'est ce qu'on appellera une déclaration forward. L'intérêt principal des déclarations forward est de ne pas avoir à déclarer une fonction (ou une procédure) dans l'interface si on ne veut vraiment pas la laisser accessible de l'extérieur de l'unité.
Pour ceux que cela intéresse, la raison pour laquelle on ajoute forward est assez simple : si on ne l'ajoute pas, tout ce qui suivra dans l'implémentation sera considéré, à tort évidemment, comme étant des déclarations locales de la fonction, ce qui ne manquera de provoquer d'innombrables erreurs lors de la compilation.
Voici maintenant des exemples complets faisant apparaître chacune des différentes possibilités :
Exemple 1 : Fonctions dans le bon ordre
unit test;
interface
implementation
function AireDisque(Rayon: Single): Single;
begin
Result := PI * Rayon * Rayon;
end;
function VolumeCylindre(RayonBase, Hauteur: Single): Single;
begin
Result := AireDisque(RayonBase) * Hauteur;
end;
end.
Exemple 2 : Fonctions dans l'ordre inverse, avec une déclaration dans l'interface
unit test;
interface
function AireDisque(Rayon: Single): Single;
implementation
function VolumeCylindre(RayonBase, Hauteur: Single): Single;
begin
Result := AireDisque(RayonBase) * Hauteur;
end;
function AireDisque(Rayon: Single): Single;
begin
Result := PI * Rayon * Rayon;
end;
end.
Exemple 3 : Fonctions dans l'ordre inverse, avec une déclaration forward dans l'implémentation
unit test;
interface
implementation
function AireDisque(Rayon: Single): Single; forward;
function VolumeCylindre(RayonBase, Hauteur: Single): Single;
begin
Result := AireDisque(RayonBase) * Hauteur;
end;
function AireDisque(Rayon: Single): Single;
begin
Result := PI * Rayon * Rayon;
end;
end.
Exemple 4 : Fonctions dans deux unités différentes
unit test;
interface
uses
calcul;
implementation
{ Le bloc uses :
uses
calcul;
aurait aussi pu être placé à cet endroit, en enlevant ce descriptif et les marques de commentaire }
function VolumeCylindre(RayonBase, Hauteur: Single): Single;
begin
Result := AireDisque(RayonBase) * Hauteur;
end;
end.
unit calcul;
interface
function AireDisque(Rayon: Single): Single;
implementation
function AireDisque(Rayon: Single): Single;
begin
Result := PI * Rayon * Rayon;
end;
end.
Il est essentiel de manipuler un peu ces notions pour voir comment elles marchent. Nous allons avoir besoin de quelques petits compléments avant les manipulations. Allez donc sous Delphi, et ouvrez le projet PremierEssai dans son état actuel. Si vous ne voyez pas la seule fiche du projet, affichez l'unité qui lui correspond, à savoir 'principale' puis appuyez sur F12 pour basculer de l'unité à la fiche.
Une fois la fiche visible, effectuez un double-clic sur le bouton 'Action !'. Cela a pour effet de vous faire retourner dans l'unité 'principale' où du texte a été ajouté. Le curseur se trouve maintenant entre le begin et le end d'une procédure :
Note : plus tard, si cette procédure venait à être effacée par Delphi (rassurez-vous, Delphi ne l'efface que lorsque vous n'avez rien ajouté au "squelette" ci-dessous), il suffira de renouveler cette manipulation pour la faire revenir.
procedure TForm1.Button1Click(Sender: TObject);
begin
{ <-- Curseur }
end;
Vous allez devoir (je suis désolé, mais c'est impossible de faire autrement) ignorer les choses suivantes encore pendant un certain temps (mais je vous promets que cela ne durera pas trop longtemps) :
La seule chose qu'il est important pour vous de savoir maintenant est que cette procédure sera exécutée à chaque fois qu'un clic sera effectué sur le bouton pendant l'exécution de l'application.
Insérez le code présenté ci-dessous (entrez juste l'instruction puisque le reste est déjà écrit) :
procedure TForm1.Button1Click(Sender: TObject);
begin
ShowMessage('Salut !');
end;
Et lancez l'application. Lorsque vous cliquez sur le bouton, une petite fenêtre avec le texte 'Salut !' et un bouton OK apparaît :
Ceci ne constitue qu'un premier essai qui vous permet de vérifier par vous-même votre agilité sous Delphi. Lorsque vous en avez assez vu, quittez l'application (n'oubliez jamais de quitter l'application avant de la modifier sous Delphi, les résultats seraient assez imprévisibles). Nous allons maintenant passer à quelque chose de plus consistant.
Ajoutez maintenant une nouvelle unité au projet (une unité sans fiche) : pour cela, allez dans le menu 'Fichier', puis 'Nouveau...' et choisissez 'Unité' dans la liste qui vous est proposée. Une nouvelle unité est alors générée par Delphi, avec un texte minimal (le bloc unit, les deux parties interface et implémentation et le end final). Commencez par enregistrer cette unité (Menu 'Fichier', puis 'Enregistrer'), en donnant 'calculs' comme nom de fichier ('.pas' sera ajouté automatiquement, et la première ligne de l'unité changera pour afficher le nouveau nom : 'calculs'.
Dans cette nouvelle unité, écrivez la fonction 'AireDisque' (texte ci-dessus) et déclarez-là pour qu'elle soit accessible de l'extérieur. Essayez de le faire sans regarder le listing ci-dessous. Une fois terminé, comparez votre travail avec ce qui suit :
unit calculs;
interface
function AireDisque(Rayon: Single): Single;
implementation
function AireDisque(Rayon: Single): Single;
begin
Result := PI * Rayon * Rayon;
end;
end.
Il va maintenant nous falloir appeler cette fonction. Pour cela, il suffira d'écrire :
AireDisque(3.2);
Mais il serait plus avantageux de pouvoir stocker ce résultat, même si nous n'allons rien en faire dans l'immédiat. Pour stocker ce résultat, il nous faut de toute manière une variable, et son type doit être choisi pour que la variable accepte les valeurs renvoyées par AireDisque. 'single' paraît donc le plus adapté. Sans regarder le listing ci-dessous, déclarez une variable locale nommée 'Aire' de type 'single' dans la procédure nommée 'TForm1.Button1Click'. Vérifiez ensuite ci-dessous :
procedure TForm1.Button1Click(Sender: TObject);
var
Aire: Single;
begin
ShowMessage('Salut !');
end;
Nous allons pouvoir utiliser cette variable pour stocker le résultat de la fonction AireDisque. Ceci se fera au moyen d'une affectation (Le résultat de la fonction sera affecté à la variable 'Aire' après son exécution). L'instruction sera donc :
Aire := AireDisque(3.2);
L'affectation étant la moins prioritaire, AireDisque sera exécutée, avec 3.2 comme valeur du paramètre 'Rayon', et le résultat sera stocké dans la variable 'Aire'.
Tapez cette instruction à la fin de la procédure 'TForm1.Button1Click'. Le code source de la procédure doit maintenant être :
procedure TForm1.Button1Click(Sender: TObject);
var
Aire: Single;
begin
ShowMessage('Salut !');
Aire := AireDisque(3.2);
end;
Il reste un détail à règler : 'AireDisque' est une fonction de l'unité 'Calculs', il vous faudra donc utiliser cette unité dans l'unité 'Principale' (bloc uses de l'interface).
Vous pouvez maintenant essayer de lancer l'application, mais comme nous n'avons rien prévu pour afficher le résultat à l'écran comme nous l'avions fait pour la chaine 'Salut !', rien d'autre ne s'affichera quand vous cliquerez sur le bouton, et pourtant, la fonction AireDisque sera appelée et son résultat stocké dans 'Aire'. Il est assez tentant de vérifier cela, non ?
Pour cela, il va nous falloir afficher à l'écran un nombre à virgule. C'est impossible directement : il va falloir convertir ce nombre en chaîne de caractère (transformer par exemple 1.23 en '1.23' car seules les chaînes de caractères peuvent être affichées via la procédure 'ShowMessage'. La conversion se fera au moyen de l'utilisation de la fonction nommée 'FloatToStr' (lisez « Float To Str », « de Flottant à Chaîne » en français). Cette fonction accepte un unique paramètre de type 'extended' et son résultat est de type 'string' (chaîne de caractères). La valeur du paramètre, dans notre cas, sera donnée par la variable 'Aire'.
Il va nous falloir une variable de type 'string' pour stocker le résultat de cette fonction : déclarez donc (toujours dans la même procédure une variable 'ChaineAire' de type 'string'. Ajoutez en fin de procédure l'instruction qui affecte à 'ChaineAire' le résultat de la fonction 'FloatToStr' à laquelle on donne comme unique paramètre la variable 'Aire'. Regardez ensuite le listing ci-dessous pour vous corriger.
procedure TForm1.Button1Click(Sender: TObject);
var
Aire: Single;
ChaineAire: String;
begin
ShowMessage('Salut !');
Aire := AireDisque(3.2);
ChaineAire := FloatToStr(Aire);
end;
Lorsque vous cliquerez sur le bouton, un petit message s'affichera, puis la fonction AireDisque sera exécutée et son résultat sera stocké dans 'Aire'. Enfin, la valeur de cette variable est transmise comme valeur de paramètre à la fonction 'FloatToStr' qui renvoie une chaîne de caractères. Cette dernière sera stockée dans la variable 'ChaineAire'.
Il ne reste plus maintenant qu'à afficher cette chaîne. Nous allons nous servir à nouveau de 'ShowMessage' en transmettant comme valeur de l'unique paramètre la valeur de 'ChaineAire' qui est bien de type chaîne. Essayez de rajouter à la fin de la procédure l'appel de la procédure 'ShowMessage' avec pour paramètre 'ChaineAire' (c'est plus simple que ce qui précède). Corrigez-vous, désormais comme d'habitude, avec le listing suivant :
procedure TForm1.Button1Click(Sender: TObject);
var
Aire: Single;
ChaineAire: String;
begin
ShowMessage('Salut !');
Aire := AireDisque(3.2);
ChaineAire := FloatToStr(Aire);
ShowMessage(ChaineAire);
end;
Je tiens un pari sur votre erreur probable : vous avez peut-être bien écrit 'ChaineAire' au lieu de ChaineAire. La première écriture, qui est fausse, désigne la chaîne contenant le texte « ChaineAire ». Le second désigne la variable ChaineAire. C'est déjà une variable de type chaîne, qui n'a donc pas besoin d'être à nouveau mise entre simples quotes.
Vous pouvez maintenant lancer l'application : deux messages vont s'afficher, l'un que vous connaissez déjà, et l'autre qui est nouveau, il doit ressembler à cela :
Ce qui répond bien à nos attentes.
Note : Un problème subsiste : pour le trouver, utilisez une calculatrice (même celle de Windows) convient, en utilisant la valeur de PI : 3.141592653589. Vous avez deviné ? Eh oui, le résultat donné par notre petite application est... faux ! Pas de beaucoup, je vous l'accorde : les 4 premières décimales sont justes. Mais je profite de cette occasion pour vous apprendre une chose : le problème est insoluble en programmation comme avec une calculatrice : vous aurez toujours un résultat approché, jamais exact. On pourrait augmenter la précision du calcul en n'utilisant pas le type 'single' mais le type 'extended' plus précis, mais l'erreur reviendrait au bout de quelques décimales. Vous ne pouvez pas y faire grand chose, mis à part être vigilant et tenir compte des approximations lorsque vous écrirez des programmes faisant intervenir des nombres à virgule.
Il faudrait maintenant faire quelque chose de plus convivial. Annoncer comme cela un nombre n'est pas très convenable. Il faudrait mieux quelque chose du genre : « L'aire d'un disque de rayon 3,2 cm vaut ??? cm² ». Pour cela, nous allons utiliser une notion vue avec les chaînes de caractères : la concatènation. On va en effet coller plusieurs chaînes ensemble, même si l'une d'entre elle est la valeur d'une variable, c'est possible.
Déclarez donc une autre variable nommée 'ChaineAire2' de type 'string' (pensez que plusieurs variables du même type peuvent se déclarer en un seul bloc). Cette nouvelle variable va devoir recevoir la concatènation de plusieurs chaînes, qui vont constituer ensemble un message complet. Voici :
ChaineAire2 := 'L''aire d''un disque de rayon 3,2 cm vaut ' + ChaineAire +
' cm².';
Les deux chaînes que l'on donne directement entre simples quotes seront concatènées avec la valeur de 'ChaineAire' pour donner un message compréhensible par l'utilisateur. Restera à bien afficher ce nouveau message et non plus l'ancien dans l'instruction finale. Voici le nouveau code source :
procedure TForm1.Button1Click(Sender: TObject);
var
Aire: Single;
ChaineAire, ChaineAire2: String;
begin
ShowMessage('Salut !');
Aire := AireDisque(3.2);
ChaineAire := FloatToStr(Aire);
ChaineAire2 := 'L''aire d''un disque de rayon 3,2 cm vaut ' + ChaineAire +
' cm².';
ShowMessage(ChaineAire2);
end;
Effectuez les modifications dans Delphi et lancez l'application, vous devriez maintenant obtenir ceci :
Ce qui, vous me l'accorderez, est nettement plus convivial, mais qui n'est encore pas terminé. supprimez la première instruction (celle qui affiche 'Salut !') car vous avez certainement aussi marre que moi de voir s'afficher cette petite fenêtre ! (songez à la première fois, comme vous étiez content de la voir, vous vous habituez déjà à savoir l'afficher)
Nous allons maintenant passer à une autre phase de la programmation : l'optimisation du code source. En effet, notre code a beau fonctionner, il ferait hurler la plupart des habitués à Pascal. Nous allons donc procèder à quelques améliorations de fond.
En tout premier lieu, la variable ChaineAire2 n'a pas de raison d'être. En effet, il est autorisée d'affecter à une variable un résultat qui utilise cette variable dans sa détermination. Je m'explique : vous avez une variable, vous pouvez en une seule fois la modifier et affecter le résultat modifié dans la variable puisque l'opération est effectuée d'abord, et l'affectation ensuite. Voici ce que cela donnera avec notre exemple :
ChaineAire := 'L''aire d''un disque de rayon 3,2 cm vaut ' + ChaineAire +
' cm².';
Que cela ne vous choque pas : la valeur de ChaineAire sera d'abord concatènée avec les deux autres chaînes, et ensuite seulement, la chaîne résultante sera stockée dans ChaineAire. Ceci va nous permettre de supprimer la variable ChaineAire2 tout en n'oubliant pas d'afficher non plus ChaineAire2 mais ChaineAire dans la dernière instruction. Voici le code source avec ces modifications :
procedure TForm1.Button1Click(Sender: TObject);
var
Aire: Single;
ChaineAire: String;
begin
Aire := AireDisque(3.2);
ChaineAire := FloatToStr(Aire);
ChaineAire := 'L''aire d''un disque de rayon 3,2 cm vaut ' + ChaineAire +
' cm².';
ShowMessage(ChaineAire);
end;
Voilà qui est déjà mieux. Nous allons maintenant nous intéresser à la deuxième et à la troisième ligne. Chacune de ces deux lignes correspond à une instruction d'affectation. Le résultat de la fonction FloatToStr est d'abord stocké dans ChaineAire et cette chaîne est ensuite « habillée » pour en faire un message. Ces deux instructions peuvent être rassemblées en une seule :
ChaineAire := 'L''aire d''un disque de rayon 3,2 cm vaut ' + FloatToStr(Aire) +
' cm².';
En effet, utiliser le contenu de ChaineAire ou le résultat de FloatToStr revient au même puisque nous voulons qu'ils aient la même valeur (d'où la première affectation). Voici alors le nouveau code source de la procédure :
procedure TForm1.Button1Click(Sender: TObject);
var
Aire: Single;
ChaineAire: String;
begin
Aire := AireDisque(3.2);
ChaineAire := 'L''aire d''un disque de rayon 3,2 cm vaut ' + FloatToStr(Aire) +
' cm².';
ShowMessage(ChaineAire);
end;
Nous n'allons pas encore nous arrèter là. Une amélioration est encore possible : la variable 'ChaineAire' est utilisée dans une suite d'instructions particulière : une affectation puis une seule fois en tant que paramètre. C'est un cas typique qu'il vous faudra apprendre à reconnaitre. Dans ce cas, il est possible d'utiliser la valeur affectée à la variable directement en tant que valeur de paramètre. Voici donc le nouveau code source ; remarquez bien comme ce qui était auparavant affecté à ChaineAire est maintenant donné à 'ShowMessage' comme valeur de paramètre. La variable ChaineAire a été supprimée puisqu'elle n'est plus utilisée.
procedure TForm1.Button1Click(Sender: TObject);
var
Aire: Single;
begin
Aire := AireDisque(3.2);
ShowMessage('L''aire d''un disque de rayon 3,2 cm vaut ' + FloatToStr(Aire) +
' cm².');
end;
Notre procédure a déjà pas mal perdu de poids. Notez que la variable 'Aire' est dans le même cas que l'ex variable 'ChaineAire', et que l'on pourraît donc s'en passer. Par souci de lisibilité du code source de la procédure, et aussi parce que cette variable sera utilisée dans la suite du guide, nous la laisserons en place. Remarquez cependant que de 4 instructions et 3 variables, nous sommes descendus à 2 instructions et une seule variable : le code a été en grande partie optimisé.
La manipulation est maintenant terminée, vous pouvez télécharger le projet dans son état actuel ici ou, si vous avez téléchargé le guide pour le consulter hors connexion, vous pourvez ouvrir le sous-répertoire "Projets\Premier Essai 2\" du guide, et ouvrir le projet "PremierEssai.dpr" sous Delphi.
Cette partie vous a permis de sauter à pieds joints dans la programmation en Pascal. En effet, les procédures et les fonctions, sinon indispensables, sont souvent incontournables, au moins pour ce qui est de l'utilisation de ce que Delphi nous offre ('FloatToStr' et 'ShowMessage' par exemple).