Обсуждение статьи "Рецепты MQL5 - Реализуем ассоциативный массив или словарь для быстрого доступа к данным"

 

Опубликована статья Рецепты MQL5 - Реализуем ассоциативный массив или словарь для быстрого доступа к данным:

В данной статье описывается специальный алгоритм, позволяющий эффективно получать доступ к элементам по их уникальному ключу. В качестве ключа может быть использован любой базовый тип данных, например ключом могут быть строки или целочисленные переменные. Такой контейнер данных принято называть словарем или ассоциативным массивом. С его помощью решать многие задачи становиться гораздо проще и эффективней.

Эта статья описывает удобный класс для хранения информации - ассоциативный массив или словарь. Благодаря этому классу можно получать доступ к информации по ее ключу.

Ассоциативный массив напоминает обычный массив, однако вместо индекса он использует некий уникальный ключ, например, перечисление ENUM_TIMEFRAMES или какой-либо текст. Что конкретно является ключом - не важно, важно чтобы этот ключ был уникальным. Благодаря такому алгоритму хранения данных многие аспекты программирования значительно упрощаются.

Например, функция, которая бы принимала код ошибки и печатала текстовой эквивалент этой ошибки, могла бы выглядеть так:

//+------------------------------------------------------------------+
//| Выводит описание полученной ошибки в терминал.                   |
//| Если идентификатор ошибки неизвестен, выводит "Unknown error"    |
//+------------------------------------------------------------------+
void PrintError(int error)
 {
   Dictionary dict;
   CStringNode* node = dict.GetObjectByKey(error);
   if(node != NULL)
      printf(node.Value());
   else
      printf("Unknown error");
 }

Позже мы разберем специфику данного кода.

Прежде чем подойти к непосредственному описанию внутренней логики ассоциативного массива, мы подробно рассмотрим два основных способа хранения данных - массивы и списки. Наш словарь будет базироваться именно на основе этих двух типах данных, поэтому специфику их работы необходимо хорошо представлять. Описанию типов данных посвящена глава 1. Вторая глава посвящена описанию ассоциативного массива и способа работы с ним.

Автор: Vasiliy Sokolov

 

Крутая работа, автору респект! Это то, что давно должны были включить MQ в \MQL5\Include\Arrays, надеюсь, это войдет в библиотеку в следующих релизах. Кстати, на MQL4 также все отлично работает, вот замеры по первому тесту. Я так понимаю, простые типа данных включить вместо *CObject не получиться из-за отсутствия полноценных указателей? Или как-то можно извернуться?

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:

Крутая работа, автору респект! Это то, что давно должны были включить MQ в \MQL5\Include\Arrays, надеюсь, это войдет в библиотеку в следующих релизах. Кстати, на MQL4 также все отлично работает, вот замеры по первому тесту. Я так понимаю, простые типа данных включить вместо *CObject не получиться из-за отсутствия полноценных указателей? Или как-то можно извернуться?

Получиться. С помощью механизма boxing/unboxing и шаблонов. Идея в том, что каждый базовый тип запаковывается в контейнер KeyValuePairBase. Распаковка и возврат соответствующего типа производится внутренними функциями  типа GetObjectByKey:

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

 Важно подчеркнуть, что работа с базовыми типами не даст преимущества в плане производительности, зато будет существенно удобней.

 

Сейчас попытался на основе шаблонов создать CDictionaryBase хранящий вместо CObject один из базовых типов MQL. К сожалению сделать этого не получилось, функции не позволяют возвратить шаблонный тип. А жаль:

//+------------------------------------------------------------------+
//|                                                 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>
//+------------------------------------------------------------------+
//| Script program start function                                    |
//+------------------------------------------------------------------+
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
...

//+------------------------------------------------------------------+
//| Возвращет объект по ключу.                                       |
//+------------------------------------------------------------------+
template<typename T, typename C>
C CDictionaryBase::GetValueByKey(T key)
  {
   if(!ContainsKey(key))
      return NULL;
   return m_current_kvp.GetValue();
  }

Жаль.

Значит, для каждого базового типа придется создавать свой базовый контейнер, либо просто создать контейнеры базовых типов: CDouble, CLong, CInt и т.д. 

 
C-4:

Сейчас попытался на основе шаблонов создать CDictionaryBase хранящий вместо CObject один из базовых типов MQL. К сожалению сделать этого не получилось, функции не позволяют возвратить шаблонный тип.

Позволяют. Но тип возвращаемого значения не может быть автоматически выведен, что собственно компилятор и пишет.

Можно использовать небольшой костылик в виде псевдопараметра.

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

Позволяют. Но тип возвращаемого значения не может быть автоматически выведен, что собственно компилятор и пишет.

Можно использовать небольшой костылик в виде псевдопараметра.

А где собственно костылек?
 
C-4:
А где собственно костылек?
Добавился второй параметр
 
TheXpert:
Добавился второй параметр
Теперь вижу. Буду проверять завтра.
 
Не пробовали сравнить быстродействие. С какого размера данных начинается  преимущество по сравнению с бинарным поиском в отсортированном строковом массиве?
 
Integer:
Не пробовали сравнить быстродействие. С какого размера данных начинается  преимущество по сравнению с бинарным поиском в отсортированном строковом массиве?

Точных тестов не производил, но по моим наблюдениям преимущество в скорости начинают проявляться начиная от десятков тысяч элементов. Т.е. в повседневных задачах на 100 - 10 000 элементов выигрыш в производительности не получить. 

Здесь важно другое, а именно удобство работы с контейнером. Не нужно писать дополнительные методы по поиску элементов. Многие повседневные задачи со словарями становятся в разы проще в реализации. Не нужно создавать элемент для поиска индекса, затем извлекать нужный по соответствующему индексу и т.д. и т.п.

 

з.ы. Хотя тут подумалось, что производительность надо измерять как совокупное время на вставку элементов и их поиск. И если поиск отсортированных элементов в CArrayObj достаточно быстрая операция, то вот со вставкой настоящая беда. Т.к. быстрый поиск требует упорядоченности, то от можественных вставок не избавиться, а это существенно замедлит производительность.

 

Я извиняюсь! 

Я только разбираюсь со списками и созданием объектов

Когда я начал тестировать и копаться в вашем коде нашел ошибку незначительную, но могу и ошибаться.

замер скорости в сравнении библиотек вашей и стандартной кто быстрей вы в описание говорите про одно, а на графике показываете другое. Это вы не заметили или я неправильно читаю график.?? 

П.П. 4.3 

Причина обращения: