English Русский 中文 Español Deutsch 日本語 Português 한국어 Français Türkçe
Fondamenti di programmazione MQL5: Arrays

Fondamenti di programmazione MQL5: Arrays

MetaTrader 5Esempi | 11 gennaio 2022, 16:16
572 0
Dmitry Fedoseev
Dmitry Fedoseev

Introduzione

Gli array sono parte integrante di quasi tutti i linguaggi di programmazione insieme a variabili e funzioni. Molti programmatori alle prime armi hanno spesso paura degli array. Sembra strano ma è vero! Ti posso assicurare che non fanno affatto paura. In effetti, gli array sono simili alle variabili regolari. Senza entrare nei dettagli sulle peculiarità della notazione, non c'è una grande differenza tra scrivere un'espressione usando semplici variabili

Variable0=1;
Variable1=2;

Variable2=Variable0+Variable1;

o usando gli array:

double Variable[3];

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

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

Come puoi vedere, la differenza non è così grande tranne per il fatto che quando usiamo gli array i nomi delle variabili contengono parentesi. C'è un'altra differenza più significativa: quando si dichiarano le variabili, è necessario specificare il nome di ciascuna variabile, mentre quando si dichiara un array, è necessario scriverne il nome solo una volta e specificare il numero di variabili tra parentesi (numero di elementi dell'array) . I vantaggi dell'utilizzo di array rispetto alle variabili diventano ancora più evidenti quando si gestiscono le sfide di un gran numero di attività di programmazione nella vita reale.

È possibile che il motivo per cui gli array sono visti come qualcosa di complicato sia in qualche modo correlato all'uso di "[" e "]"? Questi simboli sono usati raramente in altri luoghi se non nella programmazione quando si lavora con gli array, quindi la loro posizione sulla tastiera può svanire dalla memoria e causare disagio. Mentre in effetti, puoi facilmente ricordare dove sono: questi due tasti si trovano accanto a "Invio" in un ordine logico: la parentesi di apertura è seguita dalla parentesi di chiusura.


Definizione e proprietà generali degli array

Quindi, un array è un insieme numerato di variabili con lo stesso nome. Le proprietà generali degli array includono il nome dell'array, il tipo di variabile (int, double, ecc.) e la dimensione dell'array. Gli elementi dell'array sono indicizzati da zero. Parlando di elementi di array, è sempre meglio usare la parola "indice" al posto di "numero" in modo da suggerire di iniziare a contare gli elementi di array da zero (mentre la numerazione di solito parte da uno). Con gli elementi indicizzati in questo modo, l'indice dell'ultimo elemento è uno in meno del numero di elementi dell'array.

Se l'array è dichiarato come segue:

double Variable[3];

ha i seguenti elementi: Variabile[0], Variabile[1] e Variabile[2].

A prima vista, tale mancanza di corrispondenza tra il numero di elementi e l'indice dell'ultimo elemento può sembrare scomoda. In effetti, offre vantaggi significativi rispetto ai linguaggi di programmazione in cui gli elementi dell'array sono indicizzati da 1 o la dimensione dell'array è definita dall'indice del suo ultimo elemento piuttosto che dal numero effettivo di elementi nell'array.

Per determinare la dimensione dell'array in MQL5, utilizziamo la ArraySize():

double Variable[3];

int Size=ArraySize(Variable);

Dopo aver eseguito il codice, il valore della variabile Size sarà pari a 3.


Array statici e dinamici

Gli array possono essere statici e dinamici. Se la dimensione dell'array è specificata nella sua dichiarazione, l'array è statico:

double Variable[3];

La dimensione di un array statico non può essere modificata nel programma. Quando si dichiara un array, la sua dimensione può essere specificata direttamente come un numero (come nell'esempio sopra) o utilizzando una costante di sostituzione (#define):

#define SIZE 3

double Variable[SIZE];

Un array la cui dimensione non è specificata nella sua dichiarazione è dinamico:

double Variable[];

Prima di poter utilizzare un tale array, è necessario impostarne le dimensioni. La dimensione è impostata dalla funzione functionArrayResize():

ArrayResize(Variable,3);

La dimensione di un array dinamico può essere modificata durante l'esecuzione del programma tutte le volte necessarie, che è la differenza fondamentale tra array dinamici e statici.

Se devi liberare completamente l'array, usa la funzione ArrayFree():

ArrayFree(Variable);

Quando si esegue questa funzione, la dimensione dell'array è impostata su 0. L'effetto prodotto da questa funzione è simile alla seguente azione:

ArrayResize(Variable,0);

Liberare l'array può essere utile quando l'array non è più necessario per ulteriori operazioni del programma (questo riduce la quantità di memoria utilizzata dal programma) o all'inizio dell'esecuzione di una funzione (se l'array viene utilizzato per la raccolta dati).

ArrayIsDynamic() funzione consente di determinare se un determinato array è statico o dinamico:

bool dynamicArray=ArrayIsDynamic(Variable);

La variabile dynamicArray conterrà un valore vero se l'array è dinamico o un valore falso se l'array è statico.


all'inizializzazione

A volte è necessario riempire un array con valori ​subito dopo la sua dichiarazione. Supponiamo di voler creare più pulsanti dello stesso tipo e disporli in fila, con ogni pulsante con il proprio testo. È qui che entrano in gioco i grandi vantaggi degli array. Non è necessario copiare il codice per ogni pulsante (potrebbero essercene decine), né è necessario chiamare ripetutamente la stessa funzione. È possibile creare il numero necessario di pulsanti iterando sull'array in un ciclo dopo aver scritto il codice di chiamata della funzione solo una volta.

Dichiariamo semplicemente un array e assegniamo immediatamente valori ai suoi elementi:

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

Dichiarato in questo modo, l'array sarà ancora statico nonostante il fatto che la sua dimensione non sia specificata. Questo perché il numero dei suoi elementi è definito dall'elenco dei valori (tra parentesi graffe).

Non ci saranno errori se specifichi il numero di elementi dell'array:

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

Ma sarebbe meglio non farlo: nel corso di ulteriori miglioramenti al programma, potrebbe essere necessario modificare l'elenco dei valori dell'array e utilizzare un numero maggiore o minore di elementi. Per determinare la dimensione dell'array nelle parti del codice in cui viene utilizzato, si consiglia di utilizzare la funzione ArraySize() invece di un determinato valore numerico. Questo approccio consente di modificare solo l'elenco dei valori senza interferire con il codice principale. Sarà più appropriato dichiarare la variabile per la dimensione dell'array e assegnarle il valore ottenuto dalla funzione ArraySize() durante l'inizializzazione del programma.

Se un array statico non può essere inizializzato dall'elenco di valori, sarebbe meglio usare una costante per specificare la dimensione dell'array. In generale, seguiamo il principio di ridurre la quantità di codice che richiederebbe di essere modificato qualora fossero necessari ulteriori miglioramenti del programma. Se è necessario riempire tutti gli elementi dell'array con gli stessi valori, utilizzare la funzione ArrayInitialize():

ArrayInitialize(Variable,1);

Dopo aver eseguito il codice sopra, tutti gli elementi dell'array Var avranno il valore 1. Se gli stessi valori devono essere assegnati solo ad alcuni degli elementi dell'array, usiamo la funzione ArrayFill():

double Variable[4];

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

Dopo aver eseguito questo codice, gli elementi 0 e 1 avranno il valore 1, mentre gli elementi 2 e 3 avranno il valore 2.


Ciclo di iterazione dell'array

Gli array vengono solitamente elaborati utilizzando un ciclo for. Quando si utilizza un array statico la cui dimensione è nota in anticipo, si itera sull'array in avanti o all'indietro, a seconda dell'attività da svolgere:

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

Se l'array è dinamico, dovresti dichiarare una variabile per una dimensione dell'array subito prima del ciclo, ottenere la dimensione dell'array ed eseguire un ciclo:

int Size=ArraySize(Var);

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

Se invece di utilizzare una variabile per la dimensione dell'array si chiama la funzione ArraySize() durante il controllo della condizione in un ciclo for, il tempo di ciclo può essere notevolmente prolungato poiché la funzione ArraySize() verrà chiamata ad ogni iterazione del ciclo. Quindi la chiamata alla funzione richiede più tempo rispetto alla chiamata a una variabile:

for(int i=0; i<ArraySize(Variable); i++){
   // some manipulations on the Variable[i] element
}
L'uso del codice sopra non è raccomandato.

Se l'algoritmo del programma consente l'iterazione del ciclo all'indietro, puoi fare a meno di una variabile per la dimensione dell'array:

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

In questo caso, la funzione ArraySize() verrà chiamata solo una volta all'inizio del ciclo e il ciclo verrà eseguito velocemente.


array multidimensionale

Finora abbiamo considerato solo array unidimensionali. Possono essere rappresentati come segue:

Array unidimensionale

Gli array possono essere multidimensionali. Mentre un array unidimensionale contiene un solo valore per indice, un array multidimensionale ha più di un valore per indice. Gli array multidimensionali sono dichiarati come segue:

double Variable[10][3];

Ciò significa che la prima dimensione dell'array ha dieci elementi e la seconda dimensione ha tre elementi. Può essere illustrato come segue:

Array multidimensionale

Per semplificare la comprensione, una matrice bidimensionale può essere rappresentata come un piano. La dimensione della prima dimensione determina la lunghezza, la dimensione della seconda determina la larghezza e il valore dell'elemento definisce i parametri di un dato punto sul piano, ad esempio l'altezza sul livello del mare.

Un array può anche essere tridimensionale:

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

Questa matrice può essere rappresentata come un cubo o un parallelogramma: la prima dimensione determina la lunghezza, la seconda dimensione determina la larghezza, la terza determina l'altezza e il valore dell'elemento definisce i parametri di un dato punto nello spazio.

Il numero massimo di dimensioni dell'array consentito in MQL5 è 4.

Un array multidimensionale può essere statico o dinamico solo nella prima dimensione, mentre tutte le ulteriori dimensioni sono statiche. Pertanto, la funzione ArrayResize() consente di modificare solo la dimensione della prima dimensione. Le dimensioni di altre dimensioni devono essere specificate quando si dichiara un array:

double Variable[][3][3];

Nel determinare la dimensione di un array multidimensionale utilizzando la funzione ArraySize(), dovremmo tenere a mente una cosa: quando si modifica la dimensione dell'array utilizzando la funzione ArrayResize(), il secondo parametro della funzione è la dimensione della prima dimensione del Vettore. Tuttavia, la funzione ArraySize() restituisce il numero totale di elementi anziché la dimensione della prima dimensione:

double Variable[][3][3]; 

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

A seguito dell'esecuzione del codice precedente, la variabile Size sarà uguale a 27. Ricorda questa particolarità quando si itera su array multidimensionali in un ciclo se è necessario ottenere la dimensione della prima dimensione:

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

Come accennato in precedenza, è consigliabile seguire il principio di ridurre la quantità di codice che richiederebbe di essere modificato qualora fossero necessari ulteriori miglioramenti del programma. Nell'esempio di codice sopra, abbiamo usato il numero 9 che però può anche essere calcolato. A questo scopo possiamo utilizzare la funzione ArrayRange() che restituisce il numero di elementi contenuti nella dimensione specificata dell'array. Se il numero di dimensioni dell'array è noto, possiamo fare un semplice calcolo:

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

Possiamo renderlo più universale:

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
}

A questo punto, potresti pensare che sarebbe utile creare una funzione per tale calcolo. Sfortunatamente questo non è possibile poiché un array casuale non può essere passato a una funzione. Quando si dichiara un argomento di funzione, è necessario specificare chiaramente il numero di elementi in tutte le dimensioni dell'array tranne la prima, rendendo così inutile tale funzione. Questi calcoli sono più facili e vengono eseguiti meglio nell'inizializzazione del programma. Quando si dichiara un array, è consigliabile utilizzare costanti che determinano le dimensioni delle dimensioni:

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

L'inizializzazione di array multidimensionali utilizzando l'elenco di valori è simile all'inizializzazione di array unidimensionali. Ma poiché un array multidimensionale è in qualche modo composto da diversi altri array, ognuno di questi array deve essere separato da parentesi graffe.

Supponiamo di avere un array come segue:

double Variable[3][3];

Questo array è composto da tre array di tre elementi ciascuno:

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

Un array tridimensionale viene gestito allo stesso modo. Il codice può essere scomposto in più righe per facilitare la comprensione della struttura dell'array:

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'inizializzazione di un array multidimensionale utilizzando la funzione ArrayInitialize() viene eseguita allo stesso modo dell'inizializzazione di un array unidimensionale:

ArrayInitialize(Variable,1);

Dopo aver eseguito il codice, tutti gli elementi dell'array avranno il valore 1. Lo stesso vale per la funzione ArrayFill():

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

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

A seguito dell'esecuzione di questo codice, tutti gli elementi associati al primo elemento della prima dimensione avranno il valore 1, quelli associati al secondo elemento avranno il valore 10 e quelli associati al terzo elemento - 100.


Passare un array a una funzione

A differenza delle variabili, gli array possono essere passati a una funzione solo per riferimento. Ciò significa che la funzione non crea la propria istanza dell'array e lavora invece direttamente con l'array passato ad essa. Quindi tutte le modifiche apportate dalla funzione all'array influiscono sull'array originale.

Se una variabile viene passata a una funzione in un modo normale (per valore), il valore della variabile passata non può essere modificato dalla funzione:

int x=1;
Func(x);

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

Dopo aver eseguito la funzione Func(), il valore x rimane uguale a 1.

Se una variabile viene passata per riferimento (indicato da &), la funzione può modificare il valore di tale variabile passato ad essa:

int x=1;
Func(x);

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

Dopo aver eseguito la funzione Func(), il valore x diventa uguale a 2.

Quando si passa un array a una funzione, è necessario specificare che l'argomento viene passato per riferimento e rappresenta un array (tra parentesi):

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

Quando si passano array multidimensionali a una funzione, è necessario specificare le dimensioni delle dimensioni (ad eccezione della prima):

double var[][3][3];

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

In questo caso, è più consigliabile utilizzare le costanti:

#define SIZE1 3
#define SIZE2 3

double Var[][SIZE1][SIZE2];

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


Salvataggio e caricamento di array da un file

Quando si salva e si carica un array da un file, si dovrebbe sempre considerare la differenza dei valori della grandezza della prima dimensione dell'array, e il numero totale di elementi dell'array. Per salvare un array, scriviamo prima la dimensione dell'array (numero totale di elementi come determinato dalla funzione ArraySize()) e poi l'intero array nel file:

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
  }

Di conseguenza, otteniamo una funzione abbastanza universale per il salvataggio di array unidimensionali.

Per caricare un array da un file, dobbiamo prima leggere la dimensione dell'array, ridimensionarlo e infine leggere l'array:

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
  }

Quando si carica un array multidimensionale da un file, sarà necessario calcolare la dimensione della prima dimensione. Ad esempio, supponiamo di leggere un array tridimensionale:

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
  }

Può darsi che il file contenga un array 2 per 3 mentre proviamo a leggerlo come un array 3 per 3. Puoi verificare la corrispondenza tra le dimensioni moltiplicando la dimensione calcolata della prima dimensione per il numero di elementi. Se il valore risultante è uguale al numero totale di elementi dell'array, si può parlare di corrispondenza.

Tuttavia, l'array Var[2][3] corrisponderà all'array Var[3][2]. Se hai bisogno di coprire anche questo caso, dovresti salvare più informazioni su un array multidimensionale. Ad esempio, puoi prima salvare il numero di elementi dell'array, quindi il numero di dimensioni dell'array seguite dalle dimensioni di ciascuna delle dimensioni e dell'array stesso.

L'ultima funzione fornita sopra non è universale ed è progettata per leggere solo array tridimensionali in cui la grandezza della seconda dimensione è uguale a SIZE1 e la grandezza della terza dimensione è uguale a SIZE2. Poiché non è possibile modificare dinamicamente le grandezze di tutte le dimensioni dell'array, tranne la prima, questo non è un problema: creeremo funzioni per gli array che devono essere utilizzati nel programma.

L'universalità in questo caso non è necessaria: le grandezze delle dimensioni dell'array (eccetto la prima) non saranno controllate tramite i parametri esterni del programma. Tuttavia, se è necessario implementare la possibilità di controllare grandezze di altre dimensioni, è possibile risolvere questo compito utilizzando un array multidimensionale di grandezze consapevolmente più grandi e variabili aggiuntive o applicando tecniche di programmazione orientata agli oggetti (OOP). Parleremo di più del secondo approccio più avanti in questo articolo.


Array dinamici

Gli array dinamici vengono utilizzati quando non si conosce in anticipo la dimensione dell'array. Se la dimensione dell'array dipende dai parametri impostati nella finestra delle proprietà del programma, l'utilizzo di array dinamici non sarà un problema: la dimensione dell'array verrà modificata solo una volta durante l'inizializzazione del programma.

Un array può essere utilizzato per raccogliere dinamicamente determinate informazioni, ad esempio sugli ordini in sospeso. Il loro numero può variare, cioè la dimensione richiesta non è nota in anticipo. In questo caso, il modo più semplice sarebbe cambiare la dimensione dell'array su 0 prima di passare attraverso gli ordini e aumentare la dimensione dell'array di un elemento mentre si passa attraverso ogni ordine. Funzionerà, ma molto lentamente.

Si può cambiare la dimensione dell'array solo una volta in base al numero di ordini, prima di passare attraverso gli ordini. Ciò richiederà un'altra variabile per l'indice dell'ultimo elemento attivo dell'array (o un numero di elementi dell'array effettivamente attivi, invece dell'indice). Questo metodo è adatto se si conosce già la dimensione massima dell'array. Se la dimensione massima dell'array non è nota, possiamo velocizzare il lavoro con essa ridimensionando l'array usando i blocchi, come mostrato nella classe seguente:

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

È possibile trovare questa classe nel file CDynamicArray.mqh allegato. Il file deve essere posizionato nella cartella MQL5\Include della directory dei dati del terminale.

Valutiamo ora e confrontiamo le prestazioni del codice in entrambe le situazioni: dove la dimensione dell'array viene aumentata in sequenza di 1 e dove viene aumentata utilizzando i blocchi:

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

  }

Questo test sotto forma di script può essere trovato nel file sTest_Speed.mq5 allegato. Il file deve essere posizionato nella cartella MQL5\Scripts della directory dei dati del terminale.

L'esecuzione della prima opzione ha richiesto alcuni secondi, mentre la seconda opzione è stata quasi istantanea.


Ordine di indicizzazione dell'array

Di solito, quando si ridimensiona un array, vengono aggiunti nuovi elementi alla fine dell'array:

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

