Biblioteca de classes genéricas - bugs, descrição, perguntas, recursos de uso e sugestões - página 14

 
Sergey Dzyublik:

Quanto a mim, se um objeto não implementa alguma interface ou método, é melhor gerar uma exceção do que ficar em silêncio e depois procurar a origem do problema.

  • Na MQL5 não há interfaces.
  • Não há excepções na MQL5.

E, no final, você tem uma grande auto-bomba e uma ocultação evidente do problema com o código.

 
Vasiliy Sokolov:

Pela mesma razão, a comparação é incorrecta. Como você pode comparar CHashMap personalizado e trabalhar com as funções do sistema para obter um ambiente de negociação?

Continue escrevendo código sub-ótimo, uma vez que você não aceita argumentos de ferro.

Fórum sobre negociação, sistemas de negociação automatizados e testes estratégicos

Biblioteca de classes genéricas - bugs, descrição, problemas, casos de uso e sugestões

fxsaber, 2017.12.08 22:46

Para um caso de teste mais realista (2000 negócios e 1.000.000 acessos únicos ao histórico) o resultado é o seguinte

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

Quase 100ms de poupança por passe! Se, digamos, fizermos Optimização para 10.000 passes completos, a variante Hash acabaria 15 minutos mais rápida.

Vasiliy Sokolov:

Em suma, sim, é um nobre tiro no escuro e aparente encobrimento do problema com o código.

Ambos escreveram alguns disparates e não entenderam que o HashMap na sua forma actual não funciona para a estruturação e união.
 
fxsaber:

Continue escrevendo código sub-ótimo se você não aceitar argumentos de ferro.

Alguns disparates ambos escreveram, não compreendendo minimamente que o HashMap na sua forma actual não funciona para a estruturação e união.

Querida, se você fuma alguma coisa e não consegue ler cuidadosamente o posto do interlocutor - então o problema é seu e não é todo mundo que precisa de tratamento, mas apenas a pessoa doente.
Repito: ninguém levantou a questão da estruturação e união, ninguém contestou suas idéias sobre o assunto....


Foi especificamente sobre esses termos de código.
Da biblioteca padrão:

//+------------------------------------------------------------------+
//| 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 os teus acrescentados:

Fórum sobre negociação, sistemas de negociação automatizados e testes estratégicos

Biblioteca de classes genéricas - bugs, descrição, problemas, casos de uso e sugestões

fxsaber, 2017.12.08 21:17

Acrescentou outra sobrecarga

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


O que você estava a tentar transmitir:
O haxixe é usado para acelerar a procura de itens em um recipiente. A velocidade é O(1) - não depende de quantos itens são adicionados ao recipiente.
Situação - o usuário usa a sua própria classe como chave.
1) Devido à ausência de herança múltipla de interfaces(não há interfaces no MQL5), o usuário não pode herdar do IEqualityComparable;
2) O usuário também esquece de especificar uma especificação explícita para a função de modelo GetHashCode.

As consequências - o usuário não é informado de que omitiu algo e o código é executado com sucesso sem gerar exceções(não há exceções no MQL5).
A velocidade cai de O(1) com uma constante de cálculo de hash média para O(n) com uma constante bastante grande em comparação.
Somente com um grande número de elementos em um container e perdendo muito tempo procurando por
um gargalo na implementação, o usuário pode ser capaz de encontrar a causa dos problemas - a falta de uma especificação explícita do GetHashCode para sua classe.
Sem ofensa, obrigado.

 
Mas porquê todas estas coisas sem dúvida fixes para negociar?
Nunca na minha vida tive de recuperar milhares de negócios da história, procurar as ordens que os geraram, etc.
 
Sergey Dzyublik:

Caro Senhor, se você fuma alguma coisa e não consegue ler cuidadosamente o posto do interlocutor, o problema é seu e não são todos os outros que devem ser tratados, mas apenas a pessoa doente.

Aparentemente não há maneira de os abstémios fecharem a luva.

#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)));
}
 
Comentários não relevantes para este tópico foram movidos para "Algoritmos, métodos de solução, comparando seu desempenho".
 

Embora não fosse originalmente uma coleção de exemplos, ainda sinto a necessidade de adicionar alguns exemplos para que aqueles que ainda não utilizaram os algoritmos em sua prática, possam entender porque é conveniente e, o mais importante, simples.

 

Exemplo 1: Associando um erro de tempo de execução com a descrição de sua string

Muitas vezes é necessário traduzir as constantes numéricas em literais de corda. Por exemplo, é melhor duplicar os códigos de erro com uma legenda clara descrevendo o erro. Esta não é uma tarefa muito difícil e normalmente é resolvida por uma função especial ou caso de interruptor ou muitos "se":

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

Qualquer solução desse tipo tem direito à vida. Mas vamos descrever aqui uma solução baseada no CHashMap e mostrar-lhe as suas vantagens.

O algoritmo pode parecer-se com isto:

  • Nós criamos um array associativo do código <bug - descrição do bug> tipo;
  • Adicione possíveis códigos de erro e sua descrição a este dicionário;
  • Dirigimo-nos ao dicionário directamente e sem intermediários para obter a descrição do erro através do seu código
Este código parece ser o seguinte:

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

Uma vez que os códigos de erro são preenchidos, eles são acessados com apenas uma linha, sem utilizar funções diferentes. Além disso, deixe-me lembrar que em alguns casos este código funcionará ainda mais rápido do que várias dezenas de ifs, já que o endereçamento ao erro necessário ocorre diretamente, com a velocidade média de O(1).

 
Vasiliy Sokolov:

Uma vez preenchidos os códigos de erro, eles podem ser acessados com apenas uma string, sem o uso de diferentes funções.

ErrorToString terá que ser escrito de qualquer forma. Portanto, o argumento, como vantagem, é fraco.

Além disso, deixe-me lembrar que em alguns casos este código irá funcionar ainda mais rápido do que várias dezenas de ifs, pois o endereçamento ao erro necessário ocorre diretamente, com velocidade média O(1).

Mas isto é uma vantagem óbvia.

 

Há várias vantagens na solução proposta para o dicionário, a mais importante das quais não é totalmente óbvia à primeira vista. Quando escrevemos códigos como este:

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

Estamos conectados ao próprio código do Expert Advisor. Quando preenchemos o dicionário, fazemo-lo dinamicamente, ou seja, no momento da execução do programa. A abordagem dinâmica dá-nos mais flexibilidade. Por exemplo, os códigos de erro podem estar contidos num ficheiro especial, por exemplo ErrorsCode.txt:

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

No momento de lançar o programa pode ler este arquivo e preencher o dicionário com os códigos necessários e depois retornar a variante necessária da cadeia de caracteres para o usuário. Pode haver vários arquivos desse tipo: um arquivo por idioma. Desta forma, a localização pode ser realizada, onde são exibidos códigos de erro no idioma do usuário, dependendo do idioma do usuário. Além disso, o próprio utilizador pode traduzir uma vez esses códigos de erro para a sua própria língua e o próprio programa "aprende" a emitir a mensagem desejada na sua língua. É assim que a maioria dos programas são localizados, quando a tradução de um menu está contida em um arquivo de texto e o programa o carrega, dependendo das configurações. Ou seja, sem qualquer recompilação do programa e sem alterar o seu algoritmo, podemos influenciar significativamente a apresentação dos seus resultados.

Razão: