menu
  Home  ==>  articles  ==>  prog_objet_composants  ==>  c_list_of_double   

c_list_of_double - John COLIBRI.


1 - Liste de Réels

Pour réaliser des traitements scientifiques, il est fréquent d'avoir à utiliser des tableaux ou des liste de réels, en général de type Double.

Lorsque la taille de la série est inconnue à la compilation, il faut armer un série pouvant être dimensionnée, ou redimensionnée par l'utilisateur. Nous allons présenter ici une CLASS Delphi et d'autres techniques de programmation Objet Delphi permettant cette encapsulation de listes de valeurs numériques.




2 - Encapsulation de liste de Double

2.1 - Les techniques Standard

Si nous connaissons la taille de la série à utiliser, par exemple 1000 ou 1024 points, nous pouvons utiliser:

VAR tableauARRAY[0..999] OF Double

Ici, c'est le programmeur Delphi qui a figé la taille du tableau. Cela fonctionne parfaitement si l'utilisateur a exactement besoin de 1000 Doubles. En revanche, si l'utilisateur a besoin de 10 Doubles, il en a 990 de trop, et s'il en a besoin de 1200, il lui en manque 200.

Depuis Delphi 2, nous pouvons utiliser des tableaux dynamiques, ou c'est l'utilisateur qui peut indiquer quelle taille de tableau il souhaite utiliser:

  • nous déclarons une ARRAY OF (sans donner les bornes initiales et finales du tableau):

    VAR tableau_dynamiqueARRAY OF Double

    Ceci peut être représenté par le schéma suivant:

    of_double_declaration

  • nous demandons à l'utilisateur de fournir la taille. Il tape par exemple cette taille dans un tEdit, et nous dimensionnons le tableau dans un tButton.OnClick:

    l_taille:= IntToStr(Edit1.Text);
    SetLength(tableau_dynamiquel_taille);

    Supposons que l'utilisateur demande 3 cellules. Delphi alloue les 3 cellules:

    array_of_double

  • l'utilisateur ajoute ses valeurs dans le tableau, exactement comme pour un tableau statique:

    tableau_dynamique[0]:= 3.14;
    tableau_dynamique[1]:= -45.67;

    soit:

    of_double_assignment

  • s'il change d'avis, et modifie la taille du tableau, Delphi redimensionne le tableau. Supposons que l'utilisateur décide d'agrandir son tableau à 7 cellules. Pour cela, il exécute simplement la même instruction SetLength avec la même valeur:

    SetLength(tableau_dynamique, 7);

    et l'allocateur mémoire de Delphi va:

    • chercher en mémoire un espace assez grand pour contenir nos 7 cellules, et va le réserver en le remplissant de 0

      reallocate_array_of_double

    • il va copier les valeurs du tableau actuel au début de la nouvelle zone:

      of_double_copy_old_values

    • il va faire pointer notre variable tableau_dynamique vers la nouvelle zone et rendre l'ancienne zone (de 3 cellules) au système

      array_of_double

     

  • la fonction Length permet à chaque instant de connaître la taille. Par exemple, un développeur prudent vérifiera que l'utilisateur n'essaye pas de lire ou écrire un zone en dehors du tableau:

    IF indiceLength(tableau_dynamique)
      THEN tableau_dynamique[indice]:= valeur
      ELSE ShowMessage('débordement d''indice');

  • et finalement, lorsque nous souhaitons libérer la mémoire contenant les cellules, nous affectons simplement NIL au tableau:

    tableau_dynamique:= NIL;



Notez que:
  • un ARRAY OF correspond à un pointeur, et SetLength alloue les données contenues dans le tableau. Mais le compilateur gère cette allocation, et lorsque nous souhaitons accéder aux données, nous n'avons pas besoin d'utiliser le symbole ^
  • de plus l'allocation / libération est réalisée en utilisant SetLength et l'affectation de NIL (et non pas via NEW et DISPOSE, comme pour les pointeurs ordinaires)


Nous constatons alors que nous disposons bien de toutes les fonctionnalités pour gérer des tableaux de n'importe quelle taille. Toutefois, cela nous oblige à truffer notre code utilisateur de nombreux tests et appels à des routines de gestion des tableaux dynamiques. En fait, vu du point de vue de 'utilisateur, tout ce qu'il souhaite est
  • créer son tableau
  • ajouter quelques valeur, et ceci sans provoquer d'erreur de débordement
  • pouvoir accéder aux valeurs de son tableau pour modifier des valeurs, ou les lire, avec la syntaxe la plus légère possible.


Compte tenu de cet énoncé du problème, la solution pour un développeur Delphi est instantannée: il faut créer une CLASS qui encapsule nos instructions Delphi et représente un liste de Double.



2.2 - La première CLASSe encapsulant un ARRAY OF Double

Voici la définition de notre CLASS c_double_list:

c_array_of_doubleClass
                     m_array_of_doubleARRAY of Double;
                     m_countInteger;

                     Constructor create;
                     procedure add_double(p_doubleDouble);
                     function f_double(p_indexInteger): Double;
                     Destructor DestroyOverride;
                   end// c_array_of_double

et:

  • m_array_of_double contiendra, comme ci-dessus, les Doubles
  • le CONSTRUCTOR va initialiser la CLASS
  • add_double permet d'ajouter des valeurs au tableau
  • f_double récupère une valeur
Et voici les procédures correspondantes:

Constructor c_array_of_double.create;
  begin
    InHerited Create;
  end// create

procedure c_array_of_double.add_double(p_doubleDouble);
  begin
    if m_count>= Length(m_array_of_double)
      then
        if Length(m_array_of_double)= 0
          then SetLength(m_array_of_double, 1)
          else SetLength(m_array_of_double, 2* Length(m_array_of_double));

    m_array_of_double[m_count]:= p_double;
    Inc(m_count);
  end// add_double

function c_array_of_double.f_double(p_indexInteger): Double;
  begin
    if p_index>= Length(m_array_of_double)
      then Raise Exception.Create('p_index> m_count');

    Result:= m_array_of_double[p_index];
  end// f_double

Destructor c_array_of_double.destroy;
  begin
    m_array_of_double:= Nil;

    Inherited;
  end// destroy



Et voici un exemple de programme utilisant notre CLASSe:

var g_c_array_of_doublec_array_of_doubleNil;

procedure TForm1.create_arrayClick(SenderTObject);
  begin
    g_c_array_of_double:= c_array_of_double.Create;
  end// create_arrayClick

procedure TForm1.add_doublesClick(SenderTObject);
  begin
    if g_c_array_of_doubleNil
      then g_c_array_of_double.Create;

    g_c_array_of_double.add_double(1.1);
    g_c_array_of_double.add_double(2.2);
    g_c_array_of_double.add_double(3.3);
    g_c_array_of_double.add_double(4.4);
  end// add_doubles_Click

procedure TForm1.display_doublesClick(SenderTObject);
  var l_indexInteger;
  begin
    for l_index:= 0 to g_c_array_of_double.m_count- 1 do
      Memo1.Lines.Add(FloatToStr(g_c_array_of_double.f_double(l_index)));
  end// display_doubles_Click

procedure TForm1.free_doubleClick(SenderTObject);
  begin
    g_c_array_of_double.Free;
    g_c_array_of_double:= Nil;
    // or : FreeAndNil(g_c_array_of_double);
  end// free_double_Click



Notez que:

  • nous n'avons pas donné de dimension initiale à notre tableau. La première addition se chargera de donner la taille qu'il faut à notre tableau
  • notre encapsulation est loin d'être parfaite: l'utilisateur doit encore accéder à g_c_array_of_double.m_array_of_double, et utiliser g_c_array_of_double.m_count


Pour mieux rendre notre représentation opaque à l'utilisateur, nous allons
  • créer une PROPERTY Doubles et les deux méthodes set_double et get_double correspondantes
  • créer une PROPERTY Count
Voici alors la nouvelle CLASSe:
  • sa définition:

    const k_granularity= 4;

    type c_array_of_double_2=
            Class
              private
                m_array_of_doubleARRAY of Double;
                m_countInteger;

                function get_double(p_indexInteger): Double;
                procedure set_double(p_indexIntegerp_doubleDouble);
              public
                Constructor create;
                procedure add_double(p_doubleDouble);

                procedure set_length(p_lengthInteger);

                Destructor DestroyOverride;

                Property Doubles[p_indexInteger] : Double
                    read get_double write set_doubleDefault;
                Property CountInteger read m_count write m_count;
            end// c_array_of_double

  • son implémentation

    Constructor c_array_of_double_2.create;
      begin
        InHerited Create;
        SetLength(m_array_of_doublek_granularity);
      end// create

    procedure c_array_of_double_2.add_double(p_doubleDouble);
      begin
        if m_count>= Length(m_array_of_double)
          then SetLength(m_array_of_double, 2* Length(m_array_of_double));

        m_array_of_double[m_count]:= p_double;
        Inc(m_count);
      end// add_double

    function c_array_of_double_2.get_double(p_indexInteger): Double;
      begin
        if p_index>= Length(m_array_of_double)
          then Raise Exception.Create('p_index> m_count');

        Result:= m_array_of_double[p_index];
      end// get_double

    procedure c_array_of_double_2.set_double(p_indexIntegerp_doubleDouble);
      begin
        if p_index>= Length(m_array_of_double)
          then begin
              m_count:= p_index+ 1;
              SetLength(m_array_of_doublem_count);
            end;

        m_array_of_double[p_index]:= p_double;
      end// set_double

    procedure c_array_of_double_2.set_length(p_lengthInteger);
      begin
        m_array_of_double:= Nil;

        m_count:= p_length;
        SetLength(m_array_of_doublem_count);
      end// set_length

    Destructor c_array_of_double_2.destroy;
      begin
        m_array_of_double:= Nil;

        Inherited;
      end// destroy

  • et un exemple d'utilisation:

    var g_c_array_of_double_2c_array_of_double_2Nil;

    procedure TForm1.create_array_2Click(SenderTObject);
      begin
        g_c_array_of_double_2:= c_array_of_double_2.Create;
      end// create_array_2Click

    procedure TForm1.add_doubles_2Click(SenderTObject);
      begin
        g_c_array_of_double_2.set_length(4);

        g_c_array_of_double_2[0]:= 10.0;
        g_c_array_of_double_2[1]:= 20.0;
        g_c_array_of_double_2[2]:= 40.0;
        g_c_array_of_double_2[3]:= 40.0;

        g_c_array_of_double_2[4]:= 50.0;
        g_c_array_of_double_2[5]:= 60.0;
      end// add_doubles_2Click

    procedure TForm1.display_double_2Click(SenderTObject);
      var l_indexInteger;
      begin
        Memo1.Lines.Add('nombre 'IntToStr(g_c_array_of_double_2.Count));
        for l_index:= 0 to g_c_array_of_double_2.Count- 1 do
          Memo1.Lines.Add(FloatToStr(g_c_array_of_double_2[l_index]));
      end// display_double_2Click

    procedure TForm1.free_double_2Click(SenderTObject);
      begin
        FreeAndNil(g_c_array_of_double_2);
      end// free_double_2Click


Et voici l'image de notre application complète:

array_of_double



Notez que:

  • dans cet exemple nous avons commencé par donner une taille initiale dans le CONSTRUCTOR (4 dans notre cas). Une autre possibilité encore aurait été de doter le CONSTRUCTOR d'un paramètre p_taille_initiale
  • pour redimensionner le tableau
    • nous vérifions à chaque utilisation d'un indice, que celui-ci est bien dans la plage des valeurs actuellement permises. Si ce n'est pas le cas, nous redimensionnons automatiquement le tableau
    • pour redimensionner le tableau, plusieurs stratégies sont possibles:
      • soit augmenter le tableau d'une cellule à la fois. Il a 3 cellules, l'utilisateur en veut une de plus, eh bien nous en allouons une de plus et il a a présent un tableau de 4 cellules, puis 5 cellules

        reallocation

      • soit augmenter d'une quantité fixe. Par exemple 10 ou 16. Dans notre exemple, si nous augmentons de 2, il a 3 au départ, puis 5, 7, 9 etc:

        linear_reallocation

      • soit utiliser le système "Buddy-Buddy" qui consiste à doubler la taille à chaque réallocation. Nous commençons par exemple à 3, puis augmentons à 6, 12, 24 etc.

        buddy_buddy_reallocation


      Dans les deux derniers cas, il faut gérer deux variables
      • la taille allouée (fournie par Length())
      • le nombre de cellules actuellement utilisées, contenu dans m_count
  • nous avons aussi décidé de laisser l'utilisateur taper n'importe que indice, en allouant au besoin ce qu'il faut entre la taille actuelle du tableau et la cellule demandée par l'utilisateur. Par exemple:
    • la taille actuelle est de 23
    • l'utilisateur veut modifier la cellule 34
    • nous allons les cellules entre 24 et 32 en les remplissant de 0
    Cette stratégie est astucieuse, mais, en fonction du problème à traiter peut être risquée. Par exemple, si l'utilisateur se trompe et tape 110 au lieu de 10, notre CLASS lui fournira, sans rien lui dire, un tableau plus grand qu'il ne le souhaitait. Plusieurs solutions si vous souhaitez éviter ces redimensionnements automatiques:
    • tester l'indice demandé, et provoquer une exception si l'utilisateur dépasse la limite qu'il a initialement demandé
    • forcer le dimensionnement lors de l'appel du CONSTRUCTOR, et ne pas redimensionner par la suite


2.3 - Utilisation d'une tList de ^Double

