English Русский 中文 Español Deutsch 日本語 Português 한국어 Italiano Türkçe
Les bases de la programmation MQL5 : Tableaux

Les bases de la programmation MQL5 : Tableaux

MetaTrader 5Exemples | 13 janvier 2022, 09:29
899 0
Dmitry Fedoseev
Dmitry Fedoseev

Introduction

Les tableaux font partie intégrante de presque tous les langages de programmation, avec les variables et les fonctions. De nombreux programmeurs novices ont souvent peur des tableaux. Cela semble étrange mais c'est vrai ! Je peux vous assurer qu'ils ne sont pas effrayants du tout. En fait, les tableaux sont similaires aux variables régulières. Sans entrer dans le détail des particularités de la notation, il n'y a pas de grande différence entre écrire une expression à l'aide de variables simples :

Variable0=1;
Variable1=2;

Variable2=Variable0+Variable1;

ou en utilisant des tableaux :

double Variable[3];

Variable[0]=1;
Variable[1]=2;

Variable[2]=Variable[0]+Variable[1];

Comme vous pouvez le voir, la différence n'est pas si grande, sauf que lorsque nous utilisons des tableaux, les noms des variables contiennent des crochets. Il y a une autre différence, plus significative - lors de la déclaration de variables, vous devez spécifier le nom de chaque variable, tandis que lors de la déclaration d'un tableau, vous devez écrire son nom une seule fois et spécifier le nombre de variables entre parenthèses (nombre d'éléments du tableau) . Les avantages de l'utilisation de tableaux par rapport aux variables deviennent encore plus évidents lorsque l'on relève les défis d'un grand nombre de tâches de programmation dans la vie réelle.

Est-il possible que la raison pour laquelle les tableaux soient considérés comme quelque chose de compliqué soit en quelque sorte liée à l'utilisation de "[" et "]" ? Ces symboles sont rarement utilisés ailleurs que dans la programmation lorsque l'on travaille avec des tableaux, de sorte que leur emplacement sur le clavier peut s'effacer de la mémoire et provoquer une gêne. En fait, vous pouvez facilement vous rappeler où elles se trouvent - ces deux touches sont situées à côté de la touche "Entrée" dans un ordre logique : la parenthèse ouvrante est suivie de la parenthèse fermante.


Définition et propriétés générales des tableaux

Ainsi, un tableau est un ensemble numéroté de variables portant le même nom. Les propriétés générales des tableaux incluent le nom du tableau, le type de variable (int, double, etc.) et la taille du tableau. Les éléments du tableau sont indexés à partir de zéro. En parlant d'éléments de tableau, il est toujours préférable d'utiliser le mot "index" au lieu de "nombre" afin de suggérer de commencer à compter les éléments de tableau à partir de zéro (alors que la numérotation commence généralement à partir de un). Avec des éléments indexés de cette manière, l'index du dernier élément est un de moins que le nombre d'éléments du tableau.

Si le tableau est déclaré comme suit :

double Variable[3];

il comporte les éléments suivants : Variable[0], Variable[1] et Variable[2].

A première vue, une telle absence de correspondance entre le nombre d'éléments et l'indice du dernier élément peut sembler gênant. En fait, il offre des avantages significatifs par rapport aux langages de programmation où les éléments du tableau sont indexés à partir de 1 ou la taille du tableau est définie par l'index de son dernier élément plutôt que par le nombre réel d'éléments dans le tableau.

Pour déterminer la taille du tableau dans MQL5, nous utilisons la la fonction ArraySize() :

double Variable[3];

int Size=ArraySize(Variable);

Après exécution du code, la valeur de la variable Size sera égale à 3.


Tableaux statiques et dynamiques

Les tableaux peuvent être statiques et dynamiques. Si la taille du tableau est spécifiée dans sa déclaration, le tableau est statique :

double Variable[3];

La taille d'un tableau statique ne peut pas être modifiée dans le programme. Lors de la déclaration d'un tableau, sa taille peut être spécifiée directement sous forme de nombre (comme dans l'exemple ci-dessus) ou en utilisant prédéfinie :

#define SIZE 3

double Variable[SIZE];

Un tableau dont la taille n'est pas précisée dans sa déclaration est dynamique :

double Variable[];

Avant de pouvoir utiliser un tel tableau, vous devez définir sa taille. La taille est définie par ArrayResize() :

ArrayResize(Variable,3);

La taille d'un tableau dynamique peut être modifiée pendant l'exécution du programme autant de fois que nécessaire, ce qui constitue la différence fondamentale entre les tableaux dynamiques et statiques.

Si vous devez libérer complètement le tableau, utilisez ArrayFree() :

ArrayFree(Variable);

Lors de l'exécution de cette fonction, la taille du tableau est définie sur 0. L'effet produit par cette fonction est similaire à l'action suivante :

ArrayResize(Variable,0);

La libération du tableau peut être utile lorsque le tableau n'est plus nécessaire pour le fonctionnement ultérieur du programme (cela réduit la quantité de mémoire utilisée par le programme) ou au début de l'exécution d'une fonction (si le tableau est utilisé pour la collecte de données).

La ArrayIsDynamic() vous permet de déterminer si un tableau donné est statique ou dynamique :

bool dynamicArray=ArrayIsDynamic(Variable);

La variable dynamicArray contiendra une valeur vraie si le tableau est dynamique ou une valeur fausse si le tableau est statique.


Initialisation

Il est parfois nécessaire de remplir un tableau avec des valeurs ​immédiatement après sa déclaration. Supposons que vous souhaitiez créer plusieurs boutons du même type et les disposer en ligne, chaque bouton ayant son propre texte. C'est là que les grands avantages des tableaux entrent en jeu. Il n'est pas nécessaire de copier le code de chaque bouton (il peut y en avoir des dizaines), ni d'appeler à plusieurs reprises la même fonction. Vous pouvez créer le nombre nécessaire de boutons en itérant sur le tableau dans une boucle après avoir écrit le code d'appel de fonction une seule fois.

Nous déclarons simplement un tableau et affectons immédiatement des valeurs à ses éléments :

string Variable[] = {"Button 1", "Button 2", "Button 3"};

Déclaré de cette façon, le tableau sera toujours statique malgré le fait que sa taille ne soit pas spécifiée. En effet, le nombre de ses éléments est défini par la liste de valeurs (entre accolades).

Il n'y aura pas d'erreur si vous spécifiez le nombre d'éléments du tableau :

string Variable[3] = {"Button 1", "Button 2", "Button 3"};

Mais il vaudrait mieux ne pas le faire - au cours d'améliorations ultérieures du programme, vous devrez peut-être modifier la liste des valeurs du tableau et utiliser un nombre plus ou moins grand d'éléments. Pour déterminer la taille du tableau dans les parties du code où il est utilisé, il est recommandé d'utiliser la fonction ArraySize() au lieu d'une certaine valeur numérique. Cette approche vous permet de modifier uniquement la liste de valeurs sans interférer avec le code principal. Il sera plus approprié de déclarer la variable pour la taille du tableau et de lui affecter la valeur obtenue par la fonction ArraySize() lors de l'initialisation du programme.

Si un tableau statique ne peut pas être initialisé par la liste de valeurs, il serait préférable d'utiliser une constante pour spécifier la taille du tableau. En général, nous suivons le principe de réduire la quantité de code qui devrait être modifié si d'autres améliorations du programme étaient nécessaires. Si vous devez remplir tous les éléments du tableau avec les mêmes valeurs, utilisez la fonction ArrayInitialize() :

ArrayInitialize(Variable,1);

Après avoir exécuté le code ci-dessus, tous les éléments du tableau Var auront la valeur 1. Si les mêmes valeurs doivent être affectées uniquement à certains des éléments du tableau, nous utilisons la fonction ArrayFill() :

double Variable[4];

ArrayFill(Variable,0,2,1);
ArrayFill(Variable,2,2,2);

Après avoir exécuté ce code, les éléments 0 et 1 auront la valeur 1, tandis que les éléments 2 et 3 auront la valeur 2.


Boucle d'itération de tableau

Les tableaux sont généralement traités à l'aide d'une boucle for. Lors de l'utilisation d'un tableau statique dont la taille est connue à l'avance, nous parcourons le tableau en avant ou en arrière, selon la tâche à accomplir :

//--- forwards
for(int i=0; i<SIZE; i++){ 
  // some manipulations on the Variable[i] element
}

//--- backwards
for(int i=SIZE-1; i>=0; i--){
  // some manipulations on the Variable[i] element
}

Si le tableau est dynamique, vous devez déclarer une variable pour une taille de tableau juste avant la boucle, obtenir la taille du tableau et faire une boucle :

int Size=ArraySize(Var);

for(int i=0; i<Size; i++){
  // some manipulations on the Variable[i] element
}

Si, au lieu d'utiliser une variable pour la taille du tableau, vous appelez la fonction ArraySize() lors de la vérification de la condition dans une boucle for, le temps de boucle peut être considérablement prolongé car la fonction ArraySize() sera appelée à chaque itération de boucle. L'appel de fonction prend donc plus de temps que l'appel d'une variable :

for(int i=0; i<ArraySize(Variable); i++){
   // some manipulations on the Variable[i] element
}
L'utilisation du code ci-dessus n'est pas recommandée.

Si l'algorithme du programme permet une itération de boucle arrière, vous pouvez vous passer d'une variable pour la taille du tableau :

for(int i=ArraySize(Variable)-1; i>=0; i--){
  // some manipulations on the Variable[i] element
}

Dans ce cas, la fonction ArraySize() ne sera appelée qu'une seule fois au début de la boucle et la boucle s'exécutera rapidement.


Tableaux multidimensionnels

Nous n'avons considéré jusqu'à présent que des tableaux à une dimension. Ils peuvent être représentés comme suit :

Tableau unidimensionnel

Les tableaux peuvent être multidimensionnels. Alors qu'un tableau unidimensionnel ne contient qu'une seule valeur par index, un tableau multidimensionnel a plusieurs valeurs par index. Les tableaux multidimensionnels sont déclarés comme suit :

double Variable[10][3];

Cela signifie que la première dimension du tableau a dix éléments et la deuxième dimension a trois éléments. Il peut être illustré comme suit :

Tableau multidimensionnel

Pour faciliter la compréhension, un tableau à deux dimensions peut être représenté comme un plan. La taille de la première dimension détermine la longueur, la taille de la seconde détermine la largeur et la valeur de l'élément définit les paramètres d'un point donné du plan, par exemple la hauteur au-dessus du niveau de la mer.

Un tableau peut également être tridimensionnel :

double Variable[10][10][10];

Ce tableau peut être représenté comme un cube ou un parallélogramme : la première dimension détermine la longueur, la deuxième dimension détermine la largeur, la troisième détermine la hauteur et la valeur de l'élément définit les paramètres d'un point donné de l'espace.

Le nombre maximal de dimensions de tableau autorisé dans MQL5 est de 4.

Un tableau multidimensionnel peut être statique ou dynamique uniquement dans la première dimension, toutes les autres dimensions étant statiques. Ainsi, la fonction ArrayResize() vous permet de modifier uniquement la taille de la première dimension. Les tailles des autres dimensions doivent être spécifiées lors de la déclaration d'un tableau :

double Variable[][3][3];

Pour déterminer la taille d'un tableau multidimensionnel à l'aide de la fonction ArraySize(), nous devons garder à l'esprit une chose : lors de la modification de la taille du tableau à l'aide de la fonction ArrayResize(), le deuxième paramètre de la fonction est la taille de la première dimension du tableau. Pourtant, la fonction ArraySize() renvoie le nombre total d'éléments plutôt que la taille de la première dimension :

double Variable[][3][3]; 

ArrayResize(Variable,3); 
int Size = ArraySize(Variable);

Suite à l'exécution du code ci-dessus, la variable Size sera égale à 27. N'oubliez pas cette particularité lors de l'itération sur des tableaux multidimensionnels dans une boucle si vous avez besoin d'obtenir la taille de la première dimension :

double Variable[][3][3];
 
ArrayResize(Variable,3); 

int Size=ArraySize(Variable)/9; // Determine the size of the first dimension

for(int i=0; i<Size; i++) {
   for(int j=0; j<3; j++) {
      for(int k=0; k<3; k++) {
            //  some manipulations on the Var[i][j][k] element;
      }   
   }   
}

Comme mentionné précédemment, il est conseillé de suivre le principe de réduction de la quantité de code qui devrait être modifié si d'autres améliorations du programme sont nécessaires. Dans l'exemple de code ci-dessus, nous avons utilisé le nombre 9 qui peut cependant également être calculé. Pour cela, nous pouvons utiliser la fonction ArrayRange() qui renvoie le nombre d'éléments contenus dans la dimension spécifiée du tableau. Si le nombre de dimensions du tableau est connu, nous pouvons faire un calcul simple :

int Elements=ArrayRange(Variable,1)*ArrayRange(Variable,2);
int Size=ArraySize(Variable)/Elements;

Nous pouvons le rendre plus universel :

int Elements=1; // One element for a one-dimensional array
int n=1; // Start with the second dimension (dimensions are numbered from zero)

while(ArrayRange(Variable,n) > 0){ // Until there are elements in the dimension
   Elements*=ArrayRange(Variable,n); // Multiplication of the number of elements
   n++; // Increase in the dimension's number
}

À ce stade, vous pouvez penser qu'il serait bon de créer une fonction pour un tel calcul. Malheureusement, cela n'est pas possible car un tableau aléatoire ne peut pas être passé à une fonction. Lors de la déclaration d'un argument de fonction, vous devez spécifier clairement le nombre d'éléments dans toutes les dimensions du tableau à l'exception de la première, rendant ainsi une telle fonction inutile. Ces calculs sont plus faciles et mieux effectués lors de l'initialisation du programme. Lors de la déclaration d'un tableau, il est conseillé d'utiliser des constantes qui déterminent les tailles des dimensions :

#define SIZE1 3
#define SIZE2 3
#define TOTAL SIZE1*SIZE2 

L'initialisation de tableaux multidimensionnels à l'aide de la liste de valeurs est similaire à l'initialisation de tableaux unidimensionnels. Mais comme un tableau multidimensionnel est en quelque sorte composé de plusieurs autres tableaux, chacun de ces tableaux doit être séparé par des accolades.

Supposons que nous ayons un tableau comme suit :

double Variable[3][3];

Ce tableau est composé de trois tableaux de trois éléments chacun :

double Variable[][3]={{1, 2, 3},{ 4, 5, 6},{7, 8, 9}};

Un tableau à trois dimensions est traité de la même manière. Le code peut être décomposé en plusieurs lignes pour faciliter la compréhension de la structure du tableau :

double Variable[][3][3]={
   {
      {1, 2, 3},
      {4, 5, 6},
      {7, 8, 9}
   },
   {
      {10, 20, 30},
      {40, 50, 60},
      {70, 80, 90}
   },
   {
      {100, 200, 300},
      {400, 500, 600},
      {700, 800, 900}
   }
};

L'initialisation d'un tableau multidimensionnel à l'aide de la fonction ArrayInitialize() s'effectue de la même manière que l'initialisation d'un tableau unidimensionnel :

ArrayInitialize(Variable,1);

Après avoir exécuté le code, tous les éléments du tableau auront la valeur 1. Il en est de même pour la fonction ArrayFill() :

double var[3][3][3];

ArrayFill(Variable,0,9,1);
ArrayFill(Variable,9,9,10);
ArrayFill(Variable,18,9,100);

Suite à l'exécution de ce code, tous les éléments associés au premier élément de la première dimension auront la valeur 1, ceux associés au deuxième élément auront la valeur 10 et ceux associés au troisième élément - 100.


Passage d'un tableau à une fonction

Contrairement aux variables, les tableaux ne peuvent être transmis à une fonction que par référence. Cela signifie que la fonction ne crée pas sa propre instance du tableau et fonctionne à la place directement avec le tableau qui lui est passé. Ainsi, toutes les modifications apportées par la fonction au tableau affectent le tableau d'origine.

Si une variable est passée à une fonction de manière habituelle (par valeur), la valeur de la variable passée ne peut pas être modifiée par la fonction :

int x=1;
Func(x);

void  Func(int arg){
   arg=2;
}

Après avoir exécuté la fonction Func(), la valeur x reste égale à 1.

Si une variable est passée par référence (notée &), la fonction peut changer la valeur de cette variable qui lui est passée :

int x=1;
Func(x);

void  Func(int &arg){
   arg=2;
}

Après avoir exécuté la fonction Func(), la valeur x devient égale à 2.

Lorsque vous passez un tableau à une fonction, vous devez spécifier que l'argument est passé par référence et représente un tableau (entre parenthèses) :

void Func(double &arg[]){
   // ...
}

Lors de la transmission de tableaux multidimensionnels à une fonction, les tailles de dimension (sauf pour la première) doivent être spécifiées :

double var[][3][3];

void Func(double &arg[][3][3]){
   // ...
}

Dans ce cas, il est plus conseillé d'utiliser des constantes :

#define SIZE1 3
#define SIZE2 3

double Var[][SIZE1][SIZE2];

void Func(double &arg[][SIZE1][SIZE2]){
   // ...
}


Enregistrement et chargement de tableaux à partir d'un fichier

Lors de l'enregistrement et du chargement d'un tableau à partir d'un fichier, vous devez toujours tenir compte de la différence de valeurs entre la taille de la première dimension du tableau et le nombre total d'éléments du tableau. Pour enregistrer un tableau, nous écrivons d'abord la taille du tableau (nombre total d'éléments déterminé par la fonction ArraySize()), puis l'ensemble du tableau dans le fichier :

bool SaveArrayToFile(string FileName,string &Array[])
  {
//--- Open the file
   int h=FileOpen(FileName,FILE_TXT|FILE_WRITE);
   if(h==-1) return(false); // Error opening the file
//--- Write to the file
   FileWriteInteger(h,ArraySize(Array),INT_VALUE); // Write the array size
   FileWriteArray(h,Array); // Write the array
//--- Close the file
   FileClose(h);
   return(true); // Saving complete
  }

En conséquence, nous obtenons une fonction assez universelle pour enregistrer des tableaux à une dimension.

Pour charger un tableau à partir d'un fichier, nous devons d'abord lire la taille du tableau, le redimensionner et enfin lire le tableau :

bool LoadArrayFromFile(string FileName,double &Array[])
  {
//--- Open the file
   int h=FileOpen(FileName,FILE_BIN|FILE_READ);
   if(h==-1) return(false); // Error opening the file
//--- Read the file
   int Size=FileReadInteger(h,INT_VALUE); // Read the number of array elements
   ArrayResize(Array,Size); // Resize the array. 
                            // In one-dimensional arrays the size of the first dimension is equal to the number of array elements.
   FileReadArray(h,Array); // Read the array from the file
//--- Close the file
   FileClose(h);
   return(true); // Reading complete
  }

Lors du chargement d'un tableau multidimensionnel à partir d'un fichier, vous devrez calculer la taille de la première dimension. Par exemple, supposons que nous lisons un tableau à trois dimensions :

bool LoadArrayFromFile3(string FileName,double &Array[][SIZE1][SIZE2])
  {
//--- Open the file
   int h=FileOpen(FileName,FILE_BIN|FILE_READ);
   if(h==-1)return(false); // Error opening the file
//--- Read the file   
   int SizeTotal=FileReadInteger(h,INT_VALUE); // Read the number of array elements
   int Elements=SIZE1*SIZE2; // Calculate the number of elements 
   int Size=SizeTotal/Elements; // Calculate the size of the first dimension
   ArrayResize(Array,Size); // Resize the array
   FileReadArray(h,Array); // Read the array
//--- Close the file
   FileClose(h);
   return(true); // Reading complete
  }

Il se peut que le fichier contienne un tableau 2 par 3 alors que nous essayons de le lire comme un tableau 3 par 3. Vous pouvez vérifier la correspondance entre les tailles en multipliant la taille calculée de la première dimension par le nombre d'éléments. Si la valeur résultante est égale au nombre total d'éléments du tableau, on peut parler de correspondance.

Cependant, le tableau Var[2][3] correspondra au tableau Var[3][2]. Si vous devez également couvrir ce cas, vous devez enregistrer plus d'informations sur un tableau multidimensionnel. Par exemple, vous pouvez d'abord enregistrer le nombre d'éléments du tableau, puis le nombre de dimensions du tableau suivi des tailles de chacune des dimensions et du tableau lui-même.

La dernière fonction fournie ci-dessus n'est pas universelle et est conçue pour lire uniquement des tableaux tridimensionnels où la taille de la deuxième dimension est égale à SIZE1 et la taille de la troisième dimension est égale à SIZE2. Puisqu'il n'y a aucun moyen de modifier dynamiquement les tailles de toutes les dimensions des tableaux, à l'exception de la première, ce n'est pas un problème - nous allons créer des fonctions pour les tableaux qui doivent être utilisés dans le programme.

L'universalité n'est dans ce cas pas nécessaire : les tailles des dimensions du tableau (sauf la première) ne seront pas contrôlées via les paramètres externes du programme. Cependant, si vous devez implémenter la possibilité de contrôler les tailles d'autres dimensions, vous pouvez résoudre cette tâche en utilisant un tableau multidimensionnel de taille sciemment plus grande et de variables supplémentaires ou en appliquant des techniques de programmation orientée objet (OOP). Nous parlerons plus en détail de la deuxième approche plus loin dans cet article.


Utilisation de tableaux dynamiques

Les tableaux dynamiques sont utilisés lorsque vous ne connaissez pas la taille du tableau à l'avance. Si la taille du tableau dépend des paramètres définis dans la fenêtre des propriétés du programme, l'utilisation de tableaux dynamiques ne sera pas un problème : la taille du tableau ne sera modifiée qu'une seule fois lors de l'initialisation du programme.

Un tableau peut être utilisé pour collecter dynamiquement certaines informations, par exemple concernant les ordres en attente. Leur nombre peut varier, c'est-à-dire que la taille requise n'est pas connue à l'avance. Dans ce cas, le plus simple serait de changer la taille du tableau à 0 avant de passer par les commandes et d'augmenter la taille du tableau d'un élément au fur et à mesure que nous parcourons chaque ordre. Cela fonctionnera, mais très lentement.

On ne peut changer la taille du tableau qu'une seule fois en fonction du nombre d’ordres, avant de passer par les ordres. Cela nécessitera une autre variable pour l'index du dernier élément actif du tableau (ou un certain nombre d'éléments du tableau réellement actifs, au lieu de l'index). Cette méthode convient si vous connaissez déjà la taille maximale du tableau. Si la taille maximale du tableau n'est pas connue, nous pouvons accélérer son utilisation en redimensionnant le tableau à l'aide de morceaux, comme indiqué dans la classe suivante :

class CDynamicArray
  {
private:
   int               m_ChunkSize;    // Chunk size
   int               m_ReservedSize; // Actual size of the array
   int               m_Size;         // Number of active elements in the array
public:
   double            Element[];      // The array proper. It is located in the public section, 
                                     // so that we can use it directly, if necessary
   //+------------------------------------------------------------------+
   //|   Constructor                                                    |
   //+------------------------------------------------------------------+
   void CDynamicArray(int ChunkSize=1024)
     {
      m_Size=0;                            // Number of active elements
      m_ChunkSize=ChunkSize;               // Chunk size
      m_ReservedSize=ChunkSize;            // Actual size of the array
      ArrayResize(Element,m_ReservedSize); // Prepare the array
     }
   //+------------------------------------------------------------------+
   //|   Function for adding an element at the end of array             |
   //+------------------------------------------------------------------+
   void AddValue(double Value)
     {
      m_Size++; // Increase the number of active elements
      if(m_Size>m_ReservedSize)
        { // The required number is bigger than the actual array size
         m_ReservedSize+=m_ChunkSize; // Calculate the new array size
         ArrayResize(Element,m_ReservedSize); // Increase the actual array size
        }
      Element[m_Size-1]=Value; // Add the value
     }
   //+------------------------------------------------------------------+
   //|   Function for getting the number of active elements in the array|
   //+------------------------------------------------------------------+
   int Size()
     {
      return(m_Size);
     }
  };

Vous pouvez trouver cette classe dans le fichier CDynamicArray.mqh joint. Le fichier doit être placé dans le dossier MQL5\Include du répertoire de données du terminal.

Évaluons et comparons maintenant les performances du code dans les deux situations : où la taille du tableau est séquentiellement augmentée de 1 et où elle est augmentée à l'aide de morceaux :

int n=50000;
   double ar[];
   CDynamicArray da;

//--- Option 1 (increasing the size by the 1st element)
   long st=GetTickCount(); // Store the start time 
   ArrayResize(ar,0); // Set the array size to zero 
   for(int i=0;i<n;i++)
     {
      ArrayResize(ar,i+1); // Resize the array sequentially
      ar[i]=i;
     }
   Alert("Option 1: "+IntegerToString(GetTickCount()-st)+" ms"); // Message regarding the amount of time required to perform the first option

//--- Option 2 (increasing the size using chunks)
   st=GetTickCount(); // Store the start time 
   for(int i=0;i<n;i++)
     {
      da.AddValue(i); // Add an element
     }
   Alert("Option 2: "+IntegerToString(GetTickCount()-st)+" ms"); // Message regarding the amount of time required to perform the second option

  }

Ce test sous forme de script se trouve dans le fichier joint sTest_Speed.mq5. Le fichier doit être placé dans le dossier MQL5\Scripts du répertoire de données du terminal.

La performance de la première option a pris quelques secondes, tandis que la deuxième option était presque instantanée.


Ordre d'indexation des tableaux

Habituellement, lors du redimensionnement d'un tableau, de nouveaux éléments sont ajoutés à la fin du tableau :

double ar[]; // Array
ArrayResize(ar,2); // Prepare the array
ar[0]=1; // Set the values
ar[1]=2; 
ArrayResize(ar,3); // Increase the array size
ar[2]=3; // Set the value for the new array element
Alert(ar[0]," ",ar[1]," ",ar[2]); // Print array values

Après l'exécution de ce code, les valeurs contenues dans le tableau doivent être 1, 2 et 3.

Les éléments des tableaux peuvent également être indexés dans l'ordre inverse. L'ordre d'indexation est défini par la fonction ArraySetAsSeries() :

ArraySetAsSeries(ar,true); // set indexing in reverse order
ArraySetAsSeries(ar,false); // set normal indexing

Lors de la modification de la taille du tableau indexé dans l'ordre inverse, un nouvel élément est ajouté au début du tableau :

double ar[]; // Array
ArrayResize(ar,2); // Prepare the array
ar[0]=1; // Set the values
ar[1]=2; 
ArraySetAsSeries(ar,true); // Change the indexing order
ArrayResize(ar,3); // Increase the array size
ar[0]=3; // Set the value for the new array element
Alert(ar[0]," ",ar[1]," ",ar[2]); // Print array values

