Guide Pascal et Delphi


précédentsommairesuivant

VI. Procédures et Fonctions

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).

Les procédures et les fonctions seront de ce fait les endroits privilégiés où nous écrirons les instructions en Pascal. Une instruction permettra l'exécution (on dira aussi l'appel) d'une procédure ou d'une fonction (des instructions contenues dedans). Il existe d'autres endroits que les procédures ou les fonctions où les instructions peuvent être écrites, mais chacun de ces endroits à une spécificité.

Le seul connu de vous actuellement est entre le mot begin et le mot end du fichier-projet. Cet endroit est réservé aux instructions qui doivent être exécutées au tout début de l'application, avant tout le reste.

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 syntaxique, 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.

VI-A. Procédures

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 :

  • Dans le fichier-projet, entre le bloc uses et le bloc d'instructions :

     
    Sélectionnez
    
    ...
    {$R *.RES}
    
    { <-- Ici }
    
    begin
      Application.Initialize;
    ...
    
  • Dans l'implémentation d'une unité.

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.

 
Sélectionnez

procedure identificateur [(paramètres)];
[déclarations locales]
begin
  [instructions]
end;

(Cette présentation sous forme de spécifications est affreuse, j'en conviens volontiers, 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 :

paramètre 1[; paramètre 2] ... [; paramètre n]

Chaque paramètre est de la forme suivante :

identificateur : type

Si plusieurs paramètres sont du même type, il est possible de les regrouper comme suit :

identificateur1, identificateur2 : type

Mais c'en est assez de toutes ces spécifications : voici des exemples (juste pour la première ligne).

 
Sélectionnez

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 :

 
Sélectionnez

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 :

 
Sélectionnez

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 convaincre 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) :

 
Sélectionnez

unit test;

interface

procedure Cercle (X, Y: Integer; Rayon: Word);

implementation

procedure Cercle (X, Y: Integer; Rayon: Word);
var
  Angle: Single;
begin
end;

end.

VI-B. Fonctions

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 :

 
Sélectionnez

function identificateur [(paramètres)]: type_resultat;

'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.

VI-C. Premières instructions en Pascal

VI-C-1. Affectations

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 :

variable := valeur

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 :

 
Sélectionnez

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 :

 
Sélectionnez

const
  TauxEuro = 6.55957;

Mais le terme 'valeur' dans la structure de l'affectation peut être beaucoup de choses, parmi lesquelles :

  • Une constante
  • Une variable
  • Un paramètre (dans une fonction ou une procédure)
  • Le résultat d'une fonction
  • Une valeur déterminée à partir des éléments ci-dessus. Lorsqu'une instruction contient une affectation, cette affectation est exécutée en dernier, ce qui permet d'effectuer des calculs, par exemple, et d'affecter le résultat à une variable.

Voici un exemple illustrant cela :

 
Sélectionnez

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 :

  • La fonction est déclarée dans l'interface, pour pouvoir être utilisée de l'extérieur.
  • La fonction a un unique paramètre : 'rayon', qui est utilisé dans le calcul de l'aire.
  • La fonction fait également appel à une fonction du même genre que celle qui est écrite ci-dessus : pi. C'est bien une fonction qui est appelée mais nous en reparlerons dans le paragraphe suivant.
  • Ce qui est important, c'est que le résultat ('result') est calculé directement à partie d'un calcul : la multiplication d'un paramètre (dont on ne connait pas la valeur mais ceci ne nous gène pas puisque c'est le justement le but de la fonction que de calculer l'aire d'un disque en fonction de son rayon) et du résultat d'une fonction (car PI renvoie évidemment 3.14159...). L'opération est effectuée d'abord et le résultat de cette opération est mis dans 'result'.

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 ?

VI-C-2. Appels de procédures et de fonctions

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 [(valeurs_des_parametres)]

'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 :

 
Sélectionnez

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 :

 
Sélectionnez

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)

  1. Ecrivez une fonction VolumeCyl qui calcule directement le volume d'un cylindre, sans faire appel à la fonction 'AireDisque'.
  2. Ecrivez une fonction PerimetreCercle qui calcule le périmètre d'un cercle (périmètre = 2 x PI x Rayon).
  3. Ecrivez enfin une fonction SurfaceCyl qui calcule la surface totale d'un cylindre régulier droit. Pour cela, vous devrez additionner l'aire des deux disques de base avec l'aire de la partie circulaire, que vous pourrez calculer en utilisant la fonction PerimetreCercle écrite auparavant.

Pour fonctionner, la deuxième fonction doit pouvoir avoir accès à la première. Il y a plusieurs moyens que vous connaissez déjà :

  • Si elles sont dans deux unités différentes, alors l'unité contenant 'VolumeCylindre' doit utiliser (bloc uses) l'unité contenant 'AireDisque', que ce soit dans l'interface ou dans l'implémentation. De plus, la fonction AireDisque devra être déclarée dans l'interface de l'unité dans laquelle elle est écrite.
  • Si les deux fonctions sont dans la même unité :

    • Si la fonction 'AireDisque' est écrite au dessus de la fonction 'VolumeCylindre', alors il n'y a rien à faire de plus.
    • Si la fonction 'AireDisque' est écrite en dessous de la fonction 'VolumeCylindre', alors 'AireDisque doit être déclarée dans l'interface de l'unité. Il existe une possibilité pour éviter cela si vous ne voulez pas déclarer 'AireDisque' dans l'interface, c'est de le faire dans l'implémentation, au dessus de 'VolumeCylindre'. Mais, me direz-vous, j'ai dit au dessus que c'était interdit, et je maintiens. La déclaration, pour être acceptée dans l'implémentation, doit ajouter à la fin de la ligne de déclaration, après le point-virgule final, le texte suivant :

       
      Sélectionnez
      
      forward;
      

      ('forward' signifie littéralement 'plus loin')

      La déclaration de la fonction devient alors :

       
      Sélectionnez
      
      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

 
Sélectionnez

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

 
Sélectionnez

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

 
Sélectionnez

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

 
Sélectionnez

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.
 
Sélectionnez

unit calcul;

interface

function AireDisque(Rayon: Single): Single;

implementation

function AireDisque(Rayon: Single): Single;
begin
  Result := PI * Rayon * Rayon;
end;

end.

VI-D. Manipulations

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.

 
Sélectionnez

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) :

  • comment cette procédure est arrivée là.
  • le nom étrange de cette procédure : 'TForm1.Button1Click', qui n'est pas un identificateur valide (pour vous, le point est encore interdit, nous verrons plus tard dans quelles circonstances l'utiliser).
  • le type de l'unique paramètre de cette procédure : 'TObject' (pour les curieux de tout, c'est une classe d'objets, mais ne m'en demandez pas plus pour l'instant, c'est pour plus tard !).

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.

Il va commencer à être urgent d'avoir des réponses visibles de la part de l'ordinateur, c'est pour cela que nous utiliserons la procédure 'ShowMessage' (lisez « Show Message », « Montrer Message » en français) fournie par Delphi et contenue dans une unité appelée 'Dialogs' que vous n'aurez pas besoin d'ajouter dans l'un des blocs 'uses de l'unité 'principale' car elle doit déjà y être : vérifiez et ajoutez-là si elle n'y est pas.

Cette procédure accepte un unique paramètre, de type chaîne de caractères ('string'). La chaîne en question, lors de l'exécution de la procédure, sera affichée dans une petite boite de dialogue avec un bouton OK. Ce sera suffisant dans l'immédiat pour nous permettre de contrôler ce que fait notre application pendant son exécution.

Insérez le code présenté ci-dessous (entrez juste l'instruction puisque le reste est déjà écrit) :

 
Sélectionnez

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 :

Image non disponible

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 :

 
Sélectionnez

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 :

 
Sélectionnez

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 :

 
Sélectionnez

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 :

 
Sélectionnez

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 :

 
Sélectionnez

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.

 
Sélectionnez

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 :

 
Sélectionnez

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 :

Image non disponible

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 :

 
Sélectionnez

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 :

 
Sélectionnez

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 :

Image non disponible

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 :

 
Sélectionnez

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 :

 
Sélectionnez

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 :

 
Sélectionnez

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 :

 
Sélectionnez

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.

 
Sélectionnez

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.

VI-E. Conclusion

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).


précédentsommairesuivant

  

Copyright © 2008 Frédéric Beaulieu. Aucune reproduction, même partielle, ne peut être faite de ce site et de l'ensemble de son contenu : textes, documents, images, etc. sans l'autorisation expresse de l'auteur. Sinon vous encourez selon la loi jusqu'à trois ans de prison et jusqu'à 300 000 € de dommages et intérêts.