Библиотека Generic классов - ошибки, описание, вопросы, особенности использования и предложения - страница 14

 
Sergey Dzyublik:

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

  • В MQL5 нет интерфейсов.
  • В MQL5 нет исключений.

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

 
Vasiliy Sokolov:

По той же причине сравнение некорректное. Как можно сравнивать пользовательский CHashMap и работу с системными функциями для получения торгового окружения? 

Пишите дальше неоптимальный код, раз железных аргументов не принимаете.

Форум по трейдингу, автоматическим торговым системам и тестированию торговых стратегий

Библиотека Generic классов - ошибки, описание, вопросы, особенности использования и предложения

fxsaber, 2017.12.08 22:46

Для более реального тестерного случая (2000 сделок и 1 000 000 единичных обращений к истории) результат выглядит так

2017.12.05 00:00:00   Time[Print(SumProfit(Deals,GetDealProfitFull))] = 122969
2017.12.05 00:00:00   Time[SetHashMap()] = 816
2017.12.05 00:00:00   4829800340.792288
2017.12.05 00:00:00   Time[Print(SumProfit(Deals,GetDealProfitHashClear))] = 23852
2017.12.05 00:00:00   Time[HistorySelect(0,INT_MAX)] = 1
2017.12.05 00:00:00   4829800340.792288
2017.12.05 00:00:00   Time[Print(SumProfit(Deals,GetDealProfitClear))] = 114427

Почти 100 мс экономии на проход! Если, допустим, делаем Оптимизацию на 10 000 полноценных проходов, то Hash-вариант закончится на 15 минут быстрее.

Vasiliy Sokolov:

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

Какую-то ахинею оба написали, ни грамма не поняв, что HashMap в текущем виде не работает для struct и union.
 
fxsaber:

Пишите дальше неоптимальный код, раз железных аргументов не принимаете.

Какую-то ахинею оба написали, ни грамма не поняв, что HashMap в текущем виде не работает для struct и union.

Уважаемый, если вы что-то покуриваете и не в состоянии внимательно причитать пост собеседника - то это ваши проблемы и лечиться стоит не всем остальным, а исключительно больному.
Повторяем: речи о struct и union ни кто не подымал, ваши идеи ни кто не оспаривал по этому поводу....


Речь шла конкретно об этих сроках кода.
Из стандартной библиотеки:

//+------------------------------------------------------------------+
//| Returns a hashcode for custom object.                            |
//+------------------------------------------------------------------+
template<typename T>
int GetHashCode(T value)
  {
//--- try to convert to equality comparable object  
   IEqualityComparable<T>*equtable=dynamic_cast<IEqualityComparable<T>*>(value);
   if(equtable)
     {
      //--- calculate hash by specied method   
      return equtable.HashCode();
     }
   else
     {
      //--- calculate hash from name of object
      return GetHashCode(typename(value));
     }
  }
//+------------------------------------------------------------------+



И ваши добавленные:


Что вам пытались донести:
Хеш используется для ускорения поиска элементов в контейнере. Скорость O(1) - не зависит от количества добавленных элементов в контейнер.
Ситуация - пользователь использует в качестве ключа свой собственный класс.
1) Из-за отсутствия множественного наследования интерфейсов (В MQL5 нет интерфейсов) пользователь не может наследоваться от IEqualityComparable;
2) Так же пользователь забывает указать явную спецификацию для шаблонной функции GetHashCode.

Последствия - пользователю ни как не сообщается о том, что он что-то опустил и код удачно выполняется без генерации исключений (В MQL5 нет исключений).
Скорость падает от O(1) со средней константой расчета хеша до O(n) с достаточно большой константой на сравнение.
Только при большом количестве элементов в контейнере и при трате уймы времени на поиск 
bottleneck в реализации, пользователю, возможно, удастся найти причину проблем - отсутствие явной спецификация GetHashCode для его класса.
Без обид, спасибо.

 
А всё таки, зачем все эти бесспорно крутые штуки для трейдинга?
 Ну вот ниразу в жизни не приходилось тысячи сделок поднимать из истории, искать породившие их ордера и т.д.
 
Sergey Dzyublik:

Уважаемый, если вы что-то покуриваете и не в состоянии внимательно причитать пост собеседника - то это ваши проблемы и лечиться стоит не всем остальным, а исключительно больному.

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

#include <TypeToBytes.mqh> // https://www.mql5.com/ru/code/16280
#include <crc64.mqh>       // https://www.mql5.com/en/blogs/post/683577

template<typename T>
int GetHashCode( T &value )
{
  ulong crc = 0;

  return((int)crc64(crc, _R(value).Bytes, sizeof(T)));
}

template<typename T>
int GetHashCode( T &value[] )
{
  ulong crc = 0;

  return((int)crc64(crc, _R(value).Bytes, ArraySize(value) * sizeof(T)));
}
 
Комментарии, не относящиеся к этой теме, были перенесены в "Алгоритмы, методы решений, сравнение их производительности".
 

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

 

Пример 1: Ассоциация ошибки времени выполнения с ее строковым описанием

Довольно часто требуется переводить числовые константы в строковые литералы. Например, коды ошибок лучше дублировать понятной надписью, раскрывающей суть ошибки. Это не очень сложная задача и обычно ее решают через специальную функцию или switch-case или множество if такого плана:

string ErrorDescription(int error_code)
   if(error_code == 40001)
      return ("Неожиданная внутренняя ошибка");
   //...
}

Любое такое решение имеет право на жизнь. Но мы опишем решение, основанное на CHashMap и покажем те преимущества которые оно дает. 

Данный алгоритм может выглядит так: 

  • Создаем ассоциативный массив вида <код ошибки - описание ошибки>;
  • Добавляем в этот словарь возможные коды ошибки и их описание;
  • Напрямую и без посредников обращаемся к словарю для получения описания ошибки по ее коду
Данный код выглядит следующим образом:

//+------------------------------------------------------------------+
//|                                                     OrdersID.mq5 |
//|                        Copyright 2017, MetaQuotes Software Corp. |
//|                                              http://www.mql5.com |
//+------------------------------------------------------------------+
#property copyright "Copyright 2017, MetaQuotes Software Corp."
#property link      "http://www.mql5.com"
#property version   "1.00"
#include <Generic\HashMap.mqh>
input ulong FindTicketOrder = 82479995;

CHashMap<int, string> ErrorDescription;
//+------------------------------------------------------------------+
//| Script program start function                                    |
//+------------------------------------------------------------------+
void AddDescriptionInfo(void)
{
   // Добавим коды системных ошибок
   ErrorDescription.Add(0,    "Операция выполнена успешно");
   ErrorDescription.Add(4001, "Неожиданная внутренняя ошибка");
   ErrorDescription.Add(4002, "Ошибочный параметр при внутреннем вызове функции клиентского терминала");
   ErrorDescription.Add(4003, "Ошибочный параметр при вызове системной функции");
   ErrorDescription.Add(4004, "Недостаточно памяти для выполнения системной функции");
   // Можно добавлять константные значения вместо чисел
   ErrorDescription.Add(ERR_STRUCT_WITHOBJECTS_ORCLASS, "Структура содержит объекты строк и/или динамических массивов и/или структуры с такими объектами и/или классы");
   ErrorDescription.Add(ERR_INVALID_ARRAY, "Массив неподходящего типа, неподходящего размера или испорченный объект динамического массива");   
   ErrorDescription.Add(ERR_ARRAY_RESIZE_ERROR, "Недостаточно памяти для перераспределения массива либо попытка изменения размера статического массива");
   //...
}
//+------------------------------------------------------------------+
//| Script program start function                                    |
//+------------------------------------------------------------------+
void OnStart()
{
   AddDescriptionInfo();
   string last_error = "";
   ErrorDescription.TryGetValue(GetLastError(), last_error);
   printf("Последняя ошибка: " + last_error);
}
//+------------------------------------------------------------------+

После того, как коды ошибок заполнены, доступ к ним осуществляется всего одной строкой, без использования разных функций. Кроме того, напомню, что в некоторых случаях, этот код будет работать даже быстрее чем несколько десятков if, т.к. адресация к нужной ошибки происходит напрямую, со средней скоростью O(1).

 
Vasiliy Sokolov:

После того, как коды ошибок заполнены, доступ к ним осуществляется всего одной строкой, без использования разных функций.

ErrorToString в любом случае придется писать. Поэтому аргумент, как плюс, слабый.

Кроме того, напомню, что в некоторых случаях, этот код будет работать даже быстрее чем несколько десятков if, т.к. адресация к нужной ошибки происходит напрямую, со средней скоростью O(1).

А вот это несомненный плюс.

 

В предолженном решение через словарь есть несколько плюсов, главный из которых не совсем очевиден с первого взгляда. Когда мы пишем код вроде такого:

string ErrorDescription(int error_code)
   if(error_code == 40001)
      return ("Неожиданная внутренняя ошибка");
   //...
}

То мы намертво зашиваем коды ошибок в сам код эксперта. Когда пы заполняем словарь, то мы делаем это динамически, т.е. в момент выполнения программы. Динамический подход нам дает большую гибкость. Например, коды ошибок могут содержаться в специальном файле, например ErrorsCode.txt:

4001;Операция выполнена успешно
4002;Неожиданная внутренняя ошибка
4003;Ошибочный параметр при вызове системной функции
...

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