Dopo l'esecuzione di questo codice, i valori contenuti nell'array dovrebbero essere 1, 2 e 3.

Gli elementi negli array possono anche essere indicizzati in ordine inverso. L'ordine di indicizzazione è impostato dalla funzione ArraySetAsSeries():

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

Quando si modifica la dimensione dell'array indicizzato in ordine inverso, viene aggiunto un nuovo elemento all'inizio dell'array:

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

Dopo l'esecuzione di questo codice, i valori contenuti nell'array dovrebbero essere 3, 2 e 1.

Si scopre che in entrambi i casi il nuovo elemento viene aggiunto allo stesso lato dell'array, l'unica differenza è l'ordine di indicizzazione. Questa funzione non può essere utilizzata per aggiungere elementi all'inizio dell'array i cui elementi sono indicizzati nell'ordine normale. Per aggiungere un elemento alla fine dell'array normalmente indicizzato, è sufficiente aumentare la dimensione dell'array e assegnare un valore all'ultimo elemento.

Per aggiungere un elemento all'inizio dell'array, dovresti aumentare la dimensione dell'array, spostare tutti i valori e assegnare un nuovo valore all'elemento zero. Negli array indicizzati in ordine inverso, è possibile aggiungere facilmente un nuovo elemento all'inizio dell'array. Ma se devi aggiungere un nuovo elemento alla fine dell'array, dovresti prima aumentare la dimensione dell'array e dopo aver spostato tutti i valori all'inizio assegnare un nuovo valore all'ultimo elemento. Le manipolazioni dell'ordine di indicizzazione non risolveranno questo problema.