Après l'exécution de ce code, les valeurs contenues dans le tableau doivent être 3, 2 et 1.

Il s'avère que dans les deux cas, le nouvel élément est ajouté du même côté du tableau, la seule différence étant l'ordre d'indexation. Cette fonction ne peut pas être utilisée pour ajouter des éléments au début du tableau dont les éléments sont indexés dans l'ordre normal. Pour ajouter un élément à la fin du tableau normalement indexé, il vous suffit d'augmenter la taille du tableau et d'attribuer une valeur au dernier élément.

Pour ajouter un élément au début du tableau, vous devez augmenter la taille du tableau, déplacer toutes les valeurs et affecter une nouvelle valeur à l'élément zéro. Dans les tableaux indexés dans l'ordre inverse, un nouvel élément peut facilement être ajouté au début du tableau. Mais si vous devez ajouter un nouvel élément à la fin du tableau, vous devez d'abord augmenter la taille du tableau et après avoir déplacé toutes les valeurs au début, attribuer une nouvelle valeur au dernier élément. Les manipulations d'ordre d'indexation ne résoudront pas ce problème.

L'ordre d'indexation des tableaux peut être déterminé à l'aide de la fonction ArrayIsSeries() :

bool series=ArrayIsSeries(ar);

Si le tableau est indexé dans l'ordre inverse, la fonction retournera vrai.

Les tableaux indexés dans l'ordre inverse sont principalement utilisés dans les Expert Advisors. Lors du développement des EA, il est souvent plus pratique de compter les barres de droite à gauche et donc de copier les données de prix et les tampons d'indicateurs dans des tableaux avec indexation inversée.


Copie de tableaux

Le moyen le plus simple de copier est de parcourir un tableau dans une boucle et de copier élément par élément d'un tableau à l'autre. Il existe cependant une fonction spéciale dans MQL5 qui nous permet de copier des tableaux - ArrayCopy() :

double ar1[]={1,2,3};
double ar2[];

ArrayCopy(ar2,ar1);

Suite à l'exécution du code ci-dessus, le tableau ar2 sera composé de trois éléments avec les mêmes valeurs que dans le tableau ar1 : 1, 2, 3.

Si le nombre d'éléments à copier ne tient pas dans le tableau vers lequel vous copiez, la taille du tableau sera automatiquement augmentée (le tableau doit être dynamique). Si le tableau est plus grand que le nombre d'éléments à copier, sa taille restera la même.

La fonction ArrayCopy() vous permet également de ne copier qu'une partie d'un tableau. En utilisant les paramètres optionnels de la fonction, vous pouvez spécifier le premier élément à copier, l'indice du premier élément copié dans le nouveau tableau et le nombre d'éléments que vous allez copier.

En plus de copier des éléments d'un tableau dans l'autre, la fonction ArrayCopy() peut être utilisée pour copier des éléments dans le même tableau :

double ar1[]={1,2,3,4,5};
ArrayCopy(ar1,ar1,1,2);

Nous copions les données en commençant par l'élément d'index 2 et les collons en commençant par l'index 1. Après avoir exécuté ce code, le tableau contiendra les valeurs suivantes : 1, 3, 4, 5, 5.

La fonction ArrayCopy() vous permet également de décaler les données vers la droite :

double ar1[]={1,2,3,4,5};
ArrayCopy(ar1,ar1,2,1);

Nous prenons les données commençant par l'élément d'index 1 et les organisons en commençant par l'index 2. Après l'exécution de ce code, le tableau contiendra les valeurs suivantes : 1, 2, 2, 3, 4.

La fonction ArrayCopy() peut également être appliquée à des tableaux multidimensionnels dans lesquels elle se comporte comme si le tableau était unidimensionnel et que tous ses éléments étaient disposés en série :

double ar[3][2]={{1, 2},{3, 4},{5, 6}};
ArrayCopy(ar,ar,2,4);

Suite à l'exécution de ce code, le tableau apparaîtra comme suit : {1, 2}, {5, 6}, {5, 6}.


Tri d'un tableau

Un tableau peut être trié à l'aide de la fonction ArraySort() :

double ar[]={1,3,2,5,4};
ArraySort(ar);

Après avoir exécuté le code ci-dessus, les valeurs du tableau seront organisées dans l'ordre suivant : 1, 2, 3, 4, 5.

La fonction ArraySort() ne peut pas être appliquée aux tableaux multidimensionnels. Vous pouvez trouver des informations sur le tri des tableaux multidimensionnels et des structures de données dans l'article intitulé "Tables électroniques en MQL5".


Pour faire une recherche binaire, nous utilisons la fonction ArrayBsearch(). Cette fonction ne peut fonctionner correctement qu'avec des tableaux triés. La recherche binaire tire son nom du fait que l'algorithme divise continuellement un tableau en deux parties. L'algorithme compare d'abord la valeur cible avec la valeur de l'élément central du tableau, déterminant ainsi la moitié qui contient l'élément cible - le sous-tableau à gauche ou le sous-tableau à droite. Il compare ensuite la valeur cible avec la valeur de l'élément central des sous-tableaux et ainsi de suite.

La fonction ArrayBsearch() renvoie l'index de l'élément avec la valeur cible :

double ar[]={1,2,3,4,5};

int index=ArrayBsearch(ar,3);

Après avoir exécuté ce code, la variable d'index aura la valeur 2.

Si la valeur cible ne peut pas être trouvée dans le tableau, la fonction renverra l'index de l'élément avec la valeur inférieure la plus proche. En raison de cette propriété, la fonction peut être utilisée pour rechercher des barres par heure. S'il n'y a pas de barre avec l'heure spécifiée, une barre avec une durée inférieure doit être utilisée dans les calculs.

Si la valeur cible n'est pas dans le tableau et se situe au-delà de la plage de valeurs du tableau, la fonction renverra 0 (si la valeur cible est inférieure à la valeur minimale) ou le dernier index (si la valeur cible est supérieure à la valeur maximale ).

Il n'y a qu'une seule méthode qui vous permet d'effectuer une recherche dans un tableau non trié - l'itération sur un tableau :

int FindInArray(int &Array[],int Value){
   int size=ArraySize(Array);
      for(int i=0; i<size; i++){
         if(Array[i]==Value){
            return(i);
         }
      }
   return(-1);
}

Dans l'exemple ci-dessus, la fonction renvoie l'index de l'élément avec la valeur cible. Si la valeur cible n'est pas dans le tableau, la fonction renvoie -1.


Trouver le maximum et le minimum

Les valeurs maximale et minimale du tableau peuvent être trouvées à l'aide des fonctions ArrayMaximum() et ArrayMinimum() qui renvoient l'index de l'élément avec la valeur maximale ou minimale, respectivement :

double ar[]={3,2,1,2,3,4,5,4,3};

int MaxIndex=ArrayMaximum(ar);
int MinIndex=ArrayMinimum(ar);
double MaxValue=ar[MaxIndex];
double MinValue=ar[MinIndex];

Suite à l'exécution de ce code, la variable MaxIndex sera égale à 6, la variable MinIndex sera égale à 2, MaxValue aura la valeur 5 et MinValue sera 1.

Les fonctions ArrayMaximum() et ArrayMinimum() permettent de limiter la plage de recherche en spécifiant l'index du premier élément de la plage de recherche et le nombre d'éléments de la plage de recherche :

int MaxIndex=ArrayMaximum(ar,5,3);
int MinIndex=ArrayMinimum(ar,5,3);

Dans ce cas, MaxIndex aura la valeur 6 et MinIndex sera - 5. Veuillez noter que la plage spécifiée contient deux positions avec la valeur minimale de 4 - la position 5 et la position 7. Dans une situation comme celle-ci, la fonction renvoie l'index de l'élément le plus proche du début du tableau. Ces fonctions fonctionnent de la même manière avec des tableaux indexés dans l'ordre inverse - ils renvoient le plus petit index.

Nous avons donc passé en revue toutes les fonctions standard disponibles dans MQL5 pour travailler avec des tableaux.


Création de tableaux multidimensionnels à l'aide de la POO

Un ensemble de classes pour créer des tableaux multidimensionnels comprend trois classes : une classe de base et deux classes enfants. Selon la classe enfant sélectionnée à l'étape de création d'un objet, l'objet peut représenter un tableau de variables doubles ou un tableau d'objets. Chaque élément du tableau d'objets peut représenter un autre tableau d'objets ou de variables.

La classe de base et les classes enfants ne contiennent pratiquement aucune fonction, à l'exception du destructeur dans la classe de base et des constructeurs dans les classes enfants. Le destructeur de la classe de base sert à supprimer tous les objets à la fin du programme ou de la fonction. Les constructeurs des classes enfants ne sont utilisés que pour mettre à l'échelle des tableaux en fonction de la taille spécifiée dans les paramètres du constructeur : pour mettre à l'échelle un tableau d'objets dans une classe et un tableau de variables dans l'autre classe. Voici le code pour l'implémentation de ces classes :

//+------------------------------------------------------------------+
//|   Base class                                                     |
//+------------------------------------------------------------------+
class CArrayBase
  {
public:
   CArrayBase       *D[];
   double            V[];

   void ~CArrayBase()
     {
      for(int i=ArraySize(D)-1; i>=0; i--)
        {
         if(CheckPointer(D[i])==POINTER_DYNAMIC)
           {
            delete D[i];
           }
        }
     }
  };
//+------------------------------------------------------------------+
//|   Child class 1                                                  |
//+------------------------------------------------------------------+
class CDim : public CArrayBase
  {
public:
   void CDim(int Size)
     {
      ArrayResize(D,Size);
     }
  };
//+------------------------------------------------------------------+
//|   Child class 1                                                  |
//+------------------------------------------------------------------+
class CArr : public CArrayBase
  {
public:
   void CArr(int Size)
     {
      ArrayResize(V,Size);
     }
  };

Vous pouvez trouver ces classes dans le fichier CMultiDimArray.mqh joint. Le fichier doit être placé dans le dossier MQL5\Include du répertoire de données du terminal.

Utilisons maintenant cette classe pour construire un tableau unidimensionnel de similarité :

CArrayBase * A; // Declare a pointer
   A=new CArr(10); // Load a child class instance that scales the array of variables. 
                   // The array will consist of 10 elements.

//--- Now the array can be used:
   for(int i=0; i<10; i++)
     {
      //--- Assign to each element of the array successive values from 1 to 10
      A.V[i]=i+1;
     }
   for(int i=0;i<10;i++)
     {
      //--- Check the values
      Alert(A.V[i]);
     }
   delete A; // Delete the object
  }

Cet exemple sous forme de script se trouve dans le fichier joint sTest_1_Arr.mq5. Le fichier doit être placé dans le dossier MQL5\Scripts du répertoire de données du terminal.

Essayons maintenant de créer un tableau à deux dimensions. Chaque élément de la première dimension contiendra un nombre différent d'éléments de la deuxième dimension - un dans la première, deux dans la seconde, etc. :

CArrayBase*A;  // Declare a pointer
   A=new CDim(3); // The first dimension represents an array of objects

//--- Each object of the first dimension represents an array of variables of different sizes 
   A.D[0]=new CArr(1);
   A.D[1]=new CArr(2);
   A.D[2]=new CArr(3);
//--- Assign values
   A.D[0].V[0]=1;

   A.D[1].V[0]=10;
   A.D[1].V[1]=20;

   A.D[2].V[0]=100;
   A.D[2].V[1]=200;
   A.D[2].V[2]=300;
//--- Check the values
   Alert(A.D[0].V[0]);

   Alert(A.D[1].V[0]);
   Alert(A.D[1].V[1]);

   Alert(A.D[2].V[0]);
   Alert(A.D[2].V[1]);
   Alert(A.D[2].V[2]);
//---
   delete A; // Delete the object

Cet exemple sous forme de script se trouve dans le fichier joint sTest_2_Dim.mq5. Le fichier doit être placé dans le dossier MQL5\Scripts du répertoire de données du terminal.

Les tableaux résultants sont en quelque sorte statiques car les classes n'ont pas de méthodes pour modifier la taille des tableaux. Mais comme les tableaux D[] et V[] sont situés dans la section publique de la classe, ils sont disponibles pour toute manipulation. Et vous pouvez sans aucune difficulté mettre à l'échelle le tableau V[]. Lors de la mise à l'échelle des tableaux D[] et de la réduction de leurs tailles, vous devez d'abord supprimer les objets pointés par les objets à supprimer ou y charger des objets lors de l'augmentation de la taille des tableaux.

On peut également penser à d'autres façons d'implémenter des tableaux multidimensionnels en utilisant la POO ou des structures de données, si on le souhaite.


Conclusion

L'article a couvert toutes les fonctions standard disponibles dans MQL5 pour travailler avec des tableaux. Nous avons passé en revue les particularités et certaines des techniques les plus importantes pour la manipulation des tableaux. Le langage MQL5 offre un total de 15 fonctions dont certaines sont d'une importance capitale, tandis que d'autres peuvent rester totalement inutilisées, sauf dans les cas où vous devez résoudre un problème non conventionnel. Les fonctions peuvent être classées par importance et fréquence d'utilisation comme suit :

  1. ArraySize() et ArrayResize() sont les fonctions essentielles.

  2. ArrayMaximum(), ArrayMinimum(), ArrayCopy(), ArrayInitialize(), ArrayFill() et ArrayFree() sont des fonctions qui facilitent considérablement l'utilisation des tableaux.

  3. ArraySort() est une fonction importante et pratique qui est cependant rarement utilisée en raison de sa faible fonctionnalité.

  4. ArrayBsearch() est une fonction rarement utilisée, mais elle peut être très importante dans de rares cas exceptionnels.

  5. ArraySetAsSeries(), ArrayRange(), ArrayGetAsSeries(), ArrayIsDynamic() et ArrayIsSeries() sont des fonctions très rarement utilisées ou presque jamais utilisées.

L'utilisation de tableaux dynamiques, qui est l'une des techniques de programmation décrites dans cet article, doit faire l'objet d'une attention particulière car elle a un effet important sur les performances du programme, que l'on peut même qualifier de déterminant.

Traduit du russe par MetaQuotes Ltd.
Article original : https://www.mql5.com/ru/articles/567

Fichiers joints |
cdynamicarray.mqh (2.59 KB)
stest_speed.mq5 (1.6 KB)
cmultidimarray.mqh (1.67 KB)
stest_1_arr.mq5 (1.29 KB)
stest_2_dim.mq5 (1.43 KB)
Modifier les paramètres de l'Expert Advisor à partir du panneau de l'utilisateur "On The Fly" Modifier les paramètres de l'Expert Advisor à partir du panneau de l'utilisateur "On The Fly"
Cet article fournit un petit exemple illustrant la mise en œuvre d'un Expert Advisor dont les paramètres peuvent être contrôlés depuis le panneau utilisateur. Lors de la modification des paramètres "On The Fly", l'Expert Advisor écrit les valeurs obtenues à partir du panneau d'informations dans un fichier pour les lire davantage à partir du fichier et les afficher en conséquence sur le panneau. Cet article peut être pertinent pour ceux qui tradent manuellement ou en mode semi-automatique.
Signaux de Trading pour MetaTrader 5: Une meilleure alternative aux comptes PAMM ! Signaux de Trading pour MetaTrader 5: Une meilleure alternative aux comptes PAMM !
Nous sommes heureux d'annoncer que MetaTrader 5 propose désormais des signaux de trading, offrant ainsi un outil puissant aux investisseurs et aux gestionnaires. Pendant que vous suivez les trades d'un trader performant, le terminal les reproduira automatiquement dans votre compte !
Apprentissage automatique : Comment les machines à vecteurs de support peuvent être utilisées dans le trading Apprentissage automatique : Comment les machines à vecteurs de support peuvent être utilisées dans le trading
Les machines à vecteurs de support sont utilisées depuis longtemps dans des domaines tels que la bio-informatique et les mathématiques appliquées pour évaluer des ensembles de données complexes et extraire des modèles utiles pouvant être utilisés pour classer les données. Cet article examine ce qu'est une machine à vecteurs de support, comment elle fonctionne et pourquoi elle peut être si utile pour extraire des motifs complexes. Nous étudions ensuite comment ils peuvent être appliqués au marché et potentiellement utilisés pour conseiller sur le trading. À l'aide de l'outil d'apprentissage par machine à vecteur de support, l'article fournit des exemples concrets qui permettent aux lecteurs d'expérimenter leur propre trading.
Test rapide des idées de trading sur le graphique Test rapide des idées de trading sur le graphique
L'article décrit la méthode de test visuel rapide des idées de trading. La méthode est basée sur la combinaison d'un graphique de prix, d'un indicateur de signal et d'un indicateur de calcul de solde. J'aimerais partager ma méthode de recherche d'idées de trading, ainsi que la méthode que j'utilise pour tester rapidement ces idées.