Diziler
Bir önceki dersin problemlerinden birisinde 5 tane değişken tanımlamış ve onlarla belirli işlemler yapmıştık: önce iki katlarını almıştık, sonra da beşe bölmüştük. O değişkenleri ayrı ayrı şöyle tanımlamıştık:
real sayı_1; real sayı_2; real sayı_3; real sayı_4; real sayı_5;
Bu yöntem her duruma uygun değildir, çünkü değişken sayısı arttığında onları teker teker tanımlamak, içinden çıkılmaz bir hal alır. Bin tane sayıyla işlem yapmak gerektiğini düşünün... Bin tane değişkeni ayrı ayrı sayı_1, sayı_2, ..., sayı_1000 diye tanımlamak hemen hemen olanaksız bir iştir.
Dizilerin bir yararı böyle durumlarda ortaya çıkar: diziler bir seferde birden fazla değişken tanımlamaya yarayan olanaklardır. Birden fazla değişkeni bir araya getirmek için en çok kullanılan veri yapısı da dizidir.
Tanımlanması
Dizi tanımı değişken tanımına çok benzer. Tek farkı, dizide kaç değişken bulunacağının, yani bir seferde kaç değişken tanımlanmakta olduğunun, türün isminden sonraki köşeli parantezler içinde belirtilmesidir. Tek bir değişkenin tanımlanması ile bir dizinin tanımlanmasını şöyle karşılaştırabiliriz:
int tekDeğişken; int[10] onDeğişkenliDizi;
O iki tanımdan birincisi, şimdiye kadarki kodlarda gördüklerimiz gibi tek değişken tanımıdır; ikincisi ise 10 değişkenden oluşan bir dizidir.
Yukarıda sözü geçen problemdeki 5 ayrı değişkeni 5 elemanlı bir dizi halinde hep birden tanımlamak için şu söz dizimi kullanılır:
real[5] sayılar;
Bu tanım, "real türünde 5 tane sayı" diye okunabilir. Daha sonra kod içinde kullanıldığında tek bir sayı değişkeni sanılmasın diye ismini de çoğul olarak seçtiğime dikkat edin.
Özetle; dizi tanımı, tür isminin yanına köşeli parantezler içinde yazılan dizi uzunluğundan ve bunları izleyen dizi isminden oluşur:
tür_ismi[dizi_uzunluğu] dizi_ismi;
Tür ismi olarak temel türler kullanılabileceği gibi, programcının tanımladığı daha karmaşık türler de kullanılabilir (bunları daha sonra göreceğiz). Örnekler:
// Bütün şehirlerdeki hava durumlarını tutan bir dizi // Burada örneğin // false: "kapalı hava" // true : "açık hava" // anlamında kullanılabilir bool[şehirAdedi] havaDurumları; // Yüz kutunun ağırlıklarını ayrı ayrı tutan bir dizi double[100] kutuAğırlıkları; // Bir okuldaki bütün öğrencilerin kayıtları ÖğrenciBilgisi[öğrenciAdedi] öğrenciKayıtları;
Topluluklar ve elemanlar
Aynı türden değişkenleri bir araya getiren veri yapılarına topluluk adı verilir. Bu tanıma uydukları için diziler de toplulukturlar. Örneğin Temmuz ayındaki günlük hava sıcaklıklarını tutmak için kullanılacak bir dizi, 31 tane real değişkenini bir araya getirebilir ve bir hava sıcaklığı topluluğu oluşturur.
Topluluk değişkenlerinin her birisine eleman denir. Dizilerin barındırdıkları eleman adedine dizilerin uzunluğu denir. "Eleman adedi" ve "dizi uzunluğu" ifadelerinin ikisi de sık kullanılır.
Eleman erişimi
Problemdeki değişkenleri ayırt etmek için isimlerinin sonuna bir alt çizgi karakteri ve bir sıra numarası eklemiştik: sayı_1 gibi... Sayıları hep birden bir dizi halinde ve sayılar isminde tanımlayınca elemanlara farklı isimler verme şansımız kalmaz. Onun yerine, elemanlara dizinin erişim işleci olan [] ile ve bir sıra numarasıyla erişilir:
sayılar[0]
O yazım, "sayıların 0 numaralı elemanı" diye okunabilir. Bu şekilde yazınca sayı_1 ifadesinin yerini sayılar[0] ifadesi almış olur.
Burada dikkat edilmesi gereken iki nokta vardır:
- Numara sıfırdan başlar: Biz insanlar nesneleri 1'den başlayacak şekilde numaralamaya alışık olduğumuz halde, dizilerde numaralar 0'dan başlar. Bizim 1, 2, 3, 4, ve 5 olarak numaraladığımız sayılar dizi içinde 0, 1, 2, 3, ve 4 olarak numaralanırlar. Bu uyumsuzluğa özellikle dikkat etmek gerekir, çünkü bazı programcı hatalarının kaynağı bu uyumsuzluktur.
[]karakterlerinin iki farklı kullanımı: Dizi tanımlarken kullanılan[]karakterleri ile erişim işleci olarak kullanılan[]karakterlerini karıştırmayın. Dizi tanımlarken kullanılan[]karakterleri elemanların türünden sonra yazılır ve dizide kaç eleman bulunduğunu belirler; erişim için kullanılan[]karakterleri ise dizinin isminden sonra yazılır ve elemanın sıra numarasını belirler:
// Bu bir tanımdır... 12 tane int'ten oluşmaktadır ve her // ayda kaç gün bulunduğu bilgisini tutmaktadır int[12] ayGünleri; // Bu bir erişimdir... Aralık ayına karşılık gelen elemana // erişir ve değerini 31 olarak belirler ayGünleri[11] = 31; // Bu da bir erişimdir... Ocak ayındaki gün sayısını // yazdırmaktadır writeln("Ocak'ta ", ayGünleri[0], " gün var");
Hatırlatma: Ocak ayının sıra numarasının 0, Aralık ayının sıra numarasının 11 olduğuna dikkat edin.
İndeks
Elemanlara erişirken kullanılan sıra numaralarına indeks, elemanlara erişme işine de indeksleme denir.
İndeks sabit bir değer olmak zorunda değildir; indeks olarak değişken değerleri de kullanılabilir. Bu olanak dizilerin kullanışlılığını büyük ölçüde arttırır. Örneğin aşağıdaki kodda hangi aydaki gün sayısının yazdırılacağını ayNumarası değişkeni belirlemektedir:
writeln("Bu ay ", ayGünleri[ayNumarası], " gün çeker");
ayNumarası'nın 2 olduğu bir durumda yukarıdaki ifadede ayGünleri[2]'nin değeri, yani Mart ayındaki gün adedi yazdırılır. ayNumarası'nın başka bir değerinde de o aydaki gün sayısı yazdırılır.
Yasal olan indeksler, 0'dan dizinin uzunluğundan bir eksiğine kadar olan değerlerdir. Örneğin 3 elemanlı bir dizide yalnızca 0, 1, ve 2 indeksleri yasaldır. Bunun dışında indeks kullanıldığında program bir hata ile sonlanır.
Dizileri, elemanları yan yana duran bir topluluk olarak düşünebilirsiniz. Örneğin ayların günlerini tutan bir dizinin elemanları ve indeksleri şu şekilde gösterilebilir (Şubat'ın 28 gün çektiğini varsayarak):
indeksler → 0 1 2 3 4 5 6 7 8 9 10 11 elemanlar → | 31 | 28 | 31 | 30 | 31 | 30 | 31 | 31 | 30 | 31 | 30 | 31 |
İlk elemanın indeksi 0, ve Ocak ayındaki gün sayısı olan 31 değerine sahip; ikinci elemanın indeksi 1, ve Şubat ayındaki gün sayısı olan 28 değerine sahip; vs.
Sabit uzunluklu diziler ve dinamik diziler
Kaç eleman barındıracakları programın yazıldığı sırada bilinen dizilere sabit uzunluklu dizi; elemanlarının sayısı programın çalışması sırasında değişebilen dizilere dinamik dizi denir.
Yukarıda 5 sayı tanımlamak için kullandığımız sayılar dizisi ve 12 aydaki gün sayılarını tutmak için kullandığımız ayGünleri dizileri sabit uzunluklu dizilerdir; çünkü eleman sayıları baştan belirlenmiştir. O dizilerin uzunlukları programın çalışması sırasında değiştirilemez. Uzunluklarının değişmesi gerekse, bu ancak kaynak koddaki sabit olan değerin elle değiştirilmesi ve programın tekrar derlenmesi ile mümkündür.
Dinamik dizi tanımlamak belki de sabit uzunluklu dizi tanımlamaktan daha kolaydır, çünkü dizinin uzunluğunu boş bırakmak diziyi dinamik yapmaya yeter:
int[] dinamikDizi;
Böyle dizilerin uzunlukları programın çalışması sırasında gerektikçe arttırılabilir veya azaltılabilir.
Dizi nitelikleri
Türlerin olduğu gibi dizilerin de nitelikleri vardır. Burada yalnızca üç tanesini tanıyacağız. .length niteliğinin dinamik dizilerde farklı olduğuna dikkat edin:
.length: Dizinin uzunluğunu, yani içindeki eleman sayısını verir:
writeln("Dizide ", dizi.length, " tane eleman var");
Ek olarak, dinamik dizilerde dizinin uzunluğunu değiştirmeye de yarar:
int[] dizi; // boştur dizi.length = 5; // uzunluğu 5 olur
.sort: Elemanları küçükten büyüğe doğru sıralar:
dizi.sort;
.reverse: Elemanları ters sırada sıralar:
dizi.reverse;
Bu bilgiler ışığında 5 değişkenli probleme dönelim ve onu dizi kullanacak şekilde tekrar yazalım:
import std.stdio; import std.cstream; void main() { // Bu değişkeni döngüleri kaç kere tekrarladığımızı saymak // için kullanacağız int sayaç; // Beş real'den oluşan sabit uzunluklu bir dizi // tanımlıyoruz real[5] sayılar; // Sayıları bir döngü içinde girişten alıyoruz while (sayaç < sayılar.length) { write("Sayı ", sayaç + 1, ": "); din.readf(&sayılar[sayaç]); ++sayaç; } writeln("İki katları:"); sayaç = 0; while (sayaç < sayılar.length) { writeln(sayılar[sayaç] * 2); ++sayaç; } // Beşte birlerini hesaplayan döngü de bir önceki // döngünün benzeridir... }
Gözlemler: Döngülerin kaç kere tekrarlanacaklarını sayaç belirliyor: döngüleri, o değişkenin değeri sayılar.length'ten küçük olduğu sürece tekrarlıyoruz. Sayacın değeri her tekrarda bir arttıkça, sayılar[sayaç] ifadesi de sırayla dizinin elemanlarını göstermiş oluyor: sayılar[0], sayılar[1], vs.
Bu programın yararını görmek için girişten 5 yerine örneğin 20 sayı alınacağını düşünün... Dizi kullanan bu programda tek bir yerde küçük bir değişiklik yapmak yeter: 5 değerini 20 olarak değiştirmek... Oysa dizi kullanmayan programda 15 tane daha değişken tanımlamak ve kullanıldıkları kod satırlarını 15 değişken için tekrarlamak gerekirdi.
Elemanları ilklemek
D'de her türde olduğu gibi dizi elemanları da otomatik olarak ilklenirler. Elemanlar için kullanılan ilk değer, elemanların türüne bağlıdır: int için 0, real için real.nan, vs.
Bazen elemanların değerleri, dizi kurulduğu anda bilinir. Öyle durumlarda dizi, atama söz dizimiyle ve elemanların ilk değerleri sağ tarafta belirtilerek tanımlanır. Kullanıcıdan ay numarasını alan ve o ayın kaç gün çektiğini yazan bir program düşünelim:
import std.stdio; import std.cstream; void main() { // Şubat'ın 28 gün çektiğini varsayıyoruz int ayGünleri[12] = [ 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 ]; write("Kaçıncı ay? "); int ayNumarası; din.readf(&ayNumarası); int indeks = ayNumarası - 1; writeln(ayNumarası, ". ay ", ayGünleri[indeks], " gün çeker"); }
O programda ayGünleri dizisinin elemanlarının dizinin tanımlandığı anda ilklendiklerini görüyorsunuz. Ayrıca, kullanıcıdan alınan ve değeri
Dizileri ilklerken sağ tarafta tek bir eleman değeri de kullanılabilir. Bu durumda dizinin bütün elemanları o değeri alır:
int[10] hepsiBir = 1; // Bütün elemanları 1 olur
Basit dizi işlemleri
Diziler, bütün elemanlarını ilgilendiren bazı işlemlerde büyük kolaylık sağlarlar.
Kopyalama: Atama işleci, sağdaki dizinin elemanlarının hepsini birden soldaki diziye kopyalar:
int[5] kaynak = [ 10, 20, 30, 40, 50 ]; int[5] hedef; hedef = kaynak;
Eleman ekleme: ~= işleci, dinamik dizinin sonuna yeni bir eleman ekler:
int[] dizi; // dizi boştur dizi ~= 7; // dizide tek eleman vardır dizi ~= 360; // dizide iki eleman vardır
Sabit uzunluklu dizilerde dizinin uzunluğu değiştirilemez:
int[10] dizi; dizi ~= 7; // ← derleme HATASI
Birleştirme: ~ işleci iki diziyi uç uca birleştirerek yeni bir dizi oluşturur. Aynı işlecin atamalı olanı da vardır (~=) ve sağdaki diziyi soldaki dizinin sonuna ekler:
import std.stdio; void main() { int[10] birinci = 1; int[10] ikinci = 2; int[] sonuç; sonuç = birinci ~ ikinci; writeln(sonuç.length); // 20 yazar sonuç ~= birinci; writeln(sonuç.length); // 30 yazar }
Eğer sol tarafta sabit uzunluklu bir dizi varsa, dizinin uzunluğu değiştirilemeyeceği için ~= işleci kullanılamaz:
int[20] sonuç; // ... sonuç ~= birinci; // ← derleme HATASI
Atama işleminde de, sağ tarafın uzunluğu sol tarafa uymazsa program çöker:
int[10] birinci = 1; int[10] ikinci = 2; int[21] sonuç; sonuç = birinci ~ ikinci;
O kod, programın "dizi kopyası sırasında uzunluklar aynı değil" gibi bir hatayla çökmesine neden olur:
object.Exception: lengths don't match for array copy
Problemler
- Yazacağınız program önce kullanıcıdan kaç tane sayı girileceğini öğrensin ve girişten o kadar kesirli sayı alsın. Daha sonra bu sayıları önce küçükten büyüğe, sonra da büyükten küçüğe doğru sıralasın.
- Başka bir program yazın: girişten aldığı sayıların önce tek olanlarını sırayla, sonra da çift olanlarını sırayla yazdırsın. Özel olarak
-1 değeri girişi sonlandırmak için kullanılsın: bu değer geldiğinde artık girişten yeni sayı alınmasın. - Bir arkadaşınız yazdığı bir programın doğru çalışmadığını söylüyor.
Burada dizi niteliklerinden .sort'u ve .reverse'ü kullanabilirsiniz.
Örneğin girişten
1 4 7 2 3 8 11 -1
geldiğinde çıkışa şunları yazdırsın:
1 3 7 11 2 4 8
İpucu: Sayıları iki ayrı diziye yerleştirmek işinize yarayabilir. Girilen sayıların tek veya çift olduklarını da aritmetik işlemler sayfasında öğrendiğiniz % (kalan) işlecinin sonucuna bakarak anlayabilirsiniz.
Girişten beş tane sayı alan, bu sayıların karelerini bir diziye yerleştiren, ve sonunda da dizinin elemanlarını çıkışa yazdıran bir program yazmaya çalışmış ama programı doğru çalışmıyor.
Bu programın hatalarını giderin ve beklendiği gibi çalışmasını sağlayın:
import std.stdio; import std.cstream; void main() { int[5] kareler; writeln("5 tane sayı giriniz"); int i = 0; while (i <= 5) { int sayı; write(i + 1, ". sayı: "); din.readf(&sayı); kareler[i] = sayı * sayı; ++i; } writeln("=== Sayıların Kareleri ==="); while (i <= kareler.length) { write(kareler[i], " "); ++i; } writeln(); }
D.ershane
Forum
Wiki
Projeler
Tanıtım
İletişim
Hakları