657 lines
20 KiB
MQL5
657 lines
20 KiB
MQL5
//+------------------------------------------------------------------+
| |||
//| List.mqh |
| |||
//| Copyright 2000-2025, MetaQuotes Ltd. |
| |||
//| https://www.mql5.com |
| |||
//+------------------------------------------------------------------+
| |||
#include <Object.mqh>
| |||
//+------------------------------------------------------------------+
| |||
//| Class CList. |
| |||
//| Purpose: Provides the possibility of working with the list of |
| |||
//| CObject instances and its dervivatives |
| |||
//| Derives from class CObject. |
| |||
//+------------------------------------------------------------------+
| |||
class CList : public CObject
| |||
{
| |||
protected:
| |||
CObject *m_first_node; // pointer to the first element of the list
| |||
CObject *m_last_node; // pointer to the last element of the list
| |||
CObject *m_curr_node; // pointer to the current element of the list
| |||
int m_curr_idx; // index of the current list item
| |||
int m_data_total; // number of elements
| |||
bool m_free_mode; // flag of the necessity of "physical" deletion of object
| |||
bool m_data_sort; // flag if the list is sorted or not
| |||
int m_sort_mode; // mode of sorting of array
| |||
| |||
public:
| |||
CList(void);
| |||
~CList(void);
| |||
//--- methods of access to protected data
| |||
bool FreeMode(void) const { return(m_free_mode); }
| |||
void FreeMode(bool mode) { m_free_mode=mode; }
| |||
int Total(void) const { return(m_data_total); }
| |||
bool IsSorted(void) const { return(m_data_sort); }
| |||
int SortMode(void) const { return(m_sort_mode); }
| |||
//--- method of identifying the object
| |||
virtual int Type(void) const { return(0x7779); }
| |||
//--- methods for working with files
| |||
virtual bool Save(const int file_handle);
| |||
virtual bool Load(const int file_handle);
| |||
//--- method of creating an element of the list
| |||
virtual CObject *CreateElement(void) { return(NULL); }
| |||
//--- methods of filling the list
| |||
int Add(CObject *new_node);
| |||
int Insert(CObject *new_node,int index);
| |||
//--- methods for navigating
| |||
int IndexOf(CObject *node);
| |||
CObject *GetNodeAtIndex(int index);
| |||
CObject *GetFirstNode(void);
| |||
CObject *GetPrevNode(void);
| |||
CObject *GetCurrentNode(void);
| |||
CObject *GetNextNode(void);
| |||
CObject *GetLastNode(void);
| |||
//--- methods for deleting
| |||
CObject *DetachCurrent(void);
| |||
bool DeleteCurrent(void);
| |||
bool Delete(int index);
| |||
void Clear(void);
| |||
//--- method for comparing lists
| |||
bool CompareList(CList *List);
| |||
//--- methods for changing
| |||
void Sort(int mode);
| |||
bool MoveToIndex(int index);
| |||
bool Exchange(CObject *node1,CObject *node2);
| |||
//--- method for searching
| |||
CObject *Search(CObject *element);
| |||
protected:
| |||
void QuickSort(int beg,int end,int mode);
| |||
CObject *QuickSearch(CObject *element);
| |||
};
| |||
//+------------------------------------------------------------------+
| |||
//| Constructor |
| |||
//+------------------------------------------------------------------+
| |||
CList::CList(void) : m_first_node(NULL),
| |||
m_last_node(NULL),
| |||
m_curr_node(NULL),
| |||
m_curr_idx(-1),
| |||
m_data_total(0),
| |||
m_free_mode(true),
| |||
m_data_sort(false),
| |||
m_sort_mode(0)
| |||
{
| |||
}
| |||
//+------------------------------------------------------------------+
| |||
//| Destructor |
| |||
//+------------------------------------------------------------------+
| |||
CList::~CList(void)
| |||
{
| |||
Clear();
| |||
}
| |||
//+------------------------------------------------------------------+
| |||
//| Method QuickSort |
| |||
//+------------------------------------------------------------------+
| |||
void CList::QuickSort(int beg,int end,int mode)
| |||
{
| |||
int i,j,k;
| |||
CObject *i_ptr,*j_ptr,*k_ptr;
| |||
//---
| |||
i_ptr=GetNodeAtIndex(i=beg);
| |||
j_ptr=GetNodeAtIndex(j=end);
| |||
while(i<end)
| |||
{
| |||
//--- ">>1" is quick division by 2
| |||
k_ptr=GetNodeAtIndex(k=(beg+end)>>1);
| |||
while(i<j)
| |||
{
| |||
while(i_ptr.Compare(k_ptr,mode)<0)
| |||
{
| |||
//--- control the output of the array bounds
| |||
if(i==m_data_total-1)
| |||
break;
| |||
i++;
| |||
i_ptr=i_ptr.Next();
| |||
}
| |||
while(j_ptr.Compare(k_ptr,mode)>0)
| |||
{
| |||
//--- control the output of the array bounds
| |||
if(j==0)
| |||
break;
| |||
j--;
| |||
j_ptr=j_ptr.Prev();
| |||
}
| |||
if(i<=j)
| |||
{
| |||
Exchange(i_ptr,j_ptr);
| |||
i++;
| |||
i_ptr=GetNodeAtIndex(i);
| |||
//--- control the output of the array bounds
| |||
if(j==0)
| |||
break;
| |||
else
| |||
{
| |||
j--;
| |||
j_ptr=GetNodeAtIndex(j);
| |||
}
| |||
}
| |||
}
| |||
if(beg<j)
| |||
QuickSort(beg,j,mode);
| |||
beg=i;
| |||
i_ptr=GetNodeAtIndex(i=beg);
| |||
j_ptr=GetNodeAtIndex(j=end);
| |||
}
| |||
}
| |||
//+------------------------------------------------------------------+
| |||
//| Index of element specified via the pointer to the list item |
| |||
//+------------------------------------------------------------------+
| |||
int CList::IndexOf(CObject *node)
| |||
{
| |||
//--- check
| |||
if(!CheckPointer(node) || !CheckPointer(m_curr_node))
| |||
return(-1);
| |||
//--- searching index
| |||
if(node==m_curr_node)
| |||
return(m_curr_idx);
| |||
if(GetFirstNode()==node)
| |||
return(0);
| |||
for(int i=1;i<m_data_total;i++)
| |||
if(GetNextNode()==node)
| |||
return(i);
| |||
//---
| |||
return(-1);
| |||
}
| |||
//+------------------------------------------------------------------+
| |||
//| Adding a new element to the end of the list |
| |||
//+------------------------------------------------------------------+
| |||
int CList::Add(CObject *new_node)
| |||
{
| |||
//--- check
| |||
if(!CheckPointer(new_node))
| |||
return(-1);
| |||
//--- add
| |||
if(m_first_node==NULL)
| |||
m_first_node=new_node;
| |||
else
| |||
{
| |||
m_last_node.Next(new_node);
| |||
new_node.Prev(m_last_node);
| |||
}
| |||
m_curr_node=new_node;
| |||
m_curr_idx=m_data_total;
| |||
m_last_node=new_node;
| |||
m_data_sort=false;
| |||
//--- result
| |||
return(m_data_total++);
| |||
}
| |||
//+------------------------------------------------------------------+
| |||
//| Inserting a new element to the specified position in the list. |
| |||
//| Inserting element to the current position of the list if index=-1|
| |||
//+------------------------------------------------------------------+
| |||
int CList::Insert(CObject *new_node,int index)
| |||
{
| |||
CObject *tmp_node;
| |||
//--- check
| |||
if(!CheckPointer(new_node))
| |||
return(-1);
| |||
if(index>m_data_total || index<0)
| |||
return(-1);
| |||
//--- adjust
| |||
if(index==-1)
| |||
{
| |||
if(m_curr_node==NULL)
| |||
return(Add(new_node));
| |||
}
| |||
else
| |||
{
| |||
if(GetNodeAtIndex(index)==NULL)
| |||
return(Add(new_node));
| |||
}
| |||
//--- no need to check m_curr_node
| |||
tmp_node=m_curr_node.Prev();
| |||
new_node.Prev(tmp_node);
| |||
if(tmp_node!=NULL)
| |||
tmp_node.Next(new_node);
| |||
else
| |||
m_first_node=new_node;
| |||
new_node.Next(m_curr_node);
| |||
m_curr_node.Prev(new_node);
| |||
m_data_total++;
| |||
m_data_sort=false;
| |||
m_curr_node=new_node;
| |||
//--- result
| |||
return(index);
| |||
}
| |||
//+------------------------------------------------------------------+
| |||
//| Get a pointer to the position of element in the list |
| |||
//+------------------------------------------------------------------+
| |||
CObject *CList::GetNodeAtIndex(int index)
| |||
{
| |||
int i;
| |||
bool revers;
| |||
CObject *result;
| |||
//--- check
| |||
if(index>=m_data_total)
| |||
return(NULL);
| |||
if(index==m_curr_idx)
| |||
return(m_curr_node);
| |||
//--- optimize bust list
| |||
if(index<m_curr_idx)
| |||
{
| |||
//--- index to the left of the current
| |||
if(m_curr_idx-index<index)
| |||
{
| |||
//--- closer to the current index
| |||
i=m_curr_idx;
| |||
revers=true;
| |||
result=m_curr_node;
| |||
}
| |||
else
| |||
{
| |||
//--- closer to the top of the list
| |||
i=0;
| |||
revers=false;
| |||
result=m_first_node;
| |||
}
| |||
}
| |||
else
| |||
{
| |||
//--- index to the right of the current
| |||
if(index-m_curr_idx<m_data_total-index-1)
| |||
{
| |||
//--- closer to the current index
| |||
i=m_curr_idx;
| |||
revers=false;
| |||
result=m_curr_node;
| |||
}
| |||
else
| |||
{
| |||
//--- closer to the end of the list
| |||
i=m_data_total-1;
| |||
revers=true;
| |||
result=m_last_node;
| |||
}
| |||
}
| |||
if(!CheckPointer(result))
| |||
return(NULL);
| |||
//---
| |||
if(revers)
| |||
{
| |||
//--- search from right to left
| |||
for(;i>index;i--)
| |||
{
| |||
result=result.Prev();
| |||
if(result==NULL)
| |||
return(NULL);
| |||
}
| |||
}
| |||
else
| |||
{
| |||
//--- search from left to right
| |||
for(;i<index;i++)
| |||
{
| |||
result=result.Next();
| |||
if(result==NULL)
| |||
return(NULL);
| |||
}
| |||
}
| |||
m_curr_idx=index;
| |||
//--- result
| |||
return(m_curr_node=result);
| |||
}
| |||
//+------------------------------------------------------------------+
| |||
//| Get a pointer to the first itme of the list |
| |||
//+------------------------------------------------------------------+
| |||
CObject *CList::GetFirstNode(void)
| |||
{
| |||
//--- check
| |||
if(!CheckPointer(m_first_node))
| |||
return(NULL);
| |||
//--- save
| |||
m_curr_idx=0;
| |||
//--- result
| |||
return(m_curr_node=m_first_node);
| |||
}
| |||
//+------------------------------------------------------------------+
| |||
//| Get a pointer to the previous itme of the list |
| |||
//+------------------------------------------------------------------+
| |||
CObject *CList::GetPrevNode(void)
| |||
{
| |||
//--- check
| |||
if(!CheckPointer(m_curr_node) || m_curr_node.Prev()==NULL)
| |||
return(NULL);
| |||
//--- decrement
| |||
m_curr_idx--;
| |||
//--- result
| |||
return(m_curr_node=m_curr_node.Prev());
| |||
}
| |||
//+------------------------------------------------------------------+
| |||
//| Get a pointer to the current item of the list |
| |||
//+------------------------------------------------------------------+
| |||
CObject *CList::GetCurrentNode(void)
| |||
{
| |||
return(m_curr_node);
| |||
}
| |||
//+------------------------------------------------------------------+
| |||
//| Get a pointer to the next item of the list |
| |||
//+------------------------------------------------------------------+
| |||
CObject *CList::GetNextNode(void)
| |||
{
| |||
//--- check
| |||
if(!CheckPointer(m_curr_node) || m_curr_node.Next()==NULL)
| |||
return(NULL);
| |||
//--- increment
| |||
m_curr_idx++;
| |||
//--- result
| |||
return(m_curr_node=m_curr_node.Next());
| |||
}
| |||
//+------------------------------------------------------------------+
| |||
//| Get a pointer to the last itme of the list |
| |||
//+------------------------------------------------------------------+
| |||
CObject *CList::GetLastNode(void)
| |||
{
| |||
//--- check
| |||
if(!CheckPointer(m_last_node))
| |||
return(NULL);
| |||
//---
| |||
m_curr_idx=m_data_total-1;
| |||
//--- result
| |||
return(m_curr_node=m_last_node);
| |||
}
| |||
//+------------------------------------------------------------------+
| |||
//| Detach current item in the list |
| |||
//+------------------------------------------------------------------+
| |||
CObject *CList::DetachCurrent(void)
| |||
{
| |||
CObject *tmp_node,*result=NULL;
| |||
//--- check
| |||
if(!CheckPointer(m_curr_node))
| |||
return(result);
| |||
//--- "explode" list
| |||
result=m_curr_node;
| |||
m_curr_node=NULL;
| |||
//--- if the deleted item was not the last one, pull up the "tail" of the list
| |||
if((tmp_node=result.Next())!=NULL)
| |||
{
| |||
tmp_node.Prev(result.Prev());
| |||
m_curr_node=tmp_node;
| |||
}
| |||
//--- if the deleted item was not the first one, pull up "head" list
| |||
if((tmp_node=result.Prev())!=NULL)
| |||
{
| |||
tmp_node.Next(result.Next());
| |||
//--- if "last_node" is removed, move the current pointer to the end of the list
| |||
if(m_curr_node==NULL)
| |||
{
| |||
m_curr_node=tmp_node;
| |||
m_curr_idx=m_data_total-2;
| |||
}
| |||
}
| |||
m_data_total--;
| |||
//--- if necessary, adjust the settings of the first and last elements
| |||
if(m_first_node==result)
| |||
m_first_node=result.Next();
| |||
if(m_last_node==result)
| |||
m_last_node=result.Prev();
| |||
//--- complete the processing of element removed from the list
| |||
//--- remove references to the list
| |||
result.Prev(NULL);
| |||
result.Next(NULL);
| |||
//--- result
| |||
return(result);
| |||
}
| |||
//+------------------------------------------------------------------+
| |||
//| Delete current item of list item |
| |||
//+------------------------------------------------------------------+
| |||
bool CList::DeleteCurrent(void)
| |||
{
| |||
CObject *result=DetachCurrent();
| |||
//--- check
| |||
if(result==NULL)
| |||
return(false);
| |||
//--- complete the processing of element removed from the list
| |||
if(m_free_mode)
| |||
{
| |||
//--- delete it "physically"
| |||
if(CheckPointer(result)==POINTER_DYNAMIC)
| |||
delete result;
| |||
}
| |||
//--- successful
| |||
return(true);
| |||
}
| |||
//+------------------------------------------------------------------+
| |||
//| Delete an item from a given position in the list |
| |||
//+------------------------------------------------------------------+
| |||
bool CList::Delete(int index)
| |||
{
| |||
if(GetNodeAtIndex(index)==NULL)
| |||
return(false);
| |||
//--- result
| |||
return(DeleteCurrent());
| |||
}
| |||
//+------------------------------------------------------------------+
| |||
//| Remove all items from the list |
| |||
//+------------------------------------------------------------------+
| |||
void CList::Clear(void)
| |||
{
| |||
GetFirstNode();
| |||
while(m_data_total!=0)
| |||
if(!DeleteCurrent())
| |||
break;
| |||
}
| |||
//+------------------------------------------------------------------+
| |||
//| Equality comparing of two lists |
| |||
//+------------------------------------------------------------------+
| |||
bool CList::CompareList(CList *List)
| |||
{
| |||
CObject *node,*lnode;
| |||
//--- check
| |||
if(!CheckPointer(List))
| |||
return(false);
| |||
if((node=GetFirstNode())==NULL)
| |||
return(false);
| |||
if((lnode=List.GetFirstNode())==NULL)
| |||
return(false);
| |||
//--- compare
| |||
if(node.Compare(lnode)!=0)
| |||
return(false);
| |||
while((node=GetNextNode())!=NULL)
| |||
{
| |||
if((lnode=List.GetNextNode())==NULL)
| |||
return(false);
| |||
if(node.Compare(lnode)!=0)
| |||
return(false);
| |||
}
| |||
//--- equal
| |||
return(true);
| |||
}
| |||
//+------------------------------------------------------------------+
| |||
//| Sorting an array in ascending order |
| |||
//+------------------------------------------------------------------+
| |||
void CList::Sort(int mode)
| |||
{
| |||
//--- check
| |||
if(m_data_total==0)
| |||
return;
| |||
if(m_data_sort && m_sort_mode==mode)
| |||
return;
| |||
//--- sort
| |||
QuickSort(0,m_data_total-1,mode);
| |||
m_sort_mode=mode;
| |||
m_data_sort=true;
| |||
}
| |||
//+------------------------------------------------------------------+
| |||
//| Move the current item of list to the specified position |
| |||
//+------------------------------------------------------------------+
| |||
bool CList::MoveToIndex(int index)
| |||
{
| |||
//--- check
| |||
if(index>=m_data_total || !CheckPointer(m_curr_node))
| |||
return(false);
| |||
//--- tune
| |||
if(m_curr_idx==index)
| |||
return(true);
| |||
if(m_curr_idx<index)
| |||
index--;
| |||
//--- move
| |||
Insert(DetachCurrent(),index);
| |||
//--- successful
| |||
return(true);
| |||
}
| |||
//+------------------------------------------------------------------+
| |||
//| Move an item of the list from the specified position to the |
| |||
//| current one |
| |||
//+------------------------------------------------------------------+
| |||
bool CList::Exchange(CObject *node1,CObject *node2)
| |||
{
| |||
CObject *tmp_node,*node;
| |||
//--- check
| |||
if(!CheckPointer(node1) || !CheckPointer(node2))
| |||
return(false);
| |||
//---
| |||
tmp_node=node1.Prev();
| |||
node1.Prev(node2.Prev());
| |||
if(node1.Prev()!=NULL)
| |||
{
| |||
node=node1.Prev();
| |||
node.Next(node1);
| |||
}
| |||
else
| |||
m_first_node=node1;
| |||
node2.Prev(tmp_node);
| |||
if(node2.Prev()!=NULL)
| |||
{
| |||
node=node2.Prev();
| |||
node.Next(node2);
| |||
}
| |||
else
| |||
m_first_node=node2;
| |||
tmp_node=node1.Next();
| |||
node1.Next(node2.Next());
| |||
if(node1.Next()!=NULL)
| |||
{
| |||
node=node1.Next();
| |||
node.Prev(node1);
| |||
}
| |||
else
| |||
m_last_node=node1;
| |||
node2.Next(tmp_node);
| |||
if(node2.Next()!=NULL)
| |||
{
| |||
node=node2.Next();
| |||
node.Prev(node2);
| |||
}
| |||
else
| |||
m_last_node=node2;
| |||
//---
| |||
m_curr_idx=0;
| |||
m_curr_node=m_first_node;
| |||
m_data_sort=false;
| |||
//--- successful
| |||
return(true);
| |||
}
| |||
//+------------------------------------------------------------------+
| |||
//| Search position of an element in a sorted list |
| |||
//+------------------------------------------------------------------+
| |||
CObject *CList::QuickSearch(CObject *element)
| |||
{
| |||
int i,j,m;
| |||
CObject *t_node=NULL;
| |||
//--- check
| |||
if(m_data_total==0)
| |||
return(NULL);
| |||
//--- check the pointer is not needed
| |||
i=0;
| |||
j=m_data_total;
| |||
while(j>=i)
| |||
{
| |||
//--- ">>1" is quick division by 2
| |||
m=(j+i)>>1;
| |||
if(m<0 || m>=m_data_total)
| |||
break;
| |||
t_node=GetNodeAtIndex(m);
| |||
if(t_node.Compare(element,m_sort_mode)==0)
| |||
break;
| |||
if(t_node.Compare(element,m_sort_mode)>0)
| |||
j=m-1;
| |||
else
| |||
i=m+1;
| |||
t_node=NULL;
| |||
}
| |||
//--- result
| |||
return(t_node);
| |||
}
| |||
//+------------------------------------------------------------------+
| |||
//| Search position of an element in a sorted list |
| |||
//+------------------------------------------------------------------+
| |||
CObject *CList::Search(CObject *element)
| |||
{
| |||
CObject *result;
| |||
//--- check
| |||
if(!CheckPointer(element) || !m_data_sort)
| |||
return(NULL);
| |||
//--- search
| |||
result=QuickSearch(element);
| |||
//--- result
| |||
return(result);
| |||
}
| |||
//+------------------------------------------------------------------+
| |||
//| Writing list to file |
| |||
//+------------------------------------------------------------------+
| |||
bool CList::Save(const int file_handle)
| |||
{
| |||
CObject *node;
| |||
bool result=true;
| |||
//--- check
| |||
if(!CheckPointer(m_curr_node) || file_handle==INVALID_HANDLE)
| |||
return(false);
| |||
//--- write start marker - 0xFFFFFFFFFFFFFFFF
| |||
if(FileWriteLong(file_handle,-1)!=sizeof(long))
| |||
return(false);
| |||
//--- write type
| |||
if(FileWriteInteger(file_handle,Type(),INT_VALUE)!=INT_VALUE)
| |||
return(false);
| |||
//--- write list size
| |||
if(FileWriteInteger(file_handle,m_data_total,INT_VALUE)!=INT_VALUE)
| |||
return(false);
| |||
//--- sequential scannning of elements in the list using the call of method Save()
| |||
node=m_first_node;
| |||
while(node!=NULL)
| |||
{
| |||
result&=node.Save(file_handle);
| |||
node=node.Next();
| |||
}
| |||
//--- successful
| |||
return(result);
| |||
}
| |||
//+------------------------------------------------------------------+
| |||
//| Reading list from file |
| |||
//+------------------------------------------------------------------+
| |||
bool CList::Load(const int file_handle)
| |||
{
| |||
uint i,num;
| |||
CObject *node;
| |||
bool result=true;
| |||
//--- check
| |||
if(file_handle==INVALID_HANDLE)
| |||
return(false);
| |||
//--- read and checking begin marker - 0xFFFFFFFFFFFFFFFF
| |||
if(FileReadLong(file_handle)!=-1)
| |||
return(false);
| |||
//--- read and checking type
| |||
if(FileReadInteger(file_handle,INT_VALUE)!=Type())
| |||
return(false);
| |||
//--- read list size
| |||
num=FileReadInteger(file_handle,INT_VALUE);
| |||
//--- sequential creation of list items using the call of method Load()
| |||
Clear();
| |||
for(i=0;i<num;i++)
| |||
{
| |||
node=CreateElement();
| |||
if(node==NULL)
| |||
return(false);
| |||
Add(node);
| |||
result&=node.Load(file_handle);
| |||
}
| |||
//--- successful
| |||
return(result);
| |||
}
| |||
//+------------------------------------------------------------------+
|