English Русский 中文 Español Deutsch 日本語 한국어 Français Italiano Türkçe
Fundamentos básicos da programação MQL5: arrays

Fundamentos básicos da programação MQL5: arrays

MetaTrader 5Exemplos | 18 março 2014, 14:12
15 316 0
Dmitry Fedoseev
Dmitry Fedoseev

Introdução

Juntamente com as variáveis e funções, os arrays são partes integrais de quase todas as linguagens de programação. Muitos programadores iniciantes frequentemente ficam com medo dos arrays. Parece estranho, mas é verdade! Posso assegurar que eles não são nem um pouco assustadores. Na realidade, os arrays são semelhantes às variáveis comuns. Sem entrar em detalhes sobre as peculiaridades da notação, não há grande diferença entre escrever uma expressão com variáveis simples:

Variable0=1;
Variable1=2;

Variable2=Variable0+Variable1;

ou utilizar arrays:

double Variable[3];

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

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

Como você pode ver, a diferença não é tão grande, exceto pelo fato de que, quando utilizamos arrays, os nomes das variáveis contêm colchetes. Há outra diferença mais significativa - ao declarar variáveis, você precisa especificar o nome de cada variável, enquanto que, ao declarar um array, você precisa escrever o seu nome apenas uma vez e especificar o número de variáveis nos colchetes (número de elementos do array). As vantagens de usar arrays em comparação ao uso de variáveis se torna ainda mais óbvio ao gerenciarmos os desafios de um grande número de tarefas de programação da vida real.

Será possível que o motivo pelo qual os arrays são vistos como algo complicado está de alguma forma relacionado ao uso de "[" e "]"? Esses símbolos são raramente utilizados em outro lugar que não seja a programação quando se trabalha com arrays, de forma que a localização deles no teclado pode sumir da memória e causar desconforto. Quando, na realidade, você pode facilmente lembrar onde eles estão - essas duas teclas estão situadas perto da tecla "Enter" em sequência lógica: o colchete de abertura é seguido pelo colchete de fechamento.


Definição e propriedades gerais dos arrays

Então, um array é um conjunto numerado de variáveis com o mesmo nome. As propriedades gerais dos arrays incluem o nome, tipo de variável (int, double, etc.) e tamanho do array. Os elementos do array são indexados a partir de zero. Em relação aos elementos do array, é sempre melhor utilizar a palavra "índice" ao invés de "número" de forma a sugerir que se inicia a contagem dos elementos do array de zero (enquanto que os números geralmente começam de um). Com elementos indexados dessa forma, o índice do último elemento é um a menos do que o número de elementos do array.

Se o array é declarado da seguinte forma:

double Variable[3];

ele tem os seguintes elementos: Variável[0], Variável[1] e Variável[2].

Em sua face, a falta de correspondência entre o número de elementos e o índice do último elemento poderá parecer inconveniente. Na realidade, isso oferece vantagens significativas em comparação a linguagens de programação em que os elementos do array são indexados a partir de 1 ou em que o tamanho do array é definido pelo índice do seu último elemento em vez do número real de elementos no array.

Para determinar o tamanho do array em MQL5, utilizamos a função ArraySize():

double Variable[3];

int Size=ArraySize(Variable);

Após executar o código, o valor da variável Size (tamanho) será igual a 3.


Arrays estáticos e dinâmicos

Os arrays podem ser estáticos e dinâmicos. Se o tamanho do array é especificado em sua declaração, o array é estático:

double Variable[3];

O tamanho de um array estático não pode ser alterado no programa. Ao declarar um array, o seu tamanho pode ser especificado diretamente como um número (como no exemplo acima) ou com o uso de uma constante predefinida:

#define SIZE 3

double Variable[SIZE];

Um array cujo tamanho não está especificado em sua declaração é dinâmico:

double Variable[];

Antes de você usar um array desse tipo, você precisa definir o seu tamanho. O tamanho é definido pela função ArrayResize():

ArrayResize(Variable,3);

O tamanho de um array dinâmico pode ser alterado quantas vezes forem necessárias durante a execução do programa, o que é a diferença fundamental entre arrays dinâmicos e estáticos.

Se você precisar liberar o array totalmente, utilize a função ArrayFree():

ArrayFree(Variable);

Ao executar essa função, o tamanho do array é definido como 0. O efeito produzido por essa função é similar a seguinte ação:

ArrayResize(Variable,0);

A liberação do array pode ser útil quando ele não é mais necessário para a operação de programa adicional (isso reduz a quantidade de memória utilizada pelo programa) ou no início de uma função de execução (caso o array seja utilizado para coleta de dados).

A função ArrayIsDynamic() permite que você determine se qualquer array específico será estático ou dinâmico:

bool dynamicArray=ArrayIsDynamic(Variable);

A variável dynamicArray conterá um valor verdadeiro caso o array seja dinâmico e falso caso ele seja estático.


Inicialização do array

Às vezes é necessário preencher um array com valores imediatamente após a sua declaração. Imagine que você deseja criar diversos botões do mesmo tipo e organizá-los em uma linha, sendo que cada botão tem o seu próprio texto. É aqui que se observam as grandes vantagens dos arrays. Não há necessidade de copiar o código para cada botão (poderá haver dúzias deles), e também não há necessidade de chamar a mesma função repetidamente. Você pode criar o número necessário de botões através da iteração sobre o array em um circuito, tendo escrito o código de chama da função apenas uma vez.

Simplesmente declaramos um array e imediatamente atribuímos valores aos elementos dele:

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

Declarado dessa forma, o array ainda pode ser estático, apesar do seu tamanho não ter sido especificado: Isso acontece porque o número de seus elementos está definido pela lista de valores (entre chaves).

Não haverá erro se você especificar o número de elementos do array:

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

Entretanto, será melhor não fazer isso - durante o curso de aprimoramentos adicionais do programa, você precisará alterar a lista de valores de array e usar um número maior ou menor de elementos. Para determinar o tamanho do array nas partes do código em que ele é usado, recomenda-se a utilização da função ArraySize() ao invés de um determinado valor numérico. Esta abordagem permite que você apenas altere a lista de valores, sem interferir no código principal. Será mais apropriado declarar a variável para o tamanho do array e atribuir a ele o valor obtido pela função ArraySize() durante a inicialização do programa.

Se um array estático não pode ser inicializado pela lista de valores, será melhor utilizar uma constante para especificar o tamanho do array. Em geral, seguimos o princípio da redução da quantidade de código que deve ser modificada caso o programa precise de mais aperfeiçoamentos. Se você precisar preencher todos os elementos do array com os mesmos valores, utilize a função ArrayInitialize():

ArrayInitialize(Variable,1);

Após executar o código acima, todos os elementos do array Var terão o valor 1. Se os mesmos valores precisarem ser atribuídos somente a alguns dos elementos do array, utilizamos a função ArrayFill():

double Variable[4];

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

Após executar esse código, os elementos 0 e 1 terão o valor 1, enquanto que os elementos 2 e 3 terão o valor 2.


Circuito de iteração do array

Os arrays são geralmente processados com o uso de um circuito for. Ao utilizar um array estático cujo tamanho é conhecido antecipadamente, podemos realizar iteração sobre o array, para frente ou para trás, dependendo da tarefa que está sendo executada:

//--- 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 o array for dinâmico, você deve declarar uma variável para um tamanho de array imediatamente antes do circuito. Obtenha o tamanho do array e faça um circuito:

int Size=ArraySize(Var);

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

Se ao invés de usar uma variável para o tamanho do array você chamar a função ArraySize() ao verificar a condição em um circuito for, o tempo de ciclo pode ser prolongado significativamente, visto que a função ArraySize() será chamada a cada iteração do circuito. A chamada da função demora mais tempo que a chamada de uma variável:

for(int i=0; i<ArraySize(Variable); i++){
   // some manipulations on the Variable[i] element
}
O uso do código acima não é recomendado.

Se o algoritmo de programa permite a iteração retrógrada do circuito, você pode continuar sem uma variável para o tamanho do array:

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

Nesse caso, a função ArraySize() será chamada apenas uma vez no início do circuito e o ciclo será rápido.


Arrays multidimensionais

Até agora, apenas consideramos arrays unidimensionais. Eles podem ser representados da seguinte forma:

Array unidimensional

Os arrays podem ser multidimensionais. Enquanto que um array multidimensional contém apenas um valor por índice, um array multidimensional tem mais de um valor por índice. Os arrays multidimensionais são declarados da seguinte forma:

double Variable[10][3];

Isso significa que a primeira dimensão do array possui dez elementos e que a segunda dimensão possui três elementos. Isso pode ser ilustrado da seguinte forma:

Array multidimensional

Para facilitar a compreensão, um array de duas dimensões pode ser representado como um plano. O tamanho da primeira dimensão determina o comprimento, o tamanho da segunda determina a largura e o valor do elemento define os parâmetros de um determinado ponto no plano, por exemplo, a altura acima do nível do mar.

Um array também pode ser tridimensional:

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

Esse array pode ser representado como um cubo ou paralelogramo: a primeira dimensão determina o comprimento, a segunda determina a largura, a terceira determina a altura e o valor do elemento define parâmetros de um determinado ponto no espaço.

O número máximo permitido de dimensões de array em MQL5 é 4.

Um array multidimensional pode ser estático ou dinâmico apenas na primeira dimensão, sendo que todas as outras dimensões serão estáticas. Dessa forma, a função ArrayResize() permite que você altere somente o tamanho da primeira dimensão. Os tamanhos das outras dimensões devem ser especificados durante a declaração do array:

double Variable[][3][3];

Ao determinar o tamanho de um array multidimensional utilizando a função ArraySize(), devemos ter uma coisa em mente: ao alterar o tamanho do array utilizando a função ArrayResize(), o segundo parâmetro da função é o tamanho da primeira dimensão do array. Ainda assim, a função ArraySize() retorna o número total de elementos ao invés do tamanho da primeira dimensão:

double Variable[][3][3]; 

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

Após a execução do código acima, a variável Size (tamanho) será igual a 27. Lembre-se desse detalhe ao fazer a iteração ao longo de arrays multidimensionais em um circuito se você precisa obter o tamanho da primeira dimensão.

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

Conforme dito anteriormente, é aconselhável seguir o princípio da redução da quantidade de código que deve ser modificada caso o programa precise de mais aperfeiçoamentos. No exemplo de código acima, usamos o número 9 que, de qualquer modo, também pode ser calculado. Para isso, podemos utilizar a função ArrayRange() que retorna o número de elementos contidos na dimensão especificada do array. Se o número de dimensões do array é conhecido, podemos fazer um cálculo simples:

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

Podemos torná-lo mais universal:

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
}

Neste ponto, você pode pensar que seria bom criar uma função para esse cálculo. Infelizmente, isso não é possível, visto que um array aleatório não pode ser transformado em função. Ao declarar o argumento de uma função, você precisa especificar claramente o número de elementos em todas as dimensões do array exceto a primeira, o que tornaria sem sentido uma função desse tipo. Esses cálculos são melhores e mais facilmente feitos na inicialização do programa. Ao declarar um array, é aconselhável utilizar constantes que determinam tamanhos de dimensões:

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

A inicialização de arrays multidimensionais utilizando a lista de valores é similar à inicialização de arrays unidimensionais. Porém, visto que um array multidimensional é praticamente composto de diversos outros arrays, cada um desses arrays deve ser separado por chaves.

Suponha que tenhamos o array a seguir:

double Variable[3][3];

Esse array é composto por três arrays de três elementos cada:

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

Um array tridimensional é gerenciado da mesma forma. O código pode ser separado em diversas linhas para facilitar a compreensão da estrutura do 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}
   }
   };

A inicialização de um array multidimensional utilizando a função ArrayInitialize() é feita da mesma forma que a inicialização de um array unidimensional:

ArrayInitialize(Variable,1);

Após executar o código, todos os elementos do array terão o valor 1. O mesmo acontece para a função ArrayFill():

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

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

Após a execução desse código, todos os elementos associados ao primeiro elemento da primeira dimensão terão o valor 1, os que estiverem associados ao segundo elemento terão o valor 10 e os que estiverem associados ao terceiro elemento, 100.


Passar um array para uma função

Ao contrário das variáveis, os arrays apenas podem ser passados para um função por referência. Isso significa que a função não cria a sua própria instância do array e, ao invés disso, trabalha diretamente com o array passado para ela. Assim, todas as mudanças na função fazem com que o array afete o array original.

Se uma variável é passada para uma função de maneira usual (por valor), o valor da variável passada não pode ser alterado pela função:

int x=1;
Func(x);

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

Após executar a função Func(), o valor x permanece igual a 1.

Se uma variável é passada por referência (denotada por &), a função pode alterar o valor dessa variável passada a ela:

int x=1;
Func(x);

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

Após executar a função Func(), o valor x torna-se igual a 2.

Ao passar um array para um função, você precisa especificar que o argumento é passado por referência e que representa um array (entre colchetes):

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

Ao passar arrays multidimensionais para um função, os tamanhos de dimensão (exceto da primeira) devem ser especificados:

double var[][3][3];

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

Nesse caso, é mais recomendável utilizar constantes:

#define SIZE1 3
#define SIZE2 3

double Var[][SIZE1][SIZE2];

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


Salvando e carregando arrays de um arquivo

Ao salvar e carregar um array de um arquivo, você sempre deve considerar a diferença em valores do tamanho da primeira dimensão do array e o número total de elementos do array. Para salvar um array, primeiramente escrevemos o tamanho do array (número total de elementos, conforme determinado pela função ArraySize()) e então todo o array para o arquivo:

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
  }

Como resultado, obtemos uma função bastante universal para salvar arrays unidirecionais.

Para carregar um array a partir de um arquivo, primeiramente precisamos ler o tamanho do array, redimensioná-lo e finalmente fazer a leitura do 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
  }

Ao carregar um array multidimensional de um arquivo, você precisará calcular o tamanho da primeira dimensão. Por exemplo, imagine que estamos fazendo a leitura de um array tridimensional:

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
  }

Poderá muito bem ser que o arquivo contenha um array 2 por 3 ao passo que tentamos lê-lo como 3 por 3. Você pode verificar a correspondência entre tamanhos multiplicando o tamanho calculado da primeira dimensão pelo número de elementos. Se o valor resultante é igual ao número total de elementos do array, podemos falar em correspondência.

Entretanto, o array Var[2][3] corresponderá ao array Var[3][2]. Se você também precisa cobrir esse caso, você deverá salvar mais informações em um array multidimensional. Por exemplo, você pode primeiro salvar o número de elementos do array e, em seguida, o número de dimensões do array seguido pelos tamanhos de cada uma das dimensões e o próprio array.

A última função apresentada acima não é universal e é projetada para ler somente arrays tridimensionais em que o tamanho da segunda dimensão é igual ao SIZE1 e o tamanho da terceira dimensão é igual ao SIZE2. Porque não há nenhuma forma de alterar os tamanhos dinamicamente, com exceção do primeiro, isso não é um problema - criaremos funções para arrays que precisam ser utilizados no programa.

A universalidade, nesse caso, não é necessária: tamanhos de dimensões de array (exceto a primeira) não serão controlados através dos parâmetros externos do programa. Entretanto, se você precisar implementar a possibilidade de controle de tamanhos das outras dimensões, você pode solucionar essa tarefa utilizando um array multidimensional de tamanho conhecidamente maior e variáveis adicionais ou através da aplicação de técnicas de programação orientada a objetos (OOP). Falaremos mais sobre a segunda abordagem posteriormente neste artigo.


Utilização de arrays dinâmicos

Arrays dinâmicos são utilizados quando você não conhece o tamanho do array com antecedência. Se o tamanho do array depender do conjunto de parâmetros da janela de propriedades do programa, o uso dos arrays dinâmicos não será um problema: o tamanho do array será alterado somente uma vez durante a inicialização do programa.

Um array pode ser usado para coletar determinadas informações de forma dinâmica, por exemplo, dados relativos a ordens pendentes. O número poderá variar, ou seja, o tamanho requerido não é conhecido com antecedência. Nesse caso, o mais fácil seria alterar o tamanho do array para 0 antes de passar as ordens e aumentar o tamanho do array em um elemento à medida que passamos cada ordem. Isso funcionará, mas muito lentamente.

É possível alterar o tamanho do array apenas uma vez, de acordo com o número de ordens, antes de passá-las. Isso irá requerer outra variável para o índice do último elemento ativo do array (ou um número de elementos em vez do índice). Esse método é adequado se você já sabe o tamanho máximo do array. Se o tamanho máximo do array é desconhecido, podemos acelerar o trabalho ao redimensionar o array utilizando blocos, conforme demonstrado na seguinte classe:

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

Você pode encontrar esta classe no arquivo CDynamicArray.mqh anexo. O arquivo deve ser colocado na pasta MQL5\Include do diretório de dados do terminal.

Agora vamos avaliar e comparar o desempenho do código em ambas as situações: quando o tamanho do array é sequencialmente aumentado em 1 e quando ele é aumentado com a utilização de blocos:

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

  }

Esse teste, na forma de script, pode ser encontrado no arquivo sTest_Speed.mq5 anexo. O arquivo deve ser colocado na pasta MQL5\Scripts do diretório de dados do terminal.

A execução da primeira opção demorou alguns segundos, enquanto que a segunda opção foi quase instantânea.


Ordem de indexação do array

Geralmente, ao redimensionar um array, novos elementos são adicionados ao seu final:

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

Após a execução desse código, os valores contidos no array devem ser 1, 2 e 3.

Os elementos dos arrays também podem ser indexados em ordem inversa. A ordem de indexação é estabelecida pela função ArraySetAsSeries():

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

Quando se altera o tamanho de um array indexado na ordem inversa, um novo elemento é adicionado ao início do 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

Após a execução desse código, os valores contidos no array devem ser 3, 2 e 1.

Pode-se concluir que, em ambos os casos, o novo elemento é adicionado ao mesmo lado do array e que a única diferença é a ordem de indexação. Essa função não pode ser utilizada para adicionar elementos ao início do array que possui elementos indexados na ordem normal. Para adicionar elementos ao fim do array normalmente indexado, você apenas precisa aumentar o tamanho do array e atribuir um valor ao último elemento.

Para adicionar um elemento ao início do array, você deve aumentar o tamanho do array, mover todos os valores e atribuir um novo valor ao elemento zero. Em arrays indexados em ordem inversa, um novo elemento pode ser facilmente adicionado ao início do array. Entretanto, se você precisa adicionar um novo elemento ao fim do array, você primeiro deve aumentar o tamanho e, após mover todos os valores para o início, atribuir um novo valor ao último elemento. As manipulações da ordem de indexação não resolverão este problema.

A ordem de indexação do array pode ser determinada com o uso da função ArrayIsSeries():

bool series=ArrayIsSeries(ar);

Se o array estiver indexado na ordem inversa, a função retornará verdadeiro.

Os arrays indexados em ordem inversa são principalmente utilizados em Expert Advisors. No desenvolvimento de EAs, é frequentemente mais conveniente contar barras da direita para a esquerda e assim copiar os dados de preço e buffers do indicador para os arrays com indexação inversa.


Cópia de arrays

A maneira mais fácil de copiar é realizar uma iteração do array em um circuito e copiar elemento a elemento, de um array a outro. Entretanto, há uma função especial no MQL5 que permite copiar arrays - ArrayCopy():

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

ArrayCopy(ar2,ar1);

Após a execução do código acima, o array ar2 será composto por três elementos com os mesmos valores do array ar1: 1, 2, 3.

Se o número de elementos a ser copiado não cabe no array para o qual você está copiando, o tamanho do array será automaticamente aumentado (o array deve ser dinâmico). Se o array é maior que o número de elementos a serem copiados, o seu tamanho permanecerá o mesmo.

A função ArrayCopy() também permite que você copie apenas uma parte de um array. Utilizando os parâmetros opcionais da função, você pode especificar o primeiro elemento a ser copiado, o índice do primeiro elemento copiado no novo array e o número de elementos que você copiará.

Além de copiar elementos de um array para outro, a função ArrayCopy() pode ser usada para copiar elementos dentro do mesmo array:

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

Copiamos dados partindo do elemento com índice 2 e colamos eles iniciando com o índice 1. Após executar esse código, o array conterá os seguintes valores: 1, 3, 4, 5, 5.

A função ArrayCopy() também permite transferir dados para o lado direito:

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

Pegamos os dados partindo do elemento com índice 1 e os organizamos iniciando com o índice 2. Após a execução desse código, o array conterá os seguintes valores: 1, 2, 2, 3, 4.

A função ArrayCopy() também pode ser aplicada a arrays multidimensionais, por meio dos quais ela se comporta como se o array fosse unidimensional e como se todos os seus elementos estivessem organizados em séries:

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

Após a execução desse código, o array terá o seguinte aspecto: {1, 2}, {5, 6}, {5, 6}.


Ordenar um array

Um array pode ser ordenado através da função ArraySort():

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

Após você executar o código acima, os valores do array serão organizados na seguinte ordem: 1, 2, 3, 4, 5.

A função ArraySort() não pode ser aplicada a arrays multidimensionais. Você pode encontrar informações sobre ordenamento de arrays multidimensionais e estruturas de dados no artigo "Tabelas eletrônicas em MQL5".


Para realizar uma pesquisa binária, utilizamos a função ArrayBsearch(). Essa função apenas funciona corretamente com arrays ordenados. A pesquisa binária recebe esse nome devido ao fato de que o algoritmo divide continuamente um array em duas partes. O algoritmo primeiramente compara o valor alvo ao valor do elemento do meio do array, determinando assim a metade que contém o elemento alvo - o sub-array à esquerda ou o sub-array à direita. Em seguida, ele compara o valor alvo ao valor do elemento do meio dos sub-arrays e assim por diante.

A função ArrayBsearch() retorna o índice do elemento com o valor alvo:

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

int index=ArrayBsearch(ar,3);

Após executar esse código, a variável do índice terá o valor 2.

Se o valor alvo não puder ser encontrado no array, a função retornará o índice do elemento com o menor valor e que seja mais próximo. Devido a essa propriedade, a função pode ser utilizada para procurar barras por tempo. Se não houver nenhuma barra com o tempo especificado, a barra com o menor tempo deve ser utilizada nos cálculos.

Se o valor alvo não está no array e se encontra além do limite de valores do array, a função retornará o (caso o valor alvo seja menor que o valor mínimo) ou o último índice (caso o valor alvo seja maior que o valor máximo).

Há somente um método que permite que você faça uma busca em um array não ordenado - iteração sobre um 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);
}

No exemplo acima, a função retorna o índice do elemento com o valor alvo. Se o valor alvo não está no array, a função retorna -1.


Encontrar o máximo e o mínimo

Os valores máximo e mínimo do array podem ser encontrados com a utilização das funções ArrayMaximum() e ArrayMinimum() que retornam o índice do elemento com o valor máximo ou mínimo, respectivamente:

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

Após a execução desse código, a variável MaxIndex será igual a 6, a variável MinIndex será igual a 2, MaxValue terá o valor 5 e MinValue será 1.

As funções ArrayMaximum() e ArrayMinimum() permitem que você limite o período da busca ao especificar o índice do primeiro elemento e o número de elementos no período de busca:

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

Nesse caso, MaxIndex terá o valor 6 e MinIndex será - 5. Por favor, note que o período especificado contém duas posições com o valor mínimo 4 - a posição 5 e a 7. Em uma situação como essa, a função retorna o índice do elemento que está mais próximo ao início do array. Essas funções operam da mesma forma com arrays que estão indexados em ordem inversa - elas retornam o menor índice.

Assim, revisamos todas as funções padrão disponíveis em MQL5 para trabalhar com arrays.


Criar arrays multidimensionais utilizando OOP

Um conjunto de classes para criação de arrays multidimensionais inclui três classes: uma classe base e duas classes filhas. Dependendo da classe filha selecionada na etapa de criação de um objeto, o objeto pode representar um array de variáveis duplas ou um array de objetos. Cada elemento do array de objetos pode representar outro array de objetos ou variáveis.

A classe base e as classes filhas não contêm virtualmente qualquer função, exceto destrutor na primeira a construtores nas segundas. O destrutor na classe base serve para apagar todos os objetos após a finalização do programa ou da função. Os construtores das classes filhas são apenas utilizados para redimensionar os arrays de acordo com o tamanho especificado nos parâmetros do construtor: para redimensionar um array de objetos em uma classe e um array de variáveis na outra classe. Segue abaixo o código para a implementação dessas 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);
     }
  };

Você pode encontrar essas classes no arquivo CMultiDimArray.mqh anexo. O arquivo deve ser colocado na pasta MQL5\Include do diretório de dados do terminal.

Agora vamos utilizar essa classe para construir um array unidimensional similar:

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
  }

Esse exemplo, na forma de script, pode ser encontrado no arquivo sTest_1_Arr.mq5 anexo. O arquivo deve ser colocado na pasta MQL5\Scripts do diretório de dados do terminal.

Agora, vamos tentar criar um array de duas dimensões. Cada elemento da primeira dimensão conterá um número diferente de elementos da segunda dimensão - um na primeira, dois na segunda, 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

Esse exemplo, na forma de script, pode ser encontrado no arquivo sTest_2_Dim.mq5 anexo. O arquivo deve ser colocado na pasta MQL5\Scripts do diretório de dados do terminal.

Os arrays resultantes são meio estáticos, visto que as classes não possuem métodos para alteração de tamanho de array. Porém, visto que os arrays D[] e V[] estão localizados na seção pública da classe, eles estão disponíveis para quaisquer manipulações. E você poderá, sem qualquer dificuldade, redimensionar o array V[]. Ao redimensionar os arrays D[] e reduzir o tamanho deles, você primeiramente deve apagar os objetos apontados pelos objetos a serem apagados ou carregar objetos neles ao aumentar o tamanho dos arrays.