Au lieu d'utiliser un ARRAY OF Double, nous pouvons utiliser une tList, qui a l'avantage essentiel que c'est Delphi qui se charge de l'allocation / réallocation des cellules. Nous appelons simplement tList.Add(xxx) et cette instruction ajoute une nouvelle cellule. En fait, la tList utilise un mécanisme similaire au nôtre:
  • lors de la création, une table de 16 cellules est allouée (c'est la Granularity de la tList), et Count est égal à 0
  • lorsque nous appelons Add :
    • si Count est inférieur à la taille allouée, la valeur est placée après la dernière valeur utilisée
    • dans le cas contraire, la taille allouée est doublée (système Buddy-Buddy) et la valeur est placée après la dernière valeur utilisée
Le seul problème est que la table de données utilisée, appelée tList.Items, n'est pas prévue pour contenir des Doubles, mais de Pointer: un pointeur générique, de taille 4 octets.

Il nous faut donc placer chaque Double dans une autre cellule, associée à la cellule courante. Nous pouvons utiliser deux techniques:

  • utiliser des pointeurs de Double, et stocker nos doubles dans chaque cellule pointée
  • utiliser une CLASS c_double qui contiendra nos doubles


Commençons par la première technique

Pour cela:

  • supposons que nous ayons déjà déclaré et initialisé notre tList;

    var ma_tlisttListNil;
      
      ma_tlist:= tList.Create;

    soit:

    tlist_of_pt_double_create_tlist

  • nous définissons le type pointeur de Double, et déclarons une variable (en général locale) de ce type:

    type t_pt_double= ^Double:

    var mon_pt_doublet_pt_doubleNil:

    soit:

    tlist_of_pt_double_declare_pt_double

  • nous créons la cellules pointée avec l'instruction Pascal classique New, et nous affectons une valeur, par exemple 3.14 à la cellule pointée par mon_pt_double:

    New(mon_pt_double);
    mon_pt_double^:= 3.14;

    donc:

    tlist_of_pt_double_allocate_pointer

  • nous stockons le pointeur dans la tList (== nous copions l'adresse contenue dans mon_pt_double dans ma_tlist[nnn])

    ma_tlist.Add(mon_pt_double);

    donc:

    tlist_of_pt_double_new_cell

    et en général, si mon_pt_double est local, il est supprimé après le END de la procédure. Le résultat de cette gymnastique est bien l'ajout d'une nouvelle cellule contenant un Double:

    tlist_of_pt_double_new_cell_end

  • finalement pour libérer une cellule, il faut:
    • libérer la cellule pointée

      Dispose(ma_tlist[2]);

      ce qui peut être représenté par:

      tlist_of_pt_double_dispose_pointer

    • puis réduire le nombre de cellule de la tList:

      FOR indice:= ma_tlist.Count- 1 DOWNTO 0 DO
      BEGIN
        Dispose(ma_tlist[indice]);
        ma_tlist.Delete(indice);
      END;

      soit:

      tlist_of_pt_double_tlist_delete



Notez:
  • pour libérer un pointeur, nous utilisons Dispose(mon_pointeur) alors que pour un objet, nous utilisons mon_objet.Free
  • pour supprimer une cellule d'une tList, nous utilisons ma_tlist.Delete(nnn)
  • la suppression d'une cellule de la tList ne provoque pas automatiquement la libération de la cellule pointée
  • si nous supprimons un cellule au milieu de la tList, les cellules qui suivent sont tassées vers l'avant:

    tlist_of_pt_double_tlist_delete_middle

    et ceci explique que si nous devons supprimer toute une tList, il vaut mieux supprimer à partir de la fin:

    FOR indice:= ma_tlist.Count- 1 DOWNTO 0 DO
    BEGIN
      Dispose(ma_tlist[indice]);
      ma_tlist.Delete(indice);
    END;

  • au niveau notation, nous utilisons la notation Alsacienne t_pt_double (type pointeur de double). Or dans WINDOWS.PAS il existe déjà une définition de pDouble comme un pointeur de Doubles. Vous pouvez donc utiliser cette définition au lieu de redéfinir ce type. Pour notre part, la notation Alsacienne (c_xxx, p_xxx, g_xxx, l_xxx, k_xxx etc) nous a tellement fait gagner de temps de développement que nous ne sommes pas prêts de changer de notation.


A présent, voici donc une CLASS qui a exactement la même fonctionnalité que la CLASS précédente, mais qui utilise des pointeurs de Double:
  • voici la définition de la CLASSe:

    type t_pt_double= ^Double;

         c_tlist_of_pt_double=
             Class(tList)
               Private
                 function get_double(p_indexInteger): Double;
                 procedure set_double(p_indexIntegerp_doubleDouble);

               Public
                 Constructor create;
                 procedure add_double(p_doubleDouble);
                 Destructor DestroyOverride;

                 Property Doubles[p_indexInteger] : Double 
                     Read get_double Write set_doubleDefault;
               end// c_tlist_of_pt_double

  • et son implémentation:

    Constructor c_tlist_of_pt_double.create;
      begin
        Inherited Create;
      end// create

    procedure c_tlist_of_pt_double.add_double(p_doubleDouble);
      var l_pt_doublet_pt_double;
      begin
        New(l_pt_double);
        l_pt_double^:= p_double;
        Inherited Add(l_pt_double);
      end// add_double

    function c_tlist_of_pt_double.get_double(p_indexInteger): Double;
      var l_pt_doublet_pt_double;
      begin
        if p_index>= Count
          then Raise Exception.Create('p_index> m_count');

        Result:= t_pt_double(Items[p_index])^;
      end// get_double

    procedure c_tlist_of_pt_double.set_double(p_indexIntegerp_doubleDouble);
      var l_indexInteger;
      begin
        // -- if index beyond Count, create with 0 filled cells
        if p_index>= Count
          then
            For l_index:= Count to p_index do
              add_double(0);

        t_pt_double(Items[p_index])^:= p_double;
      end// set_double

    Destructor c_tlist_of_pt_double.destroy;
      var l_indexInteger;
      begin
        for l_index:= 0 To Count- 1 do
          Dispose(Items[l_index]);

        Inherited;
      end// destroy

  • en prime, un exemple d'utilisation:

    var g_c_tlist_of_pt_doublec_tlist_of_pt_doubleNil;

    procedure TForm1.create_listClick(SenderTObject);
      begin
        g_c_tlist_of_pt_double:= c_tlist_of_pt_double.Create;
      end// create_arrayClick

    procedure TForm1.add_doublesClick(SenderTObject);
      begin
        if g_c_tlist_of_pt_doubleNil
          then g_c_tlist_of_pt_double.Create;

        g_c_tlist_of_pt_double.add_double(1.1);
        g_c_tlist_of_pt_double.add_double(2.2);
        g_c_tlist_of_pt_double.add_double(3.3);
        g_c_tlist_of_pt_double.add_double(4.4);
      end// add_doubles_Click

    procedure TForm1.display_doublesClick(SenderTObject);
      var l_indexInteger;
      begin
        for l_index:= 0 to g_c_tlist_of_pt_double.Count- 1 do
          Memo1.Lines.Add(FloatToStr(g_c_tlist_of_pt_double.Doubles[l_index]));
      end// display_doubles_Click

    procedure TForm1.free_listClick(SenderTObject);
      begin
        FreeAndNil(g_c_tlist_of_pt_double);
      end// free_double_Click

    procedure TForm1.set_4_directClick(SenderTObject);
      begin
        if g_c_tlist_of_pt_doubleNil
          then g_c_tlist_of_pt_double.Create;

        g_c_tlist_of_pt_double.Doubles[3]:= 4.4;
      end// set_4_directClick

    qui se présente ainsi:

    tlist_of_pt_double_



Notez que
  • nous avons choisi de conserver add_double. Il aurait été possible d'utiliser Add, qui est plus traditionnel
  • si nous utilisons Add, il est impératif d'appeler INHERITED Add pour ajouter le pointeur dans la liste (INHERITED n'aurait pas été nécessaire avec add_double, car la tList n'a aucune méthode ayant ce nom)
  • nous avons choisi d'hériter de tList. Il aurait été possible de placer dans une c_tlist_of_pt_double un attribut m_c_tlist de type tList (comme nous avions inclus un attribut m_array_of_double dans la CLASS précédente). L'avantage de l'héritage est de bénéficier directement des propriétés et méthodes de la tList (Count, Find etc). Si la tList est incluse (composition plutôt qu'héritage), il faut créer des PROPERTYes qui relaient les mêmes propriétés de la tList. Dans notre cas, l'héritage semble avantageux. En revanche si nous souhaitons utiliser plusieurs tList (voir plus loin), nous n'aurons pas d'autre choix que de placer les tList dans la CLASSe


Cette technique est certes utilisable, mais reste un peu étrange pour les nouveaux venus dans Delphi qui préféreraient éviter les pointeurs, qui ont, à cause de leur utilisation un peu délicate, une mauvaise réputation.

Nous pouvons donc effectuer le même traitement en créant un objet dont la seule vocation sera de contenir notre valeur Double



2.4 - Une tList de c_double

Pour éviter les New et Dispose (qui fonctionnent parfaitement), nous pouvons utiliser une CLASS spéciale pour stocker chaque Double.

Donc:

  • voici la définition des deux CLASSes:

    type c_double=
             Class
               m_doubleDouble;
               Constructor create(p_doubleDouble);
             end// c_double

         c_tlist_of_c_double=
             Class(tList)
               Private
                 function get_double(p_indexInteger): Double;
                 procedure set_double(p_indexIntegerp_doubleDouble);

               Public
                 Constructor create;
                 procedure add_double(p_doubleDouble);
                 Destructor DestroyOverride;

                 Property Doubles[p_indexInteger] : Double
                     Read get_double Write set_doubleDefault;
               end// c_tlist_of_c_double

  • et leur implémentation:

    // -- c_double

    Constructor c_double.create(p_doubleDouble);
      begin
        Inherited Create;
        m_double:= p_double;
      end// create

    // -- c_tlist_of_c_double.

    Constructor c_tlist_of_c_double.create;
      begin
        Inherited Create;
      end// create

    procedure c_tlist_of_c_double.add_double(p_doubleDouble);
      begin
        Inherited Add(c_double.create(p_double))
      end// add_double

    function c_tlist_of_c_double.get_double(p_indexInteger): Double;
      begin
        if p_index>= Count
          then Raise Exception.Create('p_index> m_count');

        Result:= c_double(Items[p_index]).m_double;
      end// get_double

    procedure c_tlist_of_c_double.set_double(p_indexIntegerp_doubleDouble);
      var l_indexInteger;
      begin
        // -- if index beyond Count, create with 0 filled cells
        if p_index>= Count
          then
            For l_index:= Count to p_index do
              add_double(0);

        c_double(Items[p_index]).m_double:= p_double;
      end// set_double

    Destructor c_tlist_of_c_double.destroy;
      var l_indexInteger;
      begin
        for l_index:= 0 To Count- 1 do
          c_double(Items[l_index]).Free;

        Inherited;
      end// destroy

  • et le projet Delphi de test:

    var g_c_tlist_of_c_doublec_tlist_of_c_doubleNil;

    procedure TForm1.create_listClick(SenderTObject);
      begin
        g_c_tlist_of_c_double:= c_tlist_of_c_double.Create;
      end// create_arrayClick

    procedure TForm1.add_doublesClick(SenderTObject);
      begin
        if g_c_tlist_of_c_doubleNil
          then g_c_tlist_of_c_double.Create;

        g_c_tlist_of_c_double.add_double(1.1);
        g_c_tlist_of_c_double.add_double(2.2);
        g_c_tlist_of_c_double.add_double(3.3);
        g_c_tlist_of_c_double.add_double(4.4);
      end// add_doubles_Click

    procedure TForm1.display_doublesClick(SenderTObject);
      var l_indexInteger;
      begin
        for l_index:= 0 to g_c_tlist_of_c_double.Count- 1 do
          Memo1.Lines.Add(FloatToStr(g_c_tlist_of_c_double.Doubles[l_index]));
      end// display_doubles_Click

    procedure TForm1.free_listClick(SenderTObject);
      begin
        FreeAndNil(g_c_tlist_of_c_double);
      end// free_double_Click

    dont voici l'image:

    tlist_of_c_double__



2.5 - Comparaison des trois techniques

Nous pouvons facilement comparer les trois techniques par le schéma suivant:

list_of_double



Il en ressort que

  • l'utilisation d'un ARRAY OF double est certainement la plus économe au niveau mémoire, et évite en plus tout surtypage
  • l'emploi de pointeurs de Double est assez simple, mais nécessite une allocation séparée pour chaque cellule, ce qui peut se révéler coûteux en temps. De plus il faut surtyper les éléments de la tList, au départ prévus pour contenir des Pointer et pas des Doubles^
  • la création de CLASSe c_double est relativement classique. C'est la technique Delphi la plus naturelle, et elle est utilisée pour encapsuler dans une tList n'importe quel autre type de cellule (une c_string, une c_ma_classe qui contiendrait de nombreux champs). Mais il faut savoir que chaque objet contient un certain nombre d'octets pour gérer les CLASSe: la taille de l'objet, le pointeur vers la VMT (table des méthodes virtuelles) etc. D'où un consommation nettement supérieure par rapport à une cellule de Double. En revanche, si nous encapsulons un Double dans une CLASSe, nous pouvons y associer, si nous le souhaitons des méthodes qui seraient utiles pour notre application: vérifier que la valeur est comprise entre certaines bornes, afficher avec certains formats prédéfinis, sauvegarder dans un flux, ou relire un flux etc



3 - Encapsulation de liste de Boolean

Lorsque la taille de chaque élément est inférieur à la taille de la cellule d'une tList (un Pointer, soit 4 octets), nous pouvons stocker la valeur dans chaque cellule en utilisant le surtypage.

Schématiquement nous avons, pour nos trois structures canoniques:

list_of_booleans



Et, très rapidement, le code avec stockage dans les Pointer de la tList:

  • voici la définition de la CLASSe:

    c_tlist_of_boolean=
        Class(tList)
          Private
            function get_boolean(p_indexInteger): Boolean;
            procedure set_boolean(p_indexIntegerp_booleanBoolean);

          Public
            Constructor create;
            procedure add_boolean(p_booleanBoolean);
            Destructor DestroyOverride;

            Property Booleans[p_indexInteger] : Boolean
                Read get_boolean Write set_booleanDefault;
          end// c_tlist_of_boolean

  • et leur implémentation:

    Constructor c_tlist_of_boolean.create;
      begin
        Inherited;
      end// create

    procedure c_tlist_of_boolean.add_boolean(p_booleanBoolean);
      begin
        Inherited Add(Nil);
        if p_boolean
          then Items[Count- 1]:= Pointer(1);
      end// add_boolean

    function c_tlist_of_boolean.get_boolean(p_indexInteger): Boolean;
      begin
        if p_index>= Count
          then Raise Exception.Create('p_index> m_count');

        Result:= Integer(Items[p_index])> 0;
      end// get_boolean

    procedure c_tlist_of_boolean.set_boolean(p_indexIntegerp_booleanBoolean);
      var l_indexInteger;
      begin
        // -- if index beyond Count, create with 0 filled cells
        if p_index>= Count
          then
            For l_index:= Count to p_index do
              add_boolean(False);

        if p_boolean
          then Items[p_index]:= Pointer(1)
          else Items[p_index]:= Nil;
      end// set_boolean

    Destructor c_tlist_of_boolean.destroy;
      begin
        Inherited;
      end// destroy

  • et le résultat est:

    tlist_of_boolean_test



Notez que
  • nous avons ici exploité que False a la valeur 0 (et donc NIL pour un Pointer) et True la valeur 1
  • l'exemple présenté peut être aisément adapté à des liste de Byte, ShortInt, SmallInt, Integer, Word, dWord, Char et WideChar, énuméré. Bref, tout ce qui a moins de 4 octet



4 - Liste de Plusieurs Doubles

4.1 - Séries de plusieurs réels

Supposons à présent que nous souhaitions gérer des liste de plusieurs doubles:
  • les valeurs X, Y, Z d'un point en 3D
  • les mesures électriques d'un composant (tension, intensité, fréquence etc)
  • les données économiques d'une série (date, prix, volume pour une série boursière)
Dans ce cas, nous pouvons aussi mettre en place les mêmes alternatives:
  • définir un RECORD ou une CLASSe pour chaque groupe de valeurs, et utiliser un ARRAY OF t_record ou c_class, et gérer nous-mêmes la réallocation. Cette solution fonctionne, mais nous perdons la seule motivation que nous avions pour un Double, qui était d'éviter le surtypage
  • utiliser une tList et placer les données dans un t_record^. Ici c'est à nouveau possible, mais en général, si déjà nous avons plusieurs valeurs à gérer, il est plus que probable que nous aurons des traitements à réaliser sur chaque groupe de points (
  • l'utilisation d'un CLASS c_class s révèle en général la plus adaptée


4.2 - ARRAY OF record_of_double ou record_of_double OF ARRAY OF Double

En utilisant des tableaux dynamiques, ARRAY OF, nous avons deux solutions:
  • soit placer nos chaque valeur multidimensionnelle dans un RECORD et créer un ARRAY OF de ces RECORDs
  • soit créer un RECORD d'une c_array_of_double présenté ci-dessus
Schématiquement nous avons:

of_xyz



Pour la première solution:

  • voici la définition des deux CLASSes:

    type t_double_recordRecord
                            m_xm_ym_zDouble;
                          end// t_double_record

         c_array_of_record_of_doubles=
             Class
               private
                 m_array_of_double_recordARRAY of t_double_record;
                 m_countInteger;

                 function get_doubles(p_indexInteger): t_double_record;
                 procedure set_doubles(p_indexInteger;
                     p_double_recordt_double_record);

                 function get_x(p_indexInteger): Double;
                 procedure set_x(p_indexIntegerp_xDouble);

                 function get_y(p_indexInteger): Double;
                 procedure set_y(p_indexIntegerp_yDouble);

                 function get_z(p_indexInteger): Double;
                 procedure set_z(p_indexIntegerp_zDouble);
               public
                 Constructor create;
                 procedure add_doubles(p_double_recordt_double_record);

                 procedure set_length(p_lengthInteger);

                 Destructor DestroyOverride;

                 Property Doubles[p_indexInteger] : t_double_record
                     Read get_doubles Write set_doublesDefault;
                 Property X[p_indexInteger] : Double
                     Read get_x Write set_x;
                 Property Y[p_indexInteger] : Double
                     Read get_y Write set_y;
                 Property Z[p_indexInteger] : Double
                     Read get_z Write set_z;

                 Property CountInteger Read m_count Write m_count;
             end// c_array_of_double

       function f_double_record(p_xp_yp_zDouble): t_double_record;

  • et leur implémentation:

    function f_double_record(p_xp_yp_zDouble): t_double_record;
      begin
        Result.m_x:= p_x;
        Result.m_y:= p_y;
        Result.m_z:= p_z;
      end// f_double_record

    // -- c_array_of_record_of_doubles

    Constructor c_array_of_record_of_doubles.create;
      begin
        InHerited Create;
        SetLength(m_array_of_double_recordk_granularity);
      end// create

    procedure c_array_of_record_of_doubles.add_doubles(p_double_recordt_double_record);
      begin
        if m_count>= Length(m_array_of_double_record)
          then SetLength(m_array_of_double_record, 2* Length(m_array_of_double_record));

        m_array_of_double_record[m_count]:= p_double_record;
        Inc(m_count);
      end// add_double

    function c_array_of_record_of_doubles.get_doubles(p_indexInteger): t_double_record;
      begin
        if p_index>= Length(m_array_of_double_record)
          then Raise Exception.Create('p_index> m_count');

        Result:= m_array_of_double_record[p_index];
      end// get_doubles

    procedure c_array_of_record_of_doubles.set_doubles(p_indexIntegerp_double_recordt_double_record);
      begin
        if p_index>= Length(m_array_of_double_record)
          then begin
              m_count:= p_index+ 1;
              SetLength(m_array_of_double_recordm_count);
            end;

        m_array_of_double_record[p_index]:= p_double_record;
      end// set_doubles

    function c_array_of_record_of_doubles.get_x(p_indexInteger): Double;
      begin
        Result:= Doubles[p_index].m_x;
      end// get_x

    procedure c_array_of_record_of_doubles.set_x(p_indexIntegerp_xDouble);
      begin
        if p_index>= Length(m_array_of_double_record)
          then begin
              m_count:= p_index+ 1;
              SetLength(m_array_of_double_recordm_count);
            end;

        m_array_of_double_record[p_index].m_x:= p_x;
      end// set_x

    // -- ...ooo... idem for Y and Z

    procedure c_array_of_record_of_doubles.set_length(p_lengthInteger);
      begin
        m_array_of_double_record:= Nil;

        m_count:= p_length;
        SetLength(m_array_of_double_recordm_count);
      end// set_length

    Destructor c_array_of_record_of_doubles.destroy;
      begin
        m_array_of_double_record:= Nil;

        Inherited;
      end// destroy

  • et le projet Delphi de test:

    var g_c_array_of_record_of_doublesc_array_of_record_of_doublesNil;

    procedure TForm1.create_arrayClick(SenderTObject);
      begin
        g_c_array_of_record_of_doubles:= c_array_of_record_of_doubles.Create;
      end// create_arrayClick

    procedure TForm1.add_doublesClick(SenderTObject);
      begin
        if g_c_array_of_record_of_doublesNil
          then g_c_array_of_record_of_doubles.Create;

        g_c_array_of_record_of_doubles
            .add_doubles(f_double_record(1.1, 2.2, 3.3));
        g_c_array_of_record_of_doubles
            .add_doubles(f_double_record(10.1, 20.2, 30.3));
        g_c_array_of_record_of_doubles
            .add_doubles(f_double_record(100.1, 200.2, 300.3));

        // -- not allowed
        // g_c_array_of_record_of_doubles.Doubles[3].m_x:= 3.14;
        g_c_array_of_record_of_doubles.Doubles[3]:= f_double_record(3.14, 0, 0);
        g_c_array_of_record_of_doubles.X[4]:= 6.28;
      end// add_doubles_Click

    procedure TForm1.display_doublesClick(SenderTObject);
      var l_indexInteger;
      begin
        for l_index:= 0 to g_c_array_of_record_of_doubles.Count- 1 do
          with g_c_array_of_record_of_doubles[l_indexdo
            display(Format('x = %5.2f,   y =%5.1f,  z =%5.1f ', [m_xm_ym_z]));
      end// display_doubles_Click

    procedure TForm1.free_doubleClick(SenderTObject);
      begin
        FreeAndNil(g_c_array_of_record_of_doubles);
      end// free_double_Click

    dont voici l'image:

    array_of_double_record_test



Notez que
  • nous avons ajouté une FUNCTION qui convertit trois Doubles isolés en un RECORD
  • à notre grande surprise

    g_c_array_of_record_of_doubles.Doubles[3].m_x:= 3.14;

    n'est pas autorisé



Et pour la seconde solution
  • voici la définition du RECORD:

    t_double_recordRecord
                       m_c_x_arraym_c_y_arraym_c_z_arrayc_array_of_double_2;
                     end// t_double_record

  • et le projet Delphi de test:

    var g_c_record_of_array_of_doublest_double_record;

    procedure TForm1.create_arraysClick(SenderTObject);
      begin
        with g_c_record_of_array_of_doubles do
        begin
          m_c_x_array:= c_array_of_double_2.Create;
          m_c_y_array:= c_array_of_double_2.Create;
          m_c_z_array:= c_array_of_double_2.Create;
        end// with g_c_record_of_array_of_doubles
      end// create_array_2Click

    procedure TForm1.add_doubles_2Click(SenderTObject);
      begin
        with g_c_record_of_array_of_doubles do
        begin
          m_c_x_array[0]:= 1.1;
          m_c_y_array[0]:= 2.2;
          m_c_z_array[0]:= 3.3;

          m_c_x_array[1]:= 10.1;
          m_c_y_array[1]:= 20.2;
          m_c_z_array[1]:= 30.3;
        end// with g_c_record_of_array_of_doubles
      end// add_double_property_Click

    procedure TForm1.display_doubles_2Click(SenderTObject);
      var l_indexInteger;
      begin
        with g_c_record_of_array_of_doubles do
          for l_index:= 0 to m_c_x_array.Count- 1 do
            display(Format('x= %5.1f,  y =%5.1f,  z =%5.1f ',
                [  m_c_x_array[l_index], m_c_y_array[l_index],
                   m_c_z_array[l_index] ]));
      end// display_double_property_Click

    procedure TForm1.free_doubles_2Click(SenderTObject);
      begin
        with g_c_record_of_array_of_doubles do
        begin
          FreeAndNil(m_c_x_array);
          FreeAndNil(m_c_y_array);
          FreeAndNil(m_c_z_array);
        end// with g_c_record_of_array_of_doubles
      end// free_double_2Click

    dont voici l'image:

    record_of_array_of_double_test



Notez que
  • dans la seconde solution, la définition est triviale, mais le code d'utilisation plus verbeux. Nous devrions en réalité utiliser un CLASS qui encapsule les trois ARRAY OF Double. Donc une CLASS OF ARRAY OF Double
  • la seconde solution permettrait aussi d'accomoder des séries de taille différentes. Supposons que nous souhaitions modéliser des nombres et de leur valeur absolue distinctes. Voici le schéma:

    of_unequal_series



4.3 - tList of pt_record_of_double

Voici le traitement du même problème utilisant des pointeurs:

of_pt_xyz_record



Pour la première solution:

  • voici les définitions:

    type t_measureRecord
                      m_intensitym_voltagem_frequencyDouble;
                    end// t_measure

         t_pt_measure= ^t_measure;

         c_tlist_of_pt_doubles=
             Class(tList)
               Private
                 function get_measure(p_indexInteger): t_measure;
                 procedure set_measure(p_indexIntegerp_measuret_measure);

                 function get_intensity(p_indexInteger): Double;
                 procedure set_intensity(p_indexIntegerp_intensityDouble);
               Public
                 Constructor create;
                 procedure add_measure(p_measuret_measure);
                 Destructor DestroyOverride;

                 Property Measure[p_indexInteger] : t_measure
                     Read get_measure Write set_measureDefault;

                 Property Intensity[p_indexInteger] : Double
                     Read get_intensity Write set_intensity;
               end// c_tlist_of_pt_doubles

    function f_measure(p_intensityp_voltagep_frequencyDouble): t_measure;

  • et leur implémentation:

    function f_measure(p_intensityp_voltagep_frequencyDouble): t_measure;
      begin
        Result.m_intensity:= p_intensity;
        Result.m_voltage:= p_voltage;
        Result.m_frequency:= p_frequency;
      end// f_measure

    // -- c_tlist_of_pt_doubles

    Constructor c_tlist_of_pt_doubles.create;
      begin
        Inherited Create;
      end// create

    procedure c_tlist_of_pt_doubles.add_measure(p_measuret_measure);
      var l_pt_measuret_pt_measure;
      begin
        New(l_pt_measure);
        l_pt_measure^:= p_measure;
        Inherited Add(l_pt_measure);
      end// add_measure

    function c_tlist_of_pt_doubles.get_measure(p_indexInteger): t_measure;
      var l_pt_measuret_pt_measure;
      begin
        if p_index>= Count
          then Raise Exception.Create('p_index> m_count');

        Result:= t_pt_measure(Items[p_index])^;
      end// get_measure

    procedure c_tlist_of_pt_doubles.set_measure(p_indexInteger;
        p_measuret_measure);
      var l_indexInteger;
      begin
        // -- if index beyond Count, create with 0 filled cells
        if p_index>= Count
          then
            For l_index:= Count to p_index do
              add_measure(f_measure(0, 0, 0));

        t_pt_measure(Items[p_index])^:= p_measure;
      end// set_measure

    function c_tlist_of_pt_doubles.get_intensity(p_indexInteger): Double;
      begin
        if p_index>= Count
          then Raise Exception.Create('p_index> m_count');

        Result:= t_pt_measure(Items[p_index])^.m_intensity;
      end// get_intensity

    procedure c_tlist_of_pt_doubles.set_intensity(p_indexIntegerp_intensityDouble);
      var l_indexInteger;
      begin
        // -- if index beyond Count, create with 0 filled cells
        if p_index>= Count
          then
            For l_index:= Count to p_index do
              add_measure(f_measure(0, 0, 0));

        t_pt_measure(Items[p_index])^.m_intensity:= p_intensity;
      end// set_intensity

    Destructor c_tlist_of_pt_doubles.destroy;
      var l_indexInteger;
      begin
        for l_index:= 0 To Count- 1 do
          Dispose(Items[l_index]);

        Inherited;
      end// destroy

  • ainsi que le projet Delphi de test:

    var g_c_tlist_of_pt_doublesc_tlist_of_pt_doublesNil;

    procedure TForm1.create_listClick(SenderTObject);
      begin
        g_c_tlist_of_pt_doubles:= c_tlist_of_pt_doubles.Create;
      end// create_arrayClick

    procedure TForm1.add_doublesClick(SenderTObject);
      begin
        if g_c_tlist_of_pt_doublesNil
          then g_c_tlist_of_pt_doubles.Create;

        g_c_tlist_of_pt_doubles.add_measure(f_measure(1.1, 2.2, 3.3));
        g_c_tlist_of_pt_doubles.add_measure(f_measure(10.1, 20.2, 30.3));

        g_c_tlist_of_pt_doubles.Measure[2]:= f_measure(3.14, 0, 0);

        // -- ok if index< Count, but does not modify the value in the tList
        with g_c_tlist_of_pt_doubles.Measure[2] do
        begin
          m_voltage:= 60.28;
          display(Format('i =%5.1f,  v =%5.1f,  f =%5.1f',
              [m_intensitym_voltagem_frequency]));
          display_line;
        end;

        g_c_tlist_of_pt_doubles.Intensity[3]:= 9.42;
      end// add_doubles_Click

    procedure TForm1.display_doublesClick(SenderTObject);
      var l_indexInteger;
          l_measuret_measure;
      begin
        for l_index:= 0 to g_c_tlist_of_pt_doubles.Count- 1 do
        begin
    (*
          l_measure:= g_c_tlist_of_pt_doubles[l_index];
          with l_measure do
            display(Format('i =%5.1f,  v =%5.1f,  f =%5.1f',
                [m_intensity, m_voltage, m_frequency]));
    *)

          with g_c_tlist_of_pt_doubles[l_indexdo
            display(Format('i =%5.1f,  v =%5.1f,  f =%5.1f',
                [m_intensitym_voltagem_frequency]));
        end// for l_index
      end// display_doubles_Click

    procedure TForm1.free_listClick(SenderTObject);
      begin
        FreeAndNil(g_c_tlist_of_pt_doubles);
      end// free_double_Click

    dont voici l'image:

    tlist_pt_doubles



Notez que
  • le RECORD n'a pas besoin d'être pointé. Si vous devez passer le RECORD à une PROCEDURE vous pouvez utiliser un paramètres VAR ou CONST, si vous souhaitez éviter la copie des 3 pointeurs sur la pile (3 dans notre cas, mais si vous utilisez plus de données pour chaque tuple, cela peut devenir plus volumineux).
  • comme précédemment nous n'avons pu accéder aux champs par la propriété indexée Measure[], et WITH fonctionne en lecture, mais pas en écriture (il semble que nous modifions un RECORD local sur la pile, pas le RECORD de la tList)


La seconde solution consiste simplement à utiliser le RECORD de liste pointées:

type t_measureRecord
                  m_intensity_listm_voltage_list,
                      m_frequencyt_tlist_of_pt_double;
                end// t_measure

ce qui est très similaire à la technique de RECORD of t_array_of_double



4.4 - tList OF c_doubles

Finalement nous pouvons stocker les valeurs d'une même mesure dans une CLASSe, avec les deux structures suivantes:

tlist_of_c_xyz



Voici un exemple de CLASS OF CLASS, où nous avons placé dans chaque cellule des données contenant des doubles et n'importe quelle autre type de données (ici 2 Doubles et un Integer):

  • voici les définitions pour notre gestion de séries boursières:

    type c_stock_value=
            Class
              m_opening_pricem_closing_priceDouble;
              m_volumeInteger;
              Constructor create(p_opening_pricep_closing_priceDouble;
                  p_volumeInteger);
             end// c_doubles

         c_tlist_of_c_doubles=
             Class(tList)
               Private
                 function get_stock_value(p_indexInteger): c_stock_value;
                 procedure set_stock_value(p_indexInteger;
                     p_c_stock_valuec_stock_value);

                 function get_opening_price(p_indexInteger): Double;
                 procedure set_opening_price(p_indexInteger;
                     p_opening_priceDouble);
               Public
                 Constructor create;
                 procedure add_stock_value(p_c_stock_valuec_stock_value);
                 Destructor DestroyOverride;

                 Property StockValue[p_indexInteger] : c_stock_value
                     Read get_stock_value Write set_stock_valueDefault;

                 Property OpeningPrice[p_indexInteger] : Double
                     Read get_opening_price Write set_opening_price;
               end// c_tlist_of_c_doubles

  • qui sont implémentées ainsi:

    // -- c_stock_value

    Constructor c_stock_value.create(p_opening_pricep_closing_priceDouble;
        p_volumeInteger);
      begin
        Inherited Create;

        m_opening_price:= p_opening_price;
        m_closing_price:= p_closing_price;
        m_volume:= p_volume;
      end// create

    // -- c_tlist_of_c_doubles.

    Constructor c_tlist_of_c_doubles.create;
      begin
        Inherited Create;
      end// create

    procedure c_tlist_of_c_doubles.add_stock_value(p_c_stock_valuec_stock_value);
      begin
        Inherited Add(p_c_stock_value);
      end// add_stock_value

    function c_tlist_of_c_doubles.get_stock_value(p_indexInteger): c_stock_value;
      begin
        if p_index>= Count
          then Raise Exception.Create('p_index> m_count');

        Result:= c_stock_value(Items[p_index]);
      end// get_stock_value

    procedure c_tlist_of_c_doubles.set_stock_value(p_indexIntegerp_c_stock_valuec_stock_value);
      var l_indexInteger;
      begin
        // -- if index beyond Count, create with 0 filled cells
        if p_index>= Count
          then
            For l_index:= Count to p_index do
              add_stock_value(c_stock_value.create(0.0, 0.0, 0));

        // -- copy the values
        with c_stock_value(Items[p_index]) do
        begin
          m_opening_price:= p_c_stock_value.m_opening_price;
          m_closing_price:= p_c_stock_value.m_closing_price;
          m_volume:= p_c_stock_value.m_volume;
        end// with c_stock_value()
      end// set_stock_value

    function c_tlist_of_c_doubles.get_opening_price(p_indexInteger): Double;
      begin
        if p_index>= Count
          then Raise Exception.Create('p_index> m_count');

        Result:= c_stock_value(Items[p_index]).m_opening_price;
      end// get_opening_price

    procedure c_tlist_of_c_doubles.set_opening_price(p_indexIntegerp_opening_priceDouble);
      var l_indexInteger;
      begin
        // -- if index beyond Count, create with 0 filled cells
        if p_index>= Count
          then
            For l_index:= Count to p_index do
              add_stock_value(c_stock_value.create(0.0, 0.0, 0));

        // -- copy the values
        with c_stock_value(Items[p_index]) do
          m_opening_price:= p_opening_price;
      end// set_opening_price

    Destructor c_tlist_of_c_doubles.destroy;
      var l_indexInteger;
      begin
        for l_index:= 0 To Count- 1 do
          c_stock_value(Items[l_index]).Free;

        Inherited;
      end// destroy

  • et voici un projet Delphi de test:

    var g_c_tlist_of_c_doublesc_tlist_of_c_doublesNil;

    procedure TForm1.create_listClick(SenderTObject);
      begin
        g_c_tlist_of_c_doubles:= c_tlist_of_c_doubles.Create;
      end// create_arrayClick

    procedure TForm1.add_doublesClick(SenderTObject);
      begin
        if g_c_tlist_of_c_doublesNil
          then g_c_tlist_of_c_doubles.Create;

        g_c_tlist_of_c_doubles.add_stock_value(
            c_stock_value.create(1.1, 1.3, 200));
        g_c_tlist_of_c_doubles.add_stock_value(
            c_stock_value.create(5.6, 5.4, 400));

        // -- WITH tries to read beyond the end
        // with g_c_tlist_of_c_doubles.StockValue[2] do
        //  m_opening_price:= 10.11
        g_c_tlist_of_c_doubles.StockValue[2]:= c_stock_value.create(7.2, 7.8, 300);

        // -- once the cell exists, can modify it
        with g_c_tlist_of_c_doubles.StockValue[2] do
          m_closing_price:= 77.8;

        g_c_tlist_of_c_doubles.OpeningPrice[2]:= 99.9;
      end// add_doubles_Click

    procedure TForm1.display_doublesClick(SenderTObject);
      var l_indexInteger;
      begin
        for l_index:= 0 to g_c_tlist_of_c_doubles.Count- 1 do
          with g_c_tlist_of_c_doubles.StockValue[l_indexdo
            display(Format('open=%5.2f close=%5.2f  vol=%5d',
                [m_opening_pricem_closing_pricem_volume]));
      end// display_doubles_Click

    procedure TForm1.free_listClick(SenderTObject);
      begin
        FreeAndNil(g_c_tlist_of_c_doubles);
      end// free_double_Click

    dont voici l'image:

    class_of_c_class_test



La solution utilisant plusieurs tList de c_tList OF c_double serait définit par:

type t_stock_valuesRecord
                       m_c_opening_price_list,
                           m_c_closing_price_listc_tList_of_c_double;
                       m_c_volume_listc_tList_of_c_integer;
                     end// t_stock_values



Notez que:

  • si nous avions des traitements distincts sur les listes de réels (pour les intensités, calculer la moyenne, pour les puissances la somme des carrés etc), il la solution de droite serait la plus appropriée, alors que si tous les réels ont le même traitement, et que nous avons des traitements sur une mesure, la solution de gauche serait mieux adaptée



5 - Télécharger le code source Delphi

Vous pouvez télécharger les différents projets:

Ce .ZIP qui comprend:

  • le .DPR, la forme principale, les formes annexes eventuelles
  • les fichiers de paramètres (le schéma et le batch de création)
  • dans chaque .ZIP, toutes les librairies nécessaires à chaque projet (chaque .ZIP est autonaume)
Ces .ZIP, pour les projets en Delphi 6, contiennent des chemins RELATIFS. Par conséquent:
  • créez un répertoire n'importe où sur votre machine
  • placez le .ZIP dans ce répertoire
  • dézippez et les sous-répertoires nécessaires seront créés
  • compilez et exécutez
Ces .ZIP ne modifient pas votre PC (pas de changement de la Base de Registre, de DLL ou autre). Pour supprimer le projet, effacez le répertoire.

La notation utilisée est la notation alsacienne qui consiste à préfixer les identificateurs par la zone de compilation: K_onstant, T_ype, G_lobal, L_ocal, P_arametre, F_unction, C_lasse. Elle est présentée plus en détail dans l'article La Notation Alsacienne



Comme d'habitude:

  • nous vous remercions de nous signaler toute erreur, inexactitude ou problème de téléchargement en envoyant un e-mail à jcolibri@jcolibri.com. Les corrections qui en résulteront pourront aider les prochains lecteurs
  • tous vos commentaires, remarques, questions, critiques, suggestion d'article, ou mentions d'autres sources sur le même sujet seront de même les bienvenus à jcolibri@jcolibri.com.
  • plus simplement, vous pouvez taper (anonymement ou en fournissant votre e-mail pour une réponse) vos commentaires ci-dessus et nous les envoyer en cliquant "envoyer" :
    Nom :
    E-mail :
    Commentaires * :
     

  • et si vous avez apprécié cet article, faites connaître notre site, ajoutez un lien dans vos listes de liens ou citez-nous dans vos blogs ou réponses sur les messageries. C'est très simple: plus nous aurons de visiteurs et de références Google, plus nous écrirons d'articles.



6 - Références

Nous avons déjà présenté des techniques similaires. En particulier Ces techniques sont naturellement abordées dans nos formations sur les techniques de programmation objet, et en particulier


7 - L'auteur

John COLIBRI est passionné par le développement Delphi et les applications de Bases de Données. Il a écrit de nombreux livres et articles, et partage son temps entre le développement de projets (nouveaux projets, maintenance, audit, migration BDE, migration Xe_n, refactoring) pour ses clients, le conseil (composants, architecture, test) et la formation. Son site contient des articles avec code source, ainsi que le programme et le calendrier des stages de formation Delphi, base de données, programmation objet, Services Web, Tcp/Ip et UML qu'il anime personellement tous les mois, à Paris, en province ou sur site client.
Created: jan-04. Last updated: mar-2020 - 250 articles, 620 .ZIP sources, 3303 figures
Contact : John COLIBRI - Tel: 01.42.83.69.36 / 06.87.88.23.91 - email:jcolibri@jcolibri.com
Copyright © J.Colibri   http://www.jcolibri.com - 2001 - 2020
Retour:  Home  Articles  Formations  Développement Delphi  Livres  Pascalissime  Liens  Download
l'Institut Pascal

John COLIBRI

+ Home
  + articles_avec_sources
    + bases_de_donnees
    + web_internet_sockets
    + services_web_
    + prog_objet_composants
      – dump_interface
      – packages_delphi
      – ecriture_de_composant
      – c_list_of_double
      – interfaces_delphi
      – delphi_generics
      – delphi_rtti
    + office_com_automation
    + colibri_utilities
    + uml_design_patterns
    + graphique
    + delphi
    + outils
    + firemonkey
    + vcl_rtl
    + colibri_helpers
    + colibri_skelettons
    + admin
  + formations
  + developpement_delphi
  + présentations
  + pascalissime
  + livres
  + entre_nous
  – télécharger

contacts
plan_du_site
– chercher :

RSS feed  
Blog

Formation Delphi complete L'outil de développpement, le langage de programmation, les composants, les bases de données et la programmation Internet - 5 jours
Formation Ado Gestion de bases de données avec Ado : connexion, accès aux tables, édition d'états - 3 jours