L'ordine di indicizzazione dell'array può essere determinato utilizzando la funzione ArrayIsSeries():

bool series=ArrayIsSeries(ar);

Se l'array è indicizzato in ordine inverso, la funzione restituirà true.

Gli array indicizzati in ordine inverso vengono utilizzati principalmente negli Expert Advisor. Nello sviluppo di EA, è spesso più conveniente contare le barre da destra a sinistra e quindi copiare i dati sui prezzi e i buffer degli indicatori su array con indicizzazione inversa.


Copia di array

Il modo più semplice per copiare è scorrere un array in un ciclo e copiare elemento per elemento da un array all'altro. Esiste tuttavia una funzione speciale in MQL5 che ci consente di copiare gli array - ArrayCopy():

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

ArrayCopy(ar2,ar1);

Dopo l'esecuzione del codice sopra, l'array ar2 sarà composto da tre elementi con gli stessi valori dell'array ar1: 1, 2, 3.

Se il numero di elementi da copiare non rientra nell'array in cui si sta copiando, la dimensione dell'array verrà automaticamente aumentata (l'array deve essere dinamico). Se l'array è più grande del numero di elementi da copiare, la sua dimensione rimarrà la stessa.

La funzione ArrayCopy() consente anche di copiare solo una parte di un array. Usando i parametri opzionali della funzione, puoi specificare il primo elemento da copiare, l'indice del primo elemento copiato nel nuovo array e il numero di elementi che stai per copiare.

Oltre a copiare elementi di un array nell'altro, la funzione ArrayCopy() può essere utilizzata per copiare elementi all'interno dello stesso array:

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

Copiamo i dati a partire dall'elemento con indice 2 e li incolliamo a partire dall'indice 1. Dopo aver eseguito questo codice, l'array conterrà i seguenti valori: 1, 3, 4, 5, 5.

La funzione ArrayCopy() consente anche di spostare i dati a destra:

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

Prendiamo i dati che iniziano con l'elemento con indice 1 e li sistemiamo a partire dall'indice 2. Dopo l'esecuzione di questo codice, l'array conterrà i seguenti valori: 1, 2, 2, 3, 4.

La funzione ArrayCopy() può essere applicata anche a array multidimensionali per cui si comporta come se l'array fosse unidimensionale e tutti i suoi elementi fossero disposti in serie:

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

Dopo l'esecuzione di questo codice, l'array apparirà come segue: {1, 2}, {5, 6}, {5, 6}.


Ordinamento di un array

Un array può essere ordinato usando la funzione ArraySort():

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

Dopo aver eseguito il codice sopra, i valori dell'array saranno disposti nel seguente ordine: 1, 2, 3, 4, 5.

La funzione ArraySort() non può essere applicata a matrici multidimensionali. È possibile trovare informazioni sull'ordinamento di array multidimensionali e strutture dati nell'articolo intitolato "Tabelle elettroniche in MQL5".


Per fare una ricerca binaria, usiamo la funzione ArrayBsearch(). Questa funzione può funzionare correttamente solo con array ordinati. La ricerca binaria prende il nome dal fatto che l'algoritmo divide continuamente un array in due parti. L'algoritmo confronta prima il valore target con il valore dell'elemento centrale dell'array, determinando così la metà che contiene l'elemento target: il sottoarray a sinistra o il sottoarray a destra. Quindi confronta il valore di destinazione con il valore dell'elemento centrale dei sottoarray e così via.

La funzione ArrayBsearch() restituisce l'indice dell'elemento con il valore di destinazione:

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

int index=ArrayBsearch(ar,3);

Dopo aver eseguito questo codice, la variabile indice avrà il valore 2.

Se il valore di destinazione non può essere trovato nell'array, la funzione restituirà l'indice dell'elemento con il valore minore più vicino. A causa di questa proprietà, la funzione può essere utilizzata per cercare le barre in base al tempo. Se non c'è una barra con il tempo specificato, nei calcoli dovrebbe essere usata una barra con un tempo minore.

Se il valore di destinazione non è nell'array e si trova oltre l'intervallo di valori dell'array, la funzione restituirà 0 (se il valore di destinazione è inferiore al valore minimo) o l'ultimo indice (se il valore di destinazione è maggiore del valore massimo ).

C'è solo un metodo che ti permette di fare una ricerca in un array non ordinato - iterazione su un 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);
}

Nell'esempio sopra, la funzione restituisce l'indice dell'elemento con il valore di destinazione. Se il valore di destinazione non è nell'array, la funzione restituisce -1.


Trovare il Massimo e il Minimo

I valori massimo e minimo nell'array possono essere trovati utilizzando le funzioni ArrayMaximum() e ArrayMinimum() che restituiscono l'indice dell'elemento con il valore massimo o minimo, rispettivamente:

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

A seguito dell'esecuzione di questo codice, la variabile MaxIndex sarà uguale a 6, la variabile MinIndex sarà uguale a 2, MaxValue avrà il valore 5 e MinValue sarà 1.

Le funzioni ArrayMaximum() e ArrayMinimum() consentono di limitare l'intervallo di ricerca specificando l'indice del primo elemento nell'intervallo di ricerca e il numero di elementi nell'intervallo di ricerca:

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

In questo caso, MaxIndex avrà il valore di 6 e MinIndex sarà - 5. Si prega di notare che l'intervallo specificato contiene due posizioni con il valore minimo di 4 - posizione 5 e posizione 7. In una situazione come questa, la funzione restituisce l'indice dell'elemento più vicino all'inizio dell'array. Queste funzioni funzionano allo stesso modo con gli array indicizzati nell'ordine inverso: restituiscono l'indice più piccolo.

Quindi ora abbiamo esaminato tutte le funzioni standard disponibili in MQL5 per lavorare con gli array.


Creazione di array multidimensionali utilizzando OOP

Un insieme di classi per la creazione di array multidimensionali include tre classi: una classe base e due classi figlie. A seconda della classe figlia selezionata nella fase di creazione di un oggetto, l'oggetto può rappresentare un array di variabili doppie o un array di oggetti. Ogni elemento dell'array di oggetti può rappresentare un altro array di oggetti o variabili.

La classe base e le classi figlio non contengono praticamente alcuna funzione, tranne il distruttore nella classe base e i costruttori nelle classi figlio. Il distruttore nella classe base serve per eliminare tutti gli oggetti al completamento del programma o della funzione. I costruttori nelle classi figlie vengono utilizzati solo per ridimensionare gli array in base alla dimensione specificata nei parametri del costruttore: per ridimensionare un array di oggetti in una classe e un array di variabili nell'altra classe. Di seguito è riportato il codice per l'implementazione di queste classi:

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

È possibile trovare queste classi nel file CMultiDimArray.mqh allegato. Il file deve essere posizionato nella cartella MQL5\Include della directory dei dati del terminale.

Usiamo ora questa classe per costruire un array unidimensionale di 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
  }

Questo esempio sotto forma di script si trova nel file allegato sTest_1_Arr.mq5. Il file deve essere posizionato nella cartella MQL5\Scripts della directory dei dati del terminale.

Ora, proviamo a creare un array bidimensionale. Ogni elemento della prima dimensione conterrà un numero diverso di elementi della seconda dimensione - uno nella prima, due nella seconda, ecc.:

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

Questo esempio sotto forma di script si trova nel file allegato sTest_2_Dim.mq5. Il file deve essere posizionato nella cartella MQL5\Scripts della directory dei dati del terminale.

Gli array risultanti sono una sorta di statico in quanto le classi non hanno metodi per modificare le dimensioni dell'array. Ma poiché gli array D[] e V[] si trovano nella sezione pubblica della classe, essi sono disponibili per qualsiasi manipolazione. E puoi senza alcuna difficoltà ridimensionare l'array V[]. Quando si ridimensionano gli array D[] e si riducono le loro dimensioni, è necessario prima eliminare gli oggetti puntati dagli oggetti da eliminare o caricare oggetti al loro interno quando si aumentano le dimensioni dell'array.

Si possono anche pensare ad altri modi per implementare array multidimensionali usando OOP o strutture dati, se lo si desidera.


Conclusione

L'articolo ha trattato tutte le funzioni standard disponibili in MQL5 per lavorare con gli array. Abbiamo passato in rassegna le peculiarità e alcune delle tecniche più importanti per la gestione degli array. Il linguaggio MQL5 offre un totale di 15 funzioni alcune delle quali sono di fondamentale importanza, mentre altre possono rimanere completamente inutilizzate, tranne nei casi in cui è necessario risolvere un problema non convenzionale. Le funzioni possono essere ordinate per importanza e frequenza di utilizzo come segue:

  1. ArraySize() e ArrayResize() sono le funzioni essenziali.

  2. ArrayMaximum(), ArrayMinimum(), ArrayCopy(), ArrayInitialize(), ArrayFill() e ArrayFree() sono funzioni che semplificano notevolmente il lavoro con gli array.

  3. ArraySort() è una funzione importante e utile che viene tuttavia utilizzata raramente a causa della sua scarsa funzionalità.

  4. ArrayBsearch() è una funzione usata raramente, ma può essere molto importante in rari casi eccezionali.

  5. ArraySetAsSeries(), ArrayRange(), ArrayGetAsSeries(), ArrayIsDynamic() e ArrayIsSeries() sono funzioni che vengono utilizzate molto raramente o quasi mai.

All'uso di array dinamici, essendo una delle tecniche di programmazione descritte in questo articolo, dovrebbe essere prestata particolare attenzione in quanto determina le prestazioni del programma.

Tradotto dal russo da MetaQuotes Ltd.
Articolo originale: https://www.mql5.com/ru/articles/567

File allegati |
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)
Modifica "al volo" i parametri dell'Expert Advisor dal pannello utente Modifica "al volo" i parametri dell'Expert Advisor dal pannello utente
Questo articolo fornisce un piccolo esempio che dimostra l'implementazione di un Expert Advisor i cui parametri possono essere controllati dal pannello utente. Quando si modificano i parametri "al volo", l'Expert Advisor scrive i valori ottenuti dal pannello informazioni su un file per leggerli ulteriormente dal file e visualizzarli di conseguenza sul pannello. Questo articolo può essere rilevante per chi fa trading manualmente o in modalità semi-automatica.
Segnali di trading per MetaTrader 5 Una migliore alternativa agli account PAMM! Segnali di trading per MetaTrader 5 Una migliore alternativa agli account PAMM!
Siamo lieti di annunciare che MetaTrader 5 ora dispone di segnali di trading, offrendo così un potente strumento a investitori e manager. Mentre segui le operazioni di un trader di successo, il terminale le riprodurrà automaticamente nel tuo account!
Apprendimento automatico: Come le macchine a vettori di supporto possono essere utilizzate nel trading Apprendimento automatico: Come le macchine a vettori di supporto possono essere utilizzate nel trading
Le macchine a vettori di supporto sono state a lungo utilizzate in campi come la bioinformatica e la matematica applicata per valutare set di dati complessi ed estrarre modelli utili che possono essere utilizzati per classificare i dati. Questo articolo esamina cos'è una macchina a vettori di supporto, come funzionano e perché possono essere così utili nell'estrazione di modelli complessi. Indaghiamo quindi su come possono essere applicate al mercato e potenzialmente utilizzate per dare consigli sulle negoziazioni. Utilizzando il Support Vector Machine Learning Tool, questo articolo fornisce esempi funzionanti che consentono ai lettori di sperimentare con il proprio trading.
Test rapidi delle idee di trading sul grafico Test rapidi delle idee di trading sul grafico
L'articolo descrive un metodo di test visivo veloce delle idee di trading. Il metodo si basa sulla combinazione di un grafico dei prezzi, un indicatore di segnale e un indicatore di calcolo del saldo. Vorrei condividere il mio metodo di ricerca di idee di trading, così come il metodo che uso per testare rapidamente queste idee.