Se desejar, você também poderá pensar em outras formas de implementar arrays multidimensionais utilizando OOP ou estruturas de dados.


Conclusão

Este artigo abordou todas as funções padrão disponíveis em MQL5 para trabalhar com arrays. Revisamos as peculiaridades e algumas das técnicas mais importantes para manejo de arrays. A linguagem MQL5 oferece um total de 15 funções, sendo que algumas delas são de fundamental importância, enquanto outras podem ficar praticamente sem uso, exceto em casos em que você precisa solucionar um problema incomum. As funções podem ser organizadas por importância e frequência de uso da seguinte forma:

  1. ArraySize() e ArrayResize() são as funções essenciais.

  2. ArrayMaximum(), ArrayMinimum(), ArrayCopy(), ArrayInitialize(), ArrayFill() e ArrayFree() são funções que facilitam muito o trabalho com arrays.

  3. ArraySort() é uma função útil e importante que, entretanto, é utilizada raramente devido a sua baixa funcionalidade.

  4. ArrayBsearch() é uma função que é raramente utilizada, embora ela possa ser muito importante em casos raros excepcionais.

  5. ArraySetAsSeries(), ArrayRange(), ArrayGetAsSeries(), ArrayIsDynamic() e ArrayIsSeries() são funções que são utilizadas muito raramente ou quase nunca.

Deve-se prestar atenção especial ao uso de arrays dinâmicos, uma das técnicas de programação descritas neste artigo, visto que ele tem grande influência sobre, e pode-se dizer que determina, o desempenho do programa.

Traduzido do russo pela MetaQuotes Ltd.
Artigo original: https://www.mql5.com/ru/articles/567

Arquivos anexados |
cdynamicarray.mqh (2.59 KB)
cmultidimarray.mqh (1.67 KB)
stest_1_arr.mq5 (1.29 KB)
stest_2_dim.mq5 (1.43 KB)
stest_speed.mq5 (1.6 KB)
Alterar os parâmetros do Expert Advisor instantaneamente a partir do painel de usuário Alterar os parâmetros do Expert Advisor instantaneamente a partir do painel de usuário
Este artigo fornece um pequeno exemplo demonstrando a implementação de um Expert Advisor em que os parâmetros podem ser controlados a partir do painel de usuário. Quando mudar os parâmetros para "rápidos", o Expert Advisor escreve os valores obtidos a partir do painel de informações para um arquivo para ler futuramente o arquivo e exibe de acordo no painel. Este artigo pode ser relevante para aqueles que negociam em modo manual ou semi-automático.
Sinais de negociação no MetaTrader 5: uma melhor alternativa às contas PAMM! Sinais de negociação no MetaTrader 5: uma melhor alternativa às contas PAMM!
Temos o prazer de anunciar que o MetaTrader 5 agora dispõe de Sinais de negociação, proporcionando assim uma ferramenta poderosa aos investidores e gerentes. Enquanto você estiver seguindo as negociações de um trader bem sucedido, o terminal irá reproduzi-las automaticamente em sua conta!
Aprendizagem de máquina: como as máquinas de vetores de suporte podem ser utilizadas nas negociações Aprendizagem de máquina: como as máquinas de vetores de suporte podem ser utilizadas nas negociações
As máquinas de vetores de suporte foram por muito tempo usadas em campos como de bioinformática e aplicava matemática para avaliar conjuntos de dados e extrair padrões úteis que podem ser usados para classificar dados. Este artigo visa em como é uma máquina de vetor de suporte, como trabalha e por que pode ser tão útil na extração de padrões complexos. Podemos investigar como elas podem ser aplicadas ao mercado e potencialmente usadas para aconselhar sobre negócios. Usando a Ferramenta de aprendizado da máquina de vetor de suporte, o artigo fornece exemplos trabalhados que permitem que os leitores experimentem com seus próprios negócios.
Teste rápido das ideias de negociação no gráfico Teste rápido das ideias de negociação no gráfico
O artigo descreve o método do teste de visual rápido de ideias de negociação. O método baseia-se na combinação de um gráfico de preço, um indicador de sinal e um indicador de cálculo de balanço. Eu gostaria de compartilhar o meu método de busca de ideias de negociação, bem como o método que uso para testá-las rapidamente.