Files

534 lines
38 KiB
Plaintext
Raw Permalink Normal View History

2018-06-20 15:35:55 +02:00
//+------------------------------------------------------------------+
//| CDictionary.mqh |
//| Copyright 2015, Vasiliy Sokolov. |
//| http://www.mql5.com |
//+------------------------------------------------------------------+
#property copyright "Copyright 2015, Vasiliy Sokolov."
#property link "http://www.mql5.com"
#include <Object.mqh>
#include <Arrays\List.mqh>
//+------------------------------------------------------------------+
//| Контейнер для хранения элементов CObject |
//+------------------------------------------------------------------+
class KeyValuePair : public CObject
{
private:
string m_string_key; // Хранит строковый ключ.
double m_double_key; // Хранит ключ с плавающей запятой.
ulong m_ulong_key; // Хранит безнаковый целочисленный ключ.
ulong m_hash;
bool m_free_mode; // Режим освобождения памяти объекта
public:
CObject *object;
KeyValuePair *next_kvp;
KeyValuePair *prev_kvp;
template<typename T>
KeyValuePair(T key,ulong hash,CObject *obj);
~KeyValuePair();
template<typename T>
bool EqualKey(T key);
template<typename T>
void GetKey(T &gkey);
ulong GetHash(){return m_hash;}
void FreeMode(bool free_mode){m_free_mode=free_mode;}
bool FreeMode(void){return m_free_mode;}
};
//+------------------------------------------------------------------+
//| Конструктор по умолчанию. |
//+------------------------------------------------------------------+
template<typename T>
void KeyValuePair::KeyValuePair(T key,ulong hash,CObject *obj)
{
m_hash=hash;
string name=typename(key);
if(name=="string")
m_string_key=(string)key;
else if(name=="double" || name=="float")
m_double_key=(double)key;
else
m_ulong_key=(ulong)key;
object=obj;
m_free_mode=true;
}
//+------------------------------------------------------------------+
//| |
//+------------------------------------------------------------------+
template<typename T>
KeyValuePair::GetKey(T &gkey)
{
string name=typename(gkey);
if(name=="string")
gkey=(T)m_string_key;
else if(name=="double" || name=="float")
gkey=(T)m_double_key;
else
gkey=(T)m_ulong_key;
}
//+------------------------------------------------------------------+
//| Диструктор. |
//+------------------------------------------------------------------+
KeyValuePair::~KeyValuePair()
{
if(m_free_mode)
delete object;
}
//+------------------------------------------------------------------+
//| Возвращает истину, если ключи равны. |
//+------------------------------------------------------------------+
template<typename T>
bool KeyValuePair::EqualKey(T key)
{
string name=typename(key);
if(name=="string")
return m_string_key == (string)key;
if(name=="double" || name=="float")
return m_double_key == (double)key;
else
return m_ulong_key == (ulong)key;
}
//+------------------------------------------------------------------+
//| Ассоциативный массив или словарь, хранящий элементы в виде |
//| <ключ - значение>. Где ключом может являться любой базовый тип, |
//| а значением - объект типа CObject. |
//+------------------------------------------------------------------+
class CDictionary : public CObject
{
private:
int m_array_size;
int m_total;
bool m_free_mode;
bool m_auto_free;
int m_index;
ulong m_hash;
CList *m_array[];
union casting_struct
{
double d_value;
ulong l_value;
}
casting;
KeyValuePair *m_first_kvp;
KeyValuePair *m_current_kvp;
KeyValuePair *m_last_kvp;
ulong Adler32(string line);
int GetIndexByHash(ulong hash);
template<typename T>
ulong GetHashByKey(T key);
void Resize();
int FindNextSimpleNumber(int number);
int FindNextLevel();
void Init(int capacity);
public:
CDictionary();
CDictionary(int capacity);
~CDictionary();
void Compress(void);
///
/// Returns the total number of items.
///
int Total(void){return m_total;}
/// Returns the element at key   
template<typename T>
CObject *GetObjectByKey(T key);
template<typename T>
bool AddObject(T key,CObject *value);
template<typename T>
bool DeleteObjectByKey(T key);
template<typename T>
bool ContainsKey(T key);
template<typename T>
void GetCurrentKey(T &key);
bool DeleteCurrentNode(void);
bool FreeMode(void) { return(m_free_mode); }
void FreeMode(bool free_mode);
void AutoFreeMemory(bool autoFree){m_auto_free=autoFree;}
void Clear();
CObject *GetNextNode(void);
CObject *GetPrevNode(void);
CObject *GetCurrentNode(void);
CObject *GetFirstNode(void);
CObject *GetLastNode(void);
};
//+------------------------------------------------------------------+
//| Конструктор по умолчанию. |
//+------------------------------------------------------------------+
CDictionary::CDictionary()
{
Init(3);
m_free_mode= true;
m_auto_free=true;
}
//+------------------------------------------------------------------+
//| Создает словарь, с заранее определенной емкостью capacity. |
//+------------------------------------------------------------------+
CDictionary::CDictionary(int capacity)
{
if(capacity<3)
Init(3);
else
Init(capacity);
m_free_mode= true;
m_auto_free=true;
}
//+------------------------------------------------------------------+
//| Диструктор. |
//+------------------------------------------------------------------+
CDictionary::~CDictionary()
{
Clear();
}
//+------------------------------------------------------------------+
//| Режим устанавливает режим памяти для всех подузлов |
//+------------------------------------------------------------------+
void CDictionary::FreeMode(bool free_mode)
{
if(free_mode==m_free_mode)
return;
m_free_mode=free_mode;
for(int i=0; i<ArraySize(m_array); i++)
{
CList *list=m_array[i];
if(CheckPointer(list)==POINTER_INVALID)
continue;
for(KeyValuePair *kvp=list.GetFirstNode(); kvp!=NULL; kvp=list.GetNextNode())
kvp.FreeMode(m_free_mode);
}
}
//+------------------------------------------------------------------+
//| Выполняет инициализацию словаря. |
//+------------------------------------------------------------------+
void CDictionary::Init(int capacity)
{
m_array_size=ArrayResize(m_array,capacity);
m_index= 0;
m_hash = 0;
m_total=0;
}
//+------------------------------------------------------------------+
//| Находит следующий размер для словаря. |
//+------------------------------------------------------------------+
int CDictionary::FindNextLevel()
{
double value=4;
for(int i=2; i<=31; i++)
{
value=MathPow(2.0,(double)i);
if(value > m_total)return (int)value;
}
return (int)value;
}
//+------------------------------------------------------------------+
//| Принимает строку и возвращает хэширующее 32 битное число, |
//| характеризующее эту строку. |
//+------------------------------------------------------------------+
ulong CDictionary::Adler32(string line)
{
ulong s1 = 1;
ulong s2 = 0;
uint buflength=StringLen(line);
uchar char_array[];
ArrayResize(char_array,buflength,0);
StringToCharArray(line,char_array,0,-1,CP_ACP);
for(uint n=0; n<buflength; n++)
{
s1 = (s1 + char_array[n]) % 65521;
s2 = (s2 + s1) % 65521;
}
return ((s2 << 16) + s1);
}
//+------------------------------------------------------------------+
//| Рассчитывает хэш на основе переданного ключа. Ключом может быть |
//| любой базовый MQL тип.
//+------------------------------------------------------------------+
template<typename T>
ulong CDictionary::GetHashByKey(T key)
{
ulong ukey = 0;
string name=typename(key);
if(name=="string")
return Adler32((string)key);
if(name=="double" || name=="float")
{
casting.d_value=(double)key;
ukey=casting.l_value;
}
else
ukey=(ulong)key;
return ukey;
}
//+------------------------------------------------------------------+
//| Возвращает ключ текущего элемента. |
//+------------------------------------------------------------------+
template<typename T>
void CDictionary::GetCurrentKey(T &key)
{
m_current_kvp.GetKey(key);
}
//+------------------------------------------------------------------+
//| Возвращает индекс по ключу. |
//+------------------------------------------------------------------+
int CDictionary::GetIndexByHash(ulong key)
{
return (int)(key%m_array_size);
}
//+------------------------------------------------------------------+
//| Очищает словарь от всех значений. |
//+------------------------------------------------------------------+
void CDictionary::Clear(void)
{
int size=ArraySize(m_array);
for(int i=0; i<size; i++)
{
if(CheckPointer(m_array[i])!=POINTER_INVALID)
{
m_array[i].FreeMode(true); // Элементы типа KeyValuePair удаляются всегда
delete m_array[i];
}
}
ArrayFree(m_array);
if(m_auto_free)
Init(3);
else
Init(size);
m_first_kvp=m_last_kvp=m_current_kvp=NULL;
}
//+------------------------------------------------------------------+
//| Переразмечает контейнер хранения данных. |
//+------------------------------------------------------------------+
void CDictionary::Resize(void)
{
int level=FindNextLevel();
int n=level;
CList *temp_array[];
ArrayCopy(temp_array,m_array);
ArrayFree(m_array);
m_array_size=ArrayResize(m_array,n);
int total=ArraySize(temp_array);
KeyValuePair *kv=NULL;
for(int i=0; i<total; i++)
{
if(temp_array[i]==NULL)continue;
CList *list=temp_array[i];
int count=list.Total();
list.FreeMode(false);
kv=list.GetFirstNode();
while(kv!=NULL)
{
int index=GetIndexByHash(kv.GetHash());
if(CheckPointer(m_array[index])==POINTER_INVALID)
{
m_array[index]=new CList();
m_array[index].FreeMode(true); // Элементы KeyValuePair удаляются всегда
}
list.DetachCurrent();
m_array[index].Add(kv);
kv=list.GetCurrentNode();
}
delete list;
}
int size=ArraySize(temp_array);
ArrayFree(temp_array);
}
//+------------------------------------------------------------------+
//| Сжимает словарь. |
//+------------------------------------------------------------------+
CDictionary::Compress(void)
{
if(!m_auto_free)return;
double koeff=m_array_size/(double)(m_total+1);
if(koeff < 2.0 || m_total <= 4)return;
Resize();
}
//+------------------------------------------------------------------+
//| Возвращет объект по ключу. |
//+------------------------------------------------------------------+
template<typename T>
CObject *CDictionary::GetObjectByKey(T key)
{
if(!ContainsKey(key))
return NULL;
CObject *obj=m_current_kvp.object;
return obj;
}
//+------------------------------------------------------------------+
//| Проверяет содержит ли словарь ключ произвольного типа T. |
//| RETURNS: |
//| Возвращает истину, если объект с таким ключом уже существует |
//| и ложь в противном случае. |
//+------------------------------------------------------------------+
template<typename T>
bool CDictionary::ContainsKey(T key)
{
m_hash=GetHashByKey(key);
m_index=GetIndexByHash(m_hash);
if(CheckPointer(m_array[m_index])==POINTER_INVALID)
return false;
CList *list=m_array[m_index];
KeyValuePair *current_kvp=list.GetCurrentNode();
if(current_kvp == NULL)return false;
if(current_kvp.EqualKey(key))
{
m_current_kvp=current_kvp;
return true;
}
current_kvp=list.GetFirstNode();
while(true)
{
if(current_kvp.EqualKey(key))
{
m_current_kvp=current_kvp;
return true;
}
current_kvp=list.GetNextNode();
if(current_kvp==NULL)
return false;
}
return false;
}
//+------------------------------------------------------------------+
//| Добавляет в словарь элемент типа CObject с ключом T key. |
//| INPUT PARAMETRS: |
//| T key - любой базовый тип, например int, double или string. |
//| value - класс, производный от CObject. |
//| RETURNS: |
//| Истина, если элемент был добавлен и ложь в противном случае. |
//+------------------------------------------------------------------+
template<typename T>
bool CDictionary::AddObject(T key,CObject *value)
{
if(ContainsKey(key))
return false;
if(m_total==m_array_size)
{
Resize();
ContainsKey(key);
}
if(CheckPointer(m_array[m_index])==POINTER_INVALID)
{
m_array[m_index]=new CList();
m_array[m_index].FreeMode(true); // Элементы KeyValuePair удаляются всегда
}
KeyValuePair *kv=new KeyValuePair(key,m_hash,value);
kv.FreeMode(m_free_mode);
if(m_array[m_index].Add(kv)!=-1)
m_total++;
if(CheckPointer(m_current_kvp)==POINTER_INVALID)
{
m_first_kvp=kv;
m_current_kvp=kv;
m_last_kvp=kv;
}
else
{
//добавляем в самый конец, т.к. текущий узел может быть где угодно
while(m_current_kvp.next_kvp!=NULL)
m_current_kvp=m_current_kvp.next_kvp;
m_current_kvp.next_kvp=kv;
kv.prev_kvp=m_current_kvp;
m_current_kvp=kv;
m_last_kvp=kv;
}
return true;
}
//+------------------------------------------------------------------+
//| Возвращает текущий объект. Если объект не выбран возвращает |
//| NULL. |
//+------------------------------------------------------------------+
CObject *CDictionary::GetCurrentNode(void)
{
if(m_current_kvp==NULL)
return NULL;
return m_current_kvp.object;
}
//+------------------------------------------------------------------+
//| Возвращает предыдущий объект. После вызова метода текущий |
//| объект становиться предыдущим. Если объект не выбран, возвращает |
//| NULL. |
//+------------------------------------------------------------------+
CObject *CDictionary:: GetPrevNode(void)
{
if(m_current_kvp==NULL)
return NULL;
if(m_current_kvp.prev_kvp==NULL)
return NULL;
KeyValuePair *kvp=m_current_kvp.prev_kvp;
m_current_kvp=kvp;
return kvp.object;
}
//+------------------------------------------------------------------+
//| Возвращает следующий объект. После вызова метода текущий |
//| объект становиться следующим. Если объект не выбран, возвращает |
//| NULL. |
//+------------------------------------------------------------------+
CObject *CDictionary::GetNextNode(void)
{
if(m_current_kvp==NULL)
return NULL;
if(m_current_kvp.next_kvp==NULL)
return NULL;
m_current_kvp=m_current_kvp.next_kvp;
return m_current_kvp.object;
}
//+------------------------------------------------------------------+
//| Возвращает первый узел в списке узлов. Если в словаре узлов нет, |
//| возвращает NULL. |
//+------------------------------------------------------------------+
CObject *CDictionary::GetFirstNode(void)
{
if(m_first_kvp==NULL)
return NULL;
m_current_kvp=m_first_kvp;
return m_first_kvp.object;
}
//+------------------------------------------------------------------+
//| Возвращает последний узел в списке узлов. Если в словаре узлов |
//| нет возвращает NULL. |
//+------------------------------------------------------------------+
CObject *CDictionary::GetLastNode(void)
{
if(m_last_kvp==NULL)
return NULL;
m_current_kvp=m_last_kvp;
return m_last_kvp.object;
}
//+------------------------------------------------------------------+
//| Удаляет текущий узел |
//+------------------------------------------------------------------+
bool CDictionary::DeleteCurrentNode(void)
{
if(m_current_kvp==NULL)
return false;
KeyValuePair* p_kvp = m_current_kvp.prev_kvp;
KeyValuePair* n_kvp = m_current_kvp.next_kvp;
if(CheckPointer(p_kvp)!=POINTER_INVALID)
p_kvp.next_kvp=n_kvp;
if(CheckPointer(n_kvp)!=POINTER_INVALID)
n_kvp.prev_kvp=p_kvp;
m_array[m_index].FreeMode(m_free_mode);
bool res=m_array[m_index].DeleteCurrent();
if(res)
{
m_total--;
Compress();
}
return res;
}
//+------------------------------------------------------------------+
//| Удаляет объект с ключом key из словаря. |
//+------------------------------------------------------------------+
template<typename T>
bool CDictionary::DeleteObjectByKey(T key)
{
if(!ContainsKey(key))
return false;
return DeleteCurrentNode();
}
#define FOREACH_DICT(dict) for(CObject* node = (dict).GetFirstNode(); node != NULL; node = (dict).GetNextNode())
//+------------------------------------------------------------------+