Discussão do artigo "Guia Prático MQL5: Implementando um Array Associativo ou um Dicionário para Acesso Rápido ao Dados"

 

Novo artigo Guia Prático MQL5: Implementando um Array Associativo ou um Dicionário para Acesso Rápido ao Dados foi publicado:

Este artigo descreve um algoritmo especial que permite ter acesso aos elementos através de suas chaves únicas. Qualquer tipo de dados de base pode ser usado como uma chave. Por exemplo, ele pode ser representado como uma variável do tipo string ou inteiro. Tais contentores de dados são comumente referenciados a um dicionário ou um array associativo. Ele fornece uma maneira mais fácil e eficiente na resolução de problemas.

Este artigo descreve uma classe para facilitar o armazenamento de informações, ou seja, um array associativo ou em um dicionário. Esta classe permite obter acesso a informações por sua chave.

O array associativo se assemelha a um array regular. Mas em vez de um índice, ele utiliza alguma chave única, por exemplo, a enumeração ENUM_TIMEFRAMES ou algum texto. Não importa o que a chave representa. A unicidade da chave que importa. Este algoritmo de armazenamento de dados simplifica significativamente muitos aspectos da programação.

Por exemplo, uma função, que daria um código de erro e imprime um texto equivalente ao erro, poderia ser o seguinte:

//+------------------------------------------------------------------+
//| Exibe a descrição do erro no terminal. 			     |
//| Exibe um "erro desconhecido" se o ID for desconhecido            |
//+------------------------------------------------------------------+
void PrintError(int error)
 {
   Dictionary dict;
   CStringNode* node = dict.GetObjectByKey(error);
   if(node != NULL)
      printf(node.Value());
   else
      printf("Unknown error");
 }

Nós vamos olhar para as características específicas deste código mais tarde.

Antes de tratar diretamente uma descrição da lógica interna do array associativo, nós vamos considerar os detalhes dos dois métodos principais de armazenamento de dados, ou seja, arrays e listas. Nosso dicionário será com base nesses dois tipos de dados, é por isso que se deve ter uma boa compreensão de suas características específicas. O capítulo 1 é dedicado à descrição dos tipos de dados. O segundo capítulo é dedicado à descrição do array associativo e os métodos de trabalhar com ele.

Autor: Vasiliy Sokolov

 

Trabalho legal, parabéns ao autor! Isso é algo que o MQ deveria ter incluído em \MQL5\Include\Arrays há muito tempo, espero que seja incluído na biblioteca nas próximas versões. A propósito, tudo funciona bem no MQL4 também, aqui estão as medidas do primeiro teste. Entendo que é impossível incluir tipos de dados simples em vez de *CObject devido à falta de ponteiros completos? Ou há alguma maneira de contornar isso?

2015.03.23 13:25:54.617 TestDict EURUSD,M1: 1000000 elements. Add: 1373; Get: 218
2015.03.23 13:25:52.644 TestDict EURUSD,M1: 950000 elements. Add: 1216; Get: 219
2015.03.23 13:25:50.833 TestDict EURUSD,M1: 900000 elements. Add: 1217; Get: 218
2015.03.23 13:25:49.069 TestDict EURUSD,M1: 850000 elements. Add: 1154; Get: 187
2015.03.23 13:25:47.424 TestDict EURUSD,M1: 800000 elements. Add: 1092; Get: 187
2015.03.23 13:25:45.844 TestDict EURUSD,M1: 750000 elements. Add: 1061; Get: 171
2015.03.23 13:25:44.320 TestDict EURUSD,M1: 700000 elements. Add: 1107; Get: 156
2015.03.23 13:25:42.761 TestDict EURUSD,M1: 650000 elements. Add: 1045; Get: 140
2015.03.23 13:25:41.304 TestDict EURUSD,M1: 600000 elements. Add: 1014; Get: 156
2015.03.23 13:25:39.915 TestDict EURUSD,M1: 550000 elements. Add: 920; Get: 125
2015.03.23 13:25:38.665 TestDict EURUSD,M1: 500000 elements. Add: 702; Get: 109
2015.03.23 13:25:37.693 TestDict EURUSD,M1: 450000 elements. Add: 593; Get: 93
2015.03.23 13:25:36.836 TestDict EURUSD,M1: 400000 elements. Add: 577; Get: 78
2015.03.23 13:25:36.025 TestDict EURUSD,M1: 350000 elements. Add: 561; Get: 78
2015.03.23 13:25:35.247 TestDict EURUSD,M1: 300000 elements. Add: 515; Get: 78
2015.03.23 13:25:34.557 TestDict EURUSD,M1: 250000 elements. Add: 343; Get: 63
2015.03.23 13:25:34.063 TestDict EURUSD,M1: 200000 elements. Add: 312; Get: 47
2015.03.23 13:25:33.632 TestDict EURUSD,M1: 150000 elements. Add: 281; Get: 31
2015.03.23 13:25:33.264 TestDict EURUSD,M1: 100000 elements. Add: 171; Get: 16
2015.03.23 13:25:33.038 TestDict EURUSD,M1: 50000 elements. Add: 47; Get: 16
 
VDev:

Excelente trabalho, parabéns ao autor! Isso é algo que a MQ deveria ter incluído no MQL5/Include/ Arrays há muito tempo, espero que seja incluído na biblioteca nas próximas versões. A propósito, tudo funciona bem no MQL4 também, aqui estão as medidas do primeiro teste. Entendo que é impossível incluir tipos de dados simples em vez de *CObject devido à falta de ponteiros completos? Ou há alguma maneira de fazer isso funcionar?

Funcionará. Com a ajuda do mecanismo de boxing/unboxing e dos modelos. A ideia é que cada tipo de base seja empacotado em um contêiner KeyValuePairBase. A desembalagem e o retorno do tipo correspondente são feitos por funções internas do tipo GetObjectByKey:

template<typename Type, typename T>
Type GetObjectByKey(T key);

É importante enfatizar que trabalhar com tipos de base não trará nenhuma vantagem de desempenho, mas será muito mais conveniente.

 

Agora tentei criar um CDictionaryBase armazenando um dos tipos básicos de MQL em vez de CObject com base em modelos. Infelizmente, isso não funcionou, pois as funções não permitem retornar um tipo de modelo. É uma pena:

//+------------------------------------------------------------------+
//|TestDictBase.mq5 |
//|Copyright 2015, Vasiliy Sokolov. |
//|http://www.mql5.com
//+------------------------------------------------------------------+
#property copyright "Copyright 2015, Vasiliy Sokolov."
#property link      "http://www.mql5.com"
#property version   "1.00"
#include <Dictionary.mqh>
#include <DictionaryBase.mqh>
//+------------------------------------------------------------------+
//| Função de início do programa de script|
//+------------------------------------------------------------------+
void OnStart()
  {
//---
   CDictionaryBase base;
   base.AddValue("Pi", 3.14159);
   double pi = (double)base.GetValueByKey("Pi");
   printf(DoubleToString(pi, 5));
   //base.AddObject(
  }
//+------------------------------------------------------------------+
could not deduce template argument #1    TestDictBase.mq5        19      29
could not deduce template argument #0    DictionaryBase.mqh      404     25
possible loss of data due to type conversion    DictionaryBase.mqh      133     10
possible loss of data due to type conversion    DictionaryBase.mqh      135     10
possible loss of data due to type conversion    DictionaryBase.mqh      137     10
...

//+------------------------------------------------------------------+
//| Retorna o objeto por chave.|
//+------------------------------------------------------------------+
template<typename T, typename C>
C CDictionaryBase::GetValueByKey(T key)
  {
   if(!ContainsKey(key))
      return NULL;
   return m_current_kvp.GetValue();
  }

Muito ruim.

Portanto, para cada tipo de base, teremos que criar um contêiner de base, ou apenas criar contêineres de tipos de base: CDouble, CLong, CInt, etc.

 
C-4:

Agora tentei criar um CDictionaryBase armazenando um dos tipos básicos de MQL em vez de CObject com base em modelos. Infelizmente, não consegui fazer isso, pois as funções não permitem retornar um tipo de modelo.

Elas permitem. Mas o tipo do valor retornado não pode ser deduzido automaticamente, o que, na verdade, é escrito pelo compilador.

Você pode usar uma pequena muleta na forma de um pseudoparâmetro.

template<typename T, typename C>
C CDictionaryBase::GetValueByKey(T key, C)
{
   if(!ContainsKey(key))
      return NULL;
   return m_current_kvp.GetValue();
}
 
TheXpert:

Eles fazem isso. Mas o tipo do valor retornado não pode ser deduzido automaticamente, que é o que o compilador de fato escreve.

Você pode usar uma pequena muleta na forma de um pseudoparâmetro.

Onde está a muleta de fato?
 
C-4:
Onde está a muleta de fato?
Um segundo parâmetro foi adicionado
 
TheXpert:
Um segundo parâmetro foi adicionado
Estou vendo isso agora. Vou verificar amanhã.
 
Você já tentou comparar o desempenho? Em que tamanho de dados começa a vantagem sobre a pesquisa binária em um array de strings ordenadas?
 
Integer:
Você já tentou comparar o desempenho? A partir de que tamanho de dados começa a vantagem sobre a pesquisa binária em um array de strings classificadas?

Não fiz testes exatos, mas, de acordo com minhas observações, a vantagem de velocidade começa a aparecer a partir de dezenas de milhares de elementos. Ou seja, em tarefas cotidianas com 100 a 10.000 elementos, não é possível obter um ganho de desempenho.

Outro aspecto importante aqui é a conveniência de trabalhar com o contêiner. Você não precisa escrever métodos adicionais para pesquisar elementos. Muitas tarefas cotidianas com dicionários tornam-se muito mais fáceis de implementar. Você não precisa criar um elemento para pesquisar um índice e, em seguida, recuperar o elemento necessário pelo índice correspondente, etc., etc.

Embora aqui eu tenha pensado que o desempenho deveria ser medido como o tempo total para inserir elementos e pesquisá-los. E se a pesquisa de itens classificados em CArrayObj é uma operação bastante rápida, então a inserção é um verdadeiro problema. Como a pesquisa rápida exige ordem, não podemos nos livrar de possíveis inserções, e isso reduzirá significativamente o desempenho.

 

Muito interessante e está claro que o dicionário é um organizador de dados muito útil e fácil de usar.


Obrigado por seu compartilhamento.