Biblioteca de clases genéricas - errores, descripción, preguntas, características de uso y sugerencias - página 14

 
Sergey Dzyublik:

En cuanto a mí, si un objeto no implementa alguna interfaz o método, es mejor generar una excepción que guardar silencio y luego buscar el origen del problema.

  • En MQL5 no hay interfaces.
  • No hay excepciones en MQL5.

Y al final, se consigue un gran autodisparo y una evidente ocultación del problema con el código.

 
Vasiliy Sokolov:

Por la misma razón, la comparación es incorrecta. ¿Cómo se puede comparar el CHashMap personalizado y trabajar con las funciones del sistema para conseguir un entorno de negociación?

Sigue escribiendo código subóptimo ya que no acepta argumentos férreos.

Foro sobre comercio, sistemas de comercio automatizados y pruebas de estrategias

Biblioteca de clases genéricas - errores, descripción, problemas, casos de uso y sugerencias

fxsaber, 2017.12.08 22:46

Para un caso de prueba más realista (2000 operaciones y 1.000.000 de accesos al historial) el resultado es el siguiente

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

¡Casi 100ms de ahorro por pase! Si, por ejemplo, hacemos la optimización para 10.000 pases completos, la variante Hash acabaría siendo 15 minutos más rápida.

Vasiliy Sokolov:

En definitiva, sí, es un noble autodisparo y una aparente ocultación del problema con el código.

Ambos escribieron algunas tonterías y no entendieron que HashMap en la forma actual no funciona para struct y union.
 
fxsaber:

Sigue escribiendo código subóptimo si no aceptas argumentos férreos.

Menudas tonterías habéis escrito los dos, sin entender lo más mínimo que HashMap en su forma actual no funciona para struct y union.

Querido, si fumas algo y no eres capaz de leer con atención el post del interlocutor - entonces es tu problema y no son todos los demás los que necesitan tratamiento, sino sólo el enfermo.
Repito: nadie planteó la cuestión de la estructura y la unión, nadie discutió sus ideas sobre el tema....


Se trataba específicamente de esos términos del código.
De la biblioteca estándar:

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



Y sus añadidos:

Foro sobre comercio, sistemas de comercio automatizados y pruebas de estrategias

Biblioteca de clases genéricas - errores, descripción, problemas, casos de uso y sugerencias

fxsaber, 2017.12.08 21:17

Se ha añadido otra sobrecarga

template<typename T>
int GetHashCode(T &value)
  {
    return GetHashCode(typename(value));
  }


Lo que intentabas transmitir:
El hash se utiliza para acelerar la búsqueda de elementos en un contenedor. La velocidad es O(1) - no depende de cuántos elementos se añaden al contenedor.
Situación: el usuario utiliza su propia clase como clave.
1) Debido a la ausencia de herencia múltiple de interfaces(no hay interfaces en MQL5), el usuario no puede heredar de IEqualityComparable;
2) El usuario también se olvida de especificar explícitamente la función de plantilla GetHashCode.

Las consecuencias - el usuario no es informado de que ha omitido algo y el código se ejecuta con éxito sin generar excepciones(no hay excepciones en MQL5).
La velocidad cae de O(1) con una constante de cálculo de hash promedio a O(n) con una constante bastante grande en la comparación.
Sólo con un gran número de elementos en un contenedor y perdiendo mucho tiempo buscando
un cuello de botella en la implementación, el usuario podría ser capaz de encontrar la causa de los problemas - la falta de una especificación explícita de GetHashCode para su clase.
No se ofenda, gracias.

 
Pero, ¿por qué todas estas cosas indudablemente geniales para el comercio?
Nunca en mi vida he tenido que recuperar miles de operaciones del historial, buscar las órdenes que las generaron, etc.
 
Sergey Dzyublik:

Estimado señor, si usted se fuma algo y no puede leer con atención el post del interlocutor, es su problema y no son todos los demás los que deben ser tratados, sino sólo el enfermo.

Aparentemente no hay manera de que los abstemios cierren la manopla

#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)));
}
 
Los comentarios que no son relevantes para este tema se han trasladado a "Algoritmos, métodos de solución, comparación de su rendimiento".
 

Aunque en un principio no pretendía ser una recopilación de ejemplos, sigo sintiendo la necesidad de añadir algunos ejemplos para que aquellos que aún no han utilizado los algoritmos en su práctica, puedan entender por qué es conveniente y, lo más importante, sencillo.

 

Ejemplo 1: Asociar un error de ejecución con su descripción de cadena

A menudo es necesario traducir las constantes numéricas en literales de cadena. Por ejemplo, es mejor duplicar los códigos de error con una leyenda clara que describa el error. No es una tarea muy difícil y suele resolverse mediante una función especial o un caso de conmutación o muchos ifs:

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

Cualquier solución de este tipo tiene derecho a la vida. Pero aquí describiremos una solución basada en CHashMap y le mostraremos sus ventajas.

El algoritmo puede ser así:

  • Creamos una matriz asociativa del tipo <código de error - descripción de error>;
  • Añade los posibles códigos de error y su descripción en este diccionario;
  • Directamente y sin intermediarios nos dirigimos al diccionario para obtener la descripción del error por su código
Este código tiene el siguiente aspecto:

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

Una vez rellenados los códigos de error, se accede a ellos con una sola línea, sin necesidad de utilizar diferentes funciones. Además, te recuerdo que en algunos casos este código funcionará incluso más rápido que varias decenas de ifs, ya que el direccionamiento al error necesario se produce directamente, con una velocidad media de O(1).

 
Vasiliy Sokolov:

Una vez rellenados los códigos de error, se puede acceder a ellos con una sola cadena, sin necesidad de utilizar diferentes funciones.

ErrorToString tendrá que ser escrito de todos modos. Así que el argumento, como ventaja, es débil.

Además, permítame recordarle que en algunos casos este código funcionará incluso más rápido que varias docenas de ifs, porque el direccionamiento al error necesario se produce directamente, con una velocidad media O(1).

Pero esto es una ventaja evidente.

 

La solución del diccionario propuesta tiene varias ventajas, la más importante de las cuales no es del todo evidente a primera vista. Cuando escribimos un código como este:

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

Estamos integrados en el propio código del Asesor Experto. Cuando llenamos el diccionario, lo hacemos de forma dinámica, es decir, en el momento de la ejecución del programa. El enfoque dinámico nos da más flexibilidad. Por ejemplo, los códigos de error pueden estar contenidos en un archivo especial, por ejemplo, ErrorsCode.txt:

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

En el momento del lanzamiento el programa puede leer este archivo y llenar el diccionario con los códigos requeridos y luego devolver al usuario la variante requerida de la cadena. Puede haber varios archivos de este tipo: un archivo por idioma. De esta manera, se puede realizar la localización, en la que se muestran los códigos de error en el idioma del usuario, en función del mismo. Además, el propio usuario puede traducir estos códigos de error a su propio idioma una vez, y el propio programa "aprende" a emitir el mensaje deseado en su idioma. Así es como se localizan la mayoría de los programas, cuando la traducción de un menú está contenida en un archivo de texto y el programa la carga, dependiendo de la configuración. Es decir, sin necesidad de recompilar el programa y sin cambiar su algoritmo, podemos influir significativamente en la presentación de sus resultados.