English Русский 中文 Español Deutsch 日本語 Português 한국어 Français Italiano
MQL5 Programlama Temelleri: Diziler

MQL5 Programlama Temelleri: Diziler

MetaTrader 5Örnekler | 13 Ocak 2022, 09:04
261 0
Dmitry Fedoseev
Dmitry Fedoseev

Giriş

Diziler, değişkenler ve fonksiyonlar ile birlikte, hemen hemen tüm programlama dillerinin entegre bir parçasıdır. Genellikle, birçok acemi programcı dizilerden korkar. Kulağa garip geliyor ama doğru! Sizi temin edebilirim, hiç de korkutucu değiller. Aslında diziler normal değişkenlere benzer. Notasyon özelliklerine dair ayrıntılara dalmadan, basit değişkenler kullanarak:

Variable0=1;
Variable1=2;

Variable2=Variable0+Variable1;

veya dizileri kullanarak bir ifade yazmak arasında büyük bir fark yok:

double Variable[3];

Variable[0]=1;
Variable[1]=2;

Variable[2]=Variable[0]+Variable[1];

Gördüğünüz gibi, değişken adlarının dizileri kullandığımızda parantezler içermesi haricinde fark o kadar büyük değil. Daha önemli bir nokta daha var: Değişken bildirirken her değişkenin adını belirtmeniz gerekir, ancak bir dizi bildirirken adını yalnızca bir kez yazmanız ve değişkenlerin sayısını parantezler içinde (dizi öğelerinin sayısı) belirtmeniz gerekir. Dizileri kullanmanın değişkenlere kıyasla avantajları, birçok gerçek hayattaki programlama görevinin zorluklarıyla uğraşırken daha belirgin hale gelir.

Dizilerin karmaşık bir şey olarak görülmesinin nedeni bir şekilde "[" ve "]" kullanımıyla ilgili olabilir mi? Bu semboller, programlama dışında diziler ile çalışırken nadiren kullanılır, bu nedenle klavyedeki konumları kişinin hafızasından silinebilir ve rahatsızlığa neden olabilir. Aslında bunların nerede olduğunu kolayca hatırlayabilirsiniz: bu iki tuş "Enter" tuşunun yanında mantıksal bir sırayla yer alır: açma parantezinden sonra kapatma parantezi gelir.


Dizilerin Tanımı ve Genel Özellikleri

Bir dizi, aynı ada sahip numaralandırılmış bir değişkenler kümesidir. Dizilerin genel özellikleri arasında dizinin adını, değişken türünü (int, double, vb.) ve dizi boyutu yer alır. Dizi öğeleri sıfırdan itibaren indislenir. Dizi öğelerine dair konuşurken, dizi öğelerini sıfırdan itibaren saymaya başlamamızı önermek için "sayı" yerine "indis" kelimesinin kullanılması her zaman daha iyidir (numaralandırma genellikle birden başlar). Bu şekilde indislenen öğelerde, son öğenin indisi dizi öğelerinin sayısından bir eksiktir.

Dizi aşağıdaki gibi bildirilmişse:

double Variable[3];

aşağıdaki öğelere sahiptir: Değişken[0], Değişken[1] ve Değişken[2].

Öğe sayısı ile son öğenin indisi arasında böyle bir uyumsuzluk ilk bakışta uygunsuz görünebilir. Aslında bu, dizi öğelerinin 1'den indislendiği veya dizi boyutunun dizideki gerçek öğe sayısı yerine son öğesinin indisi ile tanımlandığı programlama dillerine göre önemli avantajlar sunar.

MQL5'te dizi boyutunu belirlemek için ArraySize() fonksiyonunu kullanırız:

double Variable[3];

int Size=ArraySize(Variable);

Kodu çalıştırdıktan sonra Size değişkeninin değeri 3'e eşit olacaktır.


Statik ve Dinamik Diziler

Diziler statik ve dinamik olabilir. Dizi boyutu bildiriminde belirtilmişse, dizi statiktir:

double Variable[3];

Statik bir dizi boyutu programda değiştirilemez. Bir diziyi bildirirken, boyutu doğrudan bir sayı olarak (yukarıdaki örnekte olduğu gibi) veya önceden tanımlanmış bir sabiti kullanılarak belirtilebilir:

#define SIZE 3

double Variable[SIZE];

Bildiriminde boyutu belirtilmeyen bir dizi dinamiktir:

double Variable[];

Kullanmadan önce böyle bir dizinin boyutunu ayarlamanız gerekir. Boyut, ArrayResize() fonksiyonu ile ayarlanır:

ArrayResize(Variable,3);

Dinamik bir dizinin boyutu, program yürütme sırasında gerektiği kadar değiştirilebilir, bu da dinamik ve statik diziler arasındaki temel farktır.

Diziyi tamamen boşaltmanız gerekiyorsa, ArrayFree() fonksiyonunu kullanın:

ArrayFree(Variable);

Dizi boyutu bu fonksiyonu yürütürken 0 olarak ayarlanır. Bu fonksiyonun ortaya çıkardığı etki aşağıdaki eyleme benzerdir:

ArrayResize(Variable,0);

Dizinin boşaltılması, daha fazla program işlemi için diziye artık ihtiyaç duyulmadığında (program tarafından kullanılan bellek miktarını azaltır) veya bir fonksiyon yürütme başlangıcında (dizi veri toplama için kullanılıyorsa) faydalı olabilir.

ArrayIsDynamic() fonksiyonu, sağlanan herhangi bir dizinin statik mi yoksa dinamik mi olduğunun belirlenmesine olanak sağlar:

bool dynamicArray=ArrayIsDynamic(Variable);

dynamicArray değişkeni, dizi dinamikse bir true değerini, dizi statikse bir false değerini içerecektir.


Dizi Başlatma

Bazen bir diziyi, bildirdikten hemen sonradeğerlerle doldurmak gerekir. Aynı türde birkaç düğme oluşturarak her düğmeyi kendi metnine sahip olacak şekilde bir satırda düzenlemek istediğinizi varsayalım. Dizilerin devasa avantajları burada devreye girer. Her düğme için kodu kopyalamanız gerekmez (düzinelerce olabilir) veya aynı fonksiyonu tekrar tekrar çağırmanız gerekmez. Fonksiyon çağrı kodunu yalnızca bir kez yazdıktan sonra bir döngüde dizi üzerinde yineleme yaparak gerekli sayıda düğme oluşturabilirsiniz.

Basitçe bir dizi bildiririz ve hemen öğelerine değerler atarız:

string Variable[] = {"Button 1", "Button 2", "Button 3"};

Bu şekilde bildirildiğinde dizi, boyutu belirtilmemiş olmasına rağmen hala statik olacaktır. Bunun nedeni, öğelerinin sayısının değerler listesiyle (süslü parantez içinde) tanımlanmasıdır.

Dizi öğelerinin sayısını belirtirseniz hata meydana gelmeyecektir:

string Variable[3] = {"Button 1", "Button 2", "Button 3"};

Ancak bunu yapmamanız daha iyi olacaktır; programda yapılacak iyileştirmeler sırasında dizi değerleri listesini değiştirmeniz ve daha fazla veya daha az sayıda öğe kullanmanız gerekebilir. Kullanıldığı kod kısımlarında dizi boyutunu belirlemek için, belirli bir sayısal değer yerine ArraySize() fonksiyonunun kullanılması önerilir. Bu yaklaşım, ana koda müdahale etmeksizin, yalnızca değerler listesini değiştirmenize olanak sağlar. Programı başlattığınızda dizi boyutu değişkenini bildirmek ve buna ArraySize() fonksiyonu ile elde edilen değeri atamak daha uygun olacaktır.

Değerler listesi ile statik bir dizi başlatılamıyorsa, dizi boyutunu belirtmek için bir sabit kullanmak daha iyi olacaktır. Genel olarak, programda daha fazla iyileştirme yapılması gerektiğinde değiştirilmesi gereken kod miktarını azaltma ilkesini izleriz. Tüm dizi öğelerini aynı değerlerle doldurmanız gerekiyorsa, ArrayInitialize() fonksiyonunu kullanın:

ArrayInitialize(Variable,1);

Yukarıdaki kodu çalıştırdıktan sonra, tüm Var dizi öğeleri 1 değerinde olacaktır. Aynı değerlerin yalnızca bazı dizi öğelerine atanması gerekiyorsa, ArrayFill() fonksiyonunu kullanırız:

double Variable[4];

ArrayFill(Variable,0,2,1);
ArrayFill(Variable,2,2,2);

Bu kodu çalıştırdıktan sonra, 0 ve 1 öğeleri 1 değerini alırken, 2 ve 3 öğeleri 2 değerini alacaktır.


Dizi Yineleme Döngüsü

Diziler genellikle bir for döngüsü kullanılarak işlenir. Boyutu önceden bilinen statik bir dizi kullanırken elimizdeki işe göre diziyi ileri veya geri yineleriz:

//--- forwards
for(int i=0; i<SIZE; i++){ 
  // some manipulations on the Variable[i] element
}

//--- backwards
for(int i=SIZE-1; i>=0; i--){
  // some manipulations on the Variable[i] element
}

Dizi dinamikse, döngüden hemen önce dizi boyutu için bir değişken bildirmeli, dizi boyutunu elde etmeli ve bir döngü oluşturmalısınız:

int Size=ArraySize(Var);

for(int i=0; i<Size; i++){
  // some manipulations on the Variable[i] element
}

Bir for döngüsünde koşulu kontrol ederken dizi boyutu için bir değişken kullanmak yerine ArraySize() fonksiyonunu çağırırsanız, ArraySize() fonksiyonu her döngü yinelemesinde çağrılacağı için döngü süresi önemli ölçüde uzayabilir. Yani fonksiyon çağrısı bir değişkeni çağırmaktan daha fazla zaman alır:

for(int i=0; i<ArraySize(Variable); i++){
   // some manipulations on the Variable[i] element
}
Yukarıdaki kodun kullanımı önerilmez.

Program algoritması geriye doğru döngü yinelemesine olanak sağlıyorsa, şunu dizi boyutu için bir değişken olmaksızın yapabilirsiniz:

for(int i=ArraySize(Variable)-1; i>=0; i--){
  // some manipulations on the Variable[i] element
}

Bu durumda, döngünün başında ArraySize() fonksiyonu yalnızca bir kez çağrılacak ve döngü hızlı bir şekilde çalışacaktır.


Çok Boyutlu Diziler

Şimdiye kadar yalnızca tek boyutlu dizileri ele aldık. Bunlar aşağıdaki gibi temsil edilebilir:

Tek boyutlu dizi

Diziler çok boyutlu da olabilir. Tek boyutlu bir dizide indis başına yalnızca bir değer varken çok boyutlu bir dizide indis başına birden fazla sayıda değer vardır. Çok boyutlu diziler aşağıdaki şekilde bildirilir:

double Variable[10][3];

Bu, dizinin ilk boyutunun on öğeye sahip olması ve ikinci boyutun üç öğeye sahip olması anlamına gelir. Aşağıdaki şekilde gösterilebilir:

Çok boyutlu dizi

İki boyutlu bir dizi, kolay anlaşılması için bir düzlem olarak gösterilebilir. Birinci boyutun boyutu uzunluğu belirler, ikinci boyutun boyutu genişliği belirler ve öğenin değeri örneğin deniz seviyesinden yükseklik olmak üzere düzlemde belirli bir noktanın parametrelerini tanımlar.

Bir dizi üç boyutlu da olabilir:

double Variable[10][10][10];

Bu dizi bir küp veya paralelkenar olarak gösterilebilir: birinci boyut uzunluğu belirler, ikinci boyut genişliği belirler, üçüncü boyut yüksekliği belirler ve öğenin değeri uzayda belirli bir noktanın parametrelerini tanımlar.

MQL5'te izin verilen maksimum dizi boyutu sayısı 4'tür.

Çok boyutlu bir dizi yalnızca birinci boyutta statik veya dinamik olabilir, diğer tüm boyutlarda statiktir. Dolayısıyla, ArrayResize() fonksiyonuyla yalnızca birinci boyutun boyutunu değiştirmeniz sağlanır. Bir dizi bildirilirken diğer boyutların da boyutları belirtilmelidir:

double Variable[][3][3];

Çok boyutlu bir dizinin boyutunu ArraySize() fonksiyonunu kullanarak belirlerken bir şeyi aklımızda tutmalıyız: Dizi boyutunu ArrayResize() fonksiyonunu kullanarak değiştirirken, fonksiyonun ikinci parametresi dizinin birinci boyutunun boyutudur. Buna rağmen, ArraySize() fonksiyonu, birinci boyutun boyutunu değil, toplam öğe sayısını döndürür.

double Variable[][3][3]; 

ArrayResize(Variable,3); 
int Size = ArraySize(Variable);

Yukarıdaki kodu çalıştırdıktan sonra, Size (Boyut) değişkeninin değeri 27'ye eşit olacaktır. Birinci boyutun boyutunu elde etmeniz gerekiyorsa, bir döngüde çok boyutlu diziler üzerinde yineleme yaparken bu özelliği hatırlayın:

double Variable[][3][3];
 
ArrayResize(Variable,3); 

int Size=ArraySize(Variable)/9; // Determine the size of the first dimension

for(int i=0; i<Size; i++) {
   for(int j=0; j<3; j++) {
      for(int k=0; k<3; k++) {
            //  some manipulations on the Var[i][j][k] element;
      }   
   }   
}

Daha önce bahsedildiği gibi programda daha fazla iyileştirme yapılması gerektiğinde, değiştirilmesi gereken kod miktarını azaltma ilkesine uyulması tavsiye edilir. Yukarıdaki kod örneğinde, hesaplanabilen 9 sayısını kullandık. Bunun için, dizinin belirtilen boyutunda bulunan öğelerin sayısını döndüren ArrayRange() fonksiyonunu kullanabiliriz. Dizi boyutlarının sayısı biliniyorsa basit bir hesaplama yapabiliriz:

int Elements=ArrayRange(Variable,1)*ArrayRange(Variable,2);
int Size=ArraySize(Variable)/Elements;

Bunu daha evrensel hale getirebiliriz:

int Elements=1; // One element for a one-dimensional array
int n=1; // Start with the second dimension (dimensions are numbered from zero)

while(ArrayRange(Variable,n) > 0){ // Until there are elements in the dimension
   Elements*=ArrayRange(Variable,n); // Multiplication of the number of elements
   n++; // Increase in the dimension's number
}

Bu noktada, böyle bir hesaplama için bir fonksiyon oluşturmanın iyi olacağını düşünebilirsiniz. Ne yazık ki bu mümkün değil, çünkü bir fonksiyona rastgele bir dizi aktarılamaz. Bir fonksiyon argümanı bildirirken, birincisi hariç tüm dizi boyutlarındaki öğelerin sayısını açıkça belirtmeniz gerekir, bu da böyle bir fonksiyonu anlamsız hale getirir. Bu hesaplamalar program başlatılırken daha kolay ve daha iyi şekilde yapılır. Bir diziyi bildirirken, boyutların boyutlarını belirleyen sabitlerin kullanılması tavsiye edilir:

#define SIZE1 3
#define SIZE2 3
#define TOTAL SIZE1*SIZE2 

Çok boyutlu dizilerin değer listesi kullanılarak başlatılması tek boyutlu dizilerin başlatılmasına benzerdir. Ancak, çok boyutlu bir dizi birkaç başka diziden oluştuğundan, bu dizilerin her biri süslü parantezler ile ayrılmalıdır.

Diyelim ki aşağıdaki gibi bir dizimiz var:

double Variable[3][3];

Bu dizi, her biri üç öğeden oluşan üç diziden oluşuyor:

double Variable[][3]={{1, 2, 3},{ 4, 5, 6},{7, 8, 9}};

Üç boyutlu bir dizi de aynı şekilde ele alınır. Dizi yapısının daha kolay anlaşılması için kod birkaç satıra bölünebilir:

double Variable[][3][3]={
   {
      {1, 2, 3},
      {4, 5, 6},
      {7, 8, 9}
   },
   {
      {10, 20, 30},
      {40, 50, 60},
      {70, 80, 90}
   },
   {
      {100, 200, 300},
      {400, 500, 600},
      {700, 800, 900}
   }
};

ArrayInitialize() fonksiyonu kullanılarak çok boyutlu bir dizinin başlatılması, tek boyutlu bir dizinin başlatılması ile aynı şekilde yapılır:

ArrayInitialize(Variable,1);

Yukarıdaki kodu çalıştırdıktan sonra, tüm dizi öğeleri 1 değerinde olacaktır. Bu ArrayFill() fonksiyonu için de geçerlidir:

double var[3][3][3];

ArrayFill(Variable,0,9,1);
ArrayFill(Variable,9,9,10);
ArrayFill(Variable,18,9,100);

Bu kodu çalıştırdıktan sonra, birinci boyutun ilk öğesiyle ilişkili tüm öğeler 1, ikinci öğeyle ilişkili olanlar 10 ve üçüncü öğeyle ilişkili olanlar - 100 değerini alacaktır.


Bir Diziyi Bir Fonksiyona Aktarma

Diziler, değişkenlerden farklı olarak, bir fonksiyona yalnızca referans yoluyla aktarılabilir. Bunun anlamı, fonksiyonun dizinin kendi örneğini oluşturmaması ve bunun yerine doğrudan kendisine aktarılan diziyle çalışmasıdır. Yani, fonksiyonun dizide yaptığı tüm değişiklikler orijinal diziyi etkiler.

Bir değişken, bir fonksiyona olağan bir şekilde (değere göre) aktarılırsa, aktarılan değişkenin değeri fonksiyon tarafından değiştirilemez:

int x=1;
Func(x);

void  Func(int arg){
   arg=2;
}

x değeri, Func() fonksiyonunu çalıştırdıktan sonra 1'e eşit kalır.

Eğer bir değişken referans yoluyla aktarılırsa (& ile gösterilir), fonksiyon kendisine aktarılan bu değişkenin değerini değiştirebilir:

int x=1;
Func(x);

void  Func(int &arg){
   arg=2;
}

x değeri, Func() fonksiyonunu çalıştırdıktan sonra 2'ye eşit olur.

Bir fonksiyona bir dizi aktarırken, argümanın referans olarak aktarıldığını ve bir diziyi temsil ettiğini (parantez içinde) belirtmeniz gerekir:

void Func(double &arg[]){
   // ...
}

Çok boyutlu dizileri bir fonksiyona aktarırken, boyut boyutları (birincisi hariç) belirtilmelidir:

double var[][3][3];

void Func(double &arg[][3][3]){
   // ...
}

Bu durumda, sabitlerin kullanılması daha da çok tavsiye edilir:

#define SIZE1 3
#define SIZE2 3

double Var[][SIZE1][SIZE2];

void Func(double &arg[][SIZE1][SIZE2]){
   // ...
}


Dizileri Kaydetme ve bir Dosyadan Yükleme

Bir diziyi kaydederken ve bir dosyadan yüklerken, dizinin birinci boyutunun boyutundaki değerler ile dizi öğelerinin toplam sayısı arasındaki farkı her daim göz önünde bulundurmalısınız. Bir diziyi kaydetmek için dosyaya ilk olarak dizi boyutunu (ArraySize() fonksiyonu ile belirlenen toplam öğe sayısı) ve ardından dizinin tamamını yazarız:

bool SaveArrayToFile(string FileName,string &Array[])
  {
//--- Open the file
   int h=FileOpen(FileName,FILE_TXT|FILE_WRITE);
   if(h==-1) return(false); // Error opening the file
//--- Write to the file
   FileWriteInteger(h,ArraySize(Array),INT_VALUE); // Write the array size
   FileWriteArray(h,Array); // Write the array
//--- Close the file
   FileClose(h);
   return(true); // Saving complete
  }

Bunun sonucunda, tek boyutlu dizileri kaydetmek için oldukça evrensel bir fonksiyon elde ederiz.

Bir dosyadan dizi yüklemek için ilk olarak dizi boyutunu okumamız, bunu yeniden boyutlandırmamız ve son olarak diziyi okumamız gerekir:

bool LoadArrayFromFile(string FileName,double &Array[])
  {
//--- Open the file
   int h=FileOpen(FileName,FILE_BIN|FILE_READ);
   if(h==-1) return(false); // Error opening the file
//--- Read the file
   int Size=FileReadInteger(h,INT_VALUE); // Read the number of array elements
   ArrayResize(Array,Size); // Resize the array. 
                            // In one-dimensional arrays the size of the first dimension is equal to the number of array elements.
   FileReadArray(h,Array); // Read the array from the file
//--- Close the file
   FileClose(h);
   return(true); // Reading complete
  }

Bir dosyadan çok boyutlu bir dizi yüklerken, birinci boyutun boyutunu hesaplamanız gerekecektir. Örneğin üç boyutlu bir dizi okuduğumuzu varsayalım:

bool LoadArrayFromFile3(string FileName,double &Array[][SIZE1][SIZE2])
  {
//--- Open the file
   int h=FileOpen(FileName,FILE_BIN|FILE_READ);
   if(h==-1)return(false); // Error opening the file
//--- Read the file   
   int SizeTotal=FileReadInteger(h,INT_VALUE); // Read the number of array elements
   int Elements=SIZE1*SIZE2; // Calculate the number of elements 
   int Size=SizeTotal/Elements; // Calculate the size of the first dimension
   ArrayResize(Array,Size); // Resize the array
   FileReadArray(h,Array); // Read the array
//--- Close the file
   FileClose(h);
   return(true); // Reading complete
  }

Dosya 2-3'lük bir dizi içeriyor ve biz bunu 3-3'lük bir dizi olarak okumaya çalışıyor olabiliriz. Boyutlar arasındaki uyumu, birinci boyutun hesaplanan boyutunu öğe sayısı ile çarparak kontrol edebilirsiniz. Ortaya çıkan değer dizi öğelerinin toplam sayısına eşitse uyum vardır diyebiliriz.

Ancak Var[2][3] dizisi Var[3][2] dizisine karşılık gelecektir. Bu durumu da ele almanız gerekiyorsa, çok boyutlu bir diziye dair daha fazla bilgi kaydetmelisiniz. Örneğin, ilk olarak dizi öğelerinin sayısını, ardından dizi boyutlarının sayısını ve ardından boyutların her birinin ve dizinin kendisinin boyutlarını kaydedebilirsiniz.

Yukarıda sağlanan son fonksiyon evrensel değildir ve yalnızca ikinci boyutun boyutunun SIZE1'e ve üçüncü boyutun boyutunun SIZE2'ye eşit olduğu üç boyutlu dizileri okumak için tasarlanmıştır. İlki hariç tüm dizi boyutlarının boyutlarını dinamik olarak değiştirmenin bir yolu olmadığından bu sorun değil - programda kullanılması gereken diziler için fonksiyonlar oluşturacağız.

Bu durumda evrensellik gerekli değildir: Dizi boyutlarının boyutları (birincisi hariç) programın harici parametreleri ile kontrol edilmeyecektir. Bununla birlikte, diğer boyutların boyutlarını kontrol etme olanağını uygulamanız gerekiyorsa, bilerek daha büyük boyuttan ve ek değişkenlerden oluşan çok boyutlu bir dizi kullanarak veya nesne yönelimli programlama (OOP) tekniklerini uygulayarak bu işi halledebilirsiniz. Bu makalenin ilerleyen kısımlarında daha çok ikinci yaklaşıma dair konuşacağız.


Dinamik Dizileri Kullanma

Dinamik diziler, dizi boyutunu önceden bilinmediğinde kullanılır. Dizi boyutu program özellikleri penceresinde ayarlanan parametrelere bağlıysa, dinamik dizilerin kullanılması sorun olmayacaktır: Dizi boyutu program başlatma sırasında yalnızca bir kez değiştirilecektir.

Örneğin bekleyen siparişlerle ilgili bilgiler olmak üzere belirli bilgileri dinamik olarak toplamak için bir dizi kullanılabilir. Bunların sayıları değişebilir, yani gerekli boyut önceden bilinmez. Bu durumda, emirleri aktarmadan önce dizi boyutunu 0 olarak değiştirmek ve her emri aktarırken dizi boyutunu bir öğe artırmak en kolay yol olacaktır. Bu yol işe yarayacaktır, ama çok yavaş bir şekilde.

Dizi boyutu, emirleri aktarmadan önce, emir sayısına göre yalnızca bir kez değiştirilebilir. Bu, dizinin son aktif öğesinin indisi için başka bir değişken (veya indis yerine bir dizi fiili olarak aktif dizi öğesi) gerektirecektir. Bu yöntem, maksimum dizi boyutunu zaten biliyorsanız uygundur. Maksimum dizi boyutunu bilmiyorsanız, aşağıdaki sınıfta gösterildiği gibi, parçaları kullanarak diziyi yeniden boyutlandırarak bununla çalışmayı hızlandırabiliriz:

class CDynamicArray
  {
private:
   int               m_ChunkSize;    // Chunk size
   int               m_ReservedSize; // Actual size of the array
   int               m_Size;         // Number of active elements in the array
public:
   double            Element[];      // The array proper. It is located in the public section, 
                                     // so that we can use it directly, if necessary
   //+------------------------------------------------------------------+
   //|   Constructor                                                    |
   //+------------------------------------------------------------------+
   void CDynamicArray(int ChunkSize=1024)
     {
      m_Size=0;                            // Number of active elements
      m_ChunkSize=ChunkSize;               // Chunk size
      m_ReservedSize=ChunkSize;            // Actual size of the array
      ArrayResize(Element,m_ReservedSize); // Prepare the array
     }
   //+------------------------------------------------------------------+
   //|   Function for adding an element at the end of array             |
   //+------------------------------------------------------------------+
   void AddValue(double Value)
     {
      m_Size++; // Increase the number of active elements
      if(m_Size>m_ReservedSize)
        { // The required number is bigger than the actual array size
         m_ReservedSize+=m_ChunkSize; // Calculate the new array size
         ArrayResize(Element,m_ReservedSize); // Increase the actual array size
        }
      Element[m_Size-1]=Value; // Add the value
     }
   //+------------------------------------------------------------------+
   //|   Function for getting the number of active elements in the array|
   //+------------------------------------------------------------------+
   int Size()
     {
      return(m_Size);
     }
  };

Bu sınıfı ekteki CDynamicArray.mqh dosyasında bulabilirsiniz. Dosya, terminal veri dizininin MQL5\Include klasörüne yerleştirilmelidir.

Şimdi kodun performansını her iki durumda da değerlendirip karşılaştıralım: dizi boyutunun sırayla 1 artırıldığı durum ve parçalar kullanılarak artırıldığı durum:

int n=50000;
   double ar[];
   CDynamicArray da;

//--- Option 1 (increasing the size by the 1st element)
   long st=GetTickCount(); // Store the start time 
   ArrayResize(ar,0); // Set the array size to zero 
   for(int i=0;i<n;i++)
     {
      ArrayResize(ar,i+1); // Resize the array sequentially
      ar[i]=i;
     }
   Alert("Option 1: "+IntegerToString(GetTickCount()-st)+" ms"); // Message regarding the amount of time required to perform the first option

//--- Option 2 (increasing the size using chunks)
   st=GetTickCount(); // Store the start time 
   for(int i=0;i<n;i++)
     {
      da.AddValue(i); // Add an element
     }
   Alert("Option 2: "+IntegerToString(GetTickCount()-st)+" ms"); // Message regarding the amount of time required to perform the second option

  }

Bir betik biçimindeki bu test, ekteki sTest_Speed.mq5 dosyasında bulunabilir. Dosya, terminal veri dizininin MQL5\Scripts klasörüne yerleştirilmelidir.

İlk seçeneğin performansı birkaç saniye alırken, ikinci seçenek neredeyse anında gerçekleşmiştir.


Dizi İndisleme Sırası

Genellikle, bir dizi yeniden boyutlandırılırken yeni öğeler dizinin sonuna eklenir:

double ar[]; // Array
ArrayResize(ar,2); // Prepare the array
ar[0]=1; // Set the values
ar[1]=2; 
ArrayResize(ar,3); // Increase the array size
ar[2]=3; // Set the value for the new array element
Alert(ar[0]," ",ar[1]," ",ar[2]); // Print array values

Bu kodu çalıştırdıktan sonra, dizide yer alan değerler 1, 2 ve 3 olmalıdır.

Dizilerdeki öğeler ters sırada da indislenebilir. İndisleme sırası, ArraySetAsSeries() fonksiyonu ile belirlenir:

ArraySetAsSeries(ar,true); // set indexing in reverse order
ArraySetAsSeries(ar,false); // set normal indexing

İndislenen dizinin boyutu ters sırada değiştirildiğinde, yeni öğe dizinin başına öğe eklenir:

double ar[]; // Array
ArrayResize(ar,2); // Prepare the array
ar[0]=1; // Set the values
ar[1]=2; 
ArraySetAsSeries(ar,true); // Change the indexing order
ArrayResize(ar,3); // Increase the array size
ar[0]=3; // Set the value for the new array element
Alert(ar[0]," ",ar[1]," ",ar[2]); // Print array values

Bu kodu çalıştırdıktan sonra, dizide yer alan değerler 3, 2 ve 1 olmalıdır.

İki durumda da, yeni öğe dizinin aynı tarafına eklenir, tek fark indisleme sırasıdır. Öğeleri normal sırada indislenen dizinin başına öğe eklemek için bu fonksiyon kullanılamaz. Normal şekilde indislenen dizinin sonuna bir öğe eklemek için, sadece dizi boyutunu artırmanız ve son öğeye bir değer atamanız gerekir.

Dizinin başına öğe eklemek için dizi boyutunu artırmalı, tüm değerleri taşımalı ve sıfır öğesine yeni bir değer atamanız gerekir. Ters sırada indislenen dizilerde ise, dizinin başına kolayca yeni bir öğe eklenebilir. Ancak, yeni öğeyi dizinin sonuna eklemeniz gerekiyorsa, ilk olarak dizi boyutunu artırmalı ve tüm değerleri başa taşıdıktan sonra son öğeye yeni bir değer atamalısınız. İndisleme sırası manipülasyonları bu sorunu çözmeyecektir.

Dizi indisleme sırası, ArrayIsSeries() fonksiyonu kullanılarak belirlenebilir:

bool series=ArrayIsSeries(ar);

Dizi ters sırada indislenirse, fonksiyon true değerini döndürür.

Ters sırada indislenen diziler esas olarak Uzman Danışmanlarda kullanılır. EA'ları geliştirirken, çubukların sağdan sola sayılması ve dolayısıyla fiyat verilerinin ve gösterge tamponlarının ters indisleme ile dizilere kopyalanması genelde daha uygun bir yoldur.


Dizileri Kopyalama

Kopyalamanın en kolay yolu, bir döngüdeki bir dizi üzerinde yineleme yapmak ve öğeleri bir diziden diğerine sırayla kopyalamaktır. Bununla birlikte, MQL5'te dizileri kopyalamamıza olanak sağlayan özel bir fonksiyon vardır: ArrayCopy():

double ar1[]={1,2,3};
double ar2[];

ArrayCopy(ar2,ar1);

Yukarıdaki kodu çalıştırdıktan sonra ar2 dizisi, ar1 dizisindeki ile aynı değerlere sahip üç öğeden oluşacaktır: 1, 2, 3.

Kopyalanacak öğe sayısı, kopyaladığınız diziye uymuyorsa, dizi boyutu otomatik olarak artırılacaktır (dizi dinamik olmalıdır). Dizi kopyalanacak öğe sayısından büyükse, boyutu aynı kalacaktır.

ArrayCopy() fonksiyonu bir dizinin yalnızca bir kısmının kopyalanmasına da olanak sağlar. Fonksiyonun isteğe bağlı parametrelerini kullanarak, kopyalanacak ilk öğeyi, ilk kopyalanan öğenin yeni dizideki indisini ve kopyalayacağınız öğe sayısını belirleyebilirsiniz.

Bir dizinin öğelerini diğer diziye kopyalamaya ek olarak, ArrayCopy() fonksiyonu aynı dizi içindeki öğeleri kopyalamak için de kullanılabilir:

double ar1[]={1,2,3,4,5};
ArrayCopy(ar1,ar1,1,2);

İndis 2'ye sahip öğe ile başlayan verileri kopyalarız ve bunları indis 1'den başlayarak yapıştırırız. Bu kodu çalıştırdıktan sonra dizi aşağıdaki değerleri içerecektir: 1, 3, 4, 5, 5.

ArrayCopy() fonksiyonu ayrıca verileri sağa kaydırmanıza olanak sağlar:

double ar1[]={1,2,3,4,5};
ArrayCopy(ar1,ar1,2,1);

İndis 1'e sahip öğe ile başlayan verileri alırız ve bunları indis 2'den başlayarak düzenleriz. Bu kodu çalıştırdıktan sonra dizi aşağıdaki değerleri içerecektir: 1, 2, 2, 3, 4.

ArrayCopy() fonksiyonu, çok boyutlu dizilere de uygulanabilir; bu sayede, dizi tek boyutluymuş ve tüm öğeleri seri halinde düzenlenmiş gibi davranır:

double ar[3][2]={{1, 2},{3, 4},{5, 6}};
ArrayCopy(ar,ar,2,4);

Bu kodu çalıştırdıktan sonra dizi şu şekilde görünecektir: {1, 2}, {5, 6}, {5, 6}.


Bir Diziyi Sıralama

Bir dizi, ArraySort() fonksiyonu kullanılarak sıralanabilir:

double ar[]={1,3,2,5,4};
ArraySort(ar);

Yukarıdaki kodu çalıştırdıktan sonra, dizi değerleri aşağıdaki sırayla düzenlenecektir: 1, 2, 3, 4, 5.

ArraySort() fonksiyonu çok boyutlu dizilere uygulanamaz. Çok boyutlu dizileri ve veri yapılarını sıralamaya dair bilgiyi "MQL5'te Elektronik Tablolar" başlıklı makalede bulabilirsiniz.


İkili arama yapmak için ArrayBsearch() fonksiyonunu kullanırız. Bu fonksiyon yalnızca sıralanmış dizilerde düzgün çalışabilir. İkili arama, adını algoritmanın bir diziyi sürekli olarak iki parçaya ayırmasından alır. Algoritma ilk olarak hedef değeri dizinin orta öğesinin değeri ile karşılaştırır, böylece hedef öğeyi içeren yarıyı belirler: soldaki alt dizi veya sağdaki alt dizi. Ardından, hedef değeri alt dizilerin orta öğesinin değeri ile karşılaştırır ve bu böyle devam eder.

ArrayBsearch() fonksiyonu, hedef değere sahip öğenin indisini döndürür:

double ar[]={1,2,3,4,5};

int index=ArrayBsearch(ar,3);

Bu kodu çalıştırdıktan sonra indis değişkeni 2 değerinde olacaktır.

Hedef değer dizide bulunamazsa, fonksiyon en yakın daha küçük değere sahip öğenin indisini döndürecektir. Bu özellik sayesinde fonksiyon, çubukları zamana göre aramak için kullanılabilir. Belirtilen süreye sahip çubuk yoksa, hesaplamalarda daha az süreli bir çubuk kullanılmalıdır.

Hedef değer dizide yer almıyorsa ve dizi değerleri aralığının dışındaysa, fonksiyon 0'ı (hedef değer minimum değerden küçükse) veya son indisi (hedef değer maksimum değerden büyükse) döndürür.

Sıralanmamış bir dizide arama yapmanıza olanak sağlayan tek bir yöntem vardır: bir dizi üzerinde yineleme:

int FindInArray(int &Array[],int Value){
   int size=ArraySize(Array);
      for(int i=0; i<size; i++){
         if(Array[i]==Value){
            return(i);
         }
      }
   return(-1);
}

Yukarıdaki örnekte fonksiyon, hedef değere sahip öğenin indisini döndürür. Hedef değer dizide yer almıyorsa, fonksiyon -1 değerini döndürür.


Maksimum ve Minimum Değeri Bulma

Dizideki maksimum ve minimum değerler, sırasıyla maksimum veya minimum değere sahip öğenin indisini döndüren ArrayMaximum() ve ArrayMinimum() fonksiyonları kullanılarak bulunabilir:

double ar[]={3,2,1,2,3,4,5,4,3};

int MaxIndex=ArrayMaximum(ar);
int MinIndex=ArrayMinimum(ar);
double MaxValue=ar[MaxIndex];
double MinValue=ar[MinIndex];

Bu kodu çalıştırdıktan sonra, MaxIndex değişkeni 6'ya eşit olacaktır, MinIndex değişkeni 2'ye eşit olacaktır, MaxValue 5 değerine eşit olacaktır ve MinValue 1 olacaktır.

ArrayMaximum() ve ArrayMinimum() fonksiyonları, arama aralığındaki ilk öğenin indisini ve arama aralığındaki öğelerin sayısını belirterek arama aralığını sınırlamanıza olanak sağlar:

int MaxIndex=ArrayMaximum(ar,5,3);
int MinIndex=ArrayMinimum(ar,5,3);

Bu durumda MaxIndex 6 değerini alacaktır ve MinIndex - 5 olacaktır. Lütfen belirtilen aralığın minimum 4 değerine sahip iki pozisyon - pozisyon 5 ve pozisyon 7 - içerdiğini unutmayın. Böyle bir durumda fonksiyon, dizinin başlangıcına daha yakın olan öğenin indisini döndürür. Bu fonksiyonlar, ters sırada indislenen dizilerle aynı şekilde çalışır - en küçük indisi döndürür.

Böylece, dizilerle çalışmak için MQL5'te bulunan tüm standart fonksiyonları gözden geçirmiş olduk.


OOP Kullanarak Çok Boyutlu Diziler Oluşturma

Çok boyutlu diziler oluşturmak için sınıfların bir kümesi üç sınıf içerir: bir temel sınıf ve iki alt sınıf. Bir nesne oluşturma aşamasında seçilen alt sınıfa göre nesne, double değişkenlerinin bir dizisini veya nesnelerin bir dizisini temsil edebilir. Nesnelerin dizisinin her öğesi, nesnelerin veya değişkenlerin bir başka dizisini temsil edebilir.

Temel sınıf ve alt sınıflar, temel sınıftaki yıkıcılar ve alt sınıflardaki yapıcılar dışında hemen hemen hiçbir fonksiyon içermez. Temel sınıftaki yıkıcı, programın veya fonksiyonun tamamlanmasından sonra tüm nesnelerin silinmesi içindir. Alt sınıflardaki yapıcılar yalnızca, dizileri yapıcının parametrelerinde belirtilen boyuta göre ölçeklendirmek için kullanılır: bir sınıftaki nesnelerin bir dizisini ve diğer sınıftaki değişkenlerin bir dizisini ölçeklendirmek için. Aşağıda, bu sınıfların uygulanmasına yönelik kod verilmiştir:

//+------------------------------------------------------------------+
//|   Base class                                                     |
//+------------------------------------------------------------------+
class CArrayBase
  {
public:
   CArrayBase       *D[];
   double            V[];

   void ~CArrayBase()
     {
      for(int i=ArraySize(D)-1; i>=0; i--)
        {
         if(CheckPointer(D[i])==POINTER_DYNAMIC)
           {
            delete D[i];
           }
        }
     }
  };
//+------------------------------------------------------------------+
//|   Child class 1                                                  |
//+------------------------------------------------------------------+
class CDim : public CArrayBase
  {
public:
   void CDim(int Size)
     {
      ArrayResize(D,Size);
     }
  };
//+------------------------------------------------------------------+
//|   Child class 1                                                  |
//+------------------------------------------------------------------+
class CArr : public CArrayBase
  {
public:
   void CArr(int Size)
     {
      ArrayResize(V,Size);
     }
  };

Bu sınıfları ekteki CMultiDimArray.mqh dosyasında bulabilirsiniz. Dosya, terminal veri dizininin MQL5\Include klasörüne yerleştirilmelidir.

Şimdi bu sınıfı, tek boyutlu benzerlik dizisi oluşturmak için kullanalım:

CArrayBase * A; // Declare a pointer
   A=new CArr(10); // Load a child class instance that scales the array of variables. 
                   // The array will consist of 10 elements.

//--- Now the array can be used:
   for(int i=0; i<10; i++)
     {
      //--- Assign to each element of the array successive values from 1 to 10
      A.V[i]=i+1;
     }
   for(int i=0;i<10;i++)
     {
      //--- Check the values
      Alert(A.V[i]);
     }
   delete A; // Delete the object
  }

Bir betik biçimindeki bu örnek, ekteki sTest_1_Arr.mq5 dosyasında bulunabilir. Dosya, terminal veri dizininin MQL5\Scripts klasörüne yerleştirilmelidir.

Şimdi iki boyutlu bir dizi oluşturmaya çalışalım. Birinci boyutun her bir öğesi, ikinci boyutun farklı sayıda öğesini içerecektir - birincide bir, ikincide iki vb.:

CArrayBase*A;  // Declare a pointer
   A=new CDim(3); // The first dimension represents an array of objects

//--- Each object of the first dimension represents an array of variables of different sizes 
   A.D[0]=new CArr(1);
   A.D[1]=new CArr(2);
   A.D[2]=new CArr(3);
//--- Assign values
   A.D[0].V[0]=1;

   A.D[1].V[0]=10;
   A.D[1].V[1]=20;

   A.D[2].V[0]=100;
   A.D[2].V[1]=200;
   A.D[2].V[2]=300;
//--- Check the values
   Alert(A.D[0].V[0]);

   Alert(A.D[1].V[0]);
   Alert(A.D[1].V[1]);

   Alert(A.D[2].V[0]);
   Alert(A.D[2].V[1]);
   Alert(A.D[2].V[2]);
//---
   delete A; // Delete the object

Bir betik biçimindeki bu örnek, ekteki sTest_2_Dim.mq5 dosyasında bulunabilir. Dosya, terminal veri dizininin MQL5\Scripts klasörüne yerleştirilmelidir.

Sınıfların dizi boyutlarını değiştirmek için yöntemleri olmadığından, ortaya çıkan diziler bir nevi statiktir. Ancak bunlar, D[] ve V[] dizileri sınıfın genel bölümünde yer aldığından herhangi bir manipülasyon için kullanılabilir. Ve V[] dizisini herhangi bir zorluk çekmeden ölçeklendirebilirsiniz. D[] dizilerini ölçeklendirirken ve boyutlarını küçültürken, ilk olarak silinecek nesnelerle işaret edilen nesneleri silmelisiniz; veya dizi boyutlarını artırırken nesneleri bunlara yüklemelisiniz.

İstenirse, OOP veya veri yapıları kullanılarak çok boyutlu dizileri uygulamanın başka yolları da düşünülebilir.


Sonuç

Makale, dizilerle çalışmak için MQL5'te bulunan tüm standart fonksiyonları kapsar. Dizileri ele almak için özellikleri ve en önemli tekniklerden bazılarını gözden geçirdik. MQL5 dili, bazıları çok önemli olmakla birlikte bazıları alışılmadık bir sorunu çözmeniz gereken durumlar dışında tamamen kullanım dışı olabilen toplam 15 fonksiyon sunar. Fonksiyonlar, önem ve kullanım sıklığına göre aşağıdaki şekilde sıralanabilir:

  1. ArraySize() ve ArrayResize() temel fonksiyonlardır.

  2. ArrayMaximum(), ArrayMinimum(), ArrayCopy(), ArrayInitialize(), ArrayFill() ve ArrayFree() dizilerle çalışmayı önemli ölçüde daha kolay hale getiren fonksiyonlardır.

  3. ArraySort(), önemli ve kullanışlı bir fonksiyondur, ancak düşük fonksiyonelliği nedeniyle nadiren kullanılır.

  4. ArrayBsearch() nadiren kullanılan bir fonksiyondur, ancak nadir istisnai durumlarda çok önemli hale gelebilir.

  5. ArraySetAsSeries(), ArrayRange(), ArrayGetAsSeries(), ArrayIsDynamic() ve ArrayIsSeries() çok nadir kullanılan veya neredeyse hiç kullanılmayan fonksiyonlardır.

Bu makalede açıklanan programlama tekniklerinden biri olan dinamik dizilerin kullanımına özellikle dikkat edilmelidir, çünkü bunun program performansı üzerinde büyük etkisi vardır ve aslında bunu belirlediği söylenebilir.

MetaQuotes Ltd tarafından Rusçadan çevrilmiştir.
Orijinal makale: https://www.mql5.com/ru/articles/567

Ekli dosyalar |
cdynamicarray.mqh (2.59 KB)
stest_speed.mq5 (1.6 KB)
cmultidimarray.mqh (1.67 KB)
stest_1_arr.mq5 (1.29 KB)
stest_2_dim.mq5 (1.43 KB)
"Çalışma Sırasında" Kullanıcı Panelinden Uzman Danışman Parametrelerini Değiştirme "Çalışma Sırasında" Kullanıcı Panelinden Uzman Danışman Parametrelerini Değiştirme
Bu makale, bir Uzman Danışman uygulamasını gösteren küçük bir örnek sunar, bunun parametreleri kullanıcı panelinden kontrol edilebilir. Uzman Danışman, parametreleri "çalışma sırasında" değiştirirken, dosyadan daha fazla okumak ve bunları panelde uygun şekilde görüntülemek için bilgi panelinden alınan değerleri bir dosyaya yazar. Bu makale, manuel veya yarı otomatik modda alım satım yapanların dikkatini çekebilir.
MetaTrader 5 için Alım Satım Sinyalleri: PAMM Hesaplarına Daha İyi Bir Alternatif! MetaTrader 5 için Alım Satım Sinyalleri: PAMM Hesaplarına Daha İyi Bir Alternatif!
MetaTrader 5'in artık Alım Satım Sinyalleri içerdiğini ve böylece yatırımcılara ve yöneticilere güçlü bir araç sağladığını duyurmaktan mutluluk duyuyoruz. Siz başarılı bir yatırımcının alım satımlarını takip ederken, terminal otomatik olarak bunları hesabınıza kopyalayacaktır!
Makine Öğrenmesi: Destek Vektör Makineleri Ticarette Nasıl Kullanılabilir? Makine Öğrenmesi: Destek Vektör Makineleri Ticarette Nasıl Kullanılabilir?
Destek Vektör Makineleri, biyoinformatik ve uygulamalı matematik gibi alanlarda, karmaşık veri kümelerini değerlendirmek ve verileri sınıflandırmak için kullanılabilecek faydalı modellerini çıkarmak için uzun süredir kullanılmaktadır. Bu makale, destek vektör makinelerinin ne olduğunu, bunların nasıl çalıştığını ve karmaşık modelleri çıkarmada neden bu kadar faydalı olabileceklerini inceler. Daha sonra, bunların piyasaya nasıl uygulanabileceğini ve potansiyel olarak alım satım tavsiyelerinde bulunmak için nasıl kullanılabileceğini araştırıyoruz. Makale, okuyucuların Destek Vektörü Makine Öğrenme Aracını kullanarak kendi alım satımlarını denemelerine olanak sağlayan çalışılmış örnekler sunar.
Alım Satım Fikirlerinin Grafik Üzerinde Hızlı Testi Alım Satım Fikirlerinin Grafik Üzerinde Hızlı Testi
Makale, alım satım fikirlerinin hızlı görsel test yöntemini açıklar. Yöntem bir fiyat grafiği, bir sinyal göstergesi ve bir bakiye hesaplama göstergesinin kombinasyonuna dayanır. Alım satım fikirleri için arama yöntemimi ve bu fikirleri hızlı bir şekilde test etmek için kullandığım yöntemi paylaşmak istiyorum.