通用类库 - 错误、说明、问题、使用功能和建议 - 页 19

 
瓦西里-索科洛夫
你毕竟可以为该类编写你自己的专门化函数。
 
组合器
你毕竟可以为一个类编写自己的专用函数。

没有接口,你就无法做到这一点。

 
瓦西里-索科洛夫

没有接口,你就无法做到这一点。

没有接口,你就不能做什么?)
 
组合器
没有接口,你不能做什么?)

嗯,想想看。总之,请不要把这个话题弄得太乱。

 
瓦西里-索科洛夫

问题

显然,GetHashCode不能在MQL5用户环境中正确实现。这是因为不是所有的对象都能被访问。而且,如果这个实现对于像double int这样的原始成员来说效果很好,对于复杂的对象(类、结构甚至枚举),哈希是按名字计算的。如果我们有成千上万的CObjects甚至ENUM_something_that,那么CHashMap和CHashSet将退化为LinkedList。

我们无法避免这一点,因为我们在用户层面所拥有的只是对象的名称。

因此,所有复杂类型的 对象的哈希值是相互相等的,这里的搜索代码涉及到一个完整的数组枚举。

事实上,这一点非常关键,以至于它推翻了在根部使用Generic的全部意义。 重点是,在大多数情况下,你需要关联或存储复杂的对象:枚举、结构或类。如果你需要用简单的类型来操作,你可以用更简单的东西来做。

为了使Generic集合能够正确地与类对象一起工作,这些类必须实现IEqualityComparable接口,其中Equals和HashCode方法被定义。这意味着,用户必须自己设置计算哈希代码的方法,这是目前唯一的选择,因为不可能自动实现这些方法,就像在.Net中通过MQL5等方式实现的那样。

 
罗曼-科诺佩尔科

为了使Generic集合能够正确地与类对象一起工作,这些类必须实现IEqualityComparable接口,其中定义了Equals和HashCode方法。这意味着,用户必须设置哈希代码的计算方法,这是迄今为止唯一的选择,因为不可能像在.Net中那样自动实现这些方法,例如,通过MQL5。

罗曼,你忘了说,在 MQL5没有接口.本文 将介绍MQL5中接口的概念。

p.s. 但即使接口出现在MQL5中,结构和枚举的问题仍然没有解决。

 
瓦西里-索科洛夫

罗曼,你忘了说,在 MQL5没有接口.在今天的MQL5中,任何关于接口的讨论都是一种恶意的影射蛊惑

p.s. 但是,即使MQL5中的接口出现了,结构和枚举的问题仍然没有解决。

在MQL5中,由于数据传输的特殊性,目前不可能编写同时适用于类、结构和枚举的模板方法。
 
罗曼-科诺佩尔科
目前在MQL5中,由于数据传输的特殊性,你原则上不能编写同时适用于类、结构和枚举的模板方法。

这就是我所说的。但MQL5环境对其对象的一切都了如指掌!它有指向对象的指针,知道枚举的所有标识符(EnumToString)。这就是为什么我们需要GetHashCode作为一个系统和杂食性的函数。

另外,最后允许多接口继承。为正常的接口重写Generic,你就会有一个甜蜜点。

 

情况很明显:MQ开发者经常被C++中的多重继承灼伤,以至于他们现在害怕任何形式的继承。因此,有人提议通过使用另一种废话来避免一种废话(多重继承):可笑的继承链。

你必须明白,接口与继承没有任何关系。一个接口是一个类承诺提供给定功能的声明。如果两个类实现了相同的功能,它们不应该相互继承。继承=邪恶

 

关于交易、自动交易系统和交易策略测试的论坛

通用类库 - 错误、描述、问题、使用的特殊性和建议

罗曼-科诺佩尔科, 2017.12.18 16:29

1) 体积增长因子(容量)不等于1.2,CPrimeGenerator::ExpandPrime方法被用来计算CHashMap中的新体积。

int CPrimeGenerator::ExpandPrime(const int old_size)
  {
   int new_size=2*old_size;
   if((uint)new_size>INT_MAX && INT_MAX>old_size)
      return INT_MAX;
   else
      return GetPrime(new_size);
  }

在这个方法中,旧的集合大小被乘以2,然后对于所得的值,我们从最上面的质数中找到最近的,并将其作为新的体积返回。

关于能力的初始值--我同意,它是非常小的。

但另一方面,总有一些构造函数,你可以明确地指定初始容量。

class CHashMap: public IMap<TKey,TValue>
  {
public:
                     CHashMap(const int capacity);
                     CHashMap(const int capacity,IEqualityComparer<TKey>*comparer);
  }

所以我认为在这里改变一些东西没有什么意义。


是的,我错了,我忏悔。
真的,CHashMap的体积递增系数(容量)超过了2。
谢谢你指出这个错误,并为浪费时间而道歉。



另一方面,我设法花时间研究CPrimeGenerator的实现。

//+------------------------------------------------------------------+
//| Fast generator of parime value.                                  |
//+------------------------------------------------------------------+
int CPrimeGenerator::GetPrime(const int min)
  {
//--- a typical resize algorithm would pick the smallest prime number in this array
//--- that is larger than twice the previous capacity. 
//--- get next prime value from table
   for(int i=0; i<ArraySize(s_primes); i++)
     {
      int prime=s_primes[i];
      if(prime>=min)
         return(prime);
     }
//--- outside of our predefined table
   for(int i=(min|1); i<INT_MAX;i+=2)
     {
      if(IsPrime(i) && ((i-1)%s_hash_prime!=0))
         return(i);
     }
   return(min);
  }


而且还有一些建议,主要是为了提高性能。


1.消除模棱两可的行为。
如果我们将 "INT_MAX - 10 "作为参数传递给CPrimeGenerator::ExpandPrime,结果将是 "INT_MAX"。
如果我们将 "INT_MAX - 10 "作为参数传递给CPrimeGenerator::GetPrime,将返回同样的结果:"INT_MAX - 10"。

另外,在这两种情况下,返回的值都不是素数,这就误导了用户。



2.当对大于7199369 的数字调用GetPrime 时,节省内存成为首要任务,但这并不能证明相对较差的性能和无用的计算是合理的。

建议。
- 增加与数组CPrimeGenerator::s_primes[] 的最后一个值的比较,不对所有72个数组元素进行不必要的枚举。
- 用像CPrimeGenerator::s_primes[] 那样的预定义值数组来代替简单数字的动态搜索(遍历一行中的所有数字),但有线性而非二次递增。
值的增量将约为100万(这个数字与数组最后一个元素的s_primes的增量相似)。
元素的数量最多为3000,数值范围为8M到INT_MAX。
该数组将通过上界二进制搜索进行搜索,所需的迭代次数为12次。


3.当对小于7199369 的数字调用GetPrime 时,最坏的情况是对数组CPrimeGenerator::s_primes[] 中的所有72个值进行线性搜索。

建议是。
- 将数组中的元素数量减少到70。(通过删除前两个,或第一个和最后一个)。

const static int  CPrimeGenerator::s_primes[]=
  {
   3,7,11,17,23,29,37,47,59,71,89,107,131,163,197,239,293,353,431,521,631,761,919,
   1103,1327,1597,1931,2333,2801,3371,4049,4861,5839,7013,8419,10103,12143,14591,
   17519,21023,25229,30293,36353,43627,52361,62851,75431,90523,108631,130363,156437,
   187751,225307,270371,324449,389357,467237,560689,672827,807403,968897,1162687,1395263,
   1674319,2009191,2411033,2893249,3471899,4166287,4999559,5999471,7199369
  };

- 如果输入值小于或等于新的CPrimeGenerator::s_primes 数组中的第6个值--那么就线性地迭代这些数字(最多进行6次比较)。
- 否则在第7和第70个数组值之间使用上界二进制搜索(大约6次迭代)。

我们的想法是,只要与二进制搜索相比没有性能损失,就可以使用线性搜索。
建议的元素数--6是作为一个例子,实际上这完全取决于上界二进制搜索的具体实现。
由于某个特定功能的低调用强度而带来的整体性能提升可能并不那么有利,以至于要做任何工作来改善这个功能。