
MQL5 Programlama Temelleri: Listeler
Giriş
MQL dilinin yeni versiyonu, otomatik alım satım sistemlerinin geliştiricilerine karmaşık görevlerin uygulanması için etkili araçlar sağladı. Dilin programlama işlevlerinin önemli ölçüde genişletildiği gerçeği inkar edilemez. Tek başına MQL5 OOP özellikleri bile son derece değerlidir. Dahası, Standart Kitaplık'tan da bahsetmeden geçmemek gerekir. 359 numaralı hata koduna bakılırsa sınıf şablonları yakında desteklenecektir.
Bu makalede, veri türlerini ve kümelerini anlatan konuların bir şekilde genişletilmesi veya devamı olabilecek şeyleri gündeme getirmek istiyorum. Burada MQL5.community web sitesinde yayınlanan bir makaleye atıfta bulunmak istiyorum. Dizilerle çalışmanın ilkeleri ve mantığının çok ayrıntılı ve kapsamlı bir açıklaması Dmitry Fedoseev (Tamsayı) tarafından "MQL5 Programlama Temelleri: Diziler" makalesinde verilmiştir.
Bu yüzden bugün listelere ve daha doğrusu bağlantılı doğrusal listelere dönmeyi öneriyorum. Liste yapısı, anlamı ve mantığı ile başlayacağız. Bundan sonra, Standart Kitaplıkta zaten mevcut olan ilgili araçları ele alacağız. Sonuç olarak, MQL5 ile çalışırken listelerin nasıl kullanılabileceğine dair örnekler vereceğim.
- Liste ve Düğüm Kavramı: Teori
- 1.1 Tek Bağlantılı Listedeki Düğüm
- 1.2 Çift Bağlantılı Listedeki Düğüm
- 1.3 Dairesel Çift Bağlantılı Listedeki Düğüm
- 1.4 Ana Liste İşlemleri
- Liste ve Düğüm Kavramı: Programlama
- 2.1 Tek Bağlantılı Listedeki Düğüm
- 2.2 Çift Bağlantılı Listedeki Düğüm
- 2.3 Teşhir Edilmiş Çift Bağlantılı Listedeki Düğüm
- 2.4 Tek Bağlantılı Liste
- 2.5 Çift Bağlantılı Liste
- 2.6 Teşhir Edilmiş Çift Bağlantılı Liste
- 2.7 Dairesel Çift Bağlantılı Liste
- MQL5 Standart Kitaplığındaki Listeler
- MQL5'te Liste Kullanım Örnekleri
1. Liste ve Düğüm Kavramı: Teori
Peki bir geliştirici için liste nedir ve bu konuda nasıl bir yol izlersiniz? Bu terimin genel bir tanımı için genel bilgi kaynağı Wikipedia'ya atıfta bulunacağım:
Bilgisayar biliminde liste, aynı değerin birden çok kez meydana gelebileceği, sonlu sıralı bir değerler koleksiyonunu uygulayan soyut bir veri türüdür. Listenin bir örneği, sonlu bir dizinin matematiksel kavramının bilgisayar temsilidir; bir demet. Listedeki her değer örneğine genellikle listenin bir parçası, girişi veya öğesi denir; aynı değer birden çok kez ortaya çıkarsa, her oluşum ayrı bir öğe olarak kabul edilir.
'Liste' terimi aynı zamanda soyut listeleri, özellikle bağlantılı listeleri uygulamak için kullanılabilecek birkaç somut veri yapısı için de kullanılır.
Bu tanımın biraz fazla bilimsel olduğunu kabul edeceğinize inanıyorum.
Bu makalenin amacı doğrultusunda, bu tanımın son cümlesiyle daha çok ilgileniyoruz. O halde üzerinde duralım:
Bilgisayar biliminde, bağlantılı liste, her düğümün veriden ve listenin bir sonraki ve/veya önceki düğümüne bir veya iki referanstan ("bağlantılar") oluştuğu düğümlerden oluşan temel bir dinamik veri yapısıdır. [1] Bir bağlantılı listenin geleneksel bir diziye göre başlıca yararı yapısal bir esnekliktir: bağlantılı listenin öğelerinin sırasının bilgisayar belleğindeki veri öğelerinin sırasıyla eşleşmesi gerekmez, liste öğelerinin dahili bağlantıları ise her zaman liste geçişi için korunur.
Adım adım incelemeye çalışalım.
Bilgisayar biliminde, liste başlı başına bir veri türüdür. Bunu pekiştirdik. Diğer veri türlerini içerdiği için daha çok sentetik bir veri türüdür. Liste, biraz diziye benzer. Tek tür bir veri dizisi yeni bir veri türü olarak sınıflandırılmışsa bu bir liste olacaktır. Ancak tamamen öyle değildir.
Bir listenin başlıca yararı, ihtiyaca göre listenin herhangi bir noktasında düğümlerin eklenmesine veya çıkarılmasına izin vermesidir. Burada liste dinamik bir diziye benzer ancak liste için her zaman ArrayResize() işlevini kullanmanız gerekmez.
Bellek öğelerinin sırası açısından konuşursak, liste düğümleri depolanmaz ve dizi öğelerinin bitişik bellek alanlarında depolandığı gibi depolanmaları da gerekmez.
Konu aşağır yukarı bundan ibaret. Devam ediyoruz.
1.1 Tek Bağlantılı Listedeki Düğüm
Listeler, öğeler yerine düğümleri depolamanıza izin verir. Düğüm, iki bölümden oluşan bir veri türüdür.
İlk kısım bir veri alanıdır ve ikinci kısım diğer düğümler ile bağlantılar için kullanılır (Şekil 1). Listedeki ilk düğüme "baş", listedeki son düğüme "kuyruk" denir. Kuyruk bağlantısı alanı bir NULL referansı içerir. Temel olarak listede daha fazla düğüm bulunmadığını belirtmek için kullanılır. Belirli bir amaç için üretilen diğer kaynaklar, listenin baştan sonra geri kalanını 'kuyruk' olarak adlandırır.
Şek. 1 Tek bağlantılı listedeki düğümler
Tek bağlantılı liste düğümlerinin yanı sıra, başka düğüm türleri de vardır. Çiftli bağlantılı bir listedeki bir düğüm belki de en yaygın olanıdır.
1.2 Çift Bağlantılı Listedeki Düğüm
Ayrıca çift bağlantılı bir listenin ihtiyaçlarına hizmet edecek bir düğüme ihtiyacımız olacak. Önceki türden farklıdır çünkü önceki düğüme işaret eden başka bir bağlantı içerir. Doğal olarak, listenin başındaki düğüm bir NULL referansı içerecektir. Bu tür düğümleri içeren listenin yapısını gösteren diyagramda (Şekil 2), önceki düğümlere işaret eden bağlantılar kırmızı oklarla gösterilir.
Şek. 2 Çift bağlantılı bir listedeki düğümler
Bu nedenle, çift bağlantılı bir listedeki düğümün yetenekleri, tek bağlantılı bir liste düğümününkine benzer olacaktır. Sadece önceki düğüme bir bağlantı daha işlemeniz gerekecek.
1.3 Dairesel Çift Bağlantılı Listedeki Düğüm
Yukarıdaki düğümlerin doğrusal olmayan listelerde de kullanılabileceği durumlar vardır. Ayrıca makale öncelikle doğrusal listeleri anlatacak olsa da, dairesel bir liste örneğini de vereceğim.
Şek. 3 Dairesel bir çift bağlantılı listedeki düğümler
Dairesel çift bağlantılı liste diyagramı (Şekil 3), iki bağlantı alanına sahip düğümlerin basitçe dairesel olarak bağlı olduğunu gösterir. Bu, turuncu ve yeşil oklar kullanılarak yapılır. Böylece, baş düğüm (önceki öğe gibi) kuyruğa bağlanacaktır. Kuyruk düğümünün bağlantı alanı, başa işaret edeceği için boş olmayacaktır.
1.4 Ana Liste İşlemleri
Belirli bir amaç için üretilen literatürde belirtildiği gibi, tüm liste işlemleri 3 temel gruba ayrılabilir:
- Ekleme (listeye yeni düğüm eklenmesi);
- Silme (listeden düğümün silinmesi);
- Kontrol etme (düğüm verilerinin kontrolü).
Ekleme yöntemleri şunları içerir:
- listenin başına yeni bir düğüm ekleme;
- listenin sonuna yeni bir düğüm ekleme;
- listede belirtilen konuma bir düğüm ekleme;
- boş bir listeye bir düğüm ekleme;
- parametreli kurucu.
Silme işlemleri söz konusu olduğunda, bunlar ek grubun karşılık gelen işlemlerini sanal olarak yansıtır:
- baş düğümün silinmesi;
- kuyruk düğümünün silinmesi;
- listede belirtilen konumdan bir düğümün silinmesi;
- yıkıcı.
Burada, yıkıcının sadece liste işlemini doğru bir şekilde tamamlamaya ve sonlandırmaya değil, aynı zamanda tüm öğelerini düzgün bir şekilde silmeye de hizmet ettiğini belirtmek isterim.
Çeşitli kontrol işlemlerinin üçüncü grubu aslında listedeki düğümlere veya düğüm değerlerine erişim sağlar:
- verilen bir değeri arama;
- listenin boş olup olmadığını kontrol etmek;
- listedeki ith düğümünün değerini almak;
- listedeki ith düğümünün işaretçisini almak;
- liste boyutunu almak;
- liste öğelerinin yazdırma değerleri.
Temel gruplara ek olarak, dördüncü hizmet grubunu da ayırırdım. Önceki gruplara hizmet eder:
- belirleme operatörü;
- kopya kurucu;
- dinamik işaretçi ile çalışma;
- listenin değerlere göre kopyalanması;
- sıralama.
Bu kadar. Elbette geliştirici, herhangi bir zamanda gerektiği gibi liste sınıfının işlevselliğini ve özelliklerini genişletebilir.
2. Liste ve Düğüm Kavramı: Programlama
Bu bölümde, doğrudan programlama düğümlerine ve listelerine geçmemizi öneriyorum. Gerektiğinde koda ilişkin çizimler sağlanacaktır.
2.1 Tek Bağlantılı Listedeki Düğüm
Tek bağlantılı bir listenin ihtiyaçlarına hizmet eden düğüm sınıfının (Şekil 4) temelini atalım. "UML Araçlarını Kullanarak Expert Advisor Nasıl Geliştirilir" başlıklı makalede sınıf diyagramı gösterimi (modeli) hakkında bilgi edinebilirsiniz (bkz. Şekil 5. CTradeExpert sınıfının UML modeli).
Şek. 4 CiSingleNode sınıf modeli
Şimdi kodla çalışmayı deneyelim. Art Friedman ve diğer yazarlar tarafından kitapta verilen örneğe dayanmaktadır. "C/C++ Açıklamalı Arşivler".
//+------------------------------------------------------------------+ //| CiSingleNode class | //+------------------------------------------------------------------+ class CiSingleNode { protected: int m_val; // data CiSingleNode *m_next; // pointer to the next node public: void CiSingleNode(void); // default constructor void CiSingleNode(int _node_val); // parameterized constructor void ~CiSingleNode(void); // destructor void SetVal(int _node_val); // set-method for data void SetNextNode(CiSingleNode *_ptr_next); // set-method for the next node virtual void SetPrevNode(CiSingleNode *_ptr_prev){}; // set-method for the previous node virtual CiSingleNode *GetPrevNode(void) const {return NULL;}; // get-method for the previous node CiSingleNode *GetNextNode(void) const; // get-method for the next node int GetVal(void){TRACE_CALL(_t_flag) return m_val;} // get-method for data };
CiSingleNode sınıfının her yöntemini açıklamayacağım. Ekli CiSingleNode.mqh dosyasında bunlara daha yakından bakabileceksiniz. Ancak, dikkatinizi ilginç bir nüansa çekmek istiyorum. Sınıf, önceki düğümlerle çalışan sanal yöntemler içerir. Aslında bunlar birer kukladır ve varlıkları daha çok gelecekteki alt öğeler için polimorfizm amaçlarına yöneliktir.
Kod, kullanılan her yöntemin çağrılarını izlemek için gereken TRACE_CALL(f) önişlemci yönergesini kullanır.
#define TRACE_CALL(f) if(f) Print("Calling: "+__FUNCSIG__);
Yalnızca CiSingleNode sınıfının mevcut olmasıyla, tek bağlantılı bir liste oluşturabilecek konumdasınız. Kodun bir örneğini vereyim.
//=========== Example 1 (processing the CiSingleNode type ) CiSingleNode *p_sNodes[3]; // #1 p_sNodes[0]=NULL; srand(GetTickCount()); // initialize a random number generator //--- create nodes for(int i=0;i<ArraySize(p_sNodes);i++) p_sNodes[i]=new CiSingleNode(rand()); // #2 //--- links for(int j=0;j<(ArraySize(p_sNodes)-1);j++) p_sNodes[j].SetNextNode(p_sNodes[j+1]); // #3 //--- check values for(int i=0;i<ArraySize(p_sNodes);i++) { int val=p_sNodes[i].GetVal(); // #4 Print("Node #"+IntegerToString(i+1)+ // #5 " value = "+IntegerToString(val)); } //--- check next-nodes for(int j=0;j<(ArraySize(p_sNodes)-1);j++) { CiSingleNode *p_sNode_next=p_sNodes[j].GetNextNode(); // #9 int snode_next_val=p_sNode_next.GetVal(); // #10 Print("Next-Node #"+IntegerToString(j+1)+ // #11 " value = "+IntegerToString(snode_next_val)); } //--- delete nodes for(int i=0;i<ArraySize(p_sNodes);i++) delete p_sNodes[i]; // #12
#1 dizesinde, CiSingleNode türündeki nesnelere yönelik bir dizi işaretçi bildiririz. #2 dizesinde, dizi oluşturulan işaretçilerle doldurulur. Her düğümün verisi için rand() işlevini kullanarak 0 ila 32767 aralığında yalancı rasgele bir tamsayı alırız. Düğümler, #3 dizesindeki bir sonraki işaretçi ile bağlantılıdır. #4-5 dizelerinde düğümlerin değerlerini kontrol ediyoruz ve #9-11 dizelerinde bağlantıların performansını kontrol ediyoruz. İşaretçiler #12 dizesinde silinir.
Günlüğe yazdırılan budur.
DH 0 23:23:10 test_nodes (EURUSD,H4) Node #1 value = 3335 KP 0 23:23:10 test_nodes (EURUSD,H4) Node #2 value = 21584 GI 0 23:23:10 test_nodes (EURUSD,H4) Node #3 value = 917 HQ 0 23:23:10 test_nodes (EURUSD,H4) Next-Node #1 value = 21584 HI 0 23:23:10 test_nodes (EURUSD,H4) Next-Node #2 value = 917
Ortaya çıkan düğüm yapısı şematik olarak aşağıdaki gibi gösterilebilir (Şekil 5).
Şek. 5 CiSingleNode *p_sNodes[3] dizisindeki düğümler arasındaki bağlantılar
Şimdi çift bağlantılı bir listedeki düğümlere geçelim.
2.2 Çift Bağlantılı Listedeki Düğüm
İlk olarak, çift bağlantılı listedeki bir düğümün iki işaretçisi olması bakımından farklı olduğu bilgisini yenilemeliyiz: sonraki düğüm işaretçisi ve önceki düğüm işaretçisi. Yani, sonraki düğüm bağlantısının yanı sıra, tek bağlantılı bir liste düğümüne önceki düğüme bir işaretçi eklemeniz gerekir.
Bunu yaparken, kalıtımı bir sınıf ilişkisi olarak kullanmayı öneriyorum. Daha sonra çift bağlantılı listedeki bir düğüm için sınıf modeli aşağıdaki gibi görünebilir (Şekil 6).
Şek. 6 CDoubleNode sınıf modeli
Şimdi koda bir göz atma zamanı.
//+------------------------------------------------------------------+ //| CDoubleNode class | //+------------------------------------------------------------------+ class CDoubleNode : public CiSingleNode { protected: CiSingleNode *m_prev; // pointer to the previous node public: void CDoubleNode(void); // default constructor void CDoubleNode(int node_val); // parameterized constructor void ~CDoubleNode(void){TRACE_CALL(_t_flag)};// destructor virtual void SetPrevNode(CiSingleNode *_ptr_prev); // set-method for the previous node virtual CiSingleNode *GetPrevNode(void) const; // get-method for the previous node CDoubleNode };
Çok az ek yöntem vardır; bunlar sanaldır ve önceki düğümle çalışmakla ilgilidir. Tam sınıf açıklaması CDoubleNode.mqh içinde sağlanır.
CDoubleNode sınıfına dayalı çift bağlantılı bir liste oluşturmaya çalışalım. Kodun bir örneğini vereyim.
//=========== Example 2 (processing the CDoubleNode type) CiSingleNode *p_dNodes[3]; // #1 p_dNodes[0]=NULL; srand(GetTickCount()); // initialize a random number generator //--- create nodes for(int i=0;i<ArraySize(p_dNodes);i++) p_dNodes[i]=new CDoubleNode(rand()); // #2 //--- links for(int j=0;j<(ArraySize(p_dNodes)-1);j++) { p_dNodes[j].SetNextNode(p_dNodes[j+1]); // #3 p_dNodes[j+1].SetPrevNode(p_dNodes[j]); // #4 } //--- check values for(int i=0;i<ArraySize(p_dNodes);i++) { int val=p_dNodes[i].GetVal(); // #4 Print("Node #"+IntegerToString(i+1)+ // #5 " value = "+IntegerToString(val)); } //--- check next-nodes for(int j=0;j<(ArraySize(p_dNodes)-1);j++) { CiSingleNode *p_sNode_next=p_dNodes[j].GetNextNode(); // #9 int snode_next_val=p_sNode_next.GetVal(); // #10 Print("Next-Node #"+IntegerToString(j+1)+ // #11 " value = "+IntegerToString(snode_next_val)); } //--- check prev-nodes for(int j=0;j<(ArraySize(p_dNodes)-1);j++) { CiSingleNode *p_sNode_prev=p_dNodes[j+1].GetPrevNode(); // #12 int snode_prev_val=p_sNode_prev.GetVal(); // #13 Print("Prev-Node #"+IntegerToString(j+2)+ // #14 " value = "+IntegerToString(snode_prev_val)); } //--- delete nodes for(int i=0;i<ArraySize(p_dNodes);i++) delete p_dNodes[i]; // #15
Prensipte bu, tek başına bağlantılı bir liste oluşturmaya benzer ancak birkaç tuhaflık vardır. p_dNodes[] işaretçileri dizisinin #1 dizesinde nasıl bildirildiğine dikkat edin. İşaretçilerin türü, temel sınıfla aynı şekilde ayarlanabilir. #2 dizesindeki polimorfizm ilkesi, gelecekte onları tanımamıza yardımcı olacaktır. Önceki düğümler #12-14 dizelerinde kontrol edilir.
Aşağıdaki bilgiler günlüğe eklendi.
GJ 0 16:28:12 test_nodes (EURUSD,H4) Node #1 value = 17543 IQ 0 16:28:12 test_nodes (EURUSD,H4) Node #2 value = 1185 KK 0 16:28:12 test_nodes (EURUSD,H4) Node #3 value = 23216 DS 0 16:28:12 test_nodes (EURUSD,H4) Next-Node #1 value = 1185 NH 0 16:28:12 test_nodes (EURUSD,H4) Next-Node #2 value = 23216 FR 0 16:28:12 test_nodes (EURUSD,H4) Prev-Node #2 value = 17543 LI 0 16:28:12 test_nodes (EURUSD,H4) Prev-Node #3 value = 1185
Ortaya çıkan düğüm yapısı şematik olarak aşağıdaki gibi gösterilebilir (Şekil 7):
Şek. 7 CDoubleNode *p_sNodes[3] dizisindeki düğümler arasındaki bağlantılar
Şimdi, teşhir edilmemiş çift bağlantılı bir liste oluşturmak için gerekli olacak bir düğümü düşünmemizi öneriyorum.
2.3 Teşhir Edilmiş Çift Bağlantılı Listedeki Düğüm
Tek bir değer yerine tüm diziye atfedilebilen, yani tüm diziyi içeren ve tanımlayan bir veri üyesi içeren bir düğüm düşünün. Böyle bir düğüm daha sonra teşhir edilmemiş bir liste oluşturmak için kullanılabilir. Bu düğüm, çift bağlantılı bir listedeki standart bir düğümle tamamen aynı olduğundan burada herhangi bir örnek vermemeye karar verdim. Tek fark, ‹veri' özniteliğinin tüm diziyi kapsamasıdır.
Kalıtımı tekrar kullanacağım. CDoubleNode sınıfı, teşhir edilmemiş çift bağlantılı listedeki bir düğüm için temel sınıf olarak hizmet edecektir. Teşhir edilmemiş çift bağlantılı listedeki bir düğümün sınıf modeli aşağıdaki gibi görünecektir (Şekil 8).
Şek. 8 CiUnrollDoubleNode sınıf modeli
CiUnrollDoubleNode sınıfı aşağıdaki kod kullanılarak tanımlanabilir:
//+------------------------------------------------------------------+ //| CiUnrollDoubleNode class | //+------------------------------------------------------------------+ class CiUnrollDoubleNode : public CDoubleNode { private: int m_arr_val[]; // data array public: void CiUnrollDoubleNode(void); // default constructor void CiUnrollDoubleNode(int &_node_arr[]); // parameterized constructor void ~CiUnrollDoubleNode(void); // destructor bool GetArrVal(int &_dest_arr_val[])const; // get-method for data array bool SetArrVal(const int &_node_arr_val[]); // set-method for data array };
CiUnrollDoubleNode.mqh içinde her yöntemi daha ayrıntılı olarak inceleyebilirsiniz.
Örnek olarak parametreli bir oluşturucu ele alalım.
//+------------------------------------------------------------------+ //| Parameterized constructor | //+------------------------------------------------------------------+ void CiUnrollDoubleNode::CiUnrollDoubleNode(int &_node_arr[]) : CDoubleNode(ArraySize(_node_arr)) { ArrayCopy(this.m_arr_val,_node_arr); TRACE_CALL(_t_flag) }
Burada, başlatma listesini kullanarak, this.m_val veri üyesine tek boyutlu bir dizinin boyutunu giriyoruz.
Bundan sonra, 'manuel' olarak teşhir edilmemiş bir çift bağlantılı liste oluşturuyoruz ve içindeki bağlantıları kontrol ediyoruz.
//=========== Example 3 (processing the CiUnrollDoubleNode type) //--- data arrays int arr1[],arr2[],arr3[]; // #1 int arr_size=15; ArrayResize(arr1,arr_size); ArrayResize(arr2,arr_size); ArrayResize(arr3,arr_size); srand(GetTickCount()); // initialize a random number generator //--- fill the arrays with pseudorandom integers for(int i=0;i<arr_size;i++) { arr1[i]=rand(); // #2 arr2[i]=rand(); arr3[i]=rand(); } //--- create nodes CiUnrollDoubleNode *p_udNodes[3]; // #3 p_udNodes[0]=new CiUnrollDoubleNode(arr1); p_udNodes[1]=new CiUnrollDoubleNode(arr2); p_udNodes[2]=new CiUnrollDoubleNode(arr3); //--- links for(int j=0;j<(ArraySize(p_udNodes)-1);j++) { p_udNodes[j].SetNextNode(p_udNodes[j+1]); // #4 p_udNodes[j+1].SetPrevNode(p_udNodes[j]); // #5 } //--- check values for(int i=0;i<ArraySize(p_udNodes);i++) { int val=p_udNodes[i].GetVal(); // #6 Print("Node #"+IntegerToString(i+1)+ // #7 " value = "+IntegerToString(val)); } //--- check array values for(int i=0;i<ArraySize(p_udNodes);i++) { int t_arr[]; // destination array bool isCopied=p_udNodes[i].GetArrVal(t_arr); // #8 if(isCopied) { string arr_str=NULL; for(int n=0;n<ArraySize(t_arr);n++) arr_str+=IntegerToString(t_arr[n])+", "; int end_of_string=StringLen(arr_str); arr_str=StringSubstr(arr_str,0,end_of_string-2); Print("Node #"+IntegerToString(i+1)+ // #9 " array values = "+arr_str); } } //--- check next-nodes for(int j=0;j<(ArraySize(p_udNodes)-1);j++) { int t_arr[]; // destination array CiUnrollDoubleNode *p_udNode_next=p_udNodes[j].GetNextNode(); // #10 bool isCopied=p_udNode_next.GetArrVal(t_arr); if(isCopied) { string arr_str=NULL; for(int n=0;n<ArraySize(t_arr);n++) arr_str+=IntegerToString(t_arr[n])+", "; int end_of_string=StringLen(arr_str); arr_str=StringSubstr(arr_str,0,end_of_string-2); Print("Next-Node #"+IntegerToString(j+1)+ " array values = "+arr_str); } } //--- check prev-nodes for(int j=0;j<(ArraySize(p_udNodes)-1);j++) { int t_arr[]; // destination array CiUnrollDoubleNode *p_udNode_prev=p_udNodes[j+1].GetPrevNode(); // #11 bool isCopied=p_udNode_prev.GetArrVal(t_arr); if(isCopied) { string arr_str=NULL; for(int n=0;n<ArraySize(t_arr);n++) arr_str+=IntegerToString(t_arr[n])+", "; int end_of_string=StringLen(arr_str); arr_str=StringSubstr(arr_str,0,end_of_string-2); Print("Prev-Node #"+IntegerToString(j+2)+ " array values = "+arr_str); } } //--- delete nodes for(int i=0;i<ArraySize(p_udNodes);i++) delete p_udNodes[i]; // #12 }
Kod miktarı biraz daha büyüdü. Bu, her düğüm için bir dizi oluşturmamız ve doldurmamız gerektiğiyle bağlantılıdır.
Veri dizileriyle çalışmak, #1 dizesindede başlar. Temelde, ele alınan önceki düğümlerde sahip olduğumuza benzer. Sadece tüm dizi için her düğümün veri değerlerini yazdırmamız gerekiyor (örneğin, dize #9).
Elimdeki bu:
IN 0 00:09:13 test_nodes (EURUSD.m,H4) Node #1 value = 15 NF 0 00:09:13 test_nodes (EURUSD.m,H4) Node #2 value = 15 CI 0 00:09:13 test_nodes (EURUSD.m,H4) Node #3 value = 15 FQ 0 00:09:13 test_nodes (EURUSD.m,H4) Node #1 array values = 31784, 4837, 25797, 29079, 4223, 27234, 2155, 32351, 12010, 10353, 10391, 22245, 27895, 3918, 12069 EG 0 00:09:13 test_nodes (EURUSD.m,H4) Node #2 array values = 1809, 18553, 23224, 20208, 10191, 4833, 25959, 2761, 7291, 23254, 29865, 23938, 7585, 20880, 25756 MK 0 00:09:13 test_nodes (EURUSD.m,H4) Node #3 array values = 18100, 26358, 31020, 23881, 11256, 24798, 31481, 14567, 13032, 4701, 21665, 1434, 1622, 16377, 25778 RP 0 00:09:13 test_nodes (EURUSD.m,H4) Next-Node #1 array values = 1809, 18553, 23224, 20208, 10191, 4833, 25959, 2761, 7291, 23254, 29865, 23938, 7585, 20880, 25756 JD 0 00:09:13 test_nodes (EURUSD.m,H4) Next-Node #2 array values = 18100, 26358, 31020, 23881, 11256, 24798, 31481, 14567, 13032, 4701, 21665, 1434, 1622, 16377, 25778 EH 0 00:09:13 test_nodes (EURUSD.m,H4) Prev-Node #2 array values = 31784, 4837, 25797, 29079, 4223, 27234, 2155, 32351, 12010, 10353, 10391, 22245, 27895, 3918, 12069 NN 0 00:09:13 test_nodes (EURUSD.m,H4) Prev-Node #3 array values = 1809, 18553, 23224, 20208, 10191, 4833, 25959, 2761, 7291, 23254, 29865, 23938, 7585, 20880, 25756
Düğümlerle çalışmanın altına bir çizgi çekmemizi ve doğrudan farklı listelerin sınıf tanımlarına geçmemizi öneriyorum. 1-3 arasındaki örnekler test_nodes.mq5 script dosyasında bulunabilir.
2.4 Tek Bağlantılı Liste
Ana liste işlem gruplarından tek bağlantılı bir listenin sınıf modelini yapmanın zamanı geldi (Şekil 9).
Şek. 9 CiSingleList sınıf modeli
CiSingleList sınıfının CiSingleNode türü düğümü kullandığı kolayca görülebilir. Sınıflar arasındaki ilişki türlerinden bahsederken şunu söyleyebiliriz:
- CiSingleList sınıfı, CiSingleNode sınıfını (kompozisyon) içerir;
- CiSingleList sınıfı, CiSingleNode sınıf yöntemlerini (bağımlılık) kullanır.
Yukarıdaki ilişkilerin gösterimi Şekil 10'da verilmiştir.
Şekil 10 CiSingleList sınıfı ve CiSingleNode sınıfı arasındaki ilişki türleri
Yeni bir sınıf oluşturalım - CiSingleList. İleriye bakıldığında, makalede kullanılan diğer tüm liste sınıfları bu sınıfa dayalı olacaktır. Bu yüzden bu kadar 'zengin'.
//+------------------------------------------------------------------+ //| CiSingleList class | //+------------------------------------------------------------------+ class CiSingleList { protected: CiSingleNode *m_head; // head CiSingleNode *m_tail; // tail uint m_size; // number of nodes in the list public: //--- constructor and destructor void CiSingleList(); // default constructor void CiSingleList(int _node_val); // parameterized constructor void ~CiSingleList(); // destructor //--- adding nodes void AddFront(int _node_val); // add a new node to the beginning of the list void AddRear(int _node_val); // add a new node to the end of the list virtual void AddFront(int &_node_arr[]){TRACE_CALL(_t_flag)}; // add a new node to the beginning of the list virtual void AddRear(int &_node_arr[]){TRACE_CALL(_t_flag)}; // add a new node to the end of the list //--- deleting nodes int RemoveFront(void); // delete the head node int RemoveRear(void); // delete the node from the end of the list void DeleteNodeByIndex(const uint _idx); // delete the ith node from the list //--- checking virtual bool Find(const int _node_val) const; // find the required value bool IsEmpty(void) const; // check the list for being empty virtual int GetValByIndex(const uint _idx) const; // value of the ith node in the list virtual CiSingleNode *GetNodeByIndex(const uint _idx) const; // get the ith node in the list virtual bool SetNodeByIndex(CiSingleNode *_new_node,const uint _idx); // insert the new ith node in the list CiSingleNode *GetHeadNode(void) const; // get the head node CiSingleNode *GetTailNode(void) const; // get the tail node virtual uint Size(void) const; // list size //--- service virtual void PrintList(string _caption=NULL); // print the list virtual bool CopyByValue(const CiSingleList &_sList); // copy the list by values virtual void BubbleSort(void); // bubble sorting //---templates template<typename dPointer> bool CheckDynamicPointer(dPointer &_p); // template for checking a dynamic pointer template<typename dPointer> bool DeleteDynamicPointer(dPointer &_p); // template for deleting a dynamic pointer protected: void operator=(const CiSingleList &_sList) const; // assignment operator void CiSingleList(const CiSingleList &_sList); // copy constructor virtual bool AddToEmpty(int _node_val); // add a new node to an empty list virtual void addFront(int _node_val); // add a new "native" node to the beginning of the list virtual void addRear(int _node_val); // add a new "native" node to the end of the list virtual int removeFront(void); // delete the "native" head node virtual int removeRear(void); // delete the "native" node from the end of the list virtual void deleteNodeByIndex(const uint _idx); // delete the "native" ith node from the list virtual CiSingleNode *newNode(int _val); // new "native" node virtual void CalcSize(void) const; // calculate the list size };
Sınıf yöntemlerinin tam tanımı CiSingleList.mqh içinde verilmiştir.
Bu sınıfı geliştirmeye yeni başladığımda, sadece 3 veri üyesi ve birkaç yöntem vardı. Ancak bu sınıf diğer sınıflar için temel teşkil ettiğinden, birkaç sanal üye işlevi eklemek zorunda kaldım. Bu yöntemleri ayrıntılı olarak anlatmayacağım. Bu tek bağlantılı liste sınıfını kullanmanın bir örneği, test_sList.mq5 script dosyasında bulunabilir.
İzleme bayrağı olmadan çalıştırılırsa günlükte aşağıdaki girişler görünür:
KG 0 12:58:32 test_sList (EURUSD,H1) =======List #1======= PF 0 12:58:32 test_sList (EURUSD,H1) Node #1, val=14 RL 0 12:58:32 test_sList (EURUSD,H1) Node #2, val=666 MD 0 12:58:32 test_sList (EURUSD,H1) Node #3, val=13 DM 0 12:58:32 test_sList (EURUSD,H1) Node #4, val=11 QE 0 12:58:32 test_sList (EURUSD,H1) KN 0 12:58:32 test_sList (EURUSD,H1) LR 0 12:58:32 test_sList (EURUSD,H1) =======List #2======= RE 0 12:58:32 test_sList (EURUSD,H1) Node #1, val=14 DQ 0 12:58:32 test_sList (EURUSD,H1) Node #2, val=666 GK 0 12:58:32 test_sList (EURUSD,H1) Node #3, val=13 FP 0 12:58:32 test_sList (EURUSD,H1) Node #4, val=11 KF 0 12:58:32 test_sList (EURUSD,H1) MK 0 12:58:32 test_sList (EURUSD,H1) PR 0 12:58:32 test_sList (EURUSD,H1) =======renewed List #2======= GK 0 12:58:32 test_sList (EURUSD,H1) Node #1, val=11 JP 0 12:58:32 test_sList (EURUSD,H1) Node #2, val=13 JI 0 12:58:32 test_sList (EURUSD,H1) Node #3, val=14 CF 0 12:58:32 test_sList (EURUSD,H1) Node #4, val=34 QL 0 12:58:32 test_sList (EURUSD,H1) Node #5, val=35 OE 0 12:58:32 test_sList (EURUSD,H1) Node #6, val=36 MR 0 12:58:32 test_sList (EURUSD,H1) Node #7, val=37 KK 0 12:58:32 test_sList (EURUSD,H1) Node #8, val=38 MS 0 12:58:32 test_sList (EURUSD,H1) Node #9, val=666 OF 0 12:58:32 test_sList (EURUSD,H1) QK 0 12:58:32 test_sList (EURUSD,H1)
Script dosyası, tek bağlantılı 2 listeyi doldurdu ve ardından ikinci listeyi genişletip sıraladı.
2.5 Çift Bağlantılı Liste
Şimdi önceki türün listesine dayalı olarak çift bağlantılı bir liste oluşturmaya çalışalım. Çift bağlantılı bir listenin sınıf modelinin gösterimi Şekil 11'de verilmiştir:
Şek. 11 CDoubleList sınıf modeli
Veri üyeleri hiç yokken, alt sınıf çok daha az yöntem içerir. Aşağıda CDoubleList sınıf tanımı verilmiştir.
//+------------------------------------------------------------------+ //| CDoubleList class | //+------------------------------------------------------------------+ class CDoubleList : public CiSingleList { public: void CDoubleList(void); // default constructor void CDoubleList(int _node_val); // parameterized constructor void ~CDoubleList(void){}; // destructor virtual bool SetNodeByIndex(CiSingleNode *_new_node,const uint _idx); // insert the new ith node in the list protected: virtual bool AddToEmpty(int _node_val); // add a node to an empty list virtual void addFront(int _node_val); // add a new "native" node to the beginning of the list virtual void addRear(int _node_val); // add a new "native" node to the end of the list virtual int removeFront(void); // delete the "native" head node virtual int removeRear(void); // delete the "native" tail node virtual void deleteNodeByIndex(const uint _idx); // delete the "native" ith node from the list virtual CiSingleNode *newNode(int _node_val); // new "native" node };
CDoubleList sınıf yöntemlerinin tam açıklaması CDoubleList.mqh içinde verilmiştir.
Genel konuşursak, sanal işlevler burada yalnızca, tek bağlantılı listelerde mevcut olmayan bir önceki düğüme yönelik işaretçinin ihtiyaçlarına hizmet etmek için kullanılır.
CDoubleList türünün listesini kullanma örneği test_dList.mq5 script dosyasında bulunabilir. Bu liste türüyle ilgili tüm yaygın liste işlemlerini gösterir. Script dosyası kodu bir tane garip dize içerir:
CiSingleNode *_new_node=new CDoubleNode(666); // create a new node of CDoubleNode type
Hata yoktur çünkü temel sınıf işaretçisinin alt sınıfın bir nesnesini tanımladığı durumlarda bu tür yapı oldukça kabul edilebilirdir. Bu, mirasın avantajlarından biridir.
MQL5'te ve С++'da, temel sınıfa yönelik işaretçi, o temel sınıftan türetilen alt sınıfın nesnesine işaret edebilir. Ama tersi geçersizdir.
Dizeyi aşağıdaki gibi yazarsanız:
CDoubleNode*_new_node=new CiSingleNode(666);
derleyici bir hata veya uyarı bildirmez ancak program bu dizeye ulaşana kadar çalışır. Bu durumda, işaretçiler tarafından atıfta bulunulan türlerin yanlış çevrimi hakkında bir mesaj göreceksiniz. Geç bağlama mekanizması yalnızca program çalışırken devreye girdiğinden, sınıflar arasındaki ilişkilerin hiyerarşisini dikkatlice düşünmemiz gerekir.
Script dosyasını çalıştırdıktan sonra günlük aşağıdaki girişleri içerecektir:
DN 0 13:10:57 test_dList (EURUSD,H1) =======List #1======= GO 0 13:10:57 test_dList (EURUSD,H1) Node #1, val=14 IE 0 13:10:57 test_dList (EURUSD,H1) Node #2, val=666 FM 0 13:10:57 test_dList (EURUSD,H1) Node #3, val=13 KD 0 13:10:57 test_dList (EURUSD,H1) Node #4, val=11 JL 0 13:10:57 test_dList (EURUSD,H1) DG 0 13:10:57 test_dList (EURUSD,H1) CK 0 13:10:57 test_dList (EURUSD,H1) =======List #2======= IL 0 13:10:57 test_dList (EURUSD,H1) Node #1, val=14 KH 0 13:10:57 test_dList (EURUSD,H1) Node #2, val=666 PR 0 13:10:57 test_dList (EURUSD,H1) Node #3, val=13 MI 0 13:10:57 test_dList (EURUSD,H1) Node #4, val=11 DO 0 13:10:57 test_dList (EURUSD,H1) FR 0 13:10:57 test_dList (EURUSD,H1) GK 0 13:10:57 test_dList (EURUSD,H1) =======renewed List #2======= PR 0 13:10:57 test_dList (EURUSD,H1) Node #1, val=11 QI 0 13:10:57 test_dList (EURUSD,H1) Node #2, val=13 QP 0 13:10:57 test_dList (EURUSD,H1) Node #3, val=14 LO 0 13:10:57 test_dList (EURUSD,H1) Node #4, val=34 JE 0 13:10:57 test_dList (EURUSD,H1) Node #5, val=35 HL 0 13:10:57 test_dList (EURUSD,H1) Node #6, val=36 FK 0 13:10:57 test_dList (EURUSD,H1) Node #7, val=37 DR 0 13:10:57 test_dList (EURUSD,H1) Node #8, val=38 FJ 0 13:10:57 test_dList (EURUSD,H1) Node #9, val=666 HO 0 13:10:57 test_dList (EURUSD,H1) JR 0 13:10:57 test_dList (EURUSD,H1)
Tek bağlantılı listede olduğu gibi, script dosyası ilk (çift bağlantılı) listeyi doldurdu, kopyaladı ve ikinci listeye geçirdi. Ardından ikinci listedeki düğüm sayısını artırdı, listeyi sıraladı ve yazdırdı.
2.6 Teşhir Edilmiş Çift Bağlantılı Liste
Bu liste türü, yalnızca bir değeri değil, tüm bir diziyi depolamanıza izin verdiği için uygundur.
CiUnrollDoubleList türü listenin temelini atalım (Şekil 12).
Şek. 12 CiUnrollDoubleList sınıf modeli
Burada bir veri dizisiyle ilgileneceğimiz için CiSingleList dolaylı temel sınıfında tanımlanan yöntemleri yeniden tanımlamamız gerekecek.
CiUnrollDoubleList sınıf tanımı aşağıdadır.
//+------------------------------------------------------------------+ //| CiUnrollDoubleList class | //+------------------------------------------------------------------+ class CiUnrollDoubleList : public CDoubleList { public: void CiUnrollDoubleList(void); // default constructor void CiUnrollDoubleList(int &_node_arr[]); // parameterized constructor void ~CiUnrollDoubleList(void){TRACE_CALL(_t_flag)}; // destructor //--- virtual void AddFront(int &_node_arr[]); // add a new node to the beginning of the list virtual void AddRear(int &_node_arr[]); // add a new node to the end of the list virtual bool CopyByValue(const CiSingleList &_udList); // copy by values virtual void PrintList(string _caption=NULL); // print the list virtual void BubbleSort(void); // bubble sorting protected: virtual bool AddToEmpty(int &_node_arr[]); // add a node to an empty list virtual void addFront(int &_node_arr[]); // add a new "native" node to the beginning of the list virtual void addRear(int &_node_arr[]); // add a new "native" node to the end of the list virtual int removeFront(void); // delete the "native" node from the beginning of the list virtual int removeRear(void); // delete the "native" node from the end of the list virtual void deleteNodeByIndex(const uint _idx); // delete the "native" ith node from the list virtual CiSingleNode *newNode(int &_node_arr[]); // new "native" node };
Sınıf yöntemlerinin tam tanımı CiUnrollDoubleList.mqh içinde verilmiştir.
Sınıf yöntemlerinin çalışmasını kontrol etmek için test_UdList.mq5 script dosyasını çalıştıralım. Burada, düğüm işlemleri önceki script dosyalarında kullanılanlara benzerdir. Sıralama ve yazdırma yöntemleri hakkında belki birkaç kelam etmeliyiz. Sıralama yöntemi, düğümleri öğe sayısına göre sıralar, böylece en küçük boyuttaki değerler dizisini içeren düğüm listenin başında olur.
Yazdırma yöntemi, belirli bir düğümde bulunan bir dizi dizesi değeri yazdırır.
Script dosyasını çalıştırdıktan sonra günlükte aşağıdaki girişler olacaktır:
II 0 13:22:23 test_UdList (EURUSD,H1) =======List #1======= FN 0 13:22:23 test_UdList (EURUSD,H1) List node #1, array: 55, 12, 1, 2, 11, 114, 33, 113, 14, 15, 16, 17, 18, 19, 20 OO 0 13:22:23 test_UdList (EURUSD,H1) List node #2, array: 11, 12, 13, 14, 15, 16, 17, 18, 19, 20 GG 0 13:22:23 test_UdList (EURUSD,H1) GP 0 13:22:23 test_UdList (EURUSD,H1) GR 0 13:22:23 test_UdList (EURUSD,H1) =======List #2 before sorting======= JO 0 13:22:23 test_UdList (EURUSD,H1) List node #1, array: 55, 12, 1, 2, 11, 114, 33, 113, 14, 15, 16, 17, 18, 19, 20 CH 0 13:22:23 test_UdList (EURUSD,H1) List node #2, array: 11, 12, 13, 14, 15, 16, 17, 18, 19, 20 CF 0 13:22:23 test_UdList (EURUSD,H1) List node #3, array: -89, -131, -141, -139, -129, -25, -105, -24, -122, -120, -118, -116, -114, -112, -110 GD 0 13:22:23 test_UdList (EURUSD,H1) GQ 0 13:22:23 test_UdList (EURUSD,H1) LJ 0 13:22:23 test_UdList (EURUSD,H1) =======List #2 after sorting======= FN 0 13:22:23 test_UdList (EURUSD,H1) List node #1, array: 11, 12, 13, 14, 15, 16, 17, 18, 19, 20 CJ 0 13:22:23 test_UdList (EURUSD,H1) List node #2, array: 55, 12, 1, 2, 11, 114, 33, 113, 14, 15, 16, 17, 18, 19, 20 II 0 13:22:23 test_UdList (EURUSD,H1) List node #3, array: -89, -131, -141, -139, -129, -25, -105, -24, -122, -120, -118, -116, -114, -112, -110 MD 0 13:22:23 test_UdList (EURUSD,H1) MQ 0 13:22:23 test_UdList (EURUSD,H1)
Görüldüğü gibi udList2 listesi sıralandıktan sonra en küçük diziye sahip düğümden başlayarak en büyük diziyi içeren düğüme kadar yazdırılmıştır.
2.7 Dairesel Çift Bağlantılı Liste
Bu makalede doğrusal olmayan listeler ele alınmasa da, onlarla da çalışmamızı öneririm. Düğümleri dairesel olarak nasıl bağlayabileceğiniz yukarıda zaten gösterilmiştir (Şekil 3).
CiCircleDoubleList sınıfı (Şekil 13) için bir model oluşturalım. Bu sınıf, CDoubleList sınıfının alt sınıfı olacaktır.
Şek. 13 CiCircleDoubleList sınıf modeli
Bu listedeki düğümlerin belirli bir karaktere sahip olması nedeniyle (baş ve kuyruk bağlantılıdır), CiSingleList kaynak temel sınıfının neredeyse tüm yöntemlerinin sanal yapılması gerekecektir.
//+------------------------------------------------------------------+ //| CiCircleDoubleList class | //+------------------------------------------------------------------+ class CiCircleDoubleList : public CDoubleList { public: void CiCircleDoubleList(void); // default constructor void CiCircleDoubleList(int _node_val); // parameterized constructor void ~CiCircleDoubleList(void){TRACE_CALL(_t_flag)}; // destructor //--- virtual uint Size(void) const; // list size virtual bool SetNodeByIndex(CiSingleNode *_new_node,const uint _idx); // insert the new ith node in the list virtual int GetValByIndex(const uint _idx) const; // value of the ith node in the list virtual CiSingleNode *GetNodeByIndex(const uint _idx) const; // get the ith node in the list virtual bool Find(const int _node_val) const; // find the required value virtual bool CopyByValue(const CiSingleList &_sList); // copy the list by values protected: virtual void addFront(int _node_val); // add a new "native" node to the beginning of the list virtual void addRear(int _node_val); // add a new "native" node to the end of the list virtual int removeFront(void); // delete the "native" head node virtual int removeRear(void); // delete the "native" tail node virtual void deleteNodeByIndex(const uint _idx); // delete the "native" ith node from the list protected: void CalcSize(void) const; // calculate the list size void LinkHeadTail(void); // link head to tail };
Tam sınıf açıklaması CiCircleDoubleList.mqh içinde verilmektedir.
Sınıfın bazı yöntemlerini ele alalım. CiCircleDoubleList::LinkHeadTail() yöntemi, kuyruk düğümünü baş düğüme bağlar. Yeni bir kuyruk veya baş olduğunda ve önceki bağlantı kaybolduğunda çağrılmalıdır.
//+------------------------------------------------------------------+ //| Linking head to tail | //+------------------------------------------------------------------+ void CiCircleDoubleList::LinkHeadTail(void) { TRACE_CALL(_t_flag) this.m_head.SetPrevNode(this.m_tail); // link head to tail this.m_tail.SetNextNode(this.m_head); // link tail to head }
Dairesel tek bağlantılı bir listeyle uğraşıyor olsaydık, bu yöntemin nasıl olacağını bir düşünün.
Örneğin CiCircleDoubleList::addFront() yöntemini düşünün.
//+------------------------------------------------------------------+ //| New "native" node to the beginning of the list | //+------------------------------------------------------------------+ void CiCircleDoubleList::addFront(int _node_val) { TRACE_CALL(_t_flag) CDoubleList::addFront(_node_val); // call a similar method of the base class this.LinkHeadTail(); // link head and tail }
Yöntem gövdesinde, CDoubleList temel sınıfının benzer bir yönteminin çağrıldığını görebilirsiniz. Bu noktada, tek bir şeyden dolayı olmasaydı yöntem işlemini tamamlayabilirdik (burada olduğu gibi yönteme temelde ihtiyaç yoktur). Baş ve kuyruk arasındaki bağlantı kaybolur ve liste onsuz dairesel olarak bağlanamaz. Bu yüzden baş ve kuyruğu birbirine bağlama yöntemini çağırmamız gerekiyor.
Dairesel çift bağlantılı liste ile çalışmak test_UdList.mq5 script dosyasında kontrol edilir.
Görevler ve hedefler açısından, kullanılan diğer yöntemler önceki örneklerle aynıdır.
Sonuç olarak, günlük aşağıdaki girişleri içerir:
PR 0 13:34:29 test_CdList (EURUSD,H1) =======List #1======= QS 0 13:34:29 test_CdList (EURUSD,H1) Node #1, val=14 QI 0 13:34:29 test_CdList (EURUSD,H1) Node #2, val=666 LQ 0 13:34:29 test_CdList (EURUSD,H1) Node #3, val=13 OH 0 13:34:29 test_CdList (EURUSD,H1) Node #4, val=11 DP 0 13:34:29 test_CdList (EURUSD,H1) DK 0 13:34:29 test_CdList (EURUSD,H1) DI 0 13:34:29 test_CdList (EURUSD,H1) =======List #2 before sorting======= MS 0 13:34:29 test_CdList (EURUSD,H1) Node #1, val=38 IJ 0 13:34:29 test_CdList (EURUSD,H1) Node #2, val=37 IQ 0 13:34:29 test_CdList (EURUSD,H1) Node #3, val=36 EH 0 13:34:29 test_CdList (EURUSD,H1) Node #4, val=35 EO 0 13:34:29 test_CdList (EURUSD,H1) Node #5, val=34 FF 0 13:34:29 test_CdList (EURUSD,H1) Node #6, val=14 DN 0 13:34:29 test_CdList (EURUSD,H1) Node #7, val=666 GD 0 13:34:29 test_CdList (EURUSD,H1) Node #8, val=13 JK 0 13:34:29 test_CdList (EURUSD,H1) Node #9, val=11 JM 0 13:34:29 test_CdList (EURUSD,H1) JH 0 13:34:29 test_CdList (EURUSD,H1) MS 0 13:34:29 test_CdList (EURUSD,H1) =======List #2 after sorting======= LE 0 13:34:29 test_CdList (EURUSD,H1) Node #1, val=11 KL 0 13:34:29 test_CdList (EURUSD,H1) Node #2, val=13 QS 0 13:34:29 test_CdList (EURUSD,H1) Node #3, val=14 NJ 0 13:34:29 test_CdList (EURUSD,H1) Node #4, val=34 NQ 0 13:34:29 test_CdList (EURUSD,H1) Node #5, val=35 NH 0 13:34:29 test_CdList (EURUSD,H1) Node #6, val=36 NO 0 13:34:29 test_CdList (EURUSD,H1) Node #7, val=37 NF 0 13:34:29 test_CdList (EURUSD,H1) Node #8, val=38 JN 0 13:34:29 test_CdList (EURUSD,H1) Node #9, val=666 RJ 0 13:34:29 test_CdList (EURUSD,H1) RE 0 13:34:29 test_CdList (EURUSD,H1)
Bu nedenle, tanıtılan liste sınıfları arasındaki son kalıtım diyagramı aşağıdaki gibidir (Şekil 14).
Tüm sınıfların miras yoluyla ilişkilendirilmesi gerekip gerekmediğinden emin değilim ama her şeyi olduğu gibi bırakmaya karar verdim.
Şek. 14 Liste sınıfları arasında devralma
Özel listelerin tanımını ele alan makalenin bu bölümünün altınaltını çizerken, doğrusal olmayan listeler grubuna, çoklu bağlantılı listelere ve diğerlerine çok az değindiğimizi belirtmek isterim. İlgili bilgileri topladıkça ve bu tür dinamik veri yapıları ile daha fazla deneyim kazandıkça, başka bir makale yazmaya çalışacağım.
3. MQL5 Standart Kitaplığındaki Listeler
Standart Kitaplıkta bulunan liste sınıfına bir göz atalım (Şekil 15).
Veri Sınıflarına aittir.
Şek. 15 CList sınıf modeli
İlginçtir ki, CList, CObject sınıfının soyundan gelmektedir. Yani liste, bir düğüm olan sınıfın verilerini ve yöntemlerini devralır.
Liste sınıfı, etkileyici bir dizi yöntem içerir. Dürüst olmak gerekirse Standart Kitaplık'da bu kadar büyük bir sınıf bulmayı beklemiyordum.
CList sınıfı 8 veri üyesine sahiptir. Birkaç şeye dikkat çekmek istiyorum. Sınıf öznitelikleri, geçerli (int m_curr_idx) düğümünün indeksini ve geçerli (CObject* m_curr_node) düğümünün işaretçisini içerir. Listenin "akıllı" olduğu söylenebilir; kontrolün lokalize olduğu yeri gösterebilir. Dahası, bellek yönetim mekanizması (bir düğümü fiziksel olarak silebilir veya basitçe listeden hariç tutabiliriz), sıralı liste bayrağı ve sıralama modu içerir.
Yöntemlerden bahsetmişken, CList sınıfının tüm yöntemleri aşağıdaki gruplara ayrılır:
- Özellikler;
- Oluşturma yöntemleri;
- Ekleme yöntemleri;
- Silme yöntemleri;
- Navigasyon;
- Talimat yöntemleri;
- Karşılaştırma yöntemleri;
- Arama yöntemleri;
- Giriş/Çıkış.
Her zamanki gibi, standart bir oluşturucu ve yok edici vardır.
İlki tüm işaretçileri boşaltır (NULL). Bellek yönetimi bayrak durumu silmeye ayarlı. Yeni liste sıralanmamış olacaktır.
Yıkıcı, gövdesinde düğüm listesini boşaltmak için yalnızca Clear() yöntemini çağırır. Listenin varlığının sonu, mutlaka öğelerinin (düğümlerin) "ölmesini" gerektirmez. Böylece, liste öğeleri silinirken ayarlanan bellek yönetimi bayrağı, sınıf ilişkisini kompozisyondan toplamaya dönüştürür.
Bu bayrağı set- ve get-methods FreeMode() kullanarak işleyebiliriz.
Sınıfta listeyi genişletmenize izin veren iki yöntem vardır: Add() ve Insert(). İlki, makalenin ilk bölümünde kullanılan AddRear() yöntemine benzer. İkinci yöntem, SetNodeByIndex() yöntemine benzer.
Küçük bir örnekle başlayalım. Önce, CObject arabirim sınıfının soyundan gelen bir CNodeInt düğüm sınıfı oluşturmamız gerekiyor. Tamsayı türünün değerini depolayacaktır.
//+------------------------------------------------------------------+ //| CNodeInt class | //+------------------------------------------------------------------+ class CNodeInt : public CObject { private: int m_val; // node data public: void CNodeInt(void){this.m_val=WRONG_VALUE;}; // default constructor void CNodeInt(int _val); // parameterized constructor void ~CNodeInt(void){}; // destructor int GetVal(void){return this.m_val;}; // get-method for node data void SetVal(int _val){this.m_val=_val;}; // set-method for node data }; //+------------------------------------------------------------------+ //| Parameterized constructor | //+------------------------------------------------------------------+ void CNodeInt::CNodeInt(int _val):m_val(_val) { };
test_MQL5_List.mq5 script dosyasındaki CList listesiyle çalışacağız.
Örnek 1, liste ve düğümlerin dinamik bir şekilde oluşturulmasını gösterir. Liste daha sonra düğümlerle doldurulur ve liste silinmeden önce ve sonra ilk düğümün değeri kontrol edilir.
//--- Example 1 (testing memory management) CList *myList=new CList; // myList.FreeMode(false); // reset flag bool _free_mode=myList.FreeMode(); PrintFormat("\nList \"myList\" - memory management flag: %d",_free_mode); CNodeInt *p_new_nodes_int[10]; p_new_nodes_int[0]=NULL; for(int i=0;i<ArraySize(p_new_nodes_int);i++) { p_new_nodes_int[i]=new CNodeInt(rand()); myList.Add(p_new_nodes_int[i]); } PrintFormat("List \"myList\" has as many nodes as: %d",myList.Total()); Print("=======Before deleting \"myList\"======="); PrintFormat("The 1st node value is: %d",p_new_nodes_int[0].GetVal()); delete myList; int val_to_check=WRONG_VALUE; if(CheckPointer(p_new_nodes_int[0])) val_to_check=p_new_nodes_int[0].GetVal(); Print("=======After deleting \"myList\"======="); PrintFormat("The 1st node value is: %d",val_to_check);
Bayrağı sıfırlayan dize derleme dışı bırakılırsa (etkin değil), günlükte aşağıdaki girişleri alırız:
GS 0 14:00:16 test_MQL5_List (EURUSD,H1) EO 0 14:00:16 test_MQL5_List (EURUSD,H1) List "myList" - memory management flag: 1 FR 0 14:00:16 test_MQL5_List (EURUSD,H1) List "myList" has as many nodes as: 10 JH 0 14:00:16 test_MQL5_List (EURUSD,H1) =======Before deleting "myList"======= DO 0 14:00:16 test_MQL5_List (EURUSD,H1) The 1st node value is: 7189 KJ 0 14:00:16 test_MQL5_List (EURUSD,H1) =======After deleting "myList"======= QK 0 14:00:16 test_MQL5_List (EURUSD,H1) The 1st node value is: -1
Lütfen myList listesini dinamik olarak sildikten sonra, içindeki tüm düğümlerin de bellekten silindiğini unutmayın.
Ancak, sıfırlama bayrağı dizesini kaldırırsak
// myList.FreeMode(false); // reset flag
günlüğün çıktısı aşağıdaki gibi olacaktır:
NS 0 14:02:11 test_MQL5_List (EURUSD,H1) CN 0 14:02:11 test_MQL5_List (EURUSD,H1) List "myList" - memory management flag: 0 CS 0 14:02:11 test_MQL5_List (EURUSD,H1) List "myList" has as many nodes as: 10 KH 0 14:02:11 test_MQL5_List (EURUSD,H1) =======Before deleting "myList"======= NL 0 14:02:11 test_MQL5_List (EURUSD,H1) The 1st node value is: 20411 HJ 0 14:02:11 test_MQL5_List (EURUSD,H1) =======After deleting "myList"======= LI 0 14:02:11 test_MQL5_List (EURUSD,H1) The 1st node value is: 20411 QQ 1 14:02:11 test_MQL5_List (EURUSD,H1) 10 undeleted objects left DD 1 14:02:11 test_MQL5_List (EURUSD,H1) 10 objects of type CNodeInt left DL 1 14:02:11 test_MQL5_List (EURUSD,H1) 400 bytes of leaked memory
Baş düğümün, liste silindikten önceki ve sonra olan değerini koruduğunu fark etmek kolaydır. Bu durumda, script dosyası bunları doğru şekilde silmek için kod içermiyorsa, silinmemiş nesneler de kalacaktır.
Şimdi sıralama yöntemiyle çalışmayı deneyelim.
//--- Example 2 (sorting) CList *myList=new CList; CNodeInt *p_new_nodes_int[10]; p_new_nodes_int[0]=NULL; for(int i=0;i<ArraySize(p_new_nodes_int);i++) { p_new_nodes_int[i]=new CNodeInt(rand()); myList.Add(p_new_nodes_int[i]); } PrintFormat("\nList \"myList\" has as many nodes as: %d",myList.Total()); Print("=======List \"myList\" before sorting======="); for(int i=0;i<myList.Total();i++) { CNodeInt *p_node_int=myList.GetNodeAtIndex(i); int node_val=p_node_int.GetVal(); PrintFormat("Node #%d is equal to: %d",i+1,node_val); } myList.Sort(0); Print("\n=======List \"myList\" after sorting======="); for(int i=0;i<myList.Total();i++) { CNodeInt *p_node_int=myList.GetNodeAtIndex(i); int node_val=p_node_int.GetVal(); PrintFormat("Node #%d is equal to: %d",i+1,node_val); } delete myList;
Sonuç olarak, günlük aşağıdaki girişleri içerir:
OR 0 22:47:01 test_MQL5_List (EURUSD,H1) FN 0 22:47:01 test_MQL5_List (EURUSD,H1) List "myList" has as many nodes as: 10 FH 0 22:47:01 test_MQL5_List (EURUSD,H1) =======List "myList" before sorting======= LG 0 22:47:01 test_MQL5_List (EURUSD,H1) Node #1 is equal to: 30511 CO 0 22:47:01 test_MQL5_List (EURUSD,H1) Node #2 is equal to: 17404 GF 0 22:47:01 test_MQL5_List (EURUSD,H1) Node #3 is equal to: 12215 KQ 0 22:47:01 test_MQL5_List (EURUSD,H1) Node #4 is equal to: 31574 NJ 0 22:47:01 test_MQL5_List (EURUSD,H1) Node #5 is equal to: 7285 HP 0 22:47:01 test_MQL5_List (EURUSD,H1) Node #6 is equal to: 23509 IH 0 22:47:01 test_MQL5_List (EURUSD,H1) Node #7 is equal to: 26991 NS 0 22:47:01 test_MQL5_List (EURUSD,H1) Node #8 is equal to: 414 MK 0 22:47:01 test_MQL5_List (EURUSD,H1) Node #9 is equal to: 18824 DR 0 22:47:01 test_MQL5_List (EURUSD,H1) Node #10 is equal to: 1560 OR 0 22:47:01 test_MQL5_List (EURUSD,H1) OM 0 22:47:01 test_MQL5_List (EURUSD,H1) =======List "myList" after sorting======= QM 0 22:47:01 test_MQL5_List (EURUSD,H1) Node #1 is equal to: 26991 RE 0 22:47:01 test_MQL5_List (EURUSD,H1) Node #2 is equal to: 23509 ML 0 22:47:01 test_MQL5_List (EURUSD,H1) Node #3 is equal to: 18824 DD 0 22:47:01 test_MQL5_List (EURUSD,H1) Node #4 is equal to: 414 LL 0 22:47:01 test_MQL5_List (EURUSD,H1) Node #5 is equal to: 1560 IG 0 22:47:01 test_MQL5_List (EURUSD,H1) Node #6 is equal to: 17404 PN 0 22:47:01 test_MQL5_List (EURUSD,H1) Node #7 is equal to: 30511 II 0 22:47:01 test_MQL5_List (EURUSD,H1) Node #8 is equal to: 31574 OQ 0 22:47:01 test_MQL5_List (EURUSD,H1) Node #9 is equal to: 12215 JH 0 22:47:01 test_MQL5_List (EURUSD,H1) Node #10 is equal to: 7285
Herhangi bir sıralama yapılmış olsa bile, sıralama tekniği benim için bir sır olarak kaldı. Nedenini açıklayacağım. Çağrı sırası ile ilgili fazla ayrıntıya girmeden, CList::Sort() yöntemi, bir temel sınıfta herhangi bir şekilde uygulanmayan CObject::Compare() sanal yöntemini çağırır. Bu nedenle, programcı bir sıralama yönteminin uygulanmasıyla kendi başına uğraşmak zorundadır.
Ve şimdi, Total() yöntemi hakkında birkaç kelam. m_data_total veri üyesinin sorumlu olduğu öğelerin (düğümlerin) sayısını verir. Çok kısa ve öz bir yöntemdir. Bu uygulamada öğe sayısı, daha önce önerdiğimden çok daha hızlı olacaktır. Gerçekten de, neden her zaman listeden geçer ve düğümleri sayar, oysa listedeki tam düğüm sayısı düğüm eklenirken veya silinirken ayarlanabilir.
Örnek 3, CList ve CiSingleList türündeki listelerin doldurulma hızını karşılaştırır ve her bir listenin boyutunu alma süresini hesaplar.
//--- Example 3 (nodes number) int iterations=1e7; // 10 million iterations //--- the new CList CList *p_mql_List=new CList; uint start=GetTickCount(); // starting value for(int i=0;i<iterations;i++) { CNodeInt *p_node_int=new CNodeInt(rand()); p_mql_List.Add(p_node_int); } uint time=GetTickCount()-start; // time spent, msec Print("\n=======the CList type list======="); PrintFormat("Filling the list of %.3e nodes has taken %d msec",iterations,time); //--- get the size start=GetTickCount(); int list_size=p_mql_List.Total(); time=GetTickCount()-start; PrintFormat("Getting the size of the list has taken %d msec",time); delete p_mql_List; //--- the new CiSingleList CiSingleList *p_sList=new CiSingleList; start=GetTickCount(); // starting value for(int i=0;i<iterations;i++) p_sList.AddRear(rand()); time=GetTickCount()-start; // time spent, msec Print("\n=======the CiSingleList type list======="); PrintFormat("Filling the list of %.3e nodes has taken %d msec",iterations,time); //--- get the size start=GetTickCount(); list_size=(int)p_sList.Size(); time=GetTickCount()-start; PrintFormat("Getting the size of the list has taken %d msec",time); delete p_sList;
Günlükte şu var:
KO 0 22:48:24 test_MQL5_List (EURUSD,H1) CK 0 22:48:24 test_MQL5_List (EURUSD,H1) =======the CList type list======= JL 0 22:48:24 test_MQL5_List (EURUSD,H1) Filling the list of 1.000e+007 nodes has taken 2606 msec RO 0 22:48:24 test_MQL5_List (EURUSD,H1) Getting the size of the list has taken 0 msec LF 0 22:48:29 test_MQL5_List (EURUSD,H1) EL 0 22:48:29 test_MQL5_List (EURUSD,H1) =======the CiSingleList type list======= KK 0 22:48:29 test_MQL5_List (EURUSD,H1) Filling the list of 1.000e+007 nodes has taken 2356 msec NF 0 22:48:29 test_MQL5_List (EURUSD,H1) Getting the size of the list has taken 359 msec
Boyutu alma yöntemi, CList listesinde anında çalışır. Bu arada, listeye düğüm eklemek de oldukça hızlı.
Bir sonraki blokta (Örnek 4), listenin bir veri kapsayıcı olarak en önemli dezavantajlarından birine, öğelere erişim hızına dikkat etmenizi öneriyorum. Mesele şu ki, liste öğelerine doğrusal olarak erişiliyor. CList sınıfında, bunlara ikili bir şekilde erişilir, bu da algoritma zahmetini biraz azaltır.
Doğrusal olarak arama yaparken, zahmet O(N) olur. İkili bir şekilde uygulanan bir arama, log2(N)'nin zahmetli olmasına neden olur.
Bu, bir veri kümesinin öğelerine erişim kodunun bir örneğidir:
//--- Example 4 (speed of accessing the node) const uint Iter_arr[]={1e3,3e3,6e3,9e3,1e4,3e4,6e4,9e4,1e5,3e5,6e5}; for(uint i=0;i<ArraySize(Iter_arr);i++) { const uint cur_iterations=Iter_arr[i]; // iterations number uint randArr[]; // array of random numbers uint idxArr[]; // array of indexes //--- set the arrays size ArrayResize(randArr,cur_iterations); ArrayResize(idxArr,cur_iterations); CRandom myRand; // random number generator //--- fill the array of random numbers for(uint t=0;t<cur_iterations;t++) randArr[t]=myRand.int32(); //--- fill the array of indexes with random numbers (from 0 to 10 million) int iter_log10=(int)log10(cur_iterations); for(uint r=0;r<cur_iterations;r++) { uint rand_val=myRand.int32(); // random value (from 0 to 4 294 967 295) if(rand_val>=cur_iterations) { int val_log10=(int)log10(rand_val); double log10_remainder=val_log10-iter_log10; rand_val/=(uint)pow(10,log10_remainder+1); } //--- check the limit if(rand_val>=cur_iterations) { Alert("Random value error!"); return; } idxArr[r]=rand_val; } //--- time spent for the array uint start=GetTickCount(); //--- accessing the array elements for(uint p=0;p<cur_iterations;p++) uint random_val=randArr[idxArr[p]]; uint time=GetTickCount()-start; // time spent, msec Print("\n=======the uint type array======="); PrintFormat("Random accessing the array of elements %.1e has taken %d msec",cur_iterations,time); //--- the CList type list CList *p_mql_List=new CList; //--- fill the list for(uint q=0;q<cur_iterations;q++) { CNodeInt *p_node_int=new CNodeInt(randArr[q]); p_mql_List.Add(p_node_int); } start=GetTickCount(); //--- accessing the list nodes for(uint w=0;w<cur_iterations;w++) CNodeInt *p_node_int=p_mql_List.GetNodeAtIndex(idxArr[w]); time=GetTickCount()-start; // time spent, msec Print("\n=======the CList type list======="); PrintFormat("Random accessing the list of nodes %.1e has taken %d msec",cur_iterations,time); //--- free the memory ArrayFree(randArr); ArrayFree(idxArr); delete p_mql_List; }
Blok işlemi sonuçlarına göre, aşağıdaki girişler günlüğe yazdırıldı:
MR 0 22:51:22 test_MQL5_List (EURUSD,H1) QL 0 22:51:22 test_MQL5_List (EURUSD,H1) =======the uint type array======= IG 0 22:51:22 test_MQL5_List (EURUSD,H1) Random accessing the array of elements 1.0e+003 has taken 0 msec QF 0 22:51:22 test_MQL5_List (EURUSD,H1) IQ 0 22:51:22 test_MQL5_List (EURUSD,H1) =======the CList type list======= JK 0 22:51:22 test_MQL5_List (EURUSD,H1) Random accessing the list of nodes 1.0e+003 has taken 0 msec MJ 0 22:51:22 test_MQL5_List (EURUSD,H1) QD 0 22:51:22 test_MQL5_List (EURUSD,H1) =======the uint type array======= GO 0 22:51:22 test_MQL5_List (EURUSD,H1) Random accessing the array of elements 3.0e+003 has taken 0 msec QN 0 22:51:22 test_MQL5_List (EURUSD,H1) II 0 22:51:22 test_MQL5_List (EURUSD,H1) =======the CList type list======= EP 0 22:51:22 test_MQL5_List (EURUSD,H1) Random accessing the list of nodes 3.0e+003 has taken 16 msec OR 0 22:51:22 test_MQL5_List (EURUSD,H1) OL 0 22:51:22 test_MQL5_List (EURUSD,H1) =======the uint type array======= FG 0 22:51:22 test_MQL5_List (EURUSD,H1) Random accessing the array of elements 6.0e+003 has taken 0 msec CF 0 22:51:22 test_MQL5_List (EURUSD,H1) GQ 0 22:51:22 test_MQL5_List (EURUSD,H1) =======the CList type list======= CH 0 22:51:22 test_MQL5_List (EURUSD,H1) Random accessing the list of nodes 6.0e+003 has taken 31 msec QJ 0 22:51:22 test_MQL5_List (EURUSD,H1) MD 0 22:51:22 test_MQL5_List (EURUSD,H1) =======the uint type array======= MO 0 22:51:22 test_MQL5_List (EURUSD,H1) Random accessing the array of elements 9.0e+003 has taken 0 msec EN 0 22:51:22 test_MQL5_List (EURUSD,H1) MJ 0 22:51:22 test_MQL5_List (EURUSD,H1) =======the CList type list======= CP 0 22:51:22 test_MQL5_List (EURUSD,H1) Random accessing the list of nodes 9.0e+003 has taken 47 msec CR 0 22:51:22 test_MQL5_List (EURUSD,H1) KL 0 22:51:22 test_MQL5_List (EURUSD,H1) =======the uint type array======= JG 0 22:51:22 test_MQL5_List (EURUSD,H1) Random accessing the array of elements 1.0e+004 has taken 0 msec GF 0 22:51:22 test_MQL5_List (EURUSD,H1) KR 0 22:51:22 test_MQL5_List (EURUSD,H1) =======the CList type list======= MK 0 22:51:22 test_MQL5_List (EURUSD,H1) Random accessing the list of nodes 1.0e+004 has taken 343 msec GJ 0 22:51:22 test_MQL5_List (EURUSD,H1) GG 0 22:51:22 test_MQL5_List (EURUSD,H1) =======the uint type array======= LO 0 22:51:22 test_MQL5_List (EURUSD,H1) Random accessing the array of elements 3.0e+004 has taken 0 msec QO 0 22:51:24 test_MQL5_List (EURUSD,H1) MJ 0 22:51:24 test_MQL5_List (EURUSD,H1) =======the CList type list======= NP 0 22:51:24 test_MQL5_List (EURUSD,H1) Random accessing the list of nodes 3.0e+004 has taken 1217 msec OS 0 22:51:24 test_MQL5_List (EURUSD,H1) KO 0 22:51:24 test_MQL5_List (EURUSD,H1) =======the uint type array======= CP 0 22:51:24 test_MQL5_List (EURUSD,H1) Random accessing the array of elements 6.0e+004 has taken 0 msec MG 0 22:51:26 test_MQL5_List (EURUSD,H1) ER 0 22:51:26 test_MQL5_List (EURUSD,H1) =======the CList type list======= PG 0 22:51:26 test_MQL5_List (EURUSD,H1) Random accessing the list of nodes 6.0e+004 has taken 2387 msec GK 0 22:51:26 test_MQL5_List (EURUSD,H1) OG 0 22:51:26 test_MQL5_List (EURUSD,H1) =======the uint type array======= NH 0 22:51:26 test_MQL5_List (EURUSD,H1) Random accessing the array of elements 9.0e+004 has taken 0 msec JO 0 22:51:30 test_MQL5_List (EURUSD,H1) NK 0 22:51:30 test_MQL5_List (EURUSD,H1) =======the CList type list======= KO 0 22:51:30 test_MQL5_List (EURUSD,H1) Random accessing the list of nodes 9.0e+004 has taken 3619 msec HS 0 22:51:30 test_MQL5_List (EURUSD,H1) DN 0 22:51:30 test_MQL5_List (EURUSD,H1) =======the uint type array======= RP 0 22:51:30 test_MQL5_List (EURUSD,H1) Random accessing the array of elements 1.0e+005 has taken 0 msec OD 0 22:52:05 test_MQL5_List (EURUSD,H1) GS 0 22:52:05 test_MQL5_List (EURUSD,H1) =======the CList type list======= DE 0 22:52:05 test_MQL5_List (EURUSD,H1) Random accessing the list of nodes 1.0e+005 has taken 35631 msec NH 0 22:52:06 test_MQL5_List (EURUSD,H1) RF 0 22:52:06 test_MQL5_List (EURUSD,H1) =======the uint type array======= FI 0 22:52:06 test_MQL5_List (EURUSD,H1) Random accessing the array of elements 3.0e+005 has taken 0 msec HL 0 22:54:20 test_MQL5_List (EURUSD,H1) PD 0 22:54:20 test_MQL5_List (EURUSD,H1) =======the CList type list======= FN 0 22:54:20 test_MQL5_List (EURUSD,H1) Random accessing the list of nodes 3.0e+005 has taken 134379 msec RQ 0 22:54:20 test_MQL5_List (EURUSD,H1) JI 0 22:54:20 test_MQL5_List (EURUSD,H1) =======the uint type array======= MR 0 22:54:20 test_MQL5_List (EURUSD,H1) Random accessing the array of elements 6.0e+005 has taken 15 msec NE 0 22:58:48 test_MQL5_List (EURUSD,H1) FL 0 22:58:48 test_MQL5_List (EURUSD,H1) =======the CList type list======= GE 0 22:58:48 test_MQL5_List (EURUSD,H1) Random accessing the list of nodes 6.0e+005 has taken 267589 msec
Liste boyutu büyüdükçe liste öğelerine rastgele erişimin daha fazla zaman aldığını görebilirsiniz (Şekil 16).
Şek. 16 Dizi ve liste öğelerine rastgele erişim için harcanan süre
Şimdi verileri kaydetme ve yükleme yöntemlerini ele alalım.
Temel liste sınıfı CList bu tür yöntemleri içerir ancak bunlar sanaldır. Bu nedenle, bir örnek kullanarak çalışmalarını test etmek için biraz hazırlık yapmamız gerekiyor.
CIntList alt sınıfını kullanarak CList sınıfı yeteneklerini devralmalıyız. İkincisi, yeni bir CIntList::CreateElement() öğesi oluşturmak için yalnızca 1 yönteme sahip olacaktır.
//+------------------------------------------------------------------+ //| CIntList class | //+------------------------------------------------------------------+ class CIntList : public CList { public: virtual CObject *CreateElement(void); }; //+------------------------------------------------------------------+ //| New element of the list | //+------------------------------------------------------------------+ CObject *CIntList::CreateElement(void) { CObject *new_node=new CNodeInt(); return new_node; }
Ayrıca CNodeInt::Save() ve CNodeInt::Load() sanal yöntemlerini CNodeInt türetilmiş düğüm türüne eklememiz gerekecek. Bunlar, sırasıyla CList::Save() ve CList::Load() üye işlevlerinden çağrılacak.
Sonuç olarak örnek aşağıdaki gibi olacaktır (Örnek 5):
//--- Example 5 (saving list data) //--- the CIntList type list CList *p_int_List=new CIntList; int randArr[1000]; // array of random numbers ArrayInitialize(randArr,0); //--- fill the array of random numbers for(int t=0;t<1000;t++) randArr[t]=(int)myRand.int32(); //--- fill the list for(uint q=0;q<1000;q++) { CNodeInt *p_node_int=new CNodeInt(randArr[q]); p_int_List.Add(p_node_int); } //--- save the list to the file int file_ha=FileOpen("List_data.bin",FILE_WRITE|FILE_BIN); p_int_List.Save(file_ha); FileClose(file_ha); p_int_List.FreeMode(true); p_int_List.Clear(); //--- load the list from the file file_ha=FileOpen("List_data.bin",FILE_READ|FILE_BIN); p_int_List.Load(file_ha); int Loaded_List_size=p_int_List.Total(); PrintFormat("Nodes loaded from the file: %d",Loaded_List_size); //--- free the memory delete p_int_List;
Script dosyasını grafikte çalıştırdıktan sonra, Günlüğe aşağıdaki giriş eklenecektir:
ND 0 11:59:35 test_MQL5_List (EURUSD,H1) As many as 1000 nodes loaded from the file.
Böylece, CNodeInt düğüm türünün bir veri üyesi için giriş/çıkış yöntemlerinin uygulanmasını görmüş olduk.
Bir sonraki bölümde, MQL5 ile çalışırken sorunları çözmek için listelerin nasıl kullanılabileceğine dair örnekler göreceğiz.
4. MQL5'te Liste Kullanım Örnekleri
Bir önceki bölümde Standard Kitaplık sınıfı CList yöntemlerini ele alırken birkaç örnek vermiştim.
Şimdi, belirli bir sorunu çözmek için listenin kullanıldığı durumları ele alacağım. Burada bir kapsayıcı veri türü olarak listenin faydasını bir kez daha belirtmekten geçemeyeceğim. Bir listenin esnekliğinden yararlanarak kodla çalışmayı daha verimli hale getirebiliriz.
4.1 Grafik Nesneleri İşleme
Grafikte programlı olarak grafik nesneler oluşturmamız gerektiğini hayal edin. Bunlar, çeşitli nedenlerle grafikte görünebilecek farklı nesneler olabilir.
Zamanında, listenin bir meseleyi grafik nesnelerle çözmeme nasıl yardımcı olduğunu hatırlıyorum. Bu hatıramı sizinle paylaşmak istiyorum.
Belirtilen koşula göre dikey çizgiler oluşturma görevim vardı. Koşula göre, dikey çizgiler, uzunlukları duruma göre değişen belirli bir zaman aralığı için sınır görevi gördü. Bununla birlikte, aralık her zaman oluşmuş durumda değildi.
EMA21'in davranışını inceliyordum ve bu amaçla istatistik toplamam gerekiyordu.
Özellikle hareketli ortalamanın eğiminin uzunluğuyla ilgileniyordum. Örneğin, aşağı doğru bir harekette, dikey bir çizgi çizilen hareketli ortalamanın negatif hareketi (yani değer düşüşü) kaydedilerek başlangıç noktası belirlendi. Şekil 17, bir mum grafiğinin açılması üzerine EURUSD, H1 için 5 Eylül 2013 16:00 olarak tanımlanan bu noktayı göstermektedir.
Şek. 17 Aşağı doğru aralığın ilk noktası
Aşağı yönlü hareketin sona erdiğini gösteren ikinci nokta, ters ilkeye dayalı olarak; hareketli ortalamanın pozitif bir hareketinin, yani değer artışının kaydedilmesiyle belirlendi (Şekil 18).
Şek. 18 Aşağı doğru aralığın ikinci noktası
Bu nedenle, hedef aralığı 5 Eylül 2013 16:00 ile 6 Eylül 2013 17:00 arasındaydı.
Farklı aralıkların tanımlanması için sistem daha karmaşık veya daha basit olabilir. Konu bu değil. Önemli olan, grafik nesnelerle çalışmak ve eşzamanlı olarak istatistiksel veri toplamak için bu tekniğin, listenin en önemli avantajlarından birini; kompozisyon esnekliğini içermesidir.
Mevcut örneğe gelince, önce 2 grafik "Dikey çizgi" nesnesinden sorumlu CVertLineNode türünde bir düğüm oluşturdum.
Sınıf tanımı aşağıdaki gibidir:
//+------------------------------------------------------------------+ //| CVertLineNode class | //+------------------------------------------------------------------+ class CVertLineNode : public CObject { private: SVertLineProperties m_vert_lines[2]; // array of structures of vertical line properties uint m_duration; // frame duration bool m_IsFrameFormed; // flag of frame formation public: void CVertLineNode(void); void ~CVertLineNode(void){}; //--- set-methods void SetLine(const SVertLineProperties &_vert_line,bool IsFirst=true); void SetDuration(const uint _duration){this.m_duration=_duration;}; void SetFrameFlag(const bool _frame_flag){this.m_IsFrameFormed=_frame_flag;}; //--- get-methods void GetLine(SVertLineProperties &_vert_line_out,bool IsFirst=true) const; uint GetDuration(void) const; bool GetFrameFlag(void) const; //--- draw the line bool DrawLine(bool IsFirst=true) const; };
Temel olarak, bu düğüm sınıfı bir çerçeveyi tanımlar (burada iki dikey çizgiyle sınırlandırılmış bir dizi mum grafik olarak yorumlanır). Çerçeve sınırları, dikey çizgi özellikleri, süre ve oluşum bayrağının birkaç yapısı ile temsil edilir.
Standart oluşturucu ve yıkıcı dışında, sınıf, grafikte çizgi çizme yönteminin yanı sıra birkaç set-ve get- yöntemine de sahiptir.
Hatırlatmama izin verin, örneğimdeki dikey çizgiler (çerçeve) düğümü, aşağı hareketin başlangıcını gösteren birinci dikey çizgi ve yukarı hareketin başlangıcını gösteren ikinci dikey çizgi olduğunda oluşmuş sayılabilir.
Stat_collector.mq5 script dosyasını kullanarak, grafikteki tüm çerçeveleri görüntüledim ve son 2 bin çubukta kaç düğümün (çerçeve) belirli bir süre sınırına karşılık geldiğini saydım.
Örnek olarak, herhangi bir çerçeveyi içerebilecek 4 liste oluşturdum. İlk liste, 5'e kadar mum grafik sayısı; ikincisi 10'a kadar; üçüncüsü 15'e kadar ve dördüncüsü sınırsız sayıda çerçeve içeriyordu.
NS 0 15:27:32 Stat_collector (EURUSD,H1) =======List #1======= RF 0 15:27:32 Stat_collector (EURUSD,H1) Duration limit: 5 ML 0 15:27:32 Stat_collector (EURUSD,H1) Nodes number: 65 HK 0 15:27:32 Stat_collector (EURUSD,H1) OO 0 15:27:32 Stat_collector (EURUSD,H1) =======List #2======= RI 0 15:27:32 Stat_collector (EURUSD,H1) Duration limit: 10 NP 0 15:27:32 Stat_collector (EURUSD,H1) Nodes number: 15 RG 0 15:27:32 Stat_collector (EURUSD,H1) FH 0 15:27:32 Stat_collector (EURUSD,H1) =======List #3======= GN 0 15:27:32 Stat_collector (EURUSD,H1) Duration limit: 15 FG 0 15:27:32 Stat_collector (EURUSD,H1) Nodes number: 6 FR 0 15:27:32 Stat_collector (EURUSD,H1) CD 0 15:27:32 Stat_collector (EURUSD,H1) =======List #4======= PS 0 15:27:32 Stat_collector (EURUSD,H1) Nodes number: 20
Sonuç olarak aşağıdaki grafiği elde ettim (Şekil 19). Kolaylık sağlamak için ikinci dikey çerçeve çizgisi mavi renkte görüntülenir.
Şek. 19 Çerçeveleri görüntüleme
İlginçtir ki, son çerçeve 13 Aralık 2013 Cuma gününün son saatinde oluşturuldu. Süresi 6 saat olduğu için ikinci listenin altına düştü.
4.2 Sanal Alım Satımla Başa Çıkma
Bir tik akışında tek bir enstrümana göre birkaç bağımsız strateji uygulayacak bir Expert Advisor yaratmanız gerektiğini hayal edin. Gerçekte, tek bir enstrümanla ilgili olarak aynı anda yalnızca bir stratejinin uygulanabileceği açıktır. Diğer tüm stratejiler sanal nitelikte olacaktır. Bu nedenle, yalnızca bir alım satım fikrini test etmek ve optimize etmek amacıyla uygulanabilir.
Burada, genel olarak alım satımla ve özellikle MetaTrader 5 terminaliyle ilgili temel kavramların ayrıntılı bir açıklamasını sağlayan temel bir makaleye atıfta bulunmam gerekiyor: "MetaTrader 5'te Talimatlar, Pozisyonlar ve Yatırımlar".
Dolayısıyla, bu sorunu çözerken, alım satım konseptini, alım satım nesnesi yönetim sistemini ve MetaTrader 5 ortamında alışılmış olan alım satım nesneleri hakkında bilgi depolama metodolojisini kullanırsak, muhtemelen sanal bir veritabanı oluşturmayı düşünmeliyiz.
Bir geliştiricinin tüm alım satım nesnelerini talimatlar, pozisyonlar, yatırımlar ve geçmiş talimatlar olarak sınıflandırdığını hatırlatmama izin verin. Eleştirel bir göz, "alım satım nesnesi" teriminin burada yazarın kendisi tarafından kullanıldığını fark edebilir. Bu doğru...
Sanal alım satım dabenzer bir yaklaşım kullanmayı ve şu sanal alım satım nesnelerini almayı öneriyorum: sanal talimatlar, sanal pozisyonlar, sanal yatırımlar ve sanal geçmiş talimatlar.
Bu konunun derinlemesine ve daha detaylı bir tartışmaya değer olduğuna inanıyorum. Bu arada makalenin konusuna dönersek, listeler de dahil olmak üzere kapsayıcı veri türlerinin sanal stratejileri uygularken programcının hayatını kolaylaştırabileceğini söylemek isterim.
Doğal olarak alım satım sunucusu tarafında olamayacak yeni bir sanal pozisyon düşünün. Bu, onunla ilgili bilgilerin terminal tarafında kaydedilmesi gerektiği anlamına gelir. Burada, bir veritabanı, bir tanesi sanal pozisyonun düğümlerini içerecek olan birkaç listeden oluşan bir liste ile temsil edilebilir.
Geliştiricinin yaklaşımını kullanarak, aşağıdaki sanal alım satım sınıfları olacaktır:
Sınıf/Grup | Açıklama |
CSanalTalimat | sanal bekleyen talimatlarla çalışma sınıfı |
CVirtualHistoryOrder | sanal "geçmiş" talimatlarla çalışma sınıfı |
CSanalPozisyon | sanal açık pozisyonlarla çalışma sınıfı |
CVirtualYatırım | sanal "geçmiş" yatırımlarla çalışma sınıfı |
CSanallAlım Satım | sanal alım satım işlemlerini gerçekleştirme sınıfı |
Tablo 1. Sanal alım satım sınıfları
Sanal alım satım sınıflarından herhangi birinin bileşimini tartışmayacağım. Ancak muhtemelen standart bir alım satım sınıfının tüm veya neredeyse tüm yöntemlerini içerecektir. Sadece, geliştiricinin kullandığı şeyin, belirli bir alım satım nesnesinin kendisinin bir sınıfı değil, özelliklerinin bir sınıfı olduğunu belirtmek isterim.
Algoritmalarınızda listeleri kullanmak için düğümlere de ihtiyacınız olacak. Bu nedenle, bir sanal alım satım nesnesinin sınıfını bir düğümde tamamlamamız gerekiyor.
Bir sanal açılış pozisyonu düğümünün CVirtualPositionNode türünde olduğunu varsayalım. Bu türün tanımı başlangıçta aşağıdaki gibi olabilir:
//+------------------------------------------------------------------+ //| Class CVirtualPositionNode | //+------------------------------------------------------------------+ class CVirtualPositionNode : public CObject { protected: CVirtualPositionNode *m_virt_position; // pointer to the virtual function public: void CVirtualPositionNode(void); // default constructor void ~CVirtualPositionNode(void); // destructor };
Artık sanal pozisyon açıldığında, sanal pozisyonlar listesine eklenebilir.
Ayrıca, sanal alım satım nesneleri ile çalışmaya yönelik bu yaklaşımın, veritabanı rastgele erişimli bellekte depolandığından önbellek kullanımını gerektirmediğini de belirtmek isterim. Elbette, diğer depolama ortamlarında saklanmasını ayarlayabilirsiniz.
Sonuç
Bu makalede, liste gibi bir kapsayıcı veri türünün avantajlarını göstermeye çalıştım. Ama dezavantajlarından bahsetmeden geçemedim. Her neyse, bu bilgilerin genel olarak OOP ve özellikle de temel ilkelerinden biri olan polimorfizm üzerine çalışan kişiler için faydalı olacağını umuyorum.
Dosyaların konumu:
Benim görüşüme göre, proje klasöründe dosya oluşturmak ve depolamak en iyisi olacaktır. Örneğin, şunun gibi: %MQL5\Projects\UserLists. Burası tüm kaynak kod dosyalarını kaydettiğim yer. Varsayılan dizinleri kullanıyorsanız, bazı dosyaların kodundaki dosya atama yöntemi ekleme dosyasını (tırnak işaretlerini açılı ayraçlarla değiştirin) değiştirmeniz gerekecektir.
# | Dosya | Konum | Açıklama |
---|---|---|---|
1 | CiSingleNode.mqh | %MQL5\Projects\UserLists | Tek bağlantılı bir liste düğümünün sınıfı |
2 | CDoubleNode.mqh | %MQL5\Projects\UserLists | Çift bağlantılı bir liste düğümünün sınıfı |
3 | CiUnrollDoubleNode.mqh | %MQL5\Projects\UserLists | Teşhir edilmemiş çift bağlantılı liste düğümünün sınıfı |
4 | test_nodes.mq5 | %MQL5\Projects\UserLists | Düğümlerle çalışma örnekleri içeren script dosyası |
5 | CiSingleList.mqh | %MQL5\Projects\UserLists | Tek bağlantılı bir listenin sınıfı |
6 | CDobleList.mqh | %MQL5\Projects\UserLists | Çift bağlantılı bir listenin sınıfı |
7 | CiUnrollDoubleList.mqh | %MQL5\Projects\UserLists | Teşhir edilmemiş çift bağlantılı listenin sınıfı |
8 | CiCircleDoublList.mqh | %MQL5\Projects\UserLists | Dairesel çift bağlantılı bir listenin sınıfı |
9 | test_sList.mq5 | %MQL5\Projects\UserLists | Tek bağlantılı bir listeyle çalışma örnekleri içeren script dosyası |
10 | test_dList.mq5 | %MQL5\Projects\UserLists | Çift bağlantılı bir listeyle çalışma örnekleri içeren script dosyası |
11 | test_UdList.mq5 | %MQL5\Projects\UserLists | Teşhir edilmemiş çift bağlantılı bir listeyle çalışma örnekleri içeren script dosyası |
12 | test_CdList.mq5 | %MQL5\Projects\UserLists | Dairesel çift bağlantılı bir listeyle çalışma örnekleri içeren script dosyası |
13 | test_MQL5_List.mq5 | %MQL5\Projects\UserLists | CList sınıfıyla çalışma örnekleri içeren script dosyası |
14 | CNodeInt.mqh | %MQL5\Projects\UserLists | Tamsayı türündeki düğümün sınıfı |
15 | CIntList.mqh | %MQL5\Projects\UserLists | CNodeInt düğümleri için liste sınıfı |
16 | CRrandom.mqh | %MQL5\Projects\UserLists | Rastgele numara üretecinin sınıfı |
17 | CVertLineNode.mqh | %MQL5\Projects\UserLists | Dikey çizgilerin çerçevesini işlemek için düğüm sınıfı |
18 | Stat_collector.mq5 | %MQL5\Projects\UserLists | İstatistik koleksiyonu örneği içeren script dosyası |
Referanslar:
- A. Friedman, L. Klander, M. Michaelis, H. Schildt. C/C++ Açıklamalı Arşivler. Mcgraw-Hill Osborne Media, 1999. 1008 sayfa.
- V.D. Daleka, A.S. Derevyanko, O.G. Kravets, L.E. Timanovskaya. Veri Modelleri ve Yapıları. Çalışma Rehberi. Kharkov, KhGPU, 2000. 241 sayfa (Rusça).
MetaQuotes Ltd tarafından Rusçadan çevrilmiştir.
Orijinal makale: https://www.mql5.com/ru/articles/709





- Ücretsiz alım-satım uygulamaları
- İşlem kopyalama için 8.000'den fazla sinyal
- Finansal piyasaları keşfetmek için ekonomik haberler
Gizlilik ve Veri Koruma Politikasını ve MQL5.com Kullanım Şartlarını kabul edersiniz