Bu siteye giriş yaparak Çerez kullanımını kabul etmiş oluyorsunuz. İşbu sitede; çerez olarak, sadece son giriş tarihiniz ve eğer üye olursanız oturum statünüz tutulacaktır. Bunlar dışında başka hiçbir bilgi tutulmamaktadır. Çerezler için detaylı bilgi için buraya tıklayınız.
ANLADIM

DUYURULAR

Yeni eklenen ve/veya güncellenen sayfaları görmek için buraya tıklayınız.

Güncel ödev ve test listesini görmek için buraya tıklayınız.

Sitede yapılan iyileştirmeAer ve hata düzeltmelerine ait tüm bilgilendirmeleri görmek içinburaya tıklayınız.

Baş
Udemy
Konular
Son
Konular
DizilerArrayler
DizilerveDizimsiYapilar
VBAMAkro
VBAMakro Diziler ve Dizimsi Yapılar 1

Diziler(Arrayler)

Giriş

Kodumuzda aynı türden bir veya iki farklı eleman kullanacaksak standart değişkenler yeterli olacaktır. Ancak bu sayı artarsa değişken kullanımı pek pratik olmamaya başlar. İşte bu nokta, dizilerin devreye girdiği yerdir.

Mesela 20 bölgesi olan bir bankada çalışıyorsanız her bölge için bir değişken tanımlamak yerine bölge adında bir dizi tanımlanıp, her bölge bu diziye elaman olarak atanabilir.  Karşılaştırmaya bakalım:

'pratik olmayan yöntem
Dim bölge1 As String
Dim bölge2 As String
.....
Dim bölge20 As String

Bir de bunun şube versiyonu var, ki şube sayısı 1000 civarındaysa değişken tanımlarken ömrünüzden 1 yıl gider herhalde.

Peki şimdi nedir bu diziler, nasıl tanımlanır, başka neler yapılır, bunlara bakalım.

Genel bir bilgi edindikten sonra buraya da bakmanızı tavsiye ederim. Özellikle başka programlama dillerine aşinaysanız aradaki farkları ve benzerlikleri görmek için faydalı olacağını düşünüyorum.

Temel bilgiler

Tanımlama(Declaration)

Diziler, normal değişkenler gibi Dim ifadesi ile tanımlanırlar, ancak değişken adının yanında fazladan yuvarlak parantezlere sahiptirler. Parantezler; dizi boyutu baştan belliyse boyut numarasını içerirler, belli değilse boş bırakılır ve sonradan tanımlanır. Dizi boyutu baştan belirlenen dizilere Statik dizi, boyutu baştan belirtilmeyen dizilere Dinamik dizi denir.

Dim diziadı(boyut) As Tip 

Tip belirtilmezse tıpkı normal değişkenlerde olduğu gibi dizimiz Variant tipli bir dizi olur ve her tür değişkeni karışık olarak depolayabilir.

Dim bölgeler(19) As String 'boyut belirtildi
Dim şubekod() As String 'boyut belirtilmedi

Bu örnekte bölge sayısı sabit olduğu için boyut baştan belirtildi, yani Statik tanımlandı ancak bir bankada şube sayısı çok sık değişebilir; yeni şubeler açılır, mevcut şubeler kapanır, şubeler birleşir;o yüzden ona baştan bir boyut belirtmesek de olur, yani Dinamik tanımlandı. Bu dinamik dizilere aşağıda ayrıca bakıyor olacağız.

Boyut(elaman sayısı), index, alt/üst limitler

Dizi için tanımlanan eleman sayısına boyut denir. Boyut belirtmenin de iki yolu vardır.

  • Dim dizi(x to y) As Tip
  • Dim dizi(y) As Tip

İlk yöntemde dizinin kaçıncı indexten başlayıp kaçta biteceği belirtilirken ikinci yöntemde ise doğrudan kaçta biteceği belirtilir, kaçta başlayacağı ise Option Base ifadesinin kullanılıp kullanılmadığına göre değişir. İndeksin biteceği son yere üst sınır denir ve bu sınır Ubound fonksiyonu ile elde edilir.

Dim segmentler(4) As String
segmentler(0)="Bireysel"
segmentler(1)="Birebir"
segmentler(2)="Kobi"
segmentler(3)="Ticari"
'segmentler(4)="Özel"

For i=0 to UBound(segmentler)
    MsgBox segmentler(i)
Next i

Yukardaki örnekte gördüğünüz üzere son indexli elamana değer atanmadı, bu yüzden de boş olarak göründü. Anlayacağınız üzere, dizideki tüm elemanlara değer atanmak zorunda değildir. Yani, bir dizide eleman sayısı ile dolu(değer atanmış) eleman sayısı aynı olmayabilir.

Varsayılan olarak dizilerin ilk eleman indeksi 0'dır. Ancak bu index Option Base 1 ifadesi ile 1 yapılabilir, ki ben bunu zorunda olmadığınız sürece çok kullanmanızı tavsiye etmiyorum. Veya yukarda 1. yöntemdeki gibi dizi boyutu baştan 1 to y şeklinde belirtilerek de başlangıç indexi 1 yapılabilir. Yani şu iki ifade tamamen özdeştir.

Option Base 1
Dim bolgeler(20) As String

ve

Dim bolgeler(1 to 20) As String

İndex numarası açısından olmasa da eleman sayısı açısından şu dizi de yukardakilerle aynı kapasitededir, yani hepsi de 20 eleman içerir.

Dim bolgeler(19) As String

Dizi tanımının yönteminden ve Option Base kullanımından bağımsız olarak alt indexin ne olduğu ise LBound fonksiyonu ile elde edilir.

Option Base 1 kullanımını tavsiye etmiyoruz dedik, zira ilgili modüldeki tüm prosedürler için indexi 1den başlatır. Bununla beraber bazı dizileri bilinçli olarak 1 nolu indeksten başlatmak gerekebilir. Mesela ayno isimli bir dizimiz olduğunu düşünün, ayno(1) diyince Ocak ayını ele almak, anlaşılırlık açısından daha makbuldür, pek tabiki ayno(1) içinde Şubat ayı da depolanabilir ancak bu yol, konuşma diline biraz aykırılık teşkil edeceği için bunu ayno(1 to 12) şeklinde tanımlamayı tercih etmek daha akıllıca olacaktır.

Bu arada x to y yönteminde x olarak 1'den büyük değerler de belirtilebilir ama bunun pratikte çok kullanıldığı görülmez.

Çok boyutlu dizilerde eleman sayısı, boyutlardaki elemanların çarpımına eşittir.(Bunlar aşağıda ayrıca detaylı incelenecek)

Özetleyecek olursak;

Dim b(10) As String şeklinde tanımlanan 1 boyutlu bir dizide;

Aranan Yöntem Sonuç
Alt limit LBound(b) 0
Üst limit UBound(b) 10
Eleman sayısı Ubound(b)-LBound(b)+1 11

Dim b(1 to 10) As String şeklinde tanımlanan 1 boyutlu bir başka dizide;

Aranan Yöntem Sonuç
Alt limit LBound(b) 1
Üst limit UBound(b) 10
Eleman sayısı Ubound(b)-LBound(b)+1 10

Dim b(10,5) As Integer şeklinde tanımlanan 2 boyutlu bir dizide;

Aranan Yöntem Sonuç
1.boyutun Alt limiti LBound(b,1) 0
1.boyutun Üst limiti UBound(b,1) 10
2.boyutun Alt limiti LBound(b,2) 0
2.boyutun Üst limiti UBound(b,2) 5
1.boyutun Eleman sayısı Ubound(b,1)-LBound(b,1)+1 11
2.boyutun Eleman sayısı Ubound(b,2)-LBound(b,2)+1 6
Dizideki Eleman sayısı 1. boyut elaman sayısı*2.boyut eleman sayısı 66

Dikkat:Bir dizinin boyutu baştan birkez belirtilirse bir daha asla değişmez. Boyutu değişen dizilere ise aşağıda değineceğiz.

Elemanlara değer atama

İlk değer atama

Dizi elamanlarına index numaralarıyla ulaşırız. Normalde bunlara tek tek değer atamak genelde pratikte karşılaşılan bir durum değildir, belki küçük boyutlu dizilerde olabilir ancak genelde bir döngüsel yapı ile bir hücre grubundan değer okuyup onları atamak şeklinde olmaktadır. Aksi halde kodumuz oldukça uzayacaktır.

Dim Segment(3) As String
Segment(0) = "Bireysel"
Segment(1) = "Birebir"
Segment(2) = "Kobi"
Segment(3) = "Ticari"

Döngüyle atamaya örnek olarak da şunu verebiliriz;A1:A20 arasındaki bölge kodlarını bölge dizisine atıyoruz.

For i=1 to 20
   bölge(i)=Cells(i,1).Value2
Next i

Değer atanmamış elemanlar ve Erase fonksiyonu

Henüz değer atanmamış dizi elemanları, dizinin tipine göre default değerlerini alırlar. Bunlar;

  • String diziler için sıfır uzunluklu metin, yani ""
  • Nümerik diziler için 0
  • Variant diziler için Empty
  • Object diziler için Nothing

Statik dizilerde elemanların hepsini tek seferde varsayılan değerlerine döndürmek için Erase fonksiyonu kullanılır. Yani dizi elemanlarının taşıdığı değerler boşaltılır. Ancak bunlar hala hafızada yer kaplamaya devam ederler.

Sub erasestatik()
  Dim segment(2) As String
  segment(0) = "bireysel"
  segment(1) = "birebirt"
  segment(2) = "kobi"

  Erase segment()
  
  Debug.Print segment(1) '"" döndürür
End Sub

Array fonksiyonu

Elle tek tek tanımlama yapılması gereken durumlarda, bir diğer eleman tanımlama yöntemi de Array fonksiyonunu kullanmaktır, ki bu sadece elaman değeri atama değil aynı zamanda diziyi tanımlama yöntemidir de. Zira bu şekilde tanımlanan diziler Variant tipte olurlar ve bildiğiniz gibi Dim ile tanımlanmayan tüm değişkenler Variant kabul edilirler. Bu sayede elemanlar tanımlanırken dizi de yaratılmış olur. Ama biz yine de iyi bir programcı olup dizimizi Dim ile tanımlayalım. Bu yöntemle tanımlanan dizilerde başlangıç indeksi her zaman 0 olur. Yine, bu yöntemle yaratılan diziler 1 boyutlu olur

Dim Segment As Variant
Segment = Array("Bireysel","Birebir","Kobi","Ticari") 'tek satırda tanımlama imkanı

Debug.Print Segment(2) 'Kobi yazar

Bu yöntemin pratik bir kullanımı "ayisim" gibi bir fonksiyon şeklinde kendini gösterebilir. Fonksiyonlarla kafanızı karıştırmamak için şimdilik Sub prosedür şeklinde örneğe görelim.

Sub ayisimornek()
  Dim ayisim As Variant
  ayisim = Array("", "Ocak", "Şubat") 'ilk ayı boş geçtim, index 0 olduğu için
  Debug.Print ayisim(2)
End Sub

Dizi içinde dolaşma ve elemanlara erişim

Dizilere eleman atama işini döngülerle yaptığımız gibi, dizi elamanlarını okumayı da yine genelde döngülerle yaparız. Ender olarak kod içinde bir yerde bir index numarası temin edip onu doğrudan da kullandığımız da olur.

For i=1 to 20
   Cells(i,1).Value2=bölge(i)
Next i

Daha şık şekli aşağıdaki gibi olabilir, hatta sadece şık değil aynı zamanda güvenlidir de.

For i=LBound(bölge) to UBound(bölge)
   Cells(i,1).Value2=bölge(i)
Next i

Evet, biraz daha fazla kod yazmış  olduk ama güvenlik ön planda olacaksa hard-code(rakamı doğrudan belirterek) alt-üst limit vermek yerine bu fonksiyonlarla vermek daha verimlidir. Hard-code yazıldığında, bölge sayısı 1 arttığında 20 yerine 21 yapmayı unutursanız kodunuz yine hata almadan çalışır ancak eksik çalışmış olur, ve belki akabinde çalışan kodlarla bölgelere otomatik mail gidiyorsa, son bölgeye hiç mail gitmemiş olur. Bir de içiçe bir dizi varsa ve her seviye için üst limit değişiyorsa Ubound'ı kullanmak zaten zaruri olacaktır.

Üstelik bu yazım şekli alt limitin 0 mı 1 mi olduğunu da pek önemsemez . Böylece Option Base var mıydı yok muydu, diziyi (1 to 20) şeklinde mi yoksa (20) şeklinde mi tanımladığınızı hatırlamak zorunda kalmazsınız.

dikkat:Dizi içinde For döngüsü ile dolaşırken diziye eleman atama işlemi sadece For i=1 to 10 şeklindeki basit For döngüsü ile yapılabilirken, eleman değerini okuma işlemi ise hem basit For ile hem de For Each ile yapılabilir.

Mesela aşağıdaki gibi bir kullanım sorunsuz çalışırken,

Dim Bölge(1 to 20) As String
For i=1 to 20
   bölge(i)=Cells(i,1).Value2
Next i

bu kod da hata vermeden çalışır ancak eleman değerlerinin değişmediğini görebilirsiniz. (Eğer daha önce bir değer atanmadıysa içleri boş görünecektir.)

Dim Bölge(1 To 20) As String

For Each b In Bölge
   b = ActiveCell.Value2
   ActiveCell.Offset(1, 0).Select
Next b

İleri seviye işlemler

Dinamik diziler

Yukarıdaki örnekler hep statik dizi örnekleriydi(Array fonksiyonu ile tanımlananlar hariç)

Dizimizin boyutunu baştan bilmiyorsak ve kodun gidişatına göre değişken bir şekilde karşımıza çıkma durumu varsa, diziyi boyutsuz yani dinamik tanımlarız ve zamanı geldiğinde boyutunu belirtiriz. Bunu da ReDim ifadesi ile yaparız. Aslında yaptığımız şey, yeni bir statik dizi yaratmaktır. Zira ReDim kullanıp da yeni boyutunu verdiğimiz diziyi yine yeterli bulmazsak tekrar boyut değiştirebiliriz.

Dim şubeler() As String
....
....
ReDim şubeler(800)

Statik dizilerde, genelde kaynak israfı sözkonusudur, zira baştan olası en yüksek değere göre boyut belirlenir ve bu boyutların hepsi çoğu zaman kullanılmaz. Tabiki bölge sayısı gibi kesin olarak bilinen ve hepsi kullanılan durumlar istisnadır. Bu nedenle genel olarak Statik değil Dinamik dizi kullanılması önerilir.

Erase fonksiyonu

Dinamik dizilerde Erase fonksiyonu statik dizilerden farklı çalışır. Statik dizilerde Erase, elemanları default değerlerine atarken dinamik dizide diziyi boyutsuzlaştırır, yani bir nevi siler. Diziyi tekrar kullanmak isterseniz ReDim ile yeniden boyutlandırmanız gerekir.

İlave boyut artışı

Diyelim ki ReDim ile 10 elemanlık bir boyutlandırma yaptınız ancak öyle bir nokta geldi ki, 10 eleman yetmiyor, yani boyut artırmanız lazım, böyle bir durumda ReDim Preserve ifadesini kullanırız. Preserve demezsek dizi yine yeni boyuta göre boyutlanır ancak önceki elemanlar silinmiş olur. Preserve ile ilk 10 elemanın değerini de korumuş oluruz.

Boyut artırma durumların ReDim'in bir veya iki kez kullanılması önerilir. Birkaç kez boyut artırma ihtiyacı oluyorsa belki dizi değil de Collection kullanmak faydalı olabilir. 

NOT:Variant olarak tanımlanan değişkenler doğası gereği dinamiktirler.(Variant tanımlanan diziler ise statiktir)

Dim statikVar As Variant 'standart Variant değişken
Dim dinamikVar(5) As Variant 'Variant tipli dizi

Variant değişken vs Variant dizi

Variantlarla ilgili detaylı bir örnek aşağıda bulunmaktadır. Bunun oldukça aydınlatıcı olduğunu düşünüyorum.

Sub variantlı()
Dim aylar1 As Variant 'içine istediğiniz tipte değer atayabileceğiniz bir DEĞİŞKENDİR, buna dizi de dahildir,
'ama dizi atayana kadar dizi değildir ve IsArray testi false döner. Ubound da kullanamazsınız
Dim aylar2() As Variant 'Parantez olduğu için bu kesinlikle DİZİDİR, IsArray true döner

'***öncelikle parantezsiz olan yani değişken olan Aylara bakalım*****
aylar1 = 1 'number atandı
Debug.Print aylar1
'Debug.Print aylar1(1) 'hata verir, çünkü içeriği henüz bir dizi değil.
Debug.Print IsArray(aylar1) 'false
'Debug.Print UBound(aylar1) 'çalışmaz çünkü şuan için dizi değil
aylar1 = "volki" 'string
Debug.Print IsArray(aylar1) 'yine false
Debug.Print aylar1
aylar1 = Now 'tarih
Debug.Print IsArray(aylar1) 'hala false
Debug.Print aylar1
aylar1 = Array("hi", "world", "naber") 'dizi
Debug.Print IsArray(aylar1) 'artık true, çünkü Array function ile array yaptık
Debug.Print UBound(aylar1)
Debug.Print aylar1(2) 'artık hata vermez, çünkü içeriği bir dizi
aylar1 = Array(Array("hi", "world", "naber"), Array(1, "ss", 3)) 'dizi dizisi
Debug.Print UBound(aylar1)
Debug.Print aylar1(1)(1)
aylar1 = Range("a1:a5").Value ' range atadık, 2 boyutlu dizi
Debug.Print UBound(aylar1) '5 döner
Debug.Print aylar1(2, 1) '2.satırdaki değer döner

'***şmdi de Dizi olan Aylara bakalım
Debug.Print IsArray(aylar2)
'Debug.Print UBound(aylar2) hata verir, zira henüz boyut belli değil, ya ReDim yapılmalı ya da Array ile değer atanmalı
ReDim aylar2(3)
Debug.Print UBound(aylar2)
aylar2 = Array("OCAK", "ŞUBAT", "MART", "NİSAN", "MAYIS", "HAZİRAN", "TEMMUZ", "AĞUSTOS", "EYLÜL", "EKİM", "KASIM", "ARALIK") '
Debug.Print UBound(aylar2)
Debug.Print aylar2(2)
Debug.Print UBound(aylar2)
'şimdi de farklı data tiplerinde atama yapıyoruz, Variant olduğu için sorun olmuyor
aylar2 = Array("OCAK", 2, 3, "30.04.2015", Now + 60, "HAZİRAN", "TEMMUZ", "AĞUSTOS", "EYLÜL", "EKİM", "KASIM", "ARALIK") ' variant olduğu için içine karışık tipli verilen atayabilirm
Debug.Print aylar2(4)

End Sub

Çok boyutlu diziler

Şimdiye kadar gördüğümüz dizilerin çoğu tek boyutlu idi, ama ihtiyacımıza göre birden fazla boyutlu diziler de oluşturabiliyoruz. Pratikte üçten fazla boyutun kullanıldığını ben açıkçası ne duydum ne de gördüm. Şahsen kendi kodlarımda kullandığım dizilerin büyük kısmı tek boyutludur, birkaç tane 2 boyutlu bir tane de 3 boyutlu dizim bulunmakta. 3 boyutlu dizi örneğinde aynı zamanda Dictionary de kullandığım için bu örneği o bölümde ele alacağız.

Şimdi 2 boyutlu bir dizi kullanım örneğine bakalım. Excel sayfalarının kendileri zaten satır ve sütunlardan oluşmakta olup 2 boyutludurlar ve bu durumu 2 boyutlu dizilerle birlikte çok sık kullanıyor olacağız.

Mesela 10 bölgesi olan bir bankada her bölgenin bir Bireysel bir de Ticari müdürü olduğunu düşünelim. Bunları bir diziye atama işlemi nasıl oluyor ona bakalım.

Sub ikiboyutludizi()
Dim mudur(1 To 10, 1 To 2) As Long 'ilk boyut bölge için, ikinci boyut segment tipi için
Dim i As Integer, j As Integer

For i = LBound(mudur, 1) To UBound(mudur, 1)
    For j = LBound(mudur, 2) To UBound(mudur, 2)
        mudur(i, j) = Cells(i + 1, j + 1).Value
    Next j
Next i

Debug.Print mudur(3, 2)
End Sub

Tabi bunun için aşağıda başka bir yöntem var ki, bundan çok daha basittir.

İçiçe diziler(Dizi dizileri)

İngilizcede Array of Array ve Jagged Array olarak kullanılan bu dizi türünü Variant tipte dizi tanımlayarak elde ederiz. Bilindiği gibi Variant veri tipi içinde herşeyi tutabilir, buna diziler de dahildir. Yanlız burdaki ayrıma dikkat etmek lazım, klasik Variant bir değişken tanımlamıyoruz, Variant tipli bir dizi tanımlıyoruz. Aradaki ayrımı görmek için şu iki satıra bakalım:

Dim d As Variant 'bu Variant tipli klasik bir değişkendir
Dim d() As Variant 'bu ise Variant tipli bir dizidir

İçiçe dizilerin tanımlaması iki ayrı dizi şeklinde olur, ama kullanımı dizi(x)(y) şeklindedir. Yani aslında bu dizi türü tek boyutludur ama içerdiği her elaman da bir başka dizidir.

Dim dışdizi() As Variant 'ana dizi Variant olmalıdır
Dim içdizi() As String ' Alt dizinin Variant olması gerekmez

'iç diziyi dış diziye eleman olarak atama
dışdizi(n)=içdizi
'nihai kullanım şekli
Debug.Print dışdizi(x)(y)

Bu dizilerin kullanımı çok boyutlu dizilere benzer ancak, kullanım ihtiyacı ve şekli küçük farklar göstermektedir. Şöyle ki, çok boyutlu dizilerde tümboyutlar için hafızada yer ayrılır, belki o boyutlardan bazısı hiç kullanılmayacak bile. Diyelim 20 bölgeli bir banka var, bazı bölgelerde 40 şube varken bazısında 15 şube bulunabilir. Şimdi Şubeler(1 to 20, 1 to 40) şeklinde 2 boyutlu bir dizi tanımlarsak bazı bölgelerde bazı boyutlar israf olacak. İşte içiçe dizilerde bu israf olmuyor. Örneğin 2.bölgenin şube sayısı 18 ise, burdaki 18.şubeyi tanımlayacak son elaman bölge(2)(18) oluyor, bölge(2)(19) şeklinde bir tanımlama yapılmıyor.

Mesela aşağıdaki tabloya göre içiçe dizimizi oluşturalım.

Sub içiçedizi()
Dim bolge(1 To 3) As Variant 'dış dizi 
Dim subeler() As String 'iç dizi
Dim i As Integer, s As Integer, k As Integer

For i = 1 To 3
    ReDim subeler(1 To Cells(i + 1, 2)) 'iç dizinin boyutunu ayarlıyoruz, her bölgede bu boyut değişecek
    
    'iç diziyi oluşturalım
    For s = 1 To UBound(subeler)
        subeler(s) = Cells(s + k + 1, 6)        
    Next s

    k = k + s - 1 'satır sayısı resetlenmesin diye geçici bir değişkene atıyorum    
    
    'iç diziyi dış dizinin ilgili elemanına atayalım
    bolge(i) = subeler

Next i

Debug.Print bolge(1)(5) 'Şube5
Debug.Print bolge(2)(2) 'Şube8
Debug.Print bolge(3)(4) 'hata verir, çünkü 3.bölgenin 4.şubesi yok
End Sub

Dizi dizisiyle yapılabilen işlemler "Collection of Collection" veya "Dictionary of Dictionary" yapılarıyla da yapılabilir. Bunları bir sonraki bölümde görüyor olacağız. Bu arada hepsinin avantaj ve dezavantajını anlatan güzel bir sayfa var, ona buradan ulaşabilirsiniz.

Rangeler ve Diziler

Bir hücre grubunu diziye atama(Sayfadan okuma)

Dizileri, bir hücre grubundan hızlı veri okuma ve yazma amacıyla da kullanırız. Bu kullanım şekli, özellikle ilgili veri kümesi üzerinde bir güncelleme(belli bi rakamla çarpmak gibi) yapmak istediğinizde idealdir.

Tabi bu amaçla kullanmak istediğimizde standart dizi tanımı yerine Variant olarak tanımlarız, ve tanımladığımız dizi 2 boyutlu bir dizi olur. Zira bir sayfaya baktığınızda gördüğünüz şey satır ve sütunlardan oluşan iki boyutlu bir dizidir. Böyle bir dizide de ilk boyut satır ikinci boyut sütun olur. Bu noktada karışıklığa neden olan bir konu vardır ki o da şudur: Sözkonusu hücre grubu tek kolonluk bir alandan oluşuyorsa bile iki boyutludur. İlk boyut satır sayısı kadar elemanlı, ikinci grup da sütun sayısı olan 1 elamanlıdır. Bu tür dizilerde indeks 0dan değil 1den başlar.(Tek boyutlu variant dizilerde ise 0'dan başlıyordu)

Mesela aşağaıdaki kod ile A1:A1000 hücrelerindeki değerler 100 ile çarpılır.

Sub rakamguncelle()

Dim rakamlar As Variant 'Dizi tanımı
rakamlar = Range("A1:A10000").Value 'burada hücreden diziye okuma yaptık

For i = LBound(rakamlar) To UBound(rakamlar) 'i'ler satır boyutudur
  rakamlar(i, 1) = rakamlar(i, 1) / 100 '1 ise sütun boyutu
Next i

Range("A1:A10000").Value = rakamlar 'üzerine de yazabilriz başka bir yere de yazabilirdik

End Sub

Alanımız tek kolon değil de birden çok kolondan oluşuyorsa sütun için de bir iç döngü daha eklememiz gerekir. Bu sefer hücre grubumuzu dinamik düşünelim, buna göre kodumuzu aşağıdaki gibi olacaktır.

Dim rakamlar As Variant 'Dizi tanımı
rakamlar = Selection.Value 'burada hücreden diziye okuma yaptık
 
For i = LBound(rakamlar) To UBound(rakamlar) 'i'ler satır boyutudur
    For j = LBound(rakamlar, 2) To UBound(rakamlar, 2) 'j'ler satır boyutudur
        rakamlar(i, j) = rakamlar(i, j) * 1000
    Next j
Next i
 
Selection.Value = rakamlar

Tek boyutlu bir diziyi sayfaya yazdırma

Yazdırma işlemi yapmak için Resize özelliği ile hücreleri yeniden boyutlandırmayı bilmeniz gerekiyor. Resize hakkında bilgi sahibi değilseniz buradan bakıp önbilgi edinebilirsiniz.

Bu işlem için bir Range nesnesi yaratırız ve sonra hedef hücreyi belirleriz. Sonra bu hücreyi Resize ile dizi boyutu kadar genişletiriz.

' Yatay yazma(Tek satırlı çok kolonlu)
Dim dizi(1 To 3) As Integer
Dim Hedef As Range

dizi(1) = 10
dizi(2) = 20
dizi(3) = 30

Set Hedef = Range("A1").Resize(1, UBound(dizi))
Hedef.Value = dizi

Eğer dizimizi dikey şekilde yani tek kolon çok satırda yazmak isteseydik, Resize argümanlarını ters çevirmemiz ve Transpose metodunu kullanmamız gerekirdi.

' Dikey yazma(Tek kolonk çok satır)
Dim dizi(1 To 3) As Integer
Dim Hedef As Range

dizi(1) = 10
dizi(2) = 20
dizi(3) = 30

Set Hedef = Range("A1").Resize(UBound(dizi), 1)
Hedef.Value = WorksheetFunction.Transpose(dizi)

İki boyutlu dizileri sayfaya yazdırma

İki boyutlu bir dizimizi sayfaya yazdırmak istiyorsak yine Resize metodunu kullanırız, ilk boyutu satır olarak, ikinci boyutu da kolon olarak kullanabiliriz, veya tam tersini de istersek yerlerini değiştirip Transpoze yaparız.

Dim dizi(1 To 3, 1 To 2) As Integer
Dim alan As Range

dizi(1, 1) = 10
dizi(2, 1) = 20
dizi(3, 1) = 30
dizi(1, 2) = 100
dizi(2, 2) = 200
dizi(3, 2) = 300

Set alan = Range("A1").Resize(UBound(dizi, 1), UBound(dizi, 2))
alan.Value = dizi

Transpoze hali de şöyle:

Dim dizi(1 To 3, 1 To 2) As Integer
Dim alan As Range

dizi(1, 1) = 10
dizi(2, 1) = 20
dizi(3, 1) = 30
dizi(1, 2) = 100
dizi(2, 2) = 200
dizi(3, 2) = 300

Set alan = Range("A1").Resize(UBound(dizi, 2), UBound(dizi, 1))
alan.Value = WorksheetFunction.Transpose(dizi)

Dizilerin diğer kullanma şekilleri

Dizileri dizilere atama

Gelişmiş programlama dilllerinde olduğunun aksine VBA'de bir diziyi tek seferde başka bir diziye kopyalama/atama yöntemi bulunmamaktadır. Yani şöyle bir kod çalışmaz.

Dim A(1 To 3) As Long
Dim B(1 To 3) As Long

A(1)=100000
A(2)=200000
A(3)=300000
B=A

Peki nasıl yapılır? Tabiki döngülerle;

Dim A(1 To 3) As Long
Dim B(1 To 3) As Long

A(1)=100000
A(2)=200000
A(3)=300000

For i=1 to 10
   B(i)=A(i)
Next i

Debug.Print B(2)

Variant dizileri Variant dizilere atama

Dizileri başka dizilere atayamıyoruz ancak içi dizi olan bir Variantı başka bir Varianta atayabiliyoruz. Zira, bu işlem aslında dizi atama değil değişken atamadır.

Dim A As Variant
Dim B As Variant
A = Array(100000, 200000, 30000)
B=A 'atama başarılıdır
Debug.Print B(2) '30000 yazar

Range atanmış Variant dizileri normal dizilere atama

Bir hücre grubunu doğrudan bir variant diziye atama şeklini yukarda görmüştük, ama bir şekilde bunları tek boyutlu bir diziye atamanız gerekirse bunu yapmanın yolu döngü kullanmaktır. Tabi istenirse arada hiç variant dizi olmadan da doğrudan ilgili alanlar üzerinden de geçilebilir ancak, eğer bir şekilde ilgili alan kod içinde silindiyse, veya büyük bir alan ise peformans sorunu yaşamamak için Variant diziden normal diziye atamak daha verimli olacaktır, zira diziler üzerindeki işlemler range'lere göre çok daha hızlıdır.

Şimdi aşağıdaki gibi bir hücre grubu olsun, bunu önce Varianta sonra da normal bir diziye atayalım.

Sub birkolonlu_variant_to_dizi()
Dim var As Variant
Dim dizi() As String
Dim i As Integer

var = Range("B2:B10").Value2
ReDim dizi(1 To UBound(var, 1))

For i = 1 To UBound(var, 1)
    dizi(i) = var(i, 1)
Next i

MsgBox dizi(3)
End Sub

Şimdi bunu bir de çok boyutlu dizide yapalım

Sub çokkolonlu_variant_to_dizi()
Dim var As Variant
Dim dizi() As String
Dim s As Integer, k As Integer, i As Integer

var = Range("B2:D10").Value2
ReDim dizi(1 To UBound(var, 1) * UBound(var, 2))

s = 0
For k = 1 To UBound(var, 2)
   For i = 1 To UBound(var, 1)
     dizi(i + s) = var(i, k)
   Next i
   s = s + UBound(var, 1) 'her boyut geçişinde tekrar başa dönmesin diye aratoplam alınır
Next k

MsgBox dizi(15)
End Sub	

Aynı diziyi iki boyutlu bir standart diziye atamak da aşağıdaki gibi olurdur.

Sub çokkolonlu_variant_to_çokboyutludizi()
Dim var As Variant
Dim dizi() As String
Dim s As Integer, k As Integer, i As Integer

var = Range("B2:D10").Value2
ReDim dizi(1 To UBound(var, 1), 1 To UBound(var, 2))

For k = 1 To UBound(var, 2)
  For i = 1 To UBound(var, 1)
    dizi(i, k) = var(i, k)
  Next i
Next k

MsgBox dizi(2, 3)
End Sub

Dizileri collectiona atama

Bazı durumlarda elimizdeki diziyi daha fazla esnekliği olan ve bazı yönlerden dizilere benzeyen collectionlara atamak isteriz. Ancak colletion konusunu henüz işlemediğimiz için(siteyi sırayla okuduğunuz varsayımıyla hareket ederek) bu durumu collection sayfasında ele alacağız.

Dizileri bir prosedüre parametre olarak gönderme

Dizileri bir Sub veya Function prosedüre parametre olarak gönderebiliriz. Unutulmaması gereken nokta, diziler her zaman ByRef anahtar kelimesi ile gönderilir. Aşağıda basit bir örneğimiz bulunuyor.

Sub dizigonder()
Dim d(5) As String
'sadece iki elemanı dolduralım
d(0) = "volkan"
d(1) = "meltem"

Call mesajver(d)
End Sub


Sub mesajver(ByRef dz() As String)
    For Each a In dz
        If a <> "" Then i = i + 1
    Next a
eleman = UBound(dz) - LBound(dz) + 1
MsgBox eleman & " adet elemanın " & i & " tanesi doludur"
End Sub	

Prosedürden sonuç olarak dizi döndürme

Fonksiyonlar konusunda göreceğimiz gibi, genelde bir fonksiyondan sadece tek bir değer döndürülür. Aslında bu birçok yerde doğru diye anlatılan yanlış bir bilgidir. Genelde durum böyledir ancak fonksiyonlar tek bir değer döndürmek zorunda değildir, sadece dönüş değeri tek olmak zorundadır. Zira gerek ByRef kullanımı, gerek dönüş değerinin dizi/collection olması sayesinde fonksiyonlar pekala çoklu değer döndürebilirler. Aşağıdaki örnekte çoklu değer olarak dizi döndürüyoruz.

SSub diziata()
    'boyutsuz dizi yaratılır
    Dim d() As Integer
    
    d = dizidondur '6 değerli bir dizi döner, istediğimizi kullanırız
    Debug.Print d(3)
End Sub

Function dizidondur() As Integer() 'dönen değer tipi () ile biter ki dizi döndürdüğü anlaşılsın    
    Dim dizim(0 To 5) As Integer
    
    For x = 0 To 5
        dizim(x) = 10 * x
    Next x

    dizidondur = dizim
End Function				

Split ve join

Split

Dizilerle kullandığımız çok faydalı iki fonksiyon vardır.

Bunlardan ilki Split fonksiyonudur. Bununla, belirli bir ayraçla birleşmiş olan bir String ifade parçalara ayrılarak bir Variant içine veya klasik bir dizi içine aktarılır. Gördüğünüz gibi bu da aslında bir nevi dizi tanımlama yöntemi olmaktadır.

Bu fonksiyon, genelde bir hücreden ";" veya "," ile ayrılmış elemanları tek tek elde etmek için kullanılır.

Sub splitornek()
    Dim s As String
    s = "35516;37770;34234"

    Dim dizi As Variant 'veya Dim dizi() as String şeklinde boyutsuz dizi
    dizi = Split(s, ";")

    Debug.Print dizi(0) '35516 döner
End Sub			

Bu Split fonksiyonunu kullanarak bir UDF yazmıştım, ki bu sayfayı yazarkenki en yüksek Excel sürümü olan Excel 2016 içinde bile bu fonksiyon hala yerel olarak bulunmuyor. Bunu anlamak gerçekten zor, zira çok ihtiyaç duyulan bir fonksiyon olduğunu düşünüyorum. Neyseki VBA ve UDF var da başımızın çaresine bakabiliriyoruz.

Bu fonksiyon, bir metindeki belirtilen indeksteki kelimeyi seçiyor. Ayracı default olarak " " yani boşluk belirledim, ancak isteyen farklı bir ayraç da belirleyebiliyor. İşte bu fonksiyonum:

Function kelimesec(hucre As Range, kaçıncı As Byte, Optional ayrac As String = " ")
'normal bir cümlede ayrac boşluk olacağı için ayracı girmeye gerek yok, zaten default olarak " " atadım.
'ama içeriği mesela / ile ayrılmış bir hücre varsa 3.parametreyi / olarak girersiniz
  Dim kelimeler As Variant
  kelimeler = Split(hucre.Value2, ayrac)
  kelimesec = kelimeler(kaçıncı - 1)
End Function

Join

Diğer faydalı fonksiyon da Join olup bir dizi içindeki elemanları, belirtilen ayraçla birleştirip bir String ifade döndürür, yani Splitin tersi gibi çalışır.

Sub joinornek()
Dim b As String
Dim v As Variant
v = Array(35516, 34433, 32335)

b = Join(v, ";")
Debug.Print b

End Sub

Bu fonksiyon sadece bir boyutlu dizilerde çalışır. O yüzden bir Range'den okunan ve 2 boyutlu olan bir Variant dizide işe yaramayacaktır. Ama bunu yapmanın da bazı yolları var. Yukarda Variant dizileri klasik dizilere çevirmeyi görmüştük, yine böyle yaparız ve gerisi kolay.

1.Yöntem:Variant diziyi normal diziye dönüştürmek

1 kolonlularda

Sub birkolonlu_variant_to_dizi()
Dim var As Variant
Dim dizi() As String
Dim kombine As String
Dim i As Integer

var = Range("B2:B10").Value2
ReDim dizi(1 To UBound(var, 1))

For i = 1 To UBound(var, 1)
    dizi(i) = var(i, 1)
Next i

kombine = Join(dizi, ", ")

MsgBox kombine

End Sub

2 kolonlularda

Sub çokkolonlu_variant_to_dizi()
Dim var As Variant
Dim dizi() As String
Dim kombine As String
Dim s As Integer, k As Integer, i As Integer

var = Range("B2:D10").Value2
ReDim dizi(1 To UBound(var, 1) * UBound(var, 2))

s = 0
For k = 1 To UBound(var, 2)
   For i = 1 To UBound(var, 1)
     dizi(i + s) = var(i, k)
   Next i
   s = s + UBound(var, 1) 'her boyut geçişinde tekrar başa dönmesin diye aratoplam alınır
Next k

kombine = Join(dizi, ", ")
MsgBox kombine
End Sub	
2.yöntem:Transpoze(sadece tek kolonsa)
kombine = Join(WorksheetFunction.Transpose(Range("B2:B10").Value), ", ")

Varmı kontrolü ve Index bulma

Filtreleme

Dizilerle ilgili sık yapılan işlemlerden biri de, bir elemanın o dizide bulunup bulunmadığı ve /veya bulunuyorsa da index numarasını öğrenmek olacaktır.

Bu işlemlere geçmeden önce Filter fonksiyonundan bahsedelim. Bu metod ile String tipli bir dizinin içinde belirli bir metni arar ve bunları filtreler, eşleşmeyenleri hariç tutarız. (Burada filtrelemekten kastım, elemek değil seçmektir.)

Syntax:Filter(dizi,aranan,[Dahilmihariçmi],[aramatipi])

dizi olarak tek boyutlu bir diziyi parametre olarak veririz, bu nedenle range atanmış bir Variant dizi parametre olarak verilemez.

Dahilmihariçmi parametresi girilmez veya True girilirse aranan kelimeyi içeren kelimeler aranır, False girilirse aranan kelimeyi içermeyen elemanlar filtrelenir.

aramatipi girilmez veya vbBinaryCompare girilirse küçük büyük harf duyarlılığı gözetilerek arama yapılır, vbTextCompare girilirse küçükbüyük harf duyarlılığı olmadan arama yapılır. İstisna:ş ve Ş, ç ve Ç v.s aynı kabul edilirken bir tek İ ve i farklı algılanır.

Bununla beraber bu metodu, aranan metnin tam eşleşme durumu için kullanamazsınız. Yani "is" metnini aradığınızda "is, istanbul, mistik" kelimelerinin tamamı gelir.

Şimdi basit bir örnekle konuyu anlamaya çalışalım. Aşağıdaki dizide içinde "kan" geçen isimleri filtrelemeye çalışıyoruz.

Sub basitfiltre()
Dim isimler As Variant
Dim filtreli As Variant

isimler = Array("volkan", "erhan", "hakan", "özkan", "meltem", "serkan")
filtreli = Filter(isimler, "kan", True, vbTextCompare)

For Each f In filtreli
   Debug.Print f
Next f

End Sub

Aşağıdaki örnekte ise içinde "ankara" geçen tüm kelimeler aranır ve filtrelenir. Yanlız bu sefer kaynak dizimiz 2 boyutlu ve önce onu tek boyutlu diziye çevirmemiz gerekiyor.

Sub filtreornek()
Dim şubeler As Variant
Dim filtreli As Variant
Dim geçici() As String
Dim i As Integer

'çeşitli şubelerin olduğu alandan okuma yapılır(sırayla ankara, antalya, ankara, istanbul, sivas yazsın)
şubeler = Range("A1:A5").Value

'Variant ve 2 boyutlu olan şubeler dizisi klasik bir boyutlu bir diziye atanır
ReDim geçici(1 To UBound(şubeler, 1))
For i = 1 To UBound(şubeler, 1)
  geçici(i) = şubeler(i, 1)
Next i

filtreli = Filter(geçici, "ankara", True, vbTextCompare)

Set Hedef = Range("C1").Resize(UBound(filtreli) + 1, 1)
Hedef.Value = filtreli

End Sub

Bir metin bir dizide var mı kontrolü?

Filter fonksiyonunu öğrendiğimize göre şimdi bunu daha spesifik bir amaçla kullanabiliriz: Bir metnin bir dizide olup olmadığını öğrenmek için. Bunun için birkaç yöntem bulunuyor.

İlk yöntemde diziye filtre uygular ve elde ettiğimiz yeni dizinin üst limitine bakarız. Eğer üst limit 0 veya daha üstü bir sayı ise aradığımız metin dizide var demektir, -1 ise dizide bulunmuyor demektir.

Sub varmı1sub()
'1.Yöntem:Filter, tam eşleşme aranmıyor
Dim şubeler As Variant
Dim filtreli As Variant
Dim geçici() As String
Dim i As Integer

'çeşitli şubelerin olduğu alandan okuma yapılır(sırayla ankara, antalya, ankara, istanbul,sivas yazsın)
şubeler = Range("A1:A5").Value

'Variant ve 2 boyutlu olan şubeler dizisi klasik bir boyutlu bir diziye atanır
ReDim geçici(1 To UBound(şubeler, 1))
For i = 1 To UBound(şubeler, 1)
   geçici(i) = şubeler(i, 1)
Next i

filtreli = Filter(geçici, "ankara", True, vbTextCompare)

If UBound(filtreli)=-1 Then 
  MsgBox "aradığınız kelime dizide yok"
Else
  MsgBox "aradığınız kelime dizide var"
End If

End Sub

Bunu bir fonksiyon haline getirmek daha kullanışlı yapacaktır.

'1'in function hali
Function Varmı1(aranan As String, dizi As Variant) As Boolean
    Varmı1 = (UBound(Filter(dizi, aranan, True, vbTextCompare)) > -1)
End Function
'--------
    Sub test1()
        isimler = Array("volkan", "erhan", "hakan", "özkan", "meltem", "serkan")
        Debug.Print Varmı1("volk", isimler) 'True
        Debug.Print Varmı1("volkan", isimler) 'True
    End Sub

Tabi bu yöntemde tam eşleşme durumu sağlanmıyor, zira ilgili metni içeren bir kelime var mı diye kontrol ediyor. Eğer ki tam eşleşme istiyorsak, Match fonksiyonunu kullanmalıyız veya Filter dışında çözüm sağlayan 2.yöntemi denemeliyiz. Bu yöntemde 1.yöntemin aksine sayısal değerleri de arayabiliyoruz. Bu yüzden hem sayıları hem stringleri kapsayacak şekilde Variant tipte bir aranan parametresi belirtiyoruz.

Function Varmı2(aranan As Variant, dizi As Variant) As Boolean
'2.yöntem)Match, tam eşleşme sağlanıyor
On Error GoTo hata
    Varmı2 = Not IsError(WorksheetFunction.Match(aranan, dizi, 0))
Exit Function
hata:
    Varmı2 = False
End Function
'-----------
    Sub test2()
        isimler = Array("volkan", "erhan", "hakan", "özkan", "meltem", "serkan")
        Debug.Print Varmı2("volk", isimler) 'False
        Debug.Print Varmı2("volkan", isimler) 'True

        sayılar = Array(1, 2, 3)
        Debug.Print varmı3(1, sayılar) 'true
        Debug.Print varmı3(10, sayılar) 'false
    End Sub

3. yöntem, dizi elemanlarını Join ile birleştirip, yine bir String fonksiyonu olan ve bir string içinde bir alt metin arayan InStr fonksiyonu ile arama yapmak şeklinde olabilir, üstelik bu yöntem az önce belirtildiği gibi tam eşleşme sağlar. Bunda da 2.yöntemde olduğu gibi sayısal değerleri de arayabiliyoruz, o yüzden fonknsiyonumuzu Variant tanımlıyoruz.

Şimdi bu örneği direkt Function olarak yapalım.

Function varmı3(aranan As Variant, dizi As Variant) As Boolean
ayraç = "-"
joinli = ayraç & Join(dizi, ayraç) 'tam eşleşmeyi sağlamak için başına da ilgili ayracı koyuyoruz
If InStr(1, joinli, ayraç & aranan & ayraç, vbTextCompare) Then
    varmı3 = True
Else
    varmı3 = False
End If
End Function
    Sub test3()
        isimler = Array("volkan", "erhan", "hakan", "özkan", "meltem", "serkan")
        Debug.Print varmı3("volk", isimler) 'False
        Debug.Print varmı3("volkan", isimler) 'True
        
        sayılar = Array(1, 2, 3)
        Debug.Print varmı3(1, sayılar) 'true
        Debug.Print varmı3(10, sayılar) 'false
    End Sub

4. yöntem olarak, daha basit bir yol olan Dictionary kullanılabilir, tabi kodun genel yapısı buna uygun ise. Bunu sonraki sayfalarda göreceğiz.

5.yöntem olarak arama algoritmalarından birini kullanarak kendi fonksiyonunuzu yazmayı önerebilirim ama çok profesyonelleşmek istemediğiniz sürece bu sulara girmenize gerek yoktur.

Var mı, varsa kaçta?

Yukarıdaki Match fonksiyonunu kullandığımız 2. yöntemde aslında ilgili metnin kaçıncı sırada olduğunu buldurmaya yarayan bir fonksiyon kullanmış olduk. Eğer bulamazsa hata döndürür, zaten biz de hata döndürüp döndürmediğine bakıyorduk, döndürmüyorsa elemanı içeriyor diyorduk. Peki ya hata döndürmüyorsa? Döndürdüğü şey sıra numarası olmaktadır.

Dizileri sıralama

Algoritma kavramına detaylı girdiğimizde karşımıza çıkan en temel algoritmalardan biri de sıralama algoritmalarıdır.(Diğeri de hemen az önce bahsettiğim arama algoritmasıdır).

Bu sıralama algoritmalarını anlamak önemli, zira dizilerle ilgili olarak sıralama yapmayı sağlayan ne yerleşik bir fonksiyon/metod, ne de arama yaparken kullandığımız gibi yardımcı yöntemler(Match, Join) bulunmaktadır. Kendi fonksiyonumuz kendimizin yazması gerekmektedir.

Bunların da kendi içinde türleri vardır. Bubble Sort, Merge Sort, Quick Sort gibi. Burada bunlardan ikisine bakacağız. Şu ve şu sitelerde daha bol miktarda örnek bulunmakta, şu ve şu(Türkçe) sitede ise muazzam bir kaynak bulunmaktadır. Bu son sitelerden farklı yönteleri öğrenip VBA kodlamasını yapmayı deneyebilirsiniz.

Bubble Sort

Bu yöntem en basit yöntem olup, aynı zamanda en yavaş çalışan yöntemdir de. Gerçi küçük dizilerde bu yavaşlık problemi çok farkedilmez. Çalışma yöntemi, tekrarlı bir şeklide komşu değerlerin sıralamasını yapmaktan ibarettir. Aşağıdaki örnekte tüm aşamaların üzerinden tek tek geçeceğiz.

Elimizdeki dizi 5,1,4,2,8 rakamlarından oluşsun.

1.aşama

Önce                   Sonra     
5 1 4 2 8 ) –> ( 1 5 4 2 8 )
( 1 5 4 2 8 ) –>  ( 1 4 5 2 8 )
( 1 4 5 2 8 ) –>  ( 1 4 2 5 8 )
( 1 4 2 5 8 ) –> ( 1 4 2 5 8 ) 'burada bi değişiklik olmaz, sırası doğru

2.aşama

1 4 2 5 8 ) –> ( 1 4 2 5 8 )
( 1 4 2 5 8 ) –> ( 1 2 4 5 8 )
( 1 2 4 5 8 ) –> ( 1 2 4 5 8 )
( 1 2 4 5 8 ) –>  ( 1 2 4 5 8 )
Şu anda sıralama tamam ancak bu aşamada 1 tane de olsa bir değişim olduğu için algoritmanın devam etmesi lazım, ta ki hiç değişim olmayana kadar.

3.aşama

1 2 4 5 8 ) –> ( 1 2 4 5 8 )
( 1 2 4 5 8 ) –> ( 1 2 4 5 8 )
( 1 2 4 5 8 ) –> ( 1 2 4 5 8 )
( 1 2 4 5 8 ) –> ( 1 2 4 5 8 )

Evet 3.aşamada artık bir değişiklik yok ve algoritmamız durur. Şimdi bunun kodunu görelim.

Function BubbleSort(arr) As Variant
  Dim geçici As Variant
  Dim i As Long
  Dim j As Long
  Dim lngMin As Long
  Dim lngMax As Long
  
  lngMin = LBound(arr)
  lngMax = UBound(arr)
  For i = lngMin To lngMax - 1
    For j = i + 1 To lngMax
      If arr(i) > arr(j) Then
        geçici = arr(i)
        arr(i) = arr(j)
        arr(j) = geçici
      End If
    Next j
  Next i
  
BubbleSort = arr
End Function
'-------
    Sub bubble_sort_test()
    Dim dizi As Variant
    Dim sıralı As Variant
    
    dizi = Array(5,1,4,2,8)
    sıralı = BubbleSort(dizi)

    Call Diziyazdır(sıralı)
    End Sub

Quicksort

QuickSort(Hızlı sıralama) algoritması, böl ve fethet felsefesine dayanan bir yöntem uygular. Bu anlamda Merge Sort algoritmasına benzer.

Bu yöntemde, bir referans seçilir(bu referans en sondaki, en baştaki olabilir, ortdaki veya rasgele olabilir. Buna pivot veya dayanak noktası da denir. Bu referanstan küçük olanlar sol tarafa, büyük olanlar sağ tarafa konulur ve  daha fazla yer değiştirme işlemi yapılamayana kadar bu işlem recursive olarak devam eder. Bu konuda yukarıda bahsettiğim linklerde de bolca açıklama ve örnek bulunmaktadır.

Şimdi, algoritmanın icabettiği şekilde ben de bir kod hazırladım. Referans noktasını "sondaki eleman" olarak belirledim. İşleyişin aşama aşama nasıl olduğunu kod içinde bulabilirsiniz. Verdiğim linklerde gerekli açıklamalar, videolarla birlikte sunulduğu için ben daha fazla uzatmamak adına ilave açıklamada bulunmayacağım. Detayla ilgilenmiyorsanız kodu doğrudan da kullanabilirsiniz.

Kodumuz iki kısımdan oluşmakta. Önce parçalama kısmı sonra sıralama kısmı. Kodu tek seferde çalıştırıp Immediate Windowda algoritmanın işleyişini aşama aşama  takip edebilirsiniz.

Function Parçala(ByRef dizi As Variant, ByVal low As Integer, ByVal high As Integer) As Integer
    referans = dizi(high)
    Debug.Print "Referansımız:" & referans & ".Bundan küçükler sola, büyükler sağa. referans ortalarına. Soldakiler önce ele alınacka, sağdakiler daha sonra."
    i = low - 1
    For J = low To high - 1
        If dizi(J) <= referans Then
            i = i + 1
            Debug.Print i, J, dizi(J) & "<= referans olduğu için " & dizi(i) & " ile " & dizi(J) & " yer değiştirecek." & dizi(i) & " yavaşça sağa kayaacak: " & Join(dizi, "-")
            geçici = dizi(i) '
            dizi(i) = dizi(J)
            dizi(J) = geçici
            'Call DiziBitişikyazdır(dizi, "yer değişim sonucunda:")
        Else
            Debug.Print i, J, dizi(J) & ">" & "referans(" & referans & ") olduğu için yer değişme yok"
        End If
    Next J
    Call DiziBitişikyazdır(dizi, "for çıkışında dizinin durumu:")
    
    Debug.Print dizi(i + 1) & " ile " & dizi(high) & " yer değiştirecek, yani referans orataya girecek."
    geçici2 = dizi(i + 1)
    dizi(i + 1) = dizi(high) 'referansla. yani referansı ortaya koyuyuoruz
    dizi(high) = geçici2
    Call DiziBitişikyazdır(dizi, "Bu aşamanın sonunda dizimiz:")
    Debug.Print "-----------------------------------------" & vbNewLine
    
    Parçala = i + 1
End Function
'------------------------------------
Sub hızlısırala(ByRef dizi As Variant, ByVal low As Integer, ByVal high As Integer)
    If low < high Then
        Pi = Parçala(dizi, low, high)
        hızlısırala dizi, low, Pi - 1
        hızlısırala dizi, Pi + 1, high
    End If
End Sub
'------------------------------------
'bu kodu çalıştırarak test ediyoruz
    Sub quick_sort_test() 
         karışıkdizi = Array(5, 1, 4, 2, 8)
        'karışıkdizi = Array(52, 3, 45, 32, 8, 19, 47, 26, 1, 100, 68, 24, 12, 9, 72, 55, 36)
        Call DiziBitişikyazdır(karışıkdizi, "İlk giriş:")
        hızlısırala karışıkdizi, 0, UBound(karışıkdizi) 'byref gittiği için sıralanıp gelir
        Call DiziBitişikyazdır(karışıkdizi, "Nihai sıralama sonucu:")
    End Sub

Diziler için Utility Modülü

Dizilerle ilgili olarak sık yapılan işlmleri, özellikle test/kontrol için yapılan işlemleri bir modül içinde toparlalak iyi bir fikir olacaktır. Bunlardan sıralamayı yukarıda görmüştük, diğerlerine birkaç örneği ise aşağıda bulabilirsiniz.

Sub Diziyazdır(dizi As Variant)
    For Each d In dizi
        Debug.Print d
    Next d
End Sub
'------------------
Sub DiziBitişikyazdır(dizi As Variant, Optional başlık As String = "Birleşen dizi:")
bitişik = Join(dizi, "-")
Debug.Print başlık & bitişik & vbNewLine

End Sub
'-------------
Function ikidizibirleştir(dizi1 As Variant, dizi2 As Variant)
Dim geçici As Variant

adet = UBound(dizi1) + UBound(dizi2) + 2
ReDim geçici(adet - 1)

For i = LBound(dizi1) To UBound(dizi1)
    geçici(i) = dizi1(i)
Next i

For i = UBound(dizi1) + 1 To adet - 1
    geçici(i) = dizi2(i - 1 - UBound(dizi1))
Next i
'control için
DiziBitişikyazdır (geçici)

ikidizibirleştir = geçici
End Function
	
	

Hatırlatma

Sayfanın başında belirttiğim gibi buraya da bakmanızı tavsiye ediyorum.

TEST SORULARI

Son Sorumuz şuymuş:Bir metindeki tüm noktaları yoketmek istiyorsunuz. Hangi fonksiyonu kullanırdınız?
Soru:

A şıkkı:

B şıkkı:

C şıkkı:

D şıkkı:

Doğru Cevap Etiketler

İlişkili konuyu seç

160160

Label
* Sorulara verilen yanlış cevaplardaki esprili yorumlarım için hoşgörünüze sığınıyorum.
* Test ve Ödevlerdeki bazı detaylar burada anlatılmamış olabilir. Bunları kendiniz araştırıp bulmalısınız.
* Birden çok konuya ait içeriği olan ödevler var. Algoritmik açıdan bakıldığında o an en uygun konuya adreslenmiştir.
Dikkat! Bir soruya cevap verdikten sonra geri dönemezsiniz.
6
3
0
0

Soru No:46. Aşağıdaki ifadelerden hangisi doğrudur?







ÖDEVLER

5
0
Ödev No:7. Array fonksiyonu ile mevsimleri içeren bir dizi tanımlayın. ilk mevsimi ekrana getirin.
Çözüme bakın(Başka türlü de çözülebilir tabi, bu benim çözümüm.)

Sub ödev7()
 
Dim mevsimler As Variant
mevsimler = Array("Yaz", "Kış", "Sonbahar", "İlkbahar")
MsgBox mevsimler(0)
 
End Sub



=YORUMLAR ve SORULAR=


DEVİR UYARISI

Herkese merhaba. Hosting maliyetlerinin aşırı artması yüzünden sitemi yakın zamanda(en geç Mayıs 2023) kapatmaya karar vermiştim. Ancak, siteyi yakından takip eden bir arkadaş siteyi devralmak istemiştir. Siteyi, Mayıs ayında kendisine devir etmeye karar verdim. Üyelik bilgilerini bana güvenerek girdiğiniz için, hepsini silmiş bulunuyorum, yani mail adreslerinizi kimseyle paylaşmamış olacağım. Bilginizi rica ederim.