Libreria di classi generiche - bug, descrizione, domande, caratteristiche d'uso e suggerimenti - pagina 14

 
Sergey Dzyublik:

Per quanto mi riguarda, se un oggetto non implementa qualche interfaccia o metodo, è meglio generare un'eccezione che tacere e poi cercare la fonte del problema.

  • In MQL5 non ci sono interfacce.
  • Non ci sono eccezioni in MQL5.

E alla fine, si ottiene un nobile auto-risarcimento e un evidente occultamento del problema con il codice.

 
Vasiliy Sokolov:

Per la stessa ragione, il confronto non è corretto. Come si può confrontare la CHashMap personalizzata e lavorare con le funzioni del sistema per ottenere un ambiente di trading?

Continuate a scrivere codice sub-ottimale poiché non accettate argomenti di ferro.

Forum sul trading, sistemi di trading automatico e test di strategia

Libreria di classi generiche - bug, descrizione, problemi, casi d'uso e suggerimenti

fxsaber, 2017.12.08 22:46

Per un caso di tester più realistico (2000 transazioni e 1.000.000 di accessi alla storia singola) il risultato appare così

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

Quasi 100ms di risparmio per ogni passaggio! Se, diciamo, facciamo l'ottimizzazione per 10.000 passaggi completi, la variante Hash finirebbe per essere 15 minuti più veloce.

Vasiliy Sokolov:

Tutto sommato, sì, è una nobile autocombustione e un apparente occultamento del problema con il codice.

Entrambi hanno scritto delle sciocchezze e non hanno capito che HashMap nella forma attuale non funziona per struct e union.
 
fxsaber:

Continuate a scrivere codice sub-ottimale se non accettate argomenti di ferro.

Alcune sciocchezze che entrambi avete scritto, non capendo minimamente che HashMap nella sua forma attuale non funziona per struct e union.

Caro, se fumi qualcosa e non sei in grado di leggere attentamente il post dell'interlocutore - allora è un tuo problema e non sono tutti gli altri che hanno bisogno di cure, ma solo il malato.
Ripeto: nessuno ha sollevato la questione della struttura e dell'unione, nessuno ha contestato le tue idee sull'argomento....


Si trattava specificamente di quei termini del codice.
Dalla libreria standard:

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



E quelli aggiunti:

Forum sul trading, sistemi di trading automatico e test di strategia

Libreria di classi generiche - bug, descrizione, problemi, casi d'uso e suggerimenti

fxsaber, 2017.12.08 21:17

Aggiunto un altro sovraccarico

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


Quello che volevi trasmettere:
L'hash è usato per accelerare la ricerca di elementi in un contenitore. La velocità è O(1) - non dipende da quanti elementi vengono aggiunti al contenitore.
Situazione - l'utente usa la propria classe come chiave.
1) A causa dell'assenza di ereditarietà multipla delle interfacce(non ci sono interfacce in MQL5), l'utente non può ereditare da IEqualityComparable;
2) L'utente dimentica anche di specificare una specifica esplicita per la funzione template GetHashCode.

Le conseguenze - l'utente non viene informato che ha omesso qualcosa e il codice viene eseguito con successo senza generare eccezioni(non ci sono eccezioni in MQL5).
La velocità scende da O(1) con una costante media di calcolo dell'hash a O(n) con una costante piuttosto grande al confronto.
Solo con un gran numero di elementi in un contenitore e perdendo molto tempo
a cercare un collo di bottiglia nell'implementazione, l'utente potrebbe essere in grado di trovare la causa dei problemi - la mancanza di una specifica esplicita di GetHashCode per la sua classe.
Senza offesa, grazie.

 
Ma perché tutte queste cose indubbiamente belle per il trading?
Mai nella mia vita ho dovuto recuperare migliaia di trade dalla cronologia, cercare gli ordini che li hanno generati, ecc.
 
Sergey Dzyublik:

Caro signore, se lei fuma qualcosa e non riesce a leggere attentamente il post dell'interlocutore, è un suo problema e non sono tutti gli altri che devono essere curati, ma solo il malato.

A quanto pare non c'è modo per gli astemi di chiudere il guanto

#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)));
}
 
I commenti non pertinenti a questo argomento sono stati spostati in "Algoritmi, metodi di soluzione, confronto delle loro prestazioni".
 

Anche se non era originariamente destinato ad essere una raccolta di esempi, sento ancora il bisogno di aggiungere alcuni esempi in modo che coloro che non hanno ancora utilizzato gli algoritmi nella loro pratica, possano capire perché è conveniente e, soprattutto, semplice.

 

Esempio 1: associare un errore di runtime con la sua stringa di descrizione

Molto spesso è necessario tradurre le costanti numeriche in letterali di stringa. Per esempio, è meglio duplicare i codici di errore con una didascalia chiara che descriva l'errore. Questo non è un compito molto difficile e di solito è risolto da una funzione speciale o da un caso di switch o da molti if:

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

Qualsiasi soluzione del genere ha diritto alla vita. Ma qui descriveremo una soluzione basata su CHashMap e vi mostreremo i suoi vantaggi.

L'algoritmo può assomigliare a questo:

  • Creiamo un array associativo del tipo <codice del bug - descrizione del bug>;
  • Aggiungete i possibili codici di errore e la loro descrizione in questo dizionario;
  • Ci rivolgiamo al dizionario direttamente e senza intermediari per ottenere la descrizione dell'errore tramite il suo codice
Questo codice appare come segue:

//+------------------------------------------------------------------+
//|                                                     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 volta compilati i codici di errore, si accede ad essi con una sola riga, senza utilizzare diverse funzioni. Inoltre, vi ricordo che in alcuni casi questo codice funzionerà anche più velocemente di diverse decine di if, poiché l'indirizzamento all'errore necessario avviene direttamente, con una velocità media di O(1).

 
Vasiliy Sokolov:

Una volta che i codici di errore sono popolati, si può accedere ad essi con una sola stringa, senza usare diverse funzioni.

ErrorToString dovrà essere scritto comunque. Quindi l'argomento, come plus, è debole.

Inoltre, vi ricordo che in alcuni casi questo codice funzionerà anche più velocemente di diverse decine di if, perché l'indirizzamento all'errore necessario avviene direttamente, con velocità media O(1).

Ma questo è un vantaggio evidente.

 

Ci sono diversi vantaggi nella soluzione proposta per il dizionario, il più importante dei quali non è del tutto ovvio a prima vista. Quando scriviamo codice come questo

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

Noi siamo cablati nel codice stesso dell'Expert Advisor. Quando riempiamo il dizionario, lo facciamo dinamicamente, cioè al momento dell'esecuzione del programma. L'approccio dinamico ci dà più flessibilità. Per esempio, i codici di errore possono essere contenuti in un file speciale, per esempio ErrorsCode.txt:

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

Al momento del lancio il programma può leggere questo file e riempire il dizionario con i codici richiesti e poi restituire la variante richiesta della stringa all'utente. Ci possono essere diversi file di questo tipo: un file per lingua. In questo modo, si può eseguire la localizzazione, dove vengono visualizzati i codici di errore nella lingua dell'utente, a seconda della lingua dell'utente. Inoltre, l'utente stesso può tradurre questi codici di errore nella sua lingua una volta, e il programma stesso "impara" a produrre il messaggio desiderato nella sua lingua. Questo è il modo in cui la maggior parte dei programmi sono localizzati, quando la traduzione di un menu è contenuta in un file di testo e il programma la carica, a seconda delle impostazioni. Cioè, senza alcuna ricompilazione del programma e senza cambiare il suo algoritmo, possiamo influenzare significativamente la presentazione dei suoi risultati.

Motivazione: