English Русский 中文 Español 日本語 Português 한국어 Français Italiano Türkçe
Grundlagen der Programmierung in MQL5: Arrays

Grundlagen der Programmierung in MQL5: Arrays

MetaTrader 5Beispiele | 5 Mai 2016, 14:13
7 227 1
Dmitry Fedoseev
Dmitry Fedoseev

Einleitung

Arrays sind zusammen mit Variablen und Funktionen ein wesentlicher Bestandteil so gut wie jeder Programmiersprache. Viele Programmierneulinge haben oft Angst vor Arrays. Es klingt seltsam, doch es ist wahr! Ich kann Ihnen versichern, dass sie überhaupt nicht furchteinflößend sind. Tatsächlich sind Arrays herkömmlichen Variablen ähnlich. Lässt man die Besonderheiten der Notation außer Acht, besteht kein großer Unterschied zwischen dem Schreiben eines Ausdrucks mit einfachen Variablen:

Variable0=1;
Variable1=2;

Variable2=Variable0+Variable1;

oder mir Arrays:

double Variable[3];

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

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

Wie Sie sehen können, ist der Unterschied nicht sehr groß, abgesehen davon, dass die Namen von Variablen Klammern enthalten, wenn wir Arrays verwenden. Es gibt einen weiteren, wichtigeren Unterschied: Beim Deklarieren von Variablen müssen Sie den Namen jeder Variable festlegen, während sie beim Deklarieren eines Arrays seinen Namen nur einmal schreiben und die Menge der Variablen (Menge der Elemente des Arrays) in Klammern festlegen müssen. Die Vorteile von Arrays gegenüber Variablen werden beim Umgang mit den Herausforderungen zahlreicher Programmieraufgaben aus dem echten Leben noch deutlicher.

Wäre es möglich, dass die Verwendung von "[" und "]" etwas damit zu tun hat, dass Arrays als kompliziert betrachtet werden? Diese Zeichen werden außerhalb der Programmierung unter Zuhilfenahme von Arrays nur selten verwendet, also kann man schon einmal unpraktischerweise vergessen, wo sie sich auf der Tastatur befinden. Tatsächlich kann man sich bei einer englischen Tastatur einfach merken, wo sie sind: Die zwei Tasten befinden sich in logischer Reihenfolge neben der Enter-Taste – auf die öffnende Klammer folgt die schließende Klammer. Bei der deutschen Tastatur muss man sich mit der Tatsache behelfen, dass sie sich auf denselben Tasten befinden wie ihre runden Gegenstücke und mithilfe von AltGr eingefügt werden.


Definition und allgemeine Eigenschaften von Arrays

Ein Array ist also ein durchnummerierter Satz von Variablen mit dem gleichen Namen. Zu den allgemeinen Eigenschaften von Arrays gehören der Name des Arrays, der Variablentyp (int, double usw.) und die Array-Größe. Elemente von Arrays werden ab Null indexiert. Da wir gerade von Array-Elementen sprechen: Es ist immer besser, das Wort "Index" anstatt "Nummer" zu benutzen, um darauf hinzudeuten, dass wir die Zählung von Array-Elementen bei Null beginnen (während eine Nummerierung für gewöhnlich bei Eins beginnt). Bei dieser Art der Indexierung der Elemente ist der Index des letzten Elements eins weniger als die Menge der Array-Elemente.

Wenn das Array folgendermaßen deklariert ist:

double Variable[3];

hat es die folgenden Elemente: Variable[0], Variable[1] und Variable[2].

Auf den ersten Blick erscheint diese Nichtentsprechung zwischen der Menge der Elemente und des Index des letzten Elements unpraktisch. Tatsächlich liefert sie aber wesentliche Vorteile gegenüber Programmiersprachen, in denen Elemente von Arrays ab 1 indexiert werden oder die Größe des Arrays durch den Index seines letzten Elements anstatt der tatsächlichen Menge von Elementen im Array definiert wird.

Um die Größe des Arrays in MQL5 zu bestimmen, nutzen wir die Funktion ArraySize():

double Variable[3];

int Size=ArraySize(Variable);

Nach der Ausführung des Codes entspricht der Wert der Variable Size 3.


Statische und dynamische Arrays

Arrays können statisch und dynamisch sein. Wenn die Größe des Arrays in seiner Deklarierung festgelegt ist, ist das Array statisch:

double Variable[3];

Die Größe eines statischen Arrays kann im Programm nicht verändert werden. Beim Deklarieren eines Arrays kann seine Größe direkt als Zahl festgelegt werden (wie im oben aufgeführten Beispiel) oder mithilfe einer vordefinierten Konstante:

#define SIZE 3

double Variable[SIZE];

Ein Array, dessen Größe nicht in seiner Deklarierung festgelegt ist, ist dynamisch:

double Variable[];

Bevor Sie ein solches Array verwenden können, müssen Sie seine Größe festlegen. Die Größe wird durch die Funktion ArrayResize() festgelegt:

ArrayResize(Variable,3);

Die Größe eines dynamischen Arrays lässt sich während der Ausführung des Programms so oft wie nötig verändern, was der wesentliche Unterschied zwischen dynamischen und statischen Arrays ist.

Wenn Sie das Array vollständig leeren müssen, nutzen Sie die Funktion ArrayFree():

ArrayFree(Variable);

Bei der Ausführung dieser Funktion wird die Größe des Arrays auf 0 gesetzt. Die Wirkung dieser Funktion entspricht der folgenden Aktion:

ArrayResize(Variable,0);

Die Befreiung des Arrays kann nützlich sein, wenn das Array für die weitere Ausführung des Programms nicht länger benötigt wird (die Menge des vom Programm beanspruchten Speichers wird reduziert), oder am Beginn der Ausführung einer Funktion (wenn das Array zum Sammeln von Daten genutzt wird).

Mit der Funktion ArrayIsDynamic() können Sie bestimmen, ob ein bestimmtes Array statisch oder dynamisch ist:

bool dynamicArray=ArrayIsDynamic(Variable);

Wenn das Array dynamisch ist, enthält die Variable dynamicArray den Wert true, wenn es statisch ist, enthält sie den Wert false.


Initialisierung des Arrays

Manchmal ist es erforderlich, ein Array gleich nach seiner Deklarierung mit Werten zu füllen. Nehmen wir an, Sie möchten mehrere Buttons des gleichen Typen erstellen und sie in einer Reihe anordnen, wobei jeder Button seinen eigenen Text hat. Hier kommen die großen Vorteile von Arrays ins Spiel. Sie müssen weder den Code für jeden Button wiederholen (es kann Dutzende davon geben) noch dieselbe Funktion wiederholt aufrufen. Stattdessen können Sie die erforderliche Anzahl von Buttons erstellen, indem Sie das Array nach nur einmaligem Schreiben des Aufrufcodes wiederholt in einer Schleife ausführen.

Wir deklarieren einfachen ein Array und weisen seinen Elementen sofort Werte zu:

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

Bei dieser Art der Deklarierung ist das Array statisch, obwohl seine Größe nicht festgelegt wird. Das liegt daran, dass die Menge seiner Elemente durch die Liste der Werte (in geschweiften Klammern) definiert wird.

Es ist kein Fehler, die Menge der Array-Elemente festzulegen:

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

Doch es ist besser, es nicht zu tun – im Zuge der nachfolgenden Verbesserungen des Programms müssen Sie die Liste der Array-Werte möglicherweise verändern und mehr oder weniger Elemente verwenden. Um die Größe des Arrays in den Codeteilen, in denen es verwendet wird, zu bestimmen, ist es empfehlenswert, die Funktion ArraySize() anstatt eines bestimmten numerischen Werts zu nutzen. Auf diese Weise können Sie nur die Liste der Werte verändern, ohne in den Hauptcode einzugreifen. Eine angemessenere Herangehensweise ist es, die Variable für die Array-Größe zu deklarieren und sie beim Initialisieren des Programms dem durch die Funktion ArraySize() erhaltenen Wert zuzuweisen.

Wenn sich ein statisches Array nicht durch die Liste der Werte initialisieren lässt, wäre es besser, eine Konstante zum Festlegen der Größe des Arrays zu nutzen. Im Allgemeinen befolgen wir das Prinzip der Reduzierung der Menge an Code, der modifiziert werden müsste, falls sich weitere Verbesserungen des Programms als nötig erweisen. Wenn Sie alle Array-Elemente mit den gleichen Werten befüllen müssen, nutzen Sie die Funktion ArrayInitialize():

ArrayInitialize(Variable,1);

Nach der Ausführung des obigen Codes haben alle Elemente des Arrays Var den Wert 1. Wenn nur einigen der Array-Elemente die gleichen Werte zugewiesen werden müssen, nutzen wir die Funktion ArrayFill():

double Variable[4];

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

Nach der Ausführung des Codes haben die Elemente 0 und 1 den Wert 1, während die Elemente 2 und 3 den Wert 2 haben.


Iterationsschleife des Arrays

Arrays werden für gewöhnlich mithilfe einer for-Schleife verarbeitet. Bei der Nutzung eines statischen Arrays iterieren wir je nach der vorliegenden Aufgabe vorwärts oder rückwärts über das Array:

//--- 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
}

Wenn das Array dynamisch ist, müssen Sie unmittelbar vor der Schleife eine Variable für die Größe des Arrays deklarieren, die Größe des Arrays abrufen und die Schleife ausführen:

int Size=ArraySize(Var);

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

Wenn Sie bei der Prüfung der Bedingung in einer for-Schleife die Funktion ArraySize() aufrufen, anstatt eine Variable für die Größe des Arrays zu verwenden, kann sich die Durchlaufzeit der Schleife wesentlich verlängern, da die Funktion ArraySize() bei jeder Iteration der Schleife aufgerufen wird. Der Aufruf einer Funktion dauert länger als der Aufruf einer Variable:

for(int i=0; i<ArraySize(Variable); i++){
   // some manipulations on the Variable[i] element
}
Die Nutzung des oben aufgeführten Codes wird nicht empfohlen.

Wenn der Programmalgorithmus eine Rückwärtsiteration in der Schleife erlaubt, kommen Sie auch ohne Variable für die Array-Größe aus:

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

In diesem Fall wird die Funktion ArraySize() nur einmalig am Anfang der Schleife aufgerufen und die Schleife läuft schnell durch.


Mehrdimensionale Arrays

Bisher haben wir nur eindimensionale Arrays betrachtet. Sie lassen sich folgendermaßen darstellen:

Eindimensionales Array

Arrays können auch mehrdimensional sein. Während ein eindimensionales Array nur einen Wert pro Index enthält, kann ein mehrdimensionales Array mehr als einen Wert pro Index haben. Mehrdimensionale Arrays werden folgendermaßen deklariert:

double Variable[10][3];

Das bedeutet, dass die erste Dimension des Arrays zehn und die zweite drei Elemente hat. Das lässt sich folgendermaßen illustrieren:

Mehrdimensionales Array

Zum einfacheren Verständnis kann ein zweidimensionales Array als Ebene dargestellt werden. Die Größe der ersten Dimension bestimmt die Länge, die der zweiten die Breite und der Wert des Elements definiert die Parameter eines bestimmten Punktes auf der Ebene, z. B. die Höhe über dem Meeresspiegel.

Ein Array kann auch dreidimensional sein:

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

Dieses Array lässt sich als Würfel oder Parallelogramm darstellen: Die erste Dimension bestimmt die Länge, die zweite die Breite, die dritte die Höhe und der Wert des Elements definiert die Parameter eines bestimmten Punkts im Raum.

Die maximal zulässige Anzahl an Array-Dimensionen in MQL5 ist 4.

Ein mehrdimensionales Array kann nur in der ersten Dimension statisch oder dynamisch sein. Alle weiteren Dimensionen sind statisch. Also können Sie mit der Funktion ArrayResize() nur die Größe der ersten Dimension ändern. Die Größen der anderen Dimensionen müssen beim Deklarieren des Arrays bestimmt werden:

double Variable[][3][3];

Bei der Bestimmung der Größe eines mehrdimensionalen Arrays mithilfe der Funktion ArraySize() müssen wir eines beachten: Bei der Veränderung der Größe des Arrays mithilfe der Funktion ArrayResize() ist der zweite Parameter der Funktion die Größe der ersten Dimension des Arrays. Allerdings gibt die Funktion ArraySize() die Gesamtmenge der Elemente anstatt der Größe der ersten Dimension aus:

double Variable[][3][3]; 

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

Nach der Ausführung des oben aufgeführten Codes entspricht der Wert der Variable Size 27. Denken Sie an diese Besonderheit, wenn Sie in einer Schleife über mehrdimensionale Arrays iterieren, falls Sie die Größe der ersten Dimension abrufen möchten:

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;
      }   
   }   
}

Wie bereits erwähnt, wird empfohlen, das Prinzip der Reduzierung der Menge an Code, der modifiziert werden müsste, falls sich weitere Verbesserungen des Programms als nötig erweisen, zu befolgen. Im oben aufgeführten Codebeispiel haben wir die Zahl 9 genutzt, die allerdings auch berechnet werden kann. Zu diesem Zweck können wir die Funktion ArrayRange() nutzen, die die Menge der Elemente in einer bestimmten Dimension des Arrays ausgibt. Wenn die Menge der Dimensionen des Arrays bekannt ist, können wir eine simple Berechnung durchführen:

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

Wir können sie universeller gestalten:

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
}

An dieser Stelle wünschen Sie sich möglicherweise, eine Funktion für eine solche Berechnung zu erstellen. Leider ist das nicht möglich, da ein zufälliges Array nicht an eine Funktion übergeben werden kann. Beim Deklarieren eines Funktionsarguments müssen Sie die Menge der Elemente in allen Array-Dimensionen außer der ersten angeben, was eine solche Funktion sinnlos macht. Diese Berechnungen lassen sich bei der Programminitialisierung besser und einfacher durchführen. Beim Deklarieren eines Arrays ist es empfehlenswert, Konstanten zu verwenden, die die Größen der Dimensionen bestimmen:

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

Die Initialisierung mehrdimensionaler Arrays mithilfe der Liste von Werten ähnelt der Initialisierung eindimensionaler Arrays. Doch da ein mehrdimensionales Array sozusagen aus mehreren anderen Arrays besteht, muss jedes dieser Arrays durch geschweifte Klammern abgetrennt werden.

Nehmen wir das folgende Array an:

double Variable[3][3];

Dieses Array besteht aus drei Arrays, von denen jedes aus drei Elementen besteht:

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

Ein dreidimensionales Array wird auf die gleiche Weise verarbeitet. Um das Verständnis der Struktur des Arrays zu vereinfachen, lässt sich der Code in mehrere Zeilen aufteilen:

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}
   }
};

Die Initialisierung eines mehrdimensionalen Arrays mithilfe der Funktion ArrayInitialize() wird auf die gleiche Weise ausgeführt wie die Initialisierung eines eindimensionalen Arrays:

ArrayInitialize(Variable,1);

Nach der Ausführung des Codes haben alle Elemente des Arrays den Wert 1. Das Gleiche gilt auch für die Funktion ArrayFill():

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

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

Nach der Ausführung dieses Codes haben alle Elemente, die mit dem ersten Element der ersten Dimension zusammenhängen, den Wert 1. Elemente, die mit dem zweiten Element zusammenhängen, haben den Wert 10 und Elemente, die mit dem dritten Element zusammenhängen, den Wert 100.


Übergabe eines Arrays an eine Funktion

Im Gegensatz zu Variablen können Arrays nur per Verweis an eine Funktion übergeben werden. Das bedeutet, dass die Funktion keine eigene Instanz des Arrays erstellt, sondern direkt mit dem an sie übergebenen Array arbeitet. Somit betreffen alle Änderungen, die die Funktion am Array vornimmt, das ursprüngliche Array.

Wenn eine Variable auf die herkömmliche Art (nach Wert) an eine Funktion übergeben wird, kann der Wert der übergebenen Variable nicht durch die Funktion verändert werden:

int x=1;
Func(x);

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

Nach der Ausführung der Funktion Func() verbleibt der Wert x gleich 1.

Wenn eine Variable per Verweis übergeben wird (mit & gekennzeichnet), kann die Funktion den Wert dieser Variable verändern:

int x=1;
Func(x);

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

Nach der Ausführung der Funktion Func() wird der Wert x zu 2.

Beim Übergeben eines Arrays an eine Funktion müssen Sie angeben, dass das Argument per Verweis übergeben wird und ein Array repräsentiert (in Klammern):

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

Beim Übergeben mehrdimensionaler Arrays an eine Funktion müssen die Größen der Dimensionen (mit Ausnahme der ersten) angegeben werden:

double var[][3][3];

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

In diesem Fall ist es eher empfehlenswert, Konstanten zu verwenden:

#define SIZE1 3
#define SIZE2 3

double Var[][SIZE1][SIZE2];

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


Speichern und Laden von Arrays aus einer Datei

Beim Speichern und Laden eines Arrays aus einer Datei müssen Sie immer den Unterschied zwischen den Werten der Größe der ersten Dimension des Arrays und der Gesamtmenge der Elemente des Arrays berücksichtigen. Um ein Array zu speichern, schreiben wir zuerst die Größe des Arrays (Gesamtmenge der Elemente laut Funktion ArraySize()) und anschließend das ganze Array in die Datei:

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
  }

Als Ergebnis erhalten wir eine ziemlich allgemeingültige Funktion zum Speichern von eindimensionalen Arrays.

Um ein Array aus einer Datei zu laden, müssen wir zuerst die Größe des Arrays lesen, sie verändern und zum Schluss das Array lesen:

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
  }

Beim Laden eines mehrdimensionalen Arrays aus einer Datei müssen Sie die Größe der ersten Dimension berechnen. Nehmen wir beispielsweise an, wir lesen ein dreidimensionales Array:

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
  }

Es ist gut möglich, dass die Datei ein Array mit den Größen 2 und 3 enthält, während wir versuchen, es als Array mit den Größen 3 und 3 zu lesen. Sie können die Entsprechung der Größen prüfen, indem Sie die berechnete Größe der ersten Dimension mit der Menge der Elemente multiplizieren. Wenn der resultierende Wert gleich der Gesamtmenge der Elemente des Arrays ist, können wir von einer Entsprechung sprechen.

Allerdings wird das Array Var[2][3] dem Array Var[3][2] entsprechen. Wenn Sie auch solche Fälle berücksichtigen müssen, sollten Sie mehr Informationen über das mehrdimensionale Array speichern. Beispielsweise können Sie erst die Menge der Elemente des Arrays speichern, dann die Menge der Dimensionen des Arrays, gefolgt von den Größen jeder der Dimensionen und dem Array selbst.

Die letzte oben dargelegte Funktion ist nicht allgemeingültig und ist nur für das Lesen dreidimensionaler Arrays geeignet, bei denen die Größe der zweiten Dimension gleich SIZE1 und die Größe der dritten Dimension gleich SIZE2 ist. Da es keine Möglichkeit gibt, die Größen aller Array-Dimensionen mit Ausnahme der ersten dynamisch zu verändern, ist das kein Problem – wir erstellen Funktionen für Arrays, die im Programm verwendet werden müssen.

Eine Allgemeingültigkeit ist in diesem Fall nicht erforderlich: Die Größen der Array-Dimensionen (mit Ausnahme der ersten) werden nicht durch die externen Parameter des Programms kontrolliert. Wenn Sie allerdings eine Möglichkeit zum Kontrollieren der Größen anderer Dimensionen implementieren müssen, können Sie dies mithilfe eines mehrdimensionalen Arrays mit bekanntermaßen größerer Größe und zusätzlicher Variablen oder durch die Anwendung von Techniken der objektorientierten Programmierung (OOP) bewerkstelligen. Im weiteren Verlauf dieses Beitrags gehen wir näher auf den zweiten Ansatz ein.


Verwendung dynamischer Arrays

Dynamische Arrays werden verwendet, wenn Sie die Array-Größe nicht vorab kennen. Wenn die Größe des Arrays von den in den Programmeinstellungen festgelegten Parametern abhängt, ist es kein Problem, dynamische Arrays zu verwenden: Die Array-Größe wird nur einmalig während der Initialisierung des Programms verändert.

Ein Array kann für die dynamische Sammlung bestimmter Informationen genutzt werden, z. B. in Bezug auf Pending Orders. Ihre Menge kann variieren, d. h. die erforderliche Größe ist nicht vorab bekannt. In diesem Fall wäre es am einfachsten, die Größe des Arrays zu 0 zu ändern und dann die Order durchzugehen und die Größe des Arrays beim Durchgang durch jede Order um ein Element zu erhöhen. Dieser Ansatz funktioniert, aber nur sehr langsam.

Vor dem Durchlaufen der Order kann die Größe des Arrays gemäß der Menge der Order nur einmal verändert werden. Dies erfordert eine weitere Variable für den Index des letzten aktiven Elements des Arrays (oder die Menge der tatsächlich aktiven Array-Elemente anstelle des Index). Diese Methode ist geeignet, wenn Sie die maximale Größe des Arrays bereits kennen. Falls die maximale Größe des Arrays nicht bekannt ist, können wir die Arbeit damit beschleunigen, indem wir die Größe des Arrays mithilfe von Datenblöcken verändern, wie es in der folgenden Klasse verdeutlicht wird:

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);
     }
  };

Sie finden diese Klasse in der angehängten Datei CDynamicArray.mqh. Die Datei muss im Unterordner MQL5/Include des Datenordners des Terminals abgelegt werden.

Lassen Sie uns nun die Ausführung des Codes in beiden Situationen bewerten und vergleichen – wenn die Größe des Arrays sequentiell um 1 erhöht wird und wenn sie durch Datenblöcke erhöht wird:

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

  }

Den Test in Form eines Scripts finden Sie in der angehängten Datei sTest_Speed.mq5. Die Datei muss im Unterordner MQL5/Scripts des Datenordners des Terminals abgelegt werden.

Die Ausführung der ersten Option dauerte ein paar Sekunden, während die zweite Option fast augenblicklich abgeschlossen war.


Reihenfolge der Indexierung eines Arrays

Für gewöhnlich werden bei der Änderung der Größe eines Arrays neue Elemente zum Ende des Arrays hinzugefügt:

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

Nach der Ausführung dieses Codes müssen die im Array beinhalteten Werte 1, 2 und 3 sein.

Elemente in Arrays können auch in umgekehrter Reihenfolge indexiert werden. Die Reihenfolge der Indexierung wird durch die Funktion ArraySetAsSeries() festgelegt:

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

Beim Verändern der Größe eines in umgekehrter Reihenfolge indexierten Arrays werden neue Elemente am Anfang des Arrays hinzugefügt:

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

Nach der Ausführung dieses Codes müssen die im Array beinhalteten Werte 3, 2 und 1 sein.

Es stellt sich heraus, dass das neue Element in beiden Fällen auf der gleichen Seite des Arrays hinzugefügt wird. Der einzige Unterschied ist die Reihenfolge der Indexierung. Diese Funktion kann nicht verwendet werden, um Elemente zum Anfang des Arrays hinzuzufügen, dessen Elemente in normaler Reihenfolge indexiert sind. Um ein Element zum Ende des normal indexierten Arrays hinzuzufügen, müssen Sie nur die Größe des Arrays erhöhen und dem letzten Element einen Wert zuweisen.

Um ein Element zum Anfang des Arrays hinzuzufügen, müssen Sie die Größe des Arrays erhöhen, alle Werte verschieben und dem Nullelement einen neuen Wert zuweisen. Bei in umgekehrter Reihenfolge indexierten Arrays kann ein neues Element einfach zum Anfang des Arrays hinzugefügt werden. Doch wenn Sie ein neues Element zum Ende des Arrays hinzufügen müssen, müssen Sie zuerst die Größe des Arrays erhöhen und nach dem Verschieben aller Werte zum Anfang dem letzten Element einen neuen Wert zuweisen. Manipulationen der Indexierungsreihenfolge lösen dieses Problem nicht.

Die Indexierungsreihenfolge des Arrays kann mithilfe der Funktion ArrayIsSeries() bestimmt werden:

bool series=ArrayIsSeries(ar);

Wenn das Array in umgekehrter Reihenfolge indexiert wird, gibt die Funktion true aus.

In umgekehrter Reihenfolge indexierte Arrays werden vor allem in Expert Advisors verwendet. Bei der Entwicklung von EAs ist es oft bequemer, Balken von rechts nach links zu zählen und somit Preisdaten und Indikatorpuffer in Arrays mit umgekehrter Indexierung zu kopieren.


Kopieren von Arrays

Die einfachste Art des Kopierens ist es, in einer Schleife über ein Array zu iterieren und Element für Element aus einem Array in das andere zu kopieren. MQL5 bietet allerdings eine spezielle Funktion zum Kopieren von Arrays: ArrayCopy():

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

ArrayCopy(ar2,ar1);

Nach der Ausführung des obigen Codes besteht das Array ar2 aus drei Elementen mit den gleichen Werten wie im Array ar1: 1, 2, 3.

Wenn die zu kopierende Anzahl an Elementen nicht in das Array passt, in das Sie sie kopieren möchten, wird die Größe des Arrays automatisch erhöht (das Array muss dynamisch sein). Wenn das Array größer ist als die Anzahl der zu kopierenden Elemente, bleibt seine Größe unverändert.

Die Funktion ArrayCopy() ermöglicht es Ihnen auch, nur einen Teil eines Arrays zu kopieren. Mithilfe der optionalen Parameter der Funktion können Sie das erste zu kopierende Element, den Index des ersten kopierten Elements im neuen Array und die Anzahl der zu kopierenden Elemente festlegen.

Zusätzlich zum Kopieren von Elementen von einem Array ins andere kann die Funktion ArrayCopy() auch zum Kopieren von Elementen innerhalb eines Arrays verwendet werden:

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

Wir kopieren Daten ab dem Element mit dem Index 2 und fügen sie ab dem Index 1 ein. Nach der Ausführung dieses Codes enthält das Array die folgenden Werte: 1, 3, 4, 5, 5.

Die Funktion ArrayCopy() ermöglicht es Ihnen auch, Daten nach rechts zu verschieben:

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

Wir nehmen Daten ab dem Element mit dem Index 1 und ordnen sie ab dem Index 2 an. Nach der Ausführung dieses Codes enthält das Array die folgenden Werte: 1, 2, 2, 3, 4.

Die Funktion ArrayCopy() lässt sich auch auf mehrdimensionale Arrays anwenden. Dabei verhält sie sich, als wäre das Array eindimensional und seine Elemente in Serie angeordnet:

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

Nach der Ausführung dieses Codes sieht das Array so aus: {1, 2}, {5, 6}, {5, 6}.


Sortieren eines Arrays

Arrays können mithilfe der Funktion ArraySort() sortiert werden:

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

Nach der Ausführung des obigen Codes, werden die Werte des Arrays in der folgenden Reihenfolge angeordnet: 1, 2, 3, 4, 5.

Die Funktion ArraySort() kann nicht auf mehrdimensionale Arrays angewendet werden. Informationen zum Sortieren von mehrdimensionalen Arrays und Datenstrukturen finden Sie im Beitrag "Elektronische Tabellen in MQL5".


Um eine binäre Suche durchzuführen, nutzen wir die Funktion ArrayBsearch(). Diese Funktion funktioniert nur mit sortierten Arrays ordnungsgemäß. Die binäre Suche heißt deshalb so, weil der Algorithmus ein Array kontinuierlich in zwei Stücke aufteilt. Der Algorithmus vergleicht zuerst den Zielwert mit dem Wert des mittleren Elements des Arrays und bestimmt somit die Hälfte, die das Zielelement enthält – das linke oder das rechte Sub-Array. Anschließend vergleicht er den Zielwert mit dem Wert des mittleren Elements der Sub-Arrays usw.

Die Funktion ArrayBsearch() gibt den Index des Elements mit dem Zielwert aus:

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

int index=ArrayBsearch(ar,3);

Nach der Ausführung dieses Codes hat die Variable des Index den Wert 2.

Wenn der Zielwert nicht im Array gefunden werden kann, gibt die Funktion den Index des Elements mit dem nächstkleineren Wert aus. Dank dieser Eigenschaft kann die Funktion verwendet werden, um Balken nach Zeit zu suchen. Gibt es keinen Balken mit der angegebenen Zeit, muss ein Balken mit geringerer Zeit in Berechnungen genutzt werden.

Wenn sich der Zielwert nicht im Array befindet und außerhalb des Bereichs der Array-Werte liegt, gibt die Funktion 0 (wenn der Zielwert kleiner als der Mindestwert ist) oder den letzten Index (wenn der Zielwert größer als der Höchstwert ist) aus.

Es gibt nur eine Methode, die die Suche in einem unsortierten Array möglich macht: die Iteration über ein Array:

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);
}

Im oben aufgeführten Beispiel gibt die Funktion den Index des Elements mit dem Zielwert aus. Wenn sich der Zielwert nicht im Array befindet, gibt die Funktion -1 aus.


Finden des Maximums und Minimums

Maxima und Minima im Array können mithilfe der Funktionen ArrayMaximum() und ArrayMinimum() gefunden werden, die den Index des Elements mit dem Maximum bzw. Minimum ausgeben:

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];

Nach der Ausführung dieses Codes ist die Variable MaxIndex gleich 6, die Variable MinIndex gleich 2, MaxValue hat den Wert 5 und MinValue 1.

Mit den Funktionen ArrayMaximum() und ArrayMinimum() können Sie den Suchbereich eingrenzen, indem Sie den Index des ersten Elements im Suchbereich und die Anzahl der Elemente im Suchbereich angeben:

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

In diesem Fall hat MaxIndex den Wert 6, MinIndex 5. Bitte beachten Sie, dass der angegebene Bereich zwei Positionen mit dem Mindestwert 4 hat: Position 5 und Position 7. In einer solchen Situation gibt die Funktion den Index des Elements aus, das näher am Anfang des Arrays liegt. Diese Funktionen arbeiten mit Arrays, die in umgekehrter Reihenfolge indexiert sind, auf die gleiche Weise: Sie geben den kleinsten Index aus.

Nun haben wir alle in MQL5 zur Verfügung stehenden Standardfunktionen für die Arbeit mit Arrays betrachtet.


Erstellen mehrdimensionaler Arrays mit OOP

Ein Satz von Klassen für die Erstellung mehrdimensionaler Arrays beinhaltet drei Klassen: eine Basisklasse und zwei Unterklassen. Abhängig von der Unterklasse, die während der Erstellung eines Objekts gewählt wurde, kann das Objekt ein Array von double-Variablen oder ein Array von Objekten darstellen. Jedes Element des Arrays von Objekten kann ein weiteres Array von Objekten oder Variablen darstellen.

Die Basisklasse und die Unterklassen beinhalten praktisch keine Funktionen mit Ausnahme des Destruktors in der Basisklasse und Konstruktoren in den Unterklassen. Der Destruktor in der Basisklasse dient der Löschung aller Objekte bei Abschluss des Programms oder der Funktion. Die Konstruktoren in den Unterklassen werden nur dazu genutzt, Arrays in Übereinstimmung mit der in den Parametern des Konstruktors festgelegten Größe zu skalieren: zum Skalieren eines Arrays von Objekten in einer Klasse und eines Arrays von Variablen in der anderen. Nachfolgend sehen Sie den Code für die Umsetzung dieser Klassen:

//+------------------------------------------------------------------+
//|   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);
     }
  };

Sie finden diese Klassen in der angehängten Datei CMultiDimArray.mqh. Die Datei muss im Unterordner MQL5/Include des Datenordners des Terminals abgelegt werden.

Lassen Sie uns nun diese Klasse verwenden, um ein ähnliches eindimensionales Array zu erstellen:

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
  }

Dieses Beispiel in Form eines Scripts finden Sie in der angehängten Datei sTest_1_Arr.mq5. Die Datei muss im Unterordner MQL5/Scripts des Datenordners des Terminals abgelegt werden.

Versuchen wir nun, ein zweidimensionales Array zu erstellen. Jedes Element der ersten Dimension enthält eine andere Menge von Elementen der zweiten Dimension – eins in der ersten, zwei in der zweiten usw.:

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

Dieses Beispiel in Form eines Scripts finden Sie in der angehängten Datei sTest_2_Dim.mq5. Die Datei muss im Unterordner MQL5/Scripts des Datenordners des Terminals abgelegt werden.

Die resultierenden Arrays sind gewissermaßen statisch, da die Klassen keine Methoden zum Ändern von Array-Größen enthalten. Doch da sich die Arrays D[] und V[] im öffentlichen Abschnitt der Klasse befinden, können sie beliebig manipuliert werden. Und Sie können das Array V[] problemlos skalieren. Bei der Skalierung von D[]-Arrays und der Verringerung ihrer Größe müssen Sie zuerst die Objekte löschen, auf die zu löschende Objekte zeigen oder, beim Erhöhen der Array-Größe, Objekte hineinladen.

Wahrscheinlich gibt es noch weitere Arten der Umsetzung mehrdimensionaler Arrays mithilfe von OOP oder Datenstrukturen.


Fazit

Dieser Beitrag hat alle in MQL5 zur Verfügung stehenden Standardfunktionen für die Arbeit mit Arrays behandelt. Wir haben die Besonderheiten und einige der wichtigsten Techniken für den Umgang mit Arrays betrachtet. MQL5 bietet insgesamt 15 Funktionen, von denen einige von höchster Bedeutung sind, während andere komplett ungenutzt bleiben, außer in Fällen, in denen Sie eine ungewöhnliche Aufgabe lösen müssen. Die Funktionen können folgendermaßen nach Wichtigkeit und Häufigkeit der Nutzung angeordnet werden:

  1. ArraySize() und ArrayResize() sind die essentiellen Funktionen.

  2. ArrayMaximum(), ArrayMinimum(), ArrayCopy(), ArrayInitialize(), ArrayFill() und ArrayFree() sind Funktionen, die die Arbeit mit Arrays wesentlich erleichtern.

  3. ArraySort() ist eine wichtige und praktische Funktion, die aufgrund ihrer geringen Funktionalität allerdings selten genutzt wird.

  4. ArrayBsearch() ist eine Funktion, die selten genutzt wird, in einigen Ausnahmefällen aber sehr wichtig sein kann.

  5. ArraySetAsSeries(), ArrayRange(), ArrayGetAsSeries(), ArrayIsDynamic() und ArrayIsSeries() sind Funktionen, die sehr selten oder fast niemals verwendet werden.

Die Verwendung dynamischer Arrays als eine der in diesem Beitrag beschriebenen Programmiertechniken sollte besonders beachtet werden, da sie sich wesentlich auf die Performance des Programms auswirkt und sie im Grunde genommen bestimmt.

Übersetzt aus dem Russischen von MetaQuotes Ltd.
Originalartikel: https://www.mql5.com/ru/articles/567

Beigefügte Dateien |
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)
Letzte Kommentare | Zur Diskussion im Händlerforum (1)
Tobias Johannes Zimmer
Tobias Johannes Zimmer | 16 Apr. 2021 in 16:51
Habe von Dir schon einige gute Artikel gelesen. Dieser hier war keine Ausnahme.
Wie sicher ist der Kauf von MQL5 Market-Produkten? Wie sicher ist der Kauf von MQL5 Market-Produkten?
Bei dem Verkauf von auf MetaTrader 5 basierenden Trading-Apps räumen wir der Sicherheit unserer Kunden eine unvergleichliche Priorität ein. Da wir alle damit in Verbindung stehenden Risiken minimiert haben, ist es Ihnen möglich, sich auf wichtigere Dinge zu konzentrieren - wie beispielsweise die Suche nach einem Handelsroboter, der Ihren Wünschen entspricht.
Wie man eine gute Beschreibung für ein Market-Produkt verfasst Wie man eine gute Beschreibung für ein Market-Produkt verfasst
MQL5 Market bietet zwar so manche Produkte an, allerdings lässt bei einigen davon die Beschreibung ein wenig zu wünschen übrig. Viele Texte bedürfen zweifellos verschiedener Verbesserungen, da eine ganze Reihe von Händlern nicht in der Lage ist, diese vollends zu verstehen. Dieser Artikel soll Ihnen dabei helfen, Ihr Produkt in einem möglichst positiven Licht dastehen zu lassen. Wir helfen Ihnen dabei, eine Produktbeschreibung zu verfassen, die ins Auge sticht und die Ihren Kunden leicht verständliche Informationen liefert.
Universeller Expert Advisor: Benutzerstrategien und Hilfsklassen (Teil 3) Universeller Expert Advisor: Benutzerstrategien und Hilfsklassen (Teil 3)
In diesem Artikel werden wir mit der Analyse der Algorithmen der Klasse CStrategy Trading Engine fortfahren. Der dritte Teil der Serie enthält die detaillierte Analyse von Beispielen, wie bestimmte Handelsstrategien mit diesem Ansatz entwickelt werden können. Ein besonderes Augenmerk wird auf die Hilfsalgorithmen gelegt — Ein Expert Advisor Protokollierungs-System (logging) und der Datenzugriff über gewöhnliche Indexe (Close[1], Open[0] etc.)
Wie man Handelssignale abonniert Wie man Handelssignale abonniert
Der Signal-Service zeigt Ihnen, wie sie mit MetaTrader 4 und MetaTrader 5 sozial traden. Dieser Service ist in die Handelsplattform fest integriert und erlaubt es jedermann, mit Leichtigkeit die Aktionen professioneller Trader zu kopieren. Wählen Sie einen der vielen tausend Signalanbieter, schließen Sie mit nur wenigen Klicks ein Abo ab und die Trades dieses Anbieters werden auf Ihr Konto kopiert.