sürüm: r270, 2011-10-13

D

Programlama Dili

D2 programlama dili ile bilgisayar programcılığı

Ali Çehreli

D.ershane Serisi

0.1

Copyright © 2009-2011 Ali Çehreli
Forum ve Wiki bölümlerinin içerikleri genele aittir. Her dersin hakları dersin yazarına aittir. Kişisel kullanım için olmak şartıyla istediğiniz şekilde yararlanabilirsiniz. Bu sitenin geri kalanının hakları Ali Çehreli'ye aittir. Kaynak göstermek şartıyla istediğiniz bölümlerini başka sitelere kopyalayabilirsiniz. Kişisel kullanım için olmak şartıyla, istediğiniz bölümünü istediğiniz gibi kopyalayabilirsiniz. Creative Commons License Ddili.org icerigi by Ali Cehreli is licensed under a Creative Commons AttributionNoncommercial-Share Alike 3.0 United States License. Based on a work at digitalmars.com. Permissions beyond the scope of this license may be available at http://ddili.org/ copyright.html.

0.2

"D Programlama Dili" PDF Dosyası
Bu derslerin en yeni halini bir PDF dosyası olarak şu adresten indirebilirsiniz: http://ddili.org/ders/d/D_Programlama_Dili.pdf

0.2.1

PDF dosyasının gelişimi
• r250, 2011-07-08: İşleç yüklemenin yeni söz dizimine dönüştürüldü, bazı derslerin problemleri eklendi, vs. • r243, 2011-07-05: 'Bellek Yönetimi' dersine realloc() eklendi • r240, 2011-07-04: 'Bellek Yönetimi' dersi • r235, 2011-06-26: Emekliye ayrılan 'scope' belirtecinin yerine 'clear' ve 'scoped' • r233, 2011-06-18: 'Tür Nitelikleri' dersi • r231, 2011-06-18: 'Eş Zamanlı Programlama' dersi • r206, 2011-06-04: 'Koşut İşlemler' dersi • r178, 2011-05-19: 'Çokuzlular' dersi • r175, 2011-05-18: 'Başka Aralık Olanakları' dersi • r169, 2011-05-06: 'Aralıklar' dersi • r155, 2011-03-21: std.cstream yerine std.stdio ve çok sayıda düzeltme • r149, 2011-02-04: Dilimlerin gösterildiği ders 'Başka Dizi Olanakları' yeni başlığıyla genişletildi • r132, 2010-07-21: Çeşitli düzeltmeler: 'immutable char[]' yerine 'immutable(char)[]', Throwable ve Error yerine Exception, vs. • r124, 2010-06-08: 'Hata Atma ve Yakalama' dersine ek: scope(exit), scope(success), ve scope(failure) • r121, 2010-06-05: Çeşitli düzeltmeler • r118, 2010-05-31: 'Katmalar' dersi • r114, 2010-05-28: 'Diğer İşlev Olanakları' dersi • r109, 2010-05-25: 'Ayrıntılı Şablonlar' dersi • r107, 2010-05-07: 'Etiketler' dersi • r104, 2010-05-05: 'Birlikler' dersi

Copyright © 2009-2011 Ali Çehreli, http://ddili.org

2

• • • • •

r101, 2010-05-04: 'Yapı ve Sınıflarda foreach' dersi r99, 2010-05-01: 'İşlev Göstergeleri ve Kapamalar' dersi r96, 2010-04-30: 'Koşullu Derleme' dersi r81, 2010-04-06: 'Bit İşlemleri' dersi r75, 2010-04-02: 'Göstergeler' dersi ve öncesi

Sürümlerdeki farklılıkları ddili.org'un proje sayfasında görebilirsiniz.

Copyright © 2009-2011 Ali Çehreli, http://ddili.org

3

Teşekkür

1

Teşekkür
Bu derslerin yazımı sırasında beni bir süre bütünüyle kaybedeceklerini başından bilen, bunu anlayışla karşılayan, ve heyecanımı paylaşan eşime ve kızıma çok teşekkür ederim. Bu derslerin doğruluğu ve anlaşılırlığı büyük ölçüde Ddili Forum üyelerinin katkılarına bağlıdır. Yeni dersler hep önce o forumda duyuruldular, ve ancak üyelerin önerileri dikkate alındıktan sonra genel kullanıma sunuldular. Bu dersleri yazma sürecinde heyecanımı uyanık tuttukları için Ddili Forum üyelerine teşekkür ederim. Özellikle Mert Ataol, kitabın her satırını büyük bir titizlikle gözden geçirdi ve düzeltmeler önerdi. Can Alpay Çiftçi'nin ve Faruk Erdem Öncel'in kitabın ve ddili.org'un gelişiminde büyük katkıları oldu.

Copyright © 2009-2011 Ali Çehreli, http://ddili.org

4

Tanıtım

2

Tanıtım
Derslere başlamadan önce bazı noktalarda anlaşmamız gerekiyor. Siz: Her ne kadar D için C, C++, ve Java gibi dillerdeki deneyimler yararlı olsa da, buradaki dersler D dilini ve genel olarak programcılığı hiçbir deneyim varsaymadan, temelden anlatırlar. Eğer programcılığı Türkçe kaynaklardan öğrenmeyi seviyorsanız, burası size göre... Hoşgeldiniz! :o) Hazırlık: Bu dersleri izleyebilmek için D programları yazacak bir ortama ihtiyacınız olacak. Bunun için en azından şunlar gerekir: • bir metin düzenleyici • bir D 2.0 derleyicisi • Türkçe harfleri destekleyen bir ortam Metin düzenleyiciyi ve derleyiciyi ayrı ayrı kurmak yerine, onların ikisini ve başka araçları da içeren bir geliştirme ortamı da kurabilirsiniz. Bu konuda kurulum sayfasından yararlanabilirsiniz. Eğer sizin bu programları kuracak deneyiminiz yoksa, bu aşamada birisinden yardım almanız gerekiyor. D dilini metin düzenleyici veya derleyici olmadan öğrenemezsiniz. Dersler bütünüyle Türkçe programlardan oluştukları için, çalıştığınız ortamda Türkçe harflerin doğru olarak görünmeleri gerekir. Bu konuda Windows ortamı için yardım almak için Türkçe harflerin DOS pencerelerinde gösterilmesini anlatan forum konusundan yararlanabilirsiniz. (Özellikle 2-a ve 2-b maddeleri.) Derslerin yapısı: Her ders, olabildiğince az sayıda kavramı örneklerle anlatıyor. Her sayfanın sonunda öğrencinin o zamana kadar öğrendikleriyle programlayabileceği problemler bulunuyor. Öğrenci kendisininkiyle karşılaştırabilsin diye her problemin çözümü de veriliyor. (Yerinizi kaybetmemeniz için çözümler ayrı bir tarayıcı sayfasında açılırlar.) Bir derste anlatılan kavramları anlamadan bir sonrakine geçmemenizi öneririm. Eğer anlaşılmayan kavramlar kalıyorsa; bu sizden değil, derslerdeki eksikliklerden kaynaklanıyor olabilir. O zaman lütfen sesinizi Ders Arası forumunda duyurun ve derslerin gelişimine yardımcı olun. Derslerde pencereli programlar anlatılmıyor: Pencereli (görsel) programlar çok daha hoş ve bazı durumlarda çok daha kullanışlıdırlar. Buna rağmen, bu dersler görsel program yazmayı anlatmazlar. Çünkü dili öğrenmek için pencereler kullanmak gerekmez. Hatta görsel program yazmak için kullanılan kütüphanelerin tasarımları dilin kavramlarıyla karışabilir ve dilin öğrenimini güçleştirebilir. Bu yüzden, bu dersler D dilini bütünüyle komut satırından çalışan programlarla anlatırlar. D'yi ve standart kütüphanesi Phobos'u bir kere öğrendikten sonra, zaten siz istediğiniz görsel kütüphane ile istediğiniz pencereli programı yazabileceksiniz. Ders RSS beslemesi: Bu bölüm hâlâ yazım aşamasında olduğu için, eklenen yeni ders sayfalarından haberdar olmak için RSS beslemesinden yararlanabilirsiniz. Sözlük: Her sayfada, o derste kullanılan terimleri içeren bir mini sözlük bulunuyor. Böylece derste geçen terimleri bütün sözlüğü açmanız gerekmeden hızlıca hatırlayabileceksiniz. (Yerinizi kaybetmemeniz için bütün sözlük ayrı bir tarayıcı sayfasında açılır.) Ders Arası forumu: Programlama dilleri başkalarıyla paylaşınca çok daha zevklidir. D dilini öğrenenlerin forumu olan Ders Arası'na katılın ve varsa sorularınızı sorun, veya yeni başlayanlara siz yardımcı olun.

Copyright © 2009-2011 Ali Çehreli, http://ddili.org

5

Tanıtım

Bir rica: Bu derslerin yararlı ve kullanışlı olabilmesi sizin katkınıza bağlı. Yazarın kafasında kurguladığı anlatım yöntemi öğrenmeye uygun olmayabilir. Lütfen gördüğünüz her türlü yanlışı ve eksiği en kısa zamanda yazara bildirin. Teşekkürler! Yazar: Ali Çehreli • • • • CV: http://acehreli.org/resume.html Kendisiyle bir söyleşi Kendisine Ceviz.net'in C ve C++ forumunda veya Ddili Forum'da rastlayabilirsiniz acehreli@yahoo.com adresinden ulaşabilirsiniz

Copyright © 2009-2011 Ali Çehreli, http://ddili.org

6

Programcılık

3

Programcılık
Programcılığın doğru tanımının ne olduğu konusunda tam bir anlaşma yoktur. Bu yazara sorarsanız, programcılık en çok zanaattır. Rastgele bir kaç fikir: • Bilgisayarın istediğimiz gibi davranmasını sağlayan programlar oluşturma işidir • Araçlar öğrenmeyi, ve bu araçları önceki ustaların deneyimleri doğrultusunda kullanmayı gerektirdiği için bir zanaattır • Problem çözdüğü ve bunu belirli kısıtlamalar altında yaptığı için mühendisliktir • Çok zevkli ve tatmin edicidir • Güzel sanat değildir ama insanın oluşturduğu her şeyde olduğu gibi programların da güzelliklerinden söz edilebilir • Kendisi bilim değildir ama kullandığı yöntemler bilgisayar bilimi tarafından geliştirilir

3.1

Öğrenmek ve öğretmek çok zordur
Programcılık 1950'lerden beri öğretilen bir uğraştır. O zamandan bu yana henüz programlamayı etkin bir şekilde öğretecek bir yöntem geliştirilememiştir. Ne yazık ki, insanların kabaca yarısına yakın bir bölümünün programlama öğrenmeye yatkın olmadıkları görülüyor. Bu konuda yapılmış araştırmalardan birisiyle ilgili olarak Ddili Forum'da açılan bir konuya göz atmak isteyebilirsiniz. O araştırmadan anlaşıldığına göre, programcılığa yatkın olanlar; anlamadıkları bir durumla karşılaştıklarında kafalarında doğru veya yanlış, ama tutarlı bir model kurabilenlerdir. Bence yatkın olan kişiler için programlamanın zorluğu, gerektirdiği bilgi miktarındadır.

Copyright © 2009-2011 Ali Çehreli, http://ddili.org

7

Atama ve İşlem Sıraları

4

Atama ve İşlem Sıraları
Eğer programcılık bir önceki bölümde sözedildiği gibi bazı insanlar için gerçekten çok zorsa; daha ileriye gitmeden önce bu insanların karşılaşacakları engellerden ilk ikisini hemen burada görelim.

4.1

Atama işlemi
Program içinde a = 10 gibi bir satır gördüğünüzde bu, "a'nın değeri 10 olsun" demektir. Benzer şekilde, b = 20 gördüğünüzde de "b'nin değeri 20 olsun" demektir. Şimdi bu bilgilere dayanarak, o iki satırdan sonra şunu gördüğümüzde ne düşünebiliriz? a = b Ne yazık ki matematikten alıştığımız kuralı burada uygulayamayız. O ifade, "a ile b eşittir" demek değildir! Baştaki iki ifadeyle aynı mantığı yürütünce, o ifadenin "a'nın değeri b olsun" demek olduğunu görürüz. "a'nın b olması" demek, "b'nin değeri ne ise, a'nın değeri de o olsun" demektir. Farkı görüyor musunuz? Matematikten alıştığımız = işareti programcılıkta bambaşka bir anlamda kullanılmaktadır: sağ tarafın değeri ne ise, sol tarafın değerini de o yapmak...

4.2

İşlem sıraları
Programlarda işlemler adım adım ve belirli bir sırada uygulanırlar. Yukarıdaki üç ifadeyi program içinde alt alta a = 10 b = 20 a = b şeklinde gördüğünüzde, onların anlamı şudur: "a'nın değeri 10 olsun, sonra b'nin değeri 20 olsun, sonra a'nın değeri b'nin değeri olsun" demektir. Yani oradaki üç işlem adımından sonra hem a'nın hem de b'nin değerleri 20 olur. Programcılık öğrenirken karşılaşılan bu iki önemli kavramı anladınız mı? Öyleyse devam...

Copyright © 2009-2011 Ali Çehreli, http://ddili.org

8

Derleyici

5

Derleyici
D programcılığında belki de en çok kullanılan iki araç metin düzenleyici ve derleyicidir. Metin düzenleyiciyi herkesin bildiğini varsayarak kısaca "yazı yazma programı" olarak hatırlatabiliriz. Programlar metin düzenleyicilerde yazılırlar. D gibi dillerde derleme kavramı ve derleyicinin işlevi de hiç olmazsa kaba hatlarıyla mutlaka bilinmelidir.

5.1

Makine Kodu
Bilgisayarın beyni CPU denen mikro işlemcidir. Mikro işlemciye ne işler yapacağını bildirmeye kodlama, bu bildirimlere de kod denir. Her mikro işlemci modelinin kendisine has kodları vardır. Her mikro işlemcinin nasıl kodlanacağına mimari tasarımı aşamasında ve donanım kısıtlamaları gözetilerek karar verilir. Bu kodlar çok alt düzeyde elektronik sinyaller olarak gerçekleştirilirler. Bu tasarımda kodlama kolaylığı geri planda kaldığı için, doğrudan mikro işlemciyi kodlayarak program yazmak çok güç bir iştir. Mikro işlemcinin adının parçası olan işlem kavramı, özel sayılar olarak belirlenmiştir. Örneğin kodları 8 bit olan hayalî bir işlemcide 4 sayısı yükleme işlemini, 5 sayısı depolama işlemini, 6 sayısı da arttırma işlemini gösteriyor olabilir. Bu hayalî işlemcide soldaki 3 bitin işlem sayısı ve sağdaki 5 bitin de o işlemde kullanılacak değer olduğunu düşünürsek, bu mikro işlemcinin makine kodu şöyle olabilir: İşlem 100 101 110 000 Değer 11110 10100 10100 00000 Anlamı YÜKLE 11110 DEPOLA 10100 ARTTIR 10100 BEKLE

Makine kodunun donanıma bu kadar yakın olması, oyun kağıdı veya öğrenci kayıtları gibi üst düzey kavramların bilgisayarda temsil edilmelerini son derece güç hale getirir.

5.2

Programlama Dili
Mikro işlemcileri kullanmanın daha etkin yolları aranmış, ve çözüm olarak üst düzey kavramları ifade etmeye elverişli programlama dilleri geliştirilmiştir. Bu dillerin donanım kaygıları olmadığı için, özellikle kullanım kolaylığı ve ifade gücü gözetilerek tasarlanmışlardır. Programlama dilleri insanlara uygun dillerdir ve çok kabaca konuşma dillerine benzerler: if (ortaya_kağıt_atılmış()) { oyun_kağıdını_göster(); } Programlama dillerinin bir sorunu, anahtar sözcüklerinin geleneksel olarak İngilizce olmasıdır. Neyse ki bunlar kolayca öğrenebilecek kadar az sayıdadır. Örneğin if 'in "eğer" anlamına geldiğini bir kere öğrenmek yeter.

Copyright © 2009-2011 Ali Çehreli, http://ddili.org

9

Derleyici

5.3

Derleyici
Derleyicinin görevi aracılıktır: insanların anladığı programlama dilini mikro işlemcinin anladığı kodlara çevirir. Bu işleme derleme denir. Her derleyici belirli bir programlama dilini bilir ve o dilin derleyicisi olarak anılır: "D derleyicisi" gibi...

5.4

Derlemeli Dil
Bu gibi dillerde yazılan programın çalıştırılır hale gelmeden önce derlenmesi gerekir. Bu yöntem çok hızlı çalışan programlar üretir; ama programı yazmanın yanında bir de derlemek gerektiği için, program geliştirme aşaması daha külfetlidir. D, derlemeli bir dildir.

5.5

Yorumlamalı Dil
Bazı programlama dilleri derleyici gerektirmezler. Bu gibi dillere yorumlamalı dil denir. Yorumlamalı dillerde yazılan programlar derlenmeleri gerekmeden hemen çalıştırılabilirler. Bu dillere örnek olarak Python, Ruby, ve Perl'ü gösterebiliriz... Derleme aşaması olmadığı için bu diller program geliştirmeyi çabuklaştırırlar. Bir sakıncaları, her çalıştırıldıklarında program metninin baştan taranmasının ve makine kodu karşılıklarının çalışma zamanında bulunmasının gerekmesidir. Bu yüzden, yorumlamalı dillerde yazılan programlar derlemeli dillerde yazılan eşdeğerlerinden genel olarak daha yavaş çalışırlar.

5.6

Derleme Hatası
Derleyiciler programı dilin kurallarına uygun olarak derledikleri için, kural dışı kodlar gördüklerinde bir hata mesajı vererek sonlanırlar. Örneğin kapanmamış bir parantez, unutulmuş bir noktalı virgül, yanlış yazılmış bir anahtar sözcük, vs. derleme hatasına yol açar. Derleyici bazen de kod açıkça kural dışı olmadığı halde, programcının yanlış yapmasından şüphelenebilir ve bu konuda uyarı mesajı verebilir. Program derlenmiş bile olsa, her zaman için uyarıları da hata gibi kabul edip, uyarıya neden olan kodu değiştirmek iyi olur.

Copyright © 2009-2011 Ali Çehreli, http://ddili.org

10

"Merhaba Dünya" Programı

6

"Merhaba Dünya" Programı
Her programlama dilinde gösterilen ilk program, merhaba dünya programıdır. Doğal olarak son derece basit olması gereken bu program, o dilde program yazabilmek için mutlaka bilinmesi gereken kavramları da içerdiği için önemlidir. Şimdilik bu kavramları bir kenara bırakalım ve önce bu programı görelim: import std.stdio; void main() { writeln("Merhaba dünya!"); } Bu kadar küçük bir programda bile değinilmesi gereken çok sayıda kavram vardır. Bu aşamada fazla ayrıntıya girmeden şöyle tanıtabiliriz: İç olanaklar: Her programlama dili kendi söz dizimini, temel türlerini, anahtar sözcüklerini, kurallarını, vs. tanımlar. Bunlar o dilin iç olanaklarını oluştururlar. Bu programda görülen parantezler, noktalı virgüller, main ve void gibi sözcükler; hep D dilinin kuralları dahilindedirler. Bunları Türkçe gibi bir dilin yazım kurallarına benzetebiliriz: özne, yüklem, noktalama işaretleri, vs... Anahtar sözcükler: Dilin iç olanaklarını belirleyen özel sözcüklere anahtar sözcük denir. Bu programda üç tane anahtar sözcük var: Programa modül eklemeye yarayan import , "hiçbir tür" anlamına gelen void , ve programın başlangıç noktasını belirleyen main . Kütüphaneler ve işlevler: Dilin iç olanakları yalnızca dilin yapısını belirler. Bu olanaklar kullanılarak oluşturulan işlevlerin bir araya getirilmelerine kütüphane adı verilir. Bunu yine Türkçe'ye benzetecek olursak; dil; cümle yapısı, ses uyumu, vs. gibi kavramları içerir. Türkçe'yi kullanarak yapılabilecek sohbetlerin sınırı yoktur: bilimsel sohbet, futbol sohbeti, vs. Bunlar hep Türkçe olsalar da, kendilerine has özelliklere, anlaşmalara, ve kullanımlara sahiptirler. İşte bu değişik sohbetleri biraz olsun programlama dili kütüphanelerine benzetebiliriz. Bu programdaki writeln işlevi, standart D kütüphanesinde çıkışa satır yazdırmak için kullanılan bir işlevdir. İsmi, "satır yaz"ın karşılığı olan "write line"dan gelir. Modüller: D'de kütüphaneler programlara modüller halinde tanıtılırlar. Bu programda kullanılan tek modül olan std.stdio 'nun ismi, "standart kütüphanenin standart giriş/çıkış modülü" olarak çevirebileceğimiz "standard input/output"tan türemiştir. Karakterler ve dizgiler: Bu programdaki "Merhaba dünya!" gibi bilgilere dizgi, dizgileri oluşturan elemanlara da karakter adı verilir. Örneğin bu programdaki dizgiyi oluşturan karakterlerden bazıları M, e, ve ! karakterleridir. İşlem sırası: Program, işini belirli adımları belirli sırada tekrarlayarak yapar. Bu sıranın en başında main isimli işlevin içindeki işlemler vardır; programın işleyişi, main 'le başlar. Bu küçük programda tek bir işlem bulunuyor: writeln 'li satırdaki işlem. Büyük/Küçük harf ayrımı: Programda değişiklik yaparken dizgilerin içinde istediğiniz karakterleri kullanabilirsiniz, ama diğer isimleri görüldükleri gibi küçük harfle yazmaya dikkat edin, çünkü D dilinde büyük/küçük harf ayrımı önemlidir. Örneğin writeln ile Writeln D dilinde farklı isimlerdir. Görüldüğü gibi, en küçük D programında bile sözü edilmesi gereken çok sayıda kavram bulunuyor. Bunları ayrıntılarıyla öğrenecek çok zamanımız olacak.

Copyright © 2009-2011 Ali Çehreli, http://ddili.org

11

"Merhaba Dünya" Programı

6.1

Kaynak dosya
Programcının D dili kurallarına göre yazdığı ve derleyiciye derlemesi için verdiği dosyaya kaynak dosya denir. D derlemeli bir dil olduğu için, kaynak dosyanın kendisi çalıştırılabilir bir program değildir. Kaynak dosya, ancak derleyici tarafından derlendikten sonra çalıştırılabilen program haline gelir. Her tür dosyanın olduğu gibi, kaynak dosyanın da diske kaydedilirken bir isminin olması gerekir. Kaynak dosya isminde sisteminizin izin verdiği her harfi kullanabilirsiniz. Ancak, D kaynak dosyalarının dosya isim uzantısının .d olması gelenekleşmiştir. Geliştirme ortamları, araç programlar, ve başka programcılar da bunu beklerler. Örnek olarak: deneme.d , tavla.d , fatura.d , vs.

6.2

Problemler
1. Yukarıdaki programı geliştirme ortamınıza kopyalayın, derleyin, ve çalıştırın. Programın çıktısı şöyle olmalıdır: Merhaba dünya! İlerideki bölümlerden yararlanabilmeniz için bunu başarmış olmanız çok önemli. Bu aşamada deneyimli birisinden yardım almanız gerekebilir. 2. Programa istediğiniz başka bir şey yazdırın. 3. Programı birden fazla satır yazacak şekilde değiştirin. 4. Programın başka yerlerinde değişiklikler yapın ve derlemeye çalışın; örneğin writeln satırının sonundaki noktalı virgül olmadığında derleme hatalarıyla karşılaştığınızı görün. ... çözümler

Copyright © 2009-2011 Ali Çehreli, http://ddili.org

12

writeln ve write

7

writeln ve write
Bundan önceki derste, yazdırılmak istenen dizginin writeln 'e parantez içinde verildiğini gördük. Programda writeln gibi iş yapan birimlere işlev, o işlevlerin işlerini yaparken kullanacakları bilgilere de parametre adı verilir. Parametreler işlevlere parantez içinde verilirler. writeln satıra yazdırmak için bir seferde birden fazla parametre alabilir. Parametrelerin birbirleriyle karışmalarını önlemek için aralarında virgül kullanılır. import std.stdio; void main() { writeln("Merhaba dünya!", "Merhaba balıklar!"); } Bazen, satıra yazdırılacak olan bütün bilgi writeln 'e hep birden parametre olarak geçirilebilecek şekilde hazır bulunmayabilir. Böyle durumlarda satırın baş tarafları write ile parça parça oluşturulabilir ve satırdaki en sonuncu bilgi writeln ile yazdırılabilir. writeln yazdıklarının sonunda yeni bir satıra geçer, write aynı satırda kalır: import std.stdio; void main() { // Önce elimizde hazır bulunan bilgiyi yazdırıyor olalım: write("Merhaba"); // ... arada başka işlemlerin yapıldığını varsayalım ... write("dünya!"); // ve en sonunda: writeln();

}

writeln 'i parametresiz kullanmak, satırın sonlanmasını sağlar. Başlarında // karakterleri bulunan satırlara açıklama satırı adı verilir. Bu satırlar programa dahil değildirler; programın bilgisayara yaptıracağı işleri etkilemezler. Tek amaçları, belirli noktalarda ne yapılmak istendiğini programı daha sonra okuyacak olan kişilere açıklamaktır.

7.1

Problemler
1. Buradaki programların ikisi de dizgileri aralarında boşluk olmadan birbirlerine yapışık olarak yazdırıyorlar; bu sorunu giderin 2. write 'ı da birden fazla parametreyle çağırmayı deneyin ... çözümler

Copyright © 2009-2011 Ali Çehreli, http://ddili.org

13

Bilgisayarlarda en küçük bilgi miktarı. Mikro işlemcinin tanımladığı veri türleri de kendi başlarına yeterli değillerdir. Tabloda kullanılan terimlerin açıklamalarını aşağıda bulacaksınız. vs. D'de belki de ancak bilimsel hesaplarda işe yarayan bazı ek türler de bulunur. O tür kavramlar ileride anlatılacak olan yapılarla ve sınıflarla ifade edilirler. 64. bitten oluşan daha büyük veri türleri. Yalnızca 0 ve 1 değerini tutabilen bir veri türünün kullanımı çok kısıtlı olduğu için. Kendi yapmadığı işleri de bilgisayarın yan birimlerine devreder. gibi.. D'nin temel türleri çoğunlukla diğer dillerdeki temel türlere benzerler. çünkü örneğin öğrenci ismi gibi veya oyun kağıdı gibi özel bilgileri tutamazlar.org 14 . 0 veya 1 değerini tutabilen ve bit adı verilen yapıdır. Bir programdaki işlemlerin çoğunu mikro işlemci yapar.. Bit.. mikro işlemciler birden fazla bitin yan yana getirilmesinden oluşan daha kullanışlı veri türleri tanımlamışlardır: örneğin 8 bitten oluşan bayt veya 32.. Copyright © 2009-2011 Ali Çehreli. Mikro işlemcinin sunduğu bu genel amaçlı veri türlerini daha kullanışlı türlere çevirmek programlama dillerinin görevidir. "64 bitlik işlemci". http://ddili.Temel Türler 8 Temel Türler Bilgisayarların beyni mikro işlemcidir. donanım düzeyinde bir kaç tane transistörden oluşur. o mikro işlemcinin o kadar bitlik olduğunu gösterir: "32 bitlik işlemci". Her mikro işlemcinin mimari tasarımı gereği en etkin olarak kullandığı veri türü. Ek olarak. D'nin temel türleri bile tek başlarına kullanıldıklarında oyun kağıdı gibi bir kavramı destekleyemezler.

nan + double. genel bir kural olarak tamsayı değerler için int kullanılır.0i double.nan + real. 8.nan double. hangisi daha büyükse.0i real.nan * 1.Temel Türler D'nin Temel Veri Türleri Tür bool byte ubyte short int uint long ulong cent ucent float real Bool değeri işaretli 8 bit işaretsiz 8 bit işaretli 16 bit işaretli 32 bit işaretsiz 32 bit işaretli 64 bit işaretsiz 64 bit işaretli 128 bit (ilerisi için düşünülmüştür.1 Tablodaki Terimlerin Açıklamaları Bool değer: Mantıksal ifadelerde kullanılan ve "doğruluk" durumunda true .nan * 1.nan * 1. "doğru olmama" durumunda false değerini alan türdür Copyright © 2009-2011 Ali Çehreli.nan * 1. ya da double'dır. float.nan + float.org 15 .nan * 1.. henüz geçerli bir tür değildir) Açıklama İlk Değeri false 0 0 0 0 0 0 0L 0L 0 ushort işaretsiz 16 bit işaretsiz 128 bit (ilerisi için düşünülmüştür. x86 mikro işlemcilerinde 80 bit).0i real.nan double 64 bit kayan noktalı sayı ifloat sanal float değer idouble sanal double değer ireal sanal real değer float. Aksine bir neden yoksa. henüz geçerli 0 bir tür değildir) 32 bit kayan noktalı sayı ya donanımın (mikro işlemcinin) tanımladığı en büyük kayan noktalı sayı türüdür (örneğin..nan * 1.nan real.0i 0xFF 0xFFFF 0x0000FFFF cfloat iki float'tan oluşan karmaşık sayı cdouble iki double'dan oluşan karmaşık sayı creal iki real'den oluşan karmaşık sayı char wchar dchar UTF-8 kod birimi UTF-16 kod birimi UTF-32 kod birimi ve Unicode kod noktası Bunlara ek olarak hiçbir türden olmama kavramını ifade eden void anahtar sözcüğü de vardır. http://ddili.0i float.0i double.

İşaretsiz tür: Yalnızca artı değerler alabilen türdür. int. tabloda İlk Değer sütununda geçen i .stringof niteliği ile öğrenebilirsiniz: import std. int.stringof). int. void main() { Copyright © 2009-2011 Ali Çehreli. 8. Kayan noktalı sayı: Kabaca. İsmi "size type"tan gelir ve "büyüklük türü" anlamındadır. Burada niteliklerden yalnızca dört tanesini göreceğiz. en küçük değerini. gerisini sonraki derslere bırakacağız: • . örneğin int . Bu türlerin başındaki u harfi. ". Adet gibi saymayla ilgili olan kavramları temsil ederken kullanılır.3 size_t Programlarda size_t türü ile de karşılaşacaksınız.min "en az" anlamına gelen "minimum"un kısaltmasıdır. türün kaç bitten oluştuğunu hesaplamak için bu değeri bir bayttaki bit sayısı olan 8 ile çarpmak gerekir • . "işaretsiz" anlamına gelen "unsigned"ın baş harfidir.stdio. Örneğin 0'dan 255'e kadar değer alabilen ubyte .min). matematikte -1'in kare kökü olan sayıdır nan: "Not a number"ın kısaltmasıdır ve geçersiz kesirli sayı değeri anlamına gelir 8. yalnızca tamsayı değerler alabilir Karmaşık sayı: Matematikte geçen karmaşık sayı değerlerini alabilen türdür Sanal değer: Karmaşık sayıların salt sanal değerlerini taşıyabilen türdür.23 gibi kesirli değerleri tutabilen türdür. ve en büyük değerini şöyle yazdırabiliriz: import std. 1. türün alabileceği en küçük değerdir • . hesapların hassasiyeti türlerin bit sayısıyla doğru orantılıdır (yüksek bit sayısı yüksek hassasiyet sağlar).2 Tür Nitelikleri D'de türlerin nitelikleri vardır. İsimleri eksi işaretinden gelir.max).stringof türün okunaklı ismidir • . ".max "en çok" anlamına gelen "maximum"un kısaltmasıdır.sizeof). void main() { writeln("Tür : writeln("Bayt olarak uzunluğu: writeln("En küçük değeri : writeln("En büyük değeri : } ".stdio.sizeof türün bayt olarak uzunluğudur. Bu türün sizin ortamınızda hangi temel türün takma ismi olduğunu yine . ortama bağlı olarak ulong veya başka bir işaretsiz temel türün takma ismidir.org 16 . bunların dışındaki türler kesirli değerler alamazlar. int.Temel Türler İşaretli tür: Hem eksi hem artı değerler alabilen türdür. Örneğin int 'in . size_t bütünüyle farklı bir tür değildir. türün alabileceği en büyük değerdir int 'in kaç bayttan oluştuğunu. Niteliklere türün isminden sonra bir nokta ve nitelik ismiyle erişilir. ".sizeof niteliğine int. http://ddili. Örneğin -128'den 127'ye kadar değer alabilen byte .sizeof diye erişilir.

4 Problem • Diğer türlerin de niteliklerini yazdırın. . çözüm Copyright © 2009-2011 Ali Çehreli.stringof). http://ddili. hiçbir türden olmamayı temsil eden void 'i ise bu problemdeki gibi durumlarda kullanamazsınız...org 17 . Yukarıdaki programı denediğim ortamda şu çıktıyı alıyorum: ulong 8.Temel Türler } writeln(size_t. Not: İlerisi için düşünüldükleri için geçersiz olan cent ve ucent türlerini hiçbir durumda.

. Eğer değişkenin değeri tanımlandığı sırada biliniyorsa. " öğrenci var. ama programda açıkça anılmaları gerekmeyen değişkenlerin isimleri olmayabilir de. yani programın çıktısı Bu okulda öğrenci_sayısı öğrenci var. Değişkenin ismi. ve hata riskini azalttığı için de önerilen bir yöntemdir: Copyright © 2009-2011 Ali Çehreli. Örnek olarak bir okuldaki öğrenci sayısı kavramını ifade eden bir değişken düşünebiliriz. Her değişken belirli bir türdendir ve her değişkenin bir değeri vardır. Yaptığı iş değer atamak olduğu için. değeri olarak belirmiştir. int 'in ilk değerinin temel türler tablosundan hatırlayacağınız gibi 0 olmasıdır. Örnek olarak hava sıcaklığı gibi bir değeri veya yarış arabası motoru gibi karmaşık bir nesneyi düşünebilirsiniz. Açıklayıcı bir isim olarak da öğrenci_sayısı uygun olur. türünü int olarak seçebiliriz. Dikkat ederseniz. Bunun nedeni. } Bu programın çıktısı şudur: Bu okulda 0 öğrenci var. // Değişkenin isminin kullanıldığı yerde değerine // dönüşmesi: writeln("Bu okulda ". D'nin yazım kuralları gereği. Bu okulda şimdi 200 öğrenci var.stdio. writeln("Bu okulda ".org 18 . öğrenci_sayısı. writeln("Bu okulda şimdi ". öğrenci_sayısı. Değişkenlerin çoğunun isimleri de olur. // öğrenci_sayısı'na 200 değerinin atanması: öğrenci_sayısı = 200. ve bu eyleme o değişkenin tanımlanması denir.Değişkenler 9 Değişkenler Programda kullanılan kavramları temsil eden yapılara değişken denir. Bir değişkenin bu şekilde tanıtılmasına. http://ddili. Öğrenci sayısı bir tamsayı olduğu için. Programın çıktısından anlaşıldığına göre. void main() { int öğrenci_sayısı. şeklinde olmamıştır. void main() { // Değişkenin tanımlanması. öğrenci_sayısı. tanımlanmasıyla değerinin atanması aynı anda yapılabilir. programda geçtiği her yerde değerine dönüşür. import std.. öğrenci_sayısı'nın int // türünde bir değişken olduğunu belirtir: int öğrenci_sayısı."). öğrenci_sayısı çıktıda ismi olarak değil. Değişkenlerin değerleri = işleci ile değiştirilir. } Bu okulda 0 öğrenci var. " öğrenci var.")."). o değişkenin tanımı. değişkenler önce türleri sonra isimleri yazılarak tanıtılırlar.stdio. öğrenci_sayısı 'nın değeri 0'dır. " öğrenci var. bu işlece atama işleci denir: import std.

. . hem atama: int öğrenci_sayısı = 100." yazdırın. http://ddili.org 19 .Değişkenler import std. Bu okulda 100 öğrenci var. çözüm Copyright © 2009-2011 Ali Çehreli.stdio."). " öğrenci var. void main() { // Hem tanım.11 kurundan 20 avro bozdurdum. 9.1 Problem • İki değişken kullanarak ekrana "2. } writeln("Bu okulda ". öğrenci_sayısı. Değişkenlerden kesirli sayı olanı için double türünü kullanabilirsiniz..

ve 0 şeklinde üç karakter olarak gönderilmiştir.1 Problem • Yukarıdaki programda stdout 'u yine writeln işlemiyle kullanın ama bir seferde birden fazla değişken yazdırın. Merhaba dünya programını böyle bir kısaltma kullanmadan şöyle de yazabiliriz: import std. örneğin öğrenci sayısı olan 100 değeri. 1 . Art arda gelen karakterler kavramına karakter akımı adı verilir. aslında stdout. Bu dönüşümler bizim özel bir şey yapmamıza gerek olmadan. aslında D programının standart çıkışıdır. Bu tanıma uydukları için D programlarının standart girişi ve çıkışı birer karakter akımıdır. } 10. Buna rağmen. ekrana aslında tamsayı 100 değeri olarak değil. ve o akımla yapılacak işlem yazılır: akım. Benzer şekilde.. otomatik olarak gerçekleşirler. . 0 .Standart Giriş ve Çıkış Akımları 10 Standart Giriş ve Çıkış Akımları Bizim ekran olarak algıladığımız çıkış. Standart çıkış karakter temellidir: yazdırılan bütün bilgi önce karakter karşılığına dönüştürülür ve ondan sonra art arda karakterler olarak standart çıkışa gönderilir.. aslında 4 ve 2 karakterleri olarak okunur.write 'in kısaltmasıdır. Standart giriş akımının ismi stdin . stdin ve stdout 'un özellikle belirtilmeleri gerekmez. Akımları kullanırken normalde akımın ismi. çözüm Copyright © 2009-2011 Ali Çehreli.writeln("Merhaba dünya!"). Normalde klavye olarak algıladığımız standart giriş de bunun tersi olarak çalışır: bilgi art arda karakterler olarak gelir ve ondan sonra programda kullanılacak değerlere dönüştürülür. http://ddili. Örneğin girişten okunan 42 gibi bir değer. çok kullanılan akımlar oldukları için. void main() { stdout. write da stdout.org 20 .işlem gibi.stdio. Önceki bölümlerde kullandığımız writeln .writeln 'in kısaltmasıdır. standart çıkış akımının ismi de stdout 'tur. Önceki bölümlerde çıkışa gönderilen tamsayılar. bir nokta.

verinin değişkene uygun olan düzende dönüştürüleceğini belirtir. Bu kullanım. Bu gösterme kavramı. "öğrenci_sayısı değişkenine" diye okunabilir. Örneğin girişten gelen '4' ve '2' karakterleri.Girişten Bilgi Almak 11 Girişten Bilgi Almak Girilen bilginin daha sonradan kullanılabilmesi için bir değişkende saklanması gerekir. standart çıkış demektir. Örneğin okulda kaç tane öğrenci bulunduğu bilgisini alan bir program. Okuma durumunda bundan başkaca önemli bir ayrıntı vardır: okunan bilginin nerede depolanacağının da belirtilmesi gerekir. &öğrenci_sayısı). http://ddili. Örneğin write(öğrenci_sayısı). Bu. yazmak.org 21 . Okuma işlemini de özetlersek: akım: işlem: okunan: hedef: stdin readf bir bilgi ? Bilginin depolanacağı hedef belirtilirken bir değişkenin adresi kullanılır. Çıkışa ne yazdırılacağını da parametre olarak veriyorduk. Burada &öğrenci_sayısı . çünkü stdout . o isimle belirtilen şeyin adresi anlamına gelir. Özetlersek: akım: işlem: yazdırılan: hedef: stdout write öğrenci_sayısı değişkeninin değeri normalde ekran write 'ın karşılığı readf 'tir. bir int 'e okunduklarında 42 tamsayı değerini oluşturacak şekilde okunurlar. "belirli bir düzende"nin İngilizcesi olan "formatted"dan gelir. D'de isimlerden önce kullanılan & karakteri.) "%s". readf konusunda bir noktayı ilerideki derslere bırakacağız ve şimdilik ilk parametresi olarak "%s" kullanmak gerektiğini kabul edeceğiz: readf("%s". Standart girişin de stdin olduğunu görmüştük. çıkışa öğrenci_sayısı değişkeninin değerinin yazdırılacağını söylemeye yetiyordu. Copyright © 2009-2011 Ali Çehreli. Yazdırma işlemi sırasında dolaylı olarak stdout akımının kullanıldığını bir önceki derste gördük. sonraki derslerde karşılaşacağımız referansların ve göstergelerin de özünü oluşturur. readf 'e okuduğu bilgiyi yerleştireceği yer bu şekilde bildirilir: &öğrenci_sayısı . bilginin nereye gideceğini açıklamaya yetiyordu. (Not: Çoğu durumda aslında boşluk karakteri ile " %s" yazmak gerekeceğini aşağıda gösteriyorum. bu bilgiyi int türünde bir değişkende tutabilir. yukarıdaki soru işaretini ortadan kaldırır: akım: işlem: okunan: hedef: din readf bir bilgi öğrenci_sayısı değişkeni İsimlerin başına & karakteri koymak o ismin belirttiği şeyin gösterilmesi anlamına gelir. İsmindeki "f".

İstediğiniz değeri yazdıktan sonra Enter'a basmanız gerekir. int öğretmen_sayısı.1 Boşlukların gözardı edilmelerinin gerekmesi Yukarıdaki gibi programlarda değerleri yazdıktan sonra Enter tuşuna basılması gerektiğini biliyorsunuz. } Ne yazık ki o program ikinci int 'in okunduğu noktada takılır: Okulda kaç öğrenci var? 100 Kaç öğretmen var? 200 ← Program burada takılır Öğretmen sayısı olarak 200 yazılmış olsa da bir önceki 100'ün sonunda basılmış olan Enter'ın kodları girişi tıkamıştır ve o yüzden öğretmen_sayısı değişkeninin değeri olan 200 okunamamaktadır. Programlar böylece bilgilerin tek satır olarak mı yoksa farklı satırlarda mı girildiklerini algılayabilirler.Girişten Bilgi Almak Bu anlatılanları gösteren programda önce sizden öğrenci sayısını bildirmeniz isteniyor. readf("%s".").stdio. readf("%s". writeln( "Anladım: okulda ". Programın girişine gelen kodları şu şekilde ifade edebiliriz: 100[EnterKodu]200[EnterKodu] Copyright © 2009-2011 Ali Çehreli. Bazı durumlarda ise girişte bekleyen o özel kodların hiçbir önemi yoktur. } 11. " öğrenci". write("Kaç öğretmen var? "). &öğrenci_sayısı). &öğrenci_sayısı).stdio. /* Girişten gelen bilginin öğrenci_sayısı değişkenine atanması */ readf("%s". " ve ". int öğrenci_sayısı. " öğrenci varmış. Bunun bir örneğini görmek için yukarıdaki programda ayrıca öğretmen sayısının da girilmesini isteyelim. öğrenci_sayısı. öğretmen_sayısı. öğrenci_sayısı. // Öğrenci sayısının tutulacağı değişkenin tanımlanması int öğrenci_sayısı. Kullanıcının Enter tuşuna basmış olması da özel bir kod olarak ifade edilir ve o bile programın standart girişinde belirir. " öğretmen varmış. &öğretmen_sayısı)."). void main() { write("Okulda kaç öğrenci var? "). Program düzenini biraz değiştirerek ve açıklamaları kaldırarak: import std. Yoksa standart girişi tıkarlar ve başka bilgilerin girilmesini engellerler. http://ddili. import std. writeln("Anladım: okulda ". void main() { write("Okulda kaç öğrenci var? ").org 22 . süzülüp gözardı edilmeleri gerekir.

readf(" %s". Hatta yazım hatasına yol açmadığı sürece hiç boşluk kullanmayabiliriz bile: import std.Girişten Bilgi Almak Girişin tıkandığı noktayı kırmızıyla belirttim. 11.. Düzen dizgisi içinde geçen boşluk karakterleri sıfır veya daha fazla sayıdaki görünmez kodu okuyup gözardı etmeye yararlar. .3 Problemler 1. O tek boşluk karakteri bütün görünmez karakter kodlarını okuyup gözardı eder: normal boşluk karakteri. Yalnızca değişen satırlarını göstererek: // . 11. &öğretmen_sayısı)..org 23 . http://ddili. Yukarıdaki program o değişiklikle artık istendiği gibi çalışır. // .. O yüzden fazla uzayan satırları bölebiliriz veya daha anlaşılır olacağını düşündüğümüz boşluklar ekleyebiliriz. Döviz kurunu ve avro adedini kullanıcıdan bilgi olarak alın ve bu bilgiyi çıkışa yazdırın. Bu durumda çözüm. &öğrenci_sayısı). okunan her değer için " %s" kullanabilirsiniz. Enter'la girilen satır sonu karakteri.... Genel bir kural olarak. Çıktısı: Okulda kaç öğrenci var? 100 Kaç öğretmen var? 200 Anladım: okulda 100 öğrenci ve 200 öğretmen varmış.} Fazla sıkışık kodu okumak güçtür. Aynı programda girişten sayı beklenen durumda harfler girin ve programın yanlış çalıştığını gözlemleyin.void main(){writeln("Okuması zor!"). Birden fazla satırda blok halinde açıklama yazmak için açıklamayı /* ve */ belirteçleri arasına alabilirsiniz. çözümler Copyright © 2009-2011 Ali Çehreli. Tab karakteri vs.stdio. Başka açıklama belirteçlerini de içerebilmek için /+ ve +/ belirteçleri kullanılır: /+ // Tek satırlık açıklama /* Birden fazla satırlık açıklama */ Yukarıdaki belirteçleri bile içerebilen açıklama bloğu +/ • Kaynak kodlardaki boşluk karakterlerinin çoğu önemsizdir. readf(" %s".2 Ek bilgiler • Daha önce gördüğümüz // karakterleri tek bir satır açıklama yazmaya elverişlidir.. // . öğretmen sayısından önce gelen Enter kodunun önemli olmadığını belirtmek için %s belirtecinden önce bir boşluk karakteri kullanmaktır: " %s". 2..

çünkü 42 gibi bir tamsayı sabiti bile 42 değerini ürettiği için bir ifadedir. 4. 2. Aşağıdaki örneklerde bir soru ile kullanılan writeln ifadelerini şöyle anlamanız gerekiyor: Eğer sorunun karşısına "true" yazılmışsa evet. çünkü yan etkileri vardır: çıkış akımına karakter yerleştirdikleri için çıkış akımını etkilemiş olurlar..Mantıksal İfadeler 12 Mantıksal İfadeler Programda asıl işleri ifadeler yaparlar. "Şu anda hava " ifadesi hava_sıcaklığı() ifadesi " derece" ifadesi ve o üç ifadeyi kullanan writeln 'li ifade Bu bölümde mantıksal ifadeleri göreceğiz ama daha ileri gitmeden önce en temel işlemlerden olan atama işlecini hatırlayalım.org 24 . çünkü "merhaba" sabit dizgisini üretir. İfadelerin değer üretiyor olmaları. Örneğin Türkçe "başkent İstanbul'dur" ifadesinin değeri false .. "merhaba" gibi bir dizgi de bir ifadedir. " derece"). http://ddili. Mantıksal ifadelerde yalnızca iki değer vardır: "doğru olmama" anlamını taşıyan false ve "doğruluk" anlamını taşıyan true . Her değerin bir değişkene ait olması gerekmez. Aslında oldukça geniş bir kavramdır. O satır toplam dört ifadeden oluşur: 1. onların başka ifadelerde değer olarak kullanılmalarını sağlar. (Not: Buradaki üretme kavramını değişken tanımlama ile karıştırmayın. Programda değer veya yan etki üreten her şeye ifade denir. 3. onu kullanarak şöyle bir çıktı oluşturabiliriz: writeln("Şu anda hava ". tatlı var" demektir. "başkent Ankara'dır" ifadesinin değeri true 'dur. Örneğin bir programın "eğer girilen yanıt Evet ise dosyayı kaydedeceğim" gibi bir kararında bir mantıksal ifade kullanılır.) writeln gibi kullanımlar da ifadedirler. Aynı şekilde Tatlı var: false Copyright © 2009-2011 Ali Çehreli. Örneğin hava sıcaklığını veren bir hava_sıcaklığı() işlevi olduğunu düşünürsek. değişken üretmekten değil.1 Mantıksal İfadeler Mantıksal ifadeler Bool aritmetiğinde geçen ifadelerdir. hava_sıcaklığı = 23 // hava_sıcaklığı'nın değeri 23 olur 12. Şimdiye kadar gördükleriniz arasından atama işlecini de bir ifade örneği olarak verebiliriz. Burada yalnızca değer üretmekten söz ediliyor. = (atama işleci): Sağ tarafındaki ifadenin değerini sol tarafındaki ifadeye (örneğin bir değişkene) atar. Karar verme düzeneğinin parçası oldukları için bilgisayarları akıllı gösteren davranışların da temelidirler. "false" yazılmışsa hayır. Böylece basit ifadeler kullanılarak daha karmaşık ifadeler elde edilebilir. Örneğin programın çıktısı Tatlı var: true olduğunda "evet. hava_sıcaklığı().

Bu işlece verilen iki ifadenin alabileceği olası değerler ve sonuçları şöyledir: Sol ifade false false true true false true false (bakılmaz) true (bakılmaz) Sağ ifade Sonuç false true true true import std. İki tarafındaki ifadeleri karşılaştırır ve == işlecinin tersi sonuç üretir. Sol tarafındaki ifadenin değeri true ise hiç sağ taraftaki ifadeyi işletmeden true değerini üretir. var: false". Aşağıdaki program parçalarını hep öyle okumanız gerekiyor. "doğru olma" anlamına geldiği için burada "var"ı temsil etsin */ bool baklava_var = false. haftadaki_gün_sayısı != 7 yıldaki_ay_sayısı != 11 // false // true • || "Veya" anlamındadır. veya ikisi birden true olduğunda true üretir. Sol taraf false ise sağ taraftakinin değerini üretir. void main() { /* false.org 25 . Mantıksal ifadelerde kullanılan mantıksal işleçler şunlardır: • == "Eşit midir" sorusunu yanıtlar. vs. Programlarda bu kadar çok kullanıldıkları için mantıksal ifadeleri çok iyi anlamak gerekir. İki tarafındaki ifadelerin değerlerini karşılaştırır ve eşit olduklarında "doğruluk" anlamına gelen true değerini. eşit olmadıklarında da "doğru olmama" anlamına gelen false değerini üretir.stdio. Onları kullanan iki eşitlik işleci ifadesi ve sonuçları şöyle gösterilebilir: haftadaki_gün_sayısı == 7 yıldaki_ay_sayısı == 11 // true // false • != "Eşit değil midir" sorusunu yanıtlar. int yıldaki_ay_sayısı = 12. Bu işlem Türkçe veya ifadesine benzer: birincisi. Tanımları son derece kısa olduğu için çok da kolaydırlar. http://ddili. Ürettiği değerin türü de doğal olarak bool 'dur. "yok" anlamına geliyor. çok kullanacağız.. Mantıksal ifadeleri daha ileride göreceğimiz koşullarda. ikincisi. Yani çıktıda "var" göründüğü için "var olduğunu" düşünmeyin. } writeln("Tatlı var: ". tatlı yok" demektir.. parametrelerde. Örneğin şöyle iki değişkenimiz olsun: int haftadaki_gün_sayısı = 7. bool kadayıf_var = true. "doğru olmama" anlamına geldiği için burada "yok"u temsil etsin true. Copyright © 2009-2011 Ali Çehreli.Mantıksal İfadeler olduğunda "hayır. çıktıdaki ". baklava_var || kadayıf_var). döngülerde.

Sol taraf sağ taraftan büyük (veya sıralamada daha sonra) veya ona eşit olduğunda true üretir. writeln("Yenilmedik: ". Sol taraf true ise sağ taraftakinin değerini üretir. Diğer işleçler bütün ifadelerinin değerlerini her zaman için hesaplarlar ve kullanırlar. > işlecinin tersidir. • <= "Küçük veya eşit midir" sorusunu yanıtlar. ahmet_burada ^ barış_burada). http://ddili. • ^ "Yalnızca birisi mi" sorusunu yanıtlar. Sol ifade false false true true Sağ ifade false true false true Sonuç false true true false Örneğin ancak ve ancak bir arkadaşımın geldiğinde tavla oynayacağımı. değilse false değerini üretir. Bu işlem Türkçe ve ifadesine benzer: birincisi ve ikincisi true olduğunda true üretir. Sol taraf sağ taraftan küçükse (veya sıralamada önceyse) true . < işlecinin tersidir. • >= "Büyük veya eşit midir" sorusunu yanıtlar. baklava_yemek_istiyorum && baklava_var). • && "Ve" anlamındadır. Sol ifade false false true true Sağ ifade false (bakılmaz) true (bakılmaz) false true Sonuç false false false true writeln("Baklava yiyeceğim: ".Mantıksal İfadeler Yukarıdaki programdaki || işlecini kullanan ifade. • > "Büyük müdür" sorusunu yanıtlar. Sol tarafındaki ifadenin değeri false ise hiç sağ taraftaki ifadeyi işletmeden false değerini üretir. yediğimiz_gol <= attığımız_gol). İki ifadeden ya biri ya öbürü true olduğunda (ama ikisi birden değil) true değerini üretir. yediğimiz_gol < attığımız_gol). onların gelip gelmeme durumlarına göre tavla oynayıp oynamayacağımı şöyle hesaplayabiliriz: writeln("Tavla oynayacağım: ".org 26 . • < "Küçük müdür" sorusunu yanıtlar. Sol taraf sağ taraftan küçük (veya sıralamada daha önce) veya ona eşit olduğunda true üretir. en az bir true değer olduğu için true değerini üretir. writeln("Yendik: ". writeln("Yenildik: ". Sol taraf sağ taraftan büyükse (veya sıralamada sonraysa) true . yediğimiz_gol > attığımız_gol). Copyright © 2009-2011 Ali Çehreli. Not: || ve && işleçlerinin bu "sağ tarafı ancak gerektiğinde" işletme davranışları işleçler arasında çok nadirdir. değilse false değerini üretir. ve bir de şimdilik sonraya bırakacağımız ?: işlecinde vardır. aksi taktirde onlarla başka bir şey yapacağımı düşünürsek.

kahve_var || çay_var && baklava_var || kadayıf_var). içine açıklamalar koydum ama isterseniz o işlevi şimdilik tamamen gözardı da edebilirsiniz. Girişten gelen "false" dizgisini false değerine. Copyright © 2009-2011 Ali Çehreli. Bütün işleçlerin işlem önceliklerini hemen hemen hiçbir programcı ezbere bilmez. Ama girişten_bool_oku() işlevinde henüz görmediğiniz olanaklar kullanılıyor. kahve_var || (çay_var && baklava_var) || kadayıf_var). Örneğin "kahve veya çay varsa ve yanında da baklava veya kadayıf varsa keyfim yerinde" gibi bir ifadeyi şöyle hesaplayabiliriz: writeln("Keyfim yerinde: ". • ! "Tersi" anlamındadır.) Aşağıdaki programlardaki main 'ler içinde anlaşılmaz bir taraf olmadığını umuyorum. 12. ve tamamen farklı anlamda bir ifadeye dönüşür: "kahve varsa. !ekmek_var).conv modülünde tanımlanmış olan to 'dan yararlanacak. yediğimiz_gol >= attığımız_gol). Bunun olabilmesi için. false ise true . keyfim yerinde". O yüzden.Mantıksal İfadeler writeln("Yenmedik: ". std. veya kadayıf varsa. ifadeler D dilinin kuralları ile belirlenmiş olan önceliklere uygun olarak işletilirler. Bu işlev. gerekmese bile parantezler kullanarak hangi ifadeyi kurmak istediğinizi açıkça belirtmek kodun anlaşılırlığı açısından çok yararlıdır. ve sonra ona bakarak bir bool değere dönüştürmemiz gerekir. && işlecinin önceliği de || işlecinden daha yüksek olduğu için.org 27 . Diğer mantıksal işleçlerden farklı olarak tek bir ifade ile çalışır ve sağ tarafındaki ifadenin değerinin tersini üretir: true ise false . Aşağıdaki programı denerken girişten "false" ve "true" girebilmenizi istiyorum. 12. veya çay ve baklava varsa. derslerin başında planladığım yoldan sapacak ve istemeyerek henüz anlatmadığım kodlar göstereceğim. Kendimiz parantezlerle gruplamazsak.2 İfadeleri Gruplamak İfadelerin hangi sırada işletilecekleri. gerektiğinde parantezlerle belirtilir.3 Girişten bool Okumak Yukarıdaki örneklerdeki bütün bool ifadeler çıkışa "false" veya "true" dizgileri olarak yazdırılırlar. "true" dizgisini de true değerine dönüştüren bir işlev kullanacağım. writeln("Bakkala gideceğim: ". http://ddili. girişten gelen kelimeyi önce bir dizgi olarak okumamız. Bunun karşıtı doğru değildir: girişten okunan "false" ve "true" dizgileri otomatik olarak false ve true değerlerine dönüştürülmezler. O yüzden. işlem öncelikleri nedeniyle şunun eşdeğeri olarak işletilir writeln("Keyfim yerinde: ". Böylece bu derste anlatılanları aynen deneyebileceksiniz. Karmaşık ifadelerde önce parantez içindeki ifadeler işletilir ve onların değeri dıştaki işleçle kullanılır. (kahve_var || çay_var) && (baklava_var || kadayıf_var)). yukarıdaki mantıksal ifadeyi gruplamadan şöyle yazarsak writeln("Keyfim yerinde: ". ("false" veya "true"dan başka bir dizgi girdiğinizde ConvException hatası atılır.

conv. Ayrıca. Bir arkadaşınız bunun üstesinden gelmek için şöyle bir program yazmış olsun. } writeln("Arasında: ". Programdaki ifadeyi o şekilde değiştirin ve artık çıktının beklendiği gibi "true" olduğunu görün.stdio.. Yine derlenemediğini görün. Bildiğiniz bool değerlerin 20 gibi bir sayıdan büyük olması gibi bir kavram tanımadık. Aynı arkadaşınız hatayı gidermek için bir şeyler denerken derleme hatasının gruplama ile giderildiğini farketsin: writeln("Arasında: ". void main() { int sayı = 15. 4. http://ddili. arabamız var. 2.org 28 . &kişi_sayısı). sayı 50 veya 1 olduğunda sonuç "false" çıksın. Copyright © 2009-2011 Ali Çehreli. import std. yazdığınız ifadenin sayı 'nın başka değerleri için de doğru çalıştığını denetleyin.string.. Sayıların büyüklüğü ve küçüklüğü ile ilgili olan < . ve ehliyetli birisi var Aşağıdaki programdaki mantıksal ifadeyi bu koşullar sağlandığında "true" yazdıracak şekilde kurun. (10 < sayı > 20)). (10 < sayı) > 20). D'de "arasında mıdır" sorusunu yanıtlayan mantıksal ifadeyi şu şekilde kodlamamız gerekir: alt sınırdan büyüktür ve üst sınırdan küçüktür. Neden? İpucu: Mantıksal ifadelerin değerlerinin bool türünde olduklarını hatırlayın. 10 < sayı > 20).4 Problemler 1. Yani verilen bir sayının iki değer arasında olup olmadığını hesaplayan işleç yoktur. işleçleri bu derste tanıdık. readf(" %s". Örneğin. Bu işleçler içinde "arasında mıdır" sorusunu yanıtlayan işleç bulunmaz. write("Kaç bisiklet var? "). Bu programı derlemeye çalışın ve derlenemediğini görün: import std.stdio. int kişi_sayısı. 3. void main() { write("Kaç kişiyiz? "). 12 olduğunda "true" çıksın. var mı?" sorularına "false" veya "true" diye yanıt verin: import std. Plaja ancak iki koşuldan birisi gerçekleştiğinde gidiyor olalım: ◦ Mesafe 10'dan az (kilometre varsayalım) ve yeterince bisiklet var ◦ Kişi sayısı 5 veya daha az. // ← derleme HATASI Derleme hatasını gidermek için bütün ifadenin etrafında parantez kullanmayı deneyin: writeln("Arasında: ".Mantıksal İfadeler 12.. Programı denerken ". > vs. import std.. Bu sefer de programın beklendiği gibi çalışmadığını görün: 15 değerinin 10 ile 20 arasında olduğunu bildiğimiz halde sonuç "false" çıkmaktadır.

while (giriş. &mesafe). } Çeşitli değerler girin. bool ehliyet_var = girişten_bool_oku("Ehliyet var mı? "). write("Mesafe? ").Mantıksal İfadeler int bisiklet_sayısı. } /* Bu işlevde henüz göstermediğim olanaklar kullanılıyor. aslında gerekmeyen açıklamalar da ekledim. } // O dizgiden bir bool değer üret bool dönüştürülen_değer = to!bool(giriş). /* Buradaki true'yu silin ve yerine sorudaki koşullardan birisi gerçekleştiğinde true üreten bir mantıksal ifade yazın: */ writeln("Plaja gidiyoruz: "... ve yazdığınız ifadenin her durumda doğru çalıştığını denetleyin: koşullara uyduğunda "true". readf(" %s". // Girilen satırı bir dizgiye oku string giriş. readf(" %s".length == 0) { giriş = chomp(readln()). true). &bisiklet_sayısı). Bu yüzden. . bool araba_var = girişten_bool_oku("Araba var mı? "). // Üretilen değeri işlevi çağırana döndür return dönüştürülen_değer. int mesafe. çözümler Copyright © 2009-2011 Ali Çehreli. */ bool girişten_bool_oku(string mesaj) { // Mesajı yazdır writef(mesaj ~ "(false veya true) ").org 29 . http://ddili. uymadığında "false" yazsın.

. Ben küme açma parantezini. Not: İfade ve deyim kavramlarının burada öğrendiğiniz tanımları D dilindeki tanımlarıdır.org 30 .stdio.. mantıksal ifade doğru değilse küme parantezleri içindeki ifadeleri işletmez. Copyright © 2009-2011 Ali Çehreli. 13."). writeln("Tabağı kaldırıyorum. Parantez içinde bir mantıksal ifade alır.. http://ddili. İngilizce'de "eğer" anlamındadır. } Örneğin "eğer baklava varsa baklava ye ve sonra tabağı kaldır" gibi bir program yapısını şöyle yazabiliriz: import std. eğer o ifade doğruysa (yani değeri true ise). küme parantezleri içindeki ifadeleri işletir. if (baklava_var) { writeln("Baklava yiyorum. Küme parantezleriyle gruplanmış ifadelerin tümüne blok. "eğer tatlı varsa" kullanımında olduğu gibi. Deyimler.if Koşulu 13 if Koşulu Programda asıl işlerin ifadeler tarafından yapıldığını öğrendik. okumayı kolaylaştırmak için programda istediğiniz gibi boşluklar kullanabilirsiniz.. } } O programda baklava_var 'ın değeri false yapılırsa çıkışa hiçbir şey yazdırılmaz.1 if bloğu ve kapsamı if deyimi. o bölgeye de kapsam adı verilir. "if". Başka dillerdeki tanımları farklılıklar gösterir ve hatta bazı dillerde böyle bir ayrım yoktur. Şimdiye kadar gördüğümüz programlarda işlemler main isimli işlev içinde baştan sona doğru ve yazıldıkları sırada işletiliyorlardı.. ifadelerin işletilip işletilmeyeceklerini ve bu ifadelerin hangi sırada işletileceklerini belirlerler. Yazım yanlışlarına yol açmadığı sürece. Bu kararları verirken de yine ifadelerin değerlerinden yararlanırlar. siz alt satıra da yazabilirsiniz. Kendileri değer üretmezler ve yan etkileri yoktur.."). if 'le aynı satıra yazmayı seviyorum. ifadelerin işletilme kararlarını veren ve ifadelerin işletilme sıralarını etkileyen program yapılarıdır. void main() { bool baklava_var = true. çünkü if deyimi kendisine verilen mantıksal ifade false olduğunda küme parantezi içindeki ifadeleri işletmez. Söz dizimi şöyledir: if (bir_mantıksal_ifade) { // işletilecek bir ifade // işletilecek başka bir ifade // vs. ifadelerin işletilip işletilmeyeceğine belirli bir mantıksal ifadenin sonucuna bakarak karar veren yapıdır. D'de deyimler. yani { karakterini. İkisi de oldukça yaygın kullanımlardır. Bunun tersi olarak.

(Bununla ilgili bir hatayı problemler bölümünde göreceksiniz. Küme parantezlerinin yerlerinin yine tercihe bağlı olduğunu görüyorsunuz. "değilse" demektir. 13.) Mutlaka küme parantezleri kullanmanızı bu noktada önermemin bir nedeni var: Bu öneriye hemen hemen hiçbir zaman uyulmayan tek durumu da şimdi anlatacağım. Yukarıdaki ifade küme parantezleri kullanılmadan aşağıdaki gibi de yazılabilir: if (ekmek_var) writeln("Yemek yiyorum"). else" zinciri Dilin bize verdiği güçlerden birisi. yoksa bakkala git" gibi bir kararda ekmek olsa da olmasa da bir eylem vardır. ifade ve deyimleri serbestçe karıştırarak kullanma imkanıdır. D'de ifade false olduğunda yapılacak işler else anahtar sözcüğünden sonraki küme parantezleri içinde belirtilir. Bisiklete binmeyi yürümekten daha çok sevdiğimizi varsayarsak: Copyright © 2009-2011 Ali Çehreli. İfade ve deyimleri kapsamlar içinde de kullanabiliriz. "else". hep bizim ifade ve deyimleri doğru sonuçlar verecek şekilde birbirlerine bağlamamızdan doğar. Örneğin bir else kapsamında bile if deyimi bulunabilir.3 Kapsam parantezlerini hep kullanın Hiç tavsiye edilmez ama bunu bilmenizde yarar var: Eğer if 'in veya else 'in altındaki ifade tekse. Söz dizimi şöyledir: if (bir_mantıksal_ifade) { // doğru olduğunda işletilen ifadeler } else { // doğru olMAdığında işletilen ifadeler } Örnek olarak: if (ekmek_var) { writeln("Yemek yiyorum").org 31 .2 else bloğu ve kapsamı Çoğu zaman if 'e verilen mantıksal ifadenin doğru olmadığı durumda da bazı işlemler yapmak isteriz. küme parantezleri gerekmez. else kendisi bir deyim değildir. 13. else writeln("Bakkala yürüyorum"). if deyiminin seçime bağlı bir parçasıdır.if Koşulu 13. Programların akıllı olarak algılanmaları.4 "if. http://ddili. } else { writeln("Bakkala yürüyorum"). Çoğu deneyimli programcı tek ifadelerde bile küme parantezi kullanır. tek başına kullanılamaz. } O örnekte ekmek_var 'ın değerine göre ya birinci dizgi ya da ikinci dizgi yazdırılır. else if. Örneğin "eğer ekmek varsa yemek ye.

o da değilse ama şöyleyse şunu yap. } else { if (bisiklet_var) { writeln("Uzaktaki fırına gidiyorum"). } else { if (bisiklet_var) { writeln("Uzaktaki fırına gidiyorum"). } } } Burada görüldüğü gibi "eğer böyleyse bunu yap. Ne yazık ki böyle yazınca kodda fazla boşluklar oluşur: buradaki 3 if deyimi ve 4 writeln ifadesi için toplam 13 satır yazmış olduk (boş satırları saymadan). } else if (komşu_evde) { writeln("Komşudan istiyorum"). } else if (bisiklet_var) { writeln("Uzaktaki fırına gidiyorum"). vs.org 32 . Biraz daha ileri gidelim ve bisiklet olmadığında hemen bakkala yürümek yerine. İçlerinde tek bir if olan else 'lerin küme parantezlerini kaldırınca: if (ekmek_var) { writeln("Yemek yiyorum"). yoksa bakkala yürüyorum". } } Oradaki if deyimlerinin anlamı şudur: "eğer ekmek varsa: yemek yiyorum." gibi yapılar programcılıkta çok kullanılır. böyle zincirleme kararlarda bir istisna olarak içlerinde tek bir if deyimi bulunan else 'lerin kapsam parantezlerini yazmayız. Sık karşılaşılan bu yapıyı daha düzenli olarak yazmak için. eğer ekmek yoksa: bisiklet varsa fırına gidiyorum. } Copyright © 2009-2011 Ali Çehreli. } else { if (komşu_evde) { writeln("Komşudan istiyorum"). komşunun evde olup olmamasına göre davranalım: if (ekmek_var) { writeln("Yemek yiyorum"). değilse ama öyleyse onu yap. Hiçbir zaman kodu aşağıdaki gibi çirkin bırakmayın! Ben bir sonraki adıma geçme aşaması olarak gösteriyorum (o yüzden de renksiz bıraktım). } else{ writeln("Yakındaki bakkala yürüyorum").if Koşulu if (ekmek_var) { writeln("Yemek yiyorum"). http://ddili. } else { writeln("Yakındaki bakkala yürüyorum"). } else{ writeln("Yakındaki bakkala yürüyorum").

çıktısında bir de tabak kaldırıldığını göreceksiniz: Limonata içiyorum Bardağı yıkıyorum Tabağı kaldırıyorum Neden? Programı düzelterek beklenen çıktıyı vermesini sağlayın. Çok sık karşılaşılan bu kod yapısına "if. } else if (bisiklet_var) { writeln("Uzaktaki fırına gidiyorum").stdio. Dört koşulun hangi sırada denetlendiği ve her koşulda ne yapıldığı bir bakışta anlaşılır. son derece okunaklı bir yapı oluşur: if (ekmek_var) { writeln("Yemek yiyorum"). writeln("Tabağı kaldırıyorum"). if (limonata_var) { writeln("Limonata içiyorum"). hem de kararlara göre işletilecek olan bütün ifadeler alt alta gelmiş olurlar. } Böylece hem satır sayısı azalmış olur. http://ddili. writeln("Bardağı yıkıyorum"). Aşağıdaki programdaki mantıksal ifadenin true olduğunu görüyoruz. else if. } else{ writeln("Yakındaki bakkala yürüyorum").org 33 . 2. 13. Kullanıcıyla oyun oynayan (ve ona fazlasıyla güvenen) bir program yazın. } else if (komşu_evde) { writeln("Komşudan istiyorum"). } else writeln("Baklava yiyorum").if Koşulu Bir adım daha ileri giderek if anahtar sözcüklerini de üstlerindeki else satırlarına çeker ve biraz da hizalarsak. Örneğin: Copyright © 2009-2011 Ali Çehreli. } Oysa programı çalıştırırsanız.5 Problemler 1. void main() { bool limonata_var = true. ya da program: Zar Değeri 1 2 3 4 5 6 Başka bir değer Program Çıktısı Siz kazandınız Siz kazandınız Siz kazandınız Ben kazandım Ben kazandım Ben kazandım HATA: Geçersiz değer Ek puan: Hatalı giriş oluştuğunda değeri de yazsın. else" denir. Kullanıcı attığı zarın değerini girsin. Zarın değerine göre ya kullanıcı kazansın. Dolayısıyla programın limonata içip bardağı yıkamasını bekleriz: import std.

http://ddili.org 34 .. Aynı oyunu şöyle değiştirelim: Kullanıcı 1'den 1000'e kadar bir sayı girsin ve 1-500 aralığında siz kazanın. çözümler Copyright © 2009-2011 Ali Çehreli.if Koşulu HATA: Geçersiz değer: 7 3. 501-1000 aralığında bilgisayar kazansın.. Hâlâ bir önceki problemdeki çözümleri uygulayabilir misiniz? .

döngünün geri kalanındaki ifadelerin işletilmeleri yerine. Bunu görmek için kullanıcıdan bir sayı alan ve bu sayı "0 veya daha büyük" olduğu sürece döngüde kalan bir program düşünelim. void main() { int sayı.. O program girilen sayı için teşekkür eder ve eksi bir sayı girildiğinde döngüden çıkar.. Mantıksal bir ifade alır. while (hâlâ_baklava_var) { writeln("Tabağa baklava koyuyorum"). Bu deyim. http://ddili. } Örneğin "baklava olduğu sürece baklava ye" gibi bir ifade şöyle programlanabilir: import std. Söz dizimi şöyledir: while (bir_mantıksal_ifade) { // işletilecek bir ifade // işletilecek başka bir ifade // vs.org 35 . Kapsamdaki işlemler tamamlanınca mantıksal ifadeye tekrar bakar ve doğru olduğu sürece bu döngüde devam eder. hemen döngünün başına dönülmesini sağlar. mantıksal ifadenin programın çalışması sırasında değiştiği durumlarda daha iyi anlaşılır. int türündeki değişkenlerin ilk değeri 0 olduğu için bu programda da sayı 'nın ilk değeri 0'dır: import std. while (sayı >= 0) { write("Bir sayı girin: "). çünkü hâlâ_baklava_var değişkeninin değeri hiç değişmemekte ve hep true olarak kalmaktadır.stdio. void main() { bool hâlâ_baklava_var = true. readf(" %s". "while". bu ifade doğru ise kapsamdaki ifadeleri işletir.1 continue deyimi "continue". "devam et" demektir. &sayı).stdio. " için teşekkürler!"). 14. writeln("Baklava yiyorum"). } } writeln(sayı. } } O program sonsuza kadar o döngü içinde kalacaktır. "olduğu sürece" demektir.while Döngüsü 14 while Döngüsü while döngüsü if koşuluna çok benzer ve onun tekrar tekrar işletilmesidir. Hatırlarsanız. while 'ın gücü. Copyright © 2009-2011 Ali Çehreli. writeln("Döngüden çıktım").

&sayı). readf(" %s".org 36 . 42 gelirse hemen çık. Bu programda 13'e teşekkür edilmez.while Döngüsü Yukarıdaki programda girilen her sayı için teşekkür etmek yerine. void main() { int sayı. &sayı). O programın davranışını şöyle özetleyebiliriz: girilen sayı 0 veya daha büyük olduğu sürece sayı al. break. ama 13 değerini kullanma.2 break deyimi Bir çok sözlük anlamı olan "break" D'de "döngüyü kır" anlamındadır. break bunu sağlar. Copyright © 2009-2011 Ali Çehreli. Şimdiki davranışını da şöyle özetleyebiliriz: girilen sayı 0 veya daha büyük olduğu sürece sayı al. " için teşekkürler!"). readf(" %s".. while (sayı >= 0) { write("Bir sayı girin: "). continue. çünkü sayı 13 olduğunda continue ile hemen döngünün başına gidilir: import std. } } } writeln(sayı. biraz seçici olalım ve 13 değeri girildiğinde beğenmeyip tekrar döngünün başına dönelim. writeln("Döngüden çıktım"). Bazen artık döngüyle işimiz kalmadığını anladığımızda döngüden hemen çıkmak isteriz. " için teşekkürler!"). if (sayı == 42) { writeln("ARADIĞIMI BULDUM!"). if (sayı == 13) { writeln("Uğursuz sayı kabul etmiyorum. void main() { int sayı. while (sayı >= 0) { write("Bir sayı girin: "). http://ddili. 14.. writeln("Döngüden çıktım")."). } } } writeln(sayı.stdio. Bu programın aradığı özel sayının 42 olduğunu varsayalım ve o sayıyı bulduğu an döngüyle işi bitsin: import std.stdio.

readf(" %s". Sonsuz döngü oluşturmak için while 'a sabit true değeri verilir. 2:İngilizce . readf(" %s". Ayşe'nin bu aralık dışında sayı girmesini kabul etmesin ve doğru sayı girene kadar Ayşe'den sayı almaya devam etsin. } } } 14. :/").. sonra görüşürüz. Bilgisayar iki kişiye (Ayşe ve Barış) şöyle bir oyun oynatsın: en başta Ayşe'den 1-10 aralığında bir sayı alsın.stdio..stdio.Seçiminiz? "). break.while Döngüsü 14."). Şu program girişten 3 geldiği sürece döngüde kalmak için programlanmış ama bir hata var: kullanıcıdan bir kere bile sayı istemiyor: import std. Örneğin kullanıcıya bir menü göstererek ondan bir komut bekleyen aşağıdaki program. Not: Ayşe'nin girdiği sayı ekranda göründüğü için tabii ki Barış tarafından hemen bilinir. void main() { // Sonsuz döngü. int seçim. Ondan sonra Barış'tan teker teker sayı almaya başlasın ve Barış'ın girdiği sayı Ayşe'nin baştan girdiği sayıya eşit olunca oyunu sonlandırsın. // Bu döngünün tek çıkışı } else if (seçim == 1) { writeln("Merhaba!"). } } Neden? O programdaki hatayı giderin ve beklendiği gibi çalışmasını sağlayın: kullanıcıdan sayı alsın ve sayı 3 olduğu sürece döngüde kalsın. &seçim). if (seçim == 0) { writeln("Tamam. çünkü mantıksal ifade hep true: while (true) { write("0:Çık.. } else if (seçim == 2) { writeln("Hello!"). } else { writeln("Onu bilmiyorum.. ancak kullanıcı özellikle istediğinde bu döngüden çıkmaktadır: import std.. Copyright © 2009-2011 Ali Çehreli.3 Sonsuz döngü break deyiminin kullanıldığı bazı durumlarda bilerek sonsuz döngü oluşturulur ve break deyimi o döngünün tek çıkışı olur. &sayı). :o) Bu aşamada bunun bir önemi yok.4 Problemler 1. burada amacımız döngüleri öğrenmek. http://ddili. 1:Türkçe.org 37 . while (sayı == 3) { write("Sayı? "). void main() { int sayı. 2..

.org 38 . çözümler Copyright © 2009-2011 Ali Çehreli. http://ddili.while Döngüsü ..

Örneğin: sayı += 10. taşma.. Sayıların bilgisayarda nasıl saklandıklarını bilmezsek. Aritmetik işlemler aslında son derece basittirler çünkü zaten günlük hayatımızda her zaman karşımıza çıkarlar. İsterseniz geri kalanını okumayabilirsiniz. bu kadarı yetebilir. Başka bir örnek olarak. Kırpılma: Tamsayılar virgülden sonrasını tutamazlar. ama yine de sondaki problemleri atlamayın. http://ddili.Tamsayılar ve Aritmetik İşlemler 15 Tamsayılar ve Aritmetik İşlemler D'nin karar verme ile ilgili yapılarından if 'i ve while 'ı gördük. 1 kutusu 4 çocuğa yeten dondurmadan 11 çocuk için 2 tane yetecek diye hesaplayabiliriz. Tür uzunluğu.. Örneğin 0 ile 255 arasında değerler tutabilen ubyte 'a 260 değeri verilmeye kalkışılırsa değeri 4 olur. örneğin 3 milyar borcu olan bir firmanın 3 milyar daha borç alması sonucunda borcunun 1. /= . Bu bölümde temel türlerin sayısal olanlarıyla yapılan aritmetik işlemlere bakacağız.7 milyara düştüğünü görebiliriz. Örneğin 3/2 ifadesinin değeri 1 olur. Ne yazık ki işler bilgisayarda bu kadar basit olmayabilir. Bunlar işlemin sonucunu soldaki değişkene atarlar. ve ^^= .1 Ayrıntılı Bilgi Bu bölüm ilgisiz bilgiler veriyor gibi gelebilir.ikinci birinci * ikinci birinci / ikinci birinci % ikinci birinci ^^ ikinci Tablodaki ikili işleçlerin yanına = karakteri gelenleri de vardır: += . -= . ve kırpılma kavramlarını anlıyorsanız bütün konuyu bu tabloya bakarak geçebilirsiniz: İşleç ++ -+ * / % ^^ Etkisi değerini bir arttırır değerini bir azaltır iki değeri toplar ikinciyi birinciden çıkartır iki değeri çarpar birinciyi ikinciye böler birincinin ikinciye bölümününden kalanı verir birincinin ikinci'nin değeri kadar üssünü alır (birinciyi ikinci kere kendisiyle çarpar) Örnek kullanım ++değişken --değişken birinci + ikinci birinci . %= . Eğer bu kavramları örneğin başka dillerden biliyorsanız. Böylece bundan sonraki bölümlerde çok daha becerikli ve ilginç programlar yazabileceksiniz. yani değerini 10 arttırmış olur. O ifade sayı 'ya 10 ekler ve sonucu yine sayı 'ya atar. çünkü aritmetik işlemler hepimizin günlük hayatta sürekli olarak karşılaştığımız kavramlardır: Tanesi 10 lira olan bir şeyden iki tane alırsak 20 lira veririz. *= . Copyright © 2009-2011 Ali Çehreli. Buna rağmen. Bu bölüm size öncekilerden daha teknik gelebilir ama tamsayıların bilgisayarda nasıl ifade edildiklerinin bir programcı tarafından mutlaka bilinmesi gerekir. veya üçü 45 lira olan şeylerin tanesi 15 liradır. temel türlerle ilgilenirken mutlaka bilinmesi gereken çok önemli kavramlar da vardır. Taşma: Her değer her türe sığmaz ve taşabilir.org 39 . 15.

Yani sonuçta bir bit. 15. 011. ve 11. 0. Yani bir bit eklemeyle toplam durum sayısı ikiye katlanmış olur. Bu durumlar 0 ve 1 değerleri olarak kabul edilmişlerdir.1. Bu türler 2. toplam 8 değişik durumda bulunabilir: 000. 001. 10. odada ışıkların açık olup olmadığı. vs. ikisinin birlikte saklayabilecekleri toplam değer adedi artar. iki değişik değer saklayabilir. 01. Bit eklemenin etkisini daha iyi görebilmek için bir adım daha atabiliriz: Üç bit. Yalnızca iki durumla ifade edilebilen kavramlarla fazla karşılaşmadığımız için bitin kullanışlılığı da azdır: yazı veya tura..2 Bitler ve tür uzunlukları Günümüzdeki bilgisayar sistemlerinde en küçük bilgi parçası bittir. 111. vs. Biraz ileri giderek iki biti bir araya getirirsek. İkisinin ayrı ayrı 0 veya 1 durumunda olmalarına göre toplam 4 olasılık vardır. gösteriyor da olabilirdi. gibi iki durumu olan kavramlar. http://ddili. ancak iki durumdan birisinde bulunabilir. Daha önce temel türler tablosunda da gördüğünüz tamsayı türleri şunlardır: Bit Uzunluğu 8 8 16 16 32 32 64 64 İlk Değeri 0 0 0 0 0 0 0L 0L Tür byte ubyte short ushort int uint long ulong Hatırlarsanız. bu 8 durumun işaretli ve işaretsiz olarak kullanılmasında aldığı değerler şu tablodakine benzer: Copyright © 2009-2011 Ali Çehreli. 3 bitlik bir türü örnek alırsak. sağdaki rakam da ikinci biti gösteriyor olsun: 00. 101. 10. 001 durumu 123 değerini.1 Tamsayılar Tamsayılar ancak tam değerler alabilen türlerdir: -2.5 gibi kesirli değerler tutamazlar..1. O türler eksi işareti olmayan türlerdir ve yalnızca sıfır ve daha büyük değerler alabilirler.org 40 . Soldaki rakam birinci biti. 110. 010.Tamsayılar ve Aritmetik İşlemler 15. tür isimlerinin başındaki u karakteri "unsigned"dan geliyordu ve "işaretsiz" demekti. Bit. vs. 100. Yoksa örneğin 000 durumu 42 değerini. elektronik düzeyde ve devrelerin belirli noktalarında elektrik geriliminin var olup olmaması kavramıyla belirlendiği için. Tabii bu kadar ilgisiz değerler kullanışlı olmayacaklarından. Bu sekiz durumun hangi tamsayı değerlerine karşılık gelecekleri tamamen anlaşmalara ve geleneklere kalmıştır.

15. 3 bitten nasıl 8 farklı değer elde edilebildiğidir.709... .1.967. Saklanabilecek Farklı Değer Adedi D Türü 2 4 8 16 32 64 128 256 .. Bunu devam ettirirsek. 32 . bitlerin bir araya getirilmelerinden oluşturulan değişik uzunluktaki türlerin saklayabildikleri farklı değer miktarlarını.446. eklenen her bit.073. dünyadaki bütün insanları kapsayacak bir kimlik kartı numarası gibi bir kavram için kullanılamaz.551.. örnek olarak kullanıyorum.) Öte yandan. 65..Tamsayılar ve Aritmetik İşlemler Bitlerin Durumu 000 001 010 011 100 101 110 111 İşaretsiz Değer 0 1 2 3 4 5 6 7 İşaretli Değer 0 1 2 3 -4 -3 -2 -1 Burada görmenizi istediğim. (D'de 3 bitlik tür yoktur.. Görüldüğü gibi. Bazı tablo satırlarını atladım ve aynı sayıda bitten oluşan D türlerinin işaretli ve işaretsiz olanlarını aynı satırda gösterdim (örneğin int ve uint 32 bitlik satırdalar). long -9223372036854775808 9223372036854775807 ulong 0 18446744073709551615 int uint -2147483648 0 2147483647 4294967295 -32768 0 32767 65535 byte ubyte -128 0 127 255 En Küçük Değeri En Büyük Değeri 64 18. çünkü uint dünyadaki insan nüfusu olan 6 Copyright © 2009-2011 Ali Çehreli. bir önceki bit uzunluğunun saklayabileceği değer miktarını 2 ile çarparak şöyle görebiliriz: Bit Adedi 1 2 3 4 5 6 7 8 ..294.org 41 ..744.3 Hangi durumda hangi tür Üç bitlik bir tür toplam 8 değer taşıyabildiği için örneğin ancak atılan zarın sonucu veya haftanın gün sayısı gibi kavramları ifade etmek için kullanılabilir.. 16 .296 . http://ddili......536 short ushort . saklanabilecek bilgi miktarını iki katına çıkartmaktadır...616 . uint çok büyük bir tür olsa da. 4.

++sayı. long ve ulong 'un Türkçe'de nasıl okunduğunu bile bilemeyeceğim toplam değer adedi ise çoğu kavram için fazlasıyla yeterlidir. bu değer bir tamsayı tür içinde ancak 2 olarak saklanabilir. } Yeni değeri: 11 Arttırma işleci. Önce aritmetik işlemleri tanıyalım.1. void main() { Copyright © 2009-2011 Ali Çehreli.max da en büyük değeri veriyordu.75 kutu gerekiyor olsa bile.max olan bir değişkeni arttırırsak. programda hiçbir uyarı verilmeden 6 milyarın ancak 4 milyardan geri kalanı. Örneğin 1 kutusu 4 çocuğa yeten dondurmadan 11 çocuk için 2. Bu durumda sonuç uint 'ten taşmış olur. yani 2 milyar kadarı sonuç değişkeninde kalır. Temel bir kural olarak.) 15.min olduğunu görürüz: import std. onlarla yapılan işlemlerde garip sonuçlara neden olur.5 Kırpılma Tamsayılar kesirli değerler tutamadıkları için ne kadar önemli olsa da.7 milyar.3 milyar. yeni değerinin int.stdio. eğer türün taşıyabileceği en yüksek değeri aşıyorsa. 15. özel bir neden yoksa. writeln("Yeni değeri: ". http://ddili. türün alabileceği en küçük değeri. o zaman taşar ve türün alabildiği en düşük değere dönüşür. en fazla 4 milyar kadar değer saklayabilen uint 'e sığmaz.min . 15.Tamsayılar ve Aritmetik İşlemler küsur milyardan daha az sayıda değer saklayabilir. Değişkenin isminden önce yazılır ve o değişkenin değerini 1 arttırır: import std. sayı). biraz aşağıda göreceğiniz atamalı toplama işlecinin 1 değeri ile kullanılmasının eşdeğeridir: sayı += 1. tamsayılar için öncelikle int 'i düşünebilirsiniz. void main() { int sayı = 10.1.. virgülden sonraki bilgiyi kaybederler. . (Aslında 6 milyar eksi 4.6 Tür nitelikleri hatırlatması Temel türlerin tanıtıldığı derste tür niteliklerini görmüştük: . Taşmaya ve kırpılmaya karşı alabileceğiniz bazı önlemleri işlemlerin tanıtımından sonra vereceğim. 15.1.7 Arttırma: ++ Tek bir değişkenle kullanılır. // ++sayı ifadesinin aynısı Arttırma işleminin sonucu.4 Taşma Türlerin bit sayılarıyla belirlenen bu kısıtlamaları.org 42 .1. Bunu denemek için önceki değeri int. Örneğin değerleri 3 milyar olan iki uint 'in toplamı gerçekte 6 milyar olsa da.stdio. yani yaklaşık olarak 1..

Tamsayılar ve Aritmetik İşlemler writeln("en düşük int değeri writeln("en yüksek int değeri : ".org 43 . int ikinci = 100. void main() { int birinci = 12.9 Toplama: + İki ifadeyle kullanılır ve aralarına yazıldığı iki ifadenin toplamını verir: import std. writeln("sayının sonraki değeri: ". http://ddili. 15. çünkü sayı hiçbir uyarı verilmeden. // değeri bir azalır Azaltma işleci. sayı).min). int.stdio.max). writeln("Sonuç: ". en yüksek değerinden en düşük değerine geçmektedir. birinci + ikinci).8 Azaltma: -Tek bir değişkenle kullanılır. 1000 + ikinci). writeln("Sabit ifadeyle: ". toplama. en düşük int değeri : en yüksek int değeri : sayının önceki değeri : sayının sonraki değeri: -2147483648 2147483647 2147483647 -2147483648 Bu çok önemli bir konudur. 15. sayı). Değişkenin isminden önce yazılır ve o değişkenin değerini 1 azaltır: --sayı. biraz aşağıda göreceğiniz atamalı çıkarma işlecinin 1 değeri ile kullanılmasının eşdeğeridir: sayı -= 1. hem de arttırma işlemi sonucunda! Buna üstten taşma denir. } int sayı = int. yeni değeri o türün en yüksek değeri olur. } Sonuç: 112 Sabit ifadeyle: 1100 Eğer iki ifadenin toplamı o türde saklanabilecek en yüksek değerden fazlaysa. ve çıkarma işlemlerinde de göreceğiz. ++sayı. : ".1. // --sayı ifadesinin aynısı ++ işlecine benzer şekilde. Benzer taşma davranışlarını azaltma. eğer değişkenin değeri baştan o türün en düşük değerindeyse. writeln("sayının önceki değeri : ". int. yine taşma oluşur ve değerlerin ikisinden de daha küçük bir sonuç elde edilir: Copyright © 2009-2011 Ali Çehreli.1.max. Buna da alttan taşma adı verilir.

http://ddili. birinci). void main() { uint sayı_1 = 10.sayı_1). Copyright © 2009-2011 Ali Çehreli. Yukarıdaki programı uint için tekrar yazarsak: import std. uint ikinci = 3000000000. int sayı_2 = 20.sayı_2). yine garip sonuçlar doğar. } -10 10 Eğer sonucu tutan değişken işaretsizse ve sonuç eksi bir değer alırsa.stdio.org 44 . uint sayı_2 = 20.10 Çıkarma: İki ifadeyle kullanılır ve birinci ile ikincinin farkını verir: import std. birinci + ikinci). writeln("SORUN! uint eksi değer tutamaz:"). writeln(" birinci: ".Tamsayılar ve Aritmetik İşlemler import std. writeln(sayı_1 . writeln("TAŞMA! Sonuç 6 milyar olmadı!"). Yine.sayı_2).max). } SORUN! uint eksi değer tutamaz: 4294967286 10 Eninde sonunda farkları alınacak kavramlar için hep işaretli türlerden seçmek iyi bir karardır. writeln(sayı_2 . void main() { // İki tane 3 milyar uint birinci = 3000000000. writeln(" ikinci: ".stdio. ikinci).1. writeln("uint'in en yüksek değeri: ". writeln(" toplam: ". özel bir neden yoksa normalde int 'i seçebilirsiniz. writeln(sayı_1 .sayı_1). void main() { int sayı_1 = 10.stdio. uint. } uint'in en yüksek değeri: 4294967295 birinci: 3000000000 ikinci: 3000000000 toplam: 1705032704 TAŞMA! Sonuç 6 milyar olmadı! 15. writeln(sayı_2 .

eğer varsa sonucun kesirli kısmı atılır.5 değil.Tamsayılar ve Aritmetik İşlemler 15. } writeln(sayı_1 * sayı_2). Örneğin 10'un 6'ya bölümünden kalan 4'tür: import std.stdio.13 Kalan: % Birinci ifadeyi ikinci ifadeye böler ve kalanını verir. Tek sayıların ikiye bölümünden kalan her zaman için 1 olduğundan. Yine taşmaya maruzdur: import std.1. } 3 15. 3 yazmaktadır: import std. Tamsayılar kesirli sayı tutamayacakları için. void main() { writeln(10 % 6).1. } 4 Bu işleç bir sayının tek veya çift olduğunu anlamada kullanılır.stdio.org 45 . http://ddili. } Copyright © 2009-2011 Ali Çehreli. void main() { writeln(7 / 2).stdio.11 Çarpma: * İki ifadenin değerlerini çarpar. void main() { uint sayı_1 = 6. kalanın 0 olup olmadığına bakarak sayının tek veya çift olduğu kolayca anlaşılır: if ((sayı % 2) == 0) { writeln("çift sayı"). } else { writeln("tek sayı"). uint sayı_2 = 7. Örneğin bu yüzden aşağıdaki program 3. 42 15. Buna kırpılma denir.1.12 Bölme: / Birinci ifadeyi ikinci ifadeye böler.

eksiyse artı yapar: import std.Tamsayılar ve Aritmetik İşlemler 15. } 81 15. void main() { int sayı_1 = 1. /= 3.5 * 2 / 3 % 7 ^^ 6 ile ile ile ile ile ile aynı aynı aynı aynı aynı aynı şey. %= 7. void main() { writeln(3 ^^ 4). int sayı_2 = -2.1. } -1 2 Bu işlecin sonucunun türü. değişkenin türü ile aynıdır.14 Üs alma: ^^ Birinci ifadenin ikinci ifade ile belirtilen üssünü alır. // // // // // // sayı sayı sayı sayı sayı sayı = = = = = = sayı sayı sayı sayı sayı sayı + 20 .1. Bunlar işlemi gerçekleştirdikten sonra ek olarak sonucu sol taraftaki değişkene atarlar: import std. uint gibi işaretsiz türler eksi değerler tutamadıkları için.stdio. *= 2. -= 5. sayı sayı sayı sayı sayı sayı } += 20. şey. writeln("eksi işaretlisi: ". 3'ün 4 kere kendisiyle çarpımıdır: import std. şey. 64 15. void main() { int sayı = 10. -sayı). bu işlecin onlarla kullanılması şaşırtıcı sonuçlar doğurabilir: uint sayı = 1. şey. Örneğin 3 üssü 4. ^^= 6.15 Atamalı aritmetik işleçleri Yukarıda gösterilen ve iki ifade alan aritmetik işleçlerin atamalı olanları da vardır.stdio. writeln(-sayı_2).1. http://ddili. şey.16 Eksi işareti: Önüne yazıldığı ifadenin değerini artıysa eksi. şimdi şimdi şimdi şimdi şimdi şimdi 30 25 50 16 2 64 writeln(sayı). writeln(-sayı_1).stdio. Copyright © 2009-2011 Ali Çehreli. şey.org 46 .

++önceki_değerli_arttırılan. int önceki_değerli_arttırılan = 1. ama içinde geçtiği ifadede önceki değeri olarak kullanılır. Copyright © 2009-2011 Ali Çehreli.1. writeln(önceki_değeri). // 1 yazılır Yani bir anlamda.17 Artı işareti: + Matematikte sayıların önüne yazılan + işareti gibi bunun da hiçbir etkisi yoktur. void main() { int normal_arttırılan = 1. // 1 yazılır writeln(önceki_değerli_arttırılan).1. writeln(+sayı_2). Normal arttırma işlecinden farklı olarak ifadeden sonra yazılır.org 47 . artıysa yine artı kalır: import std.stdio. // Değeri arttırılır ama ifadede önceki değeri kullanılır: writeln(önceki_değerli_arttırılan++). } 1 -2 15. Bunun etkisini görmek için normal ++ işleciyle karşılaştıralım: import std.stdio. int sayı_2 = -2. http://ddili. Yukarıda anlatılan ++ işlecinde olduğu gibi ifadenin değerini bir arttırır. sayı arttırılmıştır. void main() { int sayı_1 = 1. // 2 yazılır // 2 yazılır // 2 yazılır } 2 2 1 2 Yukarıdaki arttırma işleminin olduğu satır şunun eşdeğeridir: int önceki_değeri = önceki_değerli_arttırılan. writeln(+sayı_1). writeln(++normal_arttırılan). ama içinde bulunduğu ifadede önceki değeri kullanılmıştır.18 Önceki değerli arttırma: ++ Not: Özel bir nedeni yoksa normal arttırma işlecini kullanmanızı öneririm. İfadenin değeri eksiyse yine eksi. writeln(normal_arttırılan).Tamsayılar ve Aritmetik İşlemler -sayı ifadesinin türü de uint 'tir ve o yüzden eksi değer alamaz: eksi işaretlisi: 4294967295 15.

4 milyon olduğunu görürüz.1)) % 5. bu işleçlerin de D tarafından belirlenmiş olan öncelikleri vardır. parantezler kullanarak hem işlemleri doğru sırada uygulatmış olursunuz.20 İşlem öncelikleri Yukarıdaki işlemleri hep tek başlarına ve bir veya iki ifade ile gördük. O yüzden. 15. bu sonuç sıfırdan bile küçüktür! Bu sorunun nedeni. zaten yapılacak bir şey yoktur. Bir örneğe bakalım: kenarları 40'a 60 kilometre olan bir alanın her 1000 metre karesine bir elma ağacı dikmek istiyoruz. Mantıksal ifadelerde olduğu gibi. Ama bazen sonuç sığacak olsa da ara işlemler sırasında oluşabilecek taşmalar nedeniyle yanlış sonuçlar elde edilebilir. önce * işlemi uygulanacağı için sayı + 24 olarak hesaplanır. Önceki değerli arttırma ++ ile aynı şekilde davranır ama arttırmak yerine azaltır. Bu da yukarıdakinden farklı bir işlemdir.1. void main() { int en = 40000. Oysa mantıksal ifadelerde olduğu gibi. Buradaki ara işlem sırasında oluşan taşmayı değişken sıralarını değiştirerek giderebiliriz: int gereken_ağaç = en / ağaç_başına_yer * boy. } writeln("Gereken elma ağacı: ". 15. gereken_ağaç). programdaki en * boy alt işleminin bir int 'e sığamayacak kadar büyük olduğu için taşması.1. int gereken_ağaç = en * boy / ağaç_başına_yer. Örneğin * işlecinin önceliği + işlecininkinden yüksek olduğu için.Tamsayılar ve Aritmetik İşlemler 15.21 Taşmaya karşı önlemler Eğer bir işlemin sonucu seçilen türe sığmıyorsa. int sonuç = (((sayı + 8) * 3) / (sayı . int boy = 60000.stdio. int ağaç_başına_yer = 1000. Şimdi hesap doğru çıkar: Copyright © 2009-2011 Ali Çehreli. birden fazla aritmetik işlemi bir arada kullanarak daha karmaşık işlemler oluşturabiliriz: int sayı = 77. ve bu yüzden de geri kalan / ağaç_başına_yer işleminin de yanlış çıkmasıdır.19 Önceki değerli azaltma: -Not: Özel bir nedeni yoksa normal azaltma işlecini kullanmanızı öneririm. Gereken elma ağacı: -1894967 Bırakın yakın olmayı. hem de kodu okuyan kişilere kodu anlamalarında yardımcı olmuş olursunuz. parantezler kullanılmadığında.1.org 48 . Kaç ağaç gerekir? Bu problemi kağıt kalemle çözünce sonucun 40000 çarpı 60000 bölü 1000 olarak 2. Bunu hesaplayan bir programa bakalım: import std. http://ddili. sayı + 8 * 3 ifadesi.

Yazacağınız program 1'den 10'a kadar bütün sayıları ayrı satırlarda olacak şekilde yazdırsın. Programda şu şekilde tekrarlanan writeln ifadeleri kullanmayın: import std. Aynı programı. yalnızca yeterli bilgiyi versin: 10 = 5 * 2 3. 4. kesirli sayı türlerinden birisini kullanmaktır: float . 15.1. işlemlerin sırasını değiştirince kırpılma olmayacağı için sonuç doğru çıkar: writeln(10 * 9 / 9). şimdiki ara işlem olan en / ağaç_başına_yer ifadesinin değerinin 40 olduğu için artık int 'ten taşmamasıdır. Örneğin 7 ve 3 değerleri girilince çıkışa şunu yazsın: 7 = 3 * 2 + 1 2. 15. double . Dört işlemi destekleyen basit bir hesap makinesi yazın. } 9 Yine. bir tamsayı türü değil. Oysa: import std.Tamsayılar ve Aritmetik İşlemler Gereken elma ağacı: 2400000 Bu ifadenin doğru çalışmasının nedeni.org 49 . Örneğin 10 ve 5 verince gereksizce "10 = 5 * 2 + 0" yazmak yerine.22 Kırpılmaya karşı önlemler Benzer şekilde.stdio. 10 Burada da en iyi çözüm belki de bir kesirli sayı türü kullanmaktır. void main() { // Böyle yapmayın! Copyright © 2009-2011 Ali Çehreli. ara işlemlerin sırasını değiştirerek kırpılmanın da etkisini azaltabiliriz. Yazacağınız program kullanıcıdan iki tamsayı alsın ve birincinin içinde ikinciden kaç tane bulunduğunu ve artanını versin.2 Problemler 1. bir istisna olarak 7 değerini yazdırmasın. http://ddili. veya real . Ama. void main() { writeln(10 / 9 * 9). Aslında böyle bir durumda en doğru çözüm. aynı sayıya bölüp yine aynı sayıyla çarptığımızda görebiliriz: 10/9*9 işleminin sonucunun 10 çıkmasını bekleriz. Bu programda taşma ve kırpılma sorunlarını gözardı edebilirsiniz. Bunun ilginç bir örneğini.stdio. İşlemi bir menüden seçtirsin ve girilen iki değere o işlemi uygulasın. kalan 0 olduğunda daha kısa sonuç verecek şekilde değiştirin.

. writeln(3).. writeln(2). bir döngü içinde değeri arttırılan bir değişken düşünün ve 7'yi yazdırmama koşuluna da dikkat edin. http://ddili. writeln(9). writeln(4).. writeln(6). writeln(5). writeln(8). Burada herhalde eşit olmama koşulunu denetleyen != işlecini kullanmak zorunda kalacaksınız.Tamsayılar ve Aritmetik İşlemler } writeln(1). writeln(10). Onun yerine. çözümler Copyright © 2009-2011 Ali Çehreli.org 50 .

başka bir değerle karşılaştırıldığında . türün alabileceği en büyük değerdir.max 'ın eksi işaretlisidir: örneğin double. Eğer aşağıdaki listedeki herşeyi bildiğinizi düşünüyorsanız.min_normal "ifade edebildiği sıfıra en yakın normalize değer" anlamındadır (tür aslında bundan daha küçük değerler de ifade edebilir ama o değerlerin duyarlığı türün normal duyarlığının altındadır) • .infinity 'dir Kesirli sayı türleri çok daha kullanışlıdırlar ama onların da mutlaka bilinmesi gereken özellikleri vardır.org 51 . ayrıntılı bilgileri okumayıp doğrudan problemlere geçebilirsiniz: • Bin kere 0.nan real.min değildir. birbirleriyle olan ilişkilerini görmek için bir sayı çizgisine şöyle yerleştirebiliriz: Copyright © 2009-2011 Ali Çehreli.001 eklemek 1 eklemekle aynı şey değildir • == veya != mantıksal ifadelerini kesirli sayı türleriyle kullanmak çoğu durumda hatalıdır • Kesirli sayıların ilk değerleri 0 değil. . .1 Kesirli tür nitelikleri Kesirli türlerin nitelikleri tamsayılardan daha fazladır: • . Yukarıdaki nitelikleri. alttan taşma değeri -.nan 16. türün kaç bitten oluştuğunu hesaplamak için bu değeri bir bayttaki bit sayısı olan 8 ile çarpmak gerekir • . Diğer niteliklere bu aşamada gerek olduğunu düşünmüyorum. tamsayı türlerinin taşma durumunda sessiz kalmalarının aksine.infinity . bu değerin eksi işaretlisi de türün alabileceği en düşük değer olur • . . Bu bölümde de biraz ayrıntıya girmek zorundayız. Kırpılma konusunda çok iyidirler.max .nan 'dır. Belirli sayıda bitle sınırlı oldukları için taşma bu türlerde de vardır ancak alabildikleri değer aralığı tamsayılarla karşılaştırıldığında olağanüstü geniştir.Kesirli Sayılar 16 Kesirli Sayılar Tamsayıların ve aritmetik işlemlerin oldukça kolay olduklarını. http://ddili.dig "basamak sayısı" anlamına gelen "digits"in kısaltmasıdır. .stringof türün okunaklı ismidir • . buna rağmen yapılarından kaynaklanan taşma ve kırpılma gibi özellikleri olduğunu gördük.nan değeriyle işlem yapılamaz. kesirli sayılar "sonsuzluk" değerini alırlar. veya donanım sağlıyorsa daha fazla (örneğin 80) İlk Değeri float.infinity "sonsuz" anlamına gelir.nan double. bütün nitelikleri Properties for Floating Point Types başlığı altında bulabilirsiniz. taşma durumunda kullanılan değerdir Not: "Türün alabileceği en küçük değer".max "en çok" anlamına gelen "maximum"un kısaltmasıdır. türün kaç basamak duyarlığı olduğunu belirtir • . Önce kesirli sayı türlerini hatırlayalım: Tür float double real Bit Uzunluğu 32 64 en az 64.sizeof türün bayt olarak uzunluğudur.nan ne küçüktür ne de büyük • Üstten taşma değeri . Ek olarak. çünkü zaten özellikle virgülden sonrası için tasarlanmışlardır.

float. bir başka deyişle "1. ". sıfır * sonsuz).min_normal).67/103'tür. ". writeln("Tür ismi writeln("Duyarlık writeln("En küçük writeln("En büyük } : : normalize değeri: değeri : : : normalize değeri: değeri : : : normalize değeri: değeri : ". +----------+----------+ + | -max -1 -min 0 min 1 max | -infinity infinity Yukarıdaki çizginin ölçeğinin doğru olduğunu vurgulamak istiyorum: min ile 1 arasında ne kadar değer ifade edilebiliyorsa. + .nan olduğunu gördük. ". ". Bu da. writeln("Tür ismi writeln("Duyarlık writeln("En küçük writeln("En büyük writeln().67e-3 gibi yazılmışsa. ". bir başka deyişle "5. (Aynı durum eksi taraf için de geçerlidir. ". double sonsuz = double.nan + 1). Copyright © 2009-2011 Ali Çehreli.nan Açıkça ilk değeri verilmeyen kesirli sayıların ilk değerlerinin .2 . real.dig).00567'dir. Örneğin şu programdaki ifadelerin hepsi .min_normal). ". double.23e+4 gibi bir yazımdaki e+ .min_normal).stdio. min ile 1 arasındaki değerlerin son derece yüksek doğrulukta oldukları anlamına gelir. 1 ile max arasında da aynı sayıda değer ifade edilir.stringof).. ". ". real. ". } 16. ".stringof).dig). sonsuz / sonsuz).nan değeri bazı anlamsız işlemler sonucunda da ortaya çıkabilir.3 Kesirli sayıların yazımları Bu üç türün niteliklerine bakmadan önce kesirli sayıların nasıl yazıldıklarını görelim. Ek olarak. Eğer e 'den sonra gelen değer eksi ise. real.nan sonucunu verir: import std.23 çarpı 10000"dir ve ifadenin değeri 12300'dür. sıfır / sıfır). Kesirli sayıların bu gösterimlerini.stdio.) 16. double.Kesirli Sayılar + +-----------+------------+ .max). http://ddili.infinity. float. Yani bu örnek 1.max). float.67 bölü 1000"dir ve ifadenin değeri 0. "çarpı 10 üzeri" anlamına gelir. ". double. double. ". 1. real. void main() { double sıfır = 0. yani örneğin 5. float.23x104'tür. ".sonsuz). Kesirli sayıları 123 gibi tamsayı şeklinde veya 12.stringof).3 gibi noktalı olarak yazabiliriz. Yani bu örnek 5. "..max). .org 52 . o zaman "10 üzeri o kadar değere bölünecek" demektir. sonsuz .dig). void main() { writeln("Tür ismi writeln("Duyarlık writeln("En küçük writeln("En büyük writeln(). writeln("nan kullanan her işlem: writeln("sıfır bölü sıfır : writeln("sıfır kere sonsuz : writeln("sonsuz bölü sonsuz : writeln("sonsuz eksi sonsuz : ". double. türlerin niteliklerini yazdıran şu programın çıktısında göreceksiniz: import std.

79769e+308 Tür ismi : real Duyarlık : 18 En küçük normalize değeri: 3.282. writeln("Yarıya bölünce: ". sayı). // İkiye bölerek küçültmeye çalışalım: sayı /= 2.073. Yani 4900'den fazla basamağı olan bir sayı! Başka bir gözlem olarak double 'ın 15 duyarlıkla ifade edebileceği en düşük değere bakalım: 0..0000222507.000.000.40282e+38 Tür ismi : double Duyarlık : 15 En küçük normalize değeri: 2.000.5 Taşma gözardı edilmez Ne kadar büyük değerler tutuyor olsalar da kesirli sayılarda da taşma olabilir.709. real 'in en büyük değeri ise 104932 mertebesinde.000.000.18973e+4932 16.744.infinity haline gelir.3621e-4932 En büyük değeri : 1.000.000. sayı).burada 300 tane daha 0 var.4 Gözlemler ulong türünün tutabileceği en yüksek değerin ne kadar çok basamağı olduğunu hatırlıyor musunuz: 18. void main() { real sayı = real. Sayı zaten en büyük değerinde olduğu için.446. } Copyright © 2009-2011 Ali Çehreli.1 ile çarpmak.000.infinity . writeln("Önce: ".max. %110 haline getirmektir: sayı *= 1.. %10 arttırınca taşacak ve yarıya bölünse bile değeri "sonsuz" olacaktır: import std. Yani şunun gibi bir değer: 340. Bunu görmek için şu programda . sayı). en küçük kesirli sayı türü olan float 'un bile tutabileceği en yüksek değer 1038 mertebesindedir.. real türü donanıma bağlı olduğu için bu çıktı sizin ortamınızda farklı olabilir: Tür ismi : float Duyarlık : 6 En küçük normalize değeri: 1.org 53 . http://ddili.stdio.22507e-308 En büyük değeri : 1. 16.Kesirli Sayılar Benim ortamımdaki çıktısı aşağıdaki gibi oldu.000.1.. writeln("%10 arttırınca: ". Buna karşın.000.551.17549e-38 En büyük değeri : 3.000.616 sayısı 20 basamaktan oluşur.000. Kesirli sayı türlerinin iyi tarafı.max 'ın değerini %10 arttırmaya çalışalım. // 1. "eksi sonsuz" için . taşma oluştuğunda tamsayılardaki taşmanın tersine bundan haberimizin olabilmesidir: taşan sayının değeri "artı sonsuz" için .

o zaman düşünerek ve ölçerek karar verebilirsiniz. Türlerin bit sayıları sınırlı olduğu için. yüzde birler. Benzer durum kesirli sayılarda da vardır.org 54 .18973e+4932 %10 arttırınca: inf Yarıya bölünce: inf 16.1'dir (10 kuruş gibi). Öte yandan real 'in duyarlığı her ortamda aynı olmadığı için.7 Kırpılma yoktur Kesirli sayılarda virgülden sonrasını atmak anlamında kırpılma yoktur. sekizde birler. Çünkü 33 değeri sadece iki basamaktan ibarettir. basamaklarıdır. 1/3 değerini onlu sistemimizde tam olarak ifade edemeyiz çünkü virgülden sonra ne kadar uzatırsak uzatalım yeterli olmaz: 0. Öte yandan. Onlu sistemde tam olarak 0. yüzler. diye gitmesidir.8 Hangi durumda hangi tür Özel bir neden yoksa her zaman için real 'i kullanmak doğruluk açısından yararlıdır. float 'un duyarlığı çok düşüktür. onlar.33 dersek. vs. her değer tam olarak ifade edilemez. binde birler. vs. bu sefer dört basamak kullanmış olduğumuz için duyarlık 4 basamaktır.. ama küçük olmasının yarar sağlayacağı nadir programlardan birisini yazıyorsanız.. Kullandığımız onlu sayı sisteminde virgülden önceki basamaklar birler. Eğer ifade etmek istediğimiz değer bu basamakların bir karışımı ise.23 değeri 2 adet onda bir değerinden ve 3 adet yüzde bir değerinden oluştuğu için tam olarak ifade edilebilir. Bilgisayarlarda tam olarak ifade edilemeyen bir değer 0. http://ddili..0001100110011. ikili sistemde 0. vs. dörtler. dörtte birler.9 Her değeri ifade etmek olanaksızdır Her değerin ifade edilememesi kavramını önce günlük hayatımızda göstermek istiyorum.infinity değerini alınca yarıya bölünse bile sonsuz değerinde kalır: Önce: 1. virgülden öncesinin birler. ikiler. 16. taşınabilirlik açısından double kullanmak isteyebilirsiniz.Kesirli Sayılar O programda sayı bir kere real. Sayının doğruluğu duyarlığıyla ilgilidir: virgülden sonraki basamakların ne kadar doğru olduklarını duyarlık belirler... diye Copyright © 2009-2011 Ali Çehreli. değilse edilemez. Örneğin 100 liranın üçte birinin 33 lira olduğunu söylersek. Daha hassas değerler gereken bir durumda 33. değeri tam olarak ifade edebiliriz.. Örneğin 0. Eğer değer bunların bir karışımı ise tam olarak ifade edilebilir.33333. Duyarlık. virgülden sonrakiler de onda birler.1 şeklinde ifade edilebilen bu değer. Bit olarak uzunlukları ne kadar fazlaysa. vs. 16. Kesirli sayı türlerinin bit olarak uzunlukları yalnızca alabilecekleri en düşük ve en yüksek değerleri değil. yine günlük hayatta çok karşılaştığımız ama fazla sözünü etmediğimiz bir kavramdır. 16. bir değeri belirtirken kullandığımız basamak sayısıdır. duyarlıkları da o kadar fazladır. duyarlık 2 basamaktır. virgülden sonrasının da yarımlar. Bilgisayarlarda kullanılan ikili sayı sistemlerinin bir farkı. diye. değerlerin duyarlıklarını da etkiler.6 Duyarlık (Hassasiyet) Duyarlık.

Bu yüzden kesirli sayılarda başka bir karşılaştırma kavramı daha vardır: sırasızlık.10 Kesirli sayı karşılaştırmaları Tamsayılarda şu karşılaştırma işleçlerini kullanıyorduk: eşitlik (== ). Sonuçtan da anlaşılacağı gibi. 0. İşleçlerin hepsi ikilidir ve örneğin soldaki == sağdaki şeklinde kullanılır. ifadelerden birisinin . büyüklük (> ).. } } FARKLI: 1. Örneğin . Kesirli sayılarda geçersiz değeri gösteren . sonuç). } else { writeln("FARKLI: ".stdio.Kesirli Sayılar tekrarlar ve kesirli sayının duyarlığına bağlı olarak belirli bir yerden sonra hatalıdır. büyük veya eşit olma (>= ).nan 'ın mı yoksa 1'in mi daha büyük olduğu gibi bir soru yanıtlanamaz. Sırasızlık.. onun diğer değerlerle küçük büyük olarak karşılaştırılması anlamsızdır. eşit olmama (!= ). 16. döngüden çıkılabilmesi için 1001 kere tekrarlanması gerekmiştir. Aşağıdaki tablo kesirli sayı karşılaştırma işleçlerini gösteriyor. değerlerden en az birisinin . Örneğin 1.. if (sonuç == 1) { writeln("Beklendiği gibi 1"). void main() { float sonuç = 0.nan < 1.) Bunu gösteren aşağıdaki örneği ilginç bulabilirsiniz. işleçlerin hangi durumda ne sonuç verdiğini gösterir.nan olması durumunda o işlecin kullanımının anlamlı olup olmadığını gösterir.org 55 . ifadelerden birisi real. (Tekrarladığını söylediğim o son sayıyı ikili sistemde yazdım. Bir değişkenin değerini bir döngü içinde her seferinde 0.nan da bulunduğu için. ve bu değerdeki hata miktarının sonucu 1000 kere etkilemesidir.001 arttıralım. Döngünün 1000 kere tekrarlanmasının ardından sonucun 1 olmasını bekleriz. // Bu döngünün 1000 kere tekrarlandıktan sonra 1 değerine // ulaşacağını düşünürüz: while (sonuç < 1) { sonuç += 0. küçük veya eşit olma (<= ).nan olması demektir. } // Bakalım doğru mu.nan ifadesinin sonucu false çıksa bile.001.2 ifadesi de false verir.00099 Bunun nedeni. Sonuncu sütun. Copyright © 2009-2011 Ali Çehreli..nan olduğu için bu sonucun bir anlamı yoktur çünkü bunun tersi olan real. false ve true içeren sütunlar. küçüklük (< ). http://ddili. Oysa öyle çıkmaz: import std.2 < real.001 değerinin de tam olarak ifade edilemeyen bir değer olması. onlu değil.

çözümler Copyright © 2009-2011 Ali Çehreli. . ! karakteri içeren. büyüktür. eşit değildir küçüktür veya büyüktür küçüktür. 1.. Girişten 5 tane kesirli sayı alan bir program yazın. Eğer bu programı şimdiye kadar öğrendiklerinizle yazarsanız.23. Böylece hesap makineniz çok daha doğru sonuçlar verecektir. veya 1.nan ise false true false false false false true false false true true true true true . Denerken değerleri girmek için 1000. çoğu karşılaştırmaya "değil" sonucunu verdirir. 2. .nan ile Anlamlı evet evet hayır hayır hayır hayır evet hayır hayır evet evet evet evet evet Dikkat ederseniz.nan ile kullanılması anlamlıdır ve sonuç hep true 'dur.. dizileri anlamanız daha kolay olacak. büyük.org 56 .Kesirli Sayılar İşleç == != > >= < <= !<>= <> <>= !<= !< !>= !> !<> Anlamı eşittir eşit değildir büyüktür büyüktür veya eşittir küçüktür küçüktür veya eşittir küçük. veya eşittir küçük değildir ve eşit değildir küçük değildir büyük değildir ve eşit değildir büyük değildir küçük değildir ve büyük değildir Soldaki Soldaki İkisi Büyükse Küçükse Eşitse false true true true false false false true true true true false false false false true false false true true false true true false false true true false true false false true false true false false true false true false true true En Az Birisi . Bu sayıların önce iki katlarını yazsın. 16. http://ddili. Önceki bölümdeki hesap makinesini kesirli bir tür kullanacak şekilde değiştirin. Bu problemi bir sonra anlatılacak olan dizilere hazırlık olarak soruyorum. yani anlamında "değildir" sözü bulunan bütün işleçlerin .11 Problemler 1.23e4 şeklinde yazabilirsiniz. sonra da beşe bölümlerini.nan 'ın geçerli bir değeri göstermiyor olması.

Tek bir değişkenin tanımlanması ile bir dizinin tanımlanmasını şöyle karşılaştırabiliriz: int tekDeğişken.. şimdiye kadarki kodlarda gördüklerimiz gibi tek değişken tanımıdır. sayı_2. türün isminden sonraki köşeli parantezler içinde belirtilmesidir. 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. Bu yöntem her duruma uygun değildir. "double türünde 5 tane sayı" diye okunabilir. O iki tanımdan birincisi. çünkü değişken sayısı arttığında onları teker teker tanımlamak. sayı_2. 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: double[5] sayılar. int[10] onDeğişkenliDizi.. Tek farkı. sayı_3.org 57 .. sayı_5.Diziler 17 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. sayı_4. Ö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ı. sayı_1000 diye tanımlamak hemen hemen olanaksız bir iştir.. 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. dizide kaç değişken bulunacağının. programcının tanımladığı daha karmaşık türler de kullanılabilir (bunları daha sonra göreceğiz). . Copyright © 2009-2011 Ali Çehreli.. Dizilerin bir yararı böyle durumlarda ortaya çıkar: diziler bir seferde birden fazla değişken tanımlamaya yarayan olanaklardır. O değişkenleri ayrı ayrı şöyle tanımlamıştık: double double double double double sayı_1.1 Tanımlanması Dizi tanımı değişken tanımına çok benzer. yani bir seferde kaç değişken tanımlanmakta olduğunun. sonra da beşe bölmüştük. Özetle. ikincisi ise 10 değişkenden oluşan bir dizidir. Bin tane sayıyla işlem yapmak gerektiğini düşünün. Bin tane değişkeni ayrı ayrı sayı_1. Tür ismi olarak temel türler kullanılabileceği gibi. Birden fazla değişkeni bir araya getirmek için en çok kullanılan veri yapısı da dizidir. 17. dizi tanımı. Bu tanım. http://ddili. içinden çıkılmaz bir hal alır.

Aralık ayının sıra numarasının 11 olduğuna dikkat edin. Topluluk değişkenlerinin her birisine eleman denir. 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.. çünkü bazı programcı hatalarının kaynağı bu uyumsuzluktur. 4. Aralık ayına karşılık gelen elemana // erişir ve değerini 31 olarak belirler ayGünleri[11] = 31.Diziler // Yüz kutunun ağırlıklarını ayrı ayrı tutan bir dizi double[100] kutuAğırlıkları.2 Topluluklar ve elemanlar Aynı türden değişkenleri bir araya getiren veri yapılarına topluluk adı verilir. Sayıları hep birden bir dizi halinde ve sayılar isminde tanımlayınca elemanlara farklı isimler verme şansımız kalmaz. Bu uyumsuzluğa özellikle dikkat etmek gerekir. Bu şekilde yazınca sayı_1 ifadesinin yerini sayılar[0] ifadesi almış olur... Copyright © 2009-2011 Ali Çehreli.org 58 . Dizilerin barındırdıkları eleman adedine dizilerin uzunluğu denir. 17. Hatırlatma: Ocak ayının sıra numarasının 0. // Bu da bir erişimdir. Onun yerine. // Bir okuldaki bütün öğrencilerin kayıtları ÖğrenciBilgisi[öğrenciAdedi] öğrenciKayıtları. Örneğin Temmuz ayındaki günlük hava sıcaklıklarını tutmak için kullanılacak bir dizi. elemanlara dizinin erişim işleci olan [] ile ve bir sıra numarasıyla erişilir: sayılar[0] O yazım. ve 5 olarak numaraladığımız sayılar dizi içinde 0. http://ddili. 3.. Bu tanıma uydukları için diziler de toplulukturlar. dizilerde numaralar 0'dan başlar.. 17.. Ocak ayındaki gün sayısını // yazdırmaktadır writeln("Ocak'ta ". 12 tane int'ten oluşmaktadır ve her // ayda kaç gün bulunduğu bilgisini tutmaktadır int[12] ayGünleri. Dizi tanımlarken kullanılan [] karakterleri elemanların türünden sonra yazılır ve dizide kaç eleman bulunduğunu belirler. 2. 3. • [] karakterlerinin iki farklı kullanımı: Dizi tanımlarken kullanılan [] karakterleri ile erişim işleci olarak kullanılan [] karakterlerini karıştırmayın.3 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. 1. ve 4 olarak numaralanırlar.. 31 tane double değişkenini bir araya getirebilir ve bir hava sıcaklığı topluluğu oluşturur. "Eleman adedi" ve "dizi uzunluğu" ifadelerinin ikisi de sık kullanılır. ayGünleri[0]. " gün var").. 2. Bizim 1. // Bu bir erişimdir. "sayıların 0 numaralı elemanı" diye okunabilir. 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.

0'dan dizinin uzunluğundan bir eksiğine kadar olan değerlerdir. indeks olarak değişken değerleri de kullanılabilir. http://ddili. Örneğin 3 elemanlı bir dizide yalnızca 0. Burada yalnızca üç tanesini tanıyacağız.Diziler 17. yani Mart ayındaki gün adedi yazdırılır. ve 2 indeksleri yasaldır. İndeks sabit bir değer olmak zorunda değildir. çünkü dizinin uzunluğunu boş bırakmak diziyi dinamik yapmaya yeter: int[] dinamikDizi. Ö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 → elemanlar → 0 1 2 3 4 5 6 7 8 9 10 11 | 31 | 28 | 31 | 30 | 31 | 30 | 31 | 31 | 30 | 31 | 30 | 31 | İlk elemanın indeksi 0.6 Dizi nitelikleri Türlerin olduğu gibi dizilerin de nitelikleri vardır.5 Sabit uzunluklu diziler ve dinamik diziler Kaç eleman barındıracakları programın yazıldığı sırada bilinen dizilere sabit uzunluklu dizi.4 İndeks Elemanlara erişirken kullanılan sıra numaralarına indeks. ayNumarası 'nın başka bir değerinde de o aydaki gün sayısı yazdırılır. ve Şubat ayındaki gün sayısı olan 28 değerine sahip. Bu olanak dizilerin kullanışlılığını büyük ölçüde arttırır. elemanlarının sayısı programın çalışması sırasında değişebilen dizilere dinamik dizi denir.org 59 . 17. Ö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 ". O dizilerin uzunlukları programın çalışması sırasında değiştirilemez. Böyle dizilerin uzunlukları programın çalışması sırasında gerektikçe arttırılabilir veya azaltılabilir. 17. ikinci elemanın indeksi 1. " gün çeker").length : Dizinin uzunluğunu. Dinamik dizi tanımlamak belki de sabit uzunluklu dizi tanımlamaktan daha kolaydır. vs. ayNumarası 'nın 2 olduğu bir durumda yukarıdaki ifadede ayGünleri[2] 'nin değeri. Yasal olan indeksler. Bunun dışında indeks kullanıldığında program bir hata ile sonlanır. Copyright © 2009-2011 Ali Çehreli. elemanları yan yana duran bir topluluk olarak düşünebilirsiniz. ayGünleri[ayNumarası]. dizi. ve Ocak ayındaki gün sayısı olan 31 değerine sahip. Dizileri. . " tane eleman var").length niteliğinin dinamik dizilerde farklı olduğuna dikkat edin: • . yani içindeki eleman sayısını verir: writeln("Dizide ".length. Uzunluklarının değişmesi gerekse. elemanlara erişme işine de indeksleme denir. 1. bu ancak kaynak koddaki sabit olan değerin elle değiştirilmesi ve programın tekrar derlenmesi ile mümkündür. 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.

http://ddili.. readf(" %s". 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. 17. vs. } // Beşte birlerini hesaplayan döngü de bir önceki // döngünün benzeridir.Diziler Ek olarak. dinamik dizilerde dizinin uzunluğunu değiştirmeye de yarar: int[] dizi. ++sayaç.length) { write("Sayı ". Elemanlar için kullanılan ilk değer.length) { writeln(sayılar[sayaç] * 2).7 Elemanları ilklemek D'de her türde olduğu gibi dizi elemanları da otomatik olarak ilklenirler.org 60 .. Copyright © 2009-2011 Ali Çehreli.. sayılar[sayaç] ifadesi de sırayla dizinin elemanlarını göstermiş oluyor: sayılar[0] . sayaç = 0. sayılar[1] . dizi. } writeln("İki katları:").reverse. ++sayaç.stdio. sayaç + 1.length = 5. } Gözlemler: Döngülerin kaç kere tekrarlanacaklarını sayaç belirliyor: döngüleri. Bu bilgiler ışığında 5 değişkenli probleme dönelim ve onu dizi kullanacak şekilde tekrar yazalım: import std. while (sayaç < sayılar. void main() { // Bu değişkeni döngüleri kaç kere tekrarladığımızı saymak // için kullanacağız int sayaç. elemanların türüne bağlıdır: int için 0. // boştur // uzunluğu 5 olur • . Bu programın yararını görmek için girişten 5 yerine örneğin 20 sayı alınacağını düşünün.sort : Elemanları küçükten büyüğe doğru sıralar: dizi. double için double. o değişkenin değeri sayılar. • . Sayacın değeri her tekrarda bir arttıkça.reverse : Elemanları ters sırada sıralar: dizi.sort. &sayılar[sayaç])..nan . // Sayıları bir döngü içinde girişten alıyoruz while (sayaç < sayılar. // double türündeki beş elemandan oluşan sabit uzunluklu // bir dizi tanımlıyoruz double[5] sayılar.length 'ten küçük olduğu sürece tekrarlıyoruz... ": "). Dizi kullanan bu programda tek bir yerde küçük bir değişiklik yapmak yeter: 5 değerini 20 olarak değiştirmek. vs.

31.Diziler Yukarıdaki programdaki sayılar dizisinin beş elemanı da dizi tanımlandığı zaman double. Daha sonra da girişten gelen değeri. 30. Böylece kullanıcının 1-12 aralığında verdiği numara. 30. void main() { // Şubat'ın 28 gün çektiğini varsayıyoruz int[12] ayGünleri = [ 31. 28. } O programda ayGünleri dizisinin elemanlarının dizinin tanımlandığı anda ilklendiklerini görüyorsunuz. Copyright © 2009-2011 Ali Çehreli. 20.stdio. int indeks = ayNumarası . 31. // Bütün elemanları 1 olur 17. 31. program dizinin dışına erişildiğini bildiren bir hata ile sonlanır. sayılar isimli dizinin sayaç indeksli elemanının değeri olarak okuduk: readf(" %s". Bunun örneklerini yukarıdaki programlarda gördük. http://ddili. atama söz dizimiyle ve elemanların ilk değerleri sağ tarafta belirtilerek tanımlanır. kullanıcıdan alınan ve değeri 1-12 aralığında olan ay numarasının indekse nasıl dönüştürüldüğüne dikkat edin.nan değerine sahiptir: double[5] sayılar. int ayNumarası. Öyle durumlarda dizi. programda 0-11 aralığına dönüştürülmüş olur. Örneğin ayGünleri dizisinin 11 indeksli elemanına 31 değerini atadık: ayGünleri[11] = 31. Ayrıca. Kullanıcıdan ay numarasını alan ve o ayın kaç gün çektiğini yazan bir program düşünelim: import std. write("Kaçıncı ay? "). Kullanıcı 1-12 aralığının dışında bir değer girdiğinde. Bazen elemanların değerleri. " gün çeker"). sağdaki dizinin elemanlarının hepsini birden soldaki diziye kopyalar: int[5] kaynak = [ 10. // dizinin bütün elemanlarının // ilk değeri double.org 61 . bütün elemanlarını ilgilendiren bazı işlemlerde büyük kolaylık sağlarlar. 30. &sayılar[sayaç]). &ayNumarası). ay ". 31. readf(" %s".nan olur Elemanların bu ilk değerleri dizi kullanıldıkça değişebilir. Kopyalama: Atama işleci. int[5] hedef. 30.8 Basit dizi işlemleri Diziler. writeln(ayNumarası. Bu durumda dizinin bütün elemanları o değeri alır: int[10] hepsiBir = 1. ". ayGünleri[indeks]. 30. 50 ]. dizi kurulduğu anda bilinir.1. 31 ]. 40. hedef = kaynak. 31. Dizileri ilklerken sağ tarafta tek bir eleman değeri de kullanılabilir.

// 20 yazar // 30 yazar } Eğer sol tarafta sabit uzunluklu bir dizi varsa. int[21] sonuç. dizi ~= 7.stdio.length). sağ tarafın uzunluğu sol tarafa uymazsa program çöker: int[10] birinci = 1. dinamik dizinin sonuna yeni bir eleman veya yeni bir dizi ekler: int[] dizi. Yazacağınız program önce kullanıcıdan kaç tane sayı girileceğini öğrensin ve girişten o kadar kesirli sayı alsın. // ← derleme HATASI Birleştirme: ~ işleci iki diziyi uç uca birleştirerek yeni bir dizi oluşturur. Burada dizi niteliklerinden .sort 'u ve . // . dizi ~= 360. // // // // dizi boştur dizide tek eleman vardır dizide iki eleman olur dizide dört eleman olur Sabit uzunluklu dizilerde dizinin uzunluğu değiştirilemez: int[10] dizi.9 Problemler 1. sonuç ~= birinci.Diziler Eleman ekleme: ~= işleci. Daha sonra bu sayıları önce küçükten büyüğe. int[10] ikinci = 2. sonuç ~= birinci. sonra da büyükten küçüğe doğru sıralasın. writeln(sonuç.org 62 . dizi ~= [ 30. dizi ~= 7. sonuç = birinci ~ ikinci. dizinin uzunluğu değiştirilemeyeceği için ~= işleci kullanılamaz: int[20] sonuç. sonuç = birinci ~ ikinci. Aynı işlecin atamalı olanı da vardır (~= ) ve sağdaki diziyi soldaki dizinin sonuna ekler: import std. void main() { int[10] birinci = 1. // ← derleme HATASI Atama işleminde de. http://ddili. O kod.reverse 'ü kullanabilirsiniz. programın "dizi kopyası sırasında uzunluklar aynı değil" gibi bir hatayla çökmesine neden olur: object. int[10] ikinci = 2. Copyright © 2009-2011 Ali Çehreli. writeln(sonuç. int[] sonuç.length)... 40 ].Exception: lengths don't match for array copy 17.

++i. sonra da çift olanlarını sırayla yazdırsın. int i = 0. write(i + 1. readf(" %s".Diziler 2. bu sayıların karelerini bir diziye yerleştiren. kareler[i] = sayı * sayı. void main() { int[5] kareler. Ö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. Bu programın hatalarını giderin ve beklendiği gibi çalışmasını sağlayın: import std. çözümler writeln().length) { write(kareler[i]. Girişten beş tane sayı alan.org 63 . while (i <= kareler. " ").. } } . while (i <= 5) { int sayı. writeln("5 tane sayı giriniz"). 3..stdio. } writeln("=== Sayıların Kareleri ==="). sayı: "). Copyright © 2009-2011 Ali Çehreli. &sayı). Ö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. ++i. http://ddili. Bir arkadaşınız yazdığı bir programın doğru çalışmadığını söylüyor. Girilen sayıların tek veya çift olduklarını da aritmetik işlemler sayfasında öğrendiğiniz % (kalan) işlecinin sonucuna bakarak anlayabilirsiniz. Başka bir program yazın: girişten aldığı sayıların önce tek olanlarını sırayla. ". ve sonunda da dizinin elemanlarını çıkışa yazdıran bir program yazmaya çalışmış ama programı doğru çalışmıyor.

Buna açıklık getirmek için önce bu konunun kısa tarihine bakmamız gerekiyor. 97 Her bir değer bir harfe karşılık gelir. Hatta Felemenkçe'nin ij karakteri gibi bazı karakterler bu tablolarda yer bulamamışlardır. Örneğin küçük harf a 'nın tamsayı değeri 97'dir veya 1 rakamının tamsayı değeri 49'dur. IBM Kod Tabloları: IBM firması. Örneğin iki 'a' harfi için iki tane 97 değeri kullanılmıştır. 98. http://ddili. Bu değerler tamamen anlaşmalara bağlı olarak atanmışlardır ve kökleri ASCII kod tablosuna dayanır. sık kullanılan noktalama işaretlerini. 114. Her ne kadar ASCII'den çok daha yararlı olsalar da. Not: Bu baytların gerçekte hangi sırada durdukları platforma ve hatta baytların içinde bulundukları belgeye göre farklılıklar gösterir. "merhaba" metnindeki karakterlerin ASCII kodları sırasıyla şöyledir (bu gösterimlerde okumayı kolaylaştırmak için bayt değerleri arasında virgül kullanıyorum): 109. Eğer char türünü başka dillerden tanıyorsanız onun her harfi barındıracak kadar büyük bir tür olmadığını biliyorsunuzdur. Örnek olarak. Önceki dersteki dizilere ek olarak bu derste de karakterleri tanıyınca iki ders sonra anlatılacak olan dizgi yapısı çok kolay anlaşılacak. IBM'in kod tablolarının sorunları bu standartta da vardır. 128'den fazla özel karakteri olan diller zaten 8 bitlik bir tabloda ifade edilemezler. Ayrıca.1 Tarihçe ASCII: Donanımın çok kısıtlı olduğu günlerde tasarlanan ilk ASCII tablosu 7 bitlik değerlerden oluşuyordu ve bu yüzden ancak 128 karakter değeri barındırabiliyordu. programların çıktılarını uç birimlerde gösterirken kullanılan kontrol karakterlerini. 437 numaralı kod tablosu ile görüntülendiğinde ª karakteri olarak belirir.Karakterler 18 Karakterler Karakterler yazıları oluşturan en alt birimlerdir: harfler. 18. ASCII tablosuna dayanan ve 128 ve daha büyük karakter değerlerini dünya dillerine ayıran bir dizi kod tablosu tanımladı. noktalama işaretleri. Bu kod tabloları sayesinde İngiliz alfabesinden başka alfabelerin de desteklenmeleri sağlanmış oldu. ISO/IEC 8859 Kod Tabloları: Uluslararası standartlaşma çalışmaları sonucunda ISO/IEC 8859 standart karakter kodları çıkmış. Bu değerler İngiliz alfabesini oluşturan 26 harfin küçük ve büyük olanlarını. 104. ve örneğin Türk alfabesinin özel harfleri 8859-9 tablosunda yer almışlardır. vs. kod tablolarının önemli sorunları vardır: Yazının doğru olarak görüntülenebilmesi için yazıldığı zaman hangi kod tablosunun kullanıldığının bilinmesi gerekir. Burada yalnızca karakterleri ifade etmek için kullanılan kod değerlerini görüyoruz. Yapısal olarak IBM'in tablolarının eşdeğeri olduğu için. Donanımdaki gelişmeler doğrultusunda ASCII tablosundaki kodlar daha sonra 8 bite çıkartılarak 256 karakter destekleyen Genişletilmiş [Extended] ASCII tablosu tanımlanmıştır. çünkü farklı kod tablolarındaki kodlar farklı karakterlere karşılık gelirler. boşluk karakteri.org 64 . rakamları. Başka bir sorun. Örneğin Türk alfabesine özgü karakterler IBM'in 857 numaralı kod tablosunda yer aldılar. 97. Örneğin 857 numaralı kod tablosunda Ğ olan karakter. karakterler de bitlerin birleşimlerinden oluşan tamsayı değerler olarak ifade edilirler. Bilgisayarlar temelde bitlerden oluştukları için. vs. D'de üç farklı karakter türü bulunur. 101. Karakterler bazı dillerde geleneksel olarak 256 farklı değer tutabilen char türüyle gösterilirler. Copyright © 2009-2011 Ali Çehreli. rakamlar. yazı içinde birden fazla dilin karakteri kullanıldığında kod tablolarının yetersiz kalmalarıdır. ifade etmek için yeterliydi.

0. http://ddili. Yukarıda "merhaba"nın karakterlerinin ASCII kodlarıyla nasıl ifade edildiklerini görmüştük. 0. 0. Not: Baytların sıraları farklı olabilir ve bazılarının gerçek değerleri farklıdır. hiçbir karışıklık ve kısıtlama yaşamadan. Metin içindeki karakterlerin büyük bir bölümünün İngiliz alfabesindeki karakterlerden oluştuğu durumlarda. 0. 18. Unicode'un desteklediği karakter sayısı o kadar çok olunca. dünya dillerindeki ve yazı sistemlerindeki harflerin.org 65 . 0. Unicode'dur. Böylece. 0. bu kodlama her zaman için ASCII kodlamasının 4 katı yer harcamaktadır. 109. UTF-32: Bu kodlama her Unicode karakteri için 32 bit kullanır.Karakterler Unicode: Bütün bu sorunları kökünden çözen standart. 0. 98. Öte yandan. 97. çoğu karakter için 3 tane de 0 kullanıldığı için bu kodlama. 97. her karakter için 4 bayt kullanılmasıdır. Örnek olarak "aĞ" UTF-16'da 4 bayt olarak kodlanır: 0. Not: Bayt değerlerinin nasıl hesaplandığı konumuz dışında kalıyor. yaklaşık yüz bin Unicode karakterinin geri kalan 30 bin kadarı için daha fazla bayt kullanmak gerekir. 101. yani 2 baytla gösterir.2 Unicode kodlama çeşitleri Unicode. 0. İki bayt yaklaşık olarak 65 bin değer tutabildiği için. Copyright © 2009-2011 Ali Çehreli. 0. Ğ harfinin Unicode'daki değeri 286'dır. ve Ğ'de 2 tane anlamlı bayt olduğu için. 1. Karakterlerin elektronik ortamda nasıl ifade edildiklerine karakter kodlaması denir. her karaktere 4 bayt verebilmek için gereken doldurma baytları olarak düşünebiliriz. Bu kodlama çoğu belgede UTF-32'den daha az yer tutar ama karakterine göre değişik sayıda bayt kullanıldığı için işlenmesi daha karmaşıktır. Dikkat ederseniz. farkı. 0. 1. 0. 114. ifade edilecek metnin örneğin "aĞ" olduğunu düşünürsek: 0. 30 Not: Baytların sıraları farklı olabilir ve bazılarının gerçek değerleri farklıdır. Unicode. karakterleri ifade eden değerler de doğal olarak artık 8 bitle ifade edilemezler. yani 4 bayt. bir arada bulundurabilirler. ve yazım işaretlerinin 100 binden fazlasını tanımlar ve her birisine farklı bir kod verir. Başka bir örnek olarak. Bu yüzden UTF-32 kodlamasının şöyle olacağını düşünebilirsiniz (okumayı kolaylaştırmak için iki satır halinde gösteriyorum): 0. her bir karaktere bir kod değeri verir. 0. 0. 97. "merhaba"nın UTF-32 kodlaması da ASCII kodlamasıyla aynıdır. 0. 0. 0. 0. 30 Yani a'da 1. UTF-16: Bu kodlama. Aşağıdaki örneklerde kullanıldığı için bu iki baytın değerlerinin 1 ve 30 olduklarını söylemekle yetineceğim. 0. 0. karakterlerin her birisinin tam olarak 4 bayt yer tutuyor olmasının getirdiği yararları da vardır. Şimdi Unicode karakterlerinin standart kodlamalarından üçünü göreceğiz. Örnek olarak. 0. 0. duruma göre çok savurgan olabilir. 97 0. Unicode karakterlerinin çoğunu 16 bitle. Örneğin kod değeri 255'ten büyük olduğu için Ğ'nin en az 2 baytla gösterilmesi gerekir. 5 tane de sıfır bulunmaktadır. 104. 0. karakterlerin. Bu sıfırları. 0. Unicode'un tanımladığı kodları kullanan metinler bütün dünya karakterlerini.

) • Karakterlerin bazılarını standart kısa isimleriyle yazmak. Temel türlerin tanıtıldığı sayfada gösterildikleri gibi: Tür char Açıklama işaretsiz 8 bit UTF-8 karakter değeri İlk Değeri 0xFF 0xFFFF 0x0000FFFF wchar işaretsiz 16 bit UTF-16 karakter değeri dchar işaretsiz 32 bit UTF-32 karakter değeri Başka bazı programlama dillerinden farklı olarak. Türk alfabesinin İngiliz alfabesinde bulunmayan özel karakterleri 2 baytlık gruptadırlar. klavyeden doğrudan karakterin tuşuna basmak • Çalışma ortamındaki başka bir programdan veya bir metinden kopyalamak. tek baytla ve aynen ASCII tablosundaki değeriyle ifade edilir. Öte yandan.3 D'nin karakter türleri D'de karakterleri ifade etmek için 3 değişik tür vardır. dchar 4 bayttan oluştuğu için her Unicode karakterini tutabilir. 18. Karakterleri sabit olarak program içine yazmanın bir çok yolu vardır: • En doğal olarak. Türk alfabesindeki bazı harflerin Unicode kodları 2 bayttan oluştukları için char türündeki değişkenlere atanamazlar. http://ddili. karakterleri 1 veya daha fazla baytla ifade eder. Çoğu belge için UTF-8 bütün kodlamalar içinde en az yer tutan kodlamadır. Bunu biraz aşağıda anlatacağım. Örneğin "aĞ" için: 97. "a" tek karakterli bir dizgidir. Başka bir yararı.4 Karakter sabitleri Karakterleri program içinde tek olarak belirtmek gerektiğinde etraflarına tek tırnak işaretleri koyulur: char a_harfi = 'a'. bütün karakterler gerçekten gereken sayıda baytla ifade edilirler. Bunun söz dizimi \&karakter_ismi. diğerleri de 4 bayt olarak ifade edilirler.Karakterler UTF-8: Bu kodlama. Örneğin bir internet sitesinden. Bunlar yukarıda anlatılan Unicode kodlama yöntemlerine karşılık gelirler. şeklindedir. wchar büyük_yumuşak_g = 'Ğ'. Eğer karakter ASCII tablosundaki karakterlerden birisiyse. veya çalışma ortamında karakter seçmeye yarayan bir programdan kopyalanabilir (Şu anda çalıştığım Linux ortamında bu programın ismi Character Map. Buna rağmen D'nin Unicode desteği tam değildir. Bu kodlamada hiç savurganlık yoktur. bazıları 3. D'de her karakter aynı uzunlukta değildir. ASCII tablosundaki kodlara aynen karşılık geldiği için. çünkü o zaman iki ders sonra göreceğimiz dizgi sabiti anlamına gelir: 'a' karakter değeridir. Karakter sabitleri için çift tırnak kullanılamaz. Bunların dışındaki karakterlerin bazıları 2.org 66 . Örneğin Ğ harfi Unicode'da 2 baytla gösterildiği için 8 bitlik char türüne sığmaz. Örneğin avro karakterinin ismi euro 'dur ve programda değeri şöyle yazılabilir: Copyright © 2009-2011 Ali Çehreli. 1. ASCII kodlanarak yazılmış ve İngiliz alfabesini kullanan belgeler de otomatik olarak UTF-8 düzenine uyarlar. 30 Not: Baytların sıraları farklı olabilir ve bazılarının gerçek değerleri farklıdır. 18.

org 67 . writeln("\x41\u011f fiyatı: 10. wchar için \udört_haneli_kod söz dizimini.'. • Karakterleri tamsayı gibi Unicode değerleriyle belirlemek: char a = 97. o noktalarda yeni satır açılmasını sağlar: write("birinci satır\nikinci satır\nüçüncü satır\n"). Çıktısı: birinci satır ikinci satır üçüncü satır Copyright © 2009-2011 Ali Çehreli. Yazdırılacak metnin içinde istediğimiz noktalara \n karakterleri yerleştirmek.Karakterler wchar para_sembolü = '\&euro."). Örneğin uç birime yeni bir satıra geçileceğini bildiren yeni satır karakterinin gösterilecek bir şekli yoktur. http://ddili.5 Kontrol karakterleri Bazı karakterler yalnızca metin düzeniyle ilgilidirler. İsimleri olan başka karakterleri D'nin isimli karakterler listesinde bulabilirsiniz. Bu yazımda karakterin kodunun onaltılı sayı sisteminde (hexadecimal) yazılması gerekir: wchar Ğ_w = '\u011e'. Örneğin şu iki satır aynı çıktıyı verirler: writeln("Ağ fiyatı: 10. • ASCII tablosundaki karakterleri değerleriyle \sekizli_düzende_kod veya \xonaltılı_düzende_kod söz dizimleriyle yazmak: char soru_işareti_sekizli = '\77'. Bu yöntemler karakterleri çift tırnak içinde bir arada yazdığınız durumlarda da geçerlidir. dchar Ğ_d = '\U0000011e'. wchar Ğ = 286. yalnızca yeni bir satıra geçilmesini sağlar. dchar için de \Usekiz_haneli_kod söz dizimini kullanabilirsiniz. Kontrol karakterleri \özel_harf söz dizimiyle ifade edilirler. çıktıda yeni satır açmadığını bildiğimiz write 'la bile yeni satır açtırabiliriz. kendilerine özgü bir görünümleri yoktur. 18. Yazım \n \r \t İsim yeni satır satır başı sekme Yeni satıra geçirir Satırın başına götürür Bir sonraki sekme noktasına kadar boşluk bırakır Açıklama Örneğin \n karakterini kullanırsak.25\&euro.25€"). Böyle karakterlere kontrol karakteri denir. char soru_işareti_onaltılı = '\x3f'. • Karakterleri Unicode değerleriyle yazmak.

7 std.6 Tek tırnak ve ters bölü Tek tırnak karakterinin kendisini tek tırnaklar arasında yazamayız. toUniLower('Ğ')). } Çıktısı: Copyright © 2009-2011 Ali Çehreli. writeln("İ'nin küçüğü: ". doğruysa sıfırdan farklı bir değer döndürürler. harf midir? ". isUniUpper('ç')). İlk ikisi açma ve kapama tırnakları olarak algılanırlar. void main() { writeln("ğ küçük müdür? ". derleyici onu bir özel karakterin başlangıcı olarak algılar: '\' . çünkü derleyici ikinci tırnağı gördüğünde tırnakları kapattığımızı düşünür: ''' . toUniUpper('ı')). Ters bölü karakteri de başka özel karakterleri ifade etmek için kullanıldığı için. üçüncüsü de tek başına algılanır ve yazım hatasına neden olur. Başında is olan işlevler karakterle ilgili soru cevaplarlar ve cevap yanlışsa 0. isUniLower('ğ')).uni sayfasında görebilirsiniz. writeln("ş'nin büyüğü: ".stdio. isUniLower('Ş')). writeln("ı'nın büyüğü: ".')). Derleyici \' yazımını bir özel karakter olarak algılar ve baştaki tek tırnakla eşlemek için bir tane daha tek tırnak arar ve bulamaz. http://ddili.uni modülü std. toUniLower('İ')). Böylece bu işlevler mantıksal ifadelerde kullanışlıdırlar: • isUniLower : küçük harf mi? • isUniUpper : büyük harf mi? • isUniAlpha : bir harf mi? Başında to olan işlevler. writeln("İ büyük müdür? ". writeln("z harf midir? ". writeln("\&euro. isUniAlpha('\&euro. Bu iki karakteri sabit olarak yazmak için başlarına bir ters bölü daha yazılır: Yazım \' \\ İsim Açıklama tek tırnak Tek tırnağın karakter olarak tanımlanmasına olanak verir: '\'' ters bölü Ters bölü karakterinin yazılmasına olanak verir: '\\' veya "\\" 18.uni. writeln("Ğ'nin küçüğü: ". isUniAlpha('z')). Bu modüldeki işlevleri Ddili Wiki'nin std. writeln("Ş küçük müdür? ".org 68 .Karakterler 18. writeln("ç büyük müdür? ".uni modülü Unicode karakterleriyle ilgili yardımcı işlevler içerir. toUniUpper('ş')). import std. isUniUpper('İ')). verilen karakteri kullanarak yeni bir karakter üretirler: • toUniLower : küçük harfini üretir • toUniUpper : büyük harfini üretir Bu işlevleri kullanan bir program: import std.

stdio. trileri. Oysa çoğu yabancı alfabede bu konuda bir tutarsızlık vardır: noktalı i'nin büyüğü noktasız I'dır. Kırım Tatar.8. writeln("I'nın küçüğü: ". Türk.org/cgi-bin/trileri_deneme Copyright © 2009-2011 Ali Çehreli.8.org 69 . trileri kütüphanesini şu sayfada deneyebilirsiniz: http://www. Bu yüzden bu iki harf için özel dikkat göstermemiz gerekir.1 Her alfabe sorunludur Karakter kodları kullanılarak yapılan küçük-büyük dönüşümleri ve harf sıralamaları. Örneğin I'nın küçüğünün i olarak dönüştürülmesi Azeri ve Kelt alfabeleri için de yanlıştır.uni.Karakterler ğ küçük müdür? 1 Ş küçük müdür? 0 İ büyük müdür? 1 ç büyük müdür? 0 z harf midir? 1 € harf midir? 0 Ğ'nin küçüğü: ğ İ'nin küçüğü: i ş'nin büyüğü: Ş ı'nın büyüğü: I 18. Kürt. I'nın küçüğü de i olur. İşte bu sorunu gösteren bir program: import std. void main() { writeln("i'nin büyüğü: ".8 Türk alfabesinin şanssız harfleri: ı ve i ı ve i harflerinin küçük ve büyük biçimleri Türk alfabesinde tutarlıdır: noktalıysa noktalı. toUniUpper('i')).2 trileri kütüphanesi Alfabelerle ilgili bu gibi sorunlara çözüm getiren bir proje. } ve yanlış çıktısı: i'nin büyüğü: I I'nın küçüğü: i 18. Bilgisayar sistemlerinin temelleri İngiliz alfabesiyle başladığı için ne yazık ki i'nin büyüğü I. ve İrlanda alfabelerini desteklemekte ve bu alfabelerin küçük-büyük dönüşüm ve sıralama sorunlarını çözmektedir. http://ddili. toUniLower('I')). bu yazının yazıldığı sırada İngiliz. á gibi aksanlı harfler İngiliz alfabesinde bile z'den sonra gelirler. noktasızsa noktasız. 18. Ddili Forum üyeleri tarafından geliştirilen trileri kütüphanesidir. Örneğin ğ gibi Türk alfabesine özgü harfler z'den sonra sıralandıkları gibi. Hakas.ddili. aslında bütün alfabeler için sorunludur. Azeri. Gagavuz. Benzer sorunlar harflerin sıralanmalarında da bulunur. Türkmen. import std. Kumuk.

UTF-8 kodlaması kullanan konsolun ASCII tablosunda bulunmayan ğ gibi harfleri birden fazla kod ile temsil etmesi. write("Lütfen bir harf girin: "). char ikinci_kod. char olarak okunduğunda harfin kendisinin değil. void main() { char harf.stdio.Karakterler 18.9 Girişten karakter okumadaki sorunlar D'nin Unicode konusunda getirdiği güç ve esneklik. readf(" %s". bu sefer harfin UTF-8 kodlamasını standart çıkış tarafında tamamlamakta ve karakter doğru olarak gösterilmektedir: Lütfen bir harf girin: ğ Okuduğum harf: ğ ← iki char 'ın oluşturmuş olduğu harf Bu sonuçlar standart giriş ve çıkışın char akımları olmalarından kaynaklanır. Program girişten iki char okumakta ve onları aynı sırada çıkışa yazdırmaktadır. &birinci_kod). ve readf 'in char okurken bu kodlardan yalnızca ilkini alıyor olmasıdır. O char asıl karakteri temsil etmeye yetmediği için de writeln 'ın yazdırdığı eksik kodlanmış olan harf konsolda gösterilememektedir. onu oluşturan kodların okunmakta olduklarını harfi iki farklı char olarak okuyarak görebiliriz: import std. readf(" %s". &harf). harf). birinci_kod. programın girişinden aldığı Türkçe harfleri belki de doğru olarak yazdırdığını görebilirsiniz. Bu çelişki. &ikinci_kod). girişten karakter okurken beklenmedik sonuçlar doğurabilir. } Yukarıdaki programı Unicode kodlaması kullanılmayan bir ortamda çalıştırırsanız. karakter ile ne kastedildiğinin açık olmaması nedeniyledir. yazdırılan harfin sizin yazdığınızla aynı olmadığını görürsünüz. Öte yandan aynı programı çoğu Linux konsol penceresinde olduğu gibi bir Unicode ortamında çalıştırırsanız. ikinci_kod). Karakterlerin iki ders sonra göreceğimiz dizgiler aracılığıyla aslında çok daha rahat okunduklarını göreceksiniz. Daha ileriye gitmeden önce. bu sorunu yaşayan bir program göstermek istiyorum. writeln("Okuduğum harf: ". void main() { char birinci_kod. Örneğin UTF-8 kodlaması kullanan bir konsolda ASCII tablosunda bulunmayan bir harf girildiğinde: Lütfen bir harf girin: ğ Okuduğum harf: ← girilen harf görünmüyor Bunun nedeni. write("Lütfen bir harf girin: "). readf(" %s".org 70 . http://ddili. import std.stdio. Copyright © 2009-2011 Ali Çehreli. O char değerlerinin art arda konsola gönderilmiş olmaları. } writeln("Okuduğum harf: ".

UTF-16. ve konuşma dilinde "karakter" dediğimiz her şey bu tanıma girer. D'nin char . karakterleri ifade etmeye genelde elverişli olmasa da özel durumlarda birden fazla alfabe karakterini destekler • dchar UTF-32 kodlaması içindir. ve UTF-32 kod birimlerini ifade ederler.Karakterler 18. Unicode ile kodlanmış olan bir metin en aşağıdan en yukarıya doğru şu düzeylerden oluşur: • kod birimi (code unit): UTF kodlamalarını oluşturan kod değerleridir. Unicode'un tamamını değil ama oldukça kullanışlı bir alt kümesini destekler. D'nin gözünde tek kod noktası olan ğ ile art arda gelen g ve ̆ karakterlerinin ilgileri yoktur.11 Özet • Unicode.org 71 . 32 bit olması nedeniyle bütün Unicode karakterlerini destekler ve kod noktası olarak kullanılabilir Copyright © 2009-2011 Ali Çehreli. ğ karakteri ise iki kod biriminden oluşur. vs. • karakter (character): yazı sistemlerinde kullanılmak üzere Unicode'un tanımlamış olduğu bütün şekiller. bazı karakterlerin birden fazla kod noktasından oluşabilmeleridir. Yukarıda da değindiğim gibi. wchar . Bu kod noktaları kodlamaya bağlı olarak bir veya daha fazla kod birimi ile ifade edilirler. Öte yandan. kod noktalarının bileşimlerinden oluşan karakter kavramını desteklemez. D'de kod noktalarını tam olarak destekleyen tür dchar 'dır. im. char ve wchar ise yalnızca kod birimi türü olarak kullanılmaya elverişlidirler. Örneğin a ve ğ iki farklı kod noktasıdır. D. UTF-8 kodlamasında a tek kod birimi ile. dünya yazı sistemlerindeki bütün karakterleri destekler • char UTF-8 kodlaması içindir. http://ddili. imler. bir kod noktasıdır. her ikisi de UTF-16 ve UTF-32 kodlamalarında tek kod birimi ile ifade edilirler. • kod noktası (code point): Unicode'un tanımlamış olduğu her harf. Örneğin UTF-8 kodlamasında a karakteri tek kod biriminden.10 D'nin Unicode desteği Unicode çok büyük ve karmaşık bir standarttır. Bu konuda Unicode'un getirdiği bir karışıklık. ğ ise iki kod birimi ile ifade edilir. Örneğin ğ harfini ifade etmenin iki yolu vardır: ◦ tek başına ğ kod noktası olarak ◦ art arda gelen g ve ̆ kod noktaları olarak (g ve sonrasında gelen birleştirici (combining) breve şapkası) D. ve dchar türleri sırasıyla UTF-8. kodlamaya ve karakterin kendisine bağlı olarak bir veya daha fazla kod biriminden oluşabilirler. 18. Unicode karakterleri. karakterleri ifade etmeye genelde elverişli olmasa da ASCII tablosunu destekler • wchar UTF-16 kodlaması içindir.

yalnızca dizi dediğim zaman da fark gözetmeden dilimleri ve sabit uzunluklu dizileri kasdetmiş olacağım. 31 ]. .org 72 . 19. 9]. . Onlar asıl dizinin elemanlarına erişim sağlarlar. 6]. 31.. */ = ayGünleri[0 . Bir dilimdeki bir elemanın değiştirilmesi asıl dizideki asıl elemanı etkiler. 30.. 3]. Bunu görmek için dört çeyreğin ilk elemanlarına dört farklı değer verelim ve asıl diziyi yazdıralım: ilkÇeyrek[0] = 1. aralığın_sonundan_bir_sonrası Başlangıç indeksi aralığa dahildir.. Bu olanağa. 31. 30. Sınıf ve yapı arayüzleriyle ilgili olan Phobos aralıklarını daha ilerideki bir derste göstereceğim. ve 2 dahil. Örnek olarak ayGünleri dizisini dörde dilimleyerek birbirinden farklı dört çeyrek diziymiş gibi şöyle kullanabiliriz: int[12] ayGünleri = [ 31. 12]. 28.. 3]. // 0. 1. . O kodda sol taraftaki yapılar dilimdirler ve asıl dizinin dört değişik bölgesine erişim sağlamaktadırlar. 31. dizi gibi kullanılabilme özelliği nedeniyle bazen dinamik dizi.1 Dilimler Dilimler aslında dinamik dizilerle aynı olanaktır. 31. Buradaki önemli nokta. başka dizinin bir parçasına erişim sağlama özelliği nedeniyle de bazen dilim denir. Copyright © 2009-2011 Ali Çehreli.. bundan başkaca anlam taşımaz • sabit uzunluklu dizi: eleman adedi değiştirilemeyen dizidir. bitiş indeksi aralığın dışındadır: /* . sonÇeyrek[0] = 4. 31. elemanları bir başlangıç indeksinden bir bitiş indeksine kadar belirlemeye yarayan aralık söz dizimiyle tanımlanırlar: aralığın_başı . 3 hariç Not: Burada anlatılan aralıklar Phobos kütüphanesinin aralık kavramından farklıdır.Başka Dizi Olanakları 19 Başka Dizi Olanakları Elemanları bir araya getirmeye yarayan dizileri Diziler dersinde görmüştük.. int int int int ilkÇeyrek[] ikinciÇeyrek[] üçüncüÇeyrek[] sonÇeyrek[] = = = = ayGünleri[0 ayGünleri[3 ayGünleri[6 ayGünleri[9 . ikinciÇeyrek[0] = 2.. http://ddili. kendi elemanları yoktur. 30. Dilimler. Ama önce karışıklığa neden olabileceğini düşündüğüm bazı terimleri listelemek istiyorum: • dizi: sıra numarasıyla erişilen eleman topluluğudur. sahip olmadığı elemanlara erişim sağlar • dilim: dinamik dizilerin çok yaygın başka bir ismidir Bu derste özellikle dilim dediğim zaman dilimleri (yani dinamik dizileri). Var olan başka bir dizinin elemanlarının bir bölümünü sanki daha küçük farklı bir diziymiş gibi kullandırmaya yarar. üçüncüÇeyrek[0] = 3. o dilimlerin kendilerine ait elemanlarının bulunmuyor olmasıdır. 30. O dersi kısa tutmak için özellikle sonraya bıraktığım başka dizi olanaklarını burada göstereceğim. kendi elemanlarına sahiptir • dinamik dizi: eleman adedi değiştirilebilen dizidir..

length yazmak yerine kısaca $ karakteri kullanılabilir: writeln(dizi[dizi.Başka Dizi Olanakları writeln(ayGünleri). 1]. 28. 31.1]).dup İsmi "kopyala" anlamına gelen "duplicate"in kısası olan . çıktısı: Dilimin uzunluğu: 0 19.length). dizinin son elemanını da aralığa dahil etmek gerektiğinde ikinci indeks olarak dizinin uzunluğu kullanılır. 1. Örneğin uzunluğu 3 olan bir dizinin bütün elemanlarına erişim sağlamak için dizi[0. 30. 2 ]. Bu konuda kolaylık olarak ve yalnızca [] işleci içindeyken.3 Kopyasını almak için . 4. // dizinin son elemanı // aynı şey 19. writeln("Dilimin uzunluğu: ". 30.dup.dup niteliği. önce normal senelerdeki ayGünleri 'nin bir kopyasını almak ve o kopya dizideki Şubat'ın gün sayısını bir arttırmaktır: Copyright © 2009-2011 Ali Çehreli. 31] Dikkat ederseniz.2 dizi.length yerine $ Dizi elemanlarını [] işleci ile indekslerken bazen dizinin uzunluğundan da yararlanmak gerekebilir.1]).. Bir yöntem. dilim. indeks]. Dilim söz diziminde bitiş indeksi aralığın sonundan bir sonrası anlamına gelir. 30. 31. var olan bir dizinin elemanlarının kopyasından oluşan yeni bir dizi üretir: double[] dizi = [ 1.3] yazılır. 2. Örneğin 3 elemanlı bir dizinin yasal indeksleri 0. ve 2'dir..length . Bu yüzden. double[] kopyası = dizi. // ← çalışma zamanı HATASI Başlangıç indeksinin bitiş indeksine eşit olması ise yasaldır ve boş dilim anlamına gelir: int[] dilim = birDizi[indeks . dizi. 3.25. 1. Aralık söz dizimindeki doğal bir kısıtlama. int[] dilim = dizi[2 ..75 ].org 73 . Değişen elemanları sarıyla gösteriyorum: [1. her dilim kendisinin 0 numaralı elemanını değiştirdiğinde o dilimin asıl dizide erişim sağladığı ilk eleman değişmiştir. writeln(dizi[$ . http://ddili. indeks 'in yasal bir indeks değeri olduğunu kabul edersek. 31. Dizi indekslerinin 0'dan başladıklarını ve dizinin uzunluğundan bir eksiğine kadar olduklarını daha önce görmüştük. 3. başlangıç indeksinin bitiş indeksinden büyük olamayacağıdır: int[] dizi = [ 0. Bir örnek olarak Şubat'ın 29 gün çektiği senelerdeki ayların gün sayılarını tutan bir dizi oluşturmak isteyelim.

30. 2 ]. elemanların değerleri değişir: int[3] a = [ 1. 30. erişim sağlamakta olduğu elemanları bırakmasına ve yeni elemanlara erişim sağlamaya başlamasına neden olur: int[] tekler = [ 1. 31. 31. ++artıkYıl[1]. 31.. int[] dilim. 31. 31.org 74 . 9. int[] artıkYıl = ayGünleri. 31. void main() { int[12] ayGünleri = [ 31. 30. 30. 31. 30. Yukarıdaki koddaki dilim başlangıçta hiçbir dizinin elemanına erişim sağlamazken önce tekler 'in bazı elemanlarına. 31. 31] Artık : [31. 31. sonra da çiftler 'in bazı elemanlarına erişim sağlar: [5. 31. // henüz hiçbir elemana erişim sağlamıyor // a'nın elemanları da 2 olur dilim = tekler[2 . sabit uzunluklu dizilerde de aynı anlamdadır. writeln("Artık : ". 31.stdio. int[] çiftler = [ 2. ayGünleri). writeln(dilim). 2. 8] 19. Çıktısı: Normal: [31. 30.5 Uzunluğun artması paylaşımı sonlandırır Sabit dizilerin uzunlukları değişemediği için bu konu yalnızca dilimlerle ilgilidir. 6. int[3] b = [ 2. 11 ]. 7] [4. 31. a = b. 28. writeln(a). 7.4 Atama işlemi Değerini değiştirme olarak bildiğimiz atama işlemi. writeln(dilim). 1. 2. 28. 31] 19. Copyright © 2009-2011 Ali Çehreli. $ .dup. 30. 31. 8.Başka Dizi Olanakları import std.1]. 2] Dilimlerle kullanıldığında ise atama işleminin anlamı çok farklıdır: Dilimin. 4. 30. http://ddili. 3. artıkYıl). dilim = çiftler[1 . 30.. 30. 30. // yeni dizideki Şubat'ın gün sayısını // arttırır } writeln("Normal: ". 30. 1 ]. Çıktısı: [2. $ . 31.2]. 10 ]. 5. 31 ]. 29. 6. 31.

yalnızca artık daha az elemana erişim sağlama anlamına gelir: Copyright © 2009-2011 Ali Çehreli. Uzunluğun kısaltılması. 15] Dilimin uzunluğunun açıkça arttırılması da eleman paylaşımından ayrılmasına neden olur: ++çeyreği.stdio. 3. bu etki dilimlerin hepsi tarafından görülür: [1. 5. Bunu görmek için yukarıdaki programdaki çeyreği diliminin elemanını değiştirmeden önce ona yeni bir eleman ekleyelim: çeyreği ~= 42. int[] çeyreği = dilim[0 . 0] [1. 13. void main() { int[] dilim = [ 1. 15] Bu açıdan bakılınca dilimlerin elemanlara paylaşımlı olarak erişim sağladıkları söylenebilir. int[] yarısı = dilim[0 .org 75 . 42] [1. 7. 9. 9. 0. 5. Bu işlem sırasında o dilimin erişim sağlamakta olduğu bütün elemanlar otomatik olarak kopyalanırlar ve uzayan dilim artık bu yeni elemanlara erişim sağlamaya başlar. çeyreği 'nin elemanında yapılan değişikliğin dilim ve yarısı dilimlerini artık etkilemediği programın şimdiki çıktısında görülüyor: [1. 5. writeln(dilim). Örneğin aşağıdaki sekiz elemanın ilk ikisi üç dilim tarafından paylaşılmaktadır: import std. writeln(çeyreği). veya çeyreği. writeln(yarısı). $ / 4]. 7] [1. 13. Bu paylaşımı sonlandıran bir etki. çeyreği[1] = 0. } çeyreği diliminin ikinci elemanında yapılan değişiklik asıl elemanı değiştirdiği için. çeyreği[1] = 0. 3.. 7] [1.length += 5. 15 ]. 7.Başka Dizi Olanakları Aynı elemana aynı anda birden fazla dilimle erişilebilir. 5. 5. // paylaşımdan ayrılır // paylaşımdan ayrılır Öte yandan. http://ddili. $ / 2]. uzunluğu artan dilim paylaşımdan ayrılır. 0. 11. 11. bir dilimin uzunluğunun kısaltılması eleman paylaşımını sonlandırmaz. 9. 0. 7.. 11. 3. dilimlerden birisinin uzunluğunun artmasıdır. // uzunluğu arttığı için bu dilim // bu noktada paylaşımdan ayrılır // o yüzden bu işlem yalnızca kendi // elemanını etkiler Eklenen eleman dilimin uzunluğunu arttırdığı için dilim artık kopyalanan yeni elemanlara erişim sağlamaya başlar. 13.length.

double[3] b = [ 2. ilerideki derslerde karşılaşacağınız ^ . d = d[1 . vs. d. 20. *= . sonra ikinci elemanlar kendi aralarında. } writeln(sonuç). --d. Yukarıdaki programdaki + işleci yerine.1]. void main() { double[3] a = [ 10. daha önce gördüğünüz + .length. 34] O programdaki toplama işlemi. * . 23. int[] d = a. &= . import std. 111] Uzunluğun başka ifadeler yoluyla azaltılması da paylaşımı sonlandırmaz: d = d[0 .6 Bütün elemanlar üzerindeki işlemler Bu olanak hem sabit uzunluklu dizilerle hem de dilimlerle kullanılabilir. elemanların her birisiyle yapılması istenen işlemlerde büyük kolaylık sağlar. Not: Bu dersi yazdığım sırada kullandığım dmd 2. /= . 30 ]. ^= . double[3] sonuç = a[] + b[]. d[0] = 42. ve % aritmetik işleçlerini. Dizi isminden sonra yazılan içi boş [] karakterleri bütün elemanlar anlamına gelir. & .Başka Dizi Olanakları int[] a = [ 1. += . $ .length . 11.1. // sonundan kısaltmak // aynı şey // aynı şey 19.. -= . ve | ikili bit işleçlerini. ve |= . a ve b dizilerinin birbirlerine karşılık gelen elemanlarını ayrı ayrı toplar: önce ilk elemanlar kendi aralarında.051 bu işlemleri henüz dilimler için desteklemiyor. Eleman paylaşımı devam eder. ve bir dizinin önüne yazılan tekli . // başından kısaltıyoruz // elemanı dilim yoluyla değiştiriyoruz // diğer dilimi yazdırıyoruz Çıktısından görüldüğü gibi. http://ddili. Copyright © 2009-2011 Ali Çehreli. writeln(a). / .length = d.ve ~ işleçlerini kullanabilirsiniz.stdio. %= . 42. Bu olanak. O yüzden böyle işlemlerde kullanılan dizilerin uzunluklarının eşit olmaları şarttır. O yüzden bu başlık altındaki bazı örneklerde sabit uzunluklu diziler kullanmak zorunda kaldım. 111 ]. 3. yani paylaşım devam etmektedir: [1.. .org 76 . 4 ]. Çıktısı: [12. Bu işleçlerin atamalı olanları da kullanılabilir: = . d yoluyla yapılan değişiklik a 'nın eriştirdiği elemanı da etkilemiştir. $]..

dilim2). writeln(a). double[] dilim2 = [ 2.org 77 . 42. 20. Bu yüzden. dilim2 = dilim1.5] Bütün elemanlarını belirli bir değere eşitlemek için: a[] = 42. bir dizi yanında onun elemanlarıyla uyumlu olan bir ifade de kullanılabilir. 7. writeln("dilim3 önce : ". dilim3). // ← dilim1'in elemanlarına erişim // sağlamaya başlar // ← zaten erişim sağlamakta olduğu // elemanların değerleri değişir dilim2 'nin doğrudan atama işleciyle kullanılıyor olması. void main() { double[] dilim1 = [ 1. Sonuçta eleman değerlerinde bir fark görülmese bile aşağıdaki iki ifade aslında anlamsal açıdan çok farklıdır: dilim2 = dilim1. 30 ]. // ← erişimini dilim1'le paylaşmakta // olduğu eleman değişir // ← kendi elemanı değişir writeln("dilim1 sonra: ". Copyright © 2009-2011 Ali Çehreli.5. // ← dilim1'in elemanlarına erişim // sağlamaya başlar // ← zaten erişim sağlamakta olduğu // elemanların değerleri değişir writeln("dilim1 önce : ". dilim2[0] = 42. 1 ]. 5. writeln("dilim2 önce : ". double[] dilim3 = [ 3. 1. dilim1). Oysa dilim3[] ifadesi dilim3'ün bütün elemanları anlamını taşıdığı için. dilim1). 2 ]. Çıktısı: [2. 2. onun artık dilim1 'in elemanlarına erişim sağlamaya başlamasına neden olur. a[] /= 4. dilim3[0] = 43. writeln(a). onun bütün elemanlarının değerleri dilim1 'in elemanlarının değerlerini alırlar. dilim3[] = dilim1. unutulan bir [] işlecinin etkisi çok büyük olabilir.Başka Dizi Olanakları Bu olanak yalnızca iki diziyi ilgilendiren işlemler için değildir. Çıktısı: [42. Bunu aşağıdaki programda görebiliriz: import std. dilim3[] = dilim1. Örneğin bir dizinin bütün elemanlarını dörde bölmek için: double[3] a = [ 10. 3 ]. 3. http://ddili. 42] Bu olanağın dilimlerle kullanımında hataya açık bir durum vardır.stdio.

41. 12 22 32 42 ]. 12 ]. Şimdiye kadar gördüğümüz dizilerin elemanlarını hep soldan sağa doğru yazmıştık.7 Çok boyutlu diziler Şimdiye kadar gördüğümüz dizi işlemlerinde eleman türü olarak hep int ve double gibi temel türler kullandık. İki boyutlu dizi kavramını anlamayı kolaylaştırmak için bir diziyi bir kere de yukarıdan aşağıya doğru yazalım: int[] dizi = [ ]. [ 20. 32 ].. 1. Yukarıdaki dizi önceden olduğu gibi tek satırda da yazılabilirdi ve aynı anlama gelirdi. writeln("dilim3 sonra: ". 40. 21. örneğin diziler de kullanılabilir. 11. 31. [ 30. Kodun yasal olması için eleman türünü artık int olarak değil. 30. 31. 20. belki de o yüzden istenmeden paylaşılmaya başlanmış olan eleman değişene kadar farkedilememiş olmasıdır. http://ddili. */ dizi = [ ]. Şimdi o dizinin her bir elemanını int[] türünde bir değerle değiştirelim: /* . Böylece dizi dizisi gibi daha karmaşık topluluklar tanımlayabiliriz. Bu gibi tehlikeler yüzünden bu işlemleri dilimlerle kullanırken dikkatli olmak gerekir. dilim2 'de yapılan değişiklik dilim1 'i de etkilemiştir: dilim1 dilim2 dilim3 dilim1 dilim2 dilim3 önce : önce : önce : sonra: sonra: sonra: [1. 1] Buradaki tehlike. 1. 1. 1] [1. Elemanlarının türü dizi olan dizilere çok boyutlu dizi denir. ].Başka Dizi Olanakları } writeln("dilim2 sonra: ". 1. 10.org 78 . int[] olarak belirlememiz gerekir: int[][] dizi = [ [ 10. 1. 1] [42. dilim2). 22 ]. 11. Copyright © 2009-2011 Ali Çehreli. Eleman türü olarak aslında başka türler.. 1] [42. ]. [ [ [ [ 10. dilim2 atanırken [] işlecinin belki de unutulmuş olmasının etkisinin. 19. 20. dilim3). 21. ] Yaptığımız tek değişiklik. 1] [1. 1] [43. int yerine int[] türünde elemanlar yazmak oldu. 30. 40 Kodu güzelleştirmek için kullanılan boşlukların ve fazladan satırların derleyicinin gözünde etkisiz olduklarını biliyorsunuz. 1.

42]. // yeni bir eleman (yani dilim) ekler dizi[0] ~= 13.org 79 . 11. dilimlerde ise başka elemanlara erişim sağlanmasına neden olur • uzayan dilim paylaşımdan ayrılır ve yeni kopyalanmış olan elemanlara erişim sağlamaya başlar • dizi[] yazımı dizinin bütün elemanları anlamına gelir. Örneğin elinizde şu dizi varsa: double[] dizi = [ 1. Her bir elemanının int[] türünde olduğunu hatırlamak ve int[] türüne uyan işlemlerde kullanmak yeter: dizi ~= [ 50. [50. [30. dilimler. elemanlarının değerleri şuna dönüşsün: Copyright © 2009-2011 Ali Çehreli. 13]. kendilerine ait olmayan elemanlara erişim sağlarlar • [] işleci içindeyken dizi_ismi. [ 40. Elemanları int dizisi olan yukarıdaki dizinin kullanımı şimdiye kadar öğrendiklerimizden farklı değildir. http://ddili. Örneğin öyle bir binanın ikinci katının ilk odasında bulunan eşyaların sayısı şöyle arttırılabilir: // ikinci katın indeksi 1'dir ve o katın ilk odasına // [0][0] ile erişilir ++eşyaSayıları[1][0][0]. [20. [40. elemanların kopyalarından oluşan yeni bir dizi üretir • atama işlemi. 11 ]. 30. Öyle bir dizi. 32]. kendisine uygulanan işlem her bir elemanına ayrı ayrı uygulanır • elemanları dizi olan dizilere çok boyutlu dizi denir 19.Başka Dizi Olanakları ]. 3 satır. 22]. 41. 7. 31. 51]] Dizinin kendisi veya elemanları sabit uzunluklu da olabilir: int[2][3][4] dizi. örneğin bir macera oyununda ve her katında 2x3=6 oda bulunan 4 katlı bir bina ile ilgili bir kavram için kullanılıyor olabilir. 51 ]. 19.8 Özet • sabit uzunluklu dizilerin kendi elemanları vardır. 21. 4 düzlem Yukarıdaki tanımı iki sütunlu üç satırdan oluşan dört düzlem diye düşünebilirsiniz. sabit dizilerde eleman değerlerini değiştirir.9 Problem • Bir double dizisini başından sonuna doğru ilerleyin ve değerleri 10'dan büyük olanların değerlerini yarıya indirin. 2. 41. 12. // ilk elemanına (yani ilk dilimine) ekler Aynı dizinin şimdiki hali: [[10. // 2 sütun.dup niteliği.length yazmak yerine kısaca $ yazılabilir • . 42 ] Satır ve sütunlardan oluştukları için yukarıdaki gibi dizilere iki boyutlu dizi denir. 20.

7.Başka Dizi Olanakları [1. Ondan sonra o dilimi her seferinde baş tarafından tek eleman kısaltabilir ve dilimin hep ilk elemanını kullanabilirsiniz. 2.. İşe bütün diziye erişim sağlayan bir dilimle başlayabilirsiniz. çözüm Copyright © 2009-2011 Ali Çehreli.. Şu ifade dilimi başından tek eleman kısaltır: dilim = dilim[1 . .5] Çeşitli çözümleri olsa da.org 80 . $].. http://ddili. bunu yalnızca dilim olanakları ile başarmaya çalışın. 5. 15. 10.

. karakterler dersinde gördüğümüz gibi. Ancak.Dizgiler 20 Dizgiler "merhaba" gibi metin parçalarının dizgi olduklarını zaten öğrenmiş ve şimdiye kadarki kodlarda çok yerde kullanmıştık. Yazılan isimden sonra basılan Enter girişi sonlandırmaz. Bu yüzden readf çoğu durumda girişten dizgi okumaya uygun değildir. http://ddili. write("İsminiz nedir? "). D'de üç değişik karakter türü olduğu için. Bunun sonucunda da şimdiye kadar kullanmaya alıştığımız readf istediğimiz gibi işlemez: import std. girişten kaç karakter okunmak istendiği de bilinmediği için readf giriş tükenene kadar gelen bütün karakterleri dizginin içine okur. Dizgileri anlamaya yarayan iki olanağı da bundan önceki derslerde gördük: diziler ve karakterler. girdiğimiz bilgilerin sonunda bastığımız Enter tuşunu temsil eden kodlar da okunurlar ve dizginin parçası haline gelirler.stdio. Dahası. Windows ortamlarında da Ctrl-Z'ye basılır. Dizgiler o iki olanağın bileşiminden başka bir şey değildir: elemanlarının türü karakter olan dizilere dizgi denir. Girişi o şekilde sonlandırdığınızda Enter'lar nedeniyle oluşan satır sonu kodlarının bile dizginin parçası haline geldiklerini görürsünüz: Çok memnun oldum Mert ← isimden sonra "satır sonu" var ! ← (bir tane daha) İsimden hemen sonra yazdırılmak istenen ünlem işareti satır sonu kodlarından sonra belirmiştir. Dizgiler karakter dizileri oldukları için satır sonu anlamına gelen '\n' gibi kontrol karakterlerini de barındırabilirler.org 81 . ve readf dizgiye eklemek için karakter beklemeye devam eder: İsminiz nedir? Mert ← Enter'a basıldığı halde giriş sonlanmaz ← (bir kere daha basıldığını varsayalım) Konsolda girişi sonlandırmak için Linux ortamlarında Ctrl-D'ye. " %s" düzen dizgisini ve & işlecini gerektirmez: Copyright © 2009-2011 Ali Çehreli. isim. void main() { char[] isim. üç değişik dizgi türünden ve bunların bazen şaşırtıcı olabilecek etkileşimlerinden söz etmek gerekir. O yüzden.1 readf yerine readln ve chomp Konsoldan satır okuma ile ilgili bazı karışıklıklara burada değinmek istiyorum. Onun yerine ismi "satır oku" anlamındaki "read line"dan türemiş olan readln kullanılabilir.. &isim). readln 'ın kullanımı readf 'ten farklıdır. Örneğin char[] bir dizgi türüdür. "!"). readf(" %s". 20. } writeln("Çok memnun oldum ".

stdio. Dizgi sabitleri için ise çift tırnaklar kullanılır: 'a' karakterdir. http://ddili. O yazımı string türünü tanıttıktan sonra kullanmaya başlayacağım. } writeln("Çok memnun oldum ". isim. isim.string. Yukarıdaki chomp ifadesi isim 'in sonundaki satır sonu kodlarının silinmiş halini döndürür.org 82 . Buna rağmen satır sonunu belirleyen kodu o da barındırır: İsminiz nedir? Mert Çok memnun oldum Mert ! ← isimden sonra yine "satır sonu" var Dizgilerin sonundaki satır sonu kodları std. "!").Dizgiler import std. readln(isim). "a" tek karakterli bir dizgidir. Copyright © 2009-2011 Ali Çehreli. ve dchar[] . write("İsminiz nedir? "). 20. wchar[] . readln(isim). } writeln("Çok memnun oldum ". void main() { char[] isim. write("İsminiz nedir? "). wstring .3 string .2 Tek tırnak değil.string modülünde tanımlanmış olan chomp işlevi ile silinebilir: import std. 20. isim = chomp(isim). O halinin tekrar isim 'e atanması da isim 'i değiştirmiş olur: İsminiz nedir? Mert Çok memnun oldum Mert! ← "satır sonu" kodlarından arınmış olarak readln ve chomp zincirleme biçimde daha kısa olarak da yazılabilirler: string isim = chomp(readln()). çift tırnak Tek tırnakların karakter sabiti tanımlarken kullanıldıklarını görmüştük.stdio. ve dstring değişmezdirler D'de üç karakter türüne karşılık gelen üç farklı karakter dizisi türü vardır: char[] . "!"). void main() { char[] isim. import std.

Bu. writeln(tolower(ad_soyad. ve dstring . sağ tarafın bir dilimidir Bir önceki dersten hatırlayacağınız gibi. değişmez dizinin bir kopyasını almaktır. Bu durumda yapılması gereken. dizgi[0] = 'M'. 20. değiştirilemeyen string türüne dönüştürmek için de . değiştirilmesi istenen dizgilerin dizi yazımıyla yazılabileceklerini düşünebiliriz ama o da derlenemez.idup niteliğini kullanmak gerekir. örneğin string gereken yerde de char[] kullanılamaz. değişebilen bir dilim ile değişmez bir diziye erişilmesine izin vermemektedir.dup niteliğini kullanarak: import std. Bu bazen şaşırtıcı olabilir: writeln("aĞ".org 83 .Dizgiler Bu üç dizi türünün değişmez olanlarını göstermek için üç tane de takma isim vardır: string . wstring değişmezdir.idup)). // ← derleme HATASI O kod da derlenemez. "merhaba" gibi kodun içine hazır olarak yazılan dizgilerin türü string 'dir ve bu yüzden değişmezdirler 2. writeln(dizgi).dup. Sol tarafı dizi yazımıyla yazarsak: char[] bir_dilim = "merhaba".stdio. } Derlenir ve dizginin baş harfi değişir: Merhaba Benzer şekilde.4 string 'in şaşırtıcı olabilen uzunluğu Unicode karakterlerinin bazılarının birden fazla baytla gösterildiklerini ve Türk alfabesine özgü harflerin iki baytlık olduklarını görmüştük. değişmez[0] = 'M'. Bir önceki derste gördüğümüz .) Örneğin bir string 'in baş harfini büyütmeye çalışan şu kodda bir derleme hatası vardır: string değişmez = "merhaba". http://ddili.length). aşağıda gösterilecek olan std. (D'nin değişmezlik kavramının ayrıntılarını daha sonraki derslerde göreceğiz.string modülünün string alan işlevlerini çağırırken karşımıza çıkacak. Türü char[] olan sol taraf. wchar[] değişkendir. Değişebilen char[] türünü. Copyright © 2009-2011 Ali Çehreli. wstring . char[] değişebilir ve string değişmez olduğu için de burada bir uyumsuzluk oluşur: derleyici. Bir örnek olarak. // ← derleme HATASI Buna bakarak. Bunun iki nedeni vardır: 1. Bu takma isimler kullanılarak tanımlanan değişkenler değişmezdirler. char[] ad_soyad = ad ~ soyad. sol taraf sağ tarafı gösteren bir dilim olarak algılanır. void main() { char[] dizgi = "merhaba".

writeln("Sonra:". dizginin türü dchar[] olarak belirlenmiştir 2. d).dup. "aĞ"d hazır dizgisinin sonunda d belirteci kullanılmıştır (o belirtecin anlamı bir sonraki başlıkta açıklanıyor) 20.stdio. imlerle.length).5 Hazır dizgiler Hazır dizgilerin özellikle belirli bir karakter türünden olmasını sağlamak için sonlarına belirleyici karakterler eklenir: import std.org 84 .dup. void main() { string s = "aĞ"c. d[1] = 't'. "merhaba" şeklinde yazılan hazır dizgilerin eleman türünün char olmasıdır. writeln("Önce: ". writeln("Sonra:". ve diğer simgelerle ilgilendiğimiz durumlarda dchar türünü kullanmamız gerekir: dchar[] d = "aĞ"d. Önce: aĞ Sonra:at Doğru çalışan kodda iki değişiklik yapıldığına dikkat edin: 1. iki baytlık bir harfi tek baytlık bir harfle değiştirmeye çalıştığımızda karşımıza çıkar: char[] d = "aĞ". writeln("Önce: ". ancak 't' harfi tek bayttan oluştuğu için 'Ğ'yi oluşturan baytlardan ancak birincisinin yerine geçmiş ve ikinci bayt çıktıda belirsiz bir karaktere dönüşmüştür. d). Bunun görünür bir etkisi.length). writeln(w. Önce: aĞ Sonra:at� ← YANLIŞ O kodda dizginin 'Ğ' harfinin 't' harfi ile değiştirilmesi istenmiş. O yüzden. d). writeln(s. // bu.Dizgiler "aĞ" dizgisi 2 harf içerdiği halde dizinin uzunluğu 3'tür: 3 Bunun nedeni. o dizginin uzunluğu 3'tür (a için tek. d[1] = 't'. dstring d = "aĞ"d. Ğ için iki kod birimi). d). http://ddili. "aĞ" ile aynı şeydir Copyright © 2009-2011 Ali Çehreli. char da UTF-8 kod birimi olduğu için.) Unicode'un tanımladığı anlamda harflerle. bazı başka programlama dillerinin normal karakter türü olan char 'ı D'de bu amaçla kullanamayız. wstring w = "aĞ"w. (Aynı sakınca wchar 'da da vardır.

Copyright © 2009-2011 Ali Çehreli. 20. import std. write("İsminiz? ").string.string.stdio. void main() { char[] isim. Sıralamanın önemli olduğu durumlarda trileri gibi bir kütüphaneden yararlanmak gerekebilir. dizgi_1 = chomp(dizgi_1). Aşağıdaki işlevleri kullanırken bu konuda beklenmedik sonuçlarla karşılaşabilirsiniz. Benzer şekilde. Aynı işleçleri dizgilerle de kullanabiliriz. İsminiz? Can Merhaba Can! Hoşgeldin. import std.. readln(dizgi_1).org 85 . 3 2 2 a ve Ğ harflerinin her ikisi de wchar ve dchar türlerinden tek bir elemana sığabildikleri için. vs. // Sonuna ekleme örneği: selam ~= "! Hoşgeldin. >= .length). bir dizginin sonuna başka dizgi eklemek için de ~= işleci kullanılır: import std. işleçlerini görmüştük. // Birleştirme örneği: char[] selam = "Merhaba " ~ isim. dizi işlemleri onlar için de geçerlidir. Daha önce sayıların küçüklük büyüklük karşılaştırmalarında kullanılan < . isim = chomp(isim). http://ddili.7 Dizgileri karşılaştırmak Not: Unicode bütün yazı sistemlerindeki harfleri tanımlasa da onların nasıl sıralanacaklarını belirlemez. } writeln(selam). son iki dizginin uzunlukları 2 olmaktadır. char[] dizgi_1. İki dizgiyi birleştirmek için ~ işleci.stdio. 20. büyüklük de alfabetik sırada sonra demektir: import std.Dizgiler } writeln(d..". Bu işleçlerin küçüklük kavramı dizgilerde alfabetik sırada önce anlamındadır. readln(isim).6 Dizgi birleştirmek Dizgiler aslında dizi olduklarından.. void main() { write(" Bir dizgi giriniz: ")..

char[] sonra_olan. "aT" dizgisi. önce_olan. ASCII tablosundaki kodlarının bir yansıması olarak. char[] dizgi_2.9 std. } else { char[] önce_olan. } } 20. diziler için yararlı işlevler içeren std.string modülü std. bulamazsa -1 değerini döndürür.string modülü dizgilerle ilgili işlevler içerir. Ek olarak. } writeln("Sıralamada önce '".array ve std.8 Büyük küçük harfler farklıdır Harflerin büyük ve küçük hallerinin farklı karakter kodlarına sahip olmaları onların birbirlerinden farklı oldukları gerçeğini de getirir.").org 86 . dizgi_2 = chomp(dizgi_2). sondan başa doğru aramasıdır • countchars : Birinci dizgi içinde ikinci dizgiden kaç tane bulunduğunu sayar • tolower : Verilen dizginin. Oradaki işlevler arasından bir kaç tanesi: • indexOf : Verilen karakteri bir dizgi içinde baştan sona doğru arar ve bulursa bulduğu yerin indeksini. sonra_olan. Örneğin 'A' ile 'a' farklı harflerdir.Dizgiler write("Bir dizgi daha giriniz: "). 20. "'. Farkı. 'T' harfi 'ç'den önce olduğu için "aç" dizgisinden önce sıralanır. sıralamada 'a'dan önce gelir. bütün harfleri küçük olan eşdeğerini döndürür • toupper : tolower 'a benzer şekilde çalışır. büyük harflerin hepsi. küçük büyük harf ayrımı olmadan aranmasını sağlar • lastIndexOf : indexOf 'a benzer şekilde çalışır. aşağıda gösterilecek olan icmp işlevini kullanmak gerekir. sonra '". Böyle durumlarda yukarıda gösterilen aritmetik işleçler yerine. sonra_olan = dizgi_2. } else { önce_olan = dizgi_2. if (dizgi_1 == dizgi_2) { writeln("İkisi aynı!"). Örneğin büyük olduğu için 'B'. Seçime bağlı olarak bildirilebilen üçüncü parametre. sonra_olan = dizgi_1. Copyright © 2009-2011 Ali Çehreli. Bazen dizgileri harflerin küçük veya büyük olmalarına bakmaksızın karşılaştırmak isteriz. sıralamada küçük harflerin hepsinden önce gelir. http://ddili.string sayfasında bulabilirsiniz. Aynı şekilde.algorithm modüllerindeki işlevler de dizgilerle kullanılabilir. Bu işlevlerin tam listesini Ddili Wiki'nin std. readln(dizgi_2). if (dizgi_1 < dizgi_2) { önce_olan = dizgi_1. "' gelir. büyük harf kullanmasıdır • strip : Dizginin başındaki ve sonundaki boşlukları siler • insert : Dizginin içine başka dizgi yerleştirir Dizgiler de aslında dizi olduklarından. Farkı.

program önce bu iki kelimeyi aralarında boşluk olacak şekilde birleştirsin ve sonra baş harflerini büyütsün.10 Problemler 1. . Satırın içindeki ilk 'a' harfinden.org 87 . 2. 'a'). bütünüyle küçük harflerden oluşan ad ve soyad girsin. capitalize .. İlk 'a' harfini bulmak için şöyle bir satır kullanabilirsiniz: sizediff_t ilk_a = indexOf(satır. ve capwords işlevlerinin üçünü de deneyin ve farklılıklarını görün. 3. 'a'). sizediff_t 'dir. Örneğin "ebru" ve "domates" girildiğinde programın çıktısı "Ebru Domates" olsun.Dizgiler 20. Kullanıcıdan bir satır alın. indexOf ve lastIndexOf işlevlerinin dönüş türleri int değil. satırın içindeki son 'a' harfine kadar olan bölümü yazdırsın. http://ddili.. çözümler Copyright © 2009-2011 Ali Çehreli. std.string modülünün belgesine hızlıca da olsa bir göz atın. Bu programda indexOf ve lastIndexOf işlevlerini kullanarak iki değişik indeks bulmanız. ~ işlecini de kullanan bir program yazın: kullanıcı. Örneğin kullanıcı "balıkadam" dizgisini girdiğinde ekrana "alıkada" yazdırılsın. Bir kaç ders sonra göreceğimiz auto anahtar sözcüğü ile daha da kısa olabilir: auto ilk_a = indexOf(satır. ve bu indekslerle bir dilim oluşturmanız işe yarayabilir. Bu programda çalışırken toupper .

bütün modern uç birimlerin hepsinde bulunan ve programlama dilinden bağımsız bir olanaktır. giriş de < karakteriyle bir dosyaya bağlanabilir. Programınız ekran yerine bir dosyaya yazabilir.org 88 . 21. programcılık hayatınızda çok işinize yarayacak başka bir bilgiyi bu bölümde vermek istiyorum: programınızın standart giriş ve çıkışını. Dosya akımlarına geçmeden önce.Standart Akımları Dosyalara Bağlamak 21 Standart Akımları Dosyalara Bağlamak Önceki bölümlerdeki programlar hep standart giriş ve çıkış akımları ile etkileşiyorlardı. } writeln(sayı * 2).stdio.txt ismindeki bir dosyaya yazıldığını görürsünüz. programın standart çıkışına yazdığı herşey o dosyaya yazılır. Bu sefer de elimizde girişinden aldığı sayının onda birini hesaplayan bir program olsun: Copyright © 2009-2011 Ali Çehreli. D'nin standart akımlarının stdin ve stdout olduklarını görmüştük./iki_kat > iki_kat_sonucu. void main() { double sayı. programın çıktısı olan 2. Not: Bu program baştan "Lütfen bir sayı giriniz: " gibi bir mesaj yazmadığı halde. Standart girişinden bir sayı alan. standart girişin hep klavye olduğu. ve standart çıkışın da hep ekran olduğu durumlarda çalışmıştık. sizin kodunuzda hiçbir değişiklik gerekmeden dosyalara bağlayabilirsiniz.2 yazarsanız. &sayı). programı çalıştırmak için yazdığınız komutun sonuna > karakterinden sonra bir dosya ismi yazmanız. siz yine de sayıyı klavyeden yazıp Enter'a basmalısınız. ve bu yüzden standart giriş ve çıkışla etkileşmekten bir farkları olmadıklarını göreceksiniz. iki_kat_sonucu. Bundan sonraki bölümde programları dosyalarla etkileşecek şekilde yazmayı öğreneceğiz. o sayıyı 2 ile çarpan. Dosyaların da karakter akımı olduklarını. ve klavye yerine bir dosyadan veya bir programdan okuyabilir.2 Standart girişi < ile bir dosyaya bağlamak Çıkışın > karakteriyle bir dosyaya bağlanmasına benzer şekilde. readf(" %s". 21. ve açıkça akım bildirmeden çağrılan writeln gibi işlevlerin de arka planda bu akımları kullandıklarını öğrenmiştik. http://ddili.1 Standart çıkışı > ile bir dosyaya bağlamak Programınızı bir uç birimden başlatıyorsanız. Bu durumda da girişinden bilgi bekleyen bir program klavyeden okumak yerine. programın standart çıkış akımının o dosyaya bağlanması için yeterlidir. programı komut satırından .txt şeklinde başlatır ve girişine örneğin 1. Bu.4'ün ekrana değil. Bu durumda. belirtilen dosyadan okur. O programın isminin iki_kat olduğunu varsayarsak. ve sonucu standart çıkışına yazdıran bir program düşünelim: import std. Ek olarak.

ve onda_bir programı da ihtiyaç duyduğu sayıyı iki_kat_sonucu./onda_bir < iki_kat_sonucu. } writeln(sayı / 10).txt Bu sefer giriş iki_kat_sonucu. programı komut satırından . solundaki programın standart çıkışını sağındaki programın standart girişine bağlar.24 yazdırdığını görürsünüz.txt dosyasının iki program arasında aracılık yaptığına dikkat edin: iki_kat programı. ve çıkış da butun_sonuc.5 Problem • İkiden fazla programı art arda bağlamayı deneyin: Copyright © 2009-2011 Ali Çehreli./onda_bir < iki_kat_sonucu. ihtiyacı olan sayıyı artık klavyeden değil. ve bu programın isminin de onda_bir olduğunu varsayarsak. readf(" %s". siz yine de sayıyı klavyeden yazıp Enter'a basmalısınız.Standart Akımları Dosyalara Bağlamak import std. toplu olarak "beşte bir" hesaplayan bir komut haline dönüşür: . girişini daha önce oluşturulan iki_kat_sonucu. Not: iki_kat_sonucu. | karakteri. Örneğin komut satırında birbirine şu şekilde bağlanan iki program. bir dosyadan okumaktadır. iki_kat programının çıkışı onda_bir programının girişine verilir ve iki katı alınmış olan sayının onda biri.4 olduğunu varsayıyorum. http://ddili. | karakteri.3 Giriş ve çıkış akımlarının ikisini birden dosyalara bağlamak > ve < karakterlerini aynı anda kullanabilirsiniz: .txt > butun_sonuc. 21. onda_bir programı.stdio. &sayı).txt dosyasından okumaktadır.txt dosyasına yazılır.org 89 .txt şeklinde başlatırsanız. Sonra. Not: O programın "Lütfen bir sayı giriniz: " gibi bir mesaj yazmadığını hatırlayın.txt dosyasından okunur. 21.4 Programları | ile birbirlerine bağlamak Yukarıda kullanılan iki_kat_sonucu.txt dosyasında 2. void main() { double sayı. 21./onda_bir Önce iki_kat programı çalışır ve girişinden bir sayı alır. Eğer iki kat alan programın oluşturduğu dosya hâlâ klasörde duruyorsa. yani baştaki sayının "beşte biri" çıkışa yazılır.txt dosyasına yazmaktadır. programları böyle bir aracı dosyaya gerek olmadan birbirlerine bağlar./iki_kat | . hesapladığı sonucu iki_kat_sonucu.txt dosyasından aldığını ve çıkışa 0.

/birinci | ./ucuncu .Standart Akımları Dosyalara Bağlamak .. http://ddili. çözüm Copyright © 2009-2011 Ali Çehreli.org 90 ./ikinci | ..

erişim haklarından temelde iki tanesi ile ilgilidir: okuma ve yazma erişimi. Bu bölümde işletim sisteminin klasörlerde barındırdığı dosyalara yazmayı ve dosyalardan okumayı öğreneceğiz.1. http://ddili. böyle bir programın kayıtlarını tuttuğu öğrenci bilgilerini yazmak için bir dosyaya ihtiyacı olacaktır. Örneklere geçmeden önce dosyalarla ilgili genel kavramları tanıtacağım.1. başka ortamlarda oluşturulmuş dosyaların da bu bölümdeki bilgilerle açılabileceklerini beklemeyin. örneğin programınızın ayarlarının ve verilerinin saklandığı dosyalar • BOM'suz UTF-8 okuyabilen bir ortama gönderilen UTF-8 dosyaları. 22. Öte yandan. Copyright © 2009-2011 Ali Çehreli. o dosyanın başka bir ortamda açılıp okunması için yeterli olmayabilir. Erişim hakları hem performans. standart çıkışını kullanıcıya bir komut menüsü göstermek için kullanıyor olabilir. Standart girişini de kullanıcıdan komut almak için kullandığını düşünürsek. konu dosyaya yazmak olunca. Bu bölümde göreceğiniz File 'ın olanaklarını. ve | her duruma uygun değildir.2 Dosya erişim hakları İşletim sistemi. hem de dosya sağlığı açısından önemlidir. bu bölümdeki olanakları kullanabilirsiniz • başka bir ortamdan gelen BOM'suz UTF-8 dosyaları. < . yoksa iki programın birbirlerinden habersiz olarak yazmaları sonucunda dosyanın içeriği tutarsız hale gelebilir. 22. Benzer şekilde. bu bölümdeki olanakları kullanarak bu tür dosyaları doğru olarak açabilirsiniz 22. dosyaları programlara çeşitli erişim haklarıyla sunar. Örneğin öğrenci kayıtları ile ilgilenen bir program.stdio modülünde tanımlanmış olan File yapısı kullanılır. Henüz yapıları göstermediğim için File nesnelerinin kurulma söz dizimi konusunda bazı kabuller yapmanızı bekleyeceğim. Konu dosyadan okumak olunca.Dosyalar 22 Dosyalar Ne kadar güçlü olsalar da. aynı dosyadan okumak isteyen birden fazla programa aynı anda okuma izni verilmesi. Çünkü her program işini yalnızca standart giriş ve çıkışla etkileşerek yapamaz.1 Temel kavramlar Dosya işlemleri için yine std. önceki bölümde gördüğümüz > . programlar birbirlerini beklemeyecekleri için hız kazancı sağlar.1 Karşı taraf Bu bölümdeki bilgilerle oluşturulan dosyaların her ortamda rahatça okunabileceklerini düşünmeyin. Dosyayı yazan tarafla okuyan tarafın belirli konularda anlaşmış olmaları gerekir. Bir dosya oluşturup içine bilgiler yazmak. eğer karşı taraf BOM'suz UTF-8 dosyaları doğru olarak açabiliyorsa. Örneğin dosyaya char[] olarak yazılmış olan bir bilginin wchar[] olarak okunması yanlış sonuç doğurur. şu durumlarda kullanışlı olacaklarını düşünerek öğrenin: • yalnızca programınız tarafından yazılacak olan ve yine programınız tarafından okunacak olan dosyalar. dosyanın içeriğinin tutarlılığı açısından dosyaya belirli bir anda ancak tek bir programın yazmasına izin verilmelidir. D dosyaları.org 91 .

.1.3 Dosya açmak Programın standart giriş ve çıkış akımları olan stdin ve stdout .file modülü Klasör işlemleri ile ilgili olan std. 22. Örneğin başlangıçta yazma erişimi ile açılarak içine bilgi yazılmış olan bir dosya. Örneğin exists .eof()) { /* .writeln("merhaba"). Böyle bir durumda. program başladığında zaten açılmış ve kullanıma hazır olarak gelirler.5 Dosyaya yazmak ve dosyadan okumak Dosyalar da karakter akımları oldukları için.. // yukarıdakinin uzun yazımıdır dosya. dosyayı önce kapatmak. } // ← dosya bu kapsamdan çıkılırken otomatik olarak kapatılır Bazen.org 92 .1. Örnek: if (bir_koşul) { // dosya burada oluşturulmuş ve kullanılmış olsun // .7 Klasör işlemleri için std. http://ddili. Bir dosyanın sonuna gelene kadar işlem yapmak için şöyle bir döngü yazılabilir: while (!dosya. 22.4 Dosya kapatmak Açılan dosyaların mutlaka kapatılmaları da gerekir.1. Bu işlev. File nesneleri kendileri sonlanırken dosyalarını da kapattıkları için. Farklı olarak yapmanız gereken şey. // standart çıkışa yazar stdout. normalde bu işin programda açıkça yapılması gerekmez.Dosyalar 22. onları kullanmaya başlamadan önce özel bir işlem yapmamız gerekmez. yine aynı kapsam içinde. aynı dosyanın aynı kapsam içinde ama değişik erişim haklarıyla açılması gerekebilir. belirtilen isimde bir dosyanın klasörde zaten bulunup bulunmadığını bildirir: if (exists(dosya_ismi)) { // dosya mevcut Copyright © 2009-2011 Ali Çehreli.1. dosya nesnesinin ismini ve nokta işlecini de kullanmaktır: writeln("merhaba").file modülünün Ddili Wiki'deki sayfasında işinize yarayacak işlevler bulabilirsiniz.1. Dosya.. File nesnesinin içinde bulunduğu kapsamdan çıkılırken kendiliğinden kapatılır. dosya sonuna gelindiğinde true döndürür. // dosyaya yazar 22. ancak. "dosya sonu" anlamına gelen "end of file"ın kısaltması olan eof() üye işleviyle denetlenir.writeln("merhaba"). sonra yeni erişim hakkıyla tekrar açmak gerekir. ama bu sefer okuma erişimi ile açılabilir. alışık olduğunuz writeln ve readf gibi işlevleri onlarla da kullanabilirsiniz.. Dosyaların ise diskteki isimleri ve istenen erişim hakları bildirilerek program tarafından açılmaları gerekir.6 Dosya sonu: eof() Bir dosyadan okurken dosyanın sonuna gelinip gelinmediği. */ } 22.

dosyaya yazma ve dosyadan okuma işlemleri sırasında karakter dönüşümleri yapılmaz. C dilindeki standart fopen işlevinin kullandığı erişim belirteçlerini kullanır: Belirteç r r+ w Anlamı okuma erişimi dosya. 22. başından okunacak ve başına yazılacak şekilde hazırlanır yazma erişimi dosya yoksa: boş olarak oluşturulur dosya zaten varsa: içi boşaltılır okuma ve yazma erişimi dosya yoksa: boş olarak oluşturulur dosya zaten varsa: içi boşaltılır sonuna yazma erişimi dosya yoksa: boş olarak oluşturulur dosya zaten varsa: içeriği korunur ve sonuna yazılacak şekilde hazırlanır okuma ve sonuna yazma erişimi dosya yoksa: boş olarak oluşturulur dosya zaten varsa: içeriği korunur.stdio. başından okunacak ve sonuna yazılacak şekilde hazırlanır w+ a a+ Yukarıdaki erişim haklarının "rb" gibi sonuna 'b' karakteri gelenleri de vardır.writeln("İsim : ".writeln("Sınıf : ". http://ddili.File yapısı File .stdio. dosya.writeln("Numara: ". Bazı özel kodların değişik ortamlardaki alt düzey gösterimleriyle ilgili olan bu konuyu bu derste anlatmayacağım. "w"). "Zafer").2 std. "1A").2. 123). dosya ismini ve erişim hakkını string türü olarak kabul eder.org 93 . dosya.stdio wiki sayfasında bulabilirsiniz. O durumda.idup niteliğini çağırmanız gerekir. Copyright © 2009-2011 Ali Çehreli. File 'ı örneğin char[] türünde bir dizgi ile kuramazsınız. } Dizgiler dersinden hatırlayacağınız gibi. Bu yüzden.1 Yazma örneği import std. void main() { File dosya = File("ogrenci_bilgisi". Yani File . Bu bilgiyi problemlerden birisinde hatırlamanız gerekecek. Bu yapının belgesini std. "ogrenci_bilgisi" gibi bir dizginin türü string 'dir ve değişmezdir. yine dizgiler bölümünden hatırlayacağınız gibi. başından okunacak şekilde hazırlanır okuma ve yazma erişimi dosya.Dosyalar } else { // dosya mevcut değil } 22. kurmanız gerektiğinde o dizginin . dosya.

o dosyanın içindeki boş olmayan bütün satırları. 22. void main() { File dosya = File("ogrenci_bilgisi". dosya ismini string türünde alır.txt ise.idup ile alınmış bir kopyasını kullanmanız gerekecektir ◦ Satırın boş olup olmadığını . dosyanın ismine .2. satır).eof()) { string satır = chomp(dosya..stdio.bak dosyasına yazsın. } } Yukarıdaki program.Dosyalar Yukarıdaki program. writeln("Okuduğum satır -> |". while (!dosya.txt. Ben bu derslerde dosya isimlerinde yalnızca ASCII harfler kullanacağım.2 Okuma örneği import std. çalıştırıldığı klasör içinde ismi ogrenci_bilgisi olan bir dosya oluşturur. 22. İşinize yarayacak bilgiler: ◦ std.bak eklenmiş başka bir dosyaya yazsın. çözüm Copyright © 2009-2011 Ali Çehreli. http://ddili. boş olmayan satırlarını deneme. "r"). çalıştırıldığı klasör içindeki ogrenci_bilgisi isimli dosyanın içindeki satırları başından sonuna kadar okur ve standart çıkışa yazdırır. dosya ismi olarak onun . Örneğin.string. Not: Dosya ismi olarak dosya sisteminin izin verdiği her karakteri kullanabilirsiniz.org 94 .stdio modülünün belgelerinde görüldüğü gibi. Eğer elinizde değişebilen türde bir dizgi varsa..3 Problem • Yazacağınız program kullanıcıdan bir dosya ismi alsın. File'ın kurucu işlevi. verilen dosyanın ismi deneme. import std.readln()).length niteliğinin değerine bakarak anlayabilirsiniz . örneğin bir char[] varsa.

hazır değerin. türünü üretir.stdio. sol tarafın türünün sağ taraftan anlaşılabildiği durumlarda sol tarafın yazımını kolaylaştırmak için kullanılır: auto dosya = std. D'nin auto anahtar sözcüğü. Buna rağmen türün otomatik olarak anlaşılması kavramı ile ilgili değildir.) 23. Zaten immutable yazılmış olduğu için türün değişmez bir int olduğu o yazımdan da otomatik olarak anlaşılır. Böyle durumlarda hangi modüldeki ismin kastedildiğini belirtmek için modülün ismini de yazmak gerekir.File dosya = std.File kurularak oluşturulan bir nesnenin yine std.2.stdio.auto ve typeof 23 23. Örneğin birbirlerinden farklı iki kütüphanenin iki modülünde de File isminde bir tür bulunabilir.1 auto ve typeof auto Bazen aynı ismin iki veya daha fazla modülde birden tanımlı olduğu durumlarla karşılaşılabilir. Örneğin zaten tanımlanmış olan int türünde sayı isminde bir değişken olduğunu varsayarsak: int sayı = 100. Kendisine verilen değişkenin. otomatik anlamına gelen "automatic"in kısaltmasıdır. http://ddili. nesnenin. (immutable anahtar sözcüğünü daha sonra göreceğiz. tanım sırasında başka belirteç bulunmadığı zaman kullanılır.File("ogrenci_bilgisi". // bu zaten 'int' olarak tanımlanmış Copyright © 2009-2011 Ali Çehreli. Oysa derleyiciler çoğu durumda sol tarafın türünü sağ tarafın türüne bakarak anlayabilirler. "r"). vida = BisikletVitesDüzeneğininAyarVidası(10). O ismi tanımlayan iki modülün birden eklenmesi durumunda da yalnızca File yazmak karışıklığa neden olur. Aslında değişkenlerin yaşam süreçleri ile ilgili olan auto .stdio. veya std. O kullanımda uzun ismin hem de iki kere yazılması gerekmiştir: sol tarafta dosya nesnesinin türünü belirtmek için. sağ tarafta ise File nesnesini kurmak için.File("ogrenci_bilgisi". selam = "Merhaba".stdio.stdio. auto 'yu her türle kullanabilirsiniz: auto auto auto auto sayı = 42. "türü" anlamına gelen "type of" deyiminden türemiştir. derleyici hangi türün kullanılacağını bilemez. Başka belirteçler de türün otomatik olarak anlaşılması için yeterlidir: immutable i = 42.2 typeof Bu anahtar sözcük. vs. Örneğin 42 gibi bir tamsayı değerle ilklenen bir değişkenin int olduğu. "r"). "auto". kesirliSayı = 1. Örneğin File türü ile ilgili böyle bir isim çakışması olduğunu varsayarsak: std.org 95 .File türünden olduğu kolayca anlaşılabilir.

şu ikisinin eşdeğeridir: int sayı2. (Yani short . Türlerin zaten bilindiği yukarıdaki gibi durumlarda typeof 'un kullanılmasına gerek olmadığı açıktır. yoksa real mi? Yeni öğrendiğiniz typeof ve temel türler dersinde öğrendiğiniz .auto ve typeof typeof(sayı) sayı2. vs.stringof işinize yarayabilir. 23. double mı.3 Problem • 42 gibi bir hazır değerin D'nin tamsayı türlerinden int türünde olduğunu yukarıda okudunuz.) Bir program yazarak 1. // "sayı'nın türü" anlamında // "100 hazır değerinin türü" anlamında Yukarıdaki son iki ifade. değil.. çözüm Copyright © 2009-2011 Ali Çehreli.. int sayı3. ve bu dersin probleminde yararlı olacak. typeof(100) sayı3.2 gibi bir hazır değerin türünün D'nin kesirli sayı türlerinden hangisinden olduğunu bulun: float mu. http://ddili.org 96 . Bu anahtar sözcük daha sonra anlatılacak olan şablonlar konusunda. . long .

o kapsamın dışında geçersizdir. if (birKoşul) { int uzunluk = asalSayılar. if (birKoşul) { int içSayı = 1. tanımlandığı noktadan başlayarak hem içinde tanımlandığı kapsamda. } // ← yeni bir kapsam başlatır // ← çalışır. http://ddili. double ortalamaDeğer. Kaç satır önce tanımlanacağı programcıya bağlı olsa da. readf(" %s".org 97 . kullanılan bütün isimleri kapsamların en başında tanımlamaya alışmışlardır: int adet. write("Kaç sayı gireceksiniz? ").1 İsimleri kullanıldıkları ilk noktada tanımlamak Şimdiye kadarki örneklerde de gördüğünüz gibi. &adet). dışSayı hem dışarıdaki hem de içerideki kapsamda geçerlidir. Öte yandan. Özellikle C dilinden gelen programcılar. her ismin kullanıldığı ilk noktaya en yakın yerde tanımlanması programcılık açısından daha iyi kabul edilir. // ← derleme HATASI. Bunu kullanıcıdan aldığı sayıların ortalamalarını yazdıran bir programın main işlevinde görelim. } içSayı = 3.length. Her kapsam bir isim alanı tanımlar. dışSayı = 2. içeride de geçerlidir // ← içSayı'nın geçerliliği burada son bulur // ← derleme HATASI // içSayı'nın geçerli olduğu kapsamdan // çıkılmıştır if döngüsünün kapsamı içinde tanımlanan içSayı .İsim Alanı 24 İsim Alanı D'de her isim. hem de o kapsamın içindeki kapsamlarda geçerlidir. Bir kapsamda tanımlanmış bir ismin içerdeki bir kapsamda tekrar tanımlanması yasal değildir: int uzunluk = tekSayılar. 24. isimlerin kullanıldıkları ilk noktadan daha önce tanımlanmış olmaları gerekir: writeln(sayı). İçinde tanımlandığı kapsamdan çıkıldığında. sayı henüz bilinmiyor O kodun çalışabilmesi için sayı 'nın writeln işleminden daha önce tanımlanmış olması gerekir.length. int sayı = 42. isim artık geçersiz hale gelir ve derleyici tarafından tanınmaz: void main() { int dışSayı. int[] sayılar. } // derleme hatası Not: C dilinden beri yasal olan bu kullanım D'de artık emekliye ayrılmaktadır. // ← BURADA // ← BURADA // ← BURADA Copyright © 2009-2011 Ali Çehreli.

} else { writeln("HATA: En az bir sayı girmelisiniz!"). vs. değişkenlerin tanımlarını görmek veya hatırlamak için sık sık metnin üst tarafına gitmek ve tekrar geri gelmek gerekebilir • Kod değişikliği: Program kodları sürekli olarak gelişim halindedirler: programa ekler yapılır. Copyright © 2009-2011 Ali Çehreli. D'de bütün değişkenler ilklendikleri için.length = adet. o kod satırlarında kullanılan bütün değişkenlerin kullanıldıkları ilk yerde tanımlanmış olmaları. isimleri olabildiğince geç tanımlamak önerilir. taşınacak olan kod satırlarında kullanılan değişkenlerin de teker teker seçilerek ayrı ayrı taşınmaları gerekir. sayılar. Aynı programı bu tavsiyeye uyarak şöyle yazabiliriz: write("Kaç sayı gireceksiniz? ").. } Bütün değişkenleri bir arada en başta tanımlamak yapısal olarak daha iyi olsa da. } Bunun karşıtı olarak.. belki de hiç kullanılmayacak olan değişkenleri en baştan ilklemek. readf(" %s". &adet).. // ← BURADA // . Oysa değişkenlerini C'deki gibi tanımlayan bir programda. Örneğin yukarıdaki bu tavsiyeye uyan programın if kapsamındaki bütün satırlar hep birden programın başka bir noktasına taşınabilirler. // ← BURADA // ← BURADA double ortalamaDeğer. } else { writeln("HATA: En az bir sayı girmelisiniz!"). uzunluk gibi genel bir ismi olan bir değişken aradaki satırlarda yanlışlıkla başka bir uzunluk kavramı için kullanılmış... Bu işlemler sırasında çoğu zaman bir grup satırın hep birden başka bir işlev olarak tanımlanması istenebilir. o işlem için geçen zamanın boşa gitmesine neden olabilir • Hata riski: Değişkenlerin tanımları ile kullanımları arasına giren her satır. alttaki satırlarda kullanılan bir değişkenin tanımının programın yazıldığı ekranın dışında kalma olasılığı artar. programın bazı olanakları silinir.. // . if (adet >= 1) { int[] sayılar. farkedilen hataları giderilir. burada asıl işlemler yapılıyor olsun. değişkenleri geç tanımlamanın da bir kaç önemli yararı vardır: • Hız: her değişken tanımının program hızı açısından bir bedeli vardır. http://ddili. burada asıl işlemler yapılıyor olsun.org 98 . program hataları açısından ufak da olsa bir risk taşır: bir örnek olarak. ve asıl kullanılacağı yere gelindiğinde değeri çoktan değişmiş olabilir • Okuma kolaylığı: Kapsamdaki satırlar çoğaldıkça. hepsinin birden başka bir yere taşınmalarına olanak sağlar. int adet.length = adet.. Böyle durumlarda..İsim Alanı if (adet >= 1) { sayılar.

while (sayı < 11) { writeln(sayı). Örneğin 1'den 10'a kadar olan bütün tamsayıları yazdıran bir döngü "sayı 11'den küçük olduğu sürece" şeklinde kodlanabilir: while (sayı < 11) O döngünün ilerletilmesi. Bu işlemlerin üçü de for deyiminin parantezi içinde. ve ilerletilmesi olarak açıklayabiliriz: int sayı = 1.2 for 'un bölümleri for döngüsü bu dört işlemden üçünü tek bir tanıma indirgeyen deyimdir. http://ddili. Yararı. Çoğu duruma daha uygun olduğu için programlarda while 'dan daha çok kullanılır.1 while 'ın bölümleri Hatırlarsak. bütün bölümlerine değinmiş oluruz: writeln(sayı). Asıl işlemler ise kapsam içindedir: Copyright © 2009-2011 Ali Çehreli. Bu dört işlemi döngünün hazırlığı.. döngü ile ilgili bütün tanımların tek satırda yapılmasıdır. 25. } // ← hazırlık // ← devam koşulu // ← asıl işlemler // ← döngünün ilerletilmesi while döngüsü sırasında bu bölümler şu sırada işletilirler: hazırlık koşul denetimi asıl işlemler ilerletilmesi koşul denetimi asıl işlemler ilerletilmesi .org 99 . Kodun derlenebilmesi için sayı 'nın while 'dan önce tanımlanmış olması gerekir: int sayı = 1. ve aralarında noktalı virgül olacak şekilde yazılırlar. asıl işlemleri. ++sayı. devam etme koşulunun denetimi. sayı 'nın döngü içinde bir arttırılması ile sağlanabilir: ++sayı.for Döngüsü 25 for Döngüsü while döngüsü ile aynı işe yarar. 25.. while döngüsü tek bir koşul denetler ve o koşul doğru olduğu sürece döngüye devam eder. Döngünün asıl işlemlerini de sayarsak.

} 25. } Bu. döngüyü ilerletmek için bir tamsayı kullanılır. özellikle döngü kapsamının kalabalık olduğu durumlarda çok yararlıdır: döngüyü ilerleten işlem. /* ilerletilmesi */) { /* asıl işlemler */ } Yukarıdaki while döngüsü for ile yazıldığında çok daha düzenli bir hale gelir: for (int sayı = 1. bu bölümler boş bırakılabilir: • Bazen hazırlık için bir değişken tanımlamak gerekmez çünkü zaten tanımlanmış olan bir değişken kullanılacaktır • Bazen döngüyü sonlandırmak için döngü koşulu yerine döngü içindeki break satırlarından yararlanılır • Bazen döngüyü ilerletme adımı belirli koşullara bağlı olarak döngü içinde yapılabilir Bütün bölümler boş bırakıldığında. ++i) { // . } Öyle bir döngü. . sayı /= 2) { writeln(sayı).org 100 . Örneğin belirli bir değer aralığındaki kesirli sayıların sürekli olarak yarılarını gösteren bir döngü şöyle yazılabilir: for (double sayı = 1. for döngüsünün bölümleri de while 'ın bölümleriyle aynı sırada işletilirler. break ve continue deyimleri for döngüsünde de aynı şekilde çalışırlar.. ++sayı) { writeln(sayı). // ← derleme HATASI // i burada geçerli değildir Copyright © 2009-2011 Ali Çehreli. dışarıdaki kapsamda değil: for (int i = 0. yalnızca döngü içindeki kapsamda geçerlidir (ve onun içindekilerde). 25.. } writeln(i).001. örneğin ya hiç çıkılmayacak şekilde. sayı > 0. for ile aynı satırda durur ve kolayca görülür. ) { // .. veya belirli bir koşul gerçekleştiğinde break ile çıkılacak şekilde tasarlanmış olabilir. Çok sık olarak.4 Döngü değişkeninin geçerli olduğu kapsam for ile while 'ın tek farkı. ama öyle olması gerekmez. i < 5.for Döngüsü for (/* hazırlık */. /* devam koşulu */. kapsam içindeki diğer ifadeler arasında kaybolmak yerine. for döngüsü sonsuza kadar anlamına gelir: for ( . döngü hazırlığı sırasında tanımlanan ismin geçerlilik alanıdır: for döngüsünün hazırlık bölgesinde tanımlanan isim.3 Döngünün üç bölümü de boş bırakılabilir Gereken durumlarda isteğe bağlı olarak. sayı < 11.. http://ddili.

Copyright © 2009-2011 Ali Çehreli.5 6.0 0.0 5..3 7. while (i < 5) { // .1 6. 25.2 8.3 1.7 6.2 2.1 0.1 3.0 7.0 2. bir önceki dersin sonunda anlatılanlara benzer şekilde.5 5.5 Problemler 1. // ← çalışır.3 0.6 6.2 0.8 1.1 1.6 3.1 2.6 7.7 8.8 6.1 5. isim while 'ın da içinde bulunduğu kapsamda tanımlanmış olduğu için.7 4.8 3.3 5. İç içe iki for döngüsü kullanarak.3 6.6 5.2 1.2 3.4 7.3 2.7 7.2 4. Bir veya daha fazla for döngüsü kullanarak ve * karakterini gereken sayıda yazdırarak geometrik şekiller çizdirin: * ** *** **** ***** ****** ******* ******** ********* ******** ******** ******** ******** ******** ******** ******** ******** ******** vs.8 5.5 8.8 2.7 1.3 8.4 5.for Döngüsü while döngüsünde ise.8 8.8 2.3 4. while 'dan çıkıldığında da geçerliliğini korur: int i = 0. } writeln(i).3 3.1 8.4 2. ekrana satır ve sütun numaralarını gösteren 9'a 9'luk bir tablo yazdırın: 0.0 4.7 2. programcılık hatası risklerini de azaltır.5 1.4 6.2 7.2 6.4 1.4 0.6 2.0 3. http://ddili.6 1.4 3.0 8.org 101 .6 4.8 4.6 0.6 8.5 7.8 7.0 1. i burada hâlâ geçerlidir for döngüsünün bu ismin geçerlilik alanını küçük tutuyor olması.2 5.1 7.5 4.0 6..5 2.4 8.4 4.1 4.7 5.5 0.7 0. ++i.5 3.7 3.

.. çözümler Copyright © 2009-2011 Ali Çehreli.org 102 .for Döngüsü . http://ddili.

İfade olduğu için. baştan artık yıl değilmiş gibi ilklemek ve adedi sonra bir arttırmak olabilir: int günAdedi = 365. programın işleyişini etkilemesidir. ?: işleci ise bir ifadedir ve if-else ile aynı işi. tek etkisi. Aynı işi if ile yapmak istesek. iyimser ? "dolu" : "boş").org 103 . ifadelerin kullanılabildiği her yerde kullanılabilir. Aşağıdaki örneklerde aynı işi hem ?: işleci ile. • Değer atama Artık yıl olduğunda 366. Hatırlarsanız.Üçlü İşleç ?: 26 Üçlü İşleç ?: ?: işleci. ya da doğru olmama işleminin değeridir. aksi durumda diğer işlemleri işletir. } if ile başka bir yol. if (artıkYıl) { günAdedi = 366. } • Yazdırma Yazdırılan bir mesajın bir parçasını ?: ile duruma göre farklı yazdırmak: writeln("Bardağın yarısı ". temelde bir if-else deyimi gibi çalışır: if (/* /* } else /* } koşul */) { doğruluk işlemleri */ { doğru olmama işlemleri */ if deyimi. ?: işlecinin bu örneklerdeki gibi durumlarda çok daha kısa olduğunu göreceksiniz. Bu işlecin değeri. hem de if deyimi ile gerçekleştireceğim. ama bir değer üretecek şekilde gerçekleştirir. Copyright © 2009-2011 Ali Çehreli. Yukarıdaki kodu ?: kullanarak şöyle yazabiliriz (bölüm açıklamalarını kısaltarak gösteriyorum): /* koşul */ ? /* doğruluk işlemi */ : /* doğru olmama işlemi */ ?: işleci üç bölümündeki üç ifade yüzünden üçlü işleç olarak adlandırılır. bir yol. olmadığında 365 değerini atamak için: int günAdedi = artıkYıl ? 366 : 365. if (artıkYıl) { ++günAdedi. koşul doğru olduğunda doğruluk işlemlerini. koşula bağlı olarak ya doğruluk işleminin. } else { günAdedi = 365. if bir deyimdir ve bu yüzden kendi değeri yoktur. baştan hiç ilklememek ve değeri sonra vermektir: int günAdedi. http://ddili.

artıkYıl koşuluna bağlı olarak ya 366'dır ya da 365. bütün mesajı farklı olarak yazdırmaktır: if (iyimser) { writeln("Bardağın yarısı dolu"). Örneğin yukarıdaki günAdedi değişkeni ilklenirken seçilen değer. Örneğin: "Şu an 3 kullanıcı bağlı" Copyright © 2009-2011 Ali Çehreli.org 104 . ya da doğru olmama ifadesinin.Üçlü İşleç ?: Aynı işi yapmak için mesajın baş tarafını önce yazdırabilir ve gerisini sonra if ile seçebiliriz: write("Bardağın yarısı "). baştan bir arttırmak ve mars ise bir kere daha arttırmak olabilir: ++tavlaPuanı. Bağlı olan kullanıcı sayısı 1 olduğunda mesaj "tek" kelimesi ile yazılır: "Şu an tek kullanıcı bağlı" Kullanıcı sayısı 1'den fazla olduğunda ise rakamla gösterilir. if (iyimser) { writeln("dolu"). } if ile başka bir yol. } if ile başka bir yol. if (marsOldu) { ++tavlaPuanı. } else { tavlaPuanı += 1. } else { writeln("boş"). if ile puanı duruma göre 2 veya 1 arttırmak: if (marsOldu) { tavlaPuanı += 2. http://ddili. İşlecin kullanımının D kurallarına göre geçerli olarak kabul edilebilmesi için bu iki ifadenin türlerinin aynı olması gerekir.1 Seçilecek ifadeler aynı türden olmalıdır ?: işlecinin ifade değeri. denetlenen koşula bağlı olarak ya doğru ifadesinin değeridir. } Bu örneklerden görüldüğü gibi kod ?: işleci ile çok daha kısa olmaktadır. 26. Bu değerlerin ikisinin de türü int olduğu için ifade geçerlidir. Geçerli olmayan bir örnek olarak Ddili Forum'un mesajlarından birisine bakalım. } • Hesap Tavla puanını mars olup olmama durumuna göre ?: ile 2 veya 1 arttırmak: tavlaPuanı += marsOldu ? 2 : 1. } else { writeln("Bardağın yarısı boş").

çözüm Copyright © 2009-2011 Ali Çehreli.conv modülünde bulunan to!string işlevi. // . // ← derleme HATASI Ne yazık ki o kod yasal değildir. writeln("Şu an ". bu değerin sıfırdan küçük olması zararda olmak. Çözüm olarak adet 'i de string 'e dönüştürebiliriz. (adet == 1) ? "tek" : adet.conv. http://ddili. std. Program verilen değere göre sonu "zarardasınız" veya "kazançlısınız" ile biten bir mesaj yazdırsın.. Artık ?: işlecinin her iki ifadesi de aynı türden (string ) olduğu için kod hatasız olarak derlenir ve çalışır. Örneğin "100 lira zarardasınız" veya "70 lira kazançlısınız". çünkü koşul sonucunda seçilecek iki ifadenin türleri farklıdır: "tek" bir string olduğu halde.2 Problem • Program kullanıcıdan sıfır dışında tek bir tamsayı değer alsın. " kullanıcı bağlı"). adet bir int 'tir.Üçlü İşleç ?: O mesajı kodlarken "tek" ve 3 arasındaki seçimi ?: işleciyle kolayca gerçekleştirebileceğimizi düşünebiliriz: writeln("Şu an ". (adet == 1) ? "tek" : to!string(adet). " kullanıcı bağlı"). sıfırdan büyük olması da kazançlı olmak anlamına gelsin..org 105 . Bu dersle ilgili olabilmesi için if koşulunu kullanmak yasak! :o) .. 26. kendisine verilen ifadenin string karşılığını üretir: import std..

Hazır Değerler

27

Hazır Değerler
Programlar işlerini değişkenlerin ve nesnelerin değerlerini kullanarak yaparlar. Değişkenleri ve nesneleri işleçlerle ve işlevlerle kullanarak yeni değerler üretirler ve yeni nesneler oluştururlar. Bazı değerlerin ise hesaplanmaları gerekmez; onlar kaynak kod içine doğrudan hazır olarak yazılırlar. Örneğin şu kod parçasındaki işlemler sırasında kullanılan 0.75 kesirli sayı değeri ve "Toplam fiyat: " sabit dizgisi kod içine programcı tarafından hazır olarak yazılmıştır: öğrenciFiyatı = biletFiyatı * 0.75; fiyat += öğrenciSayısı * öğrenciFiyatı; writeln("Toplam fiyat: ", fiyat); Bu tür değerlere hazır değer denir. Şimdiye kadar gördüğümüz programlarda zaten çok sayıda hazır değer kullandık. Bu derste hazır değerlerin bütün çeşitlerini ve söz dizimlerini göreceğiz.

27.1

Tamsayılar
Tamsayıları dört değişik sayı sisteminde yazabilirsiniz: Günlük hayatımızda kullandığımız onlu sayı sisteminde, bazı durumlarda daha uygun olan onaltılı veya ikili sayı sistemlerinde, ve nadir olarak sekizli sayı sisteminde. Bütün tamsayı değerlerinin rakamlarının aralarına, istediğiniz sayıda, istediğiniz yerlerine, ve herhangi amaçla; örneğin okumayı kolaylaştırmak için _ karakterleri yerleştirebilirsiniz. Örneğin, rakamları üçer üçer ayırmak için: 1_234_567. Bu karakterler tamamen programcının isteğine bağlıdır ve derleyici tarafından gözardı edilirler. Ondalık sayı sisteminde: Günlük hayatımızda kullandığımız gibi, onlu rakamlarla yazılır. Örnek: 12 . Ondalık değerlerin ilk rakamı 0 olamaz. Bunun nedeni, 0 ile başlayan hazır değerlerin çoğu başka dilde sekizli sayı sistemine ayrılmış olmasıdır. Bu konudaki karışıklıklardan doğabilecek olan hataları önlemek için D'de tamsayı hazır değerleri 0 ile başlayamaz. Onaltılı sayı sisteminde: 0x veya 0X ile başlayarak ve onaltılı sayı sisteminin rakamları olan "0123456789abcdef" ve "ABCDEF" ile yazılır. Örnek: 0x12ab00fe . Sekizli sayı sisteminde: std.conv modülündeki octal ile ve sekizli sayı sisteminin rakamları olan "01234567" ile yazılır. Örnek: octal!576 . İkili sayı sisteminde: 0b veya 0B ile başlayarak ve ikili sayı sisteminin rakamları olan 0 ve 1 ile yazılır. Örnek: 0b01100011 .

27.1.1

Tamsayı değerlerin türleri
Her değişkenin olduğu gibi, D'de hazır değerlerin de türleri vardır. Hazır değerlerin türleri int , double , vs. gibi açıkça yazılmaz; derleyici, türü hazır değerin yazımından anlar. Hazır değerlerin türlerinin aslında programcı açısından çok büyük bir önemi yoktur. Bazen tür, hazır değerin içinde kullanıldığı ifadeye uymayabilir ve derleyici uyarı verir. Öyle durumlarda aşağıdaki bilgilerden yararlanarak hazır değerin türünü açıkça belirtmeniz gerekebilir. Tamsayı hazır değerlerin öncelikle int türünde oldukları varsayılır. Eğer değer bir int 'e sığmayacak kadar büyükse, derleyici şu şekilde karar verir:

Copyright © 2009-2011 Ali Çehreli, http://ddili.org

106

Hazır Değerler

• int 'e sığmayacak kadar büyük olan değer onlu sistemde yazılmışsa, long 'dur • int 'e sığmayacak kadar büyük olan değer başka bir sayı sisteminde yazılmışsa, öncelikle uint 'tir, ona da sığmıyorsa long 'dur, ona da sığmıyorsa ulong 'dur Bunu görmek için daha önce öğrendiğimiz typeof 'tan ve stringof 'tan yararlanan şu programı kullanabiliriz: import std.stdio; void main() { writeln("\n--- bunlar onlu olarak yazıldılar ---"); // int'e sığdığı için int writeln( 2_147_483_647, "\t\t", typeof(2_147_483_647).stringof); // int'e sığmadığı ve onlu olarak yazıldığı için long writeln( 2_147_483_648, "\t\t", typeof(2_147_483_648).stringof); writeln("\n--- bunlar onlu olarak yazılMAdılar ---"); // int'e sığdığı için int writeln( 0x7FFF_FFFF, "\t\t", typeof(0x7FFF_FFFF).stringof); // int'e sığmadığı ve onlu olarak yazılmadığı için uint writeln( 0x8000_0000, "\t\t", typeof(0x8000_0000).stringof); // uint'e sığmadığı ve onlu olarak yazılmadığı için long writeln( 0x1_0000_0000, "\t\t", typeof(0x1_0000_0000).stringof); // long'a sığmadığı ve onlu olarak yazılmadığı için ulong writeln( 0x8000_0000_0000_0000, "\t\t", typeof(0x8000_0000_0000_0000).stringof);

}

--- bunlar onlu olarak yazıldılar --2147483647 int 2147483648 long --- bunlar onlu olarak yazılMAdılar --2147483647 int 2147483648 uint 4294967296 long 9223372036854775808 ulong

27.1.2

L son eki
Değerin büyüklüğünden bağımsız olarak, eğer değerin sonunda bir L karakteri varsa, türü long 'dur. Örnek: 10L

27.1.3

U son eki
Değerin büyüklüğünden bağımsız olarak, eğer değerin sonunda bir U karakteri varsa, işaretsiz bir türdür. Örnek: 10U'nun türü uint 'tir. Küçük harf u da kullanılabilir. L ve U karakterleri birlikte ve sıralarının önemi olmadan da kullanılabilirler. Örneğin 7UL'nin ve 8LU'nun ikisinin de türleri ulong 'dur.

Copyright © 2009-2011 Ali Çehreli, http://ddili.org

107

Hazır Değerler

27.2

Kesirli sayılar
Kesirli sayılar onlu sayı sisteminde veya onaltılı sayı sisteminde yazılabilirler. Örneğin ondalık olarak 1.234 veya onaltılı olarak 0x9a.bc Ondalık sayı sisteminde: Sayının yanına, e veya E belirtecinden sonra "çarpı 10 üzeri" anlamına gelen bir çarpan eklenebilir. Örneğin 3.4e5, "3.4 çarpı 10 üzeri 5" anlamındadır. Bu belirteçten sonra bir + karakteri de yazılabilir ama onun bir etkisi yoktur. Örneğin, 5.6e2 ile 5.6e+2 aynı anlamdadır. Belirteçten sonra gelen - karakterinin etkisi vardır ve "10 üzeri o kadar değere bölünecek" anlamına gelir. Örneğin 7.8e-3, "7.8 bölü 10 üzeri 3" anlamındadır. Onaltılı sayı sisteminde: Sayı 0x veya 0X ile başlar; noktadan önceki ve sonraki bölümleri onaltılı sayı sisteminin rakamlarıyla yazılır. e ve E de onaltılı sistemde geçerli rakamlar olduklarından, üs belirteci olarak başka bir harf kullanılır: p (veya P ). Başka bir fark, bu belirteçten sonra gelen değerin "10 üzeri" değil, "2 üzeri" anlamına gelmesidir. Örneğin 0xabc.defP4'ün sonundaki belirteç, "2 üzeri 4 ile çarpılacak" anlamına gelir. Kesirli sayı değerler hemen hemen her zaman için bir nokta içerirler, ama belirteç varsa noktaya gerek yoktur. Örneğin 2e3, 2000 değerinde bir kesirli sayıdır. Noktadan önceki değer 0 ise, yazılmayabilir. Örneğin .25, "çeyrek" anlamında bir kesirli sayı değeridir. Gözardı edilen _ karakterlerini kesirli sayılarla da kullanabilirsiniz: 1_000.5

27.2.1

Kesirli sayı değerlerin türleri
Kesirli değerler özellikle belirtilmemişse double türündedir. Sonlarına f veya F eklenirse, float ; L eklenirse real olurlar. Örneğin 1.2 double 'dır, 3.4f float 'tur, ve 5.6L real 'dir. Değerin sonuna eklenen i , o değerin türünü bir sanal tür haline getirir. Örneğin 7.8i idouble 'dır, 9.1fi ifloat 'tur, ve 11.12Li ireal 'dir.

27.3

Karakterler
Karakter türündeki hazır değerler her zaman için tek tırnaklar arasında yazılırlar. Örneğin 'a' , '\n' , '\x21' . Karakterin kendisi olarak: Tek tırnaklar arasına karakterin kendisi klavyeden yazılabilir veya başka bir metinden kopyalanabilir: 'a', 'ş', vs. Karakter belirteci olarak: Ters bölü işaretinden sonra bir karakter belirteci kullanılabilir. Örneğin ters bölü karakterinin kendisi '\\' şeklinde yazılır. Karakter belirteçleri şunlardır:

Copyright © 2009-2011 Ali Çehreli, http://ddili.org

108

Hazır Değerler

Yazımı \' \" \? \\ \a \b \f \n \r \t \v

Anlamı tek tırnak çift tırnak soru işareti ters bölü uyarı karakteri (bazı ortamlarda zil sesi) silme karakteri sayfa sonu satır sonu aynı satırın başına götürür bir sonraki sekme adımına götürür bir sonraki düşey sekme adımına götürür

ASCII karakter kodu olarak: Değerleri 256'dan küçük olan karakterleri, kodunu vererek yazabilirsiniz. Yukarıda tamsayılar başlığında anlatılanlara uygun olarak, kodu \x ile başlayan 2 haneli onaltılı sayı olarak veya 3 haneye kadar sekizli sayı olarak yazabilirsiniz. Örneğin '\x21' ve '\41' ünlem işareti karakterinin iki farklı yazımıdır. Unicode karakter kodu olarak: u karakterinden sonra 4 onaltılı rakam olarak yazılırsa türü wchar olur; U karakterinden sonra 8 onaltılı rakam olarak yazılırsa türü dchar olur. Örneğin '\u011e' ve '\U0000011e' Ğ karakterinin sırasıyla wchar ve dchar türünde olan değeridir. İsimli karakter olarak: İsimleri olan karakterleri D'nin isimli karakterler listesinde gösterilen isimleriyle ve '\&karakter_ismi;' söz dizimiyle yazabilirsiniz. Örneğin '\&euro;' €, '\&hearts;' ♥, ve '\&copy;' © karakteridir.

27.4

Dizgiler
Hazır dizgiler sabit karakterlerin bileşimlerinden oluşurlar ve çok sayıda farklı söz dizimiyle yazılabilirler.

27.4.1

Çift tırnaklar arasında yazılan dizgiler
Dizgilerin başka dillerde de bulunan en yaygın yazımı, çift tırnaklar arasında yazılmalarıdır: örneğin "merhaba"... Bu şekilde yazıldığında, içindeki karakterler yukarıdaki karakter yazımlarına uygun olarak yazılırlar. Örneğin, göstermek amacıyla yukarıdaki karakter sabitlerinden bazılarını içeren "A4 ka\u011fıt: 3\&frac12;TL" dizgisi, "A4 kağıt: 3½TL"nin eşdeğeridir.

27.4.2

Göründüğü gibi çıkan dizgiler
Ters tırnak işaretleri arasında yazılan dizgilerin içindeki karakterler, yukarıda karakter sabitleriyle ilgili olarak anlatılan kurallar işletilmeden, görüldükleri anlama gelirler. Örneğin `c:\nurten` şeklinde yazılan dizgi, Windows işletim sisteminde bir klasör ismi olabilir. Oysa çift tırnaklar arasında yazılmış olsa, dizginin içinde geçen '\n', satır sonu anlamına gelirdi: writeln(`c:\nurten`); writeln("c:\nurten");

c:\nurten c: urten

← göründüğü gibi ← satır sonu olarak anlaşılan karakter

Copyright © 2009-2011 Ali Çehreli, http://ddili.org

109

Hazır Değerler

Göründüğü gibi çıkan dizgilerin diğer bir yazım şekli, çift tırnaklar kullanmak, ama öncesine bir r belirteci eklemektir: r"c:\nurten" de göründüğü gibi anlaşılır.
27.4.3

Onaltılı sistemde yazılan dizgiler
Karakterlerinin kodları onaltılı sayı sisteminde yazılacak olan dizgilerin her karakterinin başına \x yazmak yerine, başına x belirteci gelen dizgiler kullanılabilir. Hatta bu dizgilerin içine okumayı kolaylaştırmak amacıyla boşluklar da yazılabilir. Bu boşluklar derleyici tarafından gözardı edilirler. Örneğin "\x44\x64\x69\x6c\x69" yerine x"44 64 69 6c 69" yazılabilir.

27.4.4

Ayraçlı dizgiler
Çift tırnakların hemen içine gelmek koşuluyla, dizginin parçası olmayan ayraçlar yerleştirebilirsiniz. Ayraçlı dizgilerde çift tırnaklardan önce q karakteri gelir: q"{merhaba}" dizgisinin değeri "merhaba"dır. Hemen sonrası satır sonuna gelmek koşuluyla, ayraçları sözcükler olarak da belirleyebilirsiniz: writeln(q"AYRAÇ birinci satır ikinci satır AYRAÇ"); Bu örnekteki AYRAÇ sözcüğü dizginin parçası değildir: birinci satır ikinci satır

27.4.5

D kodu dizgileri
Yine başında q karakteri olmak üzere, { ve } karakterleri arasında yasal D kodu içeren dizgiler yazılabilir: auto dizgi = q{int sayı = 42; ++sayı;}; writeln(dizgi);

int sayı = 42; ++sayı;

27.4.6

Dizgi değerlerin türleri
Dizgiler özellikle belirtilmediğinde immutable(char)[] türündedirler. Sonlarına eklenen c , w , ve d karakterleri dizginin türünü sırasıyla immutable(char)[] , immutable(wchar)[] , ve immutable(dchar)[] olarak belirler. Örneğin "merhaba"d dizgisinin karakterleri immutable(dchar) türündedirler. Bu üç türün sırasıyla string , wstring , ve dstring olan takma isimlerini Dizgiler dersinde öğrenmiştiniz.

27.5

Hazır değerler derleme zamanında hesaplanırlar
Hazır değerleri işlem halinde de yazabilirsiniz. Örneğin Ocak ayındaki toplam saniye değerini 2678400 veya 2_678_400 olarak yazmak yerine, değerin doğruluğundan emin olmamıza yarayan 60 * 60 * 24 * 31 şeklinde de yazabilirsiniz. İçinde çarpma işleçleri olsa da, o

Copyright © 2009-2011 Ali Çehreli, http://ddili.org

110

Hazır Değerler

işlem programınızın çalışma hızını düşürmez; hazır değer, derleme zamanında yine de 2678400 olarak hesaplanır ve sanki siz öyle yazmışsınız gibi derlenir. Aynı durum dizgi hazır değerleri için de geçerlidir. Örneğin "merhaba " ~ "dünya" yazımındaki dizgi birleştirme işlemi çalışma zamanında değil, derleme zamanında yapıldığı için programınız sanki "merhaba dünya" yazılmış gibi derlenir ve çalışır.

27.6

Problemler
1. Bir bankacılık oyun programında oyuncuların başlangıçta ellerinde bulunan para 10 milyar olsun. Bu amaçla programa yazılan şu satır yüzünden program derlenemiyor: int kasadakiPara = 10_000_000_000; O satırın derlenmesini sağlayın. 2. Bir tamsayının değerini sonsuz bir döngüde arttıran ve bunu ekrana yazdıran bir program yazın. Döngünün her tekrarında sayının değeri ekrana yazdırıldığı halde, yazılan değer hep aynı satırda çıksın: Sayı: 25774 ← hep aynı satırın üstüne yazılsın

Bunun için yukarıdaki özel karakter belirteçlerinden birisi işinize yarayacak; '\n' olmadığı açık herhalde... :o) ... çözümler

Copyright © 2009-2011 Ali Çehreli, http://ddili.org

111

Çıktı Düzeni

28

Çıktı Düzeni
Diğer derslerden farklı olarak, bu bölüm D dilinin iç olanaklarıyla ilgili değil. Burada anlatılanlar tamamen kütüphane olanakları olarak gerçekleştirilmişlerdir ve Phobos'un çıktı düzeni için kullandığı std.format modülünün olanaklarıdır. Hazır değerler dersinde anlatılanlarla yakından ilgili olduğu için bu konuyu burada anlatmaya karar verdim. D'nin giriş ve çıkış için kullandığı düzen belirteçlerinin temelde C dilindekiler gibi olduğunu ama bazı farkları bulunduğunu göreceksiniz. Bir ön hatırlatma olarak :o), bütün düzen dizgisi karakterleri: Ayar Karakterleri (birden fazla kullanılabilir) sola dayalı + işaretli # diğer şekilde 0 solda 0'lı boşluk solda boşluklu Düzen Karakterleri s belirteçsiz gibi b ikili d onlu o sekizli x,X onaltılı e,E on üzerili f,F virgüllü g,G e veya f gibi a,A onaltılı kesirli Şimdiye kadar çıktı için writeln gibi işlevleri, gerektiğinde birden fazla parametreyle kullanmıştık. Bu parametreler otomatik olarak karakter eşdeğerlerine dönüştürülerek sırayla çıkışa gönderiliyorlardı. Çoğu zaman bu yeterli olmaz. Çıktının belirli bir düzene uyması gerekebilir. Örneğin bir faturanın maddelerini yazdıran şu koda bakalım: faturaMaddeleri ~= 1.23; faturaMaddeleri ~= 45.6; for (int i = 0; i != faturaMaddeleri.length; ++i) { writeln("Madde ", i + 1, ": ", faturaMaddeleri[i]); } Çıktısı: Madde 1: 1.23 Madde 2: 45.6 Oysa faturadaki değerlerin belirli bir düzende, örneğin her zaman için virgülden sonra iki haneyle ve geniş bir alanda sağa dayalı olarak yazılmaları okuma açısından önemli olabilir. (Not: Ben bu derste günlük kullanıma uygun olarak "virgül" diyeceğim; ama kesirli sayılarda virgül yerine nokta karakteri kullanılır.): Madde 1: Madde 2: 1.23 45.60

Copyright © 2009-2011 Ali Çehreli, http://ddili.org

112

Çıktı Düzeni

İşte çıktı düzeni, böyle konularda yarar sağlar. Şimdiye kadar kullandığımız ve isminde f harfi geçen çıktı işlevlerinin ilk parametresi bir düzen dizgisi olarak verilebilir. Bu işlevlerin isimlerindeki f harfi, "düzen, biçim" anlamına gelen "format"ın baş harfinden gelmiştir. Örneğin yukarıdaki çıktıyı veren bir düzen dizgisi writefln ile şöyle yazılabilir: writefln("Madde %d:%9.02f", i + 1, faturaMaddeleri[i]); Düzen dizgisi, normal karakterlerden ve özel düzen belirteçlerinden oluşur. Her düzen belirteci % karakteriyle başlar ve bir düzen karakteri ile biter. Yukarıdaki dizgide iki tane düzen belirteci var: %d ve %9.02f . Her belirteç, düzen dizgisinden sonra verilen parametrelerle sıra ile eşleşir. Örneğin %d ile i + 1 , ve %9.02f ile faturaMaddeleri[i] ... Her belirteç, eşleştiği parametrenin çıktı düzenini belirler. Düzen dizgisi içinde bulunan ve belirteçlere ait olmayan karakterler, oldukları gibi yazdırılırlar. Yukarıdaki dizgi içindeki normal karakterleri kırmızı renkle şöyle gösterebiliriz: "Madde %d:%9.02f" . Düzen belirteci beş bölümden oluşur (Not: okumayı kolaylaştırmak için aralarında boşluk kullanıyorum; bu bölümler aslında bitişik olarak yazılırlar): % ayar_karakterleri genişlik duyarlık düzen_karakteri

Baştaki % karakterinin ve sondaki düzen karakterinin yazılması şarttır, diğerleri ise isteğe bağlıdır. % karakterinin böyle özel bir anlamı olduğu için, çıktıda % karakterinin kendisi yazdırılmak istendiğinde %% şeklinde çift olarak yazılır.

28.1

düzen_karakteri
b : Tamsayı, ikili sayı düzeninde yazdırılır. o : Tamsayı, sekizli sayı düzeninde yazdırılır. x ve X : Tamsayı, onaltılı sayı düzeninde yazdırılır; x için küçük harfler, X için büyük harfler kullanılır. d : Tamsayı, onlu sistemde yazdırılır; eğer işaretsiz bir türse ve değeri sıfırdan küçükse, başına eksi işareti gelir; aksi durumda işaretsiz bir tür gibi yazdırılır. int değer = 12; writefln("İkili : writefln("Sekizli : writefln("Onaltılı: writefln("Ondalık : %b", %o", %x", %d", değer); değer); değer); değer);

İkili : Sekizli : Onaltılı: Ondalık :

1100 14 c 12

e : Kesirli sayı, aşağıdaki bölümlerden oluşacak şekilde yazdırılır. • virgülden önce tek hane

Copyright © 2009-2011 Ali Çehreli, http://ddili.org

113

Çıktı Düzeni

• • • •

duyarlık 0 değilse virgül virgülden sonra duyarlık adet hane (varsayılan duyarlık 6'dır) e karakteri en az iki hane olarak "10 üzeri" anlamında üs değeri

E : e ile aynı düzende, ama çıktıda E harfiyle f ve F : Kesirli sayı, onlu sistemde yazdırılır; virgülden önce en az bir hane bulunur; varsayılan duyarlık 6'dır. g : Kesirli sayı, eğer üs değeri -5 ile duyarlık arasında olacaksa, f gibi; değilse e gibi yazdırılır; duyarlık virgülden sonrasını değil, belirgin hane sayısını belirtir; virgülden sonra belirgin hane yoksa virgül de yazdırılmaz; virgülden sonra en sağdaki sıfırlar yazdırılmazlar. G : g ile aynı düzende, ama E veya F kullanılmış gibi yazdırılır a : Kesirli sayı, onaltılı sistemde ve aşağıdaki bölümlerden oluşacak şekilde yazdırılır: • • • • 0x karakterleri tek onaltılı hane duyarlık 0 değilse virgül virgülden sonra duyarlık adet hane, veya duyarlık belirtilmemişse gerektiği kadar hane

• p karakteri • üssün değerine göre - veya + karakteri • en az bir hane olarak "2 üzeri" anlamında üs değeri; (0 değerinin üs değeri 0'dır) A : a ile aynı düzende, ama çıktıda 0X ve P karakterleriyle double değer = 123.456789; writefln("e writefln("f writefln("g writefln("a ile: ile: ile: ile: %e", %f", %g", %a", değer); değer); değer); değer);

e f g a

ile: ile: ile: ile:

1.234568e+02 123.456789 123.457 0x1.edd3c07ee0b0bp+6

s : Parametrenin değeri; düzen dizgisi kullanılmadığı zamandaki gibi, türüne uygun olan şekilde yazdırılır: • • • • bool türler true veya false olarak tamsayılar %d gibi kesirli sayılar %g gibi dizgiler UTF-8 kodlamasıyla; duyarlık, en fazla kaç bayt kullanılacağını belirler (UTF-8 kodlamasında karakter sayısıyla bayt sayısının eşit olmayabileceklerini hatırlayın; örneğin "ağ" dizgisi toplam 3 bayt uzunluğunda 2 karakterden oluşur) • yapı ve sınıf nesneleri, türün toString() üye işlevinin ürettiği dizgi olarak; duyarlık, en fazla kaç bayt kullanılacağını belirler • diziler, elemanları yan yana sıralanarak bool b = true; int tamsayı = 365; double kesirli = 9.87; string dizgi = "düzenli"; auto nesne = File("deneme_dosyasi", "r");

Copyright © 2009-2011 Ali Çehreli, http://ddili.org

114

Çıktı Düzeni

int[] dizi = [ 2, 4, 6, 8 ]; writefln("%s", writefln("%s", writefln("%s", writefln("%s", writefln("%s", writefln("%s", b); tamsayı); kesirli); dizgi); nesne); dizi);

true 365 9.87 düzenli File [2, 4, 6, 8]

28.2

genişlik
Değer için çıktıda ayrılan alanın genişliğini belirler. Eğer genişlik olarak * kullanılmışsa, genişlik değeri bir sonraki parametrenin değeri olarak alınır. Eğer eksi bir sayıysa, - ayar karakteri kullanılmış gibi çalışır. int değer = 100; writefln("On karakterlik alanda :%10s", değer); writefln("Beş karakterlik alanda:%5s", değer);

On karakterlik alanda : Beş karakterlik alanda:

100 100

28.3

duyarlık
Eğer belirtilmişse, nokta karakterinden sonra yazılır. Kesirli sayı türünden olan değerlerin çıktıda kullanılacak olan duyarlığını belirler. Eğer duyarlık olarak * kullanılmışsa, duyarlık değeri bir sonraki parametrenin değeri olarak alınır (o değer int olmak zorundadır). Duyarlık eksi bir sayıysa gözardı edilir. double kesirli = 1234.56789; writefln("%.8g", writefln("%.3g", writefln("%.8f", writefln("%.3f", kesirli); kesirli); kesirli); kesirli);

1234.5679 1.23e+03 1234.56789000 1234.568 auto sayı = 0.123456789; writefln("Sayı: %.*g", 4, sayı);

Sayı: 0.1235

Copyright © 2009-2011 Ali Çehreli, http://ddili.org

115

Çıktı Düzeni

28.4

ayar_karakterleri
Birden fazla ayar karakteri kullanabilirsiniz. - : parametre değeri; kendisine ayrılan alanda sola dayalı olarak yazdırılır; bu ayar, 0 ayar karakterini geçersiz kılar int değer = 123; writefln("normalde sağa dayalı:|%10d|", değer); writefln("sola dayalı :|%-10d|", değer);

normalde sağa dayalı:| sola dayalı :|123

123| |

+ : değer artı ise başına + karakteri yazdırılır; bu ayar, boşluk ayar karakterini geçersiz kılar writefln("eksi değerde etkili değil: %+d", -50); writefln("artı değer, + ile : %+d", 50); writefln("artı değer, + olmadan : %d", 50);

eksi değerde etkili değil: -50 artı değer, + ile : +50 artı değer, + olmadan : 50 # : kullanılan düzen_karakteri'ne bağlı olarak, değeri başka şekilde yazdırır • • • • o için: sekizli sayının ilk karakteri her zaman için 0 olarak yazdırılır x ve X için: sayı sıfır değilse, başına 0x veya 0X gelir kesirli sayılarda: virgülden sonra hane olmasa da virgül yazdırılır g ve G için: virgülden sonra sağdaki sıfırlar atılmaz writefln("Sekizli sıfırla başlar writefln("Onaltılının başına 0x gelir : %#o", 1000); : %#x", 1000);

writefln("Gerekmese de virgüllü olur : %#g", 1f); writefln("Sağdaki sıfırlar da yazdırılır: %#g", 1.2);

Sekizli sıfırla başlar Onaltılının başına 0x gelir Gerekmese de virgüllü olur

: 01750 : 0x3e8 : 1.00000

Sağdaki sıfırlar da yazdırılır: 1.20000 0 : sayılarda (değer nan veya infinity değilse), sol tarafa değer için ayrılan alan dolacak kadar 0 yazdırılır; duyarlık da belirtilmişse bu ayar etkisizdir writefln("Sekiz genişlikte: %08d", 42);

Sekiz genişlikte: 00000042 boşluk karakteri: değer artı ise, eksi değerlerle alt alta düzgün dursun diye başına tek bir boşluk karakteri yazdırılır

Copyright © 2009-2011 Ali Çehreli, http://ddili.org

116

Çıktı Düzeni

writefln("Eksi değerde etkisi yok: % d", -34); writefln("Artı değer, boşluklu : % d", 56); writefln("Artı değer, boşluksuz : %d", 56);

Eksi değerde etkisi yok: -34 Artı değer, boşluklu : 56 Artı değer, boşluksuz : 56

28.5

Problemler
1. Girilen tamsayıyı onaltılı düzende yazdıran bir program yazın. 2. Girilen kesirli sayıyı bir yüzde değeri olarak ve virgülden sonra 2 haneyle yazdıran bir program yazın. Örneğin 1.2345 girildiğinde ekrana yalnızca %1.23 yazsın. ... çözümler

Copyright © 2009-2011 Ali Çehreli, http://ddili.org

117

Giriş Düzeni

29

Giriş Düzeni
Çıktı düzeni dersinde anlatılanlara benzer şekilde, girişten gelen verilerin düzeni de belirtilebilir. Bu düzen; hem okunması istenen bilgiyi, hem de gözardı edilmesi istenen bilgiyi belirtebilir. Düzen dizgisi olarak şimdiye kadar yaptığımız gibi " %s" kullanıldığında, okunmakta olan değişkenin türüne en uygun olan düzende okunur. Örneğin aşağıdaki readf çağrılarının ikisi de aynı şekilde çalışır ve ikisi de girişten bir kesirli sayı okur: double sayı; readf(" %s", &sayı); readf(" %f", &sayı); Giriş için kullanılan düzen dizgisi, C'deki scanf işlevinin düzen dizgisine çok benzer. Düzen dizgisi içinde üç tür bilgi bulunabilir: • boşluk karakteri: girişteki sıfır veya daha fazla boşluk karakteri anlamına gelir ve onların okunup gözardı edilmelerini sağlar • herhangi bir karakter: girişte aynen bulunması beklenen bir karakteri ifade eder ve onun okunup gözardı edilmesini sağlar • düzen belirteci: önceki derstekilere benzer şekilde % karakteriyle başlar ve girişten gelen karakterlerin hangi türde okunacaklarını belirler O bilgiler sayesinde, girişten gelen veri içerisinden bizim için önemli olanlarını seçip çıkartmak ve geri kalanını gözardı etmek son derece kolaydır. Ayrıntıya girmeden önce, bu üç tür bilgiyi kullanan bir örneğe bakalım. Girişte tek satır halinde şöyle bir bilgi bulunsun: numara:123 not:90 O satır içerisinden bizim için önemli olan iki bilgi, öğrencinin numarası ve notu olsun; yani girişteki numara: ve not: gibi karakterlerin bizim için bir önemi bulunmasın. İşte o satır içinden öğrencinin numarasını ve notunu seçen ve geri kalanını gözardı eden bir düzen dizgisi şöyle yazılabilir: int numara; int not; readf("numara:%s not:%s", &numara, &not); "numara:%s not:%s" düzen dizgisi içinde maviyle gösterdiğim bütün karakterler girişte aynen bulunmalıdırlar; onlar readf tarafından girişten okunup gözardı edilirler. O düzen dizgisinde kullanılan tek boşluk karakteri, girişte o noktada bulunan bütün boşluk karakterlerinin gözardı edilmelerine neden olur. % karakterinin özel anlamı nedeniyle, girişte % karakterinin kendisinin gözardı edilmesi istendiğinde %% şeklinde çift olarak yazılır.

29.1

Düzen karakterleri
d : onlu sistemde tamsayı o : sekizli sistemde tamsayı

Copyright © 2009-2011 Ali Çehreli, http://ddili.org

118

readf(" %d %o %x".2 Problem • Girişten yıl. sekizli düzende 2x8+3=19 değerinde.org 119 . int sayı_x.09. writeln("sekizli olarak okununca : ".Giriş Düzeni x : onaltılı sistemde tamsayı f : kesirli sayı s : türe uygun olan düzende c : tek bir karakter. ve onaltılı düzende 2x16+3=35 değerindedir. bu belirteç boşlukları da okur (gözardı edilmelerini önler) Örneğin girişte 3 tane "23" bulunduğunu varsayarsak. "23".gün düzeninde bir tarih bilgisi gelsin. Girişine 3 defa "23" girdiğimiz halde. sayı_o). . sayı_d). http://ddili. değer. writeln("onaltılı olarak okununca: ".ay. Örneğin 2009. 29. int sayı_o. değerler farklı okunur: onlu olarak okununca : 23 sekizli olarak okununca : 19 onaltılı olarak okununca: 35 Not: Çok kısaca. çözüm Copyright © 2009-2011 Ali Çehreli.30 geldiğinde 9 yazılsın. sayı_x). &sayı_o. Ekrana kaçıncı ay olduğunu yazdırın. düzen belirtecine göre farklı olarak okunur: int sayı_d.. &sayı_x). writeln("onlu olarak okununca : ". &sayı_d..

} } while (tahmin != sayı). } else if (sayı > tahmin) { write("tuttuğum sayı daha büyük. 101). koşul denetiminin sonda olması ve bu sayede işlemlerin en az bir kere işletilmeleridir: hazırlık (while'dan daha az durumda gerekir) asıl işlemler ilerletilmesi koşul denetimi asıl işlemler ilerletilmesi koşul denetimi .random... Daha doğal gelmeyebilir de.. Örneğin. "). :o) Seçim sizin. readf(" %s". tuttuğu sayının tahmin edilmesini bekleyen bir programda do-while döngüsü daha doğal gelebilir: import std. http://ddili.stdio.. import std..."). if (sayı < tahmin) { write("tuttuğum sayı daha küçük.do-while Döngüsü 30 do-while Döngüsü for döngüsü dersinde while 'ın işleyiş adımlarını da görmüştük: hazırlık koşul denetimi asıl işlemler ilerletilmesi koşul denetimi asıl işlemler ilerletilmesi ..org 120 . void main() { int sayı = uniform(1. &tahmin).. Copyright © 2009-2011 Ali Çehreli. do-while 'ın while 'dan farkı.. writeln("1'den 100'e kadar bir sayı tuttum!. } writeln("Doğru!").. do { write("Tahmininiz nedir? "). int tahmin. ").

çözüm Copyright © 2009-2011 Ali Çehreli. 30.random modülünde bulunan bir işlevdir. Belirtilen aralıkta eşit dağılımlı rasgele sayılar üretir. Diğer kullanımlarını öğrenmek için std. aralığı belirleyen ikinci değer.random modülünün belgesine bakabilirsiniz. std.1 Problem • Aynı oyunu bilgisayara oynatın. http://ddili. Yukarıdaki kullanımında. tuttuğunuz sayıyı en fazla 7 tahminde bulacaktır. .do-while Döngüsü uniform ..org 121 . çıkacak sayılar arasında değildir..

eleman_türü[indeks_türü] şeklindedir.1 Çok hızlı ama sırasız Eşleme tabloları arka planda hash table veri yapısını kullandıkları için algoritma karmaşıklığı açısından dizilerden geri kalmazlar: son derece hızlı topluluklardır. herhangi başka bir türdeki değere eşlemektir. Dizileri. Eşleme tablolarının en kullanışlı taraflarından birisi. "Çarşamba". Onları. Örneğin haftanın günlerinin isimlerini tutan bir dizi şöyle tanımlanabilir: string[] günİsimleri = [ "Pazartesi". ama bunun tersini yapamazlar. http://ddili.org 122 . 31. içlerindeki elemanların sıraları konusunda bir şey bilinemiyor olmasıdır. Örneğin türü string olan gün isminden.Eşleme Tabloları 31 Eşleme Tabloları Eşleme tabloları. Eşleme tablolarının kullanışlılığı işte bu gibi durumlarda ortaya çıkar. Eşleme tabloları. her türün kullanılabilmesidir. çünkü dizilerde indeks olarak yalnızca tamsayı türler kullanılabilir.2 Tanımlama Eşleme tablosu tanımı. indeks ve eleman türü olarak. hemen her zaman için sabit zamanda erişim sağlamalarıdır. Bu kadar hızlı çalışmalarının bedeli. üst düzey dillerin hepsinde bulunan veri yapılarıdır. // "Salı" yazar Diziler elemanlara numara ile (indeksle) erişmeye çok uygundurlar. "Salı" gibi dizgiler indeks olamazlar. içlerindeki eleman sayısından bağımsız olarak. herhangi bir indeks türündeki bir değeri. elemanlara yalnızca numara ile değil. belirli bir günün ismi o diziyi kullanarak şöyle yazdırılabilir: writeln(günİsimleri[1]). "Perşembe". // ← derleme HATASI O satırda "1" yazmayı umuyoruz ama derleme hatası oluşuyor. "Pazar" ]. köşeli parantezler içine dizinin uzunluğu yerine. Copyright © 2009-2011 Ali Çehreli. "Salı". "elemanları yan yana duran topluluk" olarak tanımlamış ve elemanlarına indeksle erişildiğini görmüştük. Bunun anlamı. Programlarda çok kullanılan ve çok hızlı veri yapılarıdır. dizi tanımına çok benzer. Örneğin elimizde "Salı" dizgisi bulunduğunda onun haftanın 1 numaralı günü olduğunu söyleyemezler (indekslerin 0'la başladıklarını hatırlayın): writeln(günSıraları["Salı"]). türü int olan gün sıra numarasına eşleme yapan bir eşleme tablosu şöyle tanımlanır: int[string] günSıraları. ve. Tek farkı. dizinin indeks türünün gelmesidir. ne de örneğin küçükten büyüğe doğru sıralandıklarını söyleyebiliriz. Söz dizimi. "Cumartesi". herhangi bir türle erişilen veri yapılarıdır. program içine gömülen minik veri tabanları olarak düşünülebilirsiniz. 31. "Cuma". Görevleri. daha sonra öğreneceğimiz yapı ve sınıf türleri de dahil olmak üzere. Elemanların ne dizilerdeki gibi yan yana olduklarını.

"Çarşamba":2. "Pazar":6 ]. "Salı":1. sorgulamak için in işleci kullanılır. örneğin çok sayıda eleman giriş işleminin sonunda.keys tabloda bulunan bütün indeksler. writeln(günSıraları["Salı"]). 31. tablo.Eşleme Tabloları Dinamik dizilerinki gibi. tablonun asıl kullanımı başlamadan önce bu nitelik çağrılabilir Copyright © 2009-2011 Ali Çehreli. at" anlamına gelen . tablo erişimini hızlandırmak için. 31. "içinde var mı?" sorusunu yanıtlar: if ("mor" in renkKodları) { // evet.4 İlkleme Gün sıraları kavramında olduğu gibi.remove("Salı").values tabloda bulunan bütün elemanlar. dinamik dizi olarak .7 Nitelikler • • • • . http://ddili. "Cuma":4..length tablodaki eleman sayısı . günSıraları["Salı"] = 1. buradaki kullanımında "çıkart..5 Tablodan eleman çıkartma Elemanlar. Bu kullanım. // ← çalışma zamanı HATASI Son satır. indeks değerleri ile eleman değerleri arasına : karakteri yazılır: int[string] günSıraları = [ "Pazartesi":0. tabloda bulunmayan bir elemana eriştiği için çalışma zamanında bir hata atılmasına neden olur. otomatik olarak büyür. dinamik dizi olarak . dizi söz diziminde olduğu gibi ilklenir. "Perşembe":3. // "1" yazar 31. 31.6 Eleman sorgulama Tabloda bulunmayan bir elemana erişmek bir hata atılmasına neden olacağından. eşleme bilgisi bazen tablo kurulduğu sırada bilinir. tablonun daha etkin çalışmasını sağlayabilir. } 31. Tablo.3 Atama Elemanı indeks değeri ile atamak.remove ile çıkartılırlar: günSıraları. eşleme bilgisinin kurulması ve tablonun otomatik olarak büyümesi için yeterlidir: günSıraları["Pazartesi"] = 0. bu bilgiyi tabloyu tanımladığımız zaman da verebiliriz. Eşlemeleri teker teker atayarak kurmak yerine.org 123 . yokmuş. renkKodları'nda "mor" indeksli eleman varmış } else { // hayır. eşleme tablolarının boyları da tanımlandıkları zaman belirlenmez. writeln(günSıraları["Salı"]). "Cumartesi":5.rehash ancak gerektiği durumda.

tablonun . "green". Bir eşleme tablosu kullanmak. renkler. import std. Ancak. 85.org 124 . renkler[türkçesi]).init niteliği.init niteliğini atamak Not: Bu niteliği ilk defa duyuyorsunuz: her türün . if (türkçesi in renkler) { writefln("İngilizcesi \"%s\"". notlar["emre"] = 90. // int için 0 olur 2. : : : : : "black".. 95.9 Problemler 1. "white".keys). notlara notlar["emre"] şeklinde öğrencinin ismiyle erişme konusunda yardımcı olur. Bu da bazı durumlarda kısıtlayıcıdır.. vs. http://ddili. Örneğin "emre" için 90. } } 31.").8 Örnek Girilen rengin İngilizcesini veren bir program şöyle yazılabilir: import std.string. writeln("Ben bu ".sizeof tablonun referansının büyüklüğüdür (tablonun kullanımıyla doğrudan bir ilgisi yoktur ve çoğu ortamda 8'dir) 31. notlar["emre"] = 85. örneğin: sayı = int. } else { writeln("Onu bilmiyorum.Eşleme Tabloları • . string türkçesi = chomp(readln()). void main() { string[string] renkler = [ "siyah" "beyaz" "kırmızı" "yeşil" "mavi" ]. write("Haydi sorun: "). "blue". Her öğrenci için birden fazla not tutmak istiyor olalım. renkler.stdio. notlarını barındırmak isteyelim. notları tabloya aşağıdaki şekilde yerleştirmek işe yaramaz: int[string] notlar. "red".length.init. " rengin İngilizcelerini öğrendim: ". o türün ilk değeri olarak kullanılan değerdir. Kullanmakta olduğunuz bir eşleme tablosunu nasıl bütünüyle boşaltabilirsiniz? En az üç yöntem düşünülebilir: ◦ elemanları bir döngü içinde teker teker tablodan çıkartmak ◦ boş bir eşleme tablosu atamak ◦ bir öncekine benzer şekilde. // ← Olmaz: öncekinin üstüne yazar Copyright © 2009-2011 Ali Çehreli. Dizilerde olduğu gibi. eşleme tablolarında da her indekse karşılık tek bir eleman bulunabilir.

. çözümler Copyright © 2009-2011 Ali Çehreli. http://ddili. ..org 125 .Eşleme Tabloları Ne yapabilirsiniz? Her öğrenci için birden fazla not tutabilen bir eşleme tablosu tanımlayın.

"Her birisi" anlamına gelir.length niteliğine kadar ilerletmek • i 'yi arttırmak • elemana erişmek Bu adımları ayrı ayrı elle yapmak yerine. } Bunun önemini vurgulamak için.org 126 .length. for döngüsünün her çeşit topluluk için aynı şekilde yazılmadığını hatırlatırım.length. ++i) { writeln(dizi[i]). } Oysa aynı işlem foreach ile eşleme tabloları için de aynı şekilde yazılır: foreach (eleman. for döngüsünün bir dizinin bütün elemanlarına erişmek için nasıl kullanıldığını görmüştük: for (int i = 0.values niteliği üzerinde kurmak gerekir: auto elemanlar = tablo.values. bunların anlamı ve adedi topluluk çeşidine göre değişir Copyright © 2009-2011 Ali Çehreli. http://ddili. Belirli işlemleri bir topluluktaki (veya bir aralıktaki) elemanların her birisi ile yapmayı sağlar. topluluk_veya_aralık) { işlem_bloğu } • topluluk_veya_aralık: döngünün işletileceği elemanları belirler • işlem_bloğu: her elemanla yapılacak işlemleri belirler • isimler: erişilen elemanın ve varsa başka nesnelerin isimlerini belirler. Örneğin bir eşleme tablosunun bütün elemanlarına erişmek için for döngüsünü tablo'nun . ++i) { writeln(elemanlar[i]). } Bu iş için gereken adımları şöyle özetleyebiliriz: • ismi geleneksel olarak i olan bir sayaç tanımlamak (aslında biz önceki örneklerde hep sayaç dedik) • döngüyü topluluğun . i != dizi. } 32. Topluluk elemanlarının tümüyle yapılan işlemler programcılıkta çok yaygındır. i != elemanlar. tablo) { writeln(eleman). dizi) { writeln(eleman). D'nin en kullanışlı deyimlerinden birisidir.foreach Döngüsü 32 foreach Döngüsü foreach . for (int i = 0. seçilen isimler programcıya bağlı olsa da. aynı işi foreach ile çok daha temiz bir şekilde şöyle ifade ederiz: foreach (eleman.1 Söz dizimi foreach üç bölümden oluşur: foreach (isimler.

http://ddili. kod birimlerine erişilir: foreach (sayaç.org 127 . } O örnekteki sayaç isminin seçimi de programcıya bağlıdır.3 Dizilerle kullanımı isimler bölümüne yazılan tek isim. } Eğer iki isim yazılırsa. break de döngünün sonlandırılmasını bildirir. ": ". örneğin alışılmış olan i de yazılabilir.foreach Döngüsü 32. "abcçd") { writeln(sayaç.stride Dizilerle aynı şekilde kullanılır. dizi) { writeln(sayaç. karakter. Tek isim yazılırsa dizginin karakterini ifade eder.2 continue ve break aynı anlamdadır Bu anahtar sözcüklerin ikisi de burada da aynı anlama gelirler: continue döngünün erkenden ilerletilmesini.range modülündeki stride 'dır. 32. eleman.4 Dizgilerle kullanımı ve std. çift isim yazılırsa sayaç ve karakterdir: foreach (karakter. "merhaba") { writeln(karakter).range. ": ". dizi) { writeln(eleman). "merhaba") { writeln(sayaç. dizinin elemanını ifade eder: foreach (eleman. } char ve wchar türlerinin Unicode karakterlerini barındırmaya genel olarak uygun olmadıklarını hatırlayın. kod). kod. std. eleman). ikincisi yine elemanı ifade eder: foreach (sayaç. ": ". } foreach (sayaç. } Örneğin ç'yi oluşturan kodlara ayrı ayrı erişilir: 0: 1: 2: 3: 4: 5: a b c � d UTF kodlamasından bağımsız olarak her tür dizginin foreach ile karakter karakter erişilmesini sağlayan olanak. 32. stride "adım" anlamına gelir ve karakterlerin kaçar kaçar atlanacağı bilgisini de alır: Copyright © 2009-2011 Ali Çehreli. foreach bu türlerle kullanıldığında karakterlere değil. karakter). birincisi sayaçtır.

6 Sayı aralıklarıyla kullanımı Sayı aralıklarını dilimler dersinde görmüştük. iki isim yazılırsa indeks ve eleman değerini ifade eder: foreach (eleman. stride("abcçd". yukarıdaki kullanımda 10 aralığa dahildir.range. 32. 10. foreach 'in topluluk_veya_aralık bölümüne bir sayı aralığı da yazılabilir: foreach (sayı.. foreach 'in belirli bir yapı veya sınıf türü için nasıl işlediğini ancak söz konusu yapı veya sınıfın belgesinden öğrenebiliriz.. O yüzden bu döngüde sayaç yazmadım. } Hatırlarsanız. } Not: Eşleme tablolarında indeksin de herhangi bir türden olabileceğini hatırlayın.. Copyright © 2009-2011 Ali Çehreli. tablo) { writeln(indeks. 32. // . Yapılar ve sınıflar foreach kullanımı desteğini ya opApply() isimli üye işlevleri ya da aralık (range) üye işlevleri aracılığıyla verirler. 1)) { writeln(harf). Nasıl kullanıldığı hakkında burada genel bir şey söylemek olanaksızdır. foreach (harf. bu desteği veren yapı ve sınıf nesneleriyle de kullanılabilir.7 Yapılarla ve sınıflarla kullanımı foreach . http://ddili. tablo) { writeln(eleman). çünkü tamamen o tür tarafından belirlenir. ": ". Bu işlevleri daha sonraki derslerde anlatacağım. } stride kullanıldığında UTF kodlarına değil Unicode karakterlerine erişilir: a b c ç d 32.foreach Döngüsü import std.5 Eşleme tablolarıyla kullanımı Tek isim yazılırsa eleman değerini.org 128 . eleman. 15 değildir. eleman).15) { writeln(sayı). } foreach (indeks.

} } writefln("Sonra: %s". Topluluk elemanlarının yanlışlıkla değiştirilmelerini önlemek amacıyla böyle tasarlandığını düşünebilirsiniz.4.2 3. sayılar) { sayı *= 2. normalde elemanın kendisine değil. Ancak. void main() { double[] sayılar = [ 1. Dizi elemanının kendisinin ifade edilmesi istendiğinde.6 ]. foreach kapsamında sayı 'ya yapılan atamanın etkisi olmadığını gösteriyor: Önce : 1.8 11. } Yeni çıktıda görüldüğü gibi. 3.4 5.9 Topluluğun kendisi değiştirilemez Topluluk elemanlarını ref kullanarak değiştirmekte bir sakınca yoktur. sayı 'da yapılan değişiklik artık elemanın kendisini etkilemektedir. foreach döngüsü kapsamında topluluğun kendi yapısını etkileyecek hiçbir işlem yapılmamalıdır. sayılar).4 5.foreach Döngüsü 32. bir kopyasına erişim sağlar.6 Bunun nedeni. Bu tür işlemler dizinin yapısını değiştirdikleri için ilerlemekte olan foreach döngüsünün işini bozarlar. sayılar) { sayı *= 2. foreach (sayı.4 5. http://ddili.4 6. onun bir kopyası olmasıdır. Copyright © 2009-2011 Ali Çehreli.2 Oradaki ref anahtar sözcüğü.2 3.6 Sonra: 2.8 Elemanın kopyası. asıl elemanın bir takma ismi gibi kullanılmasını sağlamaktadır. O noktadan sonra programın davranışının ne olacağı bilinemez.2 3. Bir dizinin elemanlarının her birisini iki katına çıkartmaya çalışan şu koda bakalım: import std. kendisi değil foreach döngüsü. sayılar). ref anahtar sözcüğü dizideki asıl elemanın etkilenmesini sağlamıştır: Önce : 1. writefln("Önce : %s". isim bir referans olarak tanımlanır: foreach (ref sayı.6 Sonra: 1.2.org 129 . Programın çıktısı. 5. sayı 'nın. 32.stdio. sayı 'nın dizi elemanının kendisi değil. Örneğin diziden eleman silinmemeli veya diziye eleman eklenmemelidir.

. http://ddili. çözümler Copyright © 2009-2011 Ali Çehreli.foreach Döngüsü 32.org 130 . rakamla isminde başka bir eşleme tablosu oluşturun. 7:"yedi". Bu tek yönlüdür: indeks verildiğinde eleman değerini elde ederiz. Eşleme tablolarının indeks değerleri ile eleman değerlerini eşlediklerini görmüştük. ama eleman değeri verildiğinde indeks değerini elde edemeyiz. isimle tablosunun tersi olarak çalışsın: isime karşılık rakam elde edebilelim. O tablodan ve tek bir foreach döngüsünden yararlanarak.10 Problemler 1.. 20:"yirmi" ]. Bu yeni tablo. yazdığımızda çıktı şöyle olsun: 20 . Örneğin writeln(rakamla["yirmi"]). Elinizde hazırda şöyle bir eşleme tablosu olsun: string[int] isimle = [ 1:"bir".

} else if (değer == değer_2) { // değer_2 durumundaki işlemler // . o case 'in altındaki işlemler işletilir. http://ddili. // .. } else { // hiçbir değere uymayan durumdaki işlemler // . başka case'ler . İfadenin değerine eşit olan bir case değeri varsa. default: // hiçbir değere uymayan durumdaki işlemler // . Bu açıdan bakınca...org 131 . parantez içinde bir ifade alır. veya bir while 'da olduğu gibi "böyle olduğu sürece" anlamında değildir.. switch 'in aldığı ifade.. Copyright © 2009-2011 Ali Çehreli. Buradaki kullanımında "durum" anlamına gelen case . } Bu "if else if". Bu işlemler break deyimine kadar devam eder. o ifadenin değerini kendi kapsamı içindeki case 'lerle karşılaştırır ve değere eşit olan ilk case 'in işlemlerini işletir. break . başka 'else if'ler . case 'lerdeki değerlere eşit olup olmadığına bakılır... switch . switch 'teki ifadenin değerinin.. switch 'in denetlediği değerin karşılaştırıldığı durumları belirlemek için kullanılır.. Yani bir if 'te olduğu gibi "eğer böyleyse" anlamında... çoklu koşul gibi çalışan bir deyimdir ve bu açıdan bir "if else if" zincirine benzer.. Yani buradaki koşul.. if (değer == değer_1) { // değer_1 durumundaki işlemler // . break. } Her ne kadar bir koşul gibi çalışsa da.. } // . bir eşitlik karşılaştırmasıdır. bir "if else if" zinciri gibi düşünülebilir: auto değer = ifade. "varsayılan" anlamına gelen default 'un altındaki işlemler işletilir...switch ve case 33 switch ve case switch . switch 'in işinin bitmesini sağlar. bir mantıksal ifade olarak çalışmaz.. Söz dizimini şöyle gösterebiliriz: switch (ifade) { case değer_1: // ifade'nin değer_1'e eşit olduğu durumdaki işlemler // . break. case değer_2: // ifade'nin değer_2'ye eşit olduğu durumdaki işlemler // . Eğer yoksa.. kendisi bir deyim değildir. break.. switch 'in tam eşdeğeri değildir. Nedenlerini aşağıdaki başlıklarda açıklıyorum..

Bu. switch (işlem) { case "toplama": sonuç = birinci + ikinci. Hataları ilerideki bir derste göreceğiz.switch ve case 33. bool. http://ddili. kapsamdaki işlemler sonlanınca bütün if deyiminin işi bitmiş olur.org 132 . break. hemen altlarındaki case 'lere devam edilmesine neden olur: switch (değer) { case 5: writeln("beş"). veya dizgiler kullanılabilir. // burada break yok.ikinci. default: writeln("bilmiyorum"). case "bölme": sonuç = birinci / ikinci. break. break. switch 'te ise ifade değeri olarak ancak tamsayılar. veya dizgi olabilir if 'te eşitlik karşılaştırmasında herhangi bir tür kullanılabilir.2 İfadenin değeri ancak tamsayı. Ama o satırdan sonraki ilk break 'e gelene kadar "dört" de yazdırıldığı için çıktıda ikisi de yer alır: beş dört Bir sonraki case 'e geçilmesini özellikle istemiyorsanız break 'leri unutmayın. switch 'te ise. } değer 5 olduğunda case 5 satırının altına gidilir ve orada "beş" yazdırılır. hiçbir case 'e uymayan durumda bir hata atmaktadır. Doğrusu. bool. } Not: Yukarıdaki kod. break. case "çıkarma": sonuç = birinci . Copyright © 2009-2011 Ali Çehreli. Her zaman için break 'leri hatırlayın. böyle bir program davranışı için normalde bir neden yoktur.1 case 'in kapsamı yoktur if koşulunun kapsamı olduğu için. break. break 'i olmayan case 'lerden. break. case "çarpma": sonuç = birinci * ikinci.. case 4: writeln("dört").. default: throw new Exception("Geçersiz işlem"). ifadenin değerine eşit bir case bulunduğu zaman. 33. programın işleyişi o case 'e atlar ve bir break ile karşılaşılana kadar devam eder.

} Oysa case değerlerinin derleme zamanında bilinmeleri gerekir. break.5 Ayrık değerler virgülle bildirilir Yukarıdaki oyunda [2.4 Aralık değerleri Belirli bir değer aralığındaki durumlar case 'ler arasına .5] aralığındaki değerlerinde berabere kalınmaktadır. break.. karakterleri yerleştirilerek belirtilir: switch (zarDeğeri) { case 1: writeln("Sen kazandın"). switch 'in ifadesi çalışma zamanında hesaplanır. 4: writeln("Berabere"). Örneğin bu kodda. birisi kullanıcı tarafından girilmiş ve diğeri rasgele seçilmiş iki değerin eşitliği karşılaştırılıyor: if (tahminEdilenSayı == tutulanSayı) { writeln("Bildiniz!"). break. ama case değerleri derleme zamanında bilinmelidir. eşitliğin her iki tarafındaki değer de programın çalışması sırasında belirlenmiş olabilir.6 final switch deyimi Bu deyim de switch gibidir. } Yukarıda zarla oynanan bir oyunda zarın [2.5] aralığında değil de.org 133 . case 6: writeln("Ben kazandım"). default: // Aslında bu durumun hiç gerçekleşmemesi gerekir! // Çünkü yukarıdaki durumlar zarın bütün değerlerini // kapsamaktadırlar.switch ve case Her ne kadar ifade türü olarak bool da kullanılabiliyor olsa da. break. 33.3 case değerleri derleme zamanında bilinmelidir if deyimlerindeki eşitlik koşullarında. 33. 2 ve 4 değerleri geldiğinde berabere kalındığını varsayalım. ama bazı kısıtlamaları vardır: Copyright © 2009-2011 Ali Çehreli. 33. 33. false ve true diye iki değeri olan bu tür için çoğu durumda if 'in veya ?: üçlü işlecinin daha uygun olduğunu düşünebilirsiniz. break. case 5: writeln("Berabere"). Öyle durumlarda case 'in değerlerinin aralarına virgül yazılır: case 2. case 2: . http://ddili..

. 2.. Program. sonra da sayıları double olarak alsın ve işleme göre hesap yapsın. double ikinci. Örneğin işlem "topla" olarak ve sayılar "5 7" olarak girildiğinde ekrana 12 yazsın. türün bütün değerlerinin case 'ler tarafından kapsanmış olmaları gerekir (enum 'ları çok yakında öğreneceksiniz) final switch (zarDeğeri) { case 1: writeln("Sen kazandın"). switch yerine bir "if else" daha uygun olabilir. } else { // . break. çözümler Copyright © 2009-2011 Ali Çehreli. bir ifadenin derleme zamanında bilinen değerlerle karşılaştırıldığı durumlarda kullanışlıdır. 6: writeln("Ben kazandım"). } 33. Hesap makinesini geliştirin ve "topla" gibi sözlü işlemler yanında "+" gibi simgeleri de desteklemesini sağlayın: işlem dizgisi olarak "+" girildiğinde de aynı şekilde çalışsın. } Genel bir kural olarak. 5.org 134 ....8 Problemler 1. 3. case 3. Eğer karşılaştırılacak değer yalnızca iki taneyse. switch 'i üç veya daha çok durum için düşünebilirsiniz. Girişi şu şekilde okuyabilirsiniz: string işlem. &birinci. Kullanıcıdan önce işlemi string olarak. Yukarıdaki örneklerden birisindeki gibi bir hesap makinesi yapın. .7 Ne zaman kullanmalı Yukarıda anlatılanlardan anlaşıldığı gibi. Bu konuda da karar sizin! :o) 33. break. readf(" %s %s".switch ve case • default bölümü bulunamaz. switch . işlem = chomp(readln()).. break. // . http://ddili. Örneğin yazı/tura gibi bir sonuçta if deyimi yeterlidir: if (yazıTuraSonucu == yazı) { // .. case 2. &ikinci). double birinci. bilinmeyen bir işlem girildiğinde hata atsın.. 4: writeln("Berabere"). zaten bu durum bazı koşullarda anlamsızdır: örneğin zarın değerlerinin altısının da işlemlerinin belirli olduğu bir durumda default bölüme gerek yoktur • case 'lerde aralıklı değerler kullanılamaz (virgülle gösterilen ayrık değerler olur) • eğer ifade bir enum türüyse.

ve 4 değerlerine sihirli sabit denir. "numaralandırmak" anlamına gelen "enumerate"in kısaltılmışıdır. http://ddili. işte bu tür sabitlere isimler vermeyi ve bu sayede kodun okunurluğunu arttırmayı sağlar.ikinci. Örneğin yukarıdaki kodda 1'in toplama işlemi. anlamlarına geldiklerini ancak kapsamlarındaki kodları okuduktan sonra anlayabiliyoruz. İsimli sabit değerler üretmek için kullanılır.Toplama) { sonuç = birinci + ikinci. Aynı kod. 2. Sihirli sabitler programcılıkta kaçınılan bir durumdur. enum olanağı. } Artık 1 gibi anlamı açık olmayan bir değer yerine. çünkü iyi yazılmış kodun en önemli niteliklerinden olan okunurluğunu azaltırlar. } else if (işlem == İşlem.Çarpma) { sonuç = birinci * ikinci. } else if (işlem == 4) { sonuç = birinci / ikinci. 3. } else if (işlem == İşlem. Bölme } Copyright © 2009-2011 Ali Çehreli. 34. 3. Bundan sonraki derslerdeki kodlarda sihirli sabitler yerine hep isimli sabitler kullanacağım.org 135 .ikinci. İşlem. vs. } else if (işlem == İşlem. Bu durumda şanslıyız.Toplama gibi isimli bir değer kullanılmaktadır. çünkü her kapsamda yalnızca tek satır var. } else if (işlem == 2) { sonuç = birinci . Çıkarma. Yukarıdaki 1. } else if (işlem == 3) { sonuç = birinci * ikinci. 2'nin çıkarma işlemi. 2. Çarpma.enum 34 enum enum . Kodu okuyan birisinin onların ne anlama geldiklerini bir bakışta anlaması olanaksızdır. ve 4 değerlerine karşılık gelen enum tanımı şöyle yazılır: enum İşlem { Toplama = 1.1 Sihirli sabitler (kullanmayın) Tamsayılar ve Aritmetik İşlemler bölümünün problem çözümlerinden birisinde şöyle bir koşul kullanmıştık: if (işlem == 1) { sonuç = birinci + ikinci.Bölme) { sonuç = birinci / ikinci. enum değerleriyle yazıldığında her bir if koşulunun hangi işlemle ilgili olduğu açıkça anlaşılır: if (işlem == İşlem. daha karmaşık kodlarda kodu anlamak çok güç olabilir. } O kod parçasındaki 1.Çıkarma) { sonuç = birinci .

*/ } enum anahtar sözcüğünden sonra. /* vs.ç.) Belirlediğimiz değerden sonrakilerin değerleri de yine derleyici tarafından birer birer arttırılarak verilir: enum Deneme { a. Emekli } Bu değerler. programcı özellikle belirtmediği sürece 0'dan başlar ve her isimli değer için bir tane arttırılır. c. Deneme. Tura } enum OyunKağıdıRengi { Maça. ' '. Bu yeni tür de başka türler gibi değişken tanımlamak için kullanılabilir: ParaAtışıSonucu sonuç. 1 100 224 Copyright © 2009-2011 Ali Çehreli. Yazı: 0 Tura: 1 Normalde 0'dan başlayan bu değerleri istediğimiz noktadan itibaren = işareti ile kendimiz belirleyebiliriz. yine de arka planda birer int değeridirler. f = 222.. bütün değerlerin toplu olarak ne anlama geldiğini belirten bir tür ismi verilir. Deneme. ait oldukları türün ismiyle birlikte ve ondan bir nokta ile ayrılarak yazılırlar: if (sonuç == ParaAtışıSonucu. Bir kaç örnek: enum ParaAtışıSonucu { Yazı. ParaAtışıSonucu. isimler halinde enum kapsamı içinde sıralanırlar. (Not: Aslında int yerine başka bir tür kullanmanın da yolu vardır. Karo. Kupa. (Yukarıda bunu İşlem 'in Toplama değerinde de görmüştük. ğ } writeln(Deneme. ç = 100.2 Söz dizimi enum . Örneğin yukarıda tanımlanan ParaAtışıSonucu 'nun iki değerini yazdırdığımızda 0 ve 1 değerlerine sahip olduklarını görürüz: writeln("Yazı: ". http://ddili.Tura). Yani her ne kadar Yazı ve Tura gibi isimleri olsa da. normalde arka planda yine de int olarak kullanılırlar. Sinek } enum BiletTürü { Normal.enum 34. enum türlerinin değerleri.Yazı) { // . ' '. Değerİsmi_2.. Öğrenci. en basit olarak şu söz dizimine sahiptir: enum Türİsmi { Değerİsmi_1. e. b. kod içinde sabit olarak belirtilecekleri zaman. Örneğin Yazı ve Tura . d.b. ParaAtışıSonucu. artık ParaAtışıSonucu diye tanımlanmış olan yeni bir türün değerleridir.) Bu değerler. aynı zamanda yeni bir türün parçaları haline de gelirler.org 136 . g.Yazı). writeln("Tura: ".ğ). } 34. Bütün olası değerler. Çocuk.3 Asıl değerleri enum türlerin değerleri.

Bunları bir döngüde kullanarak bütün değerleri sırayla gezebiliriz: enum OyunKağıdıRengi { Maça. enum . enum türünün sırasıyla en küçük ve en büyük değerleridir. ": ". http://ddili. enum kullanıldı diye sabit değerlere ayrıca tür ismi vermek doğal olmayabilir.max.conv. Bunu yukarıda Yazı ve Tura için 0 ve 1 olarak görmüştük. çıkışa normalde tamsayı olarak yazdırılırlar.conv modülünün olanaklarından yararlanarak enum değerleri dizgi olarak yazdırmak da mümkündür.min ve . Karo. O modüldeki to şablonu. Böyle tek bir sabitin tanımlanmasında ayrıca enum türü belirlemeye gerek yoktur.min.txt".enum 34.Yazı olduğunu varsayarsak. 34. Şimdilik to!string(atışSonucu) kullanımını. renk).org 137 . çıktısı: Yazı Not: Şablonları daha sonra göreceğiz. Phobos'un std.. writeln(to!string(atışSonucu)). // . Artık o sabiti hesaplarda ismiyle kullanabiliriz: toplamSaniye = günAdedi * günBaşınaSaniye. Örneğin tek amacımızın 24 saatteki toplam saniye sayısını tutan bir sabit tanımlamak olduğunu düşünelim. Böyle durumlarda tür ismi ve enum kapsam parantezleri yazılmayabilir: enum günBaşınaSaniye = 60 * 60 * 24. Ancak.6 Nitelikleri . enum değerleri koddaki isimleriyle de yazdırabilir: import std. Sinek } for (auto renk = OyunKağıdıRengi. 34. Copyright © 2009-2011 Ali Çehreli.max nitelikleri. "atışSonucu'nu string'e dönüştür" olarak düşünebilirsiniz. Oradaki atışSonucu 'nun değerinin ParaAtışıSonucu.5 Dizgi karşılıkları enum 'lar arka planda tamsayı oldukları için. Ek olarak. Kupa. ++renk) { } writeln(to!string(renk).4 Türsüz enum 'lar Sihirli sabitlerden kurtulmanın önemli olduğunu ve bu amaçla enum 'lardan yararlanabileceğimizi gördük. renk <= OyunKağıdıRengi. Örneğin bir string hazır değeri: enum dosyaİsmi = "liste. her türden hazır değeri tanımlamak için de kullanılabilir..

enum Maça: 0 Kupa: 1 Karo: 2 Sinek: 3 34. Not: Tür dönüşümlerini de ileride göreceğiz. Bunun tersi doğru değildir: OyunKağıdıRengi renk = 1. // ← derleme HATASI Bunun nedeni. bir enum değer otomatik olarak int 'e dönüşür.7 int 'ten dönüştürmek Yukarıdaki yazdırma örneklerinde görüldüğü gibi. http://ddili. işlemi bir menüden seçtirsin ve girilen iki değere o işlemi uygulasın. // ← geçerli bir değer olmadığı için // anlamsız olurdu Geçerli olduğunu bildiğimiz bir değeri bir enum değerine dönüştürmek istiyorsak..8 Problem • Tamsayılar ve Aritmetik İşlemler dersinin problemlerindeki hesap makinesini değiştirin: Dört işlemi destekleyen basit bir hesap makinesi. // şimdi Kupa 34.org 138 . bunu açıkça bir tür dönüşümü olarak yazmamız gerekir: renk = cast(OyunKağıdıRengi)1. çözüm Copyright © 2009-2011 Ali Çehreli.. enum değerlerden anlasın ◦ "if else if" zinciri yerine switch kullansın . Programı bu sefer şu farklarla yazın: ◦ int yerine double kullansın ◦ işlemi sihirli sabitlerden değil. enum değişkenlerine yanlışlıkla geçersiz değerlerin atanmasını önlemektir: renk = 100.

vs. Usta programcıların yazdıkları işlevler kısa ve öz olur. işlevler de program davranışlarının yapı taşlarıdır. bütün türlerin yapı taşları iseler. Bir araya getirerek yeni isim verme kavramını günlük hayattan tanıyoruz. ustalık yolunda ilerlemenin önemli adımlarındandır. deneme programları gibi hiçbir karmaşıklığı olmayan çok basit programlar olabilirler. Bunun tersi de doğrudur: kısa ve öz işlevler yazmaya çalışmak. Örnek olarak kullanıcıya bir menü gösteren şu satırlara bakalım: writeln(" writeln(" writeln(" writeln(" writeln(" 0 1 2 3 4 Çıkış"). Çarpma"). çok yararlı. Bir araya getirilen ifade ve deyimlere toplu olarak yeni bir isim verilir ve o işlemlerin hepsi birden bu isimle işletilir. programcılık konusunda gelişmenize yardım edecektir. Şimdiye kadar yazdığımız biçimdeki programlar. Yine de. http://ddili. En ufak bir karmaşıklığı bulunan bir işi işlev kullanmadan yazmaya çalışmak çok zordur. Daha hepsini bitirmedik ama D'nin programlarda çok kullanılan.) ateşi söndür Daha sonra daha da ileri gidilebilir ve bütün o adımları içeren tek bir ifade de kullanılabilir: • yağda yumurta yap (bütün adımlar) İşlevlerin bundan farkı yoktur: Yaptıkları işlerin hepsine birden genel bir isim verilebilen adımlar tek bir işlev olarak tanımlanırlar. yumurtayı çıkart) ateşi aç yumurtayı pişir (tavayı ateşe koy. yağı. ifade ve deyimleri bir araya getiren olanaklardır. hiçbirisi büyük programlar yazmak için yeterli değildir. birbiriyle ilişkili adımların bazılarına tek bir isim verilebilir: • • • • malzemeleri hazırla (tavayı. Copyright © 2009-2011 Ali Çehreli. Çıkarma"). Nasıl temel türler.org 139 . İşlemleri oluşturan alt adımları görmeye çalışmak ve o adımları küçük işlevler halinde yazmak.İşlevler 35 İşlevler Eğlence yeni başlıyor! :o) İşlevler gerçek programların temel taşlarıdır. ve ortaya çıkan program da hataya açık olur. Örneğin yağda yumurta yapma işini şu adımlarla tarif edebiliriz: • • • • • • • • • tavayı çıkart yağı çıkart yumurtayı çıkart ateşi aç tavayı ateşe koy tava ısınınca yağı içine at yağ eriyince yumurtayı içine kır yumurtanın beyazı pişince tavayı ateşten al ateşi söndür O kadar ayrıntıya girmek zamanla gereksiz ve içinden çıkılmaz bir hâl alacağı için. İşlevler. ve çok önemli olanaklarını gördük. İşlevlerin ustalıkla da ilgisi vardır. Toplama"). Bundan önceki derslerde temel deyimler ve ifadeler öğrendik. Bölme").

} Artık o işlevi main içinden kısaca ismiyle işletebiliriz: void main() { menüyüGöster(). İzlenmesi gereken adımlar aslında bu durumda da aynıdır.. Daha önce dörde indirgediğimiz adımları beş yumurtaya uygun olarak şöyle değiştirebiliriz: • • • • malzemeleri beş yumurta için hazırla ateşi aç yumurtaları pişir ateşi söndür Teke indirgediğimiz adım da şöyle değişir: • yağda beş yumurta yap Yani. parametre listesinde virgüllerle ayrılarak bildirilirler. http://ddili. writeln(" 4 Bölme"). writeln(" 3 Çarpma"). 35.1 Parametre İşlevlerin güçlü yanlarından birisi. ve bu sefer beş yumurta yapmak isteyelim. Daha önceki menüyüGöster işlevinde parametre parantezini boş olarak tanımlamıştık.org 140 . duruma göre değişen bir seçenek olmasını istesek. diğer işlemler . bunu bir parametreyle sağlayabiliriz. menüyüGöster ile main 'in tanımlarındaki benzerliğe bakarak main 'in de bir işlev olduğunu görebilirsiniz. İşlevlerin davranışları da benzer şekilde ayarlanabilir. Yine yumurta örneğine dönelim.. işlevin isminden hemen sonra yazılan parantezin içidir. D programlarının işleyişi bu işlevle başlar ve programcının istediği şekilde başka işlevlere dallanır.. yaptıkları işlerin belirli ölçüde ayarlanabiliyor olmasından gelir. Menüdeki ilk seçeneğinin hep "Çıkış" olması yerine. çünkü o işlev her zaman için aynı menüyü göstermekteydi.İşlevler Onların hepsine birden menüyüGöster gibi bir isim verilebileceği için onları bir işlev olarak şu şekilde bir araya getirebiliriz: void menüyüGöster() { writeln(" 0 Çıkış"). tek farkları. yumurta sayısındadır. Örneğin ilk seçeneği bazı durumlarda "Geri Dön" olarak yazdırmak için bu bilgiyi bir parametre olarak tanımlayabiliriz: void menüyüGöster(string ilkSeçenek) { Copyright © 2009-2011 Ali Çehreli. D programlarının ana işlevidir. Parametreler.. writeln(" 1 Toplama"). İsmi İngilizce'de "ana işlev" kullanımındaki "ana" anlamına gelen main . Parametre listesi. writeln(" 2 Çıkarma"). } // . İşlevlerin işlerini bu şekilde etkileyen bilgilere parametre denir. temelde aynı olan yumurta pişirme işiyle ilgili bir bilgiyi ek olarak belirtmemiz gerekir: "beş yumurta çıkart" veya "beş yumurta yap" gibi.

writeln(' '. ilkNumara + 2. int ilkNumara) { writeln(' '. ilkNumara. İşlev çağrısının söz dizimi şöyledir: işlevin_ismi(parametre_değerleri) İşini yaparken kullanması için işleve verilen bilgilere parametre değeri denir.2 Çağırmak İşlevin işini yapması için başlatılmasına işlevin çağrılması denir. writeln(' '. duruma göre değişik bir değerle başlamasını istiyor olalım. Tek yapmamız gereken. char[] ile string uyumlu olmadıkları için derleme hatasına neden olur: char[] birSeçenek = "Kare Kök Al". ilkNumara + 3. 35. " Toplama"). ilkNumara + 4. Bu durumda başlangıç numarasını da parametre olarak verebiliriz. ilkNumara + 1. writeln(' '. ilkSeçenek). 100). menüyüGöster 'in tanımında parametrenin türünü char[] olarak belirlediğinizde de işlevi "Çıkış" gibi bir string 'le çağıramazsınız. Yukarıdaki kullanımda writeln 'e verilen parametre değerleri. Örneğin şu kod. Çıkarma"). http://ddili. " Bölme"). Çarpma"). bu işlev char[] türünde dizgilerle kullanılamaz. ilkSeçenek parametresi bu örnekte bir dizgi olduğu için türünü de string olarak belirledik. Copyright © 2009-2011 Ali Çehreli. yazdırmasını istediğimiz değerlerle şöyle çağırıyorduk: writeln("Merhaba ". immutable ile ilgili olan bu önemli konuyu bir sonraki derste açıklayacağım. " Çarpma"). öğrenciİsmi). writeln(' '. menüyüGöster(birSeçenek). "Merhaba" ve öğrenciİsmi dizgileridir. parametre değerini parantez içinde belirtmektir: menüyüGöster("Çıkış"). } O işleve hangi numarayla başlayacağını artık biz bildirebiliriz: menüyüGöster("Geri Dön". " Çıkarma"). Toplama").İşlevler } writeln(" writeln(" writeln(" writeln(" writeln(" 0 1 2 3 4 ". ' '. Not: Burada parametrenin türüyle ilgili bir sorunla karşılaşabilirsiniz: yukarıda yazıldığı haliyle. ilkSeçenek). menüyüGöster("Geri Dön"). İki parametreyi ayırmak için aralarına virgül yazarak: void menüyüGöster(string ilkSeçenek.org 141 . Bölme"). Örneğin şimdiye kadar hep kullandığımız writeln 'i. Bu işlevi artık değişik dizgilerle işleterek menünün ilk satırının farklı olmasını sağlayabiliriz. // ← derleme HATASI Öte yandan. Biraz daha ileri gidelim ve seçenek numaralarının hep 0 ile değil.

vs.org 142 . yalnızca değer üretirler. kendisine verilen bir Öğrenci nesnesini bir öğrenciler listesine ekleyen bir işlevin etkisi. Örneğin çıkışa menü yazdıran menüyüGöster işlevi çıkışı etkilemektedir. hem de bu derste iş yapmaktan söz ettim. 12). gibi kendisine verilen bilgiyi bir araya getirerek bir Öğrenci nesnesi oluşturan bir işlevin de bir nesne ürettiği söylenir. Örneğin topla(5. dönüş değerinin de türü vardır. ya da ikisi birden.. Onun da ürettiği bir değer yoktur. sanki o değer işlev çağrısının yerine yazılmış gibi. Bu tür. iki anlama gelebilir: • Yan etki oluşturmak: Bazı işlemlerin yalnızca yan etkileri vardır. şu iki satır birbirinin eşdeğeridir: writeln("Toplam: ". dönüş türü olarak int yazılır: int topla(int birinci. topla(5. işlevin dönüş değeri denir. oluşturduğu bir değer yoktur. iş yaptıklarını söyledim. Örneğin toplama işleminin sonucunu veren bir işlev. isim. adres.. işlevin işini bitirdikten sonra bize geri dönmesi gibi bir düşünceden türemiştir. yazdırması için writeln 'e parametre olarak verilir. Her değerin olduğu gibi. Örneğin girişten okuduğu sayıların toplamını hesaplayan bir işlev. 35.4 Dönüş değeri Değer üreten bir işlevin ürettiği değere. işlevlerin. işlevin isminden önce yazılır. hem toplamın sonucunu üretmektedir. writeln çağrılmadan önce. • Hem değer üretmek. Örneğin iki tane int değeri toplayan bir işlev. listenin büyümesidir. topla(5. ifadelerin. Genel olarak. Başka bir örnek olarak. hem yan etki oluşturmak: Bazı işlemler hem değer üretirler. yapılan işlemler .. 7) çağrısının değer olarak 12 ürettiğini düşünürsek. ya yan etki veya etkileri vardır. İşlevi "çağırırız" ve "döndürdüğü" değeri kullanırız. hem de yan etkileri vardır.. 7)). Programın iş yapması. Program adımlarının.İşlevler 35. writeln("Toplam: ". int ikinci) { // . } İşlevin döndürdüğü değer. toplanan değerlerin toplamını üretir. Her işlev bu üç gruptan birisine girer: ya yalnızca değer üretir. eğer sonuçta yine int türünde bir değer üretiyorsa. 7) işlevi çağrılır ve onun döndürdüğü değer olan 12. Bu terim. hem de içinden karakterler çıkarttığı için girişi etkilemektedir. • Değer üretmek: Bazı işlemler yalnızca değer üretirler.. Bu tür işlemlerin ayrıca yan etkileri yoktur. Not: Ne değer üreten ne de yan etkisi olan bir işlevin programda bir "iş yaptığını" söyleyemeyiz. programın durumunda hiçbir değişikliğe neden olmazlar. onun yerine geçer. Başka bir örnek olarak. http://ddili. Bu sayede işlevlerin değerlerini başka işlevlere parametre olarak verebilir ve daha karmaşık ifadeler oluşturabiliriz: Copyright © 2009-2011 Ali Çehreli..3 İş yapmak: yan etki oluşturmak veya değer üretmek Hem daha önceki derslerde. programın durumunda bir değişikliğe neden olan bir işlemin yan etkisinin olduğu söylenir.

org 143 . isimleri topla 'da ve menüyüGöster 'de olduğu gibi ikinci tekil şahıs emir kipinde seçmektir. Bunu daha sonraki bir derste anlatacağım. dönüş türü olarak "boşluk. işlevin dönüş değeri olarak o return 'ün döndürdüğü değer kullanılır: int karmaşıkHesap(int birParametre.5 return İşlevin ürettiği değer. } } return birParametre * başkaParametre. yani yalnızca değer üreten işlevlere içinde eylem bulunmayan isimler de seçilebilir. kişiSayısı()))). yokluk" anlamına gelen void yazılır. iki parametresi birbirlerine eşitse 0 değerini..7 İşlevin ismi İşlevin ismi. 35. O işlev. gereken işlemleri ve hesapları yaparak dönüş değerini üretir ve en son olarak return ile döndürür.6 void işlevler Eğer işlev değer üretmeyen bir işlevse. İfade ve deyimlere bağlı olarak önce hangi return işletilirse. http://ddili. Öte yandan hiçbir yan etkileri olmayan. 35. O yüzden main 'in ve menüyüGöster 'in dönüş türlerini void olarak yazdık. İşlevlerde birden fazla return anahtar sözcüğü kullanılabilir. Copyright © 2009-2011 Ali Çehreli. ve en sonunda da topla 'nın dönüş değeri writeln 'e parametre olarak verilmektedir. Örneğin şu andaki hava sıcaklığını veren bir işlev için havaSıcaklığınıVer yerine havaSıcaklığı gibi bir ismin daha uygun olduğunu düşünebilirsiniz. 35. İşlevlere isim verirken izlenen bir kural.İşlevler writeln("Sonuç: ". int başkaParametre) { if (birParametre == başkaParametre) { return 0. int ikinci) { int toplam = birinci + ikinci. Yani toplam 'da ve menü 'de olduğu gibi isim halinde değil. } İşlev. programcı tarafından işlevin yaptığı işi açıklayacak şekilde seçilmelidir. "döndür" anlamına gelen return anahtar sözcüğü ile bildirilir: int topla(int birinci. değilse iki parametrenin çarpımını döndürür. topla(5. böl 'ün dönüş değeri topla 'ya. şimdiye kadar gördüğümüz kadarıyla ikisi de değer üretmeyen işlevlerdir. return toplam.. O örnekte kişiSayısı 'nın dönüş değeri böl 'e. böl(100. Not: main aslında int de döndürebilir. veya menüyü gösteren işleve menüyüGöster dedik. Örneğin iki sayıyı toplayan işlevin ismini topla olarak seçtik. Böylece işlevin bir eylemde bulunduğu isminden anlaşılır. İşlevin işleyişi de o noktada sona erer ve işlevden dönülmüş olur.

readf(" %s". int adet. Kod tekrarının sakıncalarından birisi. ve aynı hatanın her kopyada giderilmesinin gerekmesidir. vs. aynı işlemlerin aynı şekilde kodlanmaları şeklinde ortaya çıkabilir. Oysa. // Sayıları oku for (int i = 0. sayı. write("Kaç sayı gireceksiniz? "). write("Sayı ". foreach (i. 35. http://ddili.. sayı). i. } } Kod tekrarını görüyor musunuz? Diziyi yazdırmak için kullanılan son iki foreach döngüsü birbirinin aynısı. i. girişten aldığı sayıları önce girişten geldikleri sırada.İşlevler İşlev.stdio. kod tekrarıdır.8 Kod tekrarı (yapmayın) Programcılıkta kaçınılması gereken bir eylem. Bir örnek olarak. tekrarlanan kod tek bir işlev içinde bulunuyor olsa. diziYazdır ismiyle bir işlev tanımlasak ve yazdırmasını istediğimiz diziyi de parametre olarak versek. i != adet. Yukarıda işlevlerin ustalıkla ilgili olduklarına değinmiştim. "? "). sayılar) { writefln("%3d:%5d". ++i) { int sayı. } sayılar ~= sayı. tekrarlanan işlemlerdeki olası hataların bütün kopyalarda da bulunması. Usta programcılar koddaki işlemler arasındaki benzerlikleri yakalamaya ve kod tekrarını ortadan kaldırmaya çalışırlar. İşe yarar. hatayı yalnızca bir kere gidermek yeter. } sayılar. bu kod tekrarını ortadan kaldırmış oluruz: void diziYazdır(int[] dizi) { Copyright © 2009-2011 Ali Çehreli.sort. sayı. nesne. Bazen de farkında olmadan. &adet). sayı). sayılar) { writefln("%3d:%5d". // Diziyi çıkışa yazdır writeln("Sıralamadan önce:").org 144 .. // Diziyi çıkışa yazdır writeln("Sıraladıktan sonra:"). aynı işi yapan işlemlerin programda birden fazla yerde tekrarlanması anlamına gelir. foreach (i. yeterince kısa. Kod tekrarı. void main() { int[] sayılar. Bu tekrar bazen bilinçli olarak satırların bir yerden başka bir yere kopyalanması ile yapılabilir. &sayı). ve programdaki diğer isimlerle tutarlı olan isimler bulmak bazen yaratıcılık gerektirir. isim seçimlerinin programcılığın sanat tarafında kaldığını düşünebilirsiniz. değişken. i. sonra da sıralanmış olarak yazdıran şu programa bakalım: import std. readf(" %s".

parametrenin ismi olarak sayılar yerine ondan daha genel olan dizi ismini seçtik. foreach (i. &adet). belki bir grup insanın yaşlarıdır. Yazdırılan dizgi farklı olsa da işlem aynıdır. ++i) { int sayı. bu işlev bağlamında dizi yazdırmak dışında bir şey bilmiyor olduğumuzdur. } Dikkat ederseniz. ":"). eleman). i. } } Copyright © 2009-2011 Ali Çehreli. readf(" %s". sayılar. Eğer başlığı da diziYazdır 'a parametre olarak verirsek. // Diziyi çıkışa yazdır writeln("Sıraladıktan sonra:"). eleman). Bunun nedeni. int[] dizi) { writeln(başlık. } sayılar ~= sayı. eleman. eleman. başlığı yazdırma tekrarından da kurtulmuş oluruz. diziYazdır(sayılar). } } void main() { int[] sayılar. dizi) { writefln("%3s:%5s".stdio. } İşimiz bitmedi: iki diziYazdır çağrısından önce birer de başlık yazdırılıyor. Dizinin elemanlarının ne olduklarından bu işlev içinde haberimiz yoktur. Programın yalnızca değişen bölümlerini gösteriyorum: void diziYazdır(string başlık. write("Sayı ".sort. diziYazdır(sayılar). eleman. // Diziyi çıkışa yazdır writeln("Sıralamadan önce:"). readf(" %s". int adet. dizi) { writefln("%3s:%5s". i. ancak dizi ve eleman gibi genel isimler kullanabiliyoruz. i != adet. eleman). &sayı). http://ddili.. Ne olduklarını diziYazdır işlevi içinde bilemediğimiz için. i.. Şimdi kod biraz daha düzenli bir hale gelir: import std. "? "). Dizideki int elemanların ne anlama geldiklerini ancak bu işlevi çağıran kapsam bilir: belki öğrenci kayıt numaralarıdır. write("Kaç sayı gireceksiniz? ").İşlevler } foreach (i. i. dizi) { writefln("%3s:%5s". // Sayıları oku for (int i = 0.org 145 . void diziYazdır(int[] dizi) { foreach (i. belki bir şifrenin parçalarıdır.

int sayı.conv modülündeki to!string dönüşüm işlevini ve dizgi birleştirmek için kullanılan ~ işlecini hatırlayarak: int sayı = sayıOku("Sayı " ~ to!string(i)). Şimdi programın son satırları şöyle kısaltılabilir: diziYazdır("Sıralamadan önce". write("Kaç sayı gireceksiniz? "). adet 'i işlevin dönüş değeriyle ilkleyebiliriz: int adet = sayıOku("Kaç sayı gireceksiniz"). return sayı. "? "). diziYazdır("Sıraladıktan sonra". çünkü döngü sayacı olan i de bu mesajın bir parçası. // .. write("Sayı ". } Bu işlevi çağırarak adet 'in değerini okumak kolay.. sayılar. write(mesaj.. İşlemlere diziYazdır diye açıklayıcı bir isim verdiğimiz için. // Diziyi çıkışa yazdır diziYazdır("Sıraladıktan sonra". sayılar). readf(" %s".sort. std. sayılar). sayılar). Bu sefer bu işlevin girişten okuduğu değeri döndürmesi gerektiğine dikkat edin: int sayıOku(string mesaj) { int sayı.. Tek farkları. readf(" %s". "? "). &sayı). ayrıca "Diziyi çıkışa yazdır" gibi bir açıklamaya da gerek kalmamış oluyor. kod çok daha temiz bir hale gelir. Böylece döngü içindeki satırlar da azalmış olur: Copyright © 2009-2011 Ali Çehreli. // Diziyi çıkışa yazdır diziYazdır("Sıralamadan önce".org 146 . i. sayı 'nın for içinde tek bir yerde kullanıldığını görerek sayı 'nın tanımını da tamamen kaldırabilir ve onun kullanıldığı tek yerde doğrudan sayıOku işlevini çağırabiliriz.İşlevler // .. &sayı). kullanıcıya verilen mesaj: int adet. sayılar).. &adet). Fakat sayı 'yı okumak o kadar kolay değil.. sayıOku diye bir işlev yazarsak ve kullanıcıya gösterilecek mesajı bir parametre olarak alırsak.. Bu programda bir kod tekrarı daha var: girişten adet ve sayı için aynı şekilde tamsayı okunuyor. readf(" %s". http://ddili. Bu işlemin diziYazdır 'dan önceki açıklama satırlarını da gereksiz hale getirdiğini görebiliriz. // .

Böylece "Sayıları oku" açıklaması da ortadan kalkacak.İşlevler for (int i = 0. foreach (i. &sayı). } Programın bu halini ilk haliyle karşılaştırın. girişten tamsayı okumak için her seferinde 3 satır kod yazıyorduk. } } return sayılar. eleman. } } int sayıOku(string mesaj) { int sayı. Şimdi ise sayıOku 'yu çağırarak her noktadaki kod satırı sayısını 1'e indirmiş olduk. Copyright © 2009-2011 Ali Çehreli. http://ddili. sayılar). ++i) { sayılar ~= sayıOku("Sayı " ~ to!string(i)). i. Oysa ilk halinde programın ne yaptığını ancak kodları ve açıklamalarını okuyarak anlamak zorunda kalıyorduk. void diziYazdır(string başlık. İşlevler aslında kodu küçültürler. readf(" %s". import std.stdio. diziYazdır("Sıraladıktan sonra". ama bunu çok kısa olan bu programda göremiyoruz. write(mesaj. ama değer olarak bütün diziyi üretirse ismi ile de uyumlu bir kullanımı olur. ":"). int adet = sayıOku("Kaç sayı gireceksiniz"). Yeni programda main işlevi içinde programın ana adımları açık bir şekilde anlaşılmaktadır. i != adet. "? "). diziYazdır("Sıralamadan önce". i != adet. eleman). Programın son halinde daha fazla satır bulunuyor olması sizi yanıltmasın. ++i) { sayılar ~= sayıOku("Sayı " ~ to!string(i)). for döngüsü içindeki sayı 'nın tanımını da tamamen kaldırabildik. Böylece bütün program son olarak şöyle yazılabilir: import std. } Ben bu programda son bir değişiklik daha yapacağım ve sayıların okunmasıyla ilgili bütün işlemleri tek bir işleve taşıyacağım. for (int i = 0.conv. int[] dizi) { writeln(başlık. void main() { int[] sayılar = sayılarıOku(). Hatta. dizi) { writefln("%3s:%5s". } int[] sayılarıOku() { int[] sayılar.sort. sayılarıOku ismini verebileceğimiz bu işlevin hiçbir parametre alması gerekmez. sayılar). çünkü yeni işlevin ismi zaten ne işlem yapılmakta olduğunu açıklayacak.org 147 . sayılar. return sayı. Örneğin sayıOku işlevini yazmadan önce.

10 Parçalara ayırın Programı bir bütün halinde main içinde yazmak yerine küçük parçalara ayırmak bütün programı kolaylaştırır. Komut satırından girilen dizgiyi mors koduna çeviren bir program yazın. 35. Örneğin şu şekilde çağırabilelim: string[] seçenekler = [ "Siyah".11 Problemler Bundan sonraki bütün derslerde ve yazacağınız bütün programlarda hep işlevler olacak. Hem mors kodunun günümüzde geçerliliği kalmadı. Daha da önemlisi.org 148 . "Yeşil".İşlevler 35. 1. Bu programı yazarken amacınız olabildiğince çok işlev kullanmak olsun. İşlevin ismini açıklayıcı olarak seçmek. açıklama satırının gereğini de ortadan kaldırır. 1).. Artık açıklama satırı ya yanlış bilgi veriyordur. programda daha sonradan gereken değişiklikler de çok daha kolay olur. http://ddili. Açıklama satırlarından kurtulmanın önemli başka bir nedeni daha vardır: açıklama satırları zaman içinde kodun ne yaptığı hakkında yanlış bilgi vermeye başlarlar..-. Fikirler: ◦ Türkçe harfleri boşverin. Küçük işlevlerin birim olarak işlemleri de basit olacağından.". "Kırmızı". menüyüGöster(seçenekler. ya da tamamen işe yaramaz durumdadır. Programın diğer işlemleri bu yapı taşları üzerine kurulunca bütün program daha kolay yazılır. bütün seçeneklerini bir dizi olarak alacak şekilde değiştirin.9 Açıklamalar (gerekmesinler) Eğer programdaki işlemlerin bazılarının ne yaptıklarını açıklama satırları yazarak açıklama gereği duyuyorsanız.". Baştan iyi niyetle ve doğru olarak yazılan açıklama satırı. belki de o işlemlerin bir işleve taşınmaları zamanı gelmiştir. Çıktısı şöyle olsun: 1 Siyah 2 Kırmızı 3 Yeşil 4 Mavi 5 Beyaz 2. Yukarıdaki programdaki üç açıklama satırından bu sayede kurtulmuş olduk. menüyüGöster işlevini. kod değiştiğinde unutulur ve zamanla koddan ilgisiz hale gelebilir.-". teker teker yazılmaları çok daha kolay olur. 'c' : "-. O yüzden hiç işlev deneyimi sıkıntısı çekmeyeceksiniz. Copyright © 2009-2011 Ali Çehreli. 'b' : "-. Burada ilginç problemler bulabildiğime inanmıyorum. hem de zaten Türk harfi eki olduğunu sanmıyorum ◦ Dizgideki harflerin mors kodu karşılıklarını bir eşleme tablosu ile bulabilirsiniz: string[char] morsAlfabesi = [ 'a' : ". "Mavi". Bu yüzden programları açıklama satırlarına gerek bırakmadan yazmaya çalışmak önemlidir. Hiç olmazsa çözümlerini ilginç bulabilirsiniz. 35. "Beyaz" ].

===.=.=. Programın geri * kalanında hep dchar[sütunAdedi] yazmak yerine. immutable int sütunAdedi = 60. http://ddili.org 149 ..... diğerleri . } /* * Verilen kağıdın belirtilen yerine bir benek koyar. bir * anlamda o kareyi "boyar" Copyright © 2009-2011 Ali Çehreli. Siz bu programı istediğiniz şekilde değiştirin: import std. /* * Belirsiz sayıda satıra da kısaca 'Kağıt' takma ismini * veriyoruz. İki boyutlu bir diziyi bir resim kağıdı gibi kullanan bir program yazdım. } } writeln(). daha * açıklayıcı olarak 'Satır' yazabilmemizi sağlıyor.'.. */ ◦ Hiç gerekmese de..===. kağıt) { foreach (kare.. Mors kodunun hep kısa ve uzun işaretlerden oluştuğunu duyarız.=== a b 1 2 4. /* "Değişmez" anlamına gelen immutable'ı bir sonraki derste * göstereceğim */ immutable int satırAdedi = 20. */ alias dchar[sütunAdedi] Satır.===.===.=.===. "takma isim" anlamına gelir.stdio.=.. "ab 12" dizgisi için programın çıktısı şöyle olabilir: =. ve uzun işaret için '===' kullanırsak. kısa işaret uzunluğundadır kısa ve uzun işaretler arasında tek boşluk vardır harf aralarında 3 boşluk vardır sözcük aralarında 7 boşluk vardır Boşluk için '. * * Kağıt ise "dinamik dizi"dir. /* ..===. /* * alias.. * * Dikkat ederseniz Satır "sabit uzunluklu dizi" türüdür....=. satır) { write(kare).İşlevler ]. bu programın çıktısını daha doğru yapmak isterseniz mors kodundaki boşlukları da göze alın. */ alias Satır[] Kağıt.===. Oysa bu işaretlerin aralarındaki boşlukların uzunlukları da önemlidir: ▪ ▪ ▪ ▪ ▪ uzun işaret kısanın 3 katı uzunluğundadır boşluk. /* * Verilen kağıdı satır satır ve kare kare çıkışa gönderir */ void kağıdıGöster(Kağıt kağıt) { foreach (satır.===.. kısa işaret için '='.=.

http://ddili.. } /* Hiç satırı bulunmayan bir kağıt */ Kağıt kağıt. Copyright © 2009-2011 Ali Çehreli.org 150 .. } /* Ve kullanmaya başlıyoruz */ benekKoy(kağıt. 5. } /* * Kağıdın belirtilen yerinden aşağıya doğru. satır . 0 . satırAdedi) { kağıt ~= boşSatır.. 10. çözümler kağıdıGöster(kağıt). belirtilen * uzunlukta çizgi çizer */ void düşeyÇizgiÇiz(Kağıt kağıt. int sütun. } . sütun). int sütun) { kağıt[satır][sütun] = '#'. çizilecekSatır. /* Ona boş satırlar ekliyoruz */ foreach (i. foreach (ref kare. } } void main() { /* Biraz aşağıda yararlanmak üzere boş bir satır * oluşturuyoruz */ Satır boşSatır. 30). int satır. 4). int satır.'.İşlevler */ void benekKoy(Kağıt kağıt. satır + uzunluk) { benekKoy(kağıt.. boşSatır) { kare = '. 7. düşeyÇizgiÇiz(kağıt. int uzunluk) { foreach (çizilecekSatır.

örneğin bu işlev çalışan değişkeninin içindeki bir enum değişkeni de ÇalışmaDurumu. her türlü kavramı bir değişken olarak tanımlarız. immutable ise.1 const Bu anahtar sözcük bir değişkene sabitmiş gibi davranılacağını. Herhalde banka 'nın eleman sayısı azalacaktır. programda görevleri farklıdır ve bazı durumlarda birbirleriyle uyumsuzdurlar. programda geçen kavramları temsil ederler. değişkenler olmadan olmaz: değişebilme kavramı. cüzdandakiMiktar -= toplamFiyat. Temsil ettikleri kavramlar arasındaki etkileşimleri de bu değişkenlerin değerlerini değiştirerek sağlarız: // Parayı öde toplamFiyat = fiyatıHesapla(fiyatListesi). http://ddili. Ancak. • koddaki işlemlerin her birisinin her değişkeni değiştirebilecek kadar esnek olması. Bazı kavramların bazı işlemler sırasında değişmeyecekleri güvencesine gerek duyarız. ilk değerleri verildikten sonra artık değiştirilemezler. 36. Sonuçta ikisi de "değişmez" anlamına geliyor olsalar da. değişebilen char[] dizgileriyle uyumlu değildir. bu esnekliğin uygun olmadığı durumlar da vardır: • bazı kavramlar zaten değişmezdirler. "değişebilen" anlamına gelen "mutable"ın karşıt anlamlısıdır. çalışan) gibi bir işlev çağrısı sonucunda çalışan 'ın banka 'dan emekli edildiğini anlayabiliriz. Örneğin emekliEt(banka. ve özellikle C++'dan tanıyan kişilere yönelik olarak yazılmış. O makale.emekli olarak değiştirecek midir? Kodun içinden çıkılmaz derecede güç bir duruma gelmemesi için bazı durumlarda değişmezlik olgusundan yararlanmak isteriz. Eğer böyle bir deneyiminiz varsa. Tanımlandığı sırada tür isminden önce const yazılan değişkenler. Bu açıdan bakınca. hangi işlemlerin hangi değişkenleri değiştirdiklerini fazla serbest bıraktığı için.const ve immutable 36 const ve immutable Ddili'nin makaleler bölümünde bu konuyu anlatan "const ve immutable Kavramları" isimli bir çeviri var. D'deki değişmezlik kavramlarını gösteren iki anahtar sözcük İngilizce'de de çok yakın anlamdadırlar: const . matematikteki pi sayısının değeri bellidir. Somut veya soyut. peki çalışan değişkeni de değişecek midir. bakkaldakiMiktar += toplamFiyat. yoksa her işlev çağrısına şüpheyle bakmaya başlarız. Copyright © 2009-2011 Ali Çehreli. Ben bu derste bu kavramları yalnızca şimdiye kadar öğrendiklerimize bağlı olarak göstereceğim. vs. değişmez" anlamına gelen "constant"ın kısasıdır. yani o değişkenin değiştirilmeyeceğini belirler. "sabit. programların herhangi bir iş yapabilmeleri için kesinlikle gereklidir. o makale sizin için yeterli olabilir. bu işlevden dönüldüğünde bu iki değişkenden hangilerinin değişmiş olabileceklerini bilmek de önemlidir. örneğin haftadaki gün sayısı 7'dir. bir programın desteklediği dil sayısı programın çalıştığı sürece değişmeyecektir (örneğin Türkçe ve İngilizce'dir). kodun okunması ve geliştirilmesi güç bir duruma gelir. Bu uyumsuzluğa önceki derslerde rastladık: kendisi aslında immutable(char)[] 'ın takma ismi olan string . bu kavramları başka programlama dillerinden.org 151 . Değişkenler. Buna rağmen.

kodun anlaşılmasını çok kolaylaştırır. derleyicinin onun değiştirilmesine izin vermemesini sağlar: const int birDeğer = birHesap(). daha sonra: birDeğer = başkaHesap().00 ]. 3. } } return toplam. Olası hataları da önler: değişmeyecek olduğu düşünüldüğü için const olarak belirlenmiş olan bir değişkeni sonradan değiştirmeye çalışmaya derleyici izin vermez. programcı için yararlı bir kısıtlamadır. verilen parametrenin değişmediğinden emin olamazlar. ilerideki bir kod geliştirme sonucunda parametre değişebilir. fiyatListesi) { toplam += birimFiyatı. toplamFiyat). parametrenin değişmeyeceğini const anahtar sözcüğü ile belirtmek gerekir. Böylece parametrenin değişmesini hem derleyici önleyecektir. // . Bu sakıncayı ortadan kaldırmak için. Belki de bir yanlışlık sonucu olarak eklenebilecek bir satır. değişebilir Aynı kavramı const olarak tanımlamak. double fiyatıHesapla(double[] fiyatListesi) { double toplam = 0.org 152 . Bir örnek olarak. http://ddili.25. Değişkenin belirli bir kapsam içinde değişmeyecek olduğunu bilmek.const ve immutable Şimdiye kadar hep değiştirilebilen kavramlar gördük: int birDeğer = birHesap(). kendisine verilen fiyat listesinde bir değişiklik yapması mantıklı olmaz. ya da tasarımı gözden geçirerek const 'ın kaldırılması gerektiğine karar verir.56. programcının aklında tutması gereken bir bilgidir: bugün değiştirmiyorsa bile. O noktada programcı ya yanlışlığı farkeder.. // ← çalışır. Oysa. Bu. fiyatListesi 'nin değişmesi için bir engel yoktur. 5.. fiyatListesi 'nin değişmesine neden olur: double fiyatıHesapla(double[] fiyatListesi) { fiyatListesi[0] = 1234. listedeki birimlerin fiyatlarını ekleyerek toplam fiyatı döndürmek olmalıdır. sonra başka fiyat da eklenebilir double toplamFiyat = fiyatıHesapla(fiyatListesi). // . } fiyatıHesapla işlevinin. // . hem de Copyright © 2009-2011 Ali Çehreli. void main() { double[] fiyatListesi = [ 10.. // ← derleme HATASI Bu.. bir fiyatıHesapla işlevine bakalım. daha sonra: birDeğer = başkaHesap().stdio. Bu işlev.. // ← sorunsuz derlenir fiyatıHesapla işlevini çağıran kapsamlar. fiyat listesi olarak bir double dizisi alıyor ve dizideki bütün fiyatların toplamlarını döndürüyor olsun: import std.50. writeln("Toplam fiyat: ". Onun işi. parametresini const olarak almadığı için.. foreach (birimFiyatı.

Oysa değişkenin kendisi değişmez olmayabilir. Şimdi o işlevin çağrılmasından hemen önceye dönelim.2 const değişken. // ← derleme HATASI Bu yararlı bir denetimdir. 5. işlevin kullanışlılığını düşürmektedir. işlev parametresinin const olarak tanımlanmamış olması. ve bu durumda programcının onu doğru olarak const ile tanımladığını varsayalım: const double[] fiyatListesi = [ 10. kesinlikle değişmez değildir const . toplamFiyat da artık değiştirilemez.1. 36.50. parametre const olduğu için.56. işlev içinde değiştirilemiyordu. // ← derleme HATASI Program artık derlenemez.00 ]. değişkenin "bu kapsam içinde" veya "bu parametre yoluyla" değiştirilemeyeceğini belirler. Sonuçta. Fiyat hesaplayan bir işlevin listede değişiklik yapması olsa olsa bir hata olacağı için. const 'ın. işlevin kullanışlılığını düşürür.const ve immutable programcılar işleve verilen parametrenin değişmediğinden emin olabileceklerdir.1.. Bunu yukarıdaki fiyatıHesapla işlevinin parametresinde görmüştük: değişken. // .25. Not: const 'ın doğru olarak kullanılmasıyla ilgili olan bu konunun İngilizce'si "const correctness"tır. yani değişmeyecek olan bir dizinin.1 const değişken.. Bunu görmek için tekrar fiyatıHesapla işlevine dönelim ve parametresinin en baştaki gibi yanlış olarak const olmadan tanımlandığını varsayalım: double fiyatıHesapla(double[] fiyatListesi) { O işlev. artık başka fiyat eklenemez double toplamFiyat = fiyatıHesapla(fiyatListesi). Devam edersek. ve işlevin ancak const olmayan dizilerle kullanılabilmesine neden olmaktadır. eğer aynı örnekteki toplamFiyat 'ın bir kere ilklendikten sonra bir daha değişmeyeceği düşünülüyorsa. Bu kod artık derlenemez: double fiyatıHesapla(const double[] fiyatListesi) { fiyatListesi[0] = 1234. oysa main içindeki fiyatListesi yine de değiştirilebiliyordu. programın tutarlılığı açısından dikkat edilmesi gereken bir olgu olarak kabul edilmesi gerekir. const 'ın yalnızca programcıya yardımcı bir olanak olarak değil. 3. http://ddili. main içinde const olarak tanımlanan. onu değiştirmeyeceği güvencesini vermeyen bir işleve parametre olarak gönderilmesine derleyici izin vermez. mantıklı olan her yerde kullanılması gerekir. 36.org 153 . O haliyle derlenir ve program doğru olarak çalışır. main içindeki fiyatListesi 'nin değişmeyecek olduğunu. const olmayan parametre yerine kullanılamaz Değişmeyecek olduğu halde const olarak tanımlanmayan bir parametre. Copyright © 2009-2011 Ali Çehreli. o da const olarak tanımlanabilir: const double toplamFiyat = fiyatıHesapla(fiyatListesi). derleyicinin sağladığı bu güvenceden yararlanmak önemlidir. programın ilk gösterdiğim halinde de zaten öyleydi.

36. programın gösterdiği menü seçenekleri değişmezdir. Öğrenci: Notlar : Öğrenci: Notlar : Mehmet 80 95 90 Mehmet 81 95 90 Özetle. notlar). int[] notlar = [ 80. günİsimleri[0] = günİsimleri[] = "Salı". hem de son gördüğümüz derleme hatasında olduğu gibi. Yoksa hem sürprizlerle karşılaşırız. const . http://ddili.const ve immutable Bunu değişik bir örnekle tekrar görelim. } } writeln(). "Çarşamba". const değişkenleri o işlevle yazdıramayız. Herhangi bir değişkeni çıkışa yazdırmak için kullanılacak olan bir işlevin. const 'tan farklı olarak. Bu tür değişkenler immutable ile tanımlanırlar: immutable string [ "Pazartesi". notlar) { write(' '. notlarıYazdır(isim. const int[] notlar) { writeln("Öğrenci: ". denemeSayısıSınırı = 4. bir değişkenin programın çalışması sırasında kesinlikle değişmeyeceğini bildirir. notlar[0] = 81.2 immutable Bu anahtar sözcük. kullanıcıya üç deneme şansı veren bir oyunda deneme sayısı sınırı değişmez. ama işlevin çağrıldığı kapsamda int[] olarak tanımlandığı için değişebilir. notlarıYazdır işlevinin parametresi const olduğu için notlar orada değiştirilemez. değişkenin değişmezliği konusunda const 'tan daha kuvvetli bir anlama sahiptir. "Pazar" "İlk gün". yazdıracağı parametrede değişiklik yapmasını beklemeyiz. not). bir öğrencinin notlarını yazdıran bir işlevin parametrelerini const olarak tanımlamak doğru olur: void notlarıYazdır(const char[] isim. "Cumartesi". o işlevi çağıran tarafta değişebiliyor olabilir: string isim = "Mehmet". vs. isim). bir değişkenin "burada değişmeyeceği" veya "bu isimle değişmeyeceği" gibi bir anlam taşır. ]. notlar).org 154 . "Perşembe". notlarıYazdır(isim. // ← derleme HATASI immutable int denemeSayısıSınırı = 3. 95. "Cuma". Oysa örneğin notlar . Bu yüzden. write("Notlar :"). Örneğin haftanın gün isimlerini taşıyan bir dizi hiçbir zaman değişmez. foreach (not. // ← derleme HATASI Copyright © 2009-2011 Ali Çehreli. 90 ].

immutable int[] immutableDizi. "kopyasını al" anlamındaki "duplicate"ten gelir • . Bu durumlarda dizgilerin .. ismi "immutable duplicate"ten gelir Örneğin. // ← derleme HATASI // ← çalışır } Bu konunun benzeriyle daha önceki derslerde de karşılaştık. değişebilen bir dizgi alan bir işlevi bir string ile doğrudan çağıramayız.2.idup dizginin değişmez bir kopyasını oluşturur.org 155 .dup dizginin değişebilen bir kopyasını oluşturur. immutableAlanİşlev(immutableDizi).idup ile çağırmamız gerekebilir: Copyright © 2009-2011 Ali Çehreli.. dizgi hazır değerleri de değişmezdirler: • "merhaba"c hazır dizgisinin türü string 'dir • "merhaba"w hazır dizgisinin türü wstring 'dir • "merhaba"d hazır dizgisinin türü dstring 'dir 36.dup ve . } void main() { foo("merhaba"). dizgi türlerinin takma isimlerinin asıl türlerinin immutable olduklarından bahsetmiştik: • immutable(char)[] 'ın takma ismi string • immutable(wchar)[] 'ın takma ismi wstring • immutable(dchar)[] 'ın takma ismi dstring Ek olarak.. immutable parametre yerine kullanılamaz Her ikisi de değişmezliği belirliyor olsalar da.idup niteliklerinden yararlanmamız gerekebilir: • .dup). immutableAlanİşlev(constDizi). dizgileri işlevlere parametre olarak geçirirken uyumsuz durumlarla karşılaşabiliriz.dup ile aldığımız kopya ile çağırmamız gerekir: void foo(char[] dizgi) { // . ismi. } // ← derleme HATASI // ← çalışır Parametresinin programın çalışması süresince kesinlikle değişmeyecek olmasını isteyen ve bu yüzden de onu immutable olarak belirlemiş olan bir işlevi de . http://ddili..1 const değişken.dup ve . const değişkenler işlevlere immutable parametre olarak gönderilemezler: void immutableAlanİşlev(immutable int[] birDizi) { // . Hatırlarsanız. foo("merhaba". Bu durumda işlevi . } void main() { const int[] constDizi.3 .const ve immutable 36.idup Bu yüzden.

for (int i = 0. Öyle yaptığınızda parametreyi değiştirmeme sözü verdiğiniz için.dup. } abahrem Copyright © 2009-2011 Ali Çehreli. } void main() { char[] selam...stdio.idup).. // ← çalışır // ← çalışır // ← çalışır } Eğer parametrede bir değişiklik yapacaksanız. http://ddili. dizgi[i] = dizgi[uzunluk .1 . değişebilen.1 .const ve immutable void foo(string dizgi) { // . writeln(selam).. o parametreyi değişebilen şekilde tanımlayın.length. foo(değişebilenDizgi). zaten const veya immutable tanımladığınızda değiştiremezsiniz: import std. Parametre tanımlarken. parametreyi const olarak tanımlayın.4 Nasıl kullanmalı Değişken tanımlarken. dizgi[uzunluk . string immutableDizgi. } void main() { char[] değişebilenDizgi. void tersÇevir(char[] dizgi) { const int uzunluk = dizgi. } // ← derleme HATASI // ← çalışır 36. ve immutable değişkenleri o işleve gönderebilirsiniz: void foo(const char[] dizgi) { // .i] = geçici. foo(selam).org 156 . const .i]. eğer parametrede bir değişiklik yapılmayacaksa. Örneğin bir dakikadaki toplam saniye sayısı değişmeyecektir: immutable int dakikaBaşınaSaniye = 60. } } void main() { char[] selam = "merhaba". foo(selam. const char[] constDizgi. i != uzunluk / 2. programın çalışması sırasında kesinlikle değişmeyecek olan değerleri immutable olarak tanımlayın. foo(immutableDizgi). tersÇevir(selam). foo(constDizgi). ++i) { const char geçici = dizgi[i].

org 157 . http://ddili. Geçerli bir nedeni yoksa. olabildiği kadarıyla hep const tarafta kalmaya çalışın. Copyright © 2009-2011 Ali Çehreli. Örneğin işlevin içinde değiştirilmeyen parametreleri const olarak tanımlayın. işlev parametrelerinde immutable kullanmayın. Yoksa işlevinizin kullanışlılığı düşer.const ve immutable Genel bir kural olarak. Bundan sonraki bölümlerdeki örneklerde ben de bol bol const ve immutable kullanacağım.

enerjisiniDüşür(enerji). http://ddili. return sonuç. kopyalarıdır. } O işlevi denemek için yazılmış olan şu programa bakalım: import std. kopyalarına erişiyorduk. } O işlevde vize notu ağırlığının %40. 37. Bunu yan etki üretmeye çalışan aşağıdaki işlevde görebiliriz. Örneğin hiçbir yan etkisi olmayan ve işi yalnızca değer üretmek olan bir işlev şöyle yazılabiliyordu: double seneSonuNotu(double vizeNotu. çünkü aslında işlev tarafından kullanılanlar değişkenlerin kendileri değil. Copyright © 2009-2011 Ali Çehreli. çünkü parametrede yapılan değişiklik ancak kopyayı etkiler.0f". Önceki programlarda işlevlerin parametrelerini kullanarak nasıl sonuç ürettiklerini gördük. foreach içinde dizi elemanlarının kendilerine değil. final notununkinin de %60 olarak hesaplandığını görebiliyoruz.İşlev Parametreleri 37 İşlev Parametreleri Bu derste parametrelerin işlevlere gönderilmeleri konusundaki önemli ayrıntıları göstereceğim ve D'deki parametre çeşitlerini tanıtacağım. O işlevi örneğin şu şekilde çağırabiliriz: int vizeOrtalaması = 76. double finalNotu) { const double sonuç = vizeNotu * 0.1 Çoğu parametre kopyalanır Yukarıdaki kodun vizeOrtalaması ve finalNotu değişkenlerini kullandığını söylediğimizde aslında temelde bir hataya düşmüş oluruz. int finalNotu = 80. } void main() { double enerji = 100. finalNotu)).6. seneSonuNotu(vizeOrtalaması. Ek olarak. Bu ayrım önemlidir. Bir oyun karakterinin enerjisini düşürmek için yazılmış olsun: void enerjisiniDüşür(double enerji) { enerji /= 4. Aslında bu konunun bir kısmına foreach Döngüsü dersinde ref anahtar sözcüğünü anlatırken çok kısaca değinmiştik: o sözcük kullanılmadığında. writefln("Sene sonu notu: %2. const ve immutable 'ın parametrelerle kullanımını da bir önceki derste görmüştük.org 158 . void enerjisiniDüşür(double enerji) { enerji /= 4.4 + finalNotu * 0.stdio.

enerji). writeln("İşlevden çıkılırken } void main() { double enerji = 100. Bu tür değişkenler işlevlere referans olarak geçirilirler. enerji). parametresinin değerini dörtte birine düşürdüğü halde main içindeki enerji isimli değişkenin değeri aynı kalmaktadır.'. : ". writeln("İşlevi çağırmadan önce : ". Bu olayı biraz daha yakından incelemek için programa bazı çıktı ifadeleri yerleştirebiliriz: import std. ilerideki derslerde göreceğimiz yapı nesnelerinde de böyledir: yapı nesneleri de işlevlere kopyalanarak gönderilirler. isimleri aynı olsa da.stdio. ← asıl enerji değişmez Çıktıdan anlaşıldığı gibi. Yeni enerji: 100 ← Değişmedi enerjisiniDüşür işlevi. void enerjisiniDüşür(double enerji) { writeln("İşleve girildiğinde enerji /= 4. : ". Referans olarak geçirilen parametre. İşleve main içindeki değişkenin değeri kopyalanır ve değişiklik bu kopyayı etkiler. http://ddili.stdio. enerji). asıl nesnenin bir takma ismi gibi kullanılır ve parametrede yapılan değişiklik asıl nesneyi değiştirir. bu durum onlar için de geçerlidir. main içindeki değişkenin kopyasıdır. Parametresinde değişiklik yapan şu işleve bakalım: import std. } Copyright © 2009-2011 Ali Çehreli. main içindeki enerji ile enerjisiniDüşür içindeki enerji farklı değişkenlerdir.İşlev Parametreleri } writeln("Yeni enerji: ". enerji). Bu. main içindeki enerji ile enerjisiniDüşür işlevinin parametresi olan enerji 'nin farklı değişkenler olmalarıdır. Bunun nedeni. Parametre olan. 37. writeln("İşlevden dönüldükten sonra: ". enerji).2 Dizi ve sınıf nesnesi olan parametreler kopyalanmazlar Diziler ve daha sonraki derslerde göreceğimiz sınıf nesneleri. Dizgiler de dizi olduklarından. } İşlevi çağırmadan önce : İşleve girildiğinde : İşlevden çıkılırken : İşlevden dönüldükten sonra: 100 100 25 100 ← parametre değişir. enerjisiniDüşür(enerji). void başHarfiniNoktaYap(char[] dizgi) { dizgi[0] = '. parametrelerin kopyalanmaları kuralına uymazlar.org 159 .

89)). Bu tür parametreler işlevde yalnızca bilgi olarak kullanılırlar. Copyright © 2009-2011 Ali Çehreli. başHarfiniNoktaYap(dizgi). in anahtar sözcüğü. main içindeki asıl nesneyi değiştirmiştir: . } in parametreler değiştirilemezler: void deneme(in int değer) { değer = 1.dup. main içindeki asıl dizgi 'nin takma ismidir. parametrenin amacını daha açık bir şekilde ifade eder: import std.org 160 .İşlev Parametreleri void main() { char[] dizgi = "abc". kendileri değiştirilemezler. İngilizce'de "içeriye" anlamına geldiği için.3. double ağırlıklıToplam(in double şimdikiToplam. writeln(dizgi). in bu açıdan const 'a benzese de. } void main() { writeln(ağırlıklıToplam(1. sınıf nesneleri ve daha sonra göreceğimiz referans türleri referans olarak geçirilirler • diğer türler kopyalanırlar Bunlar. işlevin parametresi olan dizgi .stdio. 4. in double ağırlık. 37. bir belirteç kullanılmadığı zaman varsayılan kurallardır. 7. parametrenin bu düzenekte yalnızca bir giriş bilgisi olarak kullanılacağını belirler.bc Çünkü. İşlevlerinizin daha okunaklı ve daha güvenli olmalarına yardım eder. } Parametrede yapılan değişiklik. Aşağıdaki anahtar sözcükleri kullanarak bu kuralları etkileyebiliriz.3 Parametre çeşitleri Parametrelerin işlevlere geçirilmeleri normalde yukarıdaki iki temel kurala uyar: • diziler. http://ddili. 37.56.23.1 in İşlevlerin değer veya yan etki üreten düzenekler olduklarını kabul etmiş ve o düzeneğin işleyişini parametrelerin belirlediğini görmüştük. in double eklenecekDeğer) { return şimdikiToplam + (ağırlık * eklenecekDeğer). // ← derleme HATASI } Giriş amacıyla kullanılan parametreleri in anahtar sözcüğü ile belirtmenizi öneririm.

int kalanlıBöl(in int bölünen.org 161 .stdio. bazen kısıtlayıcı olabilir. İşlev. "Dışarıya" anlamına gelen out belirteci. o değerler işlevi çağıran ortamda da sonuç olarak görülebilirler. kalan).stdio. Bunları da ilerideki derslerde göreceğiz. işlevlerin parametreleri yoluyla da sonuç üretmelerini sağlar. Not: Aslında dönüş türü olarak birden fazla değer de döndürülebilir. out int kalan) { kalan = bölünen % bölen. } O işlevde parametreye hiçbir değer atanmıyor bile olsa. kalanı da bir out parametre olarak döndürebiliriz: import std. Bir anlamda bilgi işlevden dışarıya gönderilmektedir. 3. void deneme(out int parametre) { writeln("İşleve girildiğinde } void main() { int değer = 100. writeln("İşlev çağrılmadan önce: ". ". writeln("İşlevden dönüldüğünde : ". değer). işleve girildiğinde parametrenin değeri int 'in ilk değeri olmakta ve bu main içindeki değeri de etkilemektedir: Copyright © 2009-2011 Ali Çehreli. kalan: ".2 out İşlevin ürettiği bilginin işlevden dönüş değeri olarak döndürüldüğünü görmüştük. kalan: 1 out parametreler yalnızca dışarıya bilgi verirler. main içindeki kalan 'ın değişmesine neden olmaktadır: bölüm: 2. parametre). bazen işlevin birden fazla sonuç üretmesini isteyebiliriz. http://ddili. İşlevin dönüş değerini bölümü döndürmek için kullanırsak. } void main() { int bölüm. Bunu. : ".3. in int bölen. } writeln("bölüm: ". return bölünen / bölen. değer). kalan).İşlev Parametreleri 37. deneme(değer). int kalan. bölüm. İşlevlerden yalnızca tek bir bilgi döndürülebiliyor olması. bölüm = kalanlıBöl(7. Örnek olarak iki sayıyı bölen. kalan parametresini işlev içinde değiştirmek. ve hem bölümü hem de kalanı üreten bir işleve bakalım. o parametrelerin değeri çağıran tarafta ne olursa olsun. dönüş türünü bir Tuple veya struct türü olarak belirleyerek gerçekleştirebiliriz. bu tür parametrelerin değerlerini atama yoluyla değiştirdiğinde. işleve girildiğinde her zaman için türünün ilk değerine dönüşür: import std.

++i) { sonuç ~= birinci[i]. http://ddili.stdio. immutable dchar[] ikinci) { dchar[] sonuç. 37.3 const Bir önceki derste de gördüğümüz gibi.3. 37. aşağıdaki işlevi ancak immutable dizgilerle çağırabiliriz (örneğin dizgi hazır değerleriyle): import std.$]..org 162 . Seçim sizin. dchar[] karıştır(immutable dchar[] birinci. (i < birinci. } sonuç ~= birinci[i. parametrenin programın çalışması süresince değişmemesini şart koşar.length) && (i < ikinci.. anlamı "içeriye" olduğu için amacını daha açık bir şekilde ifade etmesidir. } return sonuç.İşlev Parametreleri İşlev çağrılmadan önce: 100 İşleve girildiğinde : 0 İşlevden dönüldüğünde : 0 ← int'in ilk değerinde Yani out parametreler dışarıdan bilgi alamazlar.. int i. void main() { Copyright © 2009-2011 Ali Çehreli. out parametreler yerine.1]. sonuç ~= ikinci[i..3. char sonHarfi(const char[] dizgi) { return dizgi[$ . in 'in yararı. hem de işlev const veya immutable olan değişkenlerle de çağrılabilir: import std. sonuç ~= ikinci[i]. } void main() { writeln(sonHarfi("sabit")). Bu konuda ısrarlı olduğu için.$]. parametrenin işlev içerisinde değiştirilmeyeceği garantisini verir.stdio. O olanaklar. parametre listeleri dışında da kullanılabilen bir anahtar sözcük olduğu için değişmezlik kavramını belirtme konusunda tutarlılık sağlıyor olmasıdır. dönüş türü olarak Tuple veya struct türleri kullanmayı da düşünebilirsiniz.4 immutable Bir önceki derste de gördüğümüz gibi. birden fazla bilgiyi bir arada döndürmeyi sağlarlar. for (i = 0. yalnızca dışarıya bilgi gönderebilirler.length). } Burada bir seçim yapmak durumundasınız: in ve const 'ın ikisi de parametre listesinde aynı anlama geldikleri için birisinde karar kılmak isteyebilirsiniz. işlevi çağıranlar hem parametrede değişiklik yapılmadığını bilmiş olurlar. Bu sayede. const 'ın yararı ise.

ref parametreler işlevlerin yan etki üreten türden işlevler olmalarına neden olurlar: Dikkat ederseniz.stdio. işlev parametresinde yapılan değişiklik. hatta bazı programlama dillerinde yan etkilere izin verilmez bile. Copyright © 2009-2011 Ali Çehreli. immutable 'ı ancak gerçekten gereken durumlarda ve kendi işlevlerinizde belki de hiçbir zaman kullanmayacaksınız. yani değer üreten bir işlev kullanacak şekilde yazmak için.org 163 . Değer üreten işlevlerin yan etkisi olan işlevlerden programcılık açısından daha üstün oldukları kabul edilir. enerjisiniDüşür(enerji). writeln("Yeni enerji: ". İşlevsel programlama [functional programming] denen programlama mantığında yan etkilerin özellikle azaltılmasına çalışılır. Ben de. parametresini referans olarak alması gerekir: import std. main içindeki asıl değişkeni değiştirebilmesi için.İşlev Parametreleri } writeln(karıştır("MERHABA". 37. hem de sonuç üretmek üzere çıkış bilgisidirler. Aynı işi fonksiyonel programlama mantığa uygun olacak şekilde gerçekleştirmek için.. programlarınızın güvenilirliği açısından etkili olacaktır.stdio. ref parametreler işlev içinde hem kullanılmak üzere giriş bilgisidirler.. İşlevlerin yan etkilerini azaltmak. işlevlerinizi eğer olabiliyorsa değer üretecek şekilde tasarlanamınızı öneririm. } void main() { double enerji = 100. void enerjisiniDüşür(ref double enerji) { enerji /= 4. main içindeki enerji 'nin değerini değiştirir: Yeni enerji: 25 Görüldüğü gibi. programı şöyle değiştirmek önerilir: import std. } Bu sayede. } void main() { double enerji = 100. parametresinde bir değişiklik yapıyor.5 ref İşleve normalde kopyalanarak geçirilecek olan bir parametrenin referans olarak geçirilmesini sağlar. double düşükEnerji(double enerji) { return enerji / 4. Kısıtlayıcı bir belirteç olduğu için. enerjisiniDüşür işlevi değer üretmiyor. enerji). "dünya")). http://ddili.3. Yukarıda parametresi normalde kopyalandığı için istediğimiz gibi çalışmayan enerjisiniDüşür işlevinin.

başkaBirMiktar()). işlemleri öncelik sıralarına göre hevesle işletir. adet.İşlev Parametreleri } enerji = düşükEnerji(enerji). writeln("Yeni enerji: ".3. Aşağıdaki işlev. foreach (sahip. Yoksa topla hangi iki değeri toplayacağını bilemez. " yumurtaya". işlevin işleyişine bağlı olarak belki de hiçbir zaman kullanılmayacaklardır. böyle parametrelerin gereksiz yere hesaplanmış olmalarına neden olacaktır. istenen . istenen sayıdaki yumurtayı öncelikle dolapta bulunanlarla yapmaya çalışıyor. } } Buna ek olarak. Copyright © 2009-2011 Ali Çehreli. " komşulardan da ". adetler) { writeln(sahip. Örneğin topla gibi bir işlevi başka iki işlevin sonucu ile çağırsak: sonuç = topla(birMiktar(). komşularda kaç yumurta bulunduğuna ise ancak dolapta yeterince yumurta bulunmadığında bakıyor: void yumurtaYap(in int istenen. parametre değerlerinin işlevlere gönderilmeden önce işletildiklerini söyleme gereği duymadım. toplam += adet. http://ddili. } else { writeln("O kadar yumurta yapamam"). " tane"). belirli bir hesap sonrasında bulunuyor olsun: int toplamYumurta(in int[string] adetler) { int toplam. 37. adet. Parametre değerlerinin hevesli olarak önceden işletilmeleri. dolaptaOlan. Örnek olarak. topla 'nın çağrılabilmesi için öncelikle birMiktar ve başkaBirMiktar işlevlerinin çağrılmaları gerekir. in int dolaptaOlan.dolaptaOlan. komşularda bulunan yumurta adedinin bir hesap sonucunda bilinebildiğini varsayalım. parametrelerinden birisinin değerini ancak belirli bir koşul sağlandığında kullanan bir işleve bakalım. Oysa bazı parametreler. enerji). Komşulardaki yumurta sayısı bir değişkenin değeri olarak hemen bilinmek yerine. Bunun size de doğal geldiğini düşünürüm. Program.6 lazy Şimdiye kadar. } } return toplam. İşlemlerin bu şekilde doğal olarak işletilmeleri hevesli [eager] olarak tanımlanır.org 164 . } else if (istenen <= (dolaptaOlan + komşulardaOlan)) { writeln("Dolaptaki ". " yumurta eklerim"). ": ". in int komşulardaOlan) { if (istenen <= dolaptaOlan) { writeln("Hepsini dolaptaki yumurtalarla yaparım").

Örneğin dolapta bulunanlardan daha fazla yumurta istendiğinde: yumurtaYap(9. istenen yumurta adedinin dolaptakilerden karşılanabildiği yukarıdaki durumda. toplamYumurta(komşulardakiYumurtalar)). kendisine verilen bir eşleme tablosunda ilerliyor. Programın çıktısında görüldüğü gibi. Şimdi yumurtaYap işlevini toplamYumurta 'yı kullanarak şöyle çağırabiliriz: void main() { int[string] komşulardakiYumurtalar = [ "Zümrüt Teyze":5.. http://ddili. onu lazy olarak belirleyebiliriz: void yumurtaYap(in int istenen. toplamYumurta(komşulardakiYumurtalar)). önce toplamYumurta işlevi işletilmektedir ve ondan sonra yumurtaYap çağrılmaktadır: Zümrüt Teyze: 5 tane ⎫ Veli Efendi: 7 tane ⎬ komşulardaki yumurta hesabı Yakup Abi: 3 tane ⎭ Hepsini dolaptaki yumurtalarla yaparım Ancak bu durumda gereksiz bir işlem var: sonuçta bütün yumurtalar dolaptakilerle yapılabilecek olduğu halde. bir parametrenin işlev içinde belki de hiç işletilmeyeceğini bildirmek için kullanılır. ve bütün yumurtaların toplamını döndürüyor. komşulardaki yumurta toplamı artık hesaplanmamaktadır: Hepsini dolaptaki yumurtalarla yaparım O hesap yine de gerektiği zaman yapılacaktır. "Veli Efendi":7 ]..İşlev Parametreleri O işlev. Bu sefer işlev içinde gerçekten gerektiği için. yumurta sahibini ve o kişide kaç yumurta bulunduğunu çıktıya yazdırıyor. "Yakup Abi":3. komşulardaki yumurta sayısı yine hesaplanmaktadır: Zümrüt Teyze: 5 tane Veli Efendi: 7 tane Yakup Abi: 3 tane Dolaptaki 5 yumurtaya komşulardan da 4 yumurta eklerim Copyright © 2009-2011 Ali Çehreli. 5. Kelime anlamı "tembel" olan lazy .. } yumurtaYap(2. gereksizce komşulardaki yumurtaları da saymış olduk..org 165 . gerisi aynı . in int dolaptaOlan. lazy int komşulardaOlan) { // . Şimdiki çıktıda görüldüğü gibi. 5. yumurtaYap işlevinin komşulardaOlan parametresinin her durumda hesaplanmasına gerek olmadığını ve ancak gerektiğinde hesaplanabileceğini bildirmek için.

} void tembelParametreliİşlev(lazy int değer) { int sonuç = değer + değer + değer. her erişildiğinde hesaplanır lazy parametrelerin değerleri. writeln(sonuç). başkaSayı). değeri ancak bazı durumlarda kullanılan parametreleri belirlemek için kullanabilirsiniz. return 1. O program istendiği gibi çalışmıyor: Copyright © 2009-2011 Ali Çehreli. http://ddili. birinci = ikinci. onu hesaplayan işlev de üç kere çağrılmaktadır: import std. } writeln(birSayı. } void main() { tembelParametreliİşlev(parametreyiHesaplayanİşlev()). o değer her kullanıldığında tekrar hesaplanır. } Hesap yapılıyor Hesap yapılıyor Hesap yapılıyor 3 lazy belirtecini. int parametreyiHesaplayanİşlev() { writeln("Hesap yapılıyor"). Ancak.stdio. değişTokuş(birSayı.org 166 . void değişTokuş(int birinci. Bu belirtecin sağladığı tembel değerlendirmeler olanağına bir sonraki derste de devam edeceğim. int başkaSayı = 2. ikinci = geçici. ' '.stdio. 37. başkaSayı).7 lazy parametre. int ikinci) { int geçici = birinci. değerin birden fazla sayıda hesaplanabileceğini de unutmamak gerekir.4 Problem • Kendisine verilen iki parametrenin değerlerini değiş tokuş etmek için bir işlev yazılmış: import std. Örneğin bu programda lazy olarak belirtilmiş olan parametre üç kere kullanıldığı için.İşlev Parametreleri 37. } void main() { int birSayı = 1.3.

org 167 . çözüm Copyright © 2009-2011 Ali Çehreli. http://ddili.. .İşlev Parametreleri 1 2 ← değiş tokuş olmamış İşlevi düzeltin ve programın çıktısının 2 1 olmasını sağlayın..

lazy anahtar sözcüğü... } Eğer birİfade() 'nin sonucu true ise. sonucun da true olacağı daha ikinci ifade işletilmeden bellidir. void selamVer(in int adet. • ?: işleci (üçlü işleç): koşul true olduğunda önceki ifade. çünkü belki de gerekmeyecek olan bir işlem için baştan zaman harcanmamış olur. O durumda ikinci ifade işletilmez. İşlemlerin gerekene kadar geciktirilmeleri. } Eğer birİfade() 'nin sonucu false ise.. işlevin davranışı değiştirilebilmektedir.. Bunun bir örneğini görmek için önce lazy kullanmayan bir programa bakalım: import std.random. false olduğunda sonraki ifade işletilir int i = birKoşul() ? yaBuİfade() : yaDaBuİfade(). Yararlarından birisi. Haskell gibi bazı programlama dillerinin de temel olanakları arasındadır. işleve dışarıdan verilebilmesidir. program hızı açısından önemli olabilir..Tembel Değerlendirmeler 38 Tembel Değerlendirmeler Tembel değerlendirmeler.org 168 . bu üç işlecin dışında da tembel değerlendirmeler kullanmayı sağlar. bir önceki derste de gördüğümüz gibi. 0 . " dünya!"). O durumda ikinci ifade işletilmez. adet) { writeln(selam. http://ddili. doğal olarak hız kazancı sağlayabilir. sonucun da false olacağı daha ikinci ifade işletilmeden bellidir. Bu. onu aslında daha önce gördüğümüz üç işleçten tanıyorsunuz: • || işleci (veya işleci): İkinci ifadeyi ancak birincisi false olduğunda işletir if (birİfade() || belkiDeİşletilmeyecekİfade()) { // . } } string rasgeleSelamSeç() { Copyright © 2009-2011 Ali Çehreli. import std. • && işleci (ve işleci): İkinci ifadeyi ancak birincisi true olduğunda işletir if (birİfade() && belkiDeİşletilmeyecekİfade()) { // . dikkatli kullanıldığında ilginç programlama yöntemlerine olanak verir. D'de tembel değerlendirme olanağı yalnızca lazy belirtecine bağlı değildir. işlemlerin bazı adımlarının sabit olmak yerine. Bu olanak. birKoşul() 'un sonucuna göre ifadelerden birisi gerekmediği için hiç işletilmez. lazy parametrelerin her erişildiklerinde tekrar hesaplanıyor olmaları zaman kaybına da neden olabilir.stdio. const char[] selam) { foreach (i. Diğer ve belki de daha önemli yararı. Öte yandan. Bu sayede. işlemlerin gerçekten gerekli olduğu zamana kadar geciktirilmesi anlamına gelir. yukarıda da gördüğümüz gibi işlemlerin gereksizce işletilmelerinin önüne geçmesidir. İngilizcesi "lazy evaluation" olan tembel değerlendirmeler.

Bu. Buradaki fark önemli: işleve bir değer değil. http://ddili. lazy bildirimi bir anlamda işlevin davranışını değiştirmektedir. 38. Attığı iki zarın toplamı daha büyük olan taraf puan kazanıyor. bilgisayarın hile yapıp yapmayacağı soruluyor. "Selam". bilgisayar ya hileli zarlar ya da rasgele zarlar atıyor: Copyright © 2009-2011 Ali Çehreli. "İyi akşamlar" ]. lazy kullandığımızda ise. "selam gerektiği zaman şu işlevi çağır ve onun döndürdüğü dizgiyi kullan" demiş gibi oluruz. Programın başında. Örnek: İyi İyi İyi İyi İyi akşamlar akşamlar akşamlar akşamlar akşamlar dünya! dünya! dünya! dünya! dünya! Not: Mesaj programın her çalıştırılışında rasgele seçildiği için. "Günaydın". lazy kullanılmadığında. bir hesaplama işlemidir. O koşula bağlı olarak. işleve parametre olarak verilmektedir. lazy const char[] selam) { Merhaba dünya! Selam dünya! Merhaba dünya! İyi akşamlar dünya! Selam dünya! Bunun nedeni. o değerin nasıl hesaplanacağı verilmektedir. Oysa.$)]. rasgeleSelamSeç()). return selamlar[uniform(0. beş kere tekrarlanan mesajın programın her çalıştırılışında farklı olması normaldir. void main() { selamVer(5.0. } O programda selamVer işlevinin kullandığı selam parametresinin değeri hevesli olarak hesaplandığı için. foreach döngüsü içinde kullanılan selam değerinin tembel bir şekilde her seferinde gerektikçe hesaplanmasıdır.1 Örnek İşlevin davranışını değiştirmenin bir örneği olarak bilgisayar ve kullanıcının zar attıkları bir oyun düşündüm.Tembel Değerlendirmeler } string[] selamlar = [ "Merhaba". Bu örnekte sonunda " dünya!" olan beş mesaj yazdırılacağı kesindir. selamın her yazdırılışında rasgele seçilmesini sağlar: void selamVer(in int adet. Yani parametresi bir değer değildir. işleve "al bu selamı kullan" demiş gibi oluruz. selam ifadesi daha selamVer işlevi çağrılmadan önce main içinde belirlenir ve bu yüzden de rasgele seçilen selam beş kere tekrarlanmış olur. ama mesajın baş tarafına ne yazdırılacağının seçimi. programlama açısından son derece güçlü bir olanaktır. parametrenin tanımında yapılan küçük bir değişiklik.org 169 .

oyuncununPuanı). çünkü seçtiği işlev zarOyunu işlevine bir lazy parametre olarak gönderiliyor.org 170 . import std.Tembel Değerlendirmeler hileYapsın ? hileliZarlar() : rasgeleZarlar(). import std. foreach (i. http://ddili. bilgisayarınPuanı. Not: Umarım ?: işlecinin kullanılması karışıklığa neden olmaz. } void bilgiVer(in int oyunSayısı. oyunSayısı. in int bilgisayarınAtışı. } /* * 5 ve 6 arasında eşit dağılımlı bir değer döndürür */ int hileliZar() { return uniform(5. Seçilen işlevi kendisi işletmiyor. 7).random. in int oyuncununAtışı. oyuncununAtışı.%d". in int oyuncununPuanı) { writefln( "%3d: bilgisayar %2d %2d oyuncu skor:%3d . parametre olarak verilen bu davranışa göre oynatılıyor. O işleç en başta hangi işlevin kullanılacağına karar vermek için kullanılıyor. bilgisayarınAtışı. /* * 1 ve 6 arasında eşit dağılımlı bir değer döndürür */ int rasgeleZar() { return uniform(1. } /* * Birisi hileli olan iki zarın toplamını döndürür */ int hileliZarlar() { return rasgeleZar() + hileliZar(). adet) { Copyright © 2009-2011 Ali Çehreli.stdio. lazy int bilgisayarınZarAtışı.string. } void zarOyunu(in int adet.. int oyuncununPuanı. Diğer açıklamaları programın içine yazdım: import std. Yukarıdaki iki işlevden duruma göre birisi zarOyunu işlevine lazy parametre olarak gönderiliyor ve bütün oyun. } /* * İki tane rasgele zarın toplamını döndürür */ int rasgeleZarlar() { return rasgeleZar() + rasgeleZar(). 7). 0 . in int bilgisayarınPuanı. lazy int oyuncununZarAtışı) { int bilgisayarınPuanı.

Aynı işlevin değer belirtmek için kullanılan parametresini lazy olarak belirleyerek.7] . http://ddili. Dizinin tek sayılı indekslerindeki (1. bilgisayarınPuanı. void main() { const bool hileYapsın = bilgisayarHileYapsın_mı().) elemanlarının değerleri o değerle değiştirilsin. Bu işlevi lazy parametre kullanmadan yazabilirsiniz. ve delegate gibi fonksiyonel programlama ve nesneye yönelik programlama olanaklarını da daha kolay anlayacaksınız.33. D'nin daha sonra göreceğimiz class . hileYapsın ? hileliZarlar() : rasgeleZarlar().. Bu sefer değer alan parametreyi lazy yaparak ona hem sabit bir değer.. vs. bilgisayarınBuAtışı.55] gibi bir dizi ve 7 gibi bir değer verildiğinde dizi şu hale gelsin: [0. çözümler Copyright © 2009-2011 Ali Çehreli.22. hem bir önceki çözümde olduğu gibi 7 gibi bir değerle. Eriştiğimiz değerleri iki yerel * değişkende saklıyoruz.22. string yanıt = chomp(readln()).11. Örneğin [0. } else if (bilgisayarınBuAtışı < oyuncununBuAtışı) { ++oyuncununPuanı. */ const int bilgisayarınBuAtışı = bilgisayarınZarAtışı. } rasgeleZarlar()). zarOyunu(10.1 Problemler 1. hem de örneğin 100 ve 200 arasında rasgele bir değerle çağrılabilmesini sağlayın. oyuncununPuanı). } bilgiVer(i + 1. 2.org 171 . } return (yanıt == "e") || (yanıt == "E"). 38. } } bool bilgisayarHileYapsın_mı() { write("Bilgisayar hile yapsın mı? (E/H) "). hem de bir işlev verebilirsiniz. İşlevlere değer göndermenin yanında davranış da gönderilebildiği kavramını şimdiden anlarsanız. oyuncununBuAtışı. const int oyuncununBuAtışı = oyuncununZarAtışı. 5.44. . 3.Tembel Değerlendirmeler /* * lazy parametrelerin değerlerinin yalnızca bir kere * hesaplanmaları için onlara bu döngü içinde bir kere * erişmemiz gerekir.44. if (bilgisayarınBuAtışı > oyuncununBuAtışı) { ++bilgisayarınPuanı.7.7. function . Bir int dizisi ve bir değer alan bir işlev yazın.

Örneğin klasörde bulunmayan bir dosyayı görmek istediğimizde. aşağıdaki örnekler bir Linux ortamında denenmiş olsalar da. Programımız. Aynı adımları denemek istediğinizde o satırları sizin yazarak Enter'a basmanız gerekir. Örneğin Unix türevi ortamlarda dosyaları listelemek için kullanılan ls programı. benzerlerini örneğin Windows DOS pencerelerinde de kullanabilirsiniz.1 main 'in dönüş değeri Programlar her zaman için başka bir ortam tarafından başlatılırlar. kullanıcın yazdığı satırları gösteriyor. Programın dönüş değeri olarak 0 değeri. programı ismini yazıp Enter'a basarak başlattığımız uç birim olabilir. Bunun dışındaki değerler her ortam tarafından desteklenmiyor olabilir. 0'ın başarı anlamına gelmesi standartlaşmıştır. # ls klasorde_bulunmayan_bir_dosya ls: klasorde_bulunmayan_bir_dosya: No such file or directory # echo $? 2 ← programın dönüş değeri 39. 0'dan başka bir değer ise programın çalışması sırasında bir hata oluştuğunu bildirmek için kullanılır. Ek olarak. http://ddili. main . kendisini başlatan bu ortama işini başarıyla tamamlayıp tamamlamadığı bilgisini. Aslında bu mümkün değildir. çünkü programı başlatan ortam bir dönüş değeri bekler. Şimdiye kadar gördüğümüz kadarıyla.127] aralığındaki tamsayılara güvenebilirsiniz. programın başarıyla sonuçlandığını. vs.1 Dönüş değeri void olan main 'ler de değer üretirler Şimdiye kadar karşılaştığımız işlevlerin bazılarının işlerini yapamayacakları durumlara düştüklerinde hata attıklarını görmüştük. Bu ortam. programın dönüş değerini komut satırında $? değişkeninden şöyle okuyabiliriz: Not: Aşağıdaki komut satırı örneklerinde # karakteriyle başlayan satırlar. main 'in dönüş değeri olarak bildirir. ciddi hatalarda ise 2 değerini döndürür.main'in Parametreleri ve Dönüş Değeri 39 main 'in Parametreleri ve Dönüş Değeri İşlevleri anlatırken main 'in de bir işlev olduğunu söylemiştim.Exception mesajıyla sonlanıyordu. programı kendisi başlatan başka bir program olabilir. Programlarımızdan istediğimiz değeri döndürmek bize kalmış olsa da. Sıfırdan başka değerler her programa göre değişik anlamlar taşıyabilir. Programın işleyişi main 'le başlar ve oradan başka işlevlere dallanır. main 'in şimdiye kadar gördüğümüz tanımı şöyleydi: void main() O tanıma bakarak main 'in bir değer döndürmediğini ve hiçbir parametre almadığını düşünürüz.org 172 . Not: Dönüş değeri olarak ancak [0. O satırları daha koyu olarak gösterdim.1. menülerindeki "Çalıştır" gibi bir komutla başlattığımız bir geliştirme ortamı olabilir. Copyright © 2009-2011 Ali Çehreli. hata atıldığı zaman program bir object. void döndürüyor gibi yazılmış olsa da aslında bir değer döndürür. 39. önemsiz hatalarda 1 değerini. Komut satırından başlatılan programların dönüş değerleri $? ortam değişkeninden okunabilir.

readf(" %s". sayı). Programın isminin deneme olduğunu kabul edersek ve istenen aralıkta bir sayıyla başlatırsak. write("Lütfen 3-6 arasında bir sayı giriniz: ").org 173 . main 'i dönüş türü int olacak şekilde tanımlamak ve bir return satırı kullanmak kadar basittir: import std. } writeln("Teşekkür: ".stdio.1. programı çalıştıran ortama 1 değeri döndürülür.stdio. başka işlevlerde de olduğu gibi. } Dönüş türü void olarak tanımlandığı halde programı çalıştıran ortama 1 değeri döndürülmüştür: # ~/deneme/d/deneme object. yani main bir değer döndürmeyecekmiş gibi yazılmış olsa bile. " uygun değil!"). } # ~/deneme/d/deneme Başardım! # echo $? 0 39.main'in Parametreleri ve Dönüş Değeri Bu gibi durumlarda. Yine son derece basit. sayı. Bunun bir örneğini görmek için hata atarak sonlanan şu son derece basit programı çalıştıralım: void main() { throw new Exception("Bir hata oldu").Exception: Bir hata oldu # echo $? 1 Benzer şekilde. } return 0. int main() { int sayı. return 3. http://ddili. programın dönüş değeri 0 olur: Copyright © 2009-2011 Ali Çehreli. &sayı). void main() { writeln("Başardım!"). ama bu sefer başarıyla sonlanan bir program: import std. if ((sayı < 3) || (sayı > 6)) { stderr.writeln("HATA: ".2 Kendi dönüş değerlerimizi belirlemek Kendi programlarımızdan değer döndürmek. dönüş türü void olarak tanımlanmış olan main işlevleri başarıyla sonlandıklarında. dönüş değeri olarak 0 üretirler. main 'in dönüş türü olarak void kullanılmış olsa bile.

39.d İsteğe bağlı olarak da bir veya daha çok parametreyle başlatılabilir. ve özel bir nedeni olmadan 3 değerini seçtim. bu parametrelere erişmek için yeterlidir. standart akımların üçüncüsüdür ve programın hata mesajlarını yazmak için kullanılır: • stdin : standart giriş akımı • stdout : standart çıkış akımı • stderr : standart hata akımı Programlar komut satırından başlatıldıklarında stdout ve stderr akımlarına yazılanlar normalde ekranda belirirler./deneme Lütfen 3-6 arasında bir sayı giriniz: 5 Teşekkür: 5 # echo $? 0 Aralığın dışında bir değerle başlatırsak.3 main 'in parametreleri Bazı programlar kendilerini başlatan ortamlardan parametre alabilirler. http://ddili.org 174 . main 'e bir string dizisi olarak gönderilirler. Ben yalnızca örnek olması için. programdan döndürdüğümüz 3 değerini görürüz: # ~/deneme/d/deneme Lütfen 3-6 arasında bir sayı giriniz: 10 HATA: 10 uygun değil! # echo $? 3 Normalde hata için 1 değerini kullanmak yeterlidir.stdio.main'in Parametreleri ve Dönüş Değeri # . Bu akım. Bu akımlara yazılan mesajları istendiğinde ayrı ayrı elde etmek de mümkündür. void main(string[] parametreler) Copyright © 2009-2011 Ali Çehreli. 39. main 'i string[] parametre alacak şekilde tanımlamak. Bu parametrelerin anlamları bütünüyle programa bağlıdır ve o programın belgelerinde belirtilmiştir: # ls -l deneme -rwxr-xr-x 1 acehreli users 460668 Nov 6 20:38 deneme D programlarını başlatırken kullanılan parametreler.2 Standart hata akımı stderr Yukarıdaki programda stderr akımını kullandım. Örneğin başlatılırken kendisine verilen parametrelerini çıktısına yazdıran bir program şöyle yazılabilir: import std. Örneğin yukarıda gördüğümüz ls programı bazen parametresiz olarak yalnızca ls yazarak başlatılabilir: # ls deneme deneme.

parametre). } writeln(parametreler[2].getopt modülü main 'in parametreleriyle ve dönüş değeriyle ilgili olarak bilinmesi gerekenler aslında bu kadardır. int main(string[] parametreler) { if (parametreler. http://ddili.length != 3) { stderr. o programın ekrana yazdıracağı bilgiyi belirliyordu. artık tamamen programa kalmıştır. Program. bu dizinin geri kalan elemanlarıdır. Bu parametrelerle ne yapacağı. her zaman için program başlatılırken kullanılan program ismidir. parametre. program tarafından bilgi olarak kullanılırlar. Örneğin yukarıda kullandığımız ls programına. Ancak parametreleri teker teker listeden ayıklamak zahmetli olabilir. yanlış başlatıldığında uyarı da veriyor: # . Bazı parametreler ise programın işini nasıl yapacağını belirlerler.org 175 .4 Program seçenekleri ve std./deneme dünya merhaba merhaba dünya 39. parametreler[0]. Bazı parametreler. ' '.writeln("HATA! Doğru kullanım:\n ". Onun yerine. Örneğin kendisine verilen iki sözcüğü ters sırada yazdıran bir program: import std.getopt modülünün bir kullanımını göstereceğim./deneme 1 numaralı parametre: komut 2 numaralı parametre: satirina 3 numaralı parametre: yazilan 4 numaralı parametre: parametreler 5 numaralı parametre: 42 6 numaralı parametre: -bir_secenek Parametre dizisinin ilk elemanı./deneme komut satirina yazilan parametreler 42 -bir_secenek 0 numaralı parametre: . } return 0. parametreler) { writefln("%3s numaralı parametre: %s". } # ./deneme bir_sözcük başka_sözcük # . Diğer parametreler.stdio.main'in Parametreleri ve Dönüş Değeri { } foreach (i. return 1. bunlara program seçeneği denir. i. parametreler[1]). Örneğin yukarıdaki programa verilen "dünya" ve "merhaba" parametreleri. Copyright © 2009-2011 Ali Çehreli. komut satırında seçenek olarak -l vermiştik. bu konuda yardım alabileceğimiz std. " bir_sözcük başka_sözcük")./deneme HATA! Doğru kullanım: .

her seçenek -. void main(string[] parametreler) { int adet. http://ddili. &adet./deneme --bir_secenek=17 Phobos'un std. onların da bir standardı gelişmiştir. bu değerleri program parametrelerinden seçip çıkartmakta yararlıdır.ile başlar. Seçeneklerin kestirmeleri.getopt modülü. getopt 'a okuduğu değerleri yazacağı değişkenleri. Artık programı bu kestirme seçeneklerle de başlatabiliriz: Copyright © 2009-2011 Ali Çehreli. &adet. POSIX standardına uygun bir kullanımda. Kaç tane sayı yazdıracağı ve sayıların değerlerinin hangi aralıkta olacağı programa komut satırından seçenekler olarak verilsin. Çıkışına rasgele seçtiği sayıları yazdıran bir program tasarlayalım. Böylece bu programlar örneğin betik programlar içinden çağrılabilirler. &enKüçükDeğer. readf kullanımından tanıdığımız & karakteriyle bir gösterge olarak bildiririz: import std. int enBüyükDeğer.org 176 . "en-kucuk|k". Örneğin --adet yazmak yerine kısaca -a yazılır.. } } writeln(). &enBüyükDeğer). getopt(parametreler. int enKüçükDeğer. &enKüçükDeğer. "en-kucuk". import std.main'in Parametreleri ve Dönüş Değeri Program seçenekleri. enBüyükDeğer + 1). 0 . foreach (i. import std. &enBüyükDeğer). seçenek ismi bunlara bitişik olarak yazılır. adet) { write(uniform(enKüçükDeğer. "en-buyuk|b".stdio.getopt./deneme --adet=7 --en-kucuk=10 --en-buyuk=15 getopt işlevi. ' '). "adet|a". "en-buyuk". Ben burada fazla ayrıntıya girmeden yalnızca getopt işlevinin bir kullanımını göstereceğim. Program örneğin şu şekilde başlatılabilsin: # .random. programa parametre olarak verilen bu tür seçeneklerin ayıklanmasında yardımcı olur. Komut satırı parametrelerinin ne oldukları ve ne anlama geldikleri her ne kadar tamamen programa bağlı olsalar da./deneme --adet=7 --en-kucuk=10 --en-buyuk=15 11 11 13 11 14 15 10 Çoğu zaman parametrelerin kestirmeleri de olur. Programın istediği parametreler. getopt 'a | ayracından sonra bildirilir: getopt(parametreler. ve seçenek değeri de bir = karakterinden sonra gelir: # . teker teker bir kişi tarafından verilmek yerine komut satırından okunurlar. programların kullanışlılıklarını arttırır. "adet". # .

process./deneme 3 2 4 39.conv modülünde tanımlanmış olan to 'yu daha önce hep tamsayıları string 'e dönüştürmek için to!string şeklinde kullanmıştık. getopt 'u kullanmadığımız zamanlarda bu dönüşümü daha önce de bir kaç kere kullandığımız to işleviyle kendimiz de gerçekleştirebiliriz. std.conv. if (parametreler. Program. dış dünyadan programa verilen bilgiler oldukları için ortam değişkenlerine de kısaca değinmek istiyorum. programların yararlanmaları için ortam değişkenleri de sunarlar.main'in Parametreleri ve Dönüş Değeri # .process modülündeki getenv işleviyle erişilir. import std./deneme -a=7 -k=10 -b=15 11 13 10 15 14 15 14 Parametrelerin programa string türünde geldiklerini görmüştük. adet) { write(i * 2. } } writeln(). parametre verilmediğinde 10 adet. getopt . 0 .length > 1) { // Bir parametre verilmiş adet = to!int(parametreler[1]).stdio. başka türlere de dönüştürebiliriz./deneme 2 4 6 8 10 12 14 16 18 . import std. } Copyright © 2009-2011 Ali Çehreli. http://ddili. void main() { writeln(getenv("PATH")).. Programları başlatan ortamlar. verildiğinde ise belirtildiği kadar sayı üretir: # 0 # 0 . Bu değişkenlere std. ' '). onu string 'den int 'e çevirir.5 Ortam değişkenleri Bu konuyla doğrudan ilgili olmasa da. Örneğin çalıştırılacak olan programların hangi klasörlerde arandıklarını belirten PATH değişkenini yazdırmak için: import std. Örneğin yukarıdaki programdaki adet 'in int olduğunu bildiği için. void main(string[] parametreler) { // Parametre verilmediğinde 10 varsayıyoruz int adet = 10. bu dizgileri değişkenlerin türlerine otomatik olarak dönüştürür. } foreach (i. Örneğin 0'dan başlayarak kendisine komut satırından bildirilen sayıda ikişer ikişer sayan bir programda to 'yu şöyle kullanabiliriz: import std. string yerine başka bir tür yazarsak.org 177 .stdio.

process modülünde bulunur. void main() { const int dönüşDeğeri = system("ls -l deneme"). bu programı başlatan ve dönüş türünü bildiren bir program yazın. dönüşDeğeri. Siz yine de * kullanmak isterseniz..8 26. 2./deneme /usr/local/bin:/usr/bin:/home/acehreli/dmd/linux/bin 39.. } writeln("ls programı ". Komut satırından parametre olarak iki değer ve bir işlem karakteri alan bir hesap makinesi yazın.52 Not: * karakterinin komut satırında özel bir anlamı olduğu için ben çarpma için x karakterini seçtim. Şöyle çalışsın: # .org 178 .4 x 7. çözümler Copyright © 2009-2011 Ali Çehreli. ." değerini döndürdü"). ve o programların dönüş türlerini almalarını sağlayan system işlevine de değinmek istiyorum.main'in Parametreleri ve Dönüş Değeri # .stdio. import std.7 Problemler 1.process. programların başka programları başlatmalarını. komut satırında \* şeklinde yazmanız gerekir.6 Başka programları başlatmak Yine bu konuyla doğrudan ilgili olmasa da. kendisine parametre olarak verilen dizgiyi sanki komut satırında yazılmış gibi başlatır ve programın dönüş değerini döndürür: import std./deneme -rwxr-xr-x 1 acehreli users 461107 Nov ls programı 0 değerini döndürdü 7 17:33 deneme 39. # ./deneme 3. system . Bu işlev de std. Hangi programı başlatmak istediğinizi soran. http://ddili.

break. case "x": writeln(birinci * ikinci). Örneğin gereken bir bilgi elde edilemiyordur. Copyright © 2009-2011 Ali Çehreli. Kullanıcı hataları. http://ddili. Çaresiz durumlarda atılan hata örnekleriyle Phobos'ta da karşılaşırız. bir çevre aygıtı çalışmıyordur.ConvException@std/conv. programların yaşamlarının doğal parçalarıdır.org 179 . break. vs. to!int 'in attığı bir hatayla sonlanır. eldeki bilgi geçersizdir.conv. break. } Yukarıdaki switch deyiminde case 'lerle belirtilmiş olan dört işlem dışında ne yapılacağı bilinmemektedir.d(37): std. break. Önceki dersin problem çözümlerinde de olduğu gibi: switch (işlem) { case "+": writeln(birinci + ikinci).Hata Atma ve Yakalama 40 Hata Atma ve Yakalama Beklenmedik durumlar. vs. programların çalışmaları sırasında her zaman karşılaşılan durumlardır. case "/": writeln(birinci / ikinci). int olamayacak bir dizgiyle çağrıldığında hata atar: import std. yalnızca dört aritmetik işlemi destekleyen bir işlevin bunların dışındaki bir işlemle çağrılması durumunu düşünebilirsiniz. Böyle çaresiz kalınan durumlarda D'nin hata atma düzeneği kullanılarak işleme son verilir. default: throw new Exception("Geçersiz işlem: " ~ işlem). o program. Bu durumlar bazen normal işleyişe devam edilemeyecek kadar vahim olabilir. Örneğin bir dizgiyi int türüne dönüştürmek için kullanılan to!int .ikinci). case "-": writeln(birinci . void main() { const int sayı = to!int("merhaba").conv. # .conv(1161): Can't convert value `merhaba' of type const(char)[] to type int to!int 'in attığı yukarıdaki hatayı şu şekilde çevirebiliriz: "const(char)[] türündeki `merhaba' değeri int türüne dönüştürülemez". ortamdaki beklenmedik durumlar./deneme std. } "merhaba" dizgisi bir tamsayı değer ifade etmediği için. programcı hataları. O yüzden deyimin default kapsamında bir hata atılmaktadır. Devam edilemeyecek kadar kötü bir durum örneği olarak.

O hatanın yakalanması önerilmez.conv modülü içinde tanımlanmış olduğunu anlayabiliyoruz. hem de daha önceki derslerde gördük.stdio. İsmi de "dönüşüm hatası" anlamına gelen "conversion exception"dan türemiş olan ConvException 'dır. 40. Örneğin Phobos'taki hatalar ya Exception sınıfından. throw deyiminden sonraki adımlar işletilmez.. atacağınız hataları ya doğrudan Exception 'dan. ya da Error sınıfından türemişlerdir.conv. kendisinden sonra yazılan ifadenin değerini bir hata nesnesi olarak atar ve işleme hemen son verilmesine neden olur. 40. eğer işleme devam edilebilecek gibi bir durumla karşılaşmışsak. kurulurlarken hata mesajını string olarak alırlar. hata atılacak kadar çaresiz bir durum yok demektir.) Exception nesneleri. import std.1 Hata atmak için throw Bunun örneklerini hem yukarıdaki switch deyiminde. zaten devam etmek söz konusu olmamalıdır. } } return sayılar.Exception: Geçersiz 'adet' değeri: -5 Copyright © 2009-2011 Ali Çehreli. Başka bir bakış açısıyla. dizi birleştirme işleci olan ~ ile hata nesnesini attığınız noktada oluşturmak kolaylık sağlar: import std.1. ya da ondan türeteceğiniz daha belirgin türlerden atmanız gerekir./deneme object. (Not: Sınıflarla ilgili bir konu olan türemeyi daha sonra göreceğiz. O durumda hata atılmaz ve işlev bir çaresini bulur ve işine devam edebilir. Error . import std.1 Exception ve Error hata türleri throw deyimi ile yalnızca Throwable türünden türemiş olan nesneler atılabilir. Bu yüzden.conv. } int[] sayılar. hata kavramına uygun bir davranıştır: hatalar işlemlere devam edilemeyecek durumlarda atıldıkları için. Bu. fırlat" olan throw deyimi. Buna rağmen. http://ddili. Anlamı "at. 0 . Bu mesajı. foreach (i. Bu hatanın ismine bakarak onun std.random.org 180 . int[] rasgeleZarlarAt(int adet) { if (adet < 0) { throw new Exception( "Geçersiz 'adet' değeri: " ~ to!string(adet)). giderilemez derecede hatalı durumları ifade eder.Hata Atma ve Yakalama Hata mesajının baş tarafındaki std.ConvException da hatanın türünü belirtir. void main() { writeln(rasgeleZarlarAt(-5)). 7). } # . adet) { sayılar ~= uniform(1. programlarda ondan da türemiş olan Exception ve Error türleri kullanılır.

yalnızca programın dallanmasını göstermek: import std.org 181 . işlev. http://ddili. Örneğin main 'den çağrılan yumurtaYap adlı bir işlev.1. yumurtaYe().Hata Atma ve Yakalama 40. yumurtaYap(). işlev. dallanma düzeylerini değişik miktarlarda girintiyle gösterecek şekilde aşağıdaki gibi yazabiliriz. miktar * 2) { write(' '). ve o işlev de yumurtaHazırla adlı başka bir işlevi çağırabilir. 0). yumurtalarıPişir(). in int girintiMiktarı) { girinti(girintiMiktarı). malzemeleriHazırla()..2 Hata atıldığında bütün kapsamlardan çıkılır Programın. writeln("◁ ". } void bitiyor(in char[] işlev. in int girintiMiktarı) { girinti(girintiMiktarı). 0). 0 .stdio. bitiyor("main". Tabii bu programda işlevler yararlı işler yapmıyorlar. } void main() { başlıyor("main". ardından başka işlevlerin çağrılmaları. kendisi malzemeleriHazırla adlı başka bir işlevi çağırabilir. Okların işlev çağrıları anlamına geldiklerini kabul edersek. çağrılan işlevlerin kendilerini çağıran işlevlere dönmeleri. } void yumurtaYap() { başlıyor("yumurtaYap". burada amaç. İşlevlerin birbirlerini katmanlar halinde çağırmaları. writeln("▶ ". onlardan da daha başka işlevlere dallandığını görmüştük. " son satır"). " ilk satır"). bir ağacın dalları halinde gösterilebilir. 1). import std.conv. Copyright © 2009-2011 Ali Çehreli. main işlevinden başlayarak başka işlevlere. } } void başlıyor(in char[] işlev. vs. void girinti(in int miktar) { foreach (i. böyle bir programın dallanmasını şu şekilde gösterebiliriz: main | +--▶ yumurtaYap | | | +--▶ malzemeleriHazırla | | | | | +-▶ yumurtaHazırla | | +-▶ yağHazırla | | +-▶ tavaHazırla | | | +--▶ yumurtalarıPişir | +--▶ malzemeleriKaldır | +--▶ yumurtaYe Toplam 3 alt düzeye dallanan bu programı.

3). 1). 3). } void malzemeleriHazırla() { başlıyor("malzemeleriHazırla". } void malzemeleriKaldır() { başlıyor("malzemeleriKaldır". bitiyor("yumurtaHazırla". 3). yağHazırla(). 3). } void yumurtalarıPişir() { başlıyor("yumurtalarıPişir". bitiyor("yumurtalarıPişir". 2). bitiyor("yağHazırla". } void yumurtaHazırla() { başlıyor("yumurtaHazırla". 1). 2). } Normal işleyişi sırasında program şu çıktıyı üretir: Copyright © 2009-2011 Ali Çehreli. 2). 3). bitiyor("malzemeleriHazırla". yumurtaHazırla(). bitiyor("tavaHazırla". bitiyor("malzemeleriKaldır". bitiyor("yumurtaYe". tavaHazırla(). 1). } void tavaHazırla() { başlıyor("tavaHazırla". } void yağHazırla() { başlıyor("yağHazırla".org 182 .Hata Atma ve Yakalama } malzemeleriKaldır(). 3). void yumurtaYe() { başlıyor("yumurtaYe". bitiyor("yumurtaYap". http://ddili. 2). 2). 2).

Dolaptan kaç yumurta çıkartılacağını işlevler arasında main 'den başlayarak elden elden iletebiliriz. ve bu işlev birden az bir değer geldiğinde hata atsın: void yumurtaHazırla(int adet) { başlıyor("yumurtaHazırla". 1). programı yumurtaHazırla işlevine dolaptan kaç yumurta çıkartacağını parametre olarak belirtecek şekilde değiştirelim. if (adet < 1) { throw new Exception("Dolaptan " ~ to!string(adet) ~ " yumurta çıkartılamaz"). 0). programın dallanmasını bir kere de hata atıldığında görmek: // . 3). 3). ◁ işareti ile de son satırını gösterdik. ve en son main 'in son satırıyla sonlanıyor. amaç. } void yumurtaYap(int adet) { başlıyor("yumurtaYap". void main() { başlıyor("main". Şimdi. http://ddili. malzemeleriHazırla(adet). yumurtaYe(). başka işlevlere dallanıyor. Bu örnekte. Program main 'in ilk satırıyla başlıyor. bitiyor("main". Programın doğru olarak derlenebilmesi için tabii başka işlevleri de değiştirmemiz gerekir... yumurtaYap(-8).org 183 . main 'den bilerek geçersiz olan -8 değerini gönderiyoruz. 0). Copyright © 2009-2011 Ali Çehreli.Hata Atma ve Yakalama ▶ main ilk satır ▶ yumurtaYap ilk satır ▶ malzemeleriHazırla ilk satır ▶ yumurtaHazırla ilk satır ◁ yumurtaHazırla son satır ▶ yağHazırla ilk satır ◁ yağHazırla son satır ▶ tavaHazırla ilk satır ◁ tavaHazırla son satır ◁ malzemeleriHazırla son satır ▶ yumurtalarıPişir ilk satır ◁ yumurtalarıPişir son satır ▶ malzemeleriKaldır ilk satır ◁ malzemeleriKaldır son satır ◁ yumurtaYap son satır ▶ yumurtaYe ilk satır ◁ yumurtaYe son satır ◁ main son satır başlıyor ve bitiyor işlevleri sayesinde ▶ işareti ile işlevin ilk satırını. } } bitiyor("yumurtaHazırla". Bu durumda programın diğer tarafları da aşağıdaki gibi değiştirilebilir.

. Bu çıkış sırasında. İşlemlere devam etmeden bütün işlevlerden çıkılmasının mantığı.Hata Atma ve Yakalama } yumurtalarıPişir(). sonra malzemeleriHazırla işlevinden. Hatanın izlediği yolu kırmızı ile şu şekilde gösterebiliriz: ▲ | main ◀---+ | | +--▶ yumurtaYap ◀-+ | | | | +--▶ malzemeleriHazırla ◀-------+ | | | |hata | | +-▶ yumurtaHazırla X-+ | | +-▶ yağHazırla | | +-▶ tavaHazırla | | | +--▶ yumurtalarıPişir | +--▶ malzemeleriKaldır | +--▶ yumurtaYe Hata atma düzeneğinin yararı. tavaHazırla(). 1).. malzemeleriKaldır(). bitiyor("yumurtaYap". en alt düzeyden en üst düzeye doğru. Bunu sağlayan catch anahtar sözcüğünü biraz aşağıda gösteriyorum. Copyright © 2009-2011 Ali Çehreli. Programın bu halini çalıştırdığımızda. onu çağıran daha üst düzeydeki işlevlerin de başarısız olacakları anlamına gelmesidir. Alt düzey bir işlevden atılan hata. ve en sonunda da main işlevinden çıkılır. bitiyor("malzemeleriHazırla". hatalı bir durumla karşılaşıldığında dallanılmış olan bütün işlevlerin derhal terkedilmelerini sağlamasıdır. yağHazırla(). daha sonra yumurtaYap işlevinden. teker teker o işlevi çağıran üst düzey işlevlere geçer ve en sonunda main 'den de çıkarak programın sonlanmasına neden olur. // . en alt düzeydeki yumurtaHazırla işlevinin başarısızlıkla sonuçlanmış olmasının. http://ddili. 2).. void malzemeleriHazırla(int adet) { başlıyor("malzemeleriHazırla".org 184 . işlevlerin henüz işletilmemiş olan adımları işletilmez.Exception: Dolaptan -8 yumurta çıkartılamaz Hata oluştuğu an. 2). throw ile hata atıldığı yerden sonraki hiçbir satırın işletilmediğini görürüz: ▶ main ilk satır ▶ yumurtaYap ilk satır ▶ malzemeleriHazırla ilk satır ▶ yumurtaHazırla ilk satır object. önce yumurtaHazırla işlevinden. atılan hatanın yakalanması ve programın devam edebilmesi de mümkündür. yumurtaHazırla(adet). Bazı durumlarda.. } // .

try-catch deyimini. void main() { const int zar = dosyadanZarOku().. kullanıcının girdiği bu bilgiyi denetlemek daha uygun olabilir. atıldığı işlevden üst düzey işlevlere doğru adım adım ilerlerken. "r"). Copyright © 2009-2011 Ali Çehreli. bu anlamları göze alarak "çalıştırmayı dene. } writeln("Zar: ". // mutlaka işletilmesi gereken işlemler } Bu bloğu anlamak için önce aşağıdaki try-catch kullanmayan programa bakalım. import std. eğer devam edilememesinin nedeni kullanıcının girdiği bir bilgiyse. atılan hatanın bütün işlevlerden ve en sonunda da programdan hemen çıkılmasına neden olduğunu anlattım. onunla ilgilenen bir noktada try-catch deyimi ile yakalanabilir. Kullanıcıyla etkileşilen böyle bir durum. Kullanıcıya bir hata mesajı gösterilebilir ve bilgiyi geçerli olacak şekilde tekrar girmesi istenebilir. bu değer sıfırdan küçük çıktığında hata atabilir. Söz dizimi şöyledir: try { // çalıştırılması istenen ve belki de // hata atacak olan kod bloğu } catch (ilgilenilen_bir_hata_türü_nesnesi) { // bu türden hata atıldığında // işletilecek olan işlemler } catch (ilgilenilen_diğer_bir_hata_türü_nesnesi) { // bu diğer türden hata atıldığında // işletilecek olan işlemler // . Aslında atılan bu hata yakalanabilir ve hatanın türüne veya duruma göre davranılarak programın sonlanması önlenebilir. "try"ın anlamı "dene". dosya. Hata.. } finally { // hata atılsa da atılmasa da. Öte yandan. int dosyadanZarOku() { auto dosya = File("zarin_yazili_oldugu_dosya".stdio. Bu program.readf(" %s". &zar). Çünkü örneğin eksi adet öğrenci ile işine devam etmesi olanaksızdır. int zar.3 throw 'u ne zaman kullanmalı throw 'u gerçekten işe devam edilemeyecek durumlarda kullanın.Hata Atma ve Yakalama 40.org 185 . zar). seçime bağlı olarak başka catch blokları . } return zar. 40.1. programın atılan bir hata ile sonlanmasından daha uygun olabilir. "catch"in anlamı da "yakala"dır.conv.2 Hata yakalamak için try-catch deyimi Yukarıda. eğer hata atılırsa yakala" olarak anlatabiliriz... Örneğin kayıtlı öğrenci adedini bir dosyadan okuyan bir işlev. http://ddili. zar değerini bir dosyadan okuyor ve çıkışa yazdırıyor: import std.

/deneme (Dosyadan okuyamadım. ve main 'den bu işlevi çağıralım: import std.conv.readf(" %s". int zar. Eğer programı yine aynı şekilde klasörde zarin_yazili_oldugu_dosya dosyası olmadan başlatırsak. http://ddili. zar = 1. } catch (std. program çıkışına "Zar: " yazdıramamış ve hemen sonlanmıştır. } } return zar. &zar).Hata Atma ve Yakalama Dikkat ederseniz. dosyadanZarOku işlevi hiç hatalarla ilgilenmeden ve sanki dosya başarıyla açılacakmış ve içinden bir zar değeri okunacakmış gibi yazılmış. "r"). void main() { const int zar = dosyadanZarOkumayıDene().ErrnoException hata) { writeln("(Dosyadan okuyamadım. 1 varsayıyorum)"). } writeln("Zar: ". Bu. bu sefer programın hata ile sonlanmadığını görürüz: $ . O. int dosyadanZarOkumayıDene() { int zar./deneme std.ErrnoException@std/stdio. 1 varsayıyorum) Zar: 1 Copyright © 2009-2011 Ali Çehreli. dosya. Şimdi programa dosyadanZarOku işlevini bir try bloğu içinde çağıran bir işlev ekleyelim.exception. import std. int dosyadanZarOku() { auto dosya = File("zarin_yazili_oldugu_dosya". try { zar = dosyadanZarOku(). zar). hata atma düzeneğinin başka bir yararıdır: işlevler her şey yolunda gidecekmiş gibi yazılabilirler.org 186 . yalnızca kendi işini yapıyor. } return zar. Şimdi o programı klasörde zarin_yazili_oldugu_dosya isminde bir dosya bulunmadığı zaman başlatalım: # .stdio. mesajı "'zarin_yazili_oldugu_dosya' 'r' modunda açılamıyor" olan bir ErrnoException atılmıştır. Yukarıda gördüğümüz diğer örneklere uygun olarak.d(286): Cannot open file `zarin_yazili_oldugu_dosya' in mode `r' (No such file or directory) Klasörde dosya bulunmadığı zaman.exception.

2. • ve programın işleyişine devam edilmektedir. işlevin işleyişi o catch bloğuna geçer ve o bloğun içindeki kodları çalıştırır.1 catch blokları sırayla taranır Örneklerde kendimiz hata atarken kullandığımız Exception . writeln("Komşuda yiyeceğim. hataya karşı önlem almakta.msg. Ama eğer özellikle belirtilmiş olan std. Bunu programın yukarıdaki çıktısında görüyoruz. ▶ main ilk satır ▶ yumurtaYap ilk satır ▶ malzemeleriHazırla ilk satır ▶ yumurtaHazırla ilk satır Yumurta yiyemedim: "Dolaptan -8 yumurta çıkartılamaz" Komşuda yiyeceğim.exception.org 187 . http://ddili.. o mesajı okuyan insanlara da hatayla ilgili bilgi verir. bu program bir hata atıldı diye artık hemen sonlanmamaktadır. ve hatanın içinde saklanmakta olan mesaj. try { yumurtaYap(-8). ve main işlevi normal olarak sonuna kadar işletilmektedir. File atıyor) • bu hata catch ile yakalanmakta. } catch (Exception hata) { write("Yumurta yiyemedim: "). Başka bir örnek olarak. } } bitiyor("main". hata.ErrnoException hatası atılmakta. yumurtalı programa dönelim ve onun main işlevine bir try-catch deyimi ekleyelim: void main() { başlıyor("main".. ikincisi ise sistem işlemleriyle ilgili olduğunu anlatır.ErrnoException hatası atılırsa. İşte catch . '"'). Bu derste daha önce gördüğümüz ConvException ve ErrnoException ise daha özel hata türleridir: birincisi."). ◁ main son satır Görüldüğü gibi. 40. yumurtaYe(). Eğer hatasız çalışırsa.. Özetle. atılan hatanın bir dönüşüm ile ilgili olduğunu. • zar için 1 değeri varsayılmakta. Ancak. programda bir hata olduğunu belirtir. 0). 0). işleyişine devam etmekte. dosyadanZarOku işlevinin işleyişi bir try bloğu içinde denenmektedir. (bunu bizim kodumuz değil. ve programın işleyişine devam etmesini sağlar. writeln('"'. Program.Hata Atma ve Yakalama Bu kodda. klasörde zar dosyası bulunmadığı için • önceki programdaki gibi bir std.. atılabilecek olan hataları yakalayarak o durumlara uygun olarak davranılmasını. Exception sınıfı hatanın türü konusunda bir bilgi taşımaz.exception. Copyright © 2009-2011 Ali Çehreli. genel bir hata türüdür. işlev ondan sonra return zar. Bu hatanın atılmış olması. satırı ile normal olarak sonlanır.

artık diğer catch bloklarına bakılmaz.. Error ve Exception da kendilerinden daha genel olan Throwable sınıfından türemişlerdir. Bir try deyimi içerisindeki kodların (veya onların çağırdığı başka kodların) attığı hata. Throwable (yakalamayın) ↗ ↖ Exception Error (yakalamayın) ↗ ↖ ↗ ↖ .. Exception sınıfından türemişlerdir. Uyan bir catch bloğu bulunursa.. Exception ve Error 'ın daha özel türler olduklarını ifade eder. hata atabilecek kayıt işlemleri .. Atılan hataları özellikle belirli bir türden olacak şekilde yakalayabiliriz.org 188 . Not: Sıradüzen gösterimini daha sonraki Türeme dersinde göstereceğim. o hata yakalanmış olur ve o catch bloğunun içerisindeki kodlar işletilir. ("Throwable"ın anlamı "atılabilen"dir. Error ve Exception genel hata türlerinin daha özel halleridir.... Atılan hata türleri. Yukarıdaki şekil. o try deyiminin catch bloklarında belirtilen hata türlerine sırayla uydurulmaya çalışılır. .. ancak eğer catch bloğunda belirtilen türe uyuyorsa yakalanır. Throwable 'ın en genel. . genel bir kural olarak eğer yakalanması uygun bulunuyorsa. yakalanması önerilen en genel hata türü olduğu için Exception her zaman en sondaki catch bloğunda belirtilmelidir. http://ddili. Eğer atılan hatanın türü sırayla bakılan catch bloğunun hata türüne uyuyorsa. Yakalanmasının doğru olduğu sıradüzen.. Error türünden veya ondan türemiş olan hataların yakalanmaları önerilmez. Örneğin ErrnoException türünü yakalayarak dosya açma sorunu ile karşılaşıldığını anlayabilir ve programda buna göre hareket edebiliriz. Buna göre. catch bloklarının böyle sırayla taranmalarının doğru olarak çalışması için catch bloklarının daha özel hata türlerinden daha genel hata türlerine doğru sıralanmış olmaları gerekir. Error 'dan daha genel olduğu için Throwable 'ın yakalanması da önerilmez.. . Örneğin ÖzelBirHata türünü yakalamaya çalışan bir catch bloğu. catch bloklarındaki hata türlerini özelden genele doğru şu şekilde yazabilir: try { // . } catch (KayıtNumarasıHanesiHatası hata) { // özellikle kayıt numarasının bir hanesiyle ilgili // olan bir hata } catch (KayıtNumarasıHatası hata) { // kayıt numarasıyla ilgili olan. ama hanesi ile // ilgili olmayan daha genel bir hata } catch (KayıtHatası hata) { // kayıtla ilgili daha genel bir hata } catch (Exception hata) { // kayıtla ilgisi olmayan genel bir hata } Copyright © 2009-2011 Ali Çehreli. Örneğin öğrenci kayıtlarıyla ilgili hataları yakalamaya çalışan bir try deyimi. Atılan hata.Hata Atma ve Yakalama Phobos'taki çoğu başka hata gibi ConvException ve ErrnoException . Exception sıradüzenidir..) Her ne kadar catch ile yakalanabiliyor olsa da. ErrnoException hatasını yakalamaz..

} } writeln("son satır"). void yüzdeElliHataAtanİşlev() { if (uniform(0. import std.stdio..2 finally bloğu try-catch deyiminin son bloğu olan finally .org 189 . void main() { deneme().Exception@deneme. gerekmiyorsa yazılmayabilir. } O işlev hata atmadığında programın çıktısı şöyledir: ilk satır try'ın ilk satırı try'ın son satırı finally işlemleri son satır Hata attığında ise şöyle: ilk satır try'ın ilk satırı finally işlemleri object. http://ddili. } } void deneme() { writeln("ilk satır"). finally bloğu isteğe bağlıdır.3 try-catch 'i ne zaman kullanmalı try-catch deyimi.. } finally { writeln("finally işlemleri"). Copyright © 2009-2011 Ali Çehreli. atılmış olan hataları yakalamak ve bu durumlarda özel işlemler yapmak için kullanılır. // . isteğe bağlı olarak catch blokları da olabilir . hata atılsa da atılmasa da mutlaka işletilecek olan işlemleri içerir. 40..d(7): hata mesajı Görüldüğü gibi.2.Hata Atma ve Yakalama 40.random. finally 'nin etkisini görmek için %50 olasılıkla hata atan şu programa bakalım: import std. hata atıldığında "try'ın son satırı" ve "son satır" yazdırılmamış. ama finally bloğunun içi iki durumda da işletilmiştir. 2) == 1) { throw new Exception("hata mesajı"). writeln("try'ın son satırı"). try { writeln("try'ın ilk satırı")..2. yüzdeElliHataAtanİşlev().

hatalı durumlarda işletilmeleri gereken ifadelerin de catch bloklarına yazıldıklarını gördük. onları yakalamaya çalışan işlevlere bırakın. hataAtabilecekBirİşlev(). scope deyimleridir. } catch (Exception hata) { çıkış -= birDeğer.3 scope(exit) . kapsamdan başarıyla çıkılırken işletilir • scope(failure) : ifade. http://ddili. (Not: Yaşam süreçleriyle ilgili olan bu konuyu ilerideki bir derste anlatacağım. try bloğu olmadan kullanılamaz • bu bloklarda kullanılmak istenen bazı değişkenler o noktalarda geçerli olmayabilir: void birİşlev(ref int çıkış) { try { int birDeğer = 42. çıkış += birDeğer. yine ifadelerin kapsamlardan çıkılırken kesinlikle işletilmeleri ile ilgilidir: • scope(exit) : ifade. hata atıldığında çıkış 'ın değerini düzeltmeye çalışan yukarıdaki işlevi bir scope(failure) deyimiyle ve daha kısa olarak şöyle yazabiliriz: void birİşlev(ref int çıkış) { int birDeğer = 42. ve hata atılması durumunda onu eski haline getirmeye çalışmaktadır. Bu blokların kullanımlarıyla ilgili bir kaç gözlemde bulunabiliriz: • catch ve finally blokları. Hataları. ve scope(failure) Kesinlikle işletilmeleri gereken ifadelerin finally bloklarına. scope(failure) çıkış -= birDeğer. ilgili oldukları kodlardan uzakta kalacakları için istenmeyebilir catch ve finally bloklarına benzer şekilde işleyen ve bazı durumlarda daha uygun olan bir olanak. kapsamdan ne şekilde çıkılırsa çıkılsın işletilir • scope(success) : ifade.) • bir kapsamdan çıkılırken kesinlikle işletilmesi gereken ifadelerin hepsinin bir arada en aşağıdaki finally bloğuna yazılmaları. 40. çıkış += birDeğer. Başka durumlarda hatalara karışmayın. try-catch deyimini ancak ve ancak atılan bir hata ile ilgili özel işlemler yapmanız gereken veya yapabildiğiniz durumlarda kullanın. Örneğin. Copyright © 2009-2011 Ali Çehreli. Ne yazık ki. } hataAtabilecekBirİşlev(). scope(success) . kapsamdan hata ile çıkılırken işletilir Bu deyimler yine atılan hatalarla ilgili olsalar da. birDeğer yalnızca try bloğu içinde tanımlı olduğu için bir derleme hatası alınır. referans türündeki parametresinde değişiklik yapmakta.org 190 .Hata Atma ve Yakalama Dolayısıyla. Üç farklı scope deyimi. try-catch bloğu olmadan kullanılabilirler. } // ← derleme HATASI } Yukarıdaki işlev.

scope(success) { writeln("başarılıysa 1").4 Hata çeşitleri Hata atma düzeneğinin ne kadar yararlı olduğunu gördük. ifadeler . Hem alt düzeydeki işlemlerin. Buna bakarak her hatalı durumda hata atılmasının uygun olduğunu düşünmeyin.. programın durumunda yapılan değişikliklerin geri adımlar atılarak ters sırada yapılmalarını sağlar. scope(failure) writeln("hata atılırsa 2").Hata Atma ve Yakalama Yukarıdaki scope deyimi. Bunun nedeni. İşlevin çıktısı. } scope(failure) writeln("hata atılırsa 1"). http://ddili.org 191 . scope(exit) writeln("çıkarken 2"). scope deyimlerindeki ifadelerinin ters sırada işletilmeleri. scope deyimleri bloklar halinde de bildirilebilirler: scope(exit) { // . writeln("başarılıysa 2"). 40. Bunun bir yararı. kendisinden sonra yazılan ifadenin işlevden hata ile çıkıldığı durumda işletileceğini bildirir. } yüzdeElliHataAtanİşlev(). hem de o işleme bağımlı olan daha üst düzey işlemlerin hemen sonlanmalarına neden olur.Exception: hata mesajı Çıktılardan anlaşıldığı gibi.. Copyright © 2009-2011 Ali Çehreli... scope deyimlerinin ifadeleri ters sırada işletilmektedir. Böylece program yanlış bilgiyle veya eksik işlemle devam etmemiş olur. } Bu kavramları deneyen bir işlevi şöyle yazabiliriz: void deneme() { scope(exit) writeln("çıkarken 1"). yapılan bir değişikliğin hatalı durumda geri çevrilecek olduğunun tam da değişikliğin yapıldığı yerde görülebilmesidir. hata atılmayan durumda scope(exit) ve scope(success) ifadelerini içerir: çıkarken 2 başarılıysa 1 başarılıysa 2 çıkarken 1 Hata atılan durumda ise scope(exit) ve scope(failure) ifadelerini içerir: hata atılırsa 2 çıkarken 2 hata atılırsa 1 çıkarken 1 object. daha sonra gelen kodların daha önceki değişkenlerin durumlarına bağlı olmalarıdır. Hatanın çeşidine bağlı olarak farklı davranmak gerekebilir.

while (!dosyaKullanılabildi) { try { dosyayıKullan( dizgiOku("Dosyanın ismini giriniz")). Çünkü örneğin sistemde çalışmakta olan başka bir program tarafından silinmiş veya ismi değiştirilmiş olabilir.Hata Atma ve Yakalama 40. Önemli olan.. void main() { bool dosyaKullanılabildi = false. http://ddili. void dosyayıKullan(string dosyaİsmi) { auto dosya = File(dosyaİsmi. ": "). verilen dosya ismi geçerlidir. Eğer verilen bilgi geçersizse. bu tür bir hatanın programın sonlanmasına neden olmak yerine. } string dizgiOku(in char[] soru) { write(soru. dosya. import std. Yine de. örneğin bir sayı beklenen durumda "merhaba" gibi bir dizgi girilmiş olabilir. dosyayıKullan işlevi başarıyla sonlanmış demektir. /* * * * * * Eğer bu noktaya gelebildiysek.stdio.string. program bu denetimi yaptığı anda mevcut olduğu halde. // . Bir örnek olarak. Aldığımız dosya isminin geçerli olup olmadığı konusunda iki yol izleyebiliriz: • Bilgiyi denetlemek: std. böyle durumlarda kullanıcıya bir hata mesajı göstermek ve doğru bilgi girmesini istemek daha uygun olabilir. Yukarıda da gördüğümüz gibi. Bu yüzden bu noktada bu değişkenin değerini Copyright © 2009-2011 Ali Çehreli. string dizgi = chomp(readln()). kullanıcının girdiği bilginin doğrudan işlenmesinde ve o işlemler sırasında bir hata atılmasında da bir sakınca olmayabilir. kullanıcıdan dosya ismi alan bir programa bakalım. Yani. • Bilgiyi doğrudan kullanmak: Kullanıcıdan alınan bilgiye güvenebilir ve doğrudan işlemlere geçebiliriz. belki de aşağıdaki diğer yöntem daha uygundur.org 192 .4.. Ancak. } return dizgi. zaten File bir hata atacaktır: import std. Bu yüzden. Programın kullanıcıyla etkileştiği bir durumda programın hata ile sonlanması uygun olmayacağı için. az sonra File ile açılmaya çalışıldığında mevcut olmayabilir.file modülündeki exists işlevini kullanarak verilen isimde bir dosya olup olmadığına bakabiliriz: if (exists(dosya_ismi)) { // dosya mevcut } else { // dosya mevcut değil } Dosyayı ancak dosya mevcut olduğunda açarız. "r"). kullanıcıya geçerli bilgi girmesini söylemesidir.1 Kullanıcı hataları Hataların bazıları kullanıcıdan gelir.

Zaten başka çare kalmamıştır: ne bir kullanıcı hatasıyla ne de bir programcı hatasıyla karşı karşıyayızdır.5 Özet • Eğer bir kullanıcı hatasıyla karşılaşmışsanız ya kullanıcıyı uyarın. Eğer uygunsa. Eğer işimize devam edemiyorsak. programcıya hangi kaynak dosyanın hangi satırındaki beklentinin gerçekleşmediği bilgisini verir.writeln("Bu dosya açılamadı"). ya da o mantığın gerçekleştirilmesindeki bir hatadan. attığımız hatayı yakalayarak bir çare bulabilirler. } catch (std. } } } 40. İşlevin buna rağmen eksi bir değer alması. } void main() { menüSeçeneği(-1). programın yazımıyla ilgili olan. Örneğin yazılan bir işlevin programda kesinlikle sıfırdan küçük bir değerle çağrılmayacağından eminizdir. } core.d(3): Assertion failure assert . http://ddili. ya da yine de işlemlere devam ederek nasıl olsa bir hata atılacağına güvenin • Programın mantığında veya gerçekleştirilmesinde hata olmadığını garantilemek için assert 'ü kullanın • Bunların dışındaki durumlarda throw ile hata atın Copyright © 2009-2011 Ali Çehreli.) 40.org 193 .d dosyasının üçüncü satırı olduğu anlaşılıyor. Bunların ikisi de programcı hatası olarak kabul edilir. (Bu mesajda deneme. 40.AssertError@deneme. hata atmaktan başka çare yoktur.exception.ErrnoException açmaHatası) { stderr.2 Programcı hataları Bazı hatalar programcının kendisinden kaynaklanır.4.Hata Atma ve Yakalama * 'true' yaparak while'ın sonlanmasını * sağlıyoruz. yani programcının kendisinden kaynaklanan hatalı durumlarda hata atmak yerine bir assert kullanmak daha uygun olabilir (Not: assert 'ü bir sonraki derste anlatacağım.4.): void menüSeçeneği(int sıraNumarası) { assert(sıraNumarası >= 0). */ dosyaKullanılabildi = true.3 Beklenmeyen durumlar Yukarıdaki iki durumun dışında kalan her türlü hatalı durumda hata atmak uygundur. Bizim attığımız hatalar karşısında ne yapacakları bizi çağıran üst düzey işlevlerin görevidir.exception. Programın tasarımına göre bu işlev kesinlikle eksi bir değerle çağrılmıyordur. writeln("Dosya başarıyla kullanıldı"). Böyle. ya programın mantığındaki bir hatadan kaynaklanıyordur.

Hata Atma ve Yakalama • Hataları ancak ve ancak. yakaladığınızda yararlı bir işlem yapabilecekseniz yakalayın.org 194 . scope(success) . ve scope(failure) deyimleri ile belirtin Copyright © 2009-2011 Ali Çehreli. Yoksa hiç try-catch deyimi içine almayın. http://ddili. işletilmeleri kesinlikle gereken işlemleri scope(exit) . belki de işlevinizi çağıran daha üst düzeydeki bir işlev yakalayacaktır • catch bloklarını özelden genele doğru sıralayın • İşletilmeleri mutlaka gereken işlemleri finally bloğuna yazın • try bloğu kullanmanın gerekmediği durumlarda.

bu durumda assert ifadesinin hiçbir etkisi yoktur. 41. assert . Başka bir örnek olarak. yaş parametrelerinin ikisinin de sıfır veya daha büyük olacakları varsayılarak yazılmıştır: double ortalamaYaş(double birinciYaş. Programlar ancak bu varsayımlar ve beklentiler doğru çıktıklarında doğru çalışırlar. } Yaşlardan en az birisinin eksi bir değer olarak gelmesi hatalı bir durumdur. assert . } } Böyle bir varsayımda bulunduğu için. aşağıdaki işlev yalnızca iki komuttan birisi ile çağrılacağını varsaymaktadır: "şarkı söyle" ve "dans et": void komutİşlet(in char[] komut) { if (komut == "şarkı söyle") { robotaŞarkıSöylet(). programın dayandığı bu varsayımları ve beklentileri denetlemek için kullanılır. işlev mantıklı bir ortalama üretebilir ve program bu hata hiç farkedilmeden işine yanlış da olsa devam edebilir. yakalanmaması gerekir. Böyle bir hata atıldığında programın hemen sonlanması önemlidir. bu varsayımlarımızı dile getirmemizi sağlayan ve varsayımlar hatalı çıktığında işlemlerin durdurulmalarına neden olan bir olanaktır. bu hata Error 'dan türemiştir ve Hatalar bölümünde gördüğümüz gibi. hata_mesajı). Eğer ifadenin değeri true ise varsayım doğru çıkmış kabul edilir. Eğer ifadenin değeri false ise varsayım yanlış çıkmış kabul edilir ve AssertError hatası atılır. "şarkı söyle" dışındaki her komuta karşılık robotuDansEttir işlevini çağıracaktır. Eğer bu varsayımları kendimize saklarsak. Yukarıdaki ortalamaYaş işlevindeki varsayımlarımızı iki assert ile şöyle ifade edebiliriz: Copyright © 2009-2011 Ali Çehreli. eğer yanlışsa işlemi durdur" dememizi sağlar.1 Söz dizimi assert iki şekilde yazılabilir: assert(mantıksal_ifade). geçerli olsun olmasın. http://ddili. Örneğin iki kişinin yaşlarının ortalamasını alan aşağıdaki işlevde kullanılan hesap. kendisine verilen mantıksal ifadeyi işletir. bir anlamda programa "böyle olduğunu varsayıyorum. Programcının en etkili yardımcılarındandır. Buna rağmen. sonuçta ortaya çıkan program hatalı davranabilir. double ikinciYaş) { return (birinciYaş + ikinciYaş) / 2.assert İfadesi 41 assert İfadesi Programları yazarken çok sayıda varsayımda bulunuruz ve bazı beklentilerin doğru çıkmalarını umarız. assert(mantıksal_ifade. assert . böylece programın yanlış varsayımlara dayanarak yanlış olabilecek sonuçlar üretmesi önlenmiş olur. assert .org 195 . İsminden de anlaşılabileceği gibi. } else { robotuDansEttir(). Çoğu zaman bu varsayımların farkına bile varmayız.

Copyright © 2009-2011 Ali Çehreli. 41.2 Kesinlikle doğru olan (!) varsayımlar için bile assert "Kesinlikle doğru olan"ın özellikle üzerine basıyorum. Örneğin yukarıdaki "şarkı söyle" ve "dans et" örneğinde başka komutların geçersiz olduklarını belirtmek ve bu durumlarda hata atılmasını sağlamak için: void komutİşlet(in char[] komut) { if (komut == "şarkı söyle") { robotaŞarkıSöylet(). http://ddili. } return (birinciYaş + ikinciYaş) / 2. void main() { auto sonuç = ortalamaYaş(-1. } else { assert(false).d dosyasının beşinci satırında olduğunu anlarız. Hiçbir varsayım bilerek yanlış olmayacağı için.AssertError@deneme(5): Assertion failure Hatanın @ karakterinden sonra gelen bölümü hangi dosyanın hangi satırındaki varsayımın doğru çıkmadığını gösterir. } else if (komut == "dans et") { robotuDansEttir(). Bu örnekte. zaten çoğu hata kesinlikle doğru olan varsayımlara dayanır.assert İfadesi double ortalamaYaş(double birinciYaş.exception.AssertError@deneme. assert ifadesinin yanlış çıktığı durumda kendi yazdığımız özel bir mesajı görmek istediğimizde assert ifadesinin ikinci şeklini kullanırız: assert(birinciYaş >= 0. "birinciYaş'ın 0 veya daha büyük olduğundan eminim" gibi de düşünülebilir. assert bu varsayımları denetler ve yukarıdaki programda olduğu gibi.exception. "Yaş sıfırdan küçük olamaz"). Başka bir bakış açısıyla. } O assert 'ler Türkçe olarak "birinciYaş'ın 0 veya daha büyük olduğunu varsayıyorum" ve "ikinciYaş'ın 0 veya daha büyük olduğunu varsayıyorum" anlamına gelir. core. "assert" sözcüğünün "emin olarak öne sürmek" karşılığını kullanarak. deneme(5) 'e bakarak hatanın deneme. } } Artık işlev yalnızca o iki komutu kabul eder ve başka komut geldiğinde assert(false) nedeniyle işlem durdurulur. 10). özellikle başarısız olsun diye mantıksal ifade olarak bilerek false sabit değeri kullanılır. double ikinciYaş) { assert(birinciYaş >= 0). assert(ikinciYaş >= 0). varsayımın yanlış çıktığı durumda bir hata atar: core.org 196 .d(5): Yaş sıfırdan küçük olamaz Programda kesinlikle gelinmeyeceği düşünülen veya gelinmemesi gereken noktalarda.

31. assert((diziToplamı(günler) == 365) || (diziToplamı(günler) == 366)). 31. şubatGünleri işlevinde ileride yapılabilecek bir hata. Doğal olarak bu işlevin döndürdüğü dizideki gün toplamları ya 365 olacaktır.assert İfadesi Bu yüzden bazen kesinlikle gereksizmiş gibi duran assert ifadeleri de kullanılır. her ne kadar gereksiz gibi görünse de. } return günler. 41. 30. o assert sayesinde bu hata hemen farkedilecektir. Kodun sağlamlığını arttıran ve programın yanlış sonuçlar doğuracak işlemlerle devam etmesini önleyen bir olanak olduğu için. yani dönüş türü void 'dir.4 Ne zaman kullanmalı assert ifadesinin hata atan bir olanak olması. 41. Üstelik. assert . ona verilen mantıksal ifadenin de yan etkisinin olmaması. D standardı tarafından şart koşulmuştur. dizinin Şubat elemanının örneğin 30 olmasına neden olsa. ya da 366. Bu yüzden. programın durumunu değiştirmeyen ve yalnızca varsayımları denetleyen bir yapı olarak kalmak zorundadır. assert bundan sonraki derslerde göreceğimiz birim testleri ve sözleşmeli programlama olanaklarının da temelini oluşturur. 30. assert değer üretmeyen bir ifadedir.3 Değer üretmez ve yan etkisi yoktur İfadelerin değer üretebildiklerini ve yan etkilerinin olabildiğini söylemiştim. 31. onun yerine bizim doğrudan hata atabileceğimiz düşüncesini doğurabilir. Hatalı durumlarda bu iki olanaktan hangisini kullanacağımızın kararı bazen güç olabilir. 31. 30. 30. Siz de programlarınızda bunu Copyright © 2009-2011 Ali Çehreli. Başka dillerde assert hata atmaz ve programı hemen sonlandırır. Oysa. Bu yüzden yukarıdaki assert ifadesinin gereksiz oduğunu düşünebilirsiniz. geleneksel olarak programcı hatalarını yakalamak için kullanılır. Örneğin belirli bir senenin aylarının kaç gün çektikleri bilgisini bir dizi olarak döndüren bir işlev ele alalim: int[] ayGünleri(in int yıl) { int[] günler = [ 31. o ifade şubatGünleri işlevinde ilerideki bir zamanda yapılabilecek bir hataya karşı bir güvence sağlar. şubatGünleri(yıl). assert ifadesinin yan etkisi de yoktur. Ek olarak. 31. Hatta biraz daha ileri giderek dizinin uzunluğunun her zaman için 12 olacağını da denetleyebiliriz: assert(günler. Kodun sağlamlığını arttıran ve kodu ilerideki değişiklikler karşısında güvencede tutan çok etkili yapılardır. Böylece kendimizi diziden yanlışlıkla silinebilecek veya diziye yanlışlıkla eklenebilecek bir elemana karşı da güven altına almış oluruz. 31 ]. Böyle denetimler her ne kadar gereksizmiş gibi görünseler de son derece yararlıdırlar. http://ddili.org 197 .length == 12).

in int dakika) { assert((saat >= 0) && (saat <= 23))..assert İfadesi uygulayabilir. Program kullanıcıdan bir başlangıç zamanı ve bir işlem süresi alıyor ve o işlemin ne zaman sonuçlanacağını hesaplıyor. Buna bakarak assert ifadesini belki de yalnızca birim testlere ve sözleşmeli programlamaya ayırabilir. out int sonuçSaati.org 198 . 10:8'de sonlanır import std. Program. :o) 41. } return to!dstring(saat) ~ ":" ~ to!dstring(dakika). assert((dakika >= 0) && (dakika <= 59)). out int dakika) { write(mesaj.5 Problemler 1. orada zaten seçiminiz olmayacak.. Amacım. in int başlangıçDakikası. "? (SS:DD) "). out int sonuçDakikası) Copyright © 2009-2011 Ali Çehreli. out int saat.conv. Daha önce de duyduğunuz gibi: karar sizin. if ((saat < 0) || (saat > 23) || (dakika < 0) || (dakika > 59)) { throw new Exception("Geçersiz zaman"). Programcının şubatGünleri içerisinde ileride yapabileceği hataları önler.stdio. readf(" %s:%s". /* * İki zaman bilgisini birbirine ekler ve üçüncü parametre * çifti olarak döndürür */ void zamanEkle( in int başlangıçSaati. &saat. ve diğer hatalı durumlarda hata atma olanağını kullanabilirsiniz. sayılardan sonra gelen 'da' eklerini de doğru olarak yazdırıyor: 9:6'da başlayan ve 1 saat 2 dakika süren işlem. &dakika). ve hatanın program ile ilgili olduğundan emin olduğunuz durumlarda assert ifadesini kullanabilirsiniz. Bundan sonra göreceğimiz birim testlerinde ve sözleşmeli programlamada assert temel olarak kullanıldığı için. tamamen programcılıkla ilgili hatalara karşı bir güvence olarak kullanılmıştır. } } /* * Zamanı dizgi düzeninde döndürür */ dstring zamanDizgisi(in int saat. /* * Verilen mesajı kullanıcıya gösterir ve girilen zaman * bilgisini saat ve dakika olarak okur */ void zamanOku(in dchar[] mesaj. Bu problemde size önceden yazılmış bir program göstermek istiyorum. bu assert ifadelerinin programdaki hataları ortaya çıkartma konusunda ne kadar etkili olduklarını göstermek. import std. Bu programın hata olasılığını azaltmak için bazı noktalarına assert ifadeleri yerleştirilmiş. http://ddili. in int eklenecekDakika. in int eklenecekSaat. Örneğin yukarıdaki ayGünleri işlevinde kullanılan assert .

} } /* * Sayılardan sonra kesme işaretiyle ayrılarak kullanılacak * olan "de.assert İfadesi { sonuçSaati = başlangıçSaati + eklenecekSaat. in int başlangıçDakikası. int bitişDakikası. zamanOku("İşlem süresi". break. işlemDakikası. int işlemDakikası. bitişDakikası). başlangıçDakikası. Copyright © 2009-2011 Ali Çehreli.org 199 . switch (sonHane) { case 1: case 2: case 7: case 8: ek = "de". başlangıçDakikası. default: break. break. işlemSaati. } assert(ek. int işlemSaati. zamanEkle(başlangıçSaati. başlangıçSaati). void main() { int başlangıçSaati. sonucuYazdır(başlangıçSaati. http://ddili.length != 0). int bitişSaati. işlemSaati. işlemSaati. bitişDakikası). işlemDakikası). bitişSaati. } return ek. case 6: case 9: ek = "da". const int sonHane = sayı % 10. başlangıçDakikası. sonuçDakikası = başlangıçDakikası + eklenecekDakika. case 3: case 4: case 5: ek = "te". zamanOku("Başlangıç zamanı". if (sonuçDakikası > 59) { ++sonuçSaati. } void sonucuYazdır( in int başlangıçSaati. int başlangıçDakikası. bitişSaati. da" ekini döndürür */ dstring daEki(in int sayı) { dstring ek. işlemDakikası.

2. Bunu şimdilik görmezden gelin.. in int işlemDakikası. başlangıçDakikası). O satıra da gidin ve o hatayı da giderin. çözümler Copyright © 2009-2011 Ali Çehreli. daEki(başlangıçDakikası)). 4. writef(" ve %s saat %s dakika süren işlem. işlemSaati. Hatada belirtilen satıra gidin ve bu hatayı giderin. Bu sefer programa 6:9 ve 1:1 zamanlarını girin.. Programın normal olarak sonlandığını göreceksiniz.". çünkü az sonra assert 'lerin yardımıyla bulacaksınız. Not: Aslında çıktıda bir hata farkedebilirsiniz. Bu sefer programa 6:9 ve 15:2 zamanlarını girin. writef(" %s'%s sonlanır". vs.assert İfadesi { in int işlemSaati. http://ddili. zamanDizgisi(başlangıçSaati. in int bitişSaati. Programın da ekinin doğru çalışmadığını göreceksiniz: Başlangıç zamanı? (SS:DD) 6:9 İşlem süresi? (SS:DD) 1:41 6:9'da başlayan ve 1 saat 41 dakika süren işlem. Bir AssertError atıldığını göreceksiniz. 7:50'da sonlanır Bunu düzeltin ve duruma göre doğru ek yazmasını sağlayın: 7:10'da. 5. Bu programı çalıştırın ve girişine başlangıç olarak 6:9 ve süre olarak 1:2 verin. Bu sefer programa 6:9 ve 1:41 bilgilerini girin.. 3. Yeni bir hata ile karşılaşacaksınız. Yine hata. .. zamanDizgisi(bitişSaati. Bu sefer programa 6:9 ve 20:0 bilgilerini girin.org 200 . 7:50'de. 7:40'ta. bitişDakikası). daEki(bitişDakikası)). in int bitişDakikası) writef("%s'%s başlayan". } writeln(). işlemDakikası).

Yazılım hataları.org 201 . derleyicinin verdiği hata mesajları veya uyarılar çoğunlukla programcı hatalarını gösterirler ◦ Programın programcı tarafından oluşturulması sırasında birim testleri tarafından • Kod incelenirken ◦ Kaynak kodu inceleyen araç programlar tarafından. programdan istenenleri yanlış anlamış olabilir • Programlama dili. sürekli olarak çözüm bulunmaya çalışılan ve her beş on yılda bir ümit verici yöntemlerin ortaya çıktığı bir konudur. Bu konu.1 Hata nedenleri Yazılım hatalarının çok çeşitli nedenleri vardır. günümüzde henüz tam olarak sağlam kod üreten yazılım geliştirme yöntemleri bulunamamıştır. bir insana Türkçe anlatırken bile anlaşmazlıklar yaşandığını göz önüne alırsak. hatta belki de programın tam olarak ne yapacağı başından bilinmiyordur • Programcı.Birim Testleri 42 Birim Testleri Programcılığın kaçınılmaz uğraşlarından birisi hata ayıklamaktır. baştan düşünülmemiş olan bir durumla karşılaşabilir. 42. En erkenden en geçe doğru sıralayarak: • Kod yazılırken ◦ Programı yazan kişi tarafından ◦ Başka bir programcı tarafından. örneğin bir işlem sırasında toplamFiyat yerine toptanFiyat yazabilir • vs. içinde bilgisayar programı çalışan her cihaz yazılım hataları içerir. programdan istenenleri ifade etmekte yetersizdir. örneğin bir klasördeki dosyalardan birisi. Programın fikir aşamasından başlayarak kodlanmasına doğru kabaca sıralarsak: • Programdan istenenler açık bir şekilde ortaya konmamış olabilir. yapılan bir yazım hatasını programı yazan kişinin yanındaki programcı farkedebilir ◦ Derleyici tarafından. Ne yazık ki. Her kullanıcının yakından tanıdığı gibi. (başka diller için örneğin Coverity) Copyright © 2009-2011 Ali Çehreli.14 değerinin yeterli olduğu varsayılmış olabilir • Programcının bilgisi herhangi bir konuda yetersiz veya yanlış olabilir. örneğin pi sayısı olarak 3. istenenlerin tam olarak ifade edilmesi için yeterli olmayabilir • Programcının varsayımları yanlış çıkabilir. örneğin kesirli sayıların eşitlik karşılaştırmalarında kullanılmalarının güvensiz olduğunu bilmiyordur • Program. http://ddili. program o listeyi bir döngüde kullanırken silinmiş veya o dosyanın ismi değiştirilmiş olabilir • Programcı kodu yazarken dikkatsizlik yapabilir.2 Hatanın farkedildiği zaman Yazılım hatasının ne zaman farkına varıldığı da çeşitlilik gösterir. kol saati gibi basit elektronik aletlerden uzay aracı gibi büyük sistemlere kadar her yerde bulunur. 42. örneğin çiftli programlama (pair programming) yöntemi uygulandığında. bilgisayar dilinin karmaşık söz dizimleri ve kuralları.

ve çok sayıdaki kullanıcının da zamanını alır. hem de o kadar az sayıda insanın zamanını almış olur. 42. Kodun geliştirilmesi sırasında ortaya çıkan böyle hatalar. Ne yazık ki bunun tersi doğru değildir: birim testlerinin olması. program son kullanıcılar tarafından kullanılırken. don't fix it") gibi sözler. İşte bu yararlı yaklaşımın en güçlü silahı birim testleridir. yeni hatalardan korkulduğu için koda dokunulmaz ve kod tekrarı gibi zararlı durumlara düşülebilir. kod çürümesinin önüne geçmek için kodun gerektikçe serbestçe geliştirilmesi önerilir: "acımasızca geliştir" ("refactor mercilessly"). ya çok sonraki sürüm testleri sırasında farkedilirler. Bu yüzden en iyisi. Bu tür hatalar kodun yeniden düzenlenmesinden çekinilmesine ve kodun gittikçe çürümesine (code rot) neden olurlar. Alt birimlerin bağımsız olarak testlerden geçmeleri. Bu noktaya kadar farkedilmemiş olan bir hata. bazen aylarca sürebilen uğraşlar sonucunda temizlenebilir. Bunları bir kenara bıraktığımızda.3 Hata yakalamada birim testleri Kodu yazan programcı olmazsa zaten kod olmaz. Kod hatalarını azaltma konusunda en etkili yöntemlerdendir. modern programcılığın ayrılmaz araçlarındandır. doğal olarak o kodun eski olanaklarının artık hatalı hale gelmelerine neden olabilir. Birim testleri ayrıca kodun rahatça ve güvenle geliştirilebilmesini de sağlarlar. Birim testleri olmayan kod. Modern programcılıkta bu düşüncelerin yeri yoktur. ya da programın gözlemlenen davranışından ◦ Sürümden önce beta kullanıcıları tarafından test edilirken ◦ Sürümdeyken son kullanıcılar tarafından Hata ne kadar erken farkedilirse hem zararı o kadar az olur.org 202 . Birim testi. program hatalarını yakalamada en erken ve bu yüzden de en etkin yöntem olarak birim testleri kalır. ya assert ifadelerinin başarısızlığından. http://ddili. ama hata oranını çok büyük ölçüde azaltır. derlemeli bir dil olduğu için D programları zaten derleyici kullanmadan oluşturulamazlar. Tam tersine. programı test edenlerin. Bu gibi sözler. hatanın kodun yazıldığı sırada yakalanmasıdır. Örneğin bazı satırların aslında yeni bir işlev olarak yazılmasının gerektiği bir durumda. Programcı kültüründe duyulan "bozuk değilse düzeltme" ("if it isn't broken. yazılmış olan koda dokunmamayı erdem olarak gösterdikleri için zaman geçtikçe kodun çürümesine ve üzerinde değişiklik yapılamaz hale gelmesine neden olurlar. Geç farkedilen hata ise başka programcıların. kodun hatasız olduğunu kanıtlamaz. Son kullanıcıya gidene kadar farkedilmemiş olan bir hatanın kodun hangi noktasından kaynaklandığını bulmak da çoğu durumda oldukça zordur. Ayrıca. ya da daha kötüsü. o Copyright © 2009-2011 Ali Çehreli.Birim Testleri ◦ Kodu inceleyen başka programcılar tarafından kod incelemesi (code review) sırasında • Program kullanımdayken ◦ Programın işleyişini inceleyen araç programlar tarafından (örneğin Linux ortamlarındaki açık kodlu 'valgrind' programı ile) ◦ Sürümden önce test edilirken. hep bu korkunun ürünüdür. Birim testleri. programı oluşturan en alt birimlerin birbirlerinden olabildiğince bağımsız olarak test edilmeleri anlamına gelir. hatalı kod olarak kabul edilir. Kod üzerinde değişiklik yapmak. örneğin yeni olanaklar eklemek.

CppUnit. derleyici. Bu kodların programın normal işleyişi ile ilgileri yoktur. Her iki yaklaşımın da üstün olduğu yanlar gösterilebilir. Ben burada yalnızca D'nin bu iç olanağını göstereceğim. bir önceki derste gördüğümüz ve kendisine verilen sayıya Türkçe ses uyumuna uygun olarak da eki döndüren işleve bakalım. bu blokları denetledikleri işlevlerin hemen altına yazmaktır. ve ancak özellikle istendiğinde başlatılır. Ben burada örnek olarak Digital Mars'ın derleyicisi olan dmd 'nin -unittest seçeneğini göstereceğim. yalnızca programı ve özellikle işlevleri denemek için kullanılırlar: unittest { işlevi_deneyen_kodlar_ve_assert_ifadeleri } unittest bloklarını sanki işlev tanımlıyor gibi kendi başlarına yazabilirsiniz. 42.4 Birim testlerini başlatmak Programın asıl işleyişi ile ilgili olmadıkları için.5 unittest blokları Birim testlerini oluşturan kodlar bu blokların içine yazılır.d -ofdeneme -w -unittest Bu şekilde oluşturulan program çalıştırıldığında önce birim testleri işletilir ve ancak onlar başarıyla tamamlanırsa programın işleyişi main ile devam eder. Programın deneme. Örnek olarak. Bu işlevin doğru çalışmasını denetlemek için. gibi kütüphane olanakları olarak gerçekleştirilmişlerdir. D'de birim testleri. önceki derste gördüğümüz assert ifadelerinin unittest blokları içinde kullanılmalarından oluşurlar. Programı kodun içindeki birim testlerini de ekleyerek oluşturmak için bu komut satırına unittest seçeneği eklenir: dmd deneme. Birim testleri derleyici veya geliştirme ortamı tarafından. oluşturulacak olan programın ismini belirleyen seçenek (bu durumda deneme ). http://ddili. Bunun bir örneği olarak.d . Ama daha iyisi. Birim testlerinin nasıl başlatıldıkları kullanılan derleyiciye ve geliştirme ortamına göre değişir. birim testlerinin nasıl başlatıldıklarını göstermem gerekiyor.d -ofdeneme -w Kısaca: dmd . vs. Unittest++. komut satırında normalde şu şekilde oluşturabiliriz: dmd deneme. D'de birim testleri konusunu kütüphane olarak halleden Dunit projesine de bakmak isteyebilirsiniz. 42.org 203 . Bu blokları anlatmadan önce. -of . deneme. birim testlerinin yalnızca programın geliştirilmesi aşamasında çalıştırılmaları gerekir. bütünün de doğru çalışma olasılığı artar. Eğer parçalar doğru çalışıyorsa.Birim Testleri birimlerin birlikte çalışmaları sırasında oluşacak hataların olasılığını büyük ölçüde azaltır. unittest bloğuna bu işlevin döndürmesini beklediğimiz koşullar yazarız: Copyright © 2009-2011 Ali Çehreli.d isimli bir kaynak dosyaya yazıldığını varsayarsak. D'de ise birim testleri dilin iç olanakları arasındadır. ve -w da derleyici uyarılarını etkinleştiren seçenektir. derlenmesi istenen kaynak dosya. Birim testleri başka bazı dillerde JUnit.

Örneğin.. assert(daEki(9) == "da").Birim Testleri dstring daEki(in int sayı) { // . assert(daEki(9) == "da"). assert(daEki(5) == "te"). 5. Yalnızca birim testine bakarak işlevin kullanılışı hakkında hızlıca fikir edinebiliriz. ve "da" döndürüldüğünü denetler. assert(harfBaşa(dizgi. 'e') == "emrhaba"). http://ddili. assert(harfBaşa(dizgi.. "te".6 Test yönelimli programlama: önce test. Her ne kadar testlerin temeli assert denetimleri olsa da. birim testlerinin başarıya ulaşmalarını sağlayan ikincil bir uğraştır. birim testleri aynı zamanda işlevlerin belgeleri ve örnek kodları olarak da kullanışlıdırlar. harfBaşa işlevinin nasıl çalışmasının beklendiğini denetliyorlar.. in dchar harf) { // . } unittest { dchar[] dizgi = "merhaba"d. 42. bir dizgi içindeki belirli bir harfi o dizginin en başında olacak şekilde döndüren bir işlevin testleri şöyle yazılabilir: dchar[] harfBaşa(in dchar[] dizgi. } unittest { assert(daEki(1) == "de"). önce programın birim testlerinin doğru olarak çalıştıklarını. } Oradaki üç koşul.dup. } Oradaki üç assert ifadesi. ve 9 sayıları için sırasıyla "de".org 204 . Bu örneklerde görüldüğü gibi. 'a') == "aamerhb").TDD). Bu yöntemde asıl olan birim testleridir. } unittest { assert(daEki(1) == "de"). } void main() {} Her ne kadar o işlevin hatalı olduğu açık olsa da. Yukarıdaki daEki işlevine bu bakış açısıyla yaklaşarak onu önce birim testleriyle şöyle yazmamız gerekir: dstring daEki(in int sayı) { return "bilerek hatalı". sonra kod Modern programcılık yöntemlerinden olan test yönelimli programlama ("test driven development" . assert(daEki(5) == "te"). 1. yani beklendiği gibi hata attıklarını görmek isteriz: Copyright © 2009-2011 Ali Çehreli. assert(harfBaşa(dizgi.. 'm') == "merhaba"). unittest bloklarının içinde her türlü D olanağını kullanabilirsiniz. Kodun yazılması. birim testlerinin kod yazılmadan önce yazılmasını öngörür.

case 6: case 9: case 0: ek = "da". Böylelikle kodu geliştirmeye güvenle devam edebiliriz. Bu işlevde daha sonradan yapılacak olası geliştirmeler. 42. O problemde olduğu gibi. bu işlev 50 gibi bir değer geldiğinde hatalıdır: Copyright © 2009-2011 Ali Çehreli. Örneğin yukarıdaki testlerde üç farklı eki üreten üç sayı değeri seçilmiş. const int sonHane = sayı % 10. case 3: case 4: case 5: ek = "te". break.org 205 . daEki işlevi için bunun örneğini assert dersinin problemlerinde de görmüştük. ve en sonunda kod Birim testleri bütün durumları kapsayamazlar. sonra test. ve bizim de daEki işlevi konusunda güvenimiz gelişir.AssertError@deneme.exception. birim testleri bütün hataları yakalayamazlar ve bazı hatalar bazen son kullanıcılara kadar saklı kalabilir. break.d(7): Assertion failure İşlev ancak ondan sonra ve bu testleri geçecek şekilde yazılır: dstring daEki(in int sayı) { dstring ek. Bu yüzden. } return ek. assert(daEki(5) == "te").7 Bazen de önce hata. http://ddili. } void main() {} Artık program bu testleri geçer. ve daEki işlevi bu üç testten geçtiği için başarılı kabul edilmiştir. default: // Buraya hiçbir durumda gelmemeliyiz assert(false)./deneme core. assert(daEki(9) == "da").d -ofdeneme -w -O -unittest $ . unittest bloğuna yazdığımız koşulları korumak zorundadırlar. } unittest { assert(daEki(1) == "de"). her ne kadar çok etkili yöntemler olsalar da. break.Birim Testleri $ dmd deneme. switch (sonHane) { case 1: case 2: case 7: case 8: ek = "de".

hemen işlevi düzeltmek yerine. Çünkü hatanın birim testlerinin gözünden kaçarak programın kullanımı sırasında ortaya çıkmış olması.dup. daEki(50)). işlevde ileride yapılabilecek geliştirmelerin tekrardan böyle bir hataya neden olmasının önüne geçilmiş olur. in dchar harf) { dchar[] sonuç. ve o testi geçirmek için yazılır. Kod ancak bu birim testi yazıldıktan sonra. } $ . 'm') == "merhaba")./deneme 50'da "50'de" olması gerektiği halde.Birim Testleri import std. Not: Bu işlev. öncelikle bu hatalı durumu yakalayan bir birim testinin eklenmesini şart koşar. } void main() {} Copyright © 2009-2011 Ali Çehreli.org 206 .stdio.AssertError@deneme(39): Assertion failure Artık bu hatalı durumu denetleyen bir test bulunduğu için. Test yönelimli programlama. 42. birim testlerinin bir yetersizliği olarak görülür. http://ddili. işlev yalnızca son haneye baktığı için bu durumda 50 için hatalı olarak "da" döndürmektedir. birim testlerini geçecek şekilde gerçekleştirin: dchar[] harfBaşa(in dchar[] dizgi. 50. } unittest { dchar[] dizgi = "merhaba"d. bu durumu yakalayan bir test örneğin şöyle yazılabilir: unittest { assert(daEki(1) == "de"). 'e') == "emrhaba").exception. assert(harfBaşa(dizgi./deneme core. void main() { writefln("%s'%s". Buna uygun olarak. assert(harfBaşa(dizgi. assert(daEki(9) == "da"). 'a') == "aamerhb"). return sonuç. assert(daEki(50) == "de"). } Program bu sefer bu birim testi denetimi nedeniyle sonlanır: $ . assert(harfBaşa(dizgi. assert(daEki(5) == "te").8 Problem • Yukarıda sözü geçen harfBaşa işlevini. sonu "bin" ve "milyon" gibi okunarak biten başka sayılarla da sorunlu olduğu için burada kapsamlı bir çözüm bulmaya çalışmayacağım.

..org 207 . ilk test yüzünden hata atıldığını görün. http://ddili. ve işlevi hatayı giderecek şekilde yazın.Birim Testleri O tanımdan başlayın. . çözüm Copyright © 2009-2011 Ali Çehreli.

işlevin başlatılmasıyla ilgili olan denetimlerin bir arada ve ayrı bir blok içinde yapılmasıdır. in int dakika) in { assert((saat >= 0) && (saat <= 23)). Bu düşünceye göre. D'nin sözleşmeli programlama anlayışında işlevlerin giriş koşulları "giriş" anlamına gelen in bloklarında denetlenir.Sözleşmeli Programlama 43 Sözleşmeli Programlama Sözleşmeli programlama.org 208 . Böylece assert ifadeleri işlevin asıl işlemlerinin arasına karışmamış Copyright © 2009-2011 Ali Çehreli. Sözleşmeli programlama. assert ifadeleri kullanarak işlevin tanımlandığı blok içinde yapıyorduk: dstring zamanDizgisi(in int saat. işlevin asıl bloğu da "gövde" anlamına gelen body ile belirlenir: import std. Sözleşmeli programlama blokları kullanıldığı zaman. import std. http://ddili. Bu tür koşulları daha önce assert dersinde görmüştük. bu anlaşmaları dil düzeyinde belirlemeye yarayan olanaktır. in int dakika) { assert((saat >= 0) && (saat <= 23)). 34)). işlevler ve onları çağıran kodlar arasında yazısız bazı anlaşmalar vardır. } return to!dstring(saat) ~ ":" ~ to!dstring(dakika). Örneğin karekök alan bir işlev kendisine verilen parametrenin sıfırdan küçük olmamasını şart koşar. assert ifadesine dayanır ve D'nin kod sağlamlığı sağlayan bir başka olanağıdır. ticari bir dil olan Eiffel tarafından "design by contract (DBC)" adıyla yayılmıştır. assert((dakika >= 0) && (dakika <= 59)). veya parametre olarak tarih bilgisi alan bir işlev ayın 1 ile 12 arasında olmasını şart koşar. Birim testlerinde olduğu gibi. assert((dakika >= 0) && (dakika <= 59)).stdio. İşlevlerin parametreleriyle ilgili denetimleri. Bu yöntem D dilinde "contract programming" olarak geçer. } İşlevin in bloğunun yararı. dstring zamanDizgisi(in int saat. Sözleşmeli programlama.1 Giriş koşulları için in blokları İşlevlerin doğru çalışabilmeleri. } void main() { writeln(zamanDizgisi(12.conv. işlevlerin hizmet sunan birimler olarak kabul edilmeleri düşüncesi üzerine kurulu bir programlama yöntemidir. aldıkları parametrelere bağlı olabilir. D'de sözleşmeli programlama üç temelden oluşur: • İşlevlerin in blokları • İşlevlerin out blokları • Sınıfların invariant blokları (Bunu daha sonra sınıfları anlatırken göstereceğim) 43. } body { return to!dstring(saat) ~ ":" ~ to!dstring(dakika).

Bu durum. in bloğundaki bir assert ifadesinin başarısız olması. out bloğundaki bir assert ifadesinin başarısız olması. D'de işlevler dört blok halinde yazılabilirler: • Giriş koşulları için in bloğu: seçime bağlıdır ve giriş koşullarını denetler Copyright © 2009-2011 Ali Çehreli.org 209 . dönüş değeriyle ilgili assert ifadeleri de yazılamaz. işlevlerin "çıkış" anlamına gelen out bloklarında denetlenirler. } body { return artıkYıl_mı(yıl) ? 29 : 28. sözleşmenin işlev tarafından bozulduğunu gösterir. siz dönüşDeğeri gibi başka bir isim de verebilirsiniz. işlev. bu değer return ile isimsiz olarak döndürülür.. veya dönüş değerinin denetlenmesi gerekmiyordur. Hangi ismi kullanırsanız kullanın. in bloklarındaki kodlar programın çalışması sırasında işlevin her çağrılışında otomatik olarak işletilirler.. Örneğin belirli bir senedeki Şubat ayının kaç gün çektiği bilgisini döndüren bir işlevin çıkış garantisi. Bu isim dönüş değerini temsil eder ve denetlenecek olan garantilerde bu isim kullanılır: int şubattaKaçGün(in int yıl) out (sonuç) { assert((sonuç == 28) || (sonuç == 29)). işlev. döndürdüğü değerin 28 veya 29 olmasıdır. dönüş değeriyle ilgili garantileri yazarken bir sorun doğurur: ismi olmayınca. o isim işlevin dönüş değerini temsil eder. sözleşmenin gerektirdiği şekilde çağrılmamış demektir. Bu sorun out anahtar sözcüğünden sonra verilen isimle halledilmiştir.2 Çıkış garantileri için out blokları İşlevin yaptığı kabul edilen sözleşmenin karşı tarafı da işlevin sağladığı garantilerdir. İşleve girerken in bloklarının otomatik olarak işletilmeleri gibi. sözleşmeyi işlevi çağıran tarafın bozduğunu gösterir. Bunlara yine seçime bağlı olan unittest bloklarını da eklersek. İşlevin içinde yine de gerektikçe assert ifadeleri kullanılabilir. İşlevin dönüş değerinin özel bir ismi yoktur. Daha önceki derslerde hiç kullanmamış olduğumuzdan da anlaşılabileceği gibi. ama giriş koşulları sözleşmeli programlama anlayışına uygun olarak in bloğuna yazılırlar. O zaman out bloğu parametresiz olarak yazılır: out { } // . in ve out bloklarının kullanımı seçime bağlıdır. Çıkış garantileri. İşlevin asıl işleyişi. 43. Bazen işlevin dönüş değeri yoktur. http://ddili.Sözleşmeli Programlama olurlar. sözleşmenin gerektirdiği değeri veya yan etkiyi üretememiş demektir. } Ben out bloğunun parametresinin ismi olarak sonuç yazmayı uygun buldum. Böylece işlevin geçersiz başlangıç koşulları ile çalışması ve programın yanlış sonuçlarla devam etmesi önlenmiş olur. out blokları da işlevden çıkarken otomatik olarak işletilirler. ancak bu koşullar sağlandığında devam eder.

} out { assert(toplam == (birinci + ikinci)). Gerisini ikinciye verir. // Bir tane de büyük bir değerle deneyelim bölüştür(1_000_007. ama eğer in ve out blokları kullanılmamışsa body anahtar sözcüğü yazılmayabilir • İşlevin birim testlerini içeren unittest bloğu: bu aslında işlevin parçası değildir ve kendi başına işlev gibi yazılır. } unittest { int birinci. assert(ikinci == 0). ikinci). Copyright © 2009-2011 Ali Çehreli. out int ikinci) in { assert(toplam >= 0). // Sınır koşulunu deneyelim bölüştür(7. ama birinciye hiçbir * zaman 7'den fazla vermez. birinci. assert(ikinci == 0). * * Toplamdan öncelikle birinciye verir. ikinci). ikinci = toplam . // Toplam 7'den az ise birincisi toplam'a. ikincisi 0'a // eşit olmalı bölüştür(3. out int birinci. gerisi ikinciye // gitmeli bölüştür(8. int ikinci.stdio. ikinci). birinci. assert(birinci == 7). ikinci). assert(ikinci == 1_000_000). birinci. assert(birinci == 0). ikinci). int ikinci. assert(ikinci == 0). */ void bölüştür(in int toplam.birinci. assert(birinci == 7). } body { birinci = (toplam >= 7) ? 7 : toplam. // 7'den fazla olduğunda birinci 7 olmalı. assert(birinci == 7).org 210 . birinci. aralarındaki bağı gösterme bakımından uygun olur Bütün bu blokları içeren bir işlev tanımı şöyle yazılabilir: import std.Sözleşmeli Programlama • Çıkış garantileri için out bloğu: seçime bağlıdır ve çıkış garantilerini denetler • İşlevin asıl işlemlerini içeren body bloğu: bu bloğun yazılması şarttır. birinci. assert(ikinci == 1). // Toplam 0 ise ikisi de 0 olmalı bölüştür(0. } void main() { int birinci. /* * Toplamı iki parça olarak bölüştürür. ama denetlediği işlevin hemen altına yazılması. http://ddili. assert(birinci == 3).

birim testleri ve sözleşmeli programlama ile açıkça ortaya koyulmaları. doğru olarak yazdığımız işlevlerin her zaman için doğru kalmalarına yardım eder. ondan çok daha zevkli ve verimli olan kod yazmaya ayırabiliriz./deneme birinci: 7 ikinci: 116 Bu işlevin asıl işi yalnızca 2 satırdan oluşuyor. ikinci). " ikinci: ". Ama dikkat ederseniz. in int goller2.3 Sözleşmeli programlamayı etkisizleştirmek Birim testlerinin tersine. birinci ve ikinci takımın attıkları goller olsun.. hatalar da hep böyle doğru çalışacağı düşünülen kodlar arasından çıkar. Birim testleri ve sözleşmeli programlama olanakları bizi zorlu hatalardan koruyan çok etkili araçlardır. golleri dikkate alarak birinci ve ikinci takımın puanlarını düzenlesin: fazla gol atan taraf üç puan kazansız. goller eşitse iki takım da birer puan kazansınlar. ve invariant blokları programa dahil edilmezler. Son iki parametresi de bu takımların maçtan önceki puanları olsun. http://ddili. Benim main içine yazdığım assert ifadelerini silmeyin.d -ofdeneme -w -release Program o seçenekle derlendiğinde in . Böylece zamanımızı hata ayıklamak yerine. benim bu işlevin çalışması konusundaki beklentilerimi belgeliyorlar. Ek olarak. ikinci kazanmışsa 2. birinci. Program hatalarını azaltan hiçbir olanağı küçümsememenizi öneririm.d -ofdeneme -w -unittest $ . Bu işlevin ilk iki parametresi. } Copyright © 2009-2011 Ali Çehreli.Sözleşmeli Programlama } bölüştür(123. ref int puan1. ref int puan2) in { // . Buna rağmen. Aşağıdaki programla başlayın ve işlevin dört bloğunu uygun şekilde doldurun.. Programcının yazdığı kod her zaman için doğru çalışacak şekilde yazılmıştır. $ dmd deneme. işlevin dönüş değeri de kazanan tarafı belirtsin: birinci kazanmışsa 1. writeln("birinci: ". Bu işlev.4 Problem • İki futbol takımının puanlarını bir maçın sonucuna göre arttıran bir işlev yazın. ikinci). berabere kalmışlarsa 0. denetleyen kodlar ise tam 19 satır! Bu kadar küçük bir işlev için bu kadar emeğin gereksiz olduğu düşünülebilir. int puanEkle(in int goller1. 43. birinci. 43. sözleşmeli programlama normalde etkilidir.org 211 . İşlevlerden beklenenlerin. out . Bunun için dmd derleyicisinde release seçeneği kullanılır: dmd deneme. etkisizleştirmek için özel bir derleyici veya geliştirme ortamı ayarı gerekir. programcı hiçbir zaman bilerek hatalı kod yazmaz. onlar.

çözüm Copyright © 2009-2011 Ali Çehreli. assert(birincininPuanı == 14). } unittest { // . assert(kazananTaraf == 1). return -1. Ben out bloğunda int türünden bir dönüş değerini denetleyebilelim diye işlevde int seçtim.. ikincininPuanı). birincininPuanı. assert(kazananTaraf == 0).. kazananTaraf = puanEkle(2.. kazananTaraf = puanEkle(3. int kazananTaraf... . assert(ikincininPuanı == 7). ref int puan2) // ... assert(ikincininPuanı == 8).. in int goller2. birincininPuanı. } Not: Aslında burada üç değeri olan bir enum türü döndürmek çok daha doğru olur: enum MaçSonucu { birinciKazandı. http://ddili.Sözleşmeli Programlama out (sonuç) { // . ikinciKazandı.. berabere } MaçSonucu puanEkle(in int goller1. assert(birincininPuanı == 13). 2.org 212 . ref int puan1. ikincininPuanı). } void main() { int birincininPuanı = 10.. } body { // . int ikincininPuanı = 7. 1.

Daha sonra. Şimdiye kadar kavramları temsil eden veri yapılarına değişken adını verdik.. } for (int i = 0. Ben bu derste bunların hepsine birden genel olarak değişken diyeceğim. ++i) { int hız = 100 + i. // tek bir değişken .. döngünün kapama parantezinde sona erer. D'nin nesneye dayalı programlama olanaklarının temelini oluşturan sınıfları tanıyacağız... // 10 farklı değişken vardır // . başka türleri bir araya getirmenin yanında bu türlerle ilgili özel işlemleri de belirlememizi sağlayacaklar.org 213 . ++i) { hız = 100 + i. http://ddili. Bu kavramlar ileride yapı ve sınıf tasarımları sırasında yararlı olacak.. Aşağıdaki kodda ise durum yaşam süreçleri açısından çok farklıdır: void hızDenemesi() { for (int i = 0.. ve eşleme tablolarını kullanacağım. Herhangi bir türden olan herhangi bir veri yapısı. şimdiye kadar hiç üzerinde durmadan kullandığımız bazı temel kavramları ve temel işlemleri açıklamam gerekiyor. i != 10. Sınıflar. en azından bu ders kapsamında değişken adını alacak. 10 farklı değer alır // . } // yaşamları burada sonlanır } O kodda her birisi tek bir değer alan 10 farklı değişken vardır: döngünün her tekrarında hız isminde yeni bir değişken yaşamaya başlar.1 Değişkenlerin yaşam süreçleri Bir değişkenin tanımlanması ile başlayan ve geçerliliğinin bitmesine kadar geçen süreye o değişkenin yaşam süreci denir. O konuyu hatırlamak için şu örneğe bakalım: void hızDenemesi() { int hız. Orada 100 ile 109 arasında 10 değişik değer alan tek bir değişken vardır. 44... Yapıların kullanıcı türlerinin temeli olduklarını göreceğiz. Geçerliliğin bitmesi kavramını isim alanı dersinde değişkenin tanımlandığı kapsamdan çıkılması olarak tanımlamıştım. dizileri. i != 10.Yaşam Süreçleri ve Temel İşlemler 44 Yaşam Süreçleri ve Temel İşlemler Çok yakında yapı ve sınıfları anlatmaya başlayacağım. yaşamı. } // yaşamı burada sonlanır O koddaki hız değişkeninin yaşam süreci hızDenemesi işlevinden çıkıldığında sona erer. Bu derste yalnızca şimdiye kadar gördüğümüz temel türleri. O konulara geçmeden önce. Onlar sayesinde temel türleri ve başka yapıları bir araya getirerek yeni türler oluşturabileceğiz. // . Copyright © 2009-2011 Ali Çehreli. Bir kaç noktada da yapı ve sınıf türünden olan değişkenlere özel olarak nesne dedik. siz bu kavramların bütün türler için geçerli olduklarını aklınızda tutun.

org 214 . int main_out. int main_ref.Yaşam Süreçleri ve Temel İşlemler 44. void işlev( in int p_in. Parametrenin değeri. } 44. ref : Parametre aslında işlev çağrıldığında kullanılan değişkenin takma ismidir. • Atama: Değerin değişmesi. yaşamı kullanıldığı an başlar ve yine o an biter.init olarak atanır // // // // // yaşamı işlev içinde kullanıldığı an başlar ve eğer kullanımı bitmişse hemen o an sonlanır... main_ref. işlev çağrılırken kullanılan değerin bir kopyasıdır. http://ddili. // yaşamı main_in'in kopyası olarak // işleve girilirken başlar ve işlevden // çıkılırken sonlanır // main_ref'in takma ismidir // main_out'un takma ismidir. • Sonlandırma: Yaşamın sonu.. işleve girildiğinde asıl değişkene önce otomatik olarak türünün . lazy int p_lazy) { } // .init değeri atanır. Bu dört parametre türünü kullanan ve yaşam süreçlerini açıklayan bir örnek: void main() { int main_in.. değeri için. main_out. her kullanıldığı an 'birHesap' işlevi çağrılır ref int p_ref. out int p_out. işleve girildiğinde // değeri önce int. ref 'ten farklı olarak. lazy : Parametre tembel olarak işletildiğinden. Parametrenin asıl değişkenin yaşam süreci üzerinde etkisi yoktur. ve bu değer daha sonra işlev içinde değiştirilebilir. // değeri işleve kopyalanır // işleve kendisi olarak ve kendi // değeriyle gönderilir // işleve kendisi olarak gönderilir. Copyright © 2009-2011 Ali Çehreli.2 Parametrelerin yaşam süreçleri İşlev parametreleri dersinde tanıdığımız parametre türlerine bir de yaşam süreçleri açısından bakmakta yarar var: in : Parametrenin yaşamı işleve girildiği an başlar ve işlevden çıkıldığı an sona erer. // işleve girildiği an değeri sıfırlanır } işlev(main_in. bir değişkenin yaşamı boyunca etkili olan üç temel işlem vardır: • Kurma: Yaşamın başlangıcı.3 Temel işlemler Hangi türden olursa olsun. birHesap()). int birHesap() { // . ref'ten // farklı olarak. out : Parametre aslında işlev çağrıldığında kullanılan değişkenin takma ismidir.

2. Programda bir değişkenin değerini değiştirdiğimizde.Yaşam Süreçleri ve Temel İşlemler Değişkenlerin yaşam süreçleri. Her değişken bilgisayarın belleğinde kendisine ayrılan bir yerde yaşar. Kurma iki alt adımdan oluşur: 1. Onun herhangi bir şekilde kullanılabilmesi için kurulmuş olması önemlidir. inşa etmek" anlamlarında kullanıyorum. Derleyicinin istediğimiz işleri yaptırmak için mikro işlemcinin anladığı dilde kodlar ürettiğini biliyorsunuz. her türün . kullanılmadan önce kurulmak zorundadır. O durumda hız 'ın değeri int.4 Kurma Her değişken. yani 0. Örneğin tamsayı bir kavramı temsil eden şöyle bir değişken int hız = 123. İlk değerinin verilmesi: O adrese ilk değeri yerleştirilir. Belleği soldan sağa doğru bir şerit halinde devam ediyormuş gibi gösterirsek. çünkü değişkeni kullanıma hazırlayan işlemleri içerir. tanımlanan değişkenler için bellekten yer ayırmaktır. 44. Aynı adresteki değer bir artar: --+-----+-----+-----+-| | 124 | | --+-----+-----+-----+-Kurma. http://ddili. Varsayılan değerle kurulan bir değişkenin daha sonradan başka değerler alacağını düşünebiliriz. Copyright © 2009-2011 Ali Çehreli. Değişkenler üç farklı şekilde kurulabilirler: • varsayılan şekilde: biz değer vermezsek • kopyalanarak: başka bir değişkenin değeriyle • belirli bir değerle: bizim tarafımızdan belirlenen değerle Hiçbir değer kullanılmadan kurulduğunda değişkenin değeri o türün varsayılan değeridir. değişkenin yaşamı başladığı anda gerçekleştirilir. Burada "kurma" sözcüğünü "hazırlamak. o değişkenin bellekte şu şekilde yaşadığını düşünebiliriz: --+-----+-----+-----+-| | 123 | | --+-----+-----+-----+-Her değişkenin bellekte bulunduğu yere o değişkenin adresi denir. bellekte bir int 'in büyüklüğü kadar yer tutar. Bir anlamda o değişken o adreste yaşamaktadır.init niteliğidir: int hız.init 'tir. değişkenin yeni değeri yerleştirilir: ++hız. o değişken için bellekte ayrılan yere. Derleyicinin ürettiği kodların bir bölümünün görevi.org 215 . kurma işlemiyle başlar ve sonlandırma işlemiyle sona erer. Yer ayrılması: Değişkenin yaşayacağı yer belirlenir. Varsayılan değer. Bu süreç boyunca değişkene yeni değerler atanabilir.

Öte yandan. yaşamına new ile kurulan nesneye erişim sağlayacak şekilde başlar. varsayılan şekilde kurulmuş olduğu için henüz kullanılamaz. belleğin geri verilmesi: değişkenin yaşadığı yer geri verilir Temel türlerin çoğunda.5 Sonlandırma Değişkenin yaşamının sona ermesi sırasında yapılan işlemlere sonlandırma denir. son işlemler: değişkenin yapması gereken son işlemler işletilir 2. eğer varsa. Onun da işletim sisteminin hangi dosyasına erişmek için kullanılacağının daha sonradan belirleneceğini düşünebiliriz. sınıfDeğişkeni de yaşamına başkaSınıfDeğişkeni 'nin kopyası olarak başlar. özel bir sonlanma işlemi gerekmez. O durumda hız 'ın değeri başkaHız 'ın değerinden kopyalanır ve hız yaşamına o değerle başlar. Örneğin bir File nesnesi.File türünden olan yukarıdaki dosya nesnesi ise işletim sisteminin hiçbir dosyasına bağlı olmayan bir File yapısı nesnesidir. hız ile başkaHız 'ın birbirlerinden farklı iki değer olmaları. sonlandırma sırasında özel işlemler gerekmez.Yaşam Süreçleri ve Temel İşlemler File dosya. değer türleri ile referans türleri arasındaki farktan ileri gelir. Eğer dizinin elemanları temel türlerdense. Ek olarak. sınıfDeğişkeni . Bu önemli nokta. Copyright © 2009-2011 Ali Çehreli. Değişken bazen başka bir değişkenin değeri kopyalanarak kurulur: int hız = başkaHız.org 216 . değişkenler belirli değerlerle veya özel şekillerde kurulabilirler: int hız = birHesabınSonucu(). Örneğin int türünden bir değişkenin bellekte yaşamakta olduğu yere sıfır gibi özel bir değer atanmaz. Son olarak. öte yandan sınıfDeğişkeni ile başkaSınıfDeğişkeni 'nin aynı nesneye erişim sağlamalarıdır. File 'ın sonlandırma işlemleridir. Aralarındaki önemli ayrım. Dosyalar dersinde gördüğümüz std. programın çalışması sırasındaki bir hesabın değeri olarak belirlenir. o türün sonlandırma işlemleri her eleman için uygulanır. ara belleğinde tutmakta olduğu karakterleri diske yazmak zorundadır. Sınıf değişkenlerinde ise durum farklıdır: auto sınıfDeğişkeni = başkaSınıfDeğişkeni. Dizilerde durum biraz daha üst düzeydedir: bütün dizi sonlanırken. auto sınıfDeğişkeni = new BirSınıf. 44. Bu konuyu bir sonraki derste anlatacağım. dosyayla işinin bittiğini işletim sistemine bildirmek için de dosyayı kapatmak zorundadır. Bu işlemler. Kurma gibi. Ama eğer dizinin elemanları sonlanma gerektiren bir yapı veya sınıf türündense. sonlandırma da iki adımdan oluşur: 1. http://ddili. bazı türlerden olan değişkenlerin yaşamlarının sonlanması sırasında özel işlemler gerekebilir. hız 'ın ilk değeri. Program o adresin artık boş olduğunun hesabını tutar ve orayı daha sonra başka değişkenler için kullanır.stdio. o dizinin sahip olduğu bütün elemanlar da sonlanırlar.

Eğer indeks türü olarak bir yapı veya sınıf türü kullanılmışsa. Bazı türlerin değişkenlerinin son işlemleri ise çöp toplayıcı tarafından daha sonraki bir zamanda işletilebilir. http://ddili. Yukarıdaki bellek gösteriminde olduğu gibi. Copyright © 2009-2011 Ali Çehreli. Değişkenler iki şekilde sonlandırılabilirler: • hemen: sonlandırma işlemleri hemen işletilir • sonra: çöp toplayıcı tarafından ilerideki bir zamanda Bir değişkenin bunlardan hangi şekilde sonlandırılacağı öncelikle kendi türüne bağlıdır. sonlandırma işlemleri ya hemen.org 217 . çöp toplayıcılı bir dildir. Temel türlerde atama işlemi yalnızca değişkenin değerinin değiştirilmesi olarak görülebilir. her indeks nesnesi için o türün gerektirdiği sonlandırma işlemleri uygulanır. Yaşamı sona eren bir değişkenin sonlandırılması otomatik olarak çöp toplayıcı denen düzenek tarafından halledilir.Yaşam Süreçleri ve Temel İşlemler Sonlandırma. Temel türlerin hemen sonlandırıldıklarını düşünebilirsiniz çünkü zaten sonlandırma için özel işlemleri yoktur. Bundan sonraki derste türlerin nasıl değer türleri ve referans türleri diye ikiye ayrıldıklarını anlatacağım. yeni değerin verilmesi: eski değerin yerine yeni değer atanır Bu iki adım. değişken örneğin 123 olan bir değer yerine artık 124 değerine sahip olabilir. sonlandırma işlemleri olmayan temel türlerde önemli değildir.6 Atama Bir değişkenin yaşamı boyunca karşılaştığı diğer önemli işlem atamadır. eski değerin sonlandırılması: eğer varsa. Ama sonlandırma işlemleri bulunan türlerde atamanın böyle iki adımdan oluştuğunu akılda tutmakta yarar vardır: atama aslında bir sonlandırma ve bir yeni değer verme işlemidir. ya da çöp toplayıcı tarafından daha sonra yapılır 2. 44. eşleme tablolarında da dizilerdeki gibidir. Ek olarak. eşleme tablosunun sahip olduğu indeks değişkenleri de sonlandırılırlar. Bu tür dillerde sonlandırma işlemleri programcı tarafından açıkça yapılmak zorunda değildir. Daha genel olarak aslında atama işlemi de iki adımdan oluşur: 1. Çöp toplayıcı: D.

Örneğin int türünden bir değişken.1 Değer türü Bunun tanımı son derece basittir: Değişkenleri değer taşıyan türlere değer türü denir. Bu bilgileri bir önceki dersteki nesne yaşam süreçleri konusuyla bağdaştırınca yapı ve sınıf nesnelerini anlamak daha kolay olacak. Örneğin bütün temel türler ve sabit uzunluklu diziler değer türleridir. Yeni değişkene bellekte kendisine ait bir yer ayrılır ve yeniHız 'ın da kendi değeri olur: hız ---+-----+--| 123 | ---+-----+--yeniHız ---+-----+--| 123 | ---+-----+--- Doğal olarak. çünkü bu türlerden olan her değişkenin kendi değeri vardır. bir tamsayı değer taşır: int hız = 123. Yeni değişkenin değeri değişmez: hız ---+-----+--| 200 | ---+-----+--yeniHız ---+-----+--| 123 | ---+-----+--- Copyright © 2009-2011 Ali Çehreli. artık bu değişkenlerde yapılan değişiklikler birbirlerinden bağımsızdır: hız = 200.org 218 . En sonda da şu iki önemli kavramı gösteren bir tablo vereceğim: • değer karşılaştırması • adres karşılaştırması 45.Değerler ve Referanslar 45 Değerler ve Referanslar Yakında başlayacağımız yapılara ve sınıflara geçmeden önce değer türleri ve referans türleri kavramlarını anlamak gerekiyor. http://ddili. Belleği önceki derste gördüğümüz gibi bir şerit olarak hayal edersek. Bu derste ayrıca değişkenlerin adreslerini bildiren & işlecini de tanıtacağım. bu değişkenin bellekte şu şekilde yaşadığını düşünebiliriz: hız ---+-----+--| 123 | ---+-----+--Değer türlerinin değişkenleri kopyalandıklarında kendi özel değerlerini edinirler: int yeniHız = hız.

2 Referans değişkenleri Referans türlerini anlatmaya geçmeden önce referans değişkenlerini tanıtmam gerekiyor. adreslerin int 'in uzunluğu olan 4 kadar farklı olmalarına bakarak o değişkenlerin bellekte yan yana durduklarını da anlayabiliriz.Değerler ve Referanslar 45. Değişken adresleri normalde onaltılı sayı sisteminde yazdırılır. int hız = 123. Birbirlerinden farklı olan iki değişkenin bu açıdan eşit olmaları. writeln("hız : ". Değerleri eşit olsalar bile. hız. Ayrıca. Aşağıdaki örneklerde kullandığım assert ifadelerini "bu doğrudur" demişim gibi kabul edin. Bu değişkenler işletim sisteminden alınan belleğin boş yerlerine yerleştirilirler. http://ddili.2 Değer kimliği Yukarıdaki gösterimlerden de anlaşılabileceği gibi.org 219 . writeln("yeniHız: ". 45. • Değer kimliği: Kendi değerlerine sahip olmaları açısından bakıldığında.1. hız ve yeniHız değişkenlerinin değerleri aynıdır. 45. Bir örnek olarak iki farklı değişkenin adresini yazdıran bir kod şöyle yazılabilir: int hız = 123. int yeniHız = hız. hız ve yeniHız 'ın ayrı kimlikleri vardır. assert(hız != yeniHız).3 Adres işleci & Daha önce readf kullanımında gördüğümüz gibi. "hız. değişkenlerin eşitlikleri iki anlamda ele alınabilir: • Değer eşitliği: Şimdiye kadar bir çok örnekte kullandığımız == işleci. yeniHız'a eşittir" anlamına geliyor. Okuduğu bilgiyi hangi değişkene yazacağını readf 'e o değişkenin adresini vererek bildiriyorduk. Değişkenlerin adreslerini başka amaçlar için de kullanabiliriz.1. " adresi: ". hız = 200. &hız). int yeniHız = hız. bu işleç değişkenin adresini döndürür. yeniHız. assert(hız == yeniHız).1 assert hatırlatması Bu derste kavramların doğruluklarını göstermek için assert dersinde gördüğümüz assert ifadelerini kullanacağım. 45.1. Örneğin aşağıdaki assert(hız == yeniHız) ifadesi. ama yukarıda da gösterildiği gibi bu değerler belleğin farklı adreslerinde bulunmaktadırlar: hız : 123 adresi: BF9A78F0 yeniHız: 123 adresi: BF9A78F4 Not: Programı her çalıştırdığınızda farklı adresler görmeniz normaldir. değişkenlerin değerlerini karşılaştırır. onların değerlerinin eşit olmaları anlamına gelir. Copyright © 2009-2011 Ali Çehreli. birisinde yapılan değişiklik diğerini etkilemez. " adresi: ". &yeniHız).

işleve gönderilen asıl değişkenin takma ismi gibi işlem görürler. Böyle bir değişkende yapılan bir değişiklik asıl değişkeni etkiler. writeln("çıkış'ın adresi : ".Değerler ve Referanslar Referans değişkenleri. işlev içindeki referans ve çıkış aslında aynı değere erişim sağlarlar. foreach (i. } Her ne kadar farklı parametre olarak tanımlansalar da. ref eleman. 3. } : ". sırayla asıl dizi elemanlarının takma ismi haline gelir. &çıkış). Bu durumu döngünün i 'nin örneğin 3 olduğu tekrarı için şöyle gösterebiliriz: dizi[0] dizi[1] dizi[2] dizi[3] dizi[4] ⇢ ⇢ ⇢ (eleman) --+--------+--------+--------+--------+---------+-| 0 | 1 | 2 | 3 | 4 | --+--------+--------+--------+--------+---------+-• ref ve out işlev parametrelerinde: İşlev parametreleri ref veya out olarak tanımlandıklarında. eleman. Referans değişkenlerini aslında şimdiye kadar iki konuda görmüş. Bir başka deyişle. assert(&referans == &çıkış). Daha başka bir deyişle. çünkü zaten ikisi de main içindeki asıl 'ın takma ismidir: Copyright © 2009-2011 Ali Çehreli. iki farklı değişken aslında belleğin aynı yerindeki bir değere erişim sağlıyorlar demektir: int[] dizi = [ 0. writeln("asıl'ın adresi işlev(asıl. eleman ile dizi[i] aynı değere erişim sağlarlar. döngünün her tekrarında tanımlanan eleman ve dizi[i] 'nin değer kimliği açısından aslında aynı olduklarını gösterir. kendi özel değerleri yoktur. asıl değeri etkiler. eleman . } Her ne kadar farklı iki değişken olsalar da. http://ddili. başka değişkenlerin takma isimleri gibi kullanılan değişkenlerdir. Bunu. out int çıkış) { writeln("referans'ın adresi: ". & işleci ile de gösterebiliriz. 4 ]. Aynı değer kimliğine sahip olduklarını & işleci ile şu şekilde kanıtlayabiliriz: import std. 2. &referans). &asıl). Kullanılmadığında ise dizi elemanının kopyası oluyordu. ama üzerinde fazla durmamıştık: • foreach döngüsünde ref ile: Bir grup elemana foreach döngüsünde sırayla erişilirken ref anahtar sözcüğü kullanıldığında. dizi[i] 'nin takma ismidir. eleman takma ismi. Birisinde yapılan değişiklik. & işleci ile alınan adreslerinin eşit olmaları. Her ne kadar kendileri değişken gibi olsalar da.org 220 . asıl). dizi) { assert(&eleman == &dizi[i]). void main() { int asıl. void işlev(ref int referans. dizi elemanının kendisi anlamına geliyordu.stdio. böyle iki parametre alan bir işlevin iki parametresine de aynı değişkeni gönderelim. Bunu görmek için. 1. Adresleri aynı ise.

assert(&dizi != &dilim2). İşte referans değişkenleri ile referans türlerinin ayrımı buna dayanır: • Referans değişkenlerinin kendi kimlikleri yoktur. 3. Referans değişkenlerinin tersine. http://ddili. // şimdi dilim[0] ile dizi[1] aynı değere erişirler: assert(&dilim[0] == &dizi[1]). değer taşıyan başka değişkenlere erişim sağlarlar.Değerler ve Referanslar asıl'ın adresi : BFCA9F34 referans'ın adresi: BFCA9F34 çıkış'ın adresi : BFCA9F34 O koddaki referans değişkenlerinin bellekte kendilerine ait yerleri olmadığını ve asıl 'ın takma isimleri olduklarını aşağıdaki şekilde ifade edebiliriz. Bunu görmek için aynı dilimin kopyası olan bir dilim daha oluşturalım: int[] dilim2 = dilim. referans türleri yalnızca takma isim değildirler. dilim2 ile dilim farklı adreslerde yaşarlar. Bu iki dilim kendi adresleri olan. assert(&dilim != &dilim2). Not: Aslında dinamik diziler (uzunluğu sabit olmayan diziler) de teknik olarak dilimdirler. Dilimler. $ . ama yine başka bir değere erişim sağlarlar Örneğin yukarıdaki dilim ve dilim2 'yi bellek üzerinde şöyle gösterebiliriz: Copyright © 2009-2011 Ali Çehreli..org 221 . assert(dizi[1] == 42). onlar başka bir değişkenin takma ismidirler • Referans türünden olan değişkenler ise. kendi elemanları yoktur: int[] dizi = [ 0. 1. Böyle türlere referans türü denir. // gerçekten de dilim[0]'da yapılan değişiklik dizi[1]'i // etkiler: dilim[0] = 42.1]. 2.: asıl (referans) (çıkış) ---+-----+--| 0 | ---+-----+--- 45. üçünün de adresi farklıdır: assert(&dizi != &dilim). Hatta dizi 'yi de katacak olursak. Bu kavramı daha önce dizi dilimlerinde görmüştük.3 Referans türü Bazı türlerden olan değişkenler kendi kimlikleri olduğu halde kendileri değer taşımazlar. // baştaki ve sondaki elemanı dışlayarak ortadaki üçüne // erişim sağlayan bir dilim: int[] dilim = dizi[1 . 4 ]. kendi kimlikleri olan değişkenlerdir. bir başka deyişle kendi kimlikleri olan değişkenlerdir. varolan başka bir dizinin elemanlarına erişim sağlayan türlerdir.

} Sınıf nesneleri. Dilimlerde olduğu gibi: (isimsiz BirSınıf nesnesi) değişken değişken2 ---+-------------------+--. Bunu == işleci ile görebiliriz. | | o | ---+-------------------+--. assert(değişken == değişken2).---+-|-+--▲ | | | +--------------------+ Dilimlere benzer şekilde. | | o | | o | ---+-------------------+--.---+-|-+--. new işleciyle oluşturulmuş olan isimsiz bir BirSınıf nesnesine erişim sağlayan bir referanstır: (isimsiz BirSınıf nesnesi) değişken ---+-------------------+--.. D'nin güçlü bir olanağı olan sınıfları daha ilerideki derslerde göreceğiz. adresleri farklı olduğu için farklı değişkenlerdir. erişim sağlama açısından eşit olsalar da. D'nin sınıflarının referans türleri olmalarıdır.---+-|-+--.üye = 1. aynı nesneye erişim açısından eşit olup olmama bilgisini verir: auto değişken = new BirSınıf.---+---+--| . değişken kopyalandığında kopyası da aynı nesneye erişim sağlar.---+---+--. // aynı nesneyi paylaşırlar Copyright © 2009-2011 Ali Çehreli.org 222 . auto değişken2 = değişken.. Yalnızca bunu göstermiş olmak için çok basit bir sınıf tanımlayacağım: class BirSınıf { int üye.---+---+--| . Sınıflarla kullanıldığında == işleci. http://ddili.. Yukarıda görüldüğü gibi. değişken. D'yi C++'dan ayıran önemli farklardan birisi. auto değişken2 = değişken.---+-|-+--▲ | | | | | +--------------------+------------+ Böyle iki farklı BirSınıf nesnesinin gerçekten de aynı nesneye erişim sağladıklarını bir de şöyle gösterebiliriz: auto değişken = new BirSınıf..---+---+--| 0 | 1 | 2 | 3 | 4 | | o | | o | ---+---+---+---+---+---+--.---+-|-+--▲ | | | | | +--------------------+------------+ O dilimlerin erişim sağladıkları asıl dizi elemanlarını mavi ile gösterdim. daha önce hata atarken de kullandığımız new ile oluşturulurlar: auto değişken = new BirSınıf.Değerler ve Referanslar dilim dilim2 ---+---+---+---+---+---+--. assert(&değişken != &değişken2). Bu durumda değişken .---+---+--.

3]. Başka bir referans türü. 100:"yüz". } Referans türlerinde ise atama işlemi. dilim'in artık başka elemanlara // erişim sağlamasına neden olur dilim = dizi2[$ . assert(değişken.üye == 2). // değişken'in de erişim // sağladığı nesne // değişmiştir değişken2 yoluyla 2 değerini alan üye. eşleme tablolarıdır. dilim 'in başka elemanları göstermesini sağlar: void main() { int[] dizi1 = [ 10. 12. // aynı asıl tabloyu paylaşmaya başlarlar: string[int] isimleSayılar2 = isimleSayılar..1 Atama işleminin farkı Değer türlerinde ve referans değişkenlerinde atama işleminin sonucunda asıl değer değişir: void main() { int sayı = 8.. // sonuncu elemana eriştirir dilim[0] = 888. // Bu atama işlemi dilim'in eriştirdiği elemanları // değiştirmez. Örneğin aşağıdaki kodda dilim 'e yapılan atama işlemi onun eriştirdiği elemanları değiştirmez. $]. yarıyaBöl(sayı). 14 ]). 45. birincisinde de görünür assert(isimleSayılar[4] == "dört"). assert(dizi1 == [ 10. 13.Değerler ve Referanslar değişken2..3. 11. 22 ]. 13. // asıl değer değişir } void yarıyaBöl(ref int bölünen) { bölünen /= 2.org 223 . // örneğin ikincisine eklenen eleman .. 21. 14 ]. hangi asıl nesneye erişim sağlandığını değiştirir. Eşleme tabloları da atandıklarında aynı asıl tabloya erişim sağlarlar: string[int] isimleSayılar = [ 1 : "bir". int[] dilim = dizi1[1 . 12. http://ddili. isimleSayılar2[4] = "dört". // 1 ve 2 indeksli elemanlara // eriştirir dilim[0] = 777.1 . int[] dizi2 = [ 20. assert(sayı == 4). 10: "on". Copyright © 2009-2011 Ali Çehreli. // .. 777.. değişken 'in de erişim sağladığı nesnenin üyesidir. ].üye = 2.

// İlk dizi değişmez assert(dizi1[0] == 10). 21. kopya yoluyla değeri değiştirilen üye ilk seferde değişken1 'inkidir.üye = 4. Copyright © 2009-2011 Ali Çehreli. Bu özel değeri ve is anahtar sözcüğünü bir sonraki derste anlatacağım. Temel işlemler açısından referans olarak davranırlar.3. Dinamik diziler. Referans türlerinin değişkenleri ise. sonra da değişken2 'nin nesnesine erişim sağlar. 45. dizi2[0] = 11. onların yaşam süreçleri erişim sağladıkları bir asıl değer olmadan başlamaz. kendilerine ait olmayan elemanlara erişim sağlarlar.üye == 3).Değerler ve Referanslar } assert(dizi2 == [ 20.üye = 3.üye = 1.2 Referans türleri hiçbir değere erişim sağlamıyor olabilirler Referans değişkenlerinde mutlaka bir asıl değer vardır. 20. sonra ise değişken2 'ninkidir.üye = 2. kopya. kopya = değişken2. Atama işlecinin referans türlerindeki bu etkisini bir de BirSınıf türünde görelim: auto değişken1 = new BirSınıf. kopya. assert(değişken2. http://ddili. assert(değişken1. 45.üye == 4). Oradaki atama işlemleri sonucunda kopya önce değişken1 'in nesnesine. Böyle değişkenler null özel değerine eşittirler. auto kopya = değişken1. Sabit uzunluklu diziler ise değer türleridir. değişken2. yukarıdaki örneklerde de görüldüğü gibi. auto değişken2 = new BirSınıf. referans türleridir. 888 ]). // dizi2'nin elemanları dizi1'inkilerden farklı olur auto dizi2 = dizi1. Örneğin bir BirSınıf değişkeni. Kendi elemanlarına sahiptirler ve değer türü olarak davranırlar: int[3] dizi1 = [ 10. henüz hiçbir değere erişim sağlamayacak şekilde oluşturulabilirler. değişken1. erişim sağladığı nesne henüz belli olmadan şöyle tanımlanabilir: BirSınıf değişken. dinamik diziler referans D'nin iki dizi türü bu konuda farklılık gösterir. Dinamik diziler (dilimler).4 Sabit uzunluklu diziler değer.org 224 . 30 ].

bool değerEşitliği. } Copyright © 2009-2011 Ali Çehreli. http://ddili.org 225 . to!string(adresEşitliği)). bool adresEşitliği) { writefln("%50s%9s%9s".Değerler ve Referanslar Tanımlandığı zaman uzunluğu da belirlendiği için dizi1 sabit uzunluklu bir dizidir. int evrenselDeğişken = 9.5 Özet • Değer türlerinden olan her değişkenin kendi değeri ve kendi adresi vardır • Referans değişkenlerinin ne değerleri vardır ne de adresleri. auto anahtar sözcüğü nedeniyle dizi2 de aynı türü edinir.conv. Birisinde yapılan değişiklik diğerini etkilemez. class BirSınıf { int üye. 45. başlık. foreach (i.length) { write('=').. takma isim gibidirler • Referans türlerinden olan değişkenlerin kendi adresleri vardır. Değişken Türü" a == b &a == &b". to!string(değerEşitliği). hangi asıl nesneye erişildiğini değiştirir • Referans türlerinden olan değişkenler null olabilirler Yukarıda anlatılan değişik türlerin değerlerine ve adreslerine == işlecini uygulayınca ortaya şöyle bir tablo çıkıyor: Değişken Türü a == b &a == &b ===================================================================== aynı değerli değişkenler (değer türü) true false farklı değerli değişkenler (değer türü) false false ref değişkenli foreach true true ref olmayan değişkenli foreach true false out parametreli işlev true true ref parametreli işlev true true in parametreli işlev true false aynı elemanlara erişen dilimler true false farklı elemanlara erişen dilimler false false aynı nesneye erişen BirSınıf'lar (referans türü) true false farklı nesneye erişen BirSınıf'lar (referans türü) false false O tabloyu şu programla ürettim: import std. void bilgiSatırı(const dchar[] başlık. ama erişim sağladıkları değer kendilerinin değildir • Referans türlerinde atama işlemi değer değiştirmez. import std. writeln(başlık).stdio. başlık. } void başlıkÇiz() { const dchar[] başlık = " " writeln(). Her ikisi de kendi elemanlarına sahiptirler. } } writeln(). 0 .

ref eleman. dilim1 == dilim3. dilim1 == dilim2. değişken1 == değişken1. auto değişken1 = new BirSınıf. &değişken1 == &değişken2). } foreach (i. değişken1 == değişken3.Değerler ve Referanslar void main() { başlıkÇiz(). parametre == evrenselDeğişken. &eleman == &dizi[i]). &değişken1 == &değişken3).1]. eleman == dizi[i]. &sayı1 == &sayı3). refParametre(evrenselDeğişken). http://ddili. eleman. bilgiSatırı("farklı elemanlara erişen dilimler". $ . &parametre == &evrenselDeğişken). } outParametre(evrenselDeğişken). } void outParametre(out int parametre) { bilgiSatırı("out parametreli işlev". parametre == evrenselDeğişken. dizi) { bilgiSatırı("ref değişkenli foreach". int sayı2 = 12. &dilim1 == &dilim3). bilgiSatırı("farklı değerli değişkenler (değer türü)". auto değişken3 = new BirSınıf. bilgiSatırı( "aynı nesneye erişen BirSınıf'lar (referans türü)". &parametre == &evrenselDeğişken). int[] dilim3 = dilim1[0 . int[] dizi = [ 4 ]. Copyright © 2009-2011 Ali Çehreli. sayı1 == sayı3. foreach (i. &sayı1 == &sayı2). 7 ]. int[] dilim1 = uzunDizi. } void refParametre(ref int parametre) { bilgiSatırı("ref parametreli işlev".. 6. int[] uzunDizi = [ 5. int sayı3 = 3. bilgiSatırı( "farklı nesneye erişen BirSınıf'lar (referans türü)". auto değişken2 = değişken1.org 226 . eleman == dizi[i]. inParametre(evrenselDeğişken). &dilim1 == &dilim2). &eleman == &dizi[i]). dizi) { bilgiSatırı("ref olmayan değişkenli foreach". sayı1 == sayı2. bilgiSatırı("aynı değerli değişkenler (değer türü)". int[] dilim2 = dilim1. bilgiSatırı("aynı elemanlara erişen dilimler". int sayı1 = 12.

Copyright © 2009-2011 Ali Çehreli. Evrensel değişkenler işlevlerin dışında tanımlanırlar ve içinde tanımlandıkları kaynak dosyadaki (modüldeki) bütün kodlar tarafından erişilebilirler. &parametre == &evrenselDeğişken).Değerler ve Referanslar } void inParametre(in int parametre) { bilgiSatırı("in parametreli işlev". parametre == evrenselDeğişken.org 227 . http://ddili. } Programda işlev parametrelerini karşılaştırmak için bir de evrensel değişken kullandım.

olmayan nesnenin üye 'sine erişilmeye çalışılması programın çökmesine neden olur: $ .org 228 . // erişim sağlamayan Bir referans türü olduğu için yukarıdaki değişken 'in bir kimliği vardır. } void main() { BirSınıf değişken. Bu değeri de herhangi başka bir değer gibi yazdırabiliriz: writeln(null)./deneme Segmentation fault "Segmentation fault".üye). erişim sağladığı bir BirSınıf nesnesi olmadığı için o değişken ile işlemler yapmamız beklenemez: void kullan(BirSınıf değişken) { writeln(değişken. } kullan işlevine verilen değişken hiçbir nesneye erişim sağlamadığı için. Doğal olarak. diğer bütün dillerde olduğu gibi. programın geçerli olmayan bir bellek bölgesine erişmeye çalıştığı için işletim sistemi tarafından acil olarak sonlandırıldığını gösterir. ama erişim sağladığı bir nesne henüz yoktur.1 null değeri Erişim sağladığı nesne henüz belli olmayan referans türü değişkenleri null özel değerine sahiptir. D'de de 0 özel değerini taşır. http://ddili. Böyle bir değişkenin bellekte şu şekilde durduğunu düşünebiliriz: değişken ---+------+--| null | ---+------+--Hiçbir nesneye erişim sağlamayan referansların değerleri null 'dır. Bunu aşağıda anlatıyorum. Böyle bir değişken kendisine bir nesne atanana kadar kullanılamaz bir durumdadır. 46.null değeri ve is işleci 46 null değeri ve is işleci Bir önceki derste referans türünden olan değişkenlerin hiçbir nesneye erişim sağlamadan da oluşturulabileceklerini söylemiştim: BirSınıf erişimSağlayan = new BirSınıf. kullan(değişken). BirSınıf değişken. Yukarıdaki satırın çıktısı: 0 Değeri null olan bir değişken çok kısıtlı sayıda işlemde kullanılabilir: Copyright © 2009-2011 Ali Çehreli. Bu değer.

Değerler eşit olmadığında true üretir: if (hız !is yeniHız) { // farklı değerlere sahipler } Copyright © 2009-2011 Ali Çehreli. 2. Onun yerine is 'i kullanmak gerekir. İngilizce'de "olmak" fiilinin "öyledir" kullanımındaki anlamına sahiptir. http://ddili. bir değişkenin null olup olmadığını denetlemek için is işleci kullanılır. o ifade derlenemez. 46. Not: is 'in örneğin şablon olanağında tekli işleç olarak kullanıldığı durumlar da vardır. başka türlerle de kullanılabilir. is 'in tersi !is işlecidir. Bu iki değer aynıysa true . değilse false üretir. Örneğin iki tamsayı değişkenin değerleri şöyle karşılaştırılabilir: if (hız is yeniHız) { // ikisi aynı değerde } else { // ikisi farklı değerde } Dilimlerde de iki dilimin aynı elemanlara erişim sağlayıp sağlamadıklarını denetler: if (dilim is dilim2) { // aynı elemanları paylaşıyorlar } 46. ve null bir değişkenin eriştirdiği geçerli bir nesne bulunmadığı için. İki değerden birisinin null olabildiği durumlarda == işlecinin kullanılamadığını yukarıda gördük. Bu yüzden. yani sol ve sağ tarafına iki değer alır.null değeri ve is işleci 1. Erişim sağlaması için geçerli bir nesne atamak değişken = new BirSınıf.2 is işleci is . "Bu değişken null ise" koşulunu denetlemeye yarar: if (değişken is null) { // hiçbir nesneye erişim sağlamıyor } is . Bu dersi ilgilendiren kullanımında ikili bir işleçtir. // artık nesnesi var O atamadan sonra artık değişken 'in erişim sağladığı bir nesne vardır. değişken artık BirSınıf işlemleri için kullanılabilir.org 229 . null olup olmadığını denetlemek if (değişken == null) // ← derleme HATASI Ne yazık ki. == işleci asıl nesneleri karşılaştırdığı için.3 !is işleci == ve != işleçlerine benzer şekilde.

onun bu değerle ilişkisini keser: değişken = null. eşleme tablosu değişkenine null değer atamak. asıl nesnenin sonlanmasına neden olur: değişken2 = null. Şimdi o yöntemlere bir dördüncüsünü ekleyebiliriz. isimleSayılar = null. ya da ilerideki bir zamanda sonlandıracaktır. // artık hiçbir elemana erişim // sağlamaz Copyright © 2009-2011 Ali Çehreli. auto değişken2 = değişken.---+---+--| . Böyle bir değişkene null değerini atamak...---+-|-+-▲ | | | +----------------------------------+ İsimsiz BirSınıf nesnesine erişen son referans olan değişken2 'ye de null atanması. yalnızca o nesnelere erişim sağlarlar. | |null| | o | ---+-------------------+--. asıl nesne çöp toplayıcı tarafından sonlandırılacaktır.4 null değer atamak Referans türleri erişim sağladıkları nesnelere sahip değildirler.. Program açısından artık o nesne yoktur çünkü o nesneye erişen referans kalmamıştır: değişken ---+----+--|null| ---+----+--değişken2 ---+----+--|null| ---+----+-- ---+-------------------+--| | ---+-------------------+--- Eşleme tabloları dersinin birinci problemi..---+-|-+--. http://ddili..org 230 .. önceki dersteki iki değişkenin tek bir nesneye eriştikleri duruma bakalım: auto değişken = new BirSınıf.---+---+--| . (isimsiz BirSınıf nesnesi) değişken değişken2 ---+-------------------+--.---+-|-+-▲ | | | | | +--------------------+------------+ Bu değişkenlerden birisine null atamak. Çöp toplayıcı asıl nesneyi türüne göre ya hemen. bir eşleme tablosunu boşaltan üç yöntem gösteriyordu.---+---+--.null değeri ve is işleci 46. // . değişkenin erişim sağladığı asıl tablo ile ilişkisini keser: string[int] isimleSayılar. BirSınıf nesnesine artık yalnızca değişken2 tarafından erişilmektedir: (isimsiz BirSınıf nesnesi) değişken değişken2 ---+-------------------+--. o değişkenin artık hiçbir nesneye erişim sağlamamasına neden olur. | | o | | o | ---+-------------------+--. Hiçbir referans tarafından erişilmiyor olması. Örnek olarak. Eğer bu atama sonucunda asıl nesneye erişen başka referans değişkeni kalmamışsa.---+----+--. o nesnenin artık kullanılmadığını gösterir.---+----+--.

eğer isimleSayılar asıl tabloya erişim sağlayan son referans idiyse. // . Bir dilimin de artık hiçbir elemana erişim sağlaması istenmiyorsa null atanabilir: int[] dilim = dizi[ 10 .org 231 .. null olup olmadığını denetlemek • == işleci asıl nesneye erişmeyi gerektirebileceği için. null olma olasılığı bulunan referanslar is ile denetlenmelidir • is 'in tersi !is 'dir • null atanan referans artık hiçbir elemana erişim sağlamaz • Hiçbir referansın erişim sağlamadığı nesneler çöp toplayıcı tarafından sonlandırılırlar Copyright © 2009-2011 Ali Çehreli. http://ddili.. 20 ]. // artık hiçbir elemana erişim sağlamaz 46. hiçbir değere erişim sağlamayan referans değeridir • null referanslar yalnızca iki işlemde kullanılabilirler: değer atamak.5 Özet • null .. asıl tablonun elemanları çöp toplayıcı tarafından sonlandırılacaklardır.null değeri ve is işleci Yukarıdaki BirSınıf örneğine benzer şekilde. dilim = null.

D'nin de aralarında bulunduğu bazı diller türlerin uyumluluklarını derleme zamanında denetlerler. Derleyici o kodu tür uyuşmazlığı nedeniyle reddeder. 1 de true 'ya dönüşür • Açıkça yapılan tür dönüşümünün söz dizimi şöyledir: cast (Türİsmi)değer • Tür dönüşümlerinden kaçınmak daha iyidir İşlemlerde kullanılan değişken ve nesne türlerinin hem o işlemlerle hem de birbirleriyle uyumlu olmaları gerekir. true da 1'e otomatik olarak dönüşür.25. Yazının geri kalanını daha çok başvuru kaynağı olarak düşünebilirsiniz: • Otomatik aritmetik tür dönüşümleri güvenli yönde yapılır: küçük türden büyük türe doğru ve değişebilen türden değişmez türe doğru • enum türler tamsayı türlere otomatik olarak dönüşürler. farklı tür demek değildir. çünkü bir kesirli sayının bir tamsayı kadar arttırılmasında bir uyumsuzluk olduğu düşünülemez. 47. Bu yazıyı yazdığım sırada kullandığım derleyici. Yoksa anlamsız veya yanlış sonuçlar doğabilir.2 Otomatik tür dönüşümleri Her ne kadar bir double değerin bir int değer kadar arttırılmasında bir sakınca olmasa da. Birbirinden farklı türler kullanılan işlemlerle karşılaştığında. toplam += artış. bir toplama işleminde sanki bir sayıymış gibi dizgi kullanmaya çalışan şu koda bakalım: char[] dizgi. writeln(dizgi + 5). tersi de doğrudur: 0 false 'a. derleyici önce değerlerden birisini diğer türe dönüştürür. toplam ve artış farklı türlerden oldukları halde o işlemde bir yanlışlık yoktur. int artış = 3. Bu dönüşümde kullanılan tür. Çünkü farklı türlerin güvenle kullanılabildiği işlemler de vardır.1 Tür Dönüşümleri Özet Önce tür dönüşümleri konusunda bilinmesi gerektiğini düşündüğüm noktaları vermek istiyorum. 64 bitlik olan double . Tür uyumsuzluğu. http://ddili. 32 bitlik olan int 'ten daha büyük (veya geniş) bir türdür.org 232 . ((dizgi) + (5)) ifadesinde uyumsuz türler olduğunu belirtir: char[] ve int . Bir int 'e sığabilen her değer bir double 'a da sığabilir. Böyle dillere "türleri derleme zamanında belli olan" anlamında "statically typed" dil denir. ama tamsayılar enum türlere otomatik olarak dönüşmezler • bool türünde false 0'a.Tür Dönüşümleri 47 47. o işlemin mikro işlemcide yine de belirli bir türde yapılması gerekir. türlerin uyumsuz olduğunu bildiren şu hatayı veriyor: Error: incompatible types for ((dizgi) + (5)): 'char[]' and 'int' O hata mesajı. değer kaybına neden olmayacak şekilde seçilir. Örneğin double türündeki bir değişkene int türünde bir değer eklenmesinde bir sakınca yoktur: double toplam = 1. ve işlemi ondan sonra gerçekleştirir. Anlamsız işlem örneği olarak. Kesirli sayılar dersinden hatırlayacağınız gibi. Örneğin double türü int türünün bütün Copyright © 2009-2011 Ali Çehreli.

Böyle otomatik dönüşümler aritmetik işlemlerle sınırlı değildir.2. Örneğin int türünde parametre alan bir işleve byte türünde bir değer gönderilebilir: void birİşlem(int sayı) { // . http://ddili. toplam += aslında_isimsiz_olan_double_bir_deger. Yukarıdaki işlemde perde arkasında neler olduğunu şöyle gösterebiliriz: double aslında_isimsiz_olan_double_bir_deger = artış. Asıl değerin kendisi değişmez. Değilse tamsayı terfisi uygulanır ve sonra şu işlemlere geçilir: 1. ve birİşlem o geçici int değeri ile çağrılır. her zaman için isimsiz ve geçici bir değişken veya nesnedir. Derleyici. Eğer iki tür de aynı ise durulur 2.. Eğer kullanılan türler bir dönüşüm sonucunda birlikte kullanılabiliyorlarsa. işaretsiz olan işaretliye dönüştürülür 4. Bu kadarını akılda tutmak çoğu durumda yeterlidir. birİşlem(küçükDeğer).1 Aritmetik dönüşümler Aritmetik işlemlerde kullanılan değerler güvenli yönde. Değilse ama birisi double ise diğeri double 'a dönüştürülür 3. Hiçbirisi değilse işaretli tür işaretsiz türe dönüştürülür Daha küçük tamsayı türünden daha büyük türe doğru yapılan tamsayı terfileri aşağıdaki tablodakine benzer: Copyright © 2009-2011 Ali Çehreli. Değerlerden birisi real ise diğeri real 'e dönüştürülür 2.Tür Dönüşümleri değerlerini tutabilir. } // otomatik tür dönüşümü Orada da önce küçükDeğer 'e eşit geçici bir int oluşturulur. küçük tür büyük türe dönüştürülür 3. int değeri önce double türündeki geçici bir ara değere dönüştürür ve işlemde o dönüştürdüğü değeri kullanır. Bu örnekteki geçici değer yalnızca += işlemi süresince yaşar. derleyici gerektikçe değerleri otomatik olarak dönüştürür.org 233 . Eğer işaretli tür işaretsiz türden büyükse. Dönüşüm kuralları şöyledir: 1. O yüzden yukarıdaki toplam += artış işlemi double türünde güvenle gerçekleştirilebilir. Eğer her ikisi de işaretli ise. ama artış 'ın değerine eşit olan geçici bir değer kullanılır.. } void main() { byte küçükDeğer = 7. 47. veya her ikisi de işaretsiz ise. Birbirinin aynısı olmayan türlerin kullanıldığı başka durumlarda da otomatik tür dönüşümleri uygulanır. ama bunun tersi doğru değildir. yani küçük türden büyük türe doğru gerçekleştirilirler. Dönüştürülen değer. Örneğin yukarıdaki += işlemi sırasında artış 'ın kendi türü değiştirilmez. Değilse ama birisi float ise diğeri float 'a dönüştürülür 4.

Yukarıda anlatıldığı gibi. çünkü hem zaten türün büyüklüğünde bir değişiklik olmaz.. vs. Örneğin short . int . kendisinin const olanına otomatik olarak dönüşür. onun değeri kullanılarak isimsiz bir short değer oluşturulur ve işlev o geçici değerle çağrılır.3 Açıkça yapılan tür dönüşümleri Bazı durumlarda bazı tür dönüşümlerini elle kendimiz yapmak zorunda kalabiliriz. Onun türünde bir değişiklik olmaz. uint .Tür Dönüşümleri Hangi Türden bool byte ubyte short ushort char wchar dchar Hangi Türe int int int int int int int uint Benzer dönüşümler diğer tamsayılar arasında da vardır. hem de const değerler değiştirilemezler: Copyright © 2009-2011 Ali Çehreli. 47. http://ddili. } void main() { int intSayı = 42. // şimdi derlenir O kodda intSayı 'nın değerine eşit olan short türünde bir değer oluşturulur ve küçükTürleİşlem o geçici değerle çağrılır. tür dönüşümünü elle kendimiz gerçekleştirebiliriz: küçükTürleİşlem(cast(short)intSayı). 47. Programcının isteğiyle yapılan tür dönüşümlerinin söz dizimi şöyledir: cast(Türİsmi)değer cast parantezinin içine hedef tür yazılır ve değer o türe dönüştürülür. long ve ulong gibi daha büyük türlere otomatik olarak dönüşürler. küçükTürleİşlem(intSayı). Burada intSayı 'nın türünün değişmediğine özellikle dikkat çekmek istiyorum. } // ← derleme HATASI Eğer o işlev çağrısının yine de her zaman için güvenli olduğundan. gibi küçük türler. örneğin intSayı 'nın değerinin programın çalışması sırasında her zaman için short 'a da sığabildiğinden eminsek.4 const dönüşümü Her referans türü. int türünün kendisinden daha küçük olan short 'a otomatik olarak dönüştürülmesi güvenli değildir. Bu güvenli bir dönüşümdür.. Bu yüzden derleyici aşağıdaki kodu kabul etmez: void küçükTürleİşlem(short sayı) { // .org 234 .

otomatik olarak tamsayı türlere dönüşürler. Çünkü değer türlerinde zaten değer kopyalandığı için.Kupa 1 değerini alır ve sonuç 11 olur: int sonuç = 10 + OyunKağıdıRengi. Sinek } Değerleri özellikle belirtilmediği için o tanımda değerler sıfırdan başlayarak ve birer birer arttırılarak atanır. // derlenir (değer türü) Yukarıdaki durumda const türden const olmayan türe dönüşüm yasaldır. dchar[] birSöz. Derleyici bu yüzden yukarıdaki dönüşümü reddeder. double[string] başkaKesirler = kesirler. Böyle isimli enum değerleri. çünkü değerler sabit referanslar aracılığıyla değiştirilemezler. } // .25. otomatik const dönüşümleri yalnızca referanslarla ilgilidir.Kupa. sabit parametre alan işleve güvenle gönderilebilir.org 235 . Bu yüzden. birSöz ~= "merhaba dünya". 47..Tür Dönüşümleri dchar[] parantezİçineAl(const dchar[] metin) { return "{" ~ metin ~ "}". elemanların değiştirilmelerine izin vermez. enum türleri isimli değerler kullanma olanağı sunarlar: enum OyunKağıdıRengi { Maça. O kodda sabit olmayan birSöz . const olmayan bir türe dönüşmez: const double[string] kesirler = [ "çeyrek" : 0. Bunun tersi doğru değildir: tamsayı değerler enum türlerine otomatik olarak dönüşmezler.. ama derlenemez: OyunKağıdıRengi renk = 2. Bu konu yalnızca referans değişkenleri ve referans türleri ile ilgilidir. assert(sonuç == 11).50 ].Sinek 'in değeri 3 olur. tamsayıdan enum 'a dönüşümün açıkça yapılmasını şart koşar: Copyright © 2009-2011 Ali Çehreli. Örneğin aşağıdaki koddaki toplama işlemi sırasında OyunKağıdıRengi.Karo değerini almasını bekleyebiliriz. Buna göre örneğin OyunKağıdıRengi. Bunun tersi doğru değildir: const bir referans türü. Örneğin aşağıdaki kodda renk değişkeninin 2 değerinin karşılığı olan OyunKağıdıRengi.5 enum dönüşümleri enum dersinden hatırlayacağınız gibi. // ← derleme HATASI kesirler const olduğu için. http://ddili. O elemanların değiştirilmelerinin engellenebilmesi için onlara const olmayan başkaKesirler referansıyla erişilmesi engellenmelidir. "yarım" : 0. asıl değeri değiştirmesi söz konusu değildir. kopyanın const olan asıl nesneyi değiştirmesi söz konusu olamaz: const int köşeAdedi = 4. int kopyası = köşeAdedi. // ← derleme HATASI D. Karo. parantezİçineAl(birSöz). çünkü dönüştürülen değer asıl değerin bir kopyası haline gelir. Kupa.

Kodda ileride yapılabilecek bir değişiklik artık intSayı 'nın değerini short 'a sığmayacak kadar büyük hale getirebilir ve cast(short)intSayı tür dönüşümü yanlış sonuçlara neden olabilir: import std. void küçükTürleİşlem(short sayı) { writeln("işlevdeki değer: ". assert(başkaDurum). assert(!birDurum). Bir ifadeyi açıkça bool türüne dönüştürmek. 1 değeri de true 'ya otomatik olarak dönüşür: bool birDurum = 0. // true 47. } Tamsayılar ve Aritmetik İşlemler dersinde gördüğümüz gibi. 0'dan farklı ise true değerini üretir: bool birDurum = cast(bool)(5 . // false bool başkaDurum = cast(bool)(8 * 7). küçükTürleİşlem(cast(short)intSayı). 40000 değeri short. } void main() { int intSayı = 40000. Örnek olarak yukarıda gördüğümüz cast(short) dönüşümüne bakalım.org 236 . assert(birKoşul == 0). http://ddili.max 'tan daha büyük olduğu için taşar ve eksi bir değere dönüşür: Copyright © 2009-2011 Ali Çehreli. assert(!birDurum). veya daha kısa olarak: auto renk = cast(OyunKağıdıRengi)2. intSayı). assert(başkaKoşul == 1). writeln("asıl değer : ". // false // true Sıfır ve bir dışındaki değerler otomatik olarak dönüşmezler. int başkaKoşul = true.6 bool dönüşümleri bool türünün değerlerinden false 0'a.stdio. bool başkaDurum = 1.5).7 Tür dönüşümlerinden kaçının Hem otomatik hem de elle açıkça yapılan tür dönüşümleri programlarda şaşırtıcı durumlara neden olabilirler. o ifade 0 ise false . Bunun tersi de doğrudur: 0 değeri false 'a. // derlenir 47.Tür Dönüşümleri OyunKağıdıRengi renk = cast(OyunKağıdıRengi)2. true da 1'e otomatik olarak dönüşür: int birKoşul = false. sayı). assert(başkaDurum).

En iyisi. http://ddili. hangi işlemlerin hangi türlerle yapılacağına program tasarlanırken baştan karar vermek ve işlemleri olabildiğince tür dönüşümlerine başvurmadan o türlerle gerçekleştirmektir. Copyright © 2009-2011 Ali Çehreli. ya da etkileştikleri başka kütüphanelere uymak zorunda kalırlar. Bu yüzden tür dönüşümleri az da olsa hemen hemen her programda bulunur. Ne yazık ki ne kadar iyi olurlarsa olsunlar tasarımlar eninde sonunda ya kendileri değişmek.org 237 .Tür Dönüşümleri asıl değer : 40000 işlevdeki değer: -25536 ← yanlış değer Bu yüzden tür dönüşümlerinden genel olarak kaçınmak gerekir.

48. Her ne kadar o işlev altı tane parametre alıyor gibi görünse de. } Yukarıdaki tanım.1 Tanımlanması İşte struct . struct . birbirleriyle ilgili olan parametreleri çifter çifter düşünürsek. "yapı" anlamına gelen "structure"ın kısaltmasıdır. sayı = başkaSayı. out int sonuçSaati. sonuçSaati = başlangıçSaati + eklenecekSaat. Özellikle bir araya getirerek yeni tür tanımlama kavramı. sonuçSaati %= 24. out . ama böyle bir değer her zaman tek başına kullanışlı olamaz.org 238 . Değişkenler bazen başka değişkenlerle bir araya geldiklerinde anlam kazanırlar. in int eklenecekSaat. // bir değişken // başkaSayı'nın değerini alması // bir nesne // başkaZaman'ın değerini alması Copyright © 2009-2011 Ali Çehreli. Yapı kavramını anlamak için daha önce assert dersinde gördüğümüz zamanEkle işlevine bakalım: void zamanEkle( in int başlangıçSaati. yapılarda ve sınıflarda aynıdır. int türünden bir tamsayı örneğin iki olay arasında geçen süreyi dakika türünden ifade etmek için kullanılabilir. aslında üç çift bilgi aldığını görürüz. sonuçDakikası %= 60. birbirleriyle ilişkili değişkenleri bir araya getirerek yeni bir tür olarak kullanma olanağı verir: struct GününSaati { int saat. int dakika. } Not: İşlevin in . saat ve dakika isimli iki int değişkeni bir araya getiren ve ismi GününSaati olan yeni bir tür tanımlar. Örnek olarak int türüne benzer kullanımını şöyle gösterebiliriz: int sayı. http://ddili. in int başlangıçDakikası. sonuçSaati += sonuçDakikası / 60. daha sonra göreceğimiz sınıfları anlamada da yardımcı olacak. GününSaati zaman. Yukarıdaki tanımdan sonra artık başka türler gibi kullanabileceğimiz GününSaati isminde yeni bir türümüz olur. struct anahtar sözcüğü ile oluşturulur. O çiftlerden ilk ikisi giriş olarak. in int eklenecekDakika. Yapılar. Bu derste yapılarla ilgili olarak anlatacağım çoğu bilgi. temel türleri veya başka yapıları bir araya getirerek yeni türler oluşturmaya yarayan olanaklardır. out int sonuçDakikası) { sonuçDakikası = başlangıçDakikası + eklenecekDakika. Yeni tür. zaman = başkaZaman.Yapılar 48 Yapılar Derslerin başında temel türlerin üst düzey kavramları ifade etmede yetersiz kalacaklarını söylemiştim. ve unittest bloklarını fazla yer tutmamak için bu derste göstermiyorum. sonuncusu da çıkış olarak kullanılmaktadır.

tür tanımıdır. daha sonradan yapı nesneleri oluşturulduğunda ne tür üye değişkenlerinin olacağını belirler: struct GününSaati { int saat.. Bunları daha sonraki bir derste anlatacağım. Bu derste yalnızca yapı üyelerini gösteriyorum. in GününSaati eklenecek. Bu tanıma göre. } Copyright © 2009-2011 Ali Çehreli. out GününSaati sonuç) { // . } // ← değişken tanımı değil! // ← değişken tanımı değil! Yapı tanımı.org 239 . nesnenin içinde oluşturulacak // ← sonra.. Yapının bir araya getirdiği parçalara üye adı verilir. } Yapılar için özel işlevler de tanımlanabilir. Örneğin yukarıdaki işlev altı tane int yerine. asıl amacına çok daha uygun olacak şekilde üç tane GününSaati türünde parametre alabilir: void zamanEkle(in GününSaati başlangıç. o kapsam içindeki üyelerin yapının tanımlandığı an yaşamaya başladıklarını düşünebilirsiniz.. nesne tanımı değildir Burada bir uyarıda bulunmam gerekiyor: İsim Alanı dersinde ve Yaşam Süreçleri dersinde anlatılanlar doğrultusunda. Yapı tanımı.. int dakika. 48.1 Yapı tanımı. } // ← sonra. GününSaati kalkmaZamanı. nesnenin içinde oluşturulacak O üye değişkenler.Yapılar Yapı türleri şöyle tanımlanır: struct Türİsmi { // .1. yukarıdaki GününSaati yapısının iki üyesi vardır: saat ve dakika . bu yapı türünden bir nesne oluşturulduğu zaman. yapı tanımında kullanılan küme parantezlerine bakarak.2 Kodlama kolaylığı Saat ve dakika gibi iki bilgiyi böyle bir araya getirerek tek bir tür gibi kullanabilmek büyük kolaylık sağlar... bunun saat ve dakika değişkenleri öncekinden bağımsızdır 48. http://ddili. türü oluşturan üyeler ve varsa özel işlevleri . int dakika. o nesnenin parçası olarak oluşturulurlar: GününSaati yatmaZamanı. // içinde kendi saat ve dakika // değişkenlerini barındırır // // // // // bu da kendi saat ve dakika değişkenlerini barındırır. değişken tanımlamaz: struct GününSaati { int saat. Bu doğru değildir.1.

Tek fark. işlevler return deyimiyle tek bir değer döndürebilirler. Toplantı bütçeToplantısı. İşlevler dersinden hatırlayacağınız gibi. Yapılar bu kısıtlamayı da ortadan kaldırırlar: Birden fazla bilgiyi bir araya getirerek tek bir tür oluşturdukları için. http://ddili. GününSaati bitiş. } 48. Örneğin GününSaati yapısından iki üyesi bulunan başka bir yapı şöyle tasarlanabilir: struct Toplantı { string konu. Yemek öğleYemeği. Burada aslında Süre diye yeni bir tür tanımlamak ve GününSaati nesnelerine Süre nesnelerini eklemek çok daha doğru olurdu. Ben bu derste yine de yalnızca GününSaati türünü kullanacağım. Yapılar da yapı üyesi olabilirler. } Toplantı yapısı da başka bir yapının üyesi olabilir. işlevlerin yan etki oluşturmak yerine değer üretmeleri tercih edilir. Ürettiği iki tane sonucu tek bir değer olarak döndüremiyordu. ondan sonra da üyenin isminin yazılmasıdır: başlangıç.saat = 10. GününSaati başlangıç. işlevlerin dönüş türü olarak kullanılabilirler. Yapılarla ilgili bu kadarlık bilgiyi kullanarak zamanEkle işlevini artık şu şekilde değiştirebiliriz: Copyright © 2009-2011 Ali Çehreli.2 Üye erişimi Yapı üyelerini de herhangi bir değişken gibi kullanabiliriz. Hatırlayacağınız gibi. üyelere erişmek için nesnenin isminden sonra önce erişim işleci olan nokta. int katılımcıSayısı. değer üreten bir işlev haline de gelmiş olur.. zamanEkle ürettiği saat ve dakika değerlerini zaten bu yüzden iki tane out parametre olarak tanımlamak zorunda kalıyordu. } Böylece zamanEkle artık yan etki oluşturan değil.org 240 . Artık işlevden tek bir GününSaati nesnesi döndürebiliriz: GününSaati zamanEkle(in GününSaati başlangıç.Yapılar Not: Günün saatini belirten böyle iki değerin eklenmesi aslında normal bir işlem olarak kabul edilmemelidir. Yemek diye bir yapı olduğunu da varsayarsak: struct GünlükPlan { Toplantı projeToplantısı. Örneğin kahvaltı zamanı olan 7:30'a öğle yemeği zamanı olan 12:00'yi eklemek doğal değildir.. O satır. in GününSaati eklenecek) { // . başlangıç nesnesinin saat üyesine 10 değerini atar.

sonuç. Herhangi bir değişkenin veya nesnenin tutarlı bir şekilde kullanılabilmesi için mutlaka kurulması gerekir. Bu yazım. ve sonuç gibi kısa isimler verebiliyoruz. dersSüresi).dakika = 30. başlangıçSaati gibi bileşik isimler kullanmak yerine de nesnelerin üyelerine nokta ile başlangıç.saat %= 24.saat = 1. in GününSaati eklenecek) { GününSaati sonuç. dersSonu. 1 saat 15 dakika süren ve 8:30'da başlayan dersin bitiş zamanını 9:45 olarak hesaplar: void main() { GününSaati dersBaşı. writefln("Ders sonu: %s:%s".dakika = başlangıç. http://ddili.saat + eklenecek.dakika / 60.saat şeklinde erişiyoruz. sonra saat ve dakika üyelerinin değerleri atanmaktadır. dersBaşı nesnesinin kurulması ile ilgilidir. dersSüresi. dersBaşı. O işlevi kullanan bir kod aşağıdaki şekilde yazılabilir.saat += sonuç. 30). GününSaati dersSüresi = GününSaati(1. dersBaşı. O satırlarda önce nesne tanımlanmakta. Bu çok önemli ve yaygın bir işlem olduğu için.dakika). ondan sonraki üç satır da dersSüresi nesnesinin kurulmasıdır. Biraz aşağıda bu işlemlerin çok daha kolay ve kısa olanlarını göstereceğim. const GününSaati dersSonu = zamanEkle(dersBaşı. Bu program. sonuç. sonuç. 48.dakika. GününSaati dersSüresi. dersSüresi.3 Kurma Yukarıdaki main 'in ilk üç satırı. } Ders sonu: 9:45 Yukarıdaki main 'i şimdiye kadar bildiklerimizi kullanarak yazdım. } return sonuç. türü otomatik olarak sağ taraftan çıkarsayan auto anahtar sözcüğü ile daha da kısa hale getirilebilir: Copyright © 2009-2011 Ali Çehreli. eklenecek . sonuç. dersSonu.saat.org 241 . 15). Bu işlevde kullanılan değişken isimlerinin artık çok daha kısa seçilebildiğine dikkat edin: nesnelere başlangıç .saat.dakika %= 60.Yapılar GününSaati zamanEkle(in GününSaati başlangıç.saat = başlangıç.dakika = 15.saat = 8. yapı nesneleri için kısa bir kurma söz dizimi vardır: GününSaati dersBaşı = GününSaati(8. sonuç.dakika + eklenecek.

// ← derleme HATASI const olarak işaretlenmiş olan sabit dersBaşı nesnesinin üyelerini değiştirmek yasal değildir. // Sonuncusu eksik auto d2 = Deneme('a'.dakika = 30.dakika 'ya atanır. struct Deneme { char karakter. Kurulduktan sonra artık hiç değişmeyecek oldukları durumlarda. } void main() { // Bütün değerlerle auto d1 = Deneme('a'.math. onları const olarak işaretleme olanağı da sağlar: const auto dersBaşı = GününSaati(8. // Son ikisi eksik Copyright © 2009-2011 Ali Çehreli.tamsayı == 1). http://ddili. // ← derleme HATASI dersBaşı. int tamsayı. 30). assert(d2. 2. yapının üyelerine yapı içinde tanımlandıkları sıra ile atanırlar: yapı içinde saat önce tanımlandığı için 8 değeri dersBaşı. assert(d1. double kesirli.3.init değeri ile ilklenirler.org 242 .karakter == 'a'). 48.kesirli == 2. Bu durumda sondaki üyeler yine de otomatik olarak kendi türlerinin . const auto dersSüresi = GününSaati(1. 30 değeri de dersBaşı. assert(d2. 1. dersBaşı.kesirli)). Bunu gösteren aşağıdaki programda Deneme türü gittikçe azalan sayıda parametre ile kuruluyor ve geri kalan parametrelerin de otomatik olarak ilklendikleri assert ifadeleri ile gösteriliyor (programda kullanmak zorunda kaldığım isnan işlevini programdan sonra açıklıyorum): import std.2 Sondaki üyelerin değerleri boş bırakılabilir Yapı nesneleri kurulurken sondaki üyelerin değerleri belirtilmeyebilir. 15).karakter == 'a').3).saat = 8. Yukarıdaki programda ise nesneleri const olarak işaretleyemezdik. assert(isnan(d2.3. auto dersSüresi = GününSaati(1.1 const olarak kurabilme olanağı Nesneleri aynı anda hem tanımlamak hem de değerlerini verebilmek.saat 'e. bu nesnelerin sonraki satırlarda yanlışlıkla değiştirilmeleri böylece önlenmiş olur.Yapılar auto dersBaşı = GününSaati(8. assert(d1. 48. Nesneler bu şekilde kurulurken belirtilen değerler. 30). assert(d1. 1). çünkü ondan sonra üyelerinin değerlerini atamamız mümkün olmazdı: const GününSaati dersBaşı.3).tamsayı == 1). 15).

assert(d3. assert(d5.tamsayı == int. Ancak. Üyelerin rasgele değerlerle kullanılmaları önlenmiş olur. double 'ın ilk değerini bildiren double.init). kendisine verilen değerin nan 'a eşit olup olmadığını bildirir.karakter == 'a').0. Bu sayede örneğin yukarıda gördüğümüz ve hiçbir kullanışlılığı olmayan double. assert(isnan(d3. Yukarıdaki kodun tek amacı.org 243 .%s.%s". assert(isnan(d5. 48. üyeler için hangi değerlerin varsayılacağını belirlemektir. // hiçbir üye değeri belirtilmiyor writefln("%s.nan 'dır. assert(d4. Örneğin char.kesirli == double.25. int tamsayı = 11. d.karakter == char.0 değerini kullanabiliriz. std.kesirli)).math modülünde tanımlanan isnan işlevi.kesirli). O yüzden programda d2. daha sonra nesne oluşturulurken gerekirse kullanılacaktır.init yerine isnan(d2.karakter == char.Yapılar auto d3 = Deneme('a').tamsayı == int.3. } Tür tanımı sırasında kullanılan bu yazım şeklinin bir atama işlemi olmadığına dikkat edin. assert(d3.3 Varsayılan üye değerlerinin belirlenmesi Üyelerin otomatik olarak ilkleniyor olmaları çok yararlı bir olanaktır. Buna rağmen bütün üyeler türün tanımında belirtilmiş olan ilk değerlere sahip olurlar: A.11.25 Copyright © 2009-2011 Ali Çehreli.nan değeri yerine.kesirli) yazmak zorunda kaldım.init değeri geçerli bir karakter bile değildir.init). Örneğin aşağıdaki kullanımda nesnenin hiçbir üyesinin değeri verilmemektedir: Deneme d. assert(d5. // Hiçbirisi yok auto d4 = Deneme(). http://ddili. çoğu zaman çok daha uygun olan 0.karakter.kesirli)).init).init).kesirli)). d. double kesirli = 0. d.init niteliğinin değeri double. Nesne kurulurken değerleri özellikle belirtilmeyen üyeler o varsayılan değerleri alırlar. // Yukarıdakiyle aynı şey Deneme d5. Bu değerler.init). Bu yüzden üyelerin otomatik olarak alacakları değerler programcı tarafından belirlenebilir.init değerini alması her duruma uygun değildir. Üyelerin aldıkları bu özel ilk değerlere varsayılan değer denir ve üye tanımından sonraki atama söz dizimiyle belirlenir: struct Deneme { char karakter = 'A'.tamsayı. assert(isnan(d4.tamsayı == int. } Kesirli sayılar dersinden hatırlayacağınız gibi. nan da kesirli sayılarda "geçersiz değer" anlamına geldiği için eşitlik karşılaştırmalarında bile kullanılamaz. assert(d4. her üyenin kendi türünün .

atandıklarında da yalnızca kendi değerleri değişir. Değerler ve Referanslar dersinde açıklandığı gibi. Bunu biraz aşağıda açıklıyorum. Benzer şekilde. ona erişim sağlayan referans değişir. // ← Önerilen // ← C'deki gibi 48. her yapı nesnesinin kendisine ait değeri olduğunu anlarız. Not: dmd'nin bir hatası nedeniyle. // . Bundan. atama işlemi de bütün üyelerin sırayla atanmaları anlamına gelir.dakika += 5.4 Kopyalama ve Atama Yapılar değer türleridir.4 { } karakterleriyle kurma Yukarıdaki kurma söz dizimi varken kullanmaya gerek olmasa da.. Yapı nesnelerini başka bir söz dizimiyle de kurabilirsiniz: GününSaati dersBaşı = { 8. nesneleri yukarıda gösterdiğim şekilde kurmanızı öneririm: auto dersBaşı = GününSaati(8.1 Referans türünden olan üyelere dikkat! Burada çok önemli bir konuyu hatırlatmak gerekiyor: referans türünden olan değişkenler kopyalandıklarında veya atandıklarında asıl nesne değişmez.3. 30). GününSaati dersSonu = { 9. ve sonuçta asıl nesneye birden fazla referans tarafından erişim sağlanmış olur. Kopyalama sırasında bir nesnenin bütün üyeleri sırayla diğer üyeye kopyalanır. } O türden bir nesnenin başka bir nesnenin değeriyle kurulduğu şu koda bakalım: Copyright © 2009-2011 Ali Çehreli. int[] notlar. bu dersi yazdığım sıralarda değerleri belirtilmeyen üyelerin ilk değerleri atanmıyor. (Not: Bu konuda referans türünden olan üyelere özellikle dikkat etmek gerekir.org 244 . 0). 30 }. // Yalnızca benim yemek saatim 12:05 olur: benimYemekSaatim. Bunun yapı üyeleri açısından önemi. Belirlenen değerler bu kullanımda da üyelere sıra ile atanırlar. Bunu görmek için referans türünden bir üyesi olan bir yapıya bakalım. auto benimYemekSaatim = seninYemekSaatin. Bir öğrencinin numarasını ve notlarını içeren şöyle bir yapı tanımlanmış olsun: struct Öğrenci { int numara. senin yemek saatin değişmez: assert(seninYemekSaatin.) auto seninYemekSaatin = GününSaati(12. ve bu kullanımda da üye sayısından daha az değer verilebilir.dakika == 0). Kurulduklarında kendi değerlerini edinirler.. 30 }. 48.4. http://ddili. bunu da bilmeniz gerekir. iki farklı yapı nesnesinin üyelerinin aynı asıl nesneye erişim sağlıyor olacaklarıdır. İlk değerleri atanmadığı için de üyelerin değerleri belirsizdir ve dikkat edilmezse programda hataya neden olabilir.Yapılar 48. D'ye C++ yoluyla C'den geçen bu söz dizimi yerine.

. ikinci öğrencinin notu bu sefer değişmez: writeln(öğrenci2. üyeleri sırayla öğrenci1 'in üyelerinin değerlerini alır. // İkinci öğrenciyi birincinin kopyası olarak kuralım .notlar[0] += 5.notlar[0] += 5.. yapı nesnelerini de isimleri olmayan hazır değerler olarak kullanabiliriz.org 245 . Yapı hazır değerlerini oluşturmak için yine kurma söz dizimi kullanılır ve yapı nesnesi gereken her yerde kullanılabilir..dup niteliği ile kopyalandığı için bu sefer her nesnenin ayrı notları olur. Dizilerin . GününSaati(8. 90. aslında aynı asıl dizinin elemanlarına erişim sağlarlar. dizi dilimleri referans türleri olduklarından.notlar.. 48. iki öğrenci nesnesinin aynı asıl notları paylaştıklarını gösterir: 75 Belki de burada hiç kopyalama işlemini kullanmadan. // İlk öğrencinin notunda bir değişiklik yaptığımızda . bir nesnenin notlar üyesinde yapılan değişiklik diğerini de etkiler. öğrenci1. // İlk öğrencinin notunda bir değişiklik yaptığımızda . Şimdiki çıktı. 85 ]). ve sonra numarasını değiştirelim: öğrenci2. ikinci nesneyi kendi numarasıyla ve birincinin notlarının kopyasıyla kurmak daha doğru olur: // İkinci öğrenciyi birincinin notlarının kopyası ile // kuruyoruz auto öğrenci2 = Öğrenci(2.. ikinci öğrencinin notunun etkilenmediğini gösterir: 70 Not: İstenen durumlarda referans türünden üyelerin otomatik olarak kopyalanmaları da sağlanabilir. // .. 30) // ← hazır değer Yukarıdaki main işlevini şimdiye kadar öğrendiklerimizi kullanarak şöyle yazabiliriz: Copyright © 2009-2011 Ali Çehreli. her ne kadar notlar üyeleri bağımsız olsalar da. Yukarıdaki kodun çıktısı. http://ddili.5 Yapı hazır değerleri Nasıl 10 gibi hazır değerleri hiç değişken tanımlamak zorunda kalmadan işlemlerde kullanabiliyorsak. auto öğrenci2 = öğrenci1. // . öğrenci2 nesnesi kurulduğu zaman... Ancak... her iki Öğrenci nesnesinin ayrı numara değeri olur.Yapılar // Birinci öğrenciyi kuralım: auto öğrenci1 = Öğrenci(1. Her iki Öğrenci nesnesinin birbirlerinden bağımsız olan notlar üyeleri de vardır.notlar[0]). Sonuçta. Bunu daha sonra yapı işlevlerini anlatırken göstereceğim.. int bir değer türü olduğu için. öğrenci1..dup). // . [ 70. öğrenci1.notlar[0]).numara = 2. ikinci öğrencinin notunun da değiştiğini görürüz: writeln(öğrenci2.

dersSonu. o yapı türü için akılda tutulması gereken genel bir bilgi bulunduğunda gerekebilir. } Dikkat ederseniz. Bu. } Her nesneye farklı bir numara verebilmek için sonrakiNumara gibi bir değişken barındırmak. http://ddili.Yapılar void main() { const auto dersBaşı = GününSaati(8.saat. dersSonu. const auto dersSüresi = GününSaati(1. Bütün nesnelerin tek bir üyeyi paylaşmalarının bir örneği olarak. sütun). ++sonrakiNumara. const GününSaati dersSonu = zamanEkle(dersBaşı. o programda dersBaşı ve dersSüresi nesnelerinin açıkça belirtilmelerine gerek yoktur.org 246 . 30).dakika).dakika). Copyright © 2009-2011 Ali Çehreli. int satır. } 48. 15)).saat. dersSüresi). writefln("Ders sonu: %s:%s". her bir nesne için farklı bir tanıtıcı numara olduğu bir durum düşünelim: struct Nokta { // Her nesnenin kendi tanıtıcı numarası int numara. satır.6 static üyeler Çoğu durumda her yapı nesnesinin kendi üyelerine sahip olmasını isteriz. O nesneleri açıkça tanımlamak yerine. 15). GününSaati(1. Burada karar verilmesi gereken şey. her nesnenin oluşturulması sırasında ortak olarak kullanılan sonrakiNumara bilgisinin nerede tanımlanacağıdır. zamanEkle işlevine şu şekilde hazır değer olarak da gönderebiliriz: void main() { const GününSaati dersSonu = zamanEkle(GününSaati(8. Bazı durumlarda ise belirli bir yapı türünden olan bütün nesnelerin tek bir değişkeni paylaşmaları uygun olabilir. int sütun) { int numara = sonrakiNumara. 30). } return Nokta(numara. Onlar yalnızca dersSonu nesnesini hesaplamak için kullanılan aslında geçici nesnelerdir. dersSonu. dersSonu. int sütun. static üyeler işte bu gibi durumlarda kullanışlıdırlar. ve her nesne için o sayıyı bir arttırmak gerekir: Nokta NoktaOluştur(int satır. writefln("Ders sonu: %s:%s".

♡. Kağıt değeri olarak da bir int veya bir dchar üye kullanabilirsiniz. kız ve papaz için düşünebilirsiniz. doğrudan ♠. void main() { auto üstteki = NoktaOluştur(7. vale. writeln(üstteki.7 Problemler 1. } return Nokta(numara.sonrakiNumara. auto alttaki = NoktaOluştur(9.sonrakiNumara. satır. ♢.numara). o üyeye türün ismi kullanılarak da erişilebilir: ++alttaki. int sütun. int sütun) { int numara = Nokta. } sonrakiNumara her seferinde bir arttırıldığı için her nesnenin farklı numarası olur: 0 1 2 static üyeler bütün türe ait olduklarından.org 247 . ++Nokta.numara). http://ddili.numara). Yukarıda olduğu gibi. Daha başka çözümler de bulunabilir.Yapılar O bilgi bir yapı üyesi olarak tanımlanır ve static olarak işaretlenir.sonrakiNumara. 0). Tek bir oyun kağıdını temsil eden ve ismi OyunKağıdı olan bir yapı tasarlayın. Örneğin kağıt değerini de bir enum olarak tanımlayabilirsiniz.sonrakiNumara. 0). writeln(ortadaki. sütun). ++Nokta. auto ortadaki = NoktaOluştur(8. Copyright © 2009-2011 Ali Çehreli. int seçerseniz 1'den 13'e kadar değerlerden belki de 1. ve ♣ karakterlerini de kullanabilirsiniz. Diğer üyelerin aksine. böyle üyelerden yalnızca bir adet oluşturulur: import std.stdio. struct Nokta { // Her nesnenin kendi tanıtıcı numarası int numara. 12. writeln(alttaki. 11. onlara erişmek için bir nesne bulunması gerekmez. Bu yapının kağıt rengi ve kağıt değeri için iki üyesi olduğu düşünülebilir. // Bundan sonra oluşturulacak olan nesnenin numarası static int sonrakiNumara. } Nokta NoktaOluştur(int satır. int satır. // üst satırın eşdeğeri 48. ve 13 değerlerini sırasıyla as. Renk için bir enum değer kullanabileceğiniz gibi. 0).

Eğer destedeki her kağıt gerçekten farklı olmuşsa. write(' '). } Örneğin sinek ikiliyi çıktıya şu şekilde yazdırsın: ♣2 Kupa asını da şu şekilde: ♡A O işlevin içeriği. doğal olarak yapıyı nasıl tasarladığınıza bağlı olacaktır.length == 52). 2. İsmi yeniDeste olan bir işlev yazın.. } body { // . oyunKağıdıYazdır(kağıt). şuna benzer bir çıktı olmalıdır: Copyright © 2009-2011 Ali Çehreli. } Bu işlev örneğin şöyle kullanılabilsin: void main() { OyunKağıdı[] deste = yeniDeste().. şöyle kurulabilirler: auto kağıt = OyunKağıdı('♣'..org 248 .Yapılar Bu yapının nesnelerinin nasıl kurulacakları.. Elli iki farklı oyun kağıdını temsil eden OyunKağıdı [] türünde bir dizi döndürsün: OyunKağıdı[] yeniDeste() out (sonuç) { assert(sonuç. üyeler için seçtiğiniz türlere bağlı olacak... foreach (kağıt... 3.. deste) { oyunKağıdıYazdır(kağıt). } } writeln(). */). burasını siz yazın . burasını siz yazın . Bir OyunKağıdı nesnesi alan ve o nesneyi çıkışa yazdıran oyunKağıdıYazdır isminde bir işlev tanımlayın: void oyunKağıdıYazdır(in OyunKağıdı kağıt) { // . Örneğin eğer her iki üyeyi de dchar türünde tasarladıysanız. '2').. } void main() { auto kağıt = OyunKağıdı(/* . http://ddili.

. } } writeln().random modülünde tanımlı olan uniform işlevini kullanarak rasgele seçtiği iki kağıdın yerini değiştirsin. çözümler Copyright © 2009-2011 Ali Çehreli. değişTokuşAdedi ile belirtilen değer kadar değiş tokuş işlemi gerçekleştirsin. deste) { oyunKağıdıYazdır(kağıt). std..Yapılar ♠2 ♡5 ♢8 ♣J ♠3 ♡6 ♢9 ♣Q ♠4 ♡7 ♢0 ♣K ♠5 ♠6 ♠7 ♠8 ♠9 ♠0 ♠J ♠Q ♠K ♠A ♡2 ♡3 ♡4 ♡8 ♡9 ♡0 ♡J ♡Q ♡K ♡A ♢2 ♢3 ♢4 ♢5 ♢6 ♢7 ♢J ♢Q ♢K ♢A ♣2 ♣3 ♣4 ♣5 ♣6 ♣7 ♣8 ♣9 ♣0 ♣A 4. burasını siz yazın } Şu şekilde çağrılabilsin: void main() { OyunKağıdı[] deste = yeniDeste().. 1).org 249 . Desteyi karıştıran bir işlev yazın. . in int değişTokuşAdedi) { // . http://ddili. karıştır(deste. Örneğin 1 ile çağrıldığında şuna benzer bir çıktı versin: ♠2 ♡5 ♢8 ♣J ♠3 ♡6 ♢9 ♣Q ♠4 ♡7 ♢0 ♣K ♠5 ♠6 ♠7 ♠8 ♠9 ♠0 ♠J ♠Q ♠K ♠A ♡2 ♡3 ♡4 ♡8 ♣4 ♡0 ♡J ♡Q ♡K ♡A ♢2 ♢3 ♢4 ♢5 ♢6 ♢7 ♢J ♢Q ♢K ♢A ♣2 ♣3 ♡9 ♣5 ♣6 ♣7 ♣8 ♣9 ♣0 ♣A değişTokuşAdedi olarak daha yüksek bir değer verdiğinizde deste iyice karışmış olmalıdır: karıştır(deste.. Bu işlemi kaç kere tekrarlayacağını da parametre olarak alsın: void karıştır(OyunKağıdı[] deste. ♠4 ♡J ♠8 ♠K ♣7 ♣Q ♡4 ♣9 ♢9 ♠Q ♣J ♠0 ♢6 ♡2 ♠6 ♣6 ♢A ♣5 ♢8 ♢3 ♡Q ♢J ♣K ♣8 ♣4 ♠9 ♢0 ♡A ♠A ♡9 ♠7 ♡3 ♢K ♢2 ♡0 ♠J ♢7 ♡7 ♢4 ♣0 ♡6 ♢5 ♡5 ♡K ♠3 ♢Q ♠2 ♠5 ♣2 ♡8 ♣A ♣3 Not: Deste karıştırmak için daha etkin bir yöntemi çözüm programında açıklıyorum. 100). write(' '). foreach (kağıt.

Yazdırdığı eşleme tablosunun hem indeks türü hem de değer türü string olsun. " gelecek şekilde şöyle çağrılabilir: string[string] sözlük = [ "mavi":"blue". in string[string] tablo. kırmızı:red. "kırmızı":"red". Bu. indeksler. tabloYazdır("Türkçe'den İngilizce'ye Renkler". indekslerle değerler arasına ":" . O işlev. http://ddili. elemanlar arasına da ".Parametre Serbestliği 49 Parametre Serbestliği Bu bölümde İşlev Parametreleri dersinde anlatılanlarla doğrudan ilgili olan ve işlev çağırma konusunda bazı serbestlikler sağlayan iki olanağı göstereceğim: • Varsayılan parametre değerleri • Belirsiz sayıda parametreler 49. yapı üyelerinin varsayılan değerlerinin belirlenebilmesine benzer.keys. başlık. sözlük. ". parametrelere varsayılan değerler atanabilmesidir. ve çoğu durumda hep aynı ayraçların kullanıldıklarını varsayalım. indeksler) { // İlk elemandan önce ayraç olmamalı if (sayaç != 0) { write(elemanAyracı). bir eşleme tablosunu çıkışa yazdıran bir işlev düşünelim. indeks.org 250 . çıktıda kullanacağı ayraç karakterlerini de parametre olarak alacak şekilde esnek tasarlanmış olsun: void tabloYazdır(in char[] başlık. indeksAyracı.1 Varsayılan parametre değerleri İşlevlerle ilgili bir kolaylık.Türkçe'den İngilizce'ye Renkler -gri:gray.". mavi:blue Aynı programda başka tabloların da yazdırıldıklarını. o değerler varsayılan değer olarak belirtilebilirler: Copyright © 2009-2011 Ali Çehreli. Örnek olarak. " --"). -.sort. Parametre değerlerinin çoğunlukla aynı değeri aldıkları durumlarda. in char[] indeksAyracı. const char[][] indeksler = tablo. } } } write(indeks. "gri":"gray" ]. Bazı işlevlerin bazı parametreleri çoğu durumda hep aynı değerle çağrılıyor olabilirler. in char[] elemanAyracı) { writeln("-. ":". foreach (sayaç. Bu işlev. "). writeln(). tablo[indeks]). Yalnızca bazı özel durumlarda farklı ayraçlar kullanılıyor olsun.

Örneğin yukarıdaki tabloYazdır işlevi her zaman için dört adet parametre alır. Normalin dışında değer kullanılacağı durumlarda işlev çağrılırken o parametreler için yine de özel değerler verilebilir. -.Türkçe'den İngilizce'ye Renkler -gri=gray kırmızı=red mavi=blue Varsayılan değerler yalnızca parametre listesinin son tarafındaki parametreler için belirtilebilir. char[] elemanAyracı= ". /* ← ayraçlar belirtilmemiş.. 49. 9.Türkçe'den İngilizce'ye Renkler -gri=gray. sözlük). Gerekiyorsa yalnızca ilki: tabloYazdır("Türkçe'den İngilizce'ye Renkler". "="). Örneğin writefln 'i hiçbir kısıtlamayla karşılaşmadan sınırsız sayıda parametre ile çağırabiliyorduk: writeln( "merhaba". belirtilmeyen parametrelerin varsayılan değerleri kullanılır. D'de belirsiz sayıda parametre kullanmanın üç yolu vardır: Copyright © 2009-2011 Ali Çehreli. işlev çağrısı sırasında belirtilmeyebilirler: tabloYazdır("Türkçe'den İngilizce'ye Renkler". D'nin başka bir olanağı. mavi=blue Veya gerekiyorsa her ikisi birden: tabloYazdır("Türkçe'den İngilizce'ye Renkler". 7. sözlük. Bu olanağı aslında daha önce de çok kere kullandık. kırmızı=red. Baştaki veya aradaki parametrelerin varsayılan değerleri belirtilemez. ve istediğimiz kadar * daha parametre */). string[string] tablo. işlevin aslında kaç tane parametre aldığını değiştirmez. -. } char[] başlık. "dünya". "=". http://ddili.org 251 . sözlük.2 Belirsiz sayıda parametreler Varsayılan parametre değerleri. işlevleri belirsiz sayıda parametre ile çağırabilmemizi sağlar. "\n")..Parametre Serbestliği void tabloYazdır(in in in in { // . ve işini yaparken o dört parametreyi kullanır.8 /*. char[] indeksAyracı= ":". ") Varsayılan değerleri olan parametreler. * varsayılan değerlerini alırlar */ O durumda.

) { double sonuç = 0. karakterleri. O işlevi çağırırken ilk iki parametre mutlaka belirtilmelidir: parantezle("{").. her ne kadar sözcük sayısını serbest bıraksa da. http://ddili. sonuç ~= kapama. karakterleri yazılır: double topla(in double[] sayılar .3)).. sözcükler) { sonuç ~= açma. O şekilde tanımlanan topla . Kesinlikle belirtilmesi gereken parametreler parametre listesinde baş tarafa yazılırlar. ne tür parantezler kullanılacağının belirtilmesini şart koşsun. ama bunun yanında güvenli olan D olanağı. Örneğin belirsiz sayıdaki sözcüğü parantezler arasında yazdıran bir işlev düşünelim. Bu işlev.. topla 'nın sayılar parametresi o beş sayıyı içerir. sonuç ~= sözcük.) // ← hiç belirtilmeyebilir { char[] sonuç.Parametre Serbestliği • C dilindeki gibi işleyen ve _argptr anahtar sözcüğünü kullanan düzenek: güvensiz olan bu olanağı burada anlatmayacağım • _arguments ve _typeinfo anahtar sözcüklerinden yararlanan ve writefln 'in de kullandığı olanak. hem de aynı nedenden ötürü güvensiz olabilen bu olanağı da burada anlatmayacağım • Belirsiz parametrelerin hep aynı türden olmalarını gerektiren.1... belirsiz sayıdaki parametreleri o tür işlevlere bir dizi halinde sunar. // ← işlev çağrılırken belirtilmelidir in char[][] sözcükler . foreach (sözcük. aşağıda bunu anlatıyorum D. } } return sonuç. işlevin çağrıldığı sırada kullanılan parametreleri temsil ederler.. Onu istediğimiz sayıda double ile çağırabiliriz: writeln(topla(1.2. Böyle işlevlerin şart koştukları parametreleri de bulunabilir.0.. 3. foreach (sayı. Parametre listesindeki dizi ve ondan sonra gelen . belirsiz sayıda parametre alan bir işlev haline gelmiş olur. Belirsiz sayıdaki parametreyi temsil eden dizi ise en sona yazılır: char[] parantezle( in char[] açma. sayılar) { sonuç += sayı.org 252 . topla işlevi örneğin 5 double parametre ile çağrıldığında. // ← derleme HATASI Copyright © 2009-2011 Ali Çehreli. hem henüz öğrenmediğimiz göstergeleri kullandığı için. } } return sonuç. 2.. // ← işlev çağrılırken belirtilmelidir in char[] kapama. Belirsiz sayıda parametre alacak olan işlevin parametresi olarak bir dizi belirtilir ve hemen arkasından .

Bölme.8))). 7. Bölme } O işlem çeşidini ve işlemde kullanılacak iki kesirli sayıyı içeren bir de yapı olsun: struct Hesap { İşlem işlem.7.-1. Çıkarma. http://ddili. hesapla 'nın işlem sonuçlarını yerleştirdiği dizi writefln tarafından çıktıya şöyle yazdırılacaktır: [3.4). 2.7'nin 8. "elma".1.Toplama. "armut". } Örneğin Hesap(İşlem.Çarpma.2).36. Bu yapı nesnelerinden belirsiz sayıda parametre alan. Çarpma.1. 4.6). Bu işlev örneğin şöyle çağrılabilsin: void main() { writeln(hesapla(Hesap(İşlem. Hesap(İşlem.3 Problem • Daha önce gördüğümüz şu enum türünün tanımlı olduğunu varsayın: enum İşlem { Toplama.Bölme. {elma}{armut}{muz} 49.8'e bölüneceği anlamına gelsin. 8. Buna uygun olarak açma ve kapama parantezlerini kullanan bir örnek: writeln(parantezle("{".Çıkarma. Hesap(İşlem. 8. 3. ve bütün sonuçları bir double dizisi olarak döndüren hesapla isminde bir işlev yazın.5. 1.8) nesnesi.3.. "muz")).7. çözüm Copyright © 2009-2011 Ali Çehreli.875] . her birisini teker teker hesaplayan. geri kalan parametreler konusunda serbestlik vardır.3. 7.org 253 . } Yukarıdaki gibi kullanıldığında.3.Parametre Serbestliği Kesinlikle belirtilmeleri gereken baştaki parametreler verildiği sürece. 6. "}".. double birinci. double ikinci. Hesap(İşlem. 5. 7.0.

2). } // ← derleme HATASI Not: Normalde aynı işi yapan böyle iki işlevin yazılması gereksizdir.stdio. sayı). Copyright © 2009-2011 Ali Çehreli. Bu kullanımda "yükleme" sözcüğünün "aynı isme yeni görev yükleme" anlamına geldiğini düşünebilirsiniz. çünkü double değerler de zaten kayıpsız olarak real 'e dönüşürler. void bilgiVer(in double sayı) { writeln("Kesirli sayı: ". } void main() { bilgiVer(1. } void main() { int sayı = 5. http://ddili.2 hazır değerinin türü double olduğu için. onun kullanıldığı durumda o işlevler arasından double parametre alanı çağrılır. derleyici parametrenin türüne uygun olan işlevi seçer ve onun çağrılmasını sağlar. bilgiVer("merhaba").org 254 . } İşlevlerin hepsinin de ismi bilgiVer olduğu halde. Aşağıda aynı isimde ama parametreleri farklı işlevler görüyorsunuz: import std. auto sonuç = yediKatı(sayı).İşlev Yükleme 50 İşlev Yükleme Aynı isimde birden fazla işlev tanımlamaya işlev yükleme denir. Örneğin 1. Hangi işlevin çağrılacağı derleme zamanında seçilir. Örneğin şu kodda kullanılan int değer hem real hem de double türüne uyduğu için derleyici hangisini seçeceğine karar veremez: real yediKatı(in real değer) { return 7 * değer. bilgiVer(3). Bu seçim her zaman kolay veya açık olmayabilir. Öte yandan. } void bilgiVer(in int sayı) { writeln("Tamsayı : ". Örneğin bu işlevlerden real parametreli olan yeterlidir. bu işlevlerin long parametre alan bir üçüncüsü tanımlansa. } double yediKatı(in double değer) { return 7 * değer. dizgi). İsimleri aynı olan bu işlevlerin ayırt edilebilmeleri için parametrelerinin birbirlerinden farklı olmaları gerekir. } void bilgiVer(in char[] dizgi) { writeln("Dizgi : ". sayı).

int dakika.org 255 . eğer varsa. yüklenmiş olan işlevlerden hangisini çağıracağına karar vermek için işlevleri gözden geçirir. derleyicinin bir seçim yapmasını gerektirir. Yüklenen işlevler arasından. yapılarda ve sınıflarda çok yararlıdır. Belki de işlev şablonlarını kullanmak daha doğru olacaktır. 50.dakika).. üstelik o türlerde işlev seçimi konusunda uyum sorunları da çok daha azdır.İşlev Yükleme long yediKatı(in long değer) { return 7 * değer. kullanılan parametrelere daha çok uyan işlev seçilir. Eğer birden fazla işlev aynı derecede uymuşsa. Bütün parametreler içindeki en az uyum. } void bilgiVer(in GününSaati zaman) { writef("%02s:%02s". onlardan hangisinin seçileceğine daha da karışık başka kurallar yoluyla karar verilir. http://ddili.2 Yapılar için işlev yükleme İşlev yükleme. zaman. 50. aslında programınızın tasarımında değişiklik yapma zamanı gelmiş demektir. Ben burada bu kuralların daha derinine inmeyeceğim. Program bu yeni işlev çağrılacak şekilde derlenir. Bu şekilde bütün işlevlerin uyum durumları belirlendikten sonra.. Yukarıdaki bilgiVer işlevini Yapılar dersinde kullandığımız bazı türler için yükleyelim: struct GününSaati { int saat.saat. } Copyright © 2009-2011 Ali Çehreli. Hatta belki de aynı isimde işlev tanımlamak yerine. Bu yüzden uyum kuralları geliştirilmiştir. daha açıklayıcı isimler kullanarak hangisini çağırmak istediğinizi açıkça belirtmek bütün karışıklığı ortadan kaldıracaktır: yediKatı_real ve yediKatı_double gibi. Her işlevin parametrelerine teker teker bakar ve her parametrenin yukarıdaki dört uyum durumundan hangisinde olduğunu belirler. bu işlevin de uyumu olarak kabul edilir. ama hangi işlevin daha çok uyduğu konusu bazen çok karışıktır. en çok uyan işlev seçilir. zaman. Parametreler için uyum konusunda dört durum vardır: • • • • uyumsuzluk otomatik tür dönüşümü yoluyla uyum const 'a dönüştürerek uyum tam uyum Derleyici. çünkü eğer bu kadar karışık kurallarla yüz yüze kalmışsanız. } Derleme hatası ortadan kalkar.1 Parametre uyum kuralları Aynı isimde birden fazla işlev bulunması. Bu seçim çoğu durumda kolay ve beklendiği gibi olur. çünkü yüklenen işlev seçimi konusunda int değerler long türüne kesirli türlerden daha uyumludur.

} Gördüğünüz gibi. Oysa örneğin File türünden bir dosyaya da yazabiliyor olsa. in GününSaati zaman) { akım. bu yöntemin bazı eksiklikleri vardır: • bilgiVer işlevi yalnızca stdout 'a yazdığı için fazla kullanışlı değildir.bitiş). bilgiVer(toplantı. toplantı.katılımcıSayısı). bilgiVer 'in GününSaati için yüklenmiş olanı.İşlev Yükleme O tanım sayesinde artık GününSaati nesnelerini de bilgiVer işlevine gönderebiliriz.writef("%02s:%02s". çıktının yazdırılacağı akımı da işleve bir parametre olarak vermektir: void bilgiVer(File akım. write('-'). int katılımcıSayısı. Artık Toplantı nesnelerini de alıştığımız isimdeki işlevle yazdırabiliriz: auto geziToplantısı = Toplantı("Bisikletle gezilecek yerler". artık GününSaati nesneleri de kendilerine özgü çıktı düzenleri ile yazdırılmış olurlar: 07:00 bilgiVer işlevini yapılar dersinde değinilen Toplantı yapısı için de yükleyelim: struct Toplantı { string konu. 09:00-09:10 "Bisikletle gezilecek yerler" toplantısı (3 katılımcı) 50. 0). } O sayede GününSaati nesnelerini istersek stdout 'a. kullanışlılığı artardı.konu.başlangıç). toplantı. istersek de bir dosyaya yazdırabiliriz: Copyright © 2009-2011 Ali Çehreli. bilgiVer(kahvaltıZamanı). zaman. 3. writef(" \"%s\" toplantısı (%s katılımcı)". } void bilgiVer(in Toplantı toplantı) { bilgiVer(toplantı. GününSaati başlangıç. bilgiVer(geziToplantısı). GününSaati(9. Bunu sağlamanın yolu.saat. zaman. 10)). Böylece programımızda her tür nesneyi aynı isimle yazdırabileceğimiz alışılmış bir yöntemimiz olur: auto kahvaltıZamanı = GününSaati(7. GününSaati bitiş. 0). http://ddili.org 256 . Temel türlerde olduğu gibi. GününSaati(9.dakika).3 Eksiklikler Yukarıdaki bilgiVer işlevi her ne kadar kullanım kolaylığı getirse de. Toplantı 'yı yazdıran işlev tarafından kullanılmaktadır.

İşlev Yükleme bilgiVer(stdout.saat + eklenecek.saat %= 24. } Yemek yapısı yalnızca başlangıç zamanını barındırdığı için.saat. Not: stdin . http://ddili.saat = başlangıç.dakika = başlangıç. in GününSaati eklenecek) { GününSaati sonuç. sonuç. "w"). kahvaltıZamanı). Bu işlem için yapılar dersinde tanımladığımız zamanEkle işlevi yararlı olabilir: GününSaati zamanEkle(in GününSaati başlangıç.dakika + eklenecek. sonuç. kahvaltıZamanı). string adres. auto dosya = File("bir_dosya". sonuç. ve stderr nesnelerinin türleri de aslında File 'dır. Yapı nesnelerinin de otomatik olarak string 'e dönüştürülebileceklerini bundan sonraki derste göstereceğim. onun bitiş zamanını başlangıç zamanından bir buçuk saat sonrası olarak belirleyin. } return sonuç.dakika %= 60.dakika. stdout .saat += sonuç. Toplantı akşamToplantısı. sonuç. 50. sonuç. Diğer türlerden alışık olduğumuz rahatlık yoktur: writeln(kahvaltıZamanı). yapı nesnelerini diğer türler kadar rahatça kullanabilmemiz için yeterli değildir. } struct GünlükPlan { Toplantı sabahToplantısı. bilgiVer gibi bir işlev. Yemek öğleYemeği. • Daha önemlisi. // kullanışsız: türün ismini yazar O kod çalıştırıldığında GününSaati türünün ismi yazdırılır: GününSaati Bunun yerine.org 257 . bilgiVer(dosya. yapı nesnesinin değerini örneğin "12:34" biçiminde bir string 'e dönüştürebilen bir işlev olması çok daha yararlı olur.4 Problem • bilgiVer işlevini şu iki yapı için de yükleyin: struct Yemek { GününSaati zaman.dakika / 60. Yemek bitiş zamanları o işlev yardımıyla hesaplanınca GünlükPlan nesneleri çıkışa şuna benzer şekilde yazdırılabilirler: Copyright © 2009-2011 Ali Çehreli.

http://ddili..org 258 ..İşlev Yükleme 10:30-11:45 "Bisiklet gezisi" toplantısı (4 katılımcı) 12:30-14:00 Yemek. çözüm Copyright © 2009-2011 Ali Çehreli. Yer: Taksim 15:30-17:30 "Bütçe" toplantısı (8 katılımcı) .

Bir yapının arayüzünü oluşturan işlevler çok karşılaşılan bir kavram olduğu için. 51. zamanEkle ve bilgiVer işlevlerinin ilk parametresi. Üye işlevlere yapının diğer üyelerinde olduğu gibi. &numara). o işlevler yapının içinde. ve bunların içerisinden özel olarak. tek başlarına. İlk örneğimiz olarak. O satırlardaki readf ve writeln üye işlevlerdir.writeln(numara). işlevin tanımı .saat. Bu derste yapıların ve sınıfların üye işlevlerini tanıyacağız. zaman.1 Üye işlev Bir yapının veya sınıfın küme parantezlerinin içinde tanımlanan işlevlere üye işlev denir: struct BirYapı { void üye_işlev(/* parametreleri */) { // .. nesneleri string türüne dönüştürmede kullanılan toString üye işlevini göreceğiz. nesne isminden sonraki nokta karakteri ve ardından yazılan işlev ismi ile erişilir: nesne.. o yapıyı kullanan bir grup işlev de onunla birlikte tanımlanır. http://ddili.. yapının üyeleri ve diğer işlevleri . } } // .. onlar da bağımsız olarak. Şimdiye kadar tanımladığımız bütün diğer işlevler gibi. O işlevi daha önce serbest olarak şöyle tanımlamıştık: void bilgiVer(in GününSaati zaman) { writef("%02s:%02s". Hatırlarsanız.. buradaki bilgilerin çoğu daha sonra göreceğimiz sınıflar için de geçerlidir. üzerinde işlem yaptıkları nesneyi belirliyordu. Bunun örneklerini önceki derslerde zamanEkle ve bilgiVer işlevlerinde gördük. } Üye işlev olarak yapının içinde tanımlanırken bazı değişiklikler gerekir: Copyright © 2009-2011 Ali Çehreli. GününSaati yapısını yazdıran bilgiVer işlevini bir üye işlev olarak tanımlayalım. yapının üye işlevleri olarak da tanımlanabilirler. Üye işlevleri aslında daha önce de kullandık. stdout. zaman.dakika).. Bir yapının tanımlandığı çoğu durumda.org 259 .readf(" %s".üye_işlev(parametreleri). örneğin standart giriş ve çıkış işlemlerinde stdin ve stdout nesnelerini açıkça yazabiliyorduk: stdin.. ve evrensel isim alanında tanımlanmışlardı.Üye İşlevler 51 Üye İşlevler Bu derste her ne kadar yapıları kullanıyor olsak da. O işlevler bir anlamda GününSaati yapısı ile birlikte sunulan ve o yapının arayüzünü oluşturan işlevlerdir..

http://ddili. sabah. zaman. bilgiVer(zaman). bilgiVer işlevi zaman nesnesini yazdıracak şekilde çağrılmaktadır. writeln(). 0). akşam). saat. 0).bilgiVer(). daha önceden serbest olarak yazılmış olan bilgiVer 'in şu şekilde çağrıldığı durumla eşdeğerdir: zaman. // üye işlev // serbest işlev (önceki tanım) Üye işlev her çağrıldığında. üye işlevlerin zaten her zaman için bir nesne üzerinde çağrılıyor olmalarıdır: auto zaman = GününSaati(10.bilgiVer(). saat ve dakika diye erişir Bunun nedeni.saat ve zaman. daha önce yapı dışında serbest olarak tanımladığımız bilgiVer işlevinden iki farkı vardır: • yazdırdığı nesneyi parametre olarak almaz • o yüzden. void bilgiVer() { writef("%02s:%02s". O üye işlev çağrısı. Üye işlevin tanımı içinde noktasız olarak yazılan saat ve dakika . } } Bu üye işlevin.1 Nesneyi string olarak ifade eden toString Bir önceki derste bilgiVer işlevinin eksikliklerinden söz etmiştim.dakika üyelerini temsil ederler. zaman nesnesinin üyeleridir. auto akşam = GününSaati(22. üzerinde çağrıldığı nesnenin üyelerine erişir: auto sabah = GününSaati(10.1. akşam. 30). Rahatsız edici bir diğer eksikliğini burada göstermek istiyorum: Her ne kadar zamanı okunaklı bir düzende çıktıya gönderiyor olsa da.dakika diye değil. sabah üzerinde çağrıldığında sabah 'ın değerini. nesnelerin diğer türler gibi kullanışlı olabilmeleri için örneğin şu şekilde yazabilmemiz çok yararlı olurdu: writefln("%s-%s".Üye İşlevler struct GününSaati { int saat. genel çıktı düzeni açısından '-' karakterini yazdırmayı ve satırın sonlandırılmasını kendimiz ayrıca halletmek zorunda kalıyoruz. ve sırasıyla zaman.org 260 . int dakika. akşam üzerinde çağrıldığında da akşam 'ın değerini yazdırır: 10:00-22:00 51.saat ve zaman. Orada. üyelere zaman.bilgiVer().bilgiVer(). sabah. dakika). write('-'). Oysa. Copyright © 2009-2011 Ali Çehreli. bilgiVer .

http://ddili. akşam). ürettiği sonucu bir akıma göndermek yerine. sabah. nesneleri string türüne dönüştürmek için kullanır. Bu işlev. string toString() { return "deneme". sabah. Tek farkı. "w"). yapıların toString ismindeki üye işlevlerini. int dakika. } Copyright © 2009-2011 Ali Çehreli. import std. int dakika. bir string olarak döndürmesidir. Bunun doğru olarak çalışabilmesi için. daha önceki 4 satırı böyle tek satıra indirgemiş olmanın yanında. Bu işlevin içeriğini sonraya bırakalım. string toString() { return format("%02s:%02s". 0). Phobos kütüphanesi. } writefln("%s-%s". Bu örnekte henüz anlamlı bir dizgi üretmediğimiz için çıktı da şimdilik anlamsız oluyor: deneme-deneme Ayrıca bilgiVer 'i artık emekliye ayırmakta olduğumuza da dikkat edin.Üye İşlevler Öyle yazabilseydik. o nesneyi ifade eden bir string döndürmelidir. struct GününSaati { int saat. toString 'in de zaten bir string döndürmesi gerektiği için. std. örneğin bir dosyaya da aynı şekilde yazdırabilirdik: auto dosya = File("zaman_bilgisi". } } void main() { auto sabah = GününSaati(10. struct GününSaati { int saat. akşam).stdio. auto akşam = GününSaati(22. Nesneleri dizgi olarak kullanabilen kütüphane işlevleri.org 261 . ve önce yapı içinde nasıl tanımlandığına bakalım: import std.string modülünde tanımlanmış olan format işlevini kullanmaktır.stdio. toString 'in tanımını tamamlayınca ona ihtiyacımız kalmayacak. toString işlevini yazmanın en kolay yolu.string. saat. nesnelerin toString işlevini çağırırlar ve döndürülen dizgiyi kendi amaçlarına uygun bir şekilde kullanırlar. çıktı düzeni için kullandığımız bütün olanaklara sahiptir ve örneğin writef ile aynı şekilde çalışır. nesneleri stdout 'tan başka akımlara da.writefln("%s-%s". dosya. 0). format 'ın döndürdüğü değeri olduğu gibi döndürebilir: import std. ismi "string'e dönüştür"den gelen bu işlev. dakika).

Bu bakış açısı ile. writefln çağrısı tarafından halledilmektedir. writefln . Ama ona geçmeden önce. bir süredir..Üye İşlevler } void main() { auto sabah = GününSaati(10. gün içindeki iki zamanın birbirlerinden çıkartılmaları normal bir işlem olarak görülebilir. Çıktının geri kalanı. aralarına '-' karakterini yerleştirir. GününSaati nesnelerini toplamasının normal bir işlem olmadığını görmüş.dakika += süre. sonuç. } writefln("%s-%s". auto akşam = GününSaati(22. toString 'in yalnızca bu nesneyi string 'e dönüştürdüğüne dikkat edin. 0). // Taşmaları ayarlıyoruz sonuç.dakika / 60. O işlemin sonucu da örneğin Süre türünden olmalıdır.saat %= 24. önceki derslerde yaptığımız bir yanlışı gidermek istiyorum. dakika duyarlığıyla çalışan bir Süre yapısını ve onu kullanan zamanEkle işlevini şöyle yazabiliriz: struct Süre { int dakika. "%s" düzen bilgilerine karşılık olarak toString 'i otomatik olarak iki nesne için ayrı ayrı çağırır.saat += sonuç.. // Süreyi ekliyoruz sonuç. sonuç. in GününSaati eklenecek) { // . http://ddili. Örneğin yola çıkma zamanına yol süresini ekleyerek sinemaya varış zamanını buluruz. } GününSaati zamanEkle(in GününSaati başlangıç.1.dakika %= 60.dakika. Örneğin yola çıkma zamanına sinemaya varma zamanını ekleyemeyiz. sabah. } Gün içindeki iki zamanı birbirine eklemek doğal bir işlem değildir.org 262 . akşam).2 Başka bir örnek: ekle Bu sefer de GününSaati nesnelerine zaman ekleyen bir üye işlev tanımlayalım. 0). Öte yandan. Copyright © 2009-2011 Ali Çehreli. in Süre süre) { // başlangıç'ın kopyasıyla başlıyoruz GününSaati sonuç = başlangıç. ve en sonunda da satırı sonlandırır: 10:00-22:00 51. Gün içindeki bir zamana eklenmesi normal olan. } return sonuç. ama yine de o şekilde kullanmıştık: GününSaati zamanEkle(in GününSaati başlangıç. Yapılar dersinde tanımladığımız zamanEkle işlevinin.

Daha sonraki derslerde göreceğimiz işleç yükleme olanağı sayesinde bu konuda biraz daha kolaylık kazanacağız. } } ekle . string toString() { return format("%02s:%02s". 40)).Üye İşlevler unittest { // Basit bir test assert(zamanEkle(GününSaati(10. saat += dakika / 60. 0)). Üye işlev zaten bir nesne üzerinde çalışacağı için GününSaati parametresine gerek kalmaz ve parametre olarak yalnızca süreyi geçirmek yeter: struct Süre { int dakika. // Basit bir test zaman.ekle(Süre(10)). saat.ekle(Süre(22 * 60 + 20)). // Sonraki güne taşma testi assert(zamanEkle(GününSaati(17. 0)). 45). 45)). Süre(8 * 60)) == GününSaati(1. 30). saat %= 24.dakika. int dakika. } void ekle(in Süre süre) { dakika += süre. assert(zaman == GününSaati(10. Süre(10)) == GününSaati(10. 30). assert(zaman == GününSaati(1. 40)). 9). dakika %= 60. // 22 saat ve 20 dakika sonra gece yarısı olmalı zaman. Örneğin += işlecini yükleyerek yapı nesnelerini de temel türler gibi kullanabileceğiz: zaman += Süre(10). assert(zaman == GününSaati(0. dakika). } struct GününSaati { int saat.ekle(Süre(15 * 60)). // Gece yarısı testi assert(zamanEkle(GününSaati(23. Süre(51)) == GününSaati(0. 40)). } Şimdi aynı işlevi bir üye işlev olarak tanımlayalım. nesnenin zamanını belirtilen süre kadar ilerletir.org 263 . // bunu daha sonra öğreneceğiz Copyright © 2009-2011 Ali Çehreli. } unittest { auto zaman = GününSaati(10. // 15 saat sonra bir sonraki güne taşmalı zaman. http://ddili.

. yapı tanımı . süre azaltıldığında bir önceki güne taşsın.. azalt işlevini şu birim testlerini geçecek şekilde yazın: struct GününSaati { // . Çok daha kullanışlı olmalarının yanında. bloğu bütünüyle yapının dışında da tanımlayabilirsiniz: struct GününSaati { // .. ve GünlükPlan yapıları için toString üye işlevlerini tanımlayın. // 3 gün ve 11 saat önce zaman. .. } } 2. http://ddili. 51. yapı testleri .Üye İşlevler Ayrıca gördüğünüz gibi.. assert(zaman == GününSaati(10. Denetledikleri kodlarla bir arada bulunmaları daha doğal olsa da. O blokların yapı tanımını kalabalıklaştırdığını düşünüyorsanız.. } unittest { auto zaman = GününSaati(10. her birisinin tek satırda yazılabildiğini göreceksiniz.azalt(Süre(23 * 60 + 18))... Daha önce İşlev Yükleme dersinin çözümünde kullanılan diğer bütün bilgiVer işlevlerinin yerine. üye işlevler için de unittest blokları yazılabilir. 59)). onları uygun bulduğunuz başka yerlerde de tanımlayabilirsiniz. nesnelerin değerini Süre kadar azaltan bir üye işlev ekleyin. 18)). Örneğin 00:05'ten 10 dakika azaltınca 23:55 olsun. // 23 saat ve 18 dakika önce gece yarısı olmalı zaman. // 1 dakika öncesi zaman.azalt(Süre(3 * 24 * 60 + 11 * 60))... Başka bir deyişle. 18)).azalt(Süre(1)).. void azalt(in Süre süre) { // . Toplantı .org 264 . burasını siz yazın . // Basit bir test zaman. Yemek . 0)). 30). ekle işlevinde olduğu gibi. assert(zaman == GününSaati(23..azalt(Süre(12)).2 Problemler 1. } Bunun nedeni. unittest bloklarının aslında belirli bir noktada tanımlanmalarının gerekmemesidir. assert(zaman == GününSaati(0. assert(zaman == GününSaati(23. çözümler Copyright © 2009-2011 Ali Çehreli. } unittest { // ... GününSaati yapısına...

immutable nesneler programda kesinlikle değişmeyen nesnelerdir. O parametreyi değiştirmiyor bile olsalar. veya belirli bir bağlamda değişmeyen nesnelerdir. } 52. okumaSaati.const ref Parametreler ve const Üye İşlevler 52 const ref Parametreler ve const Üye İşlevler Bu derste üye işlevlerin const nesnelerle de kullanılabilmeleri için nasıl const olarak işaretlenmeleri gerektiğini göreceğiz. veya herhangi başka bir değişiklik yapılmasına izin vermez.2 const ref parametreler const ve immutable dersinde gördüğümüz gibi. const nesneler ise belirli bir referans yoluyla. Bunun bir örneği.org 265 . Burada her ne kadar yapıları kullanıyor olsak da. 52.dakika. parametrelerdir. bunun garantisini vermedikleri için o işlevlere const nesne gönderilemez: // süre'yi değiştirmiyor olsa bile const olarak işaretlememiş int toplamSaniye(ref Süre süre) { Copyright © 2009-2011 Ali Çehreli. } O işlev. Zaten const olarak işaretlemenin amacı da budur: bazı nesnelerin değerlerinin değişmeyeceği garantisi gerekebilir. const ref olarak işaretlenen bir parametre.. o işlev içinde değiştirilmeyecek demektir: int toplamSaniye(const ref Süre süre) { return 60 * süre. bir üyesinin değiştirilmesine. // ← derleme HATASI // .dakika = 7. okumaSaati değiştirilemez: okumaSaati = GününSaati(16. parametresini const olarak işaretleyerek o parametrede değişiklik yapmayacağı garantisini vermiş olur. Kendisi aslında const olmayan bir nesne. 0). belirli bir bağlamda değiştirilmeden kullanılabilir. const üye işlevler sınıflar için de geçerlidir. http://ddili. 0).dakika += 10. // ← derleme HATASI // ← derleme HATASI Derleyici. const nesneye yeni bir değer atanmasına. Derleyici de o nesnenin değiştirilmesine izin vermez: int toplamSaniye(const ref Süre süre) { süre.1 const nesneler Şimdiye kadarki derslerde bazı örneklerde const değişkenler ve nesneler tanımlamış ve const anahtar sözcüğünün nesnelerin değiştirilemez olmalarını sağladığını görmüştük: const auto okumaSaati = GününSaati(15.. 52.3 const olmayan ref parametreler ref parametrelerin işlev içinde değiştirilmemeleri yönünde bir kısıtlama yoktur.

const ref Parametreler ve const Üye İşlevler return 60 * süre. saat += dakika / 60. const olan ısınmaSüresi nesnesinin toplamSaniye işlevine gönderilmesine izin vermez.. başlangıç. 30).ekle işlevi.046) bir hatası yüzünden derlenebiliyor. } // . Üye işlevlerin nesnede bir değişiklik yapmayacakları garantisi. üzerinde çağrıldıkları nesnede değişiklik yapmazlar: struct GününSaati { // . dakika).... nesneyi string olarak ifade etmektir.dakika. // başlangıç değişir 52..ekle(Süre(30)). string toString() { return format("%02s:%02s".. auto başlangıç = GününSaati(5. parametre listesinden sonra yazılan const sözcüğü ile belirtilir: struct GününSaati { // .. O hata giderildiğinde yukarıdaki kod derleme hatasına neden olacaktır.. Derleyici.. parametresinde değişiklik yapmayacağı garantisini vermemektedir. saat.. bu dersi yazdığım sırada kullandığım derleyicinin (dmd 2. string toString() const { Copyright © 2009-2011 Ali Çehreli. toplamSaniye(ısınmaSüresi). Örneğin GününSaati.4 const olmayan üye işlevler Nesneleri değiştirmenin başka bir yolu.org 266 . http://ddili. } // .dakika. const Süre ısınmaSüresi = Süre(3). üye işlevlerdir. saat %= 24.. nesnenin kendisinde bir değişiklik yapmaz.5 const üye işlevler Bazı üye işlevler. 52.. } // . } toString 'in işi.. çünkü toplamSaniye işlevi.. üzerinde çağrıldığı nesneyi ona bir Süre ekleyerek değiştiriyordu: struct GününSaati { // . void ekle(in Süre süre) { dakika += süre. // ← derleme HATASI Not: Yukarıdaki kod. dakika %= 60. } // .

saat. O const . yukarıda gösterdiğim gibi parametre listesinden sonra yazmayı yeğliyorum. dakika). 52. saat. } } // .. dakika). Not: İşlevin nesnede değişiklik yapmayacağını garanti eden bu const anahtar sözcüğü aslında işlevin tanımından önce de yazılabilir: // üsttekiyle aynı şey const string toString() { return format("%02s:%02s". Böylece derleyici toString 'i const nesnelerle de çağırabilir.. http://ddili.. saat.org 267 . 30). string toString() const { return format("%02s:%02s".6 Ne zaman kullanmalı toString gibi. } Dönüş türüyle karışma riski olduğu için ben const anahtar sözcüğünü bu şekilde değil. const nesneleri yazdıramamak gibi yapay bir kısıtlamayla karşı karşıya kalınır: struct GününSaati { // ... Bundan sonraki derslerdeki kodlar da buna uygun olarak tasarlanacaklar. dakika).const ref Parametreler ve const Üye İşlevler } } return format("%02s:%02s". // const olarak işaretlenmemiş (yanlış tasarım) string toString() { return format("%02s:%02s". // ← derleme HATASI Şimdiye kadarki derslerde gördüğümüz toString üye işlevlerinin bu kısıtlama yüzünden yanlış tasarlandıklarını söyleyebiliriz. nesnenin o işlev içinde değiştirilmeyeceği garantisini sağlar.toString().. dakika). nesnede değişiklik yapmayan üye işlevleri her zaman için const olarak işaretleyin: struct GününSaati { // . Çünkü toString const olarak işaretlenmemiş olsa. const auto başlangıç = GününSaati(5. saat. string dizgiOlarak = başlangıç. } } Böylece yapının ve sınıfın kullanışlılığı gereksizce kısıtlanmamış olur. Copyright © 2009-2011 Ali Çehreli.

Örneğin yukarıdaki 8 ve 30 değerleri GününSaati kurucu işlevinin parametreleridir. Yine de..Kurucu ve Diğer Özel İşlevler 53 Kurucu ve Diğer Özel İşlevler Bu derste her ne kadar yapıları kullanıyor olsak da. bir sınıf nesnesi kurulurken yine sağ tarafta: auto değişken = new BirSınıf. yapı nesnesini kullanıma hazırlamak için gereken bilgilerden oluşur.1 Kurucu işlev Kurucu işlevin asıl görevi.org 268 . Örneğin şu satırın sağ tarafında: auto dersBaşı = GününSaati(8. Çünkü o işlemler zaten derleyici tarafından otomatik olarak halledilirler. 53. "Bu" anlamına gelen "this"in "bu türden nesne kuran işlev" sözünden geldiğini düşünebilirsiniz: struct BirYapı { // .. Kurucu işlevin ismi this olmak zorundadır. kurucu işlevlerin dönüş değerleri yoktur. Yapıların üye işlevleri arasından dört tanesi. Parametre listesi yapılarda boş olamaz: Copyright © 2009-2011 Ali Çehreli.. Bu dört temel işlemin normalde yapılar için özel olarak tanımlanmaları gerekmez. nesnelerin temel işlemlerini belirledikleri için ayrıca önemlidir: • • • • kurucu işlev this sonlandırıcı işlev ~this kopya sonrasını belirleyen this(this) atama işleci opAssign Nesnelerin bu temel işlemlerini de kendimiz belirleyebiliriz.. 53. bazı durumlarda bu işlevleri kendimiz tanımlamak isteyebiliriz. Sınıflardaki farklılıklarını daha sonraki derslerde göstereceğim. this(/* kurucu parametreleri */) { // . http://ddili. Tür ismi işlev çağrısı gibi kullanılırken parantez içinde yazılanlar da kurucu işlevin parametreleri olurlar. nesneyi kuran işlemler .1 Söz dizimi Diğer işlevlerden farklı olarak.. Benzer şekilde. Kurucu işlevleri şimdiye kadar hem bütün yapı örneklerinde hem de örneğin File gibi başka türlerde gördük. bu temel işlemler daha sonra göreceğimiz sınıflar için de geçerlidir. } } Kurucu parametreleri.. ve dönüş türü olarak void bile yazılmaz. bir nesnenin üyelerine gerekli değerleri atayarak onu kullanılabilir hale getirmektir. Türün ismini bir işlev çağrısı gibi kullandığımız noktalarda o türün kurucu işlevi çağrılır.1. 30).

1. parametre değerlerini sırayla üyelere atamaktır.init. http://ddili. O kurucunun işi..this default constructor not allowed for structs Bunun sınıflar için mümkün olduğunu ileride göreceğiz.2 Derleyici tarafından sağlanan otomatik kurucu işlev Şimdiye kadar gördüğümüz bütün yapı örneklerinde derleyici tarafından sağlanan otomatik kurucu işlevi kullandık.3 Üyelere this. 53. int tamsayı.BirYapı.1. } } Derleyici. Bütün üyeler için geçerli değerlerin parametre olarak verilmesi. ile erişim Yukarıdaki kodda parametrelerle üyeler karışmasınlar diye parametrelerin sonlarına _parametre diye bir belirteç ekledim. Değerleri belirtilmeyen üyeler. derleyicinin sağladığı otomatik kurucu işlevin şu şekilde yazılmış olduğunu düşünebiliriz: struct Deneme { char karakter. Parametre Serbestliği dersinde gösterilen varsayılan parametre değerleri olanağını da hatırlarsak. kesirli = kesirli_parametre.. varsayılan kurucunun yapılar için tanımlanamayacağını bildiren bir hata mesajı vererek sonlanır: Error: constructor deneme. in double kesirli_parametre = double. Çünkü parametrelerin isimlerini de üyelerle aynı yapsaydım kod hatalı olurdu: struct Deneme { char karakter.org 269 . kendi türlerinin .init) { karakter = karakter_parametre. nesnelerin kurulmuş olmaları için yeterlidir. /* Derleyicinin sağladığı kurucu işlevin eşdeğeri */ this(in char karakter_parametre = char. in int tamsayı_parametre = int.Kurucu ve Diğer Özel İşlevler struct BirYapı { this() // ← derleme HATASI { // . tamsayı = tamsayı_parametre. Ayrıca Yapılar dersinden hatırlayacağınız gibi. double kesirli.init. parametre listesinde sonda bulunan parametreler için değer belirtilmesi gerekmez. int tamsayı.init değerlerini alırlar. Copyright © 2009-2011 Ali Çehreli. double kesirli. } } Eğer çoğu yapıda olduğu gibi o kadarı yeterliyse. 53. bizim ayrıca kurucu işlev tanımlamamız gerekmez.

in int tamsayı = int. 53.this. Örnek olarak önceki derste kullandığımız Süre yapısına bakalım: struct Süre { int dakika. in int tamsayı = int.init) { this. parametrelerin isimlerinin sonlarına artık _parametre gibi ekler yazmak gerekmez: this(in char karakter = char. } Tek bir tamsayı üyesi bulunan bu yapı için derleyicinin sağladığı kurucu çoğu durumda yeterlidir: zaman. Yukarıda da belirttiğim gibi. // ← derleme HATASI tamsayı = tamsayı. } karakter yazıldığında parametre. Copyright © 2009-2011 Ali Çehreli.init. bu nesne anlamına gelir. Bu kurucu.init. this. bu kurucuyu yazmanız gerekmez. } İşlev içinde karakter yazıldığında üye değil.tamsayı = tamsayı.karakter = karakter. this.init.kesirli = kesirli.4 Programcı tarafından tanımlanan kurucu işlev Bazen nesnenin kurulabilmesi için üyelere sırayla değer atamaktan daha karmaşık işlemler gerekebilir. . bazı durumlarda programcıların hesaplar yapmaları gerekebilir: // 23 saat ve 18 dakika öncesi zaman. Parametreler de giriş parametresi oldukları için in olarak işaretlendiklerinden sabit değerin değiştirilemeyeceğini bildiren derleme hatası alırız: Error: variable deneme. derleyicinin otomatik olarak yazdığı kurucu işlevin perde arkasında nasıl çalıştığını göstermek için anlattım. Bu olanağı kullanınca.azalt(Süre(12)). ve kod artık istediğimizi yapacak şekilde derlenir ve çalışır.karakter cannot modify const Bu konuda bir çözüm olarak this.org 270 .azalt(Süre(23 * 60 + 18)).init) { // parametreyi kendisine atıyor! karakter = karakter. eğer yapının kurulması için bu kadarı yeterliyse. kesirli = kesirli.Kurucu ve Diğer Özel İşlevler } this(in char karakter = char. Ancak. 'dan yararlanılır: üye işlevler içinde this. in double kesirli = double.Deneme. parametre anlaşılır. http://ddili. this.karakter yazıldığında da "bu nesnenin üyesi" anlaşılır.init. Bunları.1. in double kesirli = double. perde arkasında zaten derleyici tarafından otomatik olarak yazılır ve çağrılır. o kurucu yalnızca dakika miktarını aldığı için.

ekle(Süre(22 * 60 + 20)). } this(int dakika) { this. Çözüm olarak kurucuyu yükleyebilir ve bir tane de tek parametreli kurucu tanımlayabiliriz: struct Süre { int dakika.org 271 . nesnelerin { } karakterleriyle kurulmaları olanağını da ortadan kaldırır: Süre süre = { 5. } } Saat ve dakika farklı iki parametre olunca. Programcıları bu fazladan hesaplardan kurtarmak için.5 Programcının kurucusu otomatik kurucunun bazı kullanımlarını geçersizleştirir Programcı tarafından tek bir kurucu işlevin bile tanımlanmış olması. 53. Ek olarak. // ← derleme HATASI Bunlara rağmen. this(int saat. Böylece toplam dakika hesabı kurucu içinde yapılabilir: struct Süre { int dakika.dakika = saat * 60 + dakika.azalt(Süre(23.1. int dakika) { this. programcının tanımlamış olduğu iki parametreli kurucuya uymamaktadır.ekle(Süre(22.azalt(Süre(12)). saat ve dakika miktarlarını iki ayrı parametre olarak alan bir Süre kurucusu tanımlayabiliriz. programcılar da aritmetik hesabı kendileri yapmak zorunda kalmamış olurlar: // 23 saat ve 18 dakika öncesi zaman.Kurucu ve Diğer Özel İşlevler // 22 saat ve 20 dakika sonrası zaman. 20)). int dakika) { this. hiç parametre yazılmadan kurulum her zaman için geçerlidir: Copyright © 2009-2011 Ali Çehreli. } } Programcı tarafından tanımlanan kurucu.dakika = dakika.dakika = saat * 60 + dakika. // 22 saat ve 20 dakika sonrası zaman. // ← derleme HATASI O tek parametreli kullanım. Süre 'nin otomatik kurucusu o kullanımda artık geçersizdir. this(int saat. http://ddili. Örneğin Süre 'nin tek parametre ile kurulması derleme hatasına neden olur: zaman. 0 }. 18)). derleyicinin oluşturduğu kurucu işlevin varsayılan parametre değerleri ile kullanımını geçersiz hale getirir.

nesnelerin yaşam süreçlerinin.Kurucu ve Diğer Özel İşlevler auto s = Süre(). Bazı durumlarda ise nesnenin sonlanmasıyla ilgili bazı özel işlemler gerekebilir. başka bir nesnenin bir üye işlevi çağrılacaktır. ama hiç parametre yazılmadan kurulum yine de geçerlidir. tek bir kurucu işlevin bile tanımlanmış olması otomatik kurucunun bazı kullanımlarını geçersiz hale getirir. çoğu yapı için bu kadarı zaten yeterlidir. Böyle tasarımlar programlarda karışıklıklara neden olur.2. başka bir bilgisayar üzerinde çalışmakta olan bir programa onunla olan bağlantımızı kesmekte olduğumuz bildirilecektir. 53. Böylece kod tekrarı azaltılmış olur. Kurucuda olduğu gibi. Derleyicinin sunduğu otomatik sonlandırıcı. tanımlandıkları kapsamdan çıkılırken sona erdiğini görmüştük. // derlenir Görüldüğü gibi.1.dakika = saat * 60 + dakika.1. nesneler tek parametre ile kurulduklarında ne yapılmak istendiğinin açık olmayabileceğidir: auto yolSüresi = Süre(10).2 Sonlandırıcı işlev Nesnenin yaşam süreci sona ererken gereken işlemler sonlandırıcı işlev tarafından işletilir. 53. // 10 saat mi. diğer kurucuyu saat değeri olarak 0 göndererek çağırıyor. Kurucu işlevde de olduğu gibi. int dakika) { this.7 Uyarı Yukarıdaki Süre kurucularında bir tasarım hatası bulunduğunu söyleyebiliriz. 10 dakika mı? Süre 'nin belgelerine veya tanımına bakarak "10 dakika" dendiğini anlayabiliriz. vs. yapı nesnesinin geçerliliği bittiği an işletilir. } this(int dakika) { this(0. Örneğin nesnenin sahiplenmiş olduğu bir işletim sistemi kaynağının geri verilmesi gerekiyordur.org 272 .1 Sonlandırıcı işlev mutlaka işletilir Sonlandırıcı işlev. http://ddili. dakika). sıra ile bütün üyelerin kendi sonlandırıcılarını çağırır. } // diğer kurucuyu çağırıyor Yalnızca dakika alan kurucu. sonlandırıcı işlevin de dönüş türü yoktur ve ismi ~this 'tir.6 Başka kurucu işlevleri çağırmak Kurucu işlevler başka kurucu işlevleri çağırabilirler. kaçınılmaları gerekir. 53. Öte yandan. 53. Bunun nedeni. Yine de kullanımını şöyle gösterebiliriz: this(int saat. Yaşam Süreçleri dersinden hatırlayacağınız gibi. Bir yapı nesnesinin yaşamının sona erdiği durumlar şunlardır: Copyright © 2009-2011 Ali Çehreli. iki parametre alan kurucuda ilk parametrenin saat olması bir tutarsızlıktır. Süre gibi basit bir yapı bunun yararını görmek için uygun değil.

XML elemanları açılı parantezlerlerle belirtilirler. ve kapama işini de sonlandırıcı işleve yaptırırsak. hazır değer nesnesinin tanımlandığı ifadenin en sonu zaman. <isim> şeklinde açılan bir XML elemanının doğru olarak ve mutlaka </isim> şeklinde kapatılmasını sağlamak olsun: <ders1> <not> 57 </not> </ders1> ← dıştaki XML elemanının açılması ← içteki XML elemanının açılması ← veri ← içtekinin kapatılması ← dıştakinin kapatılması Bunu sağlayacak bir yapıyı iki üyesi olacak şekilde tasarlayabiliriz. o düzeyin girintisini belirleyen ve boşluklardan oluşan bir string üretir: string girintiDizgisi(in int girintiAdımı) { Copyright © 2009-2011 Ali Çehreli. Kurucu işlevi bu amaca göre şöyle yazabiliriz: this(in string isim. isim.org 273 . Örneğin çıktıya nesne kurulduğunda <eleman> . // ← Süre(5) hazır değeri bu // satırın sonunda sonlanır • üyesi olduğu yapı nesnesinin sonlanması: bir nesnenin bütün üyeleri de asıl nesne ile birlikte sonlanırlar 53.ekle(Süre(5)).girinti = girintiDizgisi(düzey). girintiDizgisi . XML elemanının açılmasını sağlar. sonlandırıldığında da </eleman> yazdırabiliriz. Bu üyeler. Burada amacımız. '>'). Kurucunun son satırı. this. string girinti.. } Eğer XML elemanını açma işini kurucu işleve. in int düzey) { this. '<'. nesnelerin yaşam süreçlerini belirleyerek istediğimiz çıktıyı elde edebiliriz. } // ← süre'nin sonlandırıcısı burada işletilir • hazır değerler için. XML elemanlarının nitelikleri de olabilir.. http://ddili.isim = isim. } writeln(girinti. XML elemanının ismini ve çıkışta ne kadar girintiyle yazdırılacağını temsil edebilirler: struct XmlElemanı { string isim. // .2 Sonlandırıcı örneği Sonlandırıcı örneği olarak XML düzeni oluşturmaya yarayan bir yapıya bakalım. onları bu örnekte dikkate almayacağız. ve verilerden ve başka XML elemanlarından oluşurlar.2.Kurucu ve Diğer Özel İşlevler • içinde tanımlandığı kapsamdan normal olarak veya atılan bir hata ile çıkıldığı zaman if (birKoşul) { auto süre = Süre(7).

foreach (dersNumarası. rasgeleNot). writeln(girintiDizgisi(3). Yararlandığı replicate işlevi. 101). Sonlandırıcı işlevi de benzer şekilde ve XML elemanını kapatmak için şöyle yazabiliriz: ~this() { writeln(girinti. foreach (i. "</". std. } } } XmlElemanı nesnelerinin üç kapsamda oluşturulduklarına dikkat edin. Programın çıktısında birbirlerine karşılık gelen kurucu ve sonlandırıcı çıktılarını aynı renkle gösterdim. Dış kapsam için kırmızı. 1). ortadaki için mavi. } O yapıyı kullanan bir deneme programı aşağıdaki gibi yazılabilir: void main() { auto dersler = XmlElemanı("dersler". girintiAdımı * 2). 0 .org 274 . '>'). kendisine verilen dizgiyi belirtilen sayıda uç uca ekleyerek yeni bir dizgi üreten bir işlevdir. Bu durumda yalnızca boşluk karakterlerinden oluşuyor ve satır başlarındaki girintileri oluşturmak için kullanılıyor..array modülünde tanımlıdır. 0 . 2) { auto ders = XmlElemanı("ders" ~ to!string(dersNumarası).Kurucu ve Diğer Özel İşlevler } return replicate(" ". ve içerdeki için yeşil: Copyright © 2009-2011 Ali Çehreli. Bu programdaki XML elemanlarının açılıp kapanmaları. bütünüyle o nesnelerin kurucu ve sonlandırıcı işlevleri tarafından oluşturulmaktadır. http://ddili. 3) { auto not = XmlElemanı("not". 0). 2).. const int rasgeleNot = uniform(50. isim.

53. oradaki tür ismi yerine geçmektedir.org 275 . auto . ve main 'den çıkılırken sonlandırıldığı için de en son onun sonlandırıcısının çıktısını görüyoruz. var olan nesnenin üyelerinden kopyalar: auto dönüşSüresi = gidişSüresi. ancak sonrasını belirleyebiliriz. var olan bir nesnenin kopyası olarak yeni bir nesne oluşturmaktır. Diğer kuruculardan ayırt edilebilmesi için de parametre listesine özel olarak this yazılır: this(this) { Copyright © 2009-2011 Ali Çehreli. Kurma ile ilgili olduğu için kopya sonrası işlevinin ismi de this 'tir.Kurucu ve Diğer Özel İşlevler <dersler> <ders0> <not> 72 </not> <not> 97 </not> <not> 90 </not> </ders0> <ders1> <not> 77 </not> <not> 87 </not> <not> 56 </not> </ders1> </dersler> Çıktıda örnek olarak <dersler> elemanına bakalım: main içinde ilk olarak dersler nesnesi kurulduğu için ilk olarak onun kurucusunun çıktısını görüyoruz. Yeni nesnenin bütün üyelerini sırayla. Atama olması için. Yapılarda kopyalama işinin ilk aşamasını derleyici gerçekleştirir. // kopyalama Bu işlemi atama işlemi ile karıştırmayın. dönüşSüresi 'nin daha önceden tanımlanmış bir nesne olması gerekir: dönüşSüresi = gidişSüresi. // atama (aşağıda anlatılıyor) Eğer herhangi bir nedenle kopyalama ile ilgili olarak özel bir işlem gerçekleştirmek istersek. dönüşSüresi isminde yeni bir nesne kurulduğunu gösterir. http://ddili.3 Kopya sonrası işlevi Kopyalama. Sol taraftaki auto . Biz otomatik kopyalamaya karışamayız. bunu ancak otomatik kopyalama işleminden sonrasını belirleyecek şekilde yapabiliriz.

// kopyalama öğrenci1. kopya sonrası işlevinde gerçekleştirebiliriz: struct Öğrenci { int numara. // atama Atama.notlar[0] == 70). 85 ]).. 90. Bu. İşleve girildiğindeki notlar . artık ikinci öğrencinin notlarını etkilemez: öğrenci1. int[] notlar. // ikincinin notu da değişir: assert(öğrenci2. zaten var olan bir nesneye yeni bir değer vermek anlamına gelir: dönüşSüresi = gidişSüresi. } O yapının notlar üyesi dinamik dizi olduğu için bir referans türüdür. this(this) { notlar = notlar.numara = 2.dup. Birinci öğrencinin notlarında yapılan değişiklik. Böylece bu nesnenin notlar 'ı yeni bir diziye erişim sağlamaya başlar. ikinci öğrencinin notlar üyesinin o nesneye ait bir dizi olması sağlanmalıdır. notlar 'ın erişim sağladığı asıl dizinin bir kopyasını almak. diğerinde de görülür: auto öğrenci1 = Öğrenci(1. assert(öğrenci2. } } this(this) işlevine girildiğinde bütün üyelerin çoktan asıl nesnenin kopyaları olarak kurulduklarını hatırlayın. 53.notlar[0] += 5.4 Atama işleci Atama. Yukarıdaki tek satırda yapılan ise.Kurucu ve Diğer Özel İşlevler } // . int[] notlar.org 276 . ve onu yine bu nesnenin notlar 'ına atamaktır. nesne temel işlemleri arasında diğerlerinden biraz daha karmaşıktır ve bu nedenle hatalara açıktır. Bunun nedeni.. Birinin notlarında yapılan değişiklik. atama işleminin aslında iki adımdan oluşuyor olmasıdır: • nesnenin sonlandırılması Copyright © 2009-2011 Ali Çehreli. asıl nesnenin notlar 'ı ile aynı diziye erişim sağlamaktadır. [ 70. öğrenci2. auto öğrenci2 = öğrenci1. bir Öğrenci nesnesinin bir başkasına kopyalanması durumunda ikisinin notlar üyelerinin aynı asıl diziye erişim sağlamalarına neden olur.notlar[0] == 75). Yapılar dersinde basit bir Öğrenci yapısı kullanmış ve onun nesnelerinin kopyalanmaları ile ilgili bir sorundan söz etmiştik: struct Öğrenci { int numara. Bunun önüne geçmek için.notlar[0] += 5. http://ddili. Bunu.

elimizde sonlandırılmış ama tekrar kurulamamış bir nesne kalır. Söz dizimi şu şekildedir: • • • • opAssign özel ismiyle tanımlanır Parametre olarak yapının türünü const ref olarak alır Dönüş türü. Derleyicinin sunduğu otomatik atama işleci bu yüzden güvenli hareket etmek zorundadır ve perde arkasında şu işlemleri gerçekleştirir: 1.. atılabilecek olan hatalara karşı dikkatli olmak gerektiğini unutmayın. yeni değeri bu nesneye kopyalar 3. Eğer herhangi bir nedenle kendiniz tanımlamak isterseniz.dakika). ref Süre opAssign(const ref Süre sağdaki) { writefln( "dakika. onu birinci adımda güvenle sonlandıramayız. // atama dakika. bu nesneyi geçici bir nesneye kopyalar 2. sağdaki.dakika. Copyright © 2009-2011 Ali Çehreli.org 277 .dakika = sağdaki. } Farkı. } return this. 100 değerinden 200 değerine değişiyor Süre gibi küçük yapılarda parametre türü const ref yerine in olarak da belirtilebilir: ref Süre opAssign(in Süre sağdaki) { // . http://ddili.. yapının türünün ref olanıdır İşlevden return this ile çıkılır Ben burada basit Süre yapısı üzerinde ve çıktıya bir mesaj yazdıracak şekilde tanımlayacağım: struct Süre { int dakika. this.. in parametrenin işleve kopyalanarak gönderilmesidir. this. küçük yapılarda hız kaygısı da doğurmaz. nesnenin yeni değerle tekrar kurulması aşamasında bir hata atılsa. bu iki adımın yazdığım sırada işletilmelerinde önemli bir sorun vardır: Daha nesnenin ikinci adımda başarıyla kurulabileceğinden emin olmadan.. %s değerinden %s değerine değişiyor". geçici nesneyi sonlandırır Derleyicinin sunduğu otomatik atama işleci hemen hemen her durumda yeterlidir.dakika. Anlamsal olarak farkları olmadığı gibi. } // . süre = Süre(200). auto süre = Süre(100).Kurucu ve Diğer Özel İşlevler • yeni değerle tekrar kurulması Ancak. Yoksa.

} return this. this.4.org 278 . parametre olarak int alan bir atama işleci daha tanımlayarak sağlayabiliriz: struct Süre { int dakika.dakika = dakika.4. %s değerinden %s değerine değişiyor". } // . this.dakika). http://ddili. Copyright © 2009-2011 Ali Çehreli.. bir tamsayı değer ile değiştiriliyor"). dakika. kimi zaman gereksiz ve sorunludur. } return this.. kolaylık getirdiği kadar karışıklıklara ve hatalara da neden olabilir.dakika. sağdaki. Atama işlecini farklı türlerden parametre alacak şekilde tanımlamanın yararlı olduğunu düşündüğünüz zamanlarda. süre = Süre(200). doğrudan bir tamsayı değer kullanmak isteyebiliriz: süre = 300. ref Süre opAssign(int dakika) { writeln( "dakika.dakika.dakika = sağdaki. Bunu. Kimi zaman yararlıdır. Örneğin atama işlecinin sağ tarafında her zaman için Süre türü kullanmak yerine.2 Uyarı Farklı türleri bu şekilde birbirlerine eşitleyebilmek. veya daha genel olarak birbirlerinin yerine kullanabilmek. süre = 300. 100 değerinden 200 değerine değişiyor dakika.Kurucu ve Diğer Özel İşlevler 53. bunun gerçekten gerekli olup olmadığını iyice tartmanızı öneririm. bir tamsayı değer ile değiştiriliyor 53. ref Süre opAssign(const ref Süre sağdaki) { writefln( "dakika. this.1 Başka türlerden atamak Bazı durumlarda nesnelere kendi türlerinden farklı türlerin değerlerini de atamak isteyebiliriz.

} Üye işlevlerin yararı. GününSaati nesnelerine Süre nesnelerini ekleyebilmekti. En belirgin fark. } } void main() { auto yemekZamanı = GününSaati(12. Örneğin GününSaati yapısı için tanımlayabileceğimiz += işleci. özel hiçbir şey yapmaya gerek olmadan işleçlerle rahatça kullanılabilirler: int ağırlık = 50. } struct GününSaati { int saat. saat += dakika / 60. // üye işlevle İşleç yükleme olanağı bu konuda yarar sağlar: Yapıları da temel türlerde olduğu gibi işleçlerle kullanabilme olanağı sunar. Bunun bir örneği. int dakika. Kodu kısa tutmak için yalnızca bu dersi ilgilendiren üyelerini gösteriyorum: struct Süre { int dakika. işleçlerin kendi türlerimizle nasıl çalışacaklarını belirleme olanağıdır. Üyeler ve işlevler bir arada tanımlanınca. http://ddili.dakika. yukarıdaki işlemi temel türlerde olduğu gibi doğal olarak yazmamızı sağlar: yemekZamanı += Süre(10). ağırlık += 10. yemekZamanı.org 279 . dakika %= 60.ekle(Süre(10)). özel işlevler dersinde gördüğümüz atama işleci opAssign 'ın sınıflar için tanımlanamıyor olmasıdır.İşleç Yükleme 54 İşleç Yükleme Bu derste yapılar üzerinde anlatılanlar daha sonra göreceğimiz sınıflar için de hemen hemen aynen geçerlidir. // işleçle Şimdiye kadar öğrendiğimiz bilgiler doğrultusunda benzer işlemleri yapılar için ancak üye işlevlerle gerçekleştirebiliyoruz: auto yemekZamanı = GününSaati(12. // yapı için de işleçle Copyright © 2009-2011 Ali Çehreli.ekle(Süre(10)). Yapıların ve onlar için özel olarak tanımlayabildiğimiz üye işlevlerin yararlarını önceki derslerde gördük. Bütün bu yararlarına karşın. yapı ile ilgili işlemlerin yapı tanımı ile aynı yerde yapılabilmesidir. İşleç yükleme. saat %= 24. 0). 0). yapının üyelerinin bütün işlevler tarafından doğru olarak kullanıldıkları da hep birden denetlenebilir. temel türlerle karşılaştırıldıklarında yapıların bir yetersizlikleri ortaya çıkar: Temel türler. void ekle(in Süre süre) { dakika += süre. yemekZamanı.

Şablonlarla ilgili olan bu söz dizimini de daha ilerideki bir derste göstereceğim. opOpAssign 'ın isminde geçen ve "değer ata" anlamına gelen "assign". // Üsttekinin eşdeğeri: yemekZamanı. Daha önce ismini ekle olarak yazdığımız işlevi. GününSaati nesnelerinin += işleciyle kullanıldığı yerlerde perde arkasında opOpAssign!"+" işlevini çağırır: yemekZamanı += Süre(10). if (işleç == "işleç_karakteri") : Aynı üye işlev ismi birden fazla işlev için kullanıldığından hangi işleç karakterinin tanımlanmakta olduğu bu söz dizimiyle belirtilir. http://ddili. opOpAssign aslında bir işlev şablonu olduğundandır. void opOpAssign(string işleç)(in Süre süre) if (işleç == "+") { dakika += süre. "+" kullanıldığına dikkat edin. 54. // (1) // (2) } } Yukarıdaki şablon tanımı iki parçadan oluşur: 1. atamalı toplama işlemi olan += işlecinin tanımıdır. bize çoğu işleç için istediğimiz şeyi yapma serbestisi verir. Şimdiye kadar gördüğümüz işlev tanımlarından farklı olan bu tanım. Kod içindeki += işlecine karşılık gelen üye işlevde "+=" değil. opOpAssign 'dan sonra yazılan !"+" . Derleyici.İşleç Yükleme Yüklenebilecek bütün işleçlere geçmeden önce bu kullanımın nasıl sağlandığını göstermek istiyorum. zaten atama kavramını içerir.dakika. kullanılabilecek bütün üye işlev isimlerini aşağıda göreceğiz. şimdilik işleç yükleme konusunda bu söz dizimini bir kalıp olarak uygulamak gerektiğini kabul etmenizi rica ediyorum: struct GününSaati { // . saat %= 24.1 Alışılmış olan davranışları değiştirmeyin İşleçlerin davranışlarını bizim belirleyebiliyor olmamız. opOpAssign 'ın + işleci için tanımı olan işlevin çağrılmakta olduğu anlamına gelir. Aslında bir şablon kısıtlaması olan bu söz diziminin ayrıntılarını da daha sonraki bir derste göreceğiz.. yani opOpAssign!"+" . üyeİşlevİsmi(string işleç) : Yukarıdaki opOpAssign 'da olduğu gibi üyeİşlevİsmi'nin ve (string işleç) 'in aynen yazılmaları gerekir. Şablonları daha ilerideki derslerde göstereceğim. Oysa kodu okuyanlar += işlecini gördüklerinde doğal olarak değerin artması gibi bir davranış bekleyeceklerdir. Örneğin yukarıdaki işlevin içeriğini süre ekleyecek şekilde değil. tam tersine süre azaltacak şekilde de yazabilirdik. Copyright © 2009-2011 Ali Çehreli.. İşleçleri doğal davranışları dışında yazdığınızda bunun herkesi yanıltacağını ve programda hatalara neden olacağını aklınızda bulundurun. dakika %= 60. saat += dakika / 60.org 280 .opOpAssign!"+"(Süre(10)). 2. D'nin özel olarak belirlediği opOpAssign(string işleç) ismiyle tanımlamak ve bu tanımın "+" işlemi için yapılmakta olduğunu belirtmek gerekir.

void opUnary(string işleç)() if (işleç == "++") { ++dakika. http://ddili. = işleci de sağındakinin değerini solundakine atar. ++ işleci tekli işleçtir çünkü işini yaparken yalnızca ağırlık 'ı kullanır ve onun değerini bir arttırır. Yukarıdaki ifadede iki farklı ikili işleç görüyoruz: + işleci.2 Tekli işleç Tek nesneyle işleyen işleçlere tekli işleç denir: ++ağırlık.4 Yüklenebilen tekli işleçler Bu işleçler opUnary üye işlev ismiyle tanımlanırlar. yalnızca çok kullanıldığını düşündüğüm işleçleri anlatacağım.İşleç Yükleme 54. Bu derste yüklenebilen bütün işleçlerin örneklerini vermeyeceğim. } } Süre nesneleri bu sayede artık ++ işleci ile arttırılabilirler: auto süre = Süre(20). 54. Zaten bu işleçlerin bazılarını yüklemeye belki de hiç ihtiyacınız olmayacak. 54. ++süre. parametre almazlar çünkü yalnızca işlecin kullanıldığı nesneyi etkilerler.3 İkili işleç İki nesne kullanan işleçlere ikili işleç denir: toplamAğırlık = kutuAğırlığı + çikolataAğırlığı.org 281 . Copyright © 2009-2011 Ali Çehreli. kendisinin solundaki ve sağındaki değerleri toplar. İşlev tanımında kullanılması gereken işleç dizgileri şunlardır: İşleç -nesne +nesne ~nesne *nesne ++nesne --nesne Anlamı ters işaretlisini üret aynı işaretlisini üret bit düzeyinde tersini al gösterdiğine eriş bir arttır bir azalt İşleç Dizgisi "-" "+" "~" "*" "++" "--" Örneğin ++ işlecini Süre için şöyle tanımlayabiliriz: struct Süre { int dakika.

/* Daha sonra bütün ifadede __öncekiDeğer__ kullanılır. /* Tanımlanmış olan normal ++ işleci çağrılır: */ ++süre. eksi değer. işlecin sol tarafındaki ile sağ tarafındakinin yer değiştirmesi durumunda sonucun aynı olup olmadığını gösterir. Öte yandan. */ 54. çünkü x-y ve y-x işlemlerinin sonuçları farklıdır.5 Yüklenebilen ikili işleçler Aşağıdaki tabloda gösterilen değişmeli kavramı. sol tarafı değiştirir Copyright © 2009-2011 Ali Çehreli. O kullanımlardaki önceki değerleri derleyici otomatik olarak halleder. Örneğin nesne++ kullanımın eşdeğeri şudur: /* Önceki değer derleyici tarafından otomatik olarak * kopyalanır: */ Süre __öncekiDeğer__ = süre. sıfır veya artı değer döndürür • =: atamalı işleç.İşleç Yükleme Önceki değerli arttırma ve önceki değerli azaltma işleçleri olan nesne++ ve nesne-kullanımları yüklenemez.org 282 . çünkü x+y ile y+x işlemlerinin sonuçları aynıdır. . İşleçleri gruplandırmak için aşağıdaki tabloda işleçlerin türlerini de belirttim: • a: aritmetik işleç • b: bit düzeyinde işlem işleci • m: mantıksal işleç. Örneğin + işleci değişmeli bir işleçtir.değişmeli bir işleç değildir. http://ddili. bool döndürür • s: sıralama işleci.

Copyright © 2009-2011 Ali Çehreli.opBinaryRight!"işleç"(x). Kodda şöyle bir ikili işleç kullanıldığını düşünelim: x işleç y Derleyici hangi üye işlevi işleteceğine karar vermek için yukarıdaki ifadeyi şu iki üye işlev çağrısına dönüştürür: x. nesne işlecin sağ tarafında da kullanılabildiğinde işletilir. y. http://ddili.opBinary!"işleç"(y).İşleç Yükleme İşleç + * / % ^^ & | ^ << >> >>> ~ == != < <= > >= = += -= *= /= %= ^^= &= |= ^= <<= >>= >>>= ~= in Anlamı topla çıkar çarp böl kalanını hesapla üssünü al bit işlemi ve bit işlemi veya bit işlemi ya da sola kaydır sağa kaydır işaretsiz sağa kaydır birleştir eşittir eşit değildir öncedir sonra değildir sonradır önce değildir ata arttır azalt katını ata bölümünü ata kalanını ata üssünü ata & sonucunu ata | sonucunu ata ^ sonucunu ata << sonucunu ata >> sonucunu ata >>> sonucunu ata sonuna ekle içinde mi? Değişmeli (Komütatif) evet hayır evet hayır hayır hayır evet evet evet hayır hayır hayır hayır evet evet hayır hayır hayır hayır hayır hayır hayır hayır hayır hayır hayır hayır hayır hayır hayır hayır hayır hayır hayır İşlev İsmi opBinary opBinary opBinary opBinary opBinary opBinary opBinary opBinary opBinary opBinary opBinary opBinary opBinary opEquals opEquals opCmp opCmp opCmp opCmp opAssign opOpAssign opOpAssign opOpAssign opOpAssign opOpAssign opOpAssign opOpAssign opOpAssign opOpAssign opOpAssign opOpAssign opOpAssign opOpAssign opBinary Sağ Taraf için İşlev İsmi opBinaryRight opBinaryRight opBinaryRight opBinaryRight opBinaryRight opBinaryRight opBinaryRight opBinaryRight opBinaryRight opBinaryRight opBinaryRight opBinaryRight opBinaryRight opBinaryRight Tür a a a a a a b b b b b b m m s s s s = = = = = = = = = = = = = = m Tabloda sağ taraf için olarak belirtilen işlev isimleri.org 283 .

} } O tanımdan sonra programlarımızda artık Süre nesnelerini + işleciyle toplayabiliriz: auto gitmeSüresi = Süre(10). Örneğin Süre nesnelerini birbirleriyle toplamak için opBinary işlecini "+" işleci için şöyle yükleyebiliriz: struct Süre { int dakika.. x. (Bu durum in işlecinde tam tersidir: Onun için çoğunlukla opBinaryRight tanımlanır.opBinaryRight!"işleç"(y). toplamSüre = gitmeSüresi + dönmeSüresi. 54.opBinary!"işleç"(x). derleyici != işleci için onun tersini kullanır: x == y. // üsttekinin eşdeğeri Copyright © 2009-2011 Ali Çehreli. O işlevi nesnelerin eşitliği için tanımlayınca. y. o işletilir.opEquals(y). Çoğu durumda bu işlevlerden ismi Right ile sonlananların hiç tanımlanmasına gerek yoktur. auto dönmeSüresi = Süre(11).org 284 . http://ddili.) Aşağıdaki örneklerde üye işlevleri tanımlarken parametre ismini sağdaki olarak seçtim. Derleyici o ifadeyi dönüştürür ve perde arkasında gitmeSüresi nesnesi üzerinde bir üye işlev olarak çağırır: toplamSüre = gitmeSüresi.. Süre opBinary(string işleç)(in Süre sağdaki) const if (işleç == "+") { return Süre(dakika + sağdaki.opBinary!"+"(dönmeSüresi). Bununla parametrenin işlecin sağındaki nesne olduğunu vurguluyorum: x işleç y O ifade kullanıldığında.İşleç Yükleme Eğer o işlevlerden birisi tanımlanmışsa. opEquals üye işlevi bu işleçlerin ikisini de karşılar. ve işleç değişmeli bir işleçse.6 Aritmetik işleçler Kendi türlerimizin aritmetik işlemlerde nasıl kullanıldıklarını belirler. 54. ayrıca şu işlevleri de dener: x. Süre toplamSüre. Normalde bu kadar karmaşık kuralları bilmek zorunda kalmayız. // .7 Eşitlik karşılaştırmaları için opEquals == ve != işleçlerinin davranışını belirler. üye işlevin sağdaki ismindeki parametresi y olacaktır. Mantıksal ifadelerde kullanıldığı için dönüş türü bool 'dur. Değilse.dakika).

if (x <= y) { ifadesi şuna dönüştürülür: if (x. // üsttekinin eşdeğeri Normalde opEquals işlevini yapılar için tanımlamaya gerek yoktur. > . !(x.opCmp(y) <= 0) { Kendi yazdığımız bu işlevin bu kurala göre doğru çalışabilmesi için işlevin şu değerleri döndürmesi gerekir: • soldaki nesne önce olduğunda eksi bir değer • sağdaki nesne önce olduğunda artı bir değer • ikisi eşit olduklarında sıfır değeri Copyright © 2009-2011 Ali Çehreli. bool opEquals(const ref GününSaati sağdaki) const { return saat == sağdaki.saat. Örneğin bazı üyeler nesnenin eşit kabul edilmesi için önemsiz olabilirler. vs. ve >= işleçlerinin hepsi opCmp üye işlevi tarafından karşılanır.8 Sıra karşılaştırmaları için opCmp Sıralama işleçleri nesnelerin öncelik/sonralık ilişkilerini belirler. assert(GününSaati(20. örnek olsun diye öylesine tanımladım. 59)).. Sıralama ile ilgili olan < .org 285 .. Bu dört işleçten birisinin şu şekilde kullanıldığını kabul edelim: if (x işleç y) { Derleyici o ifadeyi aşağıdakine dönüştürür ve o mantıksal ifadenin sonucunu kullanır: if (x. <= .) 54. Bazen nesnelerin eşitliklerini kendimiz belirlemek isteyebiliriz. bir türün nesnelerinin eşit kabul edilmeleri özel bir kurala bağlı olabilir.opEquals(y)). (Not: Bunun karışıklık doğuracağı açıktır. } } // . 10) == GününSaati(20. Bunu göstermek için GününSaati sınıfı için dakika bilgisini gözardı eden bir opEquals tanımlayalım: struct GününSaati { int saat.İşleç Yükleme x != y. http://ddili. int dakika. Eşitlik karşılaştırmasında yalnızca saat bilgisine bakıldığı için 20:10 ve 20:59 zamanları eşit çıkmaktadır. derleyici bütün üyelerin eşitliklerini sırayla otomatik olarak karşılaştırır ve nesnelerin eşit olup olmadıklarına öyle karar verir.opCmp(y) işleç 0) { Örnek olarak.

sort niteliği bizim tanımladığımız sıralama ilişkisini kullanır. } zamanlar. Sıralama algoritmalarının bir örneğini dizilerin .saat). 0 .13:42. GününSaati nesnelerinin sıralama ilişkilerini öncelikle saat değerine.İşleç Yükleme Bundan anlaşılacağı gibi. Dizinin . uniform(0. import std.dakika : saat . int opCmp(const ref GününSaati sağdaki) const { return (saat == sağdaki. sağdaki nesne önce olduğunda artı bir değer döndürür.sort.04:10.saat ? dakika .09:06. bu türün sıralama algoritmalarıyla da kullanılabilmesini sağlar.. 24).sağdaki.saat). http://ddili.11:04. saatleri eşit olduğunda da dakika değerlerine bakacak şekilde şöyle belirleyebiliriz: int opCmp(const ref GününSaati sağdaki) const { return (saat == sağdaki. Beklendiği gibi. saatler farklı olduğunda da saatlerin farkı döndürülüyor. } Saat değerleri aynı olduğunda dakika değerlerinin farkı. import std. opCmp 'un tanımlanmış olması.sağdaki.string. int dakika. 10) { zamanlar ~= GününSaati(uniform(0. opCmp 'ın dönüş türü bool değil. Aşağıdaki program 10 adet rasgele zaman değeri oluşturur ve onları dizinin .10:03. } writeln(zamanlar).21:08] Copyright © 2009-2011 Ali Çehreli.16:40.stdio. çıktıdaki saat değerleri doğru sıradadır: [03:40.saat ? dakika . zaman sıralamasında soldaki nesne önce olduğunda eksi bir değer. Sıralamayı belirlerken nesneler karşılaştırılırken hep opCmp işletilir: import std.dakika : saat .sağdaki. int 'tir.org 286 .sağdaki. } string toString() { return format("%02s:%02s".sort niteliği ile sıralar. 60)). dakika). struct GününSaati { int saat. Bu tanım.18:03. saat. } } void main() { GününSaati[] zamanlar.sort niteliğinde görmüştük.10:09. foreach (i.random.

Bu işlecin davranışı da opCall üye işlevi tarafından belirlenir.2. verilen x değerlerine karşılık y değerlerini hesaplayan bir yapı düşünelim: y = ax + b O hesaptaki a ve b'yi çarpan ve eklenen isimli üyeler olarak düşünürsek.9 İşlev gibi çağırmak için opCall İşlev çağırırken kullanılan parantezler de işleçtir.4 denklemini ifade etmeye başlar. double eklenen. çarpan ve eklenen değerlerin baştan bir kere belirlenebilmesidir. ekleneni olarak da 3. İlk satırda nesne kurulurken denklemin çarpanı olarak 1.2x + 3. nesne(). for (double x = 0. Bu işleç sayesinde türün nesnelerini de işlev gibi kullanabiliriz: BirTür nesne.4 değerinin kullanılacağı belirleniyor. y değerlerini opCall işlevi içinde şöyle hesaplayabiliriz: struct DoğrusalDenklem { double çarpan.4 }. Başka çarpan ve eklenen değerleri ile kurulan bir nesneyi bu sefer de bir döngü içinde kullanan bir örnek: DoğrusalDenklem denklem = { 0.125'in katı olan değerleri için hesaplar. Nesne o bilgiyi kendi içinde barındırır ve işlev gibi kullanıldığı zaman kullanır. double y = denklem(5. x += 0. x <= 1.4 }. denklem(x)).0 ile 1.0. Bunun bir örneği olarak bir doğrusal denklemde. O kodda nesne bir işlev gibi çağrılmaktadır.org 287 . Bunun önemi. 0.İşleç Yükleme 54.125) { writefln("%f: %f". çünkü o yazım da bir opCall çağrısı olarak kabul edilir. Copyright © 2009-2011 Ali Çehreli.01x + 0.6). x. opCall 'u tanımlanmış olan yapıların nesnelerini { } yazımıyla kurmamız gerekir. Bunun sonucunda denklem nesnesi. } O da y = 0. o sınıfın nesnelerini bir işlev gibi kullanarak verilen x değerlerine karşılık olan y değerlerini hesaplatabiliriz: DoğrusalDenklem denklem = { 1.0.01.0 aralığındaki 0. 3. // nesne işlev gibi kullanılıyor Not: opCall işlevi tanımlanmış olan yapıları Tür(parametreler) yazımıyla kuramayız. x değerlerini parametre olarak gönderiyor ve dönüş değeri olarak y değerlerini elde ediyoruz. Ondan sonra nesneyi artık bir işlev gibi kullanarak. http://ddili. y = 1. double opCall(double x) { return çarpan * x + eklenen.2. } } Bir kere bunu tanımladıktan sonra.4 denklemini x'in 0.

int notİndeksi) { return notlar[öğrenciİndeksi][notİndeksi] = not. 1]. // Belirtilen notu döndürür int opIndex(int öğrenciİndeksi.org 288 . } Copyright © 2009-2011 Ali Çehreli.opIndexOpAssign!"+"(42. İlk parametresi atanan değer. atamalı işlemin belirtilen indeksteki eleman üzerinde işleyecek olmasıdır: bütünNotlar[2. int öğrenciİndeksi. opIndexUnary ve opIndexOpAssign Bu işleçler. sonraki parametreleri de köşeli parantezler içinde kullanılan değerlerdir: bütünNotlar[1. işlemin belirtilen indeksteki eleman üzerinde işleyecek olmasıdır: ++bütünNotlar[4. Köşeli parantezler içinde kullanılan değerler işlevin parametreleri haline gelirler: birNot = bütünNotlar[3. farkı. int notİndeksi) if (işleç == "++") { ++notlar[öğrenciİndeksi][notİndeksi]. 0]. opIndex erişim amacıyla kullanılır. farkı. 1).opIndexAssign(95. 1).// üsttekinin eşdeğeri opIndexAssign atama amacıyla kullanılabilir. 0). // atamalı arttırma bütünNotlar. // erişim birNot = bütünNotlar. 1] = 95. 1] += 42. // arttırma bütünNotlar. http://ddili. // atama bütünNotlar.opIndexUnary!"++"(4.opIndex(3. } // Belirtilen notu bir arttırır void opIndexUnary(string işleç)(int öğrenciİndeksi. opUnary 'nin eşdeğeridir. opIndexAssign . bütün öğrencilerin bütün notlarını içeren bir yapıyı şöyle tanımlayabiliriz: import std.stdio. struct BütünÖğrencilerinNotları { // 5 öğrenci için 2 not int[2][5] notlar. nesneyi nesne[indeks] şeklinde dizi gibi kullanma olanağı verirler. opOpAssign 'ın eşdeğeridir. 2. // üsttekinin eşdeğeri opIndexUnary . } // Belirtilen notu belirtilen öğrencinin notuna atar int opIndexAssign(int not.// üsttekinin eşdeğeri opIndexOpAssign . 1. 1). int notİndeksi) { return notlar[öğrenciİndeksi][notİndeksi].İşleç Yükleme 54. // üsttekinin eşdeğeri Bütün bu işleçlerle kullanılan bir örnek olarak.10 Dizi erişim işleçleri opIndex .

++bütünNotlar[4. [0. j] kullanımı kullanımı void opSliceUnary(string işleç)() if (işleç == "++"). j] = değer kullanımı void opSliceOpAssign(string işleç)(int değer) if (işleç == "+"). int j). 0]. Diğer satırlar ise belirtilen öğrencilerin belirtilen notlarını etkilemiş oluyor: [[0..notlar). // nesne[i . [1.org 289 . programda bir amacı bulunmuyor.. int birNot = bütünNotlar[3. int opSlice(int i. j] kullanımı int opSliceAssign(int değer). // ++nesne[i . bütünNotlar[2. int j) if (işleç == "+"). http://ddili. j] += değer kullanımı } void main() { BirTür nesne.. ve opSliceOpAssign Yukarıdaki opIndex işleçlerine çok benzerler. bütünNotlar[1. // ++nesne[] kullanımı void opSliceUnary(string işleç)(int i. 0]. // nesne[] += değer kullanımı void opSliceOpAssign(string işleç)(int değer. } writeln(bütünNotlar. opSliceAssign . int i. Bütün bu tanımların iki farklı kullanımı vardır: köşeli parantezlerin içinin boş olduğu kullanım ve köşeli parantezler içinde bir aralık belirtilen kullanım. } void main() { BütünÖğrencilerinNotları bütünNotlar. 1]. // // // // erişim arttırma atama atamalı arttırma Erişim satırını yalnızca göstermek amacıyla kullandım.İşleç Yükleme } // Belirtilen öğrencinin notunu arttırır void opIndexOpAssign(string işleç)(int artış.. opSliceUnary . // nesne[] // nesne[i . int notİndeksi) if (işleç == "+") { notlar[öğrenciİndeksi][notİndeksi] += artış. 1] = 95. Bu sekiz kullanımın hangi işlevler tarafından yüklendiklerini bu işlevleri tanımlamadan gösteren bir örnek: class BirTür { int opSlice(). int öğrenciİndeksi.11 Dilim işleçleri opSlice . 42]. int j) if (işleç == "++"). farkları. 0]] 54. 1] += 42. int i. // nesne[] = değer kullanımı int opSliceAssign(int değer. 95]. [0. [0. int j). // nesne[i . 0]. nesneleri dilim işleciyle kullanma olanağı sağlamalarıdır. Copyright © 2009-2011 Ali Çehreli.

++nesne[3 . double opCast(Tür : double)() { return saat + (cast(double)dakika / 60). i = nesne[3 . nesne[] += değer. Bu türün nesnelerini double türüne dönüştüren işlev şöyle tanımlanabilir: import std.opSliceAssign(değer). nesne.. 4). nesne.opSliceOpAssign!"+"(değer. 4].opSlice(). 4]. nesne[3 .. } Yine şimdilik bir kalıp olarak kabul etmenizi isteyeceğim bu söz dizimini de daha sonra şablonlar dersinde anlatacağım.12 Tür dönüşümü işleci opCast Elle açıkça yapılan tür dönüşümünü belirleyen opCast .opSliceUnary!"++"(). nesne. Süre 'nin saat ve dakikadan oluşan bir tür olduğunu kabul edelim. ++nesne[]. 4] += değer. // üsttekinin eşdeğeri // üsttekinin eşdeğeri // üsttekinin eşdeğeri // üsttekinin eşdeğeri // üsttekinin eşdeğeri // üsttekinin eşdeğeri // üsttekinin eşdeğeri } nesne[3 .. i = nesne.org 290 . 4). Bu işleç de şablon olarak tanımlanır ama kalıbı farklıdır: hangi dönüşümün tanımlanmakta olduğu (Tür : dönüştürülecek_tür) söz dizimiyle belirtilir: dönüştürülecek_tür opCast(Tür : dönüştürülecek_tür)() { // . // üsttekinin eşdeğeri 54.opSlice(3. } } void main() { auto süre = Süre(2. struct Süre { int saat. 30). 3.. http://ddili.İşleç Yükleme int i. Copyright © 2009-2011 Ali Çehreli.opSliceOpAssign!"+"(değer).opSliceUnary!"++"(3.stdio. i = nesne[].. 3.opSliceAssign(değer. nesne. int dakika. nesne. dönüştürülecek her tür için ayrı ayrı yüklenebilir ve o tür opCast 'in dönüş türü olarak yazılır. 4). double kesirli = cast(double)süre. nesne[] = değer. 4] = değer. nesne. 4). i = nesne.. int değer.

Çağrılan noktada kullanılan parametreler de opDispatch 'in parametreleri haline gelirler: BirTür.opDispatch . değer: 42 BirTür.org 291 .İşleç Yükleme } writeln(kesirli). değer: %s".13 Sevk işleci opDispatch Nesnenin var olmayan bir üyesine erişildiğinde çağrılacak olan üye işlevdir. iki saat otuz dakikaya karşılık 2. } } void main() { BirTür nesne.5 değerini üretmektedir: 2. bu işleçte nesnenin sağda yazıldığı durum daha doğaldır: if (zaman in öğleTatili) { O yüzden bu işleç için daha çok opBinaryRight!"in" yüklenir ve derleyici de perde arkasında o üye işlevi çağırır: Copyright © 2009-2011 Ali Çehreli.opDispatch .) Bu işleci çok basit olarak gösteren bir örnek: import std. struct BirTür { void opDispatch(string isim. nesne. Diğer işleçlerden farklı olarak.isim: varOlmayanBaşkaİşlev. Birinci şablon parametresi işlevin ismidir. http://ddili. nesne.14 İçerme sorgusu için opBinary!"in" Eşleme tablolarından tanıdığımız in işlecini nesneler için de tanımlama olanağı sağlar.varOlmayanBaşkaİşlev(100). } Var olmayan üyelerine eriştiğimiz halde derleme hatası almayız.isim: varOlmayanİşlev.varOlmayanİşlev(42).isim: %s. double türüne dönüştüren işleç.5 54. değer: 100 54. Açıkça tür dönüşümü istenen satırda derleyici üye işlevi şöyle çağırır: double kesirli = süre. Var olmayan üyelere erişim. Var olmayan üyenin ismi. O çağrılar. bu işlece sevk edilir. parametre). opDispatch 'e bir şablon parametresi olarak gelir.stdio.opDispatch . T)(T parametre) { writefln("BirTür. (Not: Şablonları daha sonraki bir derste anlatacağım. opDispatch işlevinin çağrılmasını sağlarlar. isim.opCast!double().

GününSaati son.saat). GününSaati nesnesinin for döngüsünde nasıl temel türler kadar rahat kullanıldığına özellikle dikkat edin.sağdaki. int dakika. daha önce gördüğümüz Süre ve GününSaati yapılarına ek olarak bir ZamanAralığı yapısı tanımlıyor. dakika %= 60.15 Örnek Bu örnek. } struct GününSaati { int saat. // aralığın dışında kabul edilir } bool opBinaryRight(string işleç) (const ref GününSaati zaman) const if (işleç == "in") { return (zaman >= baş) && (zaman < son). void opOpAssign(string işleç)(in Süre süre) if (işleç == "+") { dakika += süre.dakika : saat . http://ddili. 54.org 292 .İşleç Yükleme // üsttekinin eşdeğeri if (öğleTatili. saat += dakika / 60. import std. saat %= 24. dakika). import std. Bu örnekte de yalnızca gerektiği kadar üye işlev kullandım. belirli bir zamanın belirli bir aralıkta olup olmadığını belirtiyor.sağdaki. işleç yüklemenin yararını güzelce gösteriyor. } int opCmp(const ref GününSaati sağdaki) const { return (saat == sağdaki. Bu yapı için tanımlanan in işleci. } } struct ZamanAralığı { GününSaati baş.dakika.string. saat. struct Süre { int dakika.saat ? dakika .stdio. } string toString() { return format("%02s:%02s". O döngü.opBinaryRight!"in"(zaman)) { Bu işleci aşağıdaki örnekte kullanıyorum. } void main() Copyright © 2009-2011 Ali Çehreli.

" öğle tatili dışında"). şeklindeki kullanımda yeni aralığın sonu zaman 'a eşit olsun. 2.16 Problemler 1. Bu kullanımı şüpheli olsa da.. 00). çözümler Copyright © 2009-2011 Ali Çehreli. 30). " öğle tatilinde")... 3. zaman += Süre(15)) { if (zaman in öğleTatili) { writeln(zaman. GününSaati zaman. } } } Programın çıktısı: 11:30 11:45 12:00 12:15 12:30 12:45 13:00 13:15 öğle öğle öğle öğle öğle öğle öğle öğle tatili dışında tatili dışında tatilinde tatilinde tatilinde tatilinde tatili dışında tatili dışında 54. } else { writeln(zaman. . zaman < GününSaati(13.. ~ işlecini ZamanAralığı ve GününSaati nesnelerinin aşağıdaki iki kullanımları için yükleyin: ZamanAralığı aralık.İşleç Yükleme { auto öğleTatili = ZamanAralığı(GününSaati(12. GününSaati yapısının opCmp işlevini zamanların ters sırada sıralanmalarını sağlayacak şekilde değiştirin. ve yeniAralık = zaman ~ aralık. Onun ismini değiştirerek GününSaati nesneleri için -= işleci olarak kullanabilirsiniz. 00)). http://ddili. Normalde bit işlemleri için kullanılan bu işleçler. Yukarıda kullanılan ZamanAralığı yapısı için >>= ve <<= işleçlerini tanımlayın. şeklindeki kullanımda da yeni aralığın başı zaman 'a eşit olsun. zaman aralığını belirli bir Süre kadar sağa ve sola kaydırsınlar: aralık >>= Süre(10). for (auto zaman = GününSaati(11. // bütün aralık 10 dakika // sonrasına kaysın <<= işleci için Üye İşlevler dersinin çözümlerinde gördüğümüz GününSaati. 30). yeniAralık = aralık ~ zaman. // . GününSaati(13.azalt işlevinden yararlanmak isteyebilirsiniz.org 293 .

yapıların değer türü olmalarına karşın sınıfların referans türü olmalarıdır.1. Hatırlayacağınız gibi.2 new ile kurulurlar Sınıf değişkenlerinin kendileri değer taşımadıkları için. "hiçbir nesneye erişim sağlamıyor" olabilirler. 55.org 294 . gerçekleştirme türemesidir. O üye erişimi. Copyright © 2009-2011 Ali Çehreli. Aşağıdaki farklılıkların büyük bir çoğunluğu. null ve is dersinde de gösterildiği gibi. bir değişkenin null olup olmadığını == işleciyle değil. O yüzden sınıf değişkenlerinin is ile karşılaştırılmaları gerekir. BirSınıf değişken. http://ddili. Kalıtım. D'nin nesneye yönelik programlama olanakları sınıflar yoluyla gerçekleştirilir.1 Yapılarla karşılaştırılması Sınıflar yapılara temelde çok benzerler. Bu derste sınıfları genel olarak tanıtacağım ve özellikle referans türü olduklarına dikkat çekeceğim. Bu farkları aşağıdaki bölümlerde anlatıyorum. daha sonra göreceğimiz erişim hakları ile sağlanır. sınıf değişkenleri null da olabilirler. // erişim sağlamayan assert(değişken is null).Sınıflar 55 Sınıflar Sınıflar. 55. değişkenin null olduğu durumda programın bir bellek hatası ile sonlanmasına neden olur. Sınıfların diğer olanaklarını daha sonraki derslere bırakacağım. 55.1. Bunun nedeni == işlecinin nesnenin üyelerini de kullanmasının gerekebileceğidir. is işleciyle denetlememiz gerekir: BirSınıf erişimSağlayan = new BirSınıf. Yani. sınıfların bu özelliğinden kaynaklanır. kullanıcı türleri tanımlamaya yarayan bir başka olanaktır. Aynı nedenden. Bu yüzden daha önce şu derslerde yapılar üzerinde gördüğümüz hemen hemen her konu sınıflar için de geçerlidir: • • • • • Yapılar Üye İşlevler const ref Parametreler ve const Üye İşlevler Kurucu ve Diğer Özel İşlevler İşleç Yükleme Sınıfları yapılardan ayıran önemli farklar da vardır.1 Referans türleridir Sınıfların yapılardan farklı olmalarının en büyük nedeni. Çok şekillilik ise arayüz türemesi yoluyla gerçekleştirilir. Nesneye yönelik programlamayı üç temel kavram üzerinde düşünebiliriz: • sarma: üyelere erişimin kısıtlanması (aslında yapılarda da bulunan bu olanağı genelde yapıların kullanım amaçlarının dışında kaldığı için göstermedim) • kalıtım: başka bir türün üyelerini ve üye işlevlerini kendisininmiş gibi edinmek • çok şekillilik: birbirlerine yakın türlerin daha genel ortak bir tür gibi kullanılabilmeleri Sarma. asıl nesne new anahtar sözcüğü ile oluşturulur.

---+---+--| . Sınıf nesnelerinin kopyalanmaları gerektiğinde bunu sağlayan bir üye işlev tanımlanmalıdır..1. Referans türü oldukları için. http://ddili. Bir nesne kopyalanmadığı için de. } Copyright © 2009-2011 Ali Çehreli.---+-|-+--. new anahtar sözcüğü ile oluşturulan ve kendi ismi olmayan bir program yapısıdır. isimsiz bir BirSınıf nesnesi oluşturur. değişken1 'in kopyası olarak oluşturulmaktadır. Bu işlem sırasında asıl nesne kopyalanmaz.. dchar[] dizgi. this(Yapı yapıNesnesi. ve o türün davranışını belirleyen hep bu sınıf nesnesidir.4 Kopyalama Değişkenleri etkiler. sınıf değişkenlerinin kopyalanarak oluşturulmaları. İlk satırda sağ taraftaki new . this. Sınıf nesnelerine doğrudan erişemeyiz.. O işlem her ikisinin de aynı nesneye erişim sağlamalarına neden olur. Bu işlev yeni bir nesne oluşturmalı ve ona erişim sağlayan bir değişken döndürmelidir: class Sınıf { Yapı yapıNesnesi. Kendisi iş yapmasa da eriştirdiği nesnenin aracısı gibi işlem görür. yapılarda kopya sonrası işlevi olarak öğrendiğimiz this(this) üye işlevi sınıflarda bulunmaz.---+-|-+--▲ | | | | | +--------------------+------------+ 55.3 Sınıf nesneleri ve değişkenleri Sınıf nesnesi ile sınıf değişkeni farklı kavramlardır. Sınıf nesnesi. auto değişken2 = değişken1.Sınıflar 55.dizgi = dizgi. değişken1 ve değişken2 ise yalnızca bu isimsiz nesneye erişim sağlayan değişkenlerdir: (isimsiz BirSınıf nesnesi) değişken1 değişken2 ---+-------------------+--. Bu işleve kopyala() gibi bir isim verebileceğiniz gibi. Yukarıdaki kodda değişken2 . dizilere benzemesi açısından dup() isminin daha uygun olduğunu düşünebilirsiniz. onların hangi nesneye erişim sağlayacaklarını belirler. int tamsayı) { this. Temsil ettiği kavramı gerçekleştiren. auto değişken2 = değişken1.yapıNesnesi = yapıNesnesi. | | o | | o | ---+-------------------+--.1.org 295 . // . int tamsayı.. Sınıf değişkeni ise sınıf nesnesine erişim sağlayan bir program yapısıdır. dchar[] dizgi. Daha önce Değerler ve Referanslar dersinde gördüğümüz şu koda bakalım: auto değişken1 = new BirSınıf.tamsayı = tamsayı. onun işlemlerini yapan.---+---+--. this.

sınıf değişkenlerinin atanmaları. auto değişken1 = new BirSınıf. O işlevden örneğin şöyle yararlanılabilir: auto nesne1 = new Sınıf(Yapı(1. this(dchar şekil) { this. tamsayı).şekil = şekil. asıl nesne ilerideki belirsiz bir zamanda çöp toplayıcı tarafından sonlandırılacak demektir. dizgi. daha önce erişim sağladıkları nesneyi bırakmalarına ve yeni bir nesneye erişim sağlamalarına neden olur. 55.. Atama işleminin davranışı sınıflar için değiştirilemez. auto nesne2 = nesne1. Bırakılan nesne daha sonra çöp toplayıcı tarafından sonlandırılacaktır.1.1. "merhaba"d. 42). Referans türü oldukları için.6 Tanımlama struct yerine class anahtar sözcüğü kullanılır: class SatrançTaşı { // . http://ddili. nesne2 nesne1 'in hiçbir üyesini paylaşmayan ayrı bir nesnedir. yani opAssign sınıflarda yüklenemez. 55. değişken1 = değişken2. class SatrançTaşı { dchar şekil. } dup() içinde oluşturulan yeni nesne için yalnızca dizgi üyesinin dup() ile açıkça kopyalandığına dikkat edin. yapıNesnesi ve tamsayı üyeleri ise değer türleri olduklarından onlar zaten otomatik olarak kopyalanırlar.5). Yukarıdaki son atama işlemi. Eğer bırakılan nesneye erişim sağlayan başka değişken yoksa.5 Atama Değişkenleri etkiler.7 Kurma Kurucu işlevin ismi.dup. Yapılardan farklı olarak sınıf nesneleri {} karakterleri ile kurulamaz. } 55. Sonuçta.dup().org 296 . } } Copyright © 2009-2011 Ali Çehreli.dup. auto değişken2 = new BirSınıf.1. yapılarda olduğu gibi this 'tir..Sınıflar } Sınıf dup() { return new Sınıf(yapıNesnesi. değişken1 'in kendi nesnesini bırakmasına ve değişken2 'nin nesnesine erişim sağlamaya başlamasına neden olur.

türetildiği üst sınıfın kurucusunu çağırmak bu sınıfın görevidir. 55. bu anlam değiştirilemez.. şah).Sınıflar Eğer sınıf başka bir sınıftan türetilmişse. 55. http://ddili. sınıflarda atama işleminin anlamı yeni nesneye erişim sağlamaktır. 55. Bunu bir sonraki derste super anahtar sözcüğünü tanıtırken göstereceğim. Yukarıda atama başlığında anlatıldığı gibi. Onun yerine daha sonra anlatacağım sınıf niteliklerinden yararlanmak daha uygundur. Yukarıdaki SatrançTaşı sınıfında böyle bir işlev tanımlı olmadığı halde o satır derlenir ve bir çıktı üretir: deneme. bazı işlevlerin Object sınıfından kalıtım yoluyla hazır olarak edinilmiş olmalarıdır. !is işleci Copyright © 2009-2011 Ali Çehreli. Not: Üye değişkenlere böyle doğrudan erişilmesi çoğu durumda doğru kabul edilmez.1.8 Sonlandırma Sonlandırıcı işlevin ismi yapılarda olduğu gibi ~this 'tir: ~this() { // . asıl nesnenin üyesidir. Hatırlarsanız. Bir fark. } 55.1. üyelere nokta karakteri ile erişilir.11 Üye işlevler Yapılardaki gibidir. Her ne kadar değişkenin bir üyesine erişiliyor gibi yazılsa da.1. Örneğin toString işlevini kendimiz tanımlamamış olsak bile kullanabiliriz: writefln("%s". bir nesnenin "%s" düzeniyle yazdırılması. İki değişken de aynı nesneye erişim sağlıyorlarsa true .şekil). 55.1. sınıf değişkenlerinin aynı nesneye erişim sağlayıp sağlamadıklarını bildirir.9 Üye erişimi Yapılarda olduğu gibi. Bir fark.10 İşleç yükleme Yapılardaki gibidir.. değilse false değerini üretir. writeln(şah..SatrançTaşı Hemen hemen bütün durumlarda kullanışsız olan bu çıktının override sözcüğü ile nasıl değiştirildiğini bir sonraki derste göstereceğim. onun toString üye işlevini çağırıyordu. auto şah = new SatrançTaşı('♔'). opAssign 'ın sınıflar için özel olarak tanımlanamamasıdır.. nesne sonlanırken gereken işlemler .12 is ve !is işleçleri Sınıf değişkenleri üzerinde işler. is işleci.org 297 .1. erişilen.

nesneler birbirlerinden ayrı iki nesnedir. Bu iki nesnenin aynı şekilde kurulmuş olmaları. http://ddili. o nesneye erişim sağlayan bir sınıf değişkenidir. is işleci bu durumda true üretir. assert(benimŞah !is seninŞah).org 298 . new ile isimsiz bir sınıf nesnesi kurulur. Copyright © 2009-2011 Ali Çehreli. • Sınıflar referans türleridir.2 Özet • Sınıfların yapılarla çok sayıda ortak yanları olduğu kadar büyük farkları da vardır. bu davranış değiştirilemez. Yukarıdaki atama işlemi sonucunda iki değişken de aynı nesneye erişim sağlamaya başlarlar. • Atama. döndürülen. auto seninŞah = new SatrançTaşı('♔'). nesnenin kopyalanabilmesi için dup() gibi bir üye işlev yazılması gerekir.Sınıflar de bunun tersi olarak işler: Aynı nesneye erişim sağlıyorlarsa false . değilse true değerini üretir. Yukarıdaki koddaki benimŞah ve seninŞah değişkenleri new ile oluşturulmuş olan iki farklı nesneye erişim sağladıkları için !is 'in sonucu true 'dur. • Kopyalama normalde değişkeni kopyalar. değişkenin başka bir nesneyi göstermesini sağlar. assert(benimŞah2 is benimŞah). auto benimŞah = new SatrançTaşı('♔'). bu durum is veya !is ile denetlenir (== veya != ile değil). 55. • Hiçbir nesneye erişim sağlamayan sınıf değişkenlerinin değeri null 'dır. İki değişkenin aynı nesneye erişim sağladıkları durumda ise is işleci true üretir: auto benimŞah2 = benimŞah. yani ikisinin şekil üyelerinin de '♔' olması bunu değiştirmez.

Yukarıdaki kodun çıktısı: 20:30:00 Bu kadarına bakarak Saat sınıfının bir yapı olarak da tanımlanabileceğini düşünebiliriz. mevcut başka bir sınıftan türetilerek tanımlanabilir. D'de türeme yalnızca sınıflar arasında geçerlidir. D'de iki tür türeme vardır.dakika. Not: Zaman bilgisini toString üye işlevi ile yazdırmak çok daha uygun olurdu.saat = this.. nesne oluşturulduğu an özel değerler almalarının şart olmadığını varsayalım. int saniye. writefln( "%02s:%02s:%02s". = saniye. void ayarla(int { this. http://ddili. masaSaati. int dakika.saniye). = dakika.dakika this.. ve o yapı için de üye işlevler tanımlayabilirdik. ve onun yerine geçebilir.ayarla(20. Yeni bir sınıf. Bir sınıfın türetildiği türe üst sınıf. bu kadarı yeterli de olabilirdi.Türeme 56 Türeme Daha genel bir türün daha özel bir alt türünü tanımlamaya türetme denir. Bu üç üyeyi bir yapı olarak da bir araya getirebilirdik. 30). ve varsayılan değeri belirtilmiş olduğu için de saniye değerini vermek isteğe bağlı: auto masaSaati = new Saat. arayüz türemesi olan interface 'ten türemeyi ise daha sonraki bir derse bırakacağım. Sınıfın hangi sınıftan türetildiği. O yüzden bu sınıfın kurucu işlevine gerek yok. Copyright © 2009-2011 Ali Çehreli. } Bu sınıfın üyelerinin. daha sonraki bir zamanda ayarla üye işlevi ile ayarlanabiliyor. tanımlanırken isminden sonra yazılan : karakterinden sonra belirtilir: class AltSınıf : ÜstSınıf { // . Türetilen alt tür.saniye } saat. } Masa saati kavramını temsil eden bir sınıf olduğunu varsayalım: class Saat { int saat. genel türün üyelerini edinir. O işlevi biraz aşağıda override anahtar sözcüğünü tanırken ekleyeceğiz.saat. Saat. int dakika. Bu derste gerçekleştirme türemesi olan class 'tan türemeyi göstereceğim. masaSaati. Oysa Saat 'in sınıf olması. Üst sınıfın özelliklerinin alt sınıf tarafından edinilmesine kalıtım denir. int saniye = 0) saat. onun gibi davranır. masaSaati. ondan türetilen yeni sınıfa da alt sınıf adı verilir.org 299 . bize ondan yararlanarak yeni türler tanımlama olanağı sunar. masaSaati. Programa bağlı olarak.

int dakika. Görüldüğü gibi. int saniye = 0) saat.Türeme Örneğin. Bu tanımdaki sarı ile işaretli bölüm. = dakika. başucuSaati. = saniye. int dakika) { alarmSaati = saat. Yeni türün Saat 'ten kalıtım yoluyla edindiği üyeleri de kendi üyeleri haline gelir. ve saati ayarlamak için kullanılan ayarla işlevinin yanında da bir alarmıKur işlevi gerekirdi. ÇalarSaat 'i Saat 'ten türeterek tanımlamak. Bu sınıfı tek başına tanımlamak istesek.org 300 . } void alarmıKur(int saat. bir önceki tanımdaki sarı ile işaretli bölüm yerine geçer. } Saat sınıfında da bulunan üyelerini sarı ile gösterdim. int alarmSaati. } } ÇalarSaat 'in Saat 'ten türetildiği bu tanım öncekinin eşdeğeridir. Saat ve ÇalarSaat sınıflarını aynı program içinde bu şekilde ayrı ayrı tanımlamak oldukça fazla kod tekrarına neden olur.saat = this.saniye } saat. int dakika. ve ek olarak alarm bilgisi de taşıyan bir ÇalarSaat sınıfı düşünebiliriz. alarmDakikası = dakika. Bu sınıf. void alarmıKur(int saat. int alarmDakikası. bir sınıfın üyelerinin başka bir sınıf tarafından oldukları gibi edinilmelerini sağlar. bu anlatıma uygun olarak şöyle gerçekleştirilebilir: class ÇalarSaat { int saat. yeni sınıfı büyük ölçüde kolaylaştırır ve kod tekrarını ortadan kaldırır: class ÇalarSaat : Saat { int alarmSaati. başucuSaati. 0). Saat 'in mevcut üç üyesinin aynılarına ek olarak iki tane de alarm üyesi. Saat 'in bütün üye değişkenlerini ve işlevlerini kalıtım yoluyla edindiği için bir Saat gibi de kullanılabilir: auto başucuSaati = new ÇalarSaat. http://ddili. int alarmDakikası. İşte class 'tan türetmek. ve istendiğinde dışarıdan erişilebilir: Copyright © 2009-2011 Ali Çehreli.alarmıKur(7. alarmDakikası = dakika. int saniye. void ayarla(int { this. 30).dakika this. ÇalarSaat . temelde bu Saat sınıfının olanaklarını olduğu gibi içeren. int dakika) { alarmSaati = saat.ayarla(20.

başucuSaati.saniye.Türeme writefln("%02s:%02s:%02s ♫%02s:%02s". // . Yukarıdaki kodun çıktısı: 20:30:00 ♫07:00 Not: Onun yerine de.. Bu amaçla türetmeyi. ancak türler arasında "bu özel tür.alarmSaati.saat. başucuSaati. Örneğin "çalar saat sesli bir alettir" ilişkisini gerçekleştirmek için ÇalarSaat 'i bir de SesliAlet sınıfından türetmek istesek.. çünkü "saat bir pildir" ifadesi doğru değildir: class Saat : Pil { // . içeren türün bir üyesi olarak tanımlanması doğru olur: class Saat { Pil pil. başucuSaati. } Copyright © 2009-2011 Ali Çehreli. } ← Yanlış tasarım Bunun nedeni. Bunun nedeni. Bu örnekte görüldüğü gibi.1 Uyarı: "o türden" ise türetin Gerçekleştirme türemesinin üye edinme ile ilgili olduğunu gördük.. Saat 'ten kalıtım yoluyla edinilen üyelerin ÇalarSaat 'in parçaları haline gelmiş olmalarıdır. biraz aşağıda gösterilecek olan ÇalarSaat.. o genel türdendir" gibi bir ilişki kurabiliyorsanız düşünün. çünkü "çalar saat bir saattir. başucuSaati. ama bir pil içermesidir. Örneğin Saat sınıfına Pil de eklemek istediğimizi düşünelim. http://ddili.. üye veya üye işlev edinmek amacıyla yapılan türemeye gerçekleştirme türemesi adı verilir. } ← Doğru tasarım 56..alarmDakikası). saatin bir pil olmaması.toString işlevini kullanmak çok daha doğru olur. 56.org 301 .dakika. Türler arasında böyle bir içerme ilişkisi bulunduğunda.2 En fazla bir class 'tan türetilebilir Sınıflar birden çok class 'tan türetilemezler. başucuSaati. hem de kalıtımla edindiği saatle ilgili üyeleri vardır. içerilen türün. derleme hatası ile karşılaşırız: class SesliAlet { // . Çoğu durumda türler arasında bir içerme ilişkisi vardır. Her ÇalarSaat nesnesinin artık hem kendi tanımladığı alarmla ilgili üyeleri." Bazı türler arasında ise böyle bir ilişki yoktur. Yukarıdaki örnek için böyle bir ilişkinin var olduğunu söyleyebiliriz. Pil üyesini türeme yoluyla edinmek uygun olmaz.

Bunu da daha sonra göreceğiz. Saat 'ten edindiği bir üyeye super. 56.. 56. ve | ASCII karakterlerini kullanacağım.3 Sıradüzenin gösterimi Aralarında türeme ilişkisi bulunan türlerin hepsine birden sıradüzen ismi verilir. } Yukarıdaki kodda Kemençe TelliÇalgı 'dan.Türeme class ÇalarSaat : Saat. çünkü yalnızca dakika yazıldığında da üst sınıftaki dakika anlaşılır: class ÇalarSaat : Saat { // . } class Kemençe : TelliÇalgı { // . TelliÇalgı da Çalgı 'dan türetilmiştir. Öte yandan.4 Üst sınıf üyelerine erişmek için super anahtar sözcüğü Alt sınıf içinden üst sınıfın üyelerine erişilmek istendiğinde... Nesneye yönelik programlamada sıradüzenin geleneksel bir gösterimi vardır: üst sınıflar yukarıda ve alt sınıflar aşağıda olacak şekilde gösterilirler. } // ← derleme HATASI interface 'lerden ise istenildiği kadar sayıda türetilebilir. üst sınıfı temsil etmek için super anahtar sözcüğü kullanılır.org 302 . Örneğin ÇalarSaat sınıfının üye işlevlerinin içindeyken.. Sınıflar arasındaki türeme ilişkisi de alt sınıftan üst sınıfa doğru bir okla belirtilir.. Bu tanımda Kemençe . Ama bu şart değildir. SesliAlet { // . sınıfların ne kadar derinlemesine türetildiklerinin bir sınırı yoktur: class Çalgı { // .. TelliÇalgı ve Çalgı özelden genele doğru bir sıradüzen oluştururlar... yeterince anlaşılır olduklarını düşündüğüm için /. void birÜyeİşlev() Copyright © 2009-2011 Ali Çehreli.dakika diye erişilebilir.. \. http://ddili.. } class TelliÇalgı : Çalgı { // . Örneğin yukarıdaki sınıf ilişkisini de içeren bir sıradüzen şöyle gösterilir: Çalgı ↗ ↖ TelliÇalgı NefesliÇalgı ↗ ↖ ↗ ↖ Kemençe Saz Kaval Ney Bundan sonraki gösterimlerde ok yerine.

org 303 . Yukarıdaki Saat ve ÇalarSaat sınıflarının kurucularını tanımlamamıştık. } } // .saniye = saniye. 0).init değerleri ile ilklenirler. Ancak. // .. üst sınıfın kurucusu anlamına da gelir. bu amaçla super yazımıyla çağrılır: class ÇalarSaat : Saat { this(int saat. kullanıcıların bir ÇalarSaat kurdukları durumda..alarmDakikası = alarmDakikası. int dakika. super de üst sınıfın kurucusudur. 56. Artık kullanıcıların Saat nesnelerini bu kurucu ile kurmaları gerektiğini biliyoruz: auto saat = new Saat(17.sıfırla() ve super. this. hem üst sınıfta hem de alt sınıfta aynı isimde üyeler bulunduğu durumlardaki karışıklıkları gidermek için yararlıdır. Üst sınıfın kurucusu.saat = saat.. http://ddili. saniye). ve hatırlarsanız o değer int için sıfırdır.. int dakika. alt sınıfın görevidir. // Saat'ten edindiği dakika değişir dakika = 10. Bunu biraz aşağıdaki super.dakika = dakika. Bu yüzden. this. Saat 'in kurucusunu basitçe şöyle tanımlamış olalım: class Saat { this(int saat. Kullanıcının amacı yalnızca alt sınıfı kurmak ve o tür olarak kullanmaktır: auto başucuSaati = new ÇalarSaat(/* . 15..Türeme { } super. her ikisinin üyeleri de kendi . // . int alarmDakikası) // ÇalarSaat için { super(saat.. Hatta kullanıcılar bazı durumlarda ÇalarSaat 'in bir Saat 'ten türediğini bile bilmek zorunda değillerdir. this. // Saat için int alarmSaati.. onun türemeyle edindiği Saat parçasını açıkça kurmaları olanaksızdır. aynı şey } super 'in bu kullanımı. } Copyright © 2009-2011 Ali Çehreli.toString() kullanımlarında göreceğiz.alarmSaati = alarmSaati. Bu kullanımda. Bu yüzden. Bir Saat nesnesinin öyle tek başına kurulması doğaldır. Alt sınıfın kurucusundan üst sınıfın kurucusunu çağırmak için kullanılır. kalıtımla edindiği üst sınıf parçasını kurmak. this nasıl bu sınıfın kurucusu ise. this. bir ÇalarSaat olarak kullan . int saniye.. */)..5 Üst sınıf üyelerini kurmak için super anahtar sözcüğü super anahtar sözcüğü. dakika..dakika = 10. int saniye) { this.

Türeme

}

// ...

ÇalarSaat 'in kurucusu; hem kendisi için gereken alarmla ilgili bilgileri, hem de üst sınıfı için gereken saat bilgilerini parametre olarak almakta, ve Saat 'in üyelerini super 'i çağırarak kurmaktadır. Eğer üst sınıfın bir otomatik kurucusu varsa, alt sınıfın super 'i çağırması şart değildir. Çünkü o durumda üst sınıfın üyeleri varsayılan değerleriyle zaten otomatik olarak ilklenirler.

56.6

Üye işlevleri override ile özel olarak tanımlamak
Türemenin önemli bir yararı, üst sınıfta bulunan işlevlerin alt sınıf tarafından özel olarak yeniden tanımlanabilmesidir. override , bu kullanımda "hükümsüz kılmak, bastırmak" anlamına gelir. Alt sınıf, üst sınıfın işlevini kendisine uygun olacak şekilde yeniden tanımlayabilir. Saat 'in sıfırla isminde bir üye işlevi olduğunu düşünelim. Bu işlev bütün üyelerin değerlerini sıfırlıyor olsun: class Saat { void sıfırla() { saat = 0; dakika = 0; saniye = 0; } } // ...

Hatırlayacağınız gibi, aynı işlev kalıtım yoluyla ÇalarSaat tarafından da edinilir ve onun nesneleri ile de kullanılabilir: auto başucuSaati = new ÇalarSaat(20, 30, 0, 7, 0); // ... başucuSaati.sıfırla(); Ancak, Saat 'in bu sıfırla işlevinin alarmla ilgili üyelerden haberi yoktur; o, yalnızca kendi sınıfının üyeleri ile ilgili olabilir. Bu yüzden, alt sınıfın üyelerinin de sıfırlanabilmeleri için; üst sınıftaki sıfırla işlevinin bastırılması, ve alt sınıfta yeniden tanımlanması gerekir: class ÇalarSaat : Saat { override void sıfırla() { super.sıfırla(); alarmSaati = 0; alarmDakikası = 0; } } // ...

Alt sınıfın yalnızca kendi üyelerini sıfırladığına, ve üst sınıfın işini super.sıfırla() çağrısı yoluyla üst sınıfa havale ettiğine dikkat edin.

Copyright © 2009-2011 Ali Çehreli, http://ddili.org

304

Türeme

Yukarıdaki kodda super.sıfırla() yerine yalnızca sıfırla() yazamadığımıza da ayrıca dikkat edin. Eğer yazsaydık, ÇalarSaat sınıfı içinde bulunduğumuz için öncelikle onun işlevi anlaşılırdı, ve içinde bulunduğumuz bu işlev tekrar tekrar kendisini çağırırdı. Sonuçta da program sonsuz döngüye girer, bir bellek sorunu yaşar, ve çökerek sonlanırdı. toString 'in tanımını bu noktaya kadar geciktirmemin nedeni, her sınıfın bir sonraki derste anlatacağım Object isminde bir sınıftan otomatik olarak türemiş olması ve Object 'in zaten bir toString işlevi tanımlamış olmasıdır. Bu yüzden, bir sınıfın toString işlevinin tanımlanabilmesi için override anahtar sözcüğünün de kullanılması gerekir: class Saat { override string toString() const { return format("%02s:%02s:%02s", saat, dakika, saniye); } } // ...

class ÇalarSaat : Saat { override string toString() const { return format("%s ♫%02s:%02s", super.toString(), alarmSaati, alarmDakikası); } } // ...

ÇalarSaat 'in işlevinin Saat 'in işlevini super.toString() diye çağırdığına dikkat edin. Artık ÇalarSaat nesnelerini de dizgi olarak ifade edebiliriz: void main() { auto masaSaatim = new ÇalarSaat(10, 15, 0, 6, 45); writeln(masaSaatim); } Çıktısı: 10:15:00 ♫06:45

56.7

Alt sınıf nesnesi, üst sınıf nesnesi yerine geçebilir
Üst sınıf daha genel, ve alt sınıf daha özel olduğu için; alt sınıf nesneleri üst sınıf nesneleri yerine geçebilirler. Buna çok şekillilik denir. Bu genellik ve özellik ilişkisini "bu tür o türdendir" gibi ifadelerde görebiliriz: "çalar saat bir saattir", "öğrenci bir insandır", "kedi bir omurgalı hayvandır", vs. Bu ifadelere uygun olarak; saat gereken yerde çalar saat, insan gereken yerde öğrenci, omurgalı hayvan gereken yerde de kedi kullanılabilir. Üst sınıfın yerine kullanılan alt sınıf nesneleri kendi türlerini kaybetmezler. Nasıl normal hayatta bir çalar saatin bir saat olarak kullanılması onun aslında bir çalar saat olduğu gerçeğini değiştirmiyorsa, türemede de değiştirmez. Alt sınıf kendisi gibi davranmaya devam eder.

Copyright © 2009-2011 Ali Çehreli, http://ddili.org

305

Türeme

Elimizde Saat nesneleri ile çalışabilen bir işlev olsun. Bu işlev, kendi işlemleri sırasında bu verilen saati de sıfırlıyor olsun: void kullan(Saat saat) { // ... saat.sıfırla(); // ... } Çok şekilliliğin yararı böyle durumlarda ortaya çıkar. Yukarıdaki işlev Saat türünden bir parametre beklediği halde, onu bir ÇalarSaat nesnesi ile de çağırabiliriz: auto masaSaatim = new ÇalarSaat(10, 15, 0, 6, 45); writeln("önce : ", masaSaatim); kullan(masaSaatim); writeln("sonra: ", masaSaatim); kullan işlevi, masaSaatim nesnesini bir ÇalarSaat olmasına rağmen kabul eder, ve bir Saat gibi kullanır. Bu, aralarındaki türemenin "çalar saat bir saattir" ilişkisini oluşturmuş olmasındandır. Sonuçta, masaSaatim nesnesi sıfırlanmıştır: önce : 10:15:00 ♫06:45 sonra: 00:00:00 ♫00:00 Burada dikkatinizi çekmek istediğim önemli nokta, yalnızca saat bilgilerinin değil, alarm bilgilerinin de sıfırlanmış olmasıdır. kullan işlevinde bir Saat 'in sıfırla işlevinin çağrılıyor olmasına karşın; asıl nesne bir ÇalarSaat olduğu için, o türün özel sıfırla işlevi çağrılır ve onun tanımı gereği hem saatle ilgili üyeleri, hem de alarmla ilgili üyeleri sıfırlanır. masaSaatim 'in kullan işlevine bir Saat 'miş gibi gönderilebilmesi, onun asıl türünde bir değişiklik yapmaz. Görüldüğü gibi, kullan işlevi bir Saat nesnesi kullandığını düşündüğü halde, elindeki nesnenin asıl türüne uygun olan sıfırla işlevi çağrılmıştır. Saat sıradüzenine bir sınıf daha ekleyelim. Sıfırlanmaya çalışıldığında üyelerinin rasgele değerler aldığı BozukSaat sınıfı: import std.random; class BozukSaat : Saat { this() { super(0, 0, 0); } override void sıfırla() { saat = uniform(0, 24); dakika = uniform(0, 60); saniye = uniform(0, 60); }

}

O türün parametre kullanmadan kurulabilmesi için parametresiz bir kurucu işlev tanımladığına da dikkat edin. O kurucunun tek yaptığı, kendi sorumluğunda bulunan üst sınıfını kurmaktır.

Copyright © 2009-2011 Ali Çehreli, http://ddili.org

306

Türeme

kullan işlevine bu türden bir nesne gönderdiğimiz durumda da bu türün özel sıfırla işlevi çağrılır. Çünkü bu durumda da kullan içindeki saat parametresinin asıl türü BozukSaat 'tir: auto raftakiSaat = new BozukSaat; kullan(raftakiSaat); writeln(raftakiSaat); BozukSaat 'in kullan içinde sıfırlanması sonucunda oluşan rasgele saat değerleri: 22:46:37

56.8

Türeme geçişlidir
Sınıfların birbirlerinin yerine geçmeleri yalnızca türeyen iki sınıfla sınırlı değildir. Alt sınıflar, üst sınıflarının türedikleri sınıfların da yerine geçerler. Yukarıdaki Çalgı sıradüzenini hatırlayalım: class Çalgı { // ... } class TelliÇalgı : Çalgı { // ... } class Kemençe : TelliÇalgı { // ... } Oradaki türemeler şu iki ifadeyi gerçekleştirirler: "telli çalgı bir çalgıdır" ve "kemençe bir telli çalgıdır". Dolayısıyla, "kemençe bir çalgıdır" da doğru bir ifadedir. Bu yüzden, Çalgı beklenen yerde Kemençe de kullanılabilir. Gerekli türlerin ve üye işlevlerin tanımlanmış olduklarını varsayarsak: void güzelÇal(Çalgı çalgı, Parça parça) { çalgı.akordEt(); çalgı.çal(parça); } // ... auto kemençem = new Kemençe; güzelÇal(kemençem, doğaçlama); güzelÇal işlevi bir Çalgı beklediği halde, onu bir Kemençe ile çağırabiliyoruz; çünkü geçişli olarak "kemençe bir çalgıdır".

56.9

Sıradüzen daha kapsamlı olabilir
Türeme yalnızca iki sınıfla sınırlı değildir. Eldeki probleme bağlı olarak, ve her sınıfın tek bir class 'tan türeyebileceği kuralına uyulduğu sürece, sıradüzen gerektiği kadar kapsamlı olabilir.

Copyright © 2009-2011 Ali Çehreli, http://ddili.org

307

Türeme

Bir örnek olarak, std.stream.File sınıfının bağlı olduğu sıradüzene bakalım (bu eski arayüz ve sınıflar artık Phobos'a dahil değiller): InputStream OutputStream (interface) (interface) \ / CFile --> Stream <-- SocketStream / | | \ MemoryStream | File \ | TArrayStream FilterStream / | \ / BufferedStream \ EndianStream | SliceStream BufferedFile Doğal olarak, hiçbir programcının bu sıradüzeni ezbere bilmesi gerekmez. Yalnızca bir fikir vermesi açısından gösteriyorum. Yukarıdaki sıradüzende her sınıfın tek bir class 'tan türediğine dikkat edin. Üstünde iki tür görünen tek sınıf Stream 'dir, çünkü türetildiği iki tür de interface 'tir.

56.10

Soyut üye işlevler ve soyut sınıflar
Bazen; bir sınıfta bulunmasının doğal olduğu, ama o sınıfın kendisinin tanımlayamadığı işlevlerle karşılaşılabilir. Somut bir gerçekleştirmesi bulunmayan bu işleve bu sınıfın bir soyut işlevi denir. En az bir soyut işlevi bulunan sınıflara da soyut sınıf ismi verilir. Örneğin satranç taşlarını ifade eden bir sıradüzende SatrançTaşı sınıfının taşın hamlesinin yasal olup olmadığını sorgulamaya yarayan yasal_mı isminde bir işlevi olduğunu varsayalım. Böyle bir sıradüzende bu üst sınıf, taşın hangi karelere ilerletilebileceğini bilemiyor olabilir; her taşın hareketi, onunla ilgili olan alt sınıf tarafından biliniyordur: piyonun hareketini Piyon sınıfı biliyordur, şahın hareketini Şah sınıfı, vs. abstract anahtar sözcüğü, o üye işlevin bu sınıfta gerçekleştirilmediğini, ve alt sınıflardan birisinde gerçekleştirilmesinin şart olduğunu bildirir: class SatrançTaşı { abstract bool yasal_mı(in Kare nereden, in Kare nereye); } Görüldüğü gibi; o işlev o sınıfta tanımlanmamış, yalnızca abstract olarak bildirilmiştir. Soyut sınıf türlerinin nesneleri oluşturulamaz: auto taş = new SatrançTaşı; // ← derleme HATASI

Bunun nedeni, eksik işlevi yüzünden bu sınıfın kullanılamaz durumda bulunmasıdır. Çünkü; eğer oluşturulabilse, taş.yasal_mı(buKare, şuKare) gibi bir çağrının sonucunda ne yapılacağı bu sınıf tarafından bilinemez. Öte yandan, bu işlevin tanımını veren alt sınıfların nesneleri oluşturulabilir; çünkü alt sınıf bu işlevi kendisine göre tanımlamıştır ve işlev çağrısı sonucunda ne yapılacağı böylece bilinir: class Piyon : SatrançTaşı { override bool yasal_mı(in Kare nereden, in Kare nereye) { // ... işlevin piyon tarafından gerçekleştirilmesi ...

Copyright © 2009-2011 Ali Çehreli, http://ddili.org

308

Türeme

}

}

return karar;

Bu işlevin tanımını da sunduğu için bu alt sınıftan nesneler oluşturulabilir: auto taş = new Piyon; // derlenir

56.11

Örnek
Bir örnek olarak demir yolunda ilerleyen araçlarla ilgili bir sıradüzene bakalım. Bu örnekte sonuçta şu sıradüzeni gerçekleştirmeye çalışacağız: DemirYoluAracı { ilerle() } / | \ \ Drezin | Vagon Lokomotif | Tren { lokomotif, vagonlar, indir()?, bindir()? } / \ YükTreni YolcuTreni { kondüktörler } Her sınıfın kendisinin tanımladığı üyeleri gri renkle gösterdim. Soyut olan iki işlevi de kırmızı soru işaretiyle belirttim. Aşağıda bu sınıfları ve üyelerini en üstten en alta doğru teker teker göstereceğim. Burada amacım yalnızca sınıf ve sıradüzen tasarımlarını göstermek olduğu için, hiç ayrıntıya girmeyeceğim ve yalnızca gerektiği kadar kod yazacağım. O yüzden aşağıdaki işlevlerde gerçek işler yapmak yerine yalnızca çıkışa mesaj yazdırmakla yetineceğim. Yukarıdaki tasarımdaki en genel araç olan DemirYoluAracı , yalnızca ilerleme işiyle ilgilenecek şekilde tasarlanmış. Genel olarak bir demir yolu aracı olarak kabul edilebilmek için bu tasarımda bundan daha fazlası da gerekmiyor. O sınıfı şöyle tanımlayabiliriz: class DemirYoluAracı { void ilerle(in int kilometre) { writeln(kilometre, " kilometre ilerliyorum"); } } DemirYoluAracı 'ndan türetilebilecek en basit araç, insan gücüyle giden drezin olabilir. Bu sınıfa özel bir üye eklemeye şimdilik gerek duymuyorum: class Drezin : DemirYoluAracı {} Sırada, en yaygın demir yolu aracı olan tren var. Bir trenin lokomotifinin ve bazı vagonlarının olması doğaldır. Önce bu yardımcı sınıfları tanımlayalım: class Lokomotif : DemirYoluAracı {} class Vagon : DemirYoluAracı {} Treni temsil eden sınıfı bir lokomotif ve bir dizi vagon olarak şöyle tanımlayabiliriz:

Copyright © 2009-2011 Ali Çehreli, http://ddili.org

309

Türeme

class Tren : DemirYoluAracı { Lokomotif lokomotif; Vagon[] vagonlar; } // ...

Burada çok önemli bir konuya dikkatinizi çekmek istiyorum. Her ne kadar Lokomotif ve Vagon demir yolu araçları olsalar da, trenin onlardan türetilmesi doğru olmaz. Yukarıda değindiğimiz kuralı hatırlayalım: sınıfların türemeleri için, "bu özel tür, o genel türdendir" gibi bir ilişki bulunması gerekir. Oysa tren ne bir lokomotiftir, ne de vagondur. Tren, onları içerir. Bu yüzden lokomotif ve vagon kavramlarını trenin üyeleri olarak tanımladık. Trenle ilgili başka bir durumu daha düşünmemiz gerekiyor: Her trenin indirme ve bindirme işlemlerini desteklemesi gerekir. Kullanım amacı gereği, her tren mal veya yolcu taşır. Bu genel tanıma uygun olarak bu sınıfa iki tane de işlev ekleyelim: class Tren : DemirYoluAracı { Lokomotif lokomotif; Vagon[] vagonlar; abstract void indir(); abstract void bindir();

}

Gördüğünüz gibi, bu işlevleri soyut olarak tanımlamak zorunda kaldık. Çünkü trenin indirme ve bindirme işlemleri sırasında tam olarak ne yapılacağı, o trenin türüne bağlıdır. Yolcu trenlerinin indirme işlemi, vagon kapılarının açılması ve yolcuların trenden çıkmalarını beklemek kadar basittir. Yük trenlerinde ise yük taşıyan görevlilere ve belki de vinç gibi bazı araçlara gerek duyulabilir. Bu yüzden, indir ve bindir işlevlerinin sıradüzenin bu aşamasında soyut olmaları gerekir. Soyut Tren sınıfının soyut işlevlerini gerçekleştirmek, ondan türeyen somut iki sınıfın görevidir: class YükTreni : Tren { override void indir() { writeln("mal boşalıyor"); } override void bindir() { writeln("mal yükleniyor"); }

}

Bu iki somut tür arasında başka farklar da olabileceğini göstermek için YolcuTreni 'ne kondüktörlerini ifade eden bir de dizi ekleyelim: class Kondüktör {} class YolcuTreni : Tren { Kondüktör[] kondüktörler; override void indir() {

Copyright © 2009-2011 Ali Çehreli, http://ddili.org

310

Türeme

}

writeln("yolcular iniyor");

}

override void bindir() { writeln("yolcular biniyor"); }

Soyut bir sınıf olması, Tren 'in kullanılamayacağı anlamına gelmez. Tren sınıfının nesneleri oluşturulamaz, ama Tren sınıfı bir arayüz olarak kullanılabilir. Örneğin parametre olarak Tren alan şu işlev, trenlerin duraktaki işleriyle ilgileniyor olsun: void duraktaAğırla(Tren tren) { tren.indir(); tren.bindir(); } Yukarıdaki türetmeler "yük treni bir trendir" ve "yolcu treni bir trendir" ilişkilerini gerçekleştirdikleri için, bu iki sınıfı Tren yerine kullanabiliyoruz. Bu programda geriye kalan, std.stdio modülünün eklenmesi ve bu sınıfları kullanan bir main işlevinin yazılmasıdır: import std.stdio; void main() { auto bizimEkspres = new YolcuTreni; bizimEkspres.ilerle(30); duraktaAğırla(bizimEkspres); auto sebzeTreni = new YükTreni; sebzeTreni.ilerle(400); duraktaAğırla(sebzeTreni);

}

O kod, farklı türden iki nesne oluşturur: YolcuTreni ve YükTreni . Bu iki sınıf, o kodda iki değişik arayüzle kullanılmaktadır: 1. ilerle işlevinin çağrıldığı noktalarda, bu nesneler DemirYoluAracı olarak kullanılmaktadırlar; çünkü o işlev o sınıf düzeyinde bildirilmiştir 2. duraktaAğırla işlevi, nesneleri bir Tren olarak kullanmaktadır; çünkü nesneler işlevin parametresine Tren olarak gönderilirler Çıktılarını karşılaştıralım: 30 kilometre ilerliyorum yolcular iniyor yolcular biniyor 400 kilometre ilerliyorum mal boşalıyor mal yükleniyor ilerle işlevinin her ikisi için de aynı şekilde çalıştığına dikkat edin. Öte yandan, duraktaAğırla işlevinin çağırdığı indir ve bindir üye işlevleri, her ikisinin kendi asıl türlerinin tanımladığı şekilde çalışmıştır.

Copyright © 2009-2011 Ali Çehreli, http://ddili.org

311

Türeme

56.12

Özet
• • • • • Türeme, "bu tür o türdendir" ilişkisi içindir Tek bir class 'tan türetilebilir super 'in iki anlamı vardır: üst sınıfın kurucusu, ve üst sınıfa erişim override , üst sınıfın işlevini bu sınıf için özel olarak tanımlar abstract , soyut işlevin alt sınıfta tanımlanmasını şart koşar

56.13

Problemler
1. Yukarıdaki sıradüzenin en üst sınıfı olan DemirYoluAracı 'nı değiştirelim. Kaç kilometre ilerlediğini bildirmek yanında, her kilometre için bir de ses çıkartsın: class DemirYoluAracı { void ilerle(in int kilometre) { write(kilometre, " kilometre: "); foreach (i; 0 .. kilometre) { write(ses(), ' '); } } writeln();

}

Ancak, ses işlevi DemirYoluAracı sınıfında tanımlanamasın; çünkü her aracın kendi özel sesi olsun: ◦ insan gücüyle çalışan drezin için "of puf" ◦ vagon için "takıtak tukutak" ◦ lokomotif için "çuf çuf" Not: Diğer sınıfları şimdilik bir sonraki soruya bırakın. Her aracın ayrı sesinin olabilmesi için, ses işlevinin üst sınıfta soyut olarak bildirilmesi gerekir: class DemirYoluAracı { // ... } abstract string ses();

ses işlevini alt sınıflar için gerçekleştirin ve şu main ile deneyin: void main() { auto drezin = new Drezin; drezin.ilerle(2); auto vagon = new Vagon; vagon.ilerle(3); auto lokomotif = new Lokomotif; lokomotif.ilerle(4);

}

Bu programın aşağıdaki çıktıyı vermesini sağlayın:

Copyright © 2009-2011 Ali Çehreli, http://ddili.org

312

Türeme

2 kilometre: of puf of puf 3 kilometre: takıtak tukutak takıtak tukutak takıtak tukutak 4 kilometre: çuf çuf çuf çuf çuf çuf çuf çuf 4. ses işlevini Tren , YükTreni , ve YolcuTreni sınıfları için nasıl tanımlayabileceğinizi düşünün. Bir fikir: ses işlevini, yalnızca bunların en üstündeki sınıf olan Tren 'de tanımlamak isteyebilirsiniz. Hatta, üç tren türünün sesinin de lokomotiften kaynaklanacağını varsayarsak; bu işlevi, doğrudan lokomotifin sesini döndürecek şekilde yazabilirsiniz: class Tren : DemirYoluAracı { Lokomotif lokomotif; Vagon[] vagonlar; abstract void indir(); abstract void bindir(); override string ses() { return lokomotif.ses(); }

}

Ne yazık ki, yukarıdaki sınıfı şu şekilde denediğinizde programın çöktüğünü göreceksiniz: auto tren = new YolcuTreni; tren.ilerle(1);

Segmentation fault

← Program çöker

Neden? Hatayı giderin ve o kodun beklenen şu çıktıyı vermesini sağlayın: 1 kilometre: çuf çuf ... çözümler

Copyright © 2009-2011 Ali Çehreli, http://ddili.org

313

Object

57

Object
Açıkça başka bir sınıftan türetilmeyen sınıflar, otomatik olarak Object adlı sınıftan türerler. Sıradüzenin en üstündeki sınıf Object 'ten otomatik olarak türer: class Çalgı : Object { // ... } class TelliÇalgı : Çalgı { // ... } // ": Object" yazılmaz; otomatiktir

// dolaylı olarak Object'ten türer

En üstteki sınıf Object 'ten türediği için, altındaki bütün sınıflar da dolaylı olarak Object 'ten türerler. Bu anlamda "her sınıf, Object türündendir". Bu türeme sonucunda her sınıf Object 'in bazı üye işlevlerini edinir: • • • • toString : nesnenin dizgi olarak ifadesi opEquals : eşitlik karşılaştırması opCmp : sıra karşılaştırması toHash : eşleme tablosu indeks değeri

Bu işlevlerden son üçü sınıf nesnelerinin değerlerini ön plana çıkartmak ve en azından onları eşleme tablolarında indeks türü olarak kullanmak için gereklidir. Türeme yoluyla edinildikleri için, bu işlevlerin türeyen tür için override anahtar sözcüğü ile tanımlanmaları gerekir. Not: Object 'ten edinilen başka üyeler de vardır; onları burada anlatmayacağım.

57.1

toString
Yapılarda olduğu gibi, ve bir önceki derste de gördüğümüz gibi, nesnenin dizgi olarak kullanılabilmesini sağlar: auto saat = new Saat(20, 30, 0); writeln(saat); // Object.toString'i çağırır Sınıfın Object 'ten kalıtım yoluyla edindiği toString işlevi fazla kullanışlı değildir; döndürdüğü string yalnızca türün ismini içerir: deneme.Saat Sınıfın isminden önceki bölüm, yani yukarıdaki deneme , o sınıfı içeren modülün ismini belirtir. Ona bakarak, Saat sınıfının deneme.d isimli bir kaynak dosya içinde tanımlandığını anlayabiliriz. Daha anlamlı ve daha uygun bir string üretmesi için bu işlevi hemen hemen her zaman kendimiz tanımlamak isteriz: import std.string; class Saat { override string toString() const

Copyright © 2009-2011 Ali Çehreli, http://ddili.org

314

Object

{ } }

return format("%02s:%02s:%02s", saat, dakika, saniye);

// ...

class ÇalarSaat : Saat { override string toString() const { return format("%s ♫%02s:%02s", super.toString(), alarmSaati, alarmDakikası); } } // ...

// ... auto başucuSaati = new ÇalarSaat(20, 30, 0, 7, 0); writeln(başucuSaati); Yukarıdaki ÇalarSaat.toString ; kendi üyelerini doğrudan kullanıyor ve üst sınıfın üyeleri için üst sınıfın toString işlevinden yararlanıyor. Çıktısı: 20:30:00 ♫07:00

57.2

opEquals
İşleç Yükleme dersinde gördüğümüz gibi, bu üye işlev == işlecinin tanımını belirler. İşlevin dönüş değeri nesneler eşitlerse true , değillerse false olmalıdır. Uyarı: Bu işlevin opCmp ile tutarlı olması gerekir; true döndürdüğü durumda opCmp da sıfır döndürmelidir. == işlecine karşılık öncelikle dört adımlı bir algoritma işletilir. Programcının tanımlamış olduğu opEquals ancak bazı durumlarda çağrılır: bool opEquals(Object a, Object b) { if (a is b) return true; if (a is null || b is null) return false; if (typeid(a) == typeid(b)) return a.opEquals(b); return a.opEquals(b) && b.opEquals(a); } 1. 2. 3. 4.

// // // //

(1) (2) (3) (4)

İki değişken de aynı nesneye erişim sağlıyorlarsa (veya ikisi de null iseler) eşittirler. Yalnızca birisi null ise eşit değildirler. Her iki nesne de aynı türden iseler a.opEquals(b) işletilir. Aksi taktirde eşit olarak kabul edilebilmeleri için eğer tanımlanmışlarsa hem a.opEquals(b) 'nin hem de b.opEquals(a) 'nın true üretmesi gerekir.

opEquals programcı tarafından özellikle tanımlanmamışsa sınıf nesnelerinin değerlerine bakılmaz, iki sınıf değişkeninin aynı nesneye erişim sağlayıp sağlamadıklarına bakılır: auto değişken0 = new Saat(6, 7, 8); auto değişken1 = new Saat(6, 7, 8); assert(değişken0 != değişken1); // eşit değiller // (çünkü farklı nesneler)

Copyright © 2009-2011 Ali Çehreli, http://ddili.org

315

Object

Yukarıdaki koddaki iki nesne aynı parametrelerle kuruldukları halde, new ile ayrı ayrı kurulmuş oldukları için iki farklı nesnedir. Bu yüzden; onlara erişim sağlayan değişken0 ve değişken1 değişkenleri, Object 'in gözünde eşit değillerdir. Öte yandan, aynı nesneye erişim sağladıkları için şu iki değişken eşittir: auto ortak0 = new Saat(9, 10, 11); auto ortak1 = ortak0; assert(ortak0 == ortak1); // eşitler // (çünkü aynı nesne)

Bazen nesneleri böyle kimliklerine göre değil, değerlerine göre karşılaştırmak isteriz. Örneğin değişken0 'ın ve değişken1 'in erişim sağladıkları nesnelerin değerlerinin eşit olmalarına bakarak, == işlecinin true üretmesini bekleyebiliriz. Yapılardan farklı olarak, ve Object 'ten kalıtımla edinildiği için, opEquals işlevinin parametresi Object 'tir. O yüzden, bu işlevi kendi sınıfımız için tanımlarken parametresini Object olarak yazmamız gerekir: class Saat { override bool opEquals(Object o) const { // ... } } // ...

Kendimiz Object olarak doğrudan kullanmayacağımız için bu parametrenin ismini kullanışsız olarak o diye seçmekte bir sakınca görmüyorum. İlk ve çoğu durumda da tek işimiz, onu bir tür dönüşümünde kullanmak olacak. opEquals 'a parametre olarak gelen nesne, kod içinde == işlecinin sağ tarafında yazılan nesnedir. Örneğin şu iki satır birbirinin eşdeğeridir: değişken0 == değişken1; değişken0.opEquals(değişken1);

// aynısı

Bu işleçteki amaç bu sınıftan iki nesneyi karşılaştırmak olduğu için, işlevi tanımlarken yapılması gereken ilk şey, parametre olarak gelen Object 'in türünü kendi sınıfımızın türüne dönüştürmektir. Sağdaki nesneyi değiştirmek gibi bir niyetimiz de olmadığı için, tür dönüşümünde const belirtecini kullanmak da uygun olur: override bool opEquals(Object o) const { auto sağdaki = cast(const Saat)o; } // ...

Yukarıdaki tür dönüşümü işlemi ya sağdaki 'nin türünü bu şekilde const Saat olarak belirler ya da dönüşüm uyumsuzsa null üretir. Burada karar verilmesi gereken önemli bir konu, sağdaki nesnenin türünün bu nesnenin türü ile aynı olmadığında ne olacağıdır. Sağdaki nesnenin tür dönüşümü sonucunda null üretmesi, sağdaki nesnenin aslında bu türe dönüştürülemediği anlamına gelir.

Copyright © 2009-2011 Ali Çehreli, http://ddili.org

316

8). assert(değişken0 == değişken1). override bool opEquals(Object o) const { auto sağdaki = cast(const Saat)o.. http://ddili. return (sağdaki && (saat == sağdaki. int alarmDakikası.alarmDakikası)). Örneğin alt sınıf olan ÇalarSaat 'in nesnelerini karşılaştırırken. Zaten null olduğu durumda sağdaki 'nin üyelerine erişmek hatalıdır: class Saat { int saat.org 317 . auto değişken1 = new Saat(6.. 8). } } // . return (sağdaki && (super == o) && (alarmSaati == sağdaki.saat) && (dakika == sağdaki.. override bool opEquals(Object o) const { auto sağdaki = cast(const ÇalarSaat)o. Oradaki ifade super 'in opEquals işlevini çağırır ve eşitlik kararında onun da sonucunu kullanmış olur. ve >= işleçlerinin tanımı için perde arkasında bu işlev kullanılır. 57. 7.alarmSaati) && (alarmDakikası == sağdaki. Saat 'ten kalıtımla edindiği parçaları da karşılaştırmak anlamlı olur: class ÇalarSaat : Saat { int alarmSaati. < .Object Ben nesnelerin eşit kabul edilebilmeleri için bu dönüşümün başarılı olması gerektiğini varsayacağım. int dakika. 7.3 opCmp Sınıf nesnelerini sıralamak için kullanılır. Copyright © 2009-2011 Ali Çehreli. } } // . İşlevin bu tanımı sayesinde. > . üst sınıfın üyelerini de unutmamak gerekir. int saniye. eğer varsa ve nesnelerin eşit kabul edilmeleri için gerekliyse.dakika) && (saniye == sağdaki. <= . // artık eşitler // (çünkü değerleri aynı) opEquals 'ı tanımlarken. Bu yüzden eşitlik karşılaştırmalarında öncelikle sağdaki 'nin null olmadığına bakacağım. == işleci Saat nesnelerini artık değerlerine göre karşılaştırır: auto değişken0 = new Saat(6.saniye))..

7. if-else-if zinciri yerine onun eşdeğeri olan üçlü işleci de kullanabilirsiniz: Copyright © 2009-2011 Ali Çehreli. onlar da eşitlerse saniyelere bakıyor.org 318 . Eğer daha uygun bulursanız.dakika. Yukarıdaki tanım. override int opCmp(Object o) const { /* Türler aynı olmadıklarında türlerin genel * sıralamasından yararlanıyoruz.sağdaki. bu işlevin Object sınıfından kalıtımla edinilen bir davranışı yoktur.saniye. toString 'in ve opEquals 'un aksine.. sıfır döndürdüğü durumda opEquals da true döndürmelidir. } else if (dakika != sağdaki. 7. int dakika.opCmp(typeid(o)).dakika) { return dakika . // ← Program çöker object.saat) { return saat . } else { return saniye . assert(değişken0 < değişken1).saat. Tanımlanmadan kullanılırsa hata atılır: auto değişken0 = new Saat(6. nesneleri sıralama amacıyla karşılaştırırken öncelikle türlerinin uyumlu olup olmadıklarına bakıyor. int saniye. ikisi eşit olduklarında sıfır döndürmelidir. http://ddili.Exception: need opCmp for class deneme.Saat Yukarıda opEquals için söylenenler bu işlev için de geçerlidir: Sağdaki nesnenin türünün bu nesnenin türüne eşit olmadığı durumda hangisinin daha önce sıralanması gerektiği konusuna bir şekilde karar vermek gerekir. 8). if (saat != sağdaki. 8). Uyarı: Bu işlevin opEquals ile tutarlı olması gerekir. */ if (typeid(this) != typeid(o)) { return typeid(this). auto değişken1 = new Saat(6. sağdaki nesne önce olduğunda artı bir değer. Bunun en kolayı bu kararı derleyiciye bırakmaktır. çünkü derleyici türler arasında zaten genel bir sıralama belirler.sağdaki. saatler eşitlerse dakikalara. Eğer uyumlu iseler saat bilgisini dikkate alıyor. } auto sağdaki = cast(const Saat)o. typeid 'lerinin opCmp işlevinden yararlanmaktır: class Saat { int saat. Türler aynı olmadıklarında bu sıralamadan yararlanmanın yolu.Object Bu işlevin dönüş değerini < işleci üzerinde düşünebilirsiniz: Soldaki nesne önce olduğunda eksi bir değer..sağdaki. bu işlevin bu gibi karşılaştırmalarda daha güzel veya daha etkin bir yazımı yoktur. } } } // . Ne yazık ki.

O kodda diğer bütün üyeleri eşit olduğu için.sağdaki.org 319 . return (saat != sağdaki. rasgele saat değerleriyle kurulan ÇalarSaat nesnelerini içeren bir diziyi sıralayalım. 0.sağdaki. iki nesnenin sıraları ile ilgili yeterli bilgi edinilmiştir.saniye)). if (üstSonuç != 0) { return üstSonuç. Sınıfların yalnızca bu programda kullanılan işlevlerini gösteriyorum: import std. Yukarıdaki kodda. Bu işlev yalnızca kendi yazdığımız kodlarda kullanılmak için değildir. } } Üst sınıfın sıfırdan farklı bir değer döndürmesi durumunda.sort ile sıralanmalarında.sağdaki.string. http://ddili. Programda kullandığımız kütüphaneler ve dil olanakları da bu işlevi çağırabilir. 6. Örneğin ÇalarSaat için: override int opCmp(Object o) const { auto sağdaki = cast(const ÇalarSaat)o.dakika ? dakika . 31). 0. } Bu işlevi bir alt sınıf için tanımlarken ve karşılaştırmada önemi varsa. çs0 ve çs1 'in nasıl sıralanacaklarını en son bakılan alarm dakikası belirler.opCmp(o). const int üstSonuç = super. } else if (alarmSaati != sağdaki. Artık bu türün nesneleri sıralama karşılaştırmalarında kullanılabilir: auto çs0 = new ÇalarSaat(8. Örneğin bir dizi içindeki nesnelerin .saat : (dakika != sağdaki. Yukarıdaki tanımlarımızı denemek için. 0. class Saat { Copyright © 2009-2011 Ali Çehreli. } auto sağdaki = cast(const Saat)o.alarmDakikası.alarmSaati. veya sınıfın bir eşleme tablosunda indeks türü olarak kullanılmasında da perde arkasında bu işlevden yararlanılır. alt sınıfın üyelerine ancak üst sınıf parçaları eşit çıktığında bakılmaktadır.sağdaki. ve o değer döndürülür. assert(çs0 < çs1).random.alarmSaati) { return alarmSaati .sağdaki. 0. } else { return alarmDakikası . 6. import std.Object override int opCmp(Object o) const { if (typeid(this) != typeid(o)) { return typeid(this).saat ? saat .opCmp(typeid(o)). auto çs1 = new ÇalarSaat(8. import std.stdio.dakika : saniye . üst sınıfını da unutmamak gerekir. 30).

saat. dakika. if (üstSonuç != 0) { return üstSonuç. } override string toString() const { return format("%02s:%02s:%02s". } } } class ÇalarSaat : Saat { int alarmSaati. this. this(int saat. int alarmDakikası. int dakika. } else { return saniye .sağdaki.alarmSaati.dakika) { return dakika .sağdaki.dakika = dakika.dakika. alarmSaati. this(int saat.saat = saat. http://ddili. int saniye) { this. } else { return alarmDakikası . } } } void main() { Copyright © 2009-2011 Ali Çehreli.opCmp(o).sağdaki. this. this. } override string toString() const { return format("%s ♫%02s:%02s".sağdaki. this.alarmDakikası = alarmDakikası. int alarmSaati. saniye). int dakika.org 320 . dakika. alarmDakikası). } else if (dakika != sağdaki.alarmSaati = alarmSaati.saniye = saniye. const int üstSonuç = super. int dakika. super. } else if (alarmSaati != sağdaki. int alarmDakikası) { super(saat.saat. saniye).saat) { return saat . if (saat != sağdaki.saniye.alarmSaati) { return alarmSaati . int saniye. } override int opCmp(Object o) const { auto sağdaki = cast(const Saat)o.alarmDakikası. } override int opCmp(Object o) const { auto sağdaki = cast(const ÇalarSaat)o.Object int saat.toString(). int saniye.sağdaki.

10) { saatler ~= new ÇalarSaat(uniform(0. 60)).. } 24). } saatler. Daha fazla nesne kullandığınızda veya kurucuya verilen rasgele değer aralıklarıyla oynadığınızda daha belirgin sonuçlar göreceksinizdir. uniform(0.. sıfır. Copyright © 2009-2011 Ali Çehreli. } Yalnızca on nesne kullanan bu örnekte sıralamanın öncelikle nesnelerin saat değerleri tarafından belirlendiği görülüyor: 00:00:30 06:27:38 14:05:19 17:48:09 18:51:51 20:43:11 21:56:05 22:52:08 23:39:08 23:55:04 ♫06:25 ♫16:24 ♫13:05 ♫14:06 ♫23:31 ♫07:42 ♫07:47 ♫10:42 ♫09:44 ♫11:22 Çıktının yalnızca son iki nesnesinin sıralanmalarında dakika 'ların değerlerine bakıldığı anlaşılıyor.Object ÇalarSaat[] saatler. foreach (saat. 57. 0 . saatler) { writeln(saat).3. } else if (isim > sağdaki.isim) { return -1. override int opCmp(Object o) const { auto sağdaki = cast(Öğrenci)o. uniform(0.1 Dizgi türünden olan üyeler için opCmp Dizgi üyeler için opCmp işlevini eksi.sort. foreach (i. 24). veya artı döndürecek şekilde uzun uzun şöyle yazabilirsiniz: class Öğrenci { string isim. } } } // .org 321 . if (isim < sağdaki. http://ddili.isim) { return 1.. uniform(0. 60). 60). } else { return 0. uniform(0.

zamanİsimleri[new Saat(12. class Öğrenci { string isim. Saat türünü bile indeks olarak kullanabiliriz: string[Saat] zamanİsimleri. override int opCmp(Object o) const { auto sağdaki = cast(Öğrenci)o. Bu sayede. ve kesinlikle gereken miktardan daha fazla bellek kullanıyor olabilirler. (Not: Her şeyin olduğu gibi bu hızın da bir bedeli vardır: elemanları sırasız olarak tutmak zorundadırlar.string. std. farklı nesneler için farklı indeks değerleri üretecek şekilde tanımlanmıştır.. opEquals 'un farklı nesnelerin eşit olmadıklarını kabul etmesine benzer.org 322 .isim). Yukarıdaki tabloya eklenmiş olan Saat nesnesi ile aynı değere sahip olan. Uyarı: Yalnızca bu işlevi tanımlamak yetmez. toHash .Object Onun yerine. indeks olarak kullanılan türü önce hash denen bir tamsayı değere çevirmelerinden kaynaklanır.. tamsayıya dönüştürülebilen her türün eşleme tablosu indeks türü olarak kullanılabilmesidir.4. 57.4 toHash Bu işlev. sınıf nesnelerinin bu amaç için indeks değerleri döndürmelerini sağlar.1 Eşleme tablosu indeks değerleri Eşleme tabloları eleman erişimini çok hızlı şekilde gerçekleştiren veri yapılarıdır. http://ddili. Bu yöntemin hızdan başka bir yararı. } } return cmp(isim. // . tabloda ne kadar eleman bulunduğundan bağımsız olarak yapabilirler. Üstelik bunu. Bu. ama ondan farklı bir Saat nesnesi ile erişmek istesek. sınıfın eşleme tablolarında indeks türü olarak kullanılabilmesini sağlar. 0. ama istediğimiz gibi çalışmaz. Yukarıdaki kod Saat sınıfı için özel bir toHash işlevi tanımlanmamış olsa bile derlenir.) Eşleme tablolarının bu hızı. Eşleme tablosunun eleman türü olarak kullanıldığı durumda bir etkisi yoktur. sağdaki. Bu işlevin eşleme tablolarında doğru olarak kullanılabilmesi için opEquals ve opCmp işlevlerinin de birbirleriyle tutarlı olarak tanımlanmış olmaları gerekir. doğal olarak tablodaki "öğleni gösteren saat" değerini bulmayı bekleriz: Copyright © 2009-2011 Ali Çehreli. Object 'ten kalıtım yoluyla edinilen toHash işlevi. pek mantıklı olmasa da.string modülünde tanımlanmış olan ve aynı karşılaştırmayı büyük olasılıkla daha hızlı olarak gerçekleştiren cmp işlevini de kullanabilirsiniz: import std. 0)] = "öğleni gösteren saat". Bu tamsayıyı kendilerine ait bir dizinin indeksi olarak kullanırlar. 57.

override hash_t toHash() const { return saat + dakika + saniye. ulong . Copyright © 2009-2011 Ali Çehreli. nesnenin üyeleri kullanılarak hesaplanır. int dakika.2 toHash için seçilecek üyeler İndeks değeri. Bu işlevin dönüş değeri olarak yazılan hash_t . 0) in zamanİsimleri) { writeln("var"). yerleştirilirken kullanılan nesne ile erişirken kullanılan nesnenin new ile ayrı ayrı oluşturulmuş olmalarıdır. çünkü hem birden fazla nesnede aynı not değerleri bulunabilir. Bu yüzden.. her üye bu indeks hesabına uygun değildir. int saniye. çünkü bu iki üyenin her nesnede farklı olduğunu düşünebiliriz. veya daha başka bir tür olabilir. herhangi bir üyesi değişik olan iki nesnenin indeks değerlerinin farklı olacağı garanti edilmiş olur: class Saat { int saat. toHash 'i. işaretsiz bir türü temsil eder. Object 'ten kalıtımla edinilen toHash . yani bu nesnenin tabloda bulunmadığını belirtir: yok Bunun nedeni. 57. nesneyi diğer nesnelerden ayırt etmeye yarayan üyeler olmalıdır.org 323 . Dolayısıyla. 0. Örneğin Öğrenci gibi bir sınıfın isim ve soyad üyelerinin ikisi birden nesneleri ayırt etmek için kullanılabilir.3 Tamsayı üyeler için indeks değerleri Saat nesnelerinin farklı kabul edilebilmeleri için bütün üyelerinin değerlerinin önemli olduğunu düşünebiliriz.4. } } // . Ortama bağlı olarak uint . bir tamsayı indeks döndürecek şekilde bizim yazmamız gerekir. Öğrenci sınıfının notlar dizisi uygun değildir. Ancak.) Öte yandan. Bizim için asıl önemli olan. üyeleri kullanarak bir tamsayı değer üretmektir. 57. Bu işlevi tanımlarken dönüş türünü hash_t olarak yazmanızı öneririm. eşleme tablolarında indeks değeri olarak kullanılmaya çoğu durumda elverişli değildir. http://ddili. indeks değeri olarak o üç üyenin toplanması ile elde edilen tamsayı kullanılabilir. Bunun için seçilecek üyeler. } else { writeln("yok"). yani ikisi farklı nesnelerdir. hem de aynı öğrencinin notları zamanla değişebilir.4..Object if (new Saat(12. Bu sayede. oradaki in işleci false döndürür. } Ne yazık ki. (İsim benzerliklerini gözardı ediyorum.

toHash() + alarmSaati + alarmDakikası.org 324 . Bu. 57... Hepsinin dönüş değeri. D dili. dizgiler.getHash(&isim) + typeid(soyAd). dakika.. Kulağa karmaşık geldiği halde aslında çok kısaca yapmamız gereken. kesirli sayılar. } } // . Örneğin ÇalarSaat 'in toHash işlevi. kesirli sayılar. Aynı yazımı her tür için kullanabilirsiniz. Öğrencinin ismini bir string üyesinde tutan ve eşleme tabloları için indeks değeri olarak bundan yararlanmak isteyen bir sınıfın toHash işlevi şöyle yazılabilir: class Öğrenci { string isim.. sonra da typeid 'nin döndürdüğü nesnenin getHash üye işlevini üyenin adresi ile çağırmaktır.getHash(&isim). üst sınıfı unutmamamız gerekebilir. http://ddili. ve yapı türleri için çoğu duruma uygun olan indeks değeri algoritmaları kullanır. Programın çıktısı artık beklenen sonucu verir: var Önceki işlevlerde olduğu gibi. önce typeid 'yi üye ile.Object Saat eşleme tablolarında indeks türü olarak kullanıldığında. Saat 'inkini şöyle kullanabilir: class ÇalarSaat : Saat { int alarmSaati. Bu algoritmalardan biz de yararlanabiliriz.getHash(&soyAd)). Yukarıdaki kodda new ile farklı olarak kurulmuş olsalar bile. Bir tamsayı değer ürettiği için. Copyright © 2009-2011 Ali Çehreli. birden fazla üye için çağırarak değerlerini toplayabilirsiniz: return (typeid(isim).4. ikisinin saat. dizgiler ve yapılar için hep aynı şekilde yazılır. override hash_t toHash() const { return typeid(isim). o üyeye uygun bir indeks değeridir. artık bizim tanımladığımız bu toHash kullanılır. override hash_t toHash() const { return super.4 Tamsayı olmayan üyeler için indeks değerleri Tamsayı üyeleri kullanarak indeks değeri üretmek onları toplamak kadar basittir. ve saniye değerleri aynı olduğu için eşleme tablosunda aynı indeks değerini üretirler. int alarmDakikası. } } // .

Renk renk) { this. Nokta üç) { noktalar = [ bir.4. this. Elimizde renkli noktaları ifade eden bir sınıf olsun: enum Renk { mavi. Yani assert denetimi doğru çıksın: // Renkleri farklı auto maviNokta = new Nokta(1. 2. Renk.kırmızı). 57. } } Bu sınıfın opEquals işlevini rengi gözardı edecek şekilde yazın.y = y. http://ddili.renk = renk. öncelikle x'e. assert(kırmızıNokta3 < kırmızıNokta2). iki. assert(kırmızıNokta1 < maviNokta).mavi). Yine aşağıdaki assert 'ler doğru çıksın: Copyright © 2009-2011 Ali Çehreli. Aynı sınıf için opCmp işlevini. 2. sonra y'ye bakacak şekilde yazın. 10.x = x.org 325 . Üç noktayı bir araya getiren bir sınıf daha olsun: class ÜçgenBölge { Nokta[3] noktalar. 3. yeşil.kırmızı). O algoritma. Eğer herhangi bir nedenle. 7. // Yine de eşitler assert(maviNokta == yeşilNokta). Renk renk. onların indeks değerleri zaten otomatik olarak ve etkin bir algoritmayla hesaplanır. üç ].yeşil). 2. Renk. Aşağıdaki assert denetimleri doğru çıksın: auto kırmızıNokta1 = new Nokta(-1. Renk. nesnenin bütün üyelerini dikkate alır.Object 57. Renk. this. örneğin bir öğrenci yapısının not bilgisini dışarıda bırakacak şekilde kendiniz yazmak isterseniz. int y. int y. Nokta iki.kırmızı). toHash 'i yapılar için de tanımlayabilirsiniz. auto kırmızıNokta2 = new Nokta(-2. Renk. renkleri farklı olduğu halde eşit çıksınlar. Şu iki nokta. this(int x. this(Nokta bir.5 Problemler 1. auto yeşilNokta = new Nokta(1. auto kırmızıNokta3 = new Nokta(-2.5 Yapılar için toHash Yapılar değer türleri olarak kabul edildikleri için. } } O sınıf için toHash işlevini bütün noktalarını kullanacak şekilde yazın. kırmızı }. 10. class Nokta { int x.

kırmızıNokta1).org 326 . // Yine de eşitler assert(bölge1 == bölge2). Hatırlatma: maviNokta ve yeşilNokta değer olarak eşit kabul ediliyorlardı. assert(bölgeler[bölge2] == 1. auto bölge2 = new ÜçgenBölge(yeşilNokta.25. kırmızıNokta1). maviNokta. */ auto bölge1 = new ÜçgenBölge(maviNokta.Object /* Farklı ama aynı değerli noktalarla kuruluyorlar. çözümler Copyright © 2009-2011 Ali Çehreli. .. Bu sınıf için opEquals ve opCmp işlevlerini de yazmanız gerektiğini unutmayın. http://ddili. // bölge2 ile de aynı veriye erişiliyor assert(bölge2 in bölgeler). // bölge1 ile indeksleniyor bölgeler[bölge1] = 1. // Bir eşleme tablosu double[ÜçgenBölge] bölgeler.25). yeşilNokta..

Önceki derslerde gördüğümüz class 'tan türeme. yalnızca onlardan türeyen ve o soyut işlevleri tanımlayan türlerin nesneleri oluşturulabilir. ama tanımlarını vermez: interface SesliAlet { string ses().1 Tanımlanması class yerine interface yazılarak tanımlanır: interface SesliAlet { // .Arayüzler 58 Arayüzler interface . belirli bir arayüz belirlemek. Bu ders yazıldıktan sonra D'de yapılan değişiklikler. soyut sınıfların kendi nesneleri oluşturulamaz.. o arayüzün gerektirdiği işlevleri bildirir. Yeni tanımlanan sınıf. Kendi üyeleri veya üye işlevleri bulunamaz.org 327 .2 interface 'ten türetme Türeme söz dizimi class 'tan farklı değildir: class Keman : SesliAlet { string ses() { return "♩♪♪". 58. interface 'ten türeme özetle şunu sağlar: • bildirdiği bütün işlevlerin. http://ddili. Not: Yukarıda söylenen artık doğru değil. yalnızca bir sınıf arayüzü bildirir. belirli bir tür gibi kullanılabilmeyi garanti etmeye yarar. Tek yaptığı. interface 'lerin de static ve final işlevler sunmalarına izin verir. türetildiği sınıfın bütün üyelerini ve işlevlerini ediniyordu. ve bir class 'tan türeyemez. Hiçbir gerçekleştirme sunmaz. Bu dersi değiştireceğim. Dolayısıyla üye edinmeye değil. } interface . Hatırlarsanız. abstract anahtar sözcüğü de üye işlevlerin ve dolayısıyla sınıfların soyut olmalarını sağlıyordu. gerçekleştirme türemesi idi. } // Yalnızca bildirilir (tanımı verilmez) O arayüz ile kullanılabilmeleri için. Copyright © 2009-2011 Ali Çehreli. ve bu arayüzün alt sınıflar tarafından tanımlamalarını şart koşmaktır. alt sınıflardan en az birisi tarafından tanımlanma zorunluluğu 58. Türeme dersinde gördüğümüz gibi. bütünüyle soyut işlevlerden oluşan bir sınıf olarak düşünülebilir.. yalnızca soyut işlevler içeren bir sınıf gibidir. interface 'ten türeyen sınıfların interface 'in bildirdiği işlevleri tanımlamaları gerekir. class 'tan türeme özetle şunları getirir: • üst sınıfın gerçekleştirmesini (üyelerini ve üye işlevlerini) edinme • soyut işlevleri tanımlama zorunluluğu interface .

Her aletin kendi asıl türünün tanımladığı ses işlevi çağrılır ve sonuçta sırasıyla Keman. parametre olarak interface alan işlevler. writeln(alet. Copyright © 2009-2011 Ali Çehreli. ancak tek bir class 'tan türetilebiliyordu. HaberleşmeAleti { // .ses ve Çan..ses()). başka işlemler . sesliAletKullan(new Çan).. http://ddili. string dinle(). // . ve "telefon bir haberleşme aletidir". onları asıl türlerini bilmeden kullanabilirler. Örneğin haberleşme aletlerini temsil eden şöyle bir arayüz olduğunu düşünelim: interface HaberleşmeAleti { void konuş(string mesaj)..... } } Üst sınıflarda da olduğu gibi..org 328 ... } Telefon diye bir sınıfı hem sesli bir alet. interface 'ten türemede ise böyle bir kısıtlama yoktur. o işlev SesliAlet arayüzünden türeyen her sınıf ile çağrılabilir: sesliAletKullan(new Keman)..ses üye işlevlerinin çıktısı görülür: ♩♪♪ çın 58.Arayüzler } } class Çan : SesliAlet { string ses() { return "çın". bazı işlemler . hem de bir haberleşme aleti olarak kullanabilmek için onu bu iki arayüzden birden türeterek tanımlayabiliriz: class Telefon : SesliAlet. Örneğin işlemleri sırasında bir SesliAlet kullanan bir işlev. } Sınıflarda da olduğu gibi. } O tanım şu iki ilişkiyi birden sağlar: "telefon bir sesli alettir". hangi tür bir sesli alet olduğunu bilmeden onun ses işlevinden yararlanabilir: void sesliAletKullan(SesliAlet alet) { // .3 Birden fazla interface 'ten türetme Bir sınıf.

58.4 interface 'ten ve class 'tan türetme Bir sınıf.org 329 . http://ddili. hem de kendi gerektirdiği akortEt işlevini tanımlamak gerekir... bir MüzikAleti olabilmek için. kendisinden türetilecek olan sınıfların kendi bildirdiği işlevleri de tanımlamalarını gerektirir: interface MüzikAleti : SesliAlet { void akortEt(). bir veya daha fazla interface 'ten türetilmenin yanında. Copyright © 2009-2011 Ali Çehreli. SesliAlet { string ses() { return "bi bi biip"... } Yukarıdaki tanıma göre. bir de SesliAlet arayüzünün gerektirdiği ses işlevini tanımlamak zorundadır.. return hattaDuyulanSes. programın gerekleri doğrultusunda sınırsız sayıda interface 'ten türetilebilir. bir adet olduğu sürece aynı zamanda bir sınıftan da türetilebilir: class Saat { // . } class ÇalarSaat : Saat.. } void konuş(string mesaj) { // .5 interface 'ten interface türetme Başka bir arayüzden türetilen bir arayüz. HaberleşmeAleti { string ses() // SesliAlet için { return "zırrr zırrr". Telefon sınıfının bu iki arayüzün gerektirdiği bütün işlevleri tanımlaması gerekir: class Telefon : SesliAlet.. sesi hattan oku . mesajı hatta ilet . // . } } ÇalarSaat . } // HaberleşmeAleti için // HaberleşmeAleti için } Bu örnekte görüldüğü gibi iki interface ile sınırlı değildir. hem SesliAlet 'in gerektirdiği ses işlevini.Arayüzler Nesnelerinin oluşturulabilmesi için.. kendi gerçekleştirmesi . } string dinle() { string hattaDuyulanSes.... Saat 'in bütün üyelerini ve üye işlevlerini edinmenin yanında.. 58.

. 58. http://ddili..7 Soyutlama Arayüzler programların alt bölümlerini birbirlerinden bağımsızlaştırmaya yararlar.. Bütün alt sınıflarda da bulunan ortak gerçekleştirmelerin bir ara sınıfta tanımlandığı durumlarla da sık karşılaşılır.org 330 ...Arayüzler Örneğin yukarıdaki Keman sınıfını doğrudan SesliAlet arayüzünden türetmek yerine MüzikAleti 'nden türetsek.. O ortak sınıflardan türeyen alt sınıflar da kendi daha özel tanımlarını içerebilirler. Daha karmaşık sıradüzenlerle de karşılaşılır. Alt sınıflar bu ortak sınıftan türerler. asıl türünü bilmeden bir MüzikAleti içerebilir: class Müzisyen { MüzikAleti alet. akort işlemleri . o aletlerin asıl türlerini bilmek zorunda değillerdir: Copyright © 2009-2011 Ali Çehreli. // ... } // dolaylı olarak SesliAlet için } void akortEt() // MüzikAleti için { // . En sık karşılaşılan sıradüzenlerden birisi. Müzisyen gibi bir sınıf.. Aşağıdaki sıradüzende TelliMüzikAleti ve NefesliMüzikAleti sınıfları. tek bir interface 'ten türeyen basit gerçekleştirme sınıflarından oluşan sıradüzendir: MüzikAleti (interface) / | \ \ Kemençe Saz Kaval . ama bu basit yapı çoğu programın ihtiyacı için yeterlidir.6 Ne zaman kullanmalı Oldukça sık kullanılır. Buna soyutlama denir.. onun bildirdiği akortEt işlevini de tanımlamamız gerekir: class Keman : MüzikAleti { string ses() { return "♩♪♪".. } 58.. ve yalnızca onu kullanarak yazılabilir. } Birden fazla müzik aletini bir araya getiren türler. Hemen hemen bütün sıradüzenlerin en üstünde bir veya daha fazla interface bulunur. kendi alt türlerinin ortak üyelerini ve üye işlevlerini içeriyor olabilir: MüzikAleti (interface) / \ TelliMüzikAleti NefesliMüzikAleti / | \ / | \ Kemençe Saz . Kaval Ney . Örneğin müzik aletleri kullanan bir programın büyük bir bölümü yalnızca MüzikAleti arayüzünden haberi olacak şekilde.

. } void güzelÇal(MüzikAleti alet) { if (akortGerekiyor_mu(alet)){ alet. } } interface HaberleşmeAleti { void konuş(string mesaj).stdio.. } class Çan : SesliAlet { string ses() { return "çın". Copyright © 2009-2011 Ali Çehreli. bu arayüzü kullanan kodlar o değişikliklerden etkilenmemiş olurlar. } } interface MüzikAleti : SesliAlet { void akortEt(). } } writeln(alet..akortEt(). alt sınıflarda ileride gerekebilecek kod düzenlemelerinin serbestçe yapılabilmelerini sağlar. akort işlemleri . interface SesliAlet { string ses(). // . http://ddili... Bu şekilde bir soyutlama kullanarak programın bölümlerinin birbirlerinden bağımsız hale getirilmeleri.org 331 .8 Örnek Yukarıdaki bütün arayüzleri ve sınıfları içeren bir program şöyle yazılabilir: import std. 58. return karar. } void akortEt() { // .Arayüzler MüzikAleti[] orkestradakiAletler. Programın çoğu işlevi yalnızca bu arayüzü kullanarak yazılabilir: bool akortGerekiyor_mu(MüzikAleti alet) { bool karar. } class Keman : MüzikAleti { string ses() { return "♩♪♪"..ses()). Alt sınıfların gerçekleştirmeleri bu arayüzün arkasında oldukları için.

. } string dinle() { string hattaDuyulanSes.Arayüzler } string dinle(). } foreach (alet. } } class Saat { // .. ÇalarSaat. bütün işlevleri soyut olan bir sınıf gibidir • Bir sınıfın "o türden" nesnelerinin olabilmesi için o arayüzün bildirdiği bütün işlevleri tanımlaması gerekir Copyright © 2009-2011 Ali Çehreli.. Saat'in gerçekleştirilmesi } class ÇalarSaat : Saat. // . class Telefon : SesliAlet. return hattaDuyulanSes. http://ddili. } } // . SesliAlet { string ses() { return "bi bi biip". Sonuçta programın çıktısı bütün aletlerin ürettikleri sesleri içerir: çın ♩♪♪ zırrr zırrr bi bi biip 58. } void konuş(string mesaj) { // . Telefon. HaberleşmeAleti { string ses() { return "zırrr zırrr".9 Özet • interface bir arayüz tanımlar. Keman.... ÇalarSaat'in gerçekleştirilmesi void main() { SesliAlet[] aletler... aletler aletler aletler aletler ~= ~= ~= ~= new new new new Çan...org 332 .. aletler) { writeln(alet. mesajı hatta ilet . o diziye SesliAlet 'ten türeyen her tür eklenebiliyor.ses()). } main 'in içindeki aletler bir SesliAlet dizisi olduğu için.. sesi hattan oku .

Arayüzler • Tek class 'tan türetebilme kısıtlaması interface 'lerde yoktur. ve alttaki gerçekleştirmeleridir (class ) Copyright © 2009-2011 Ali Çehreli. üstte bir arayüz (interface ). http://ddili.org 333 . sınıflar ve arayüzler birden fazla interface 'ten türetilebilirler • En sık karşılaşılan sıradüzenlerden birisi.

Böylece o sayacın değerine bakarak sınıfın nesnelerinden kaç tanesinin henüz sonlandırılmadıklarını anlayabileceğiz. Hatırlarsanız. 59. Örneğin std. Daha sonraki derslerde de nesnelerin kurulması sırasında gereken işlemlerin this isimli kurucu işlevde. Sonlandırıcı işlev. nesne için kullanılmış olan sistem kaynaklarını geri vermektir.length = 30_000. Burada önemli bir ayrım vardır: bir sınıf nesnesinin yaşamının sona ermesi ile sonlandırıcı işlevinin işletilmesi aynı zamanda gerçekleşmez. Sonlandırıcı işlevlerin görevlerinden bazıları. işletim sisteminden kendi işi için almış olduğu dosya kaynağını sonlandırıcı işlevinde geri verir.clear ve scoped 59 clear ve scoped Yaşam Süreçleri ve Temel İşlemler dersinde değişkenlerin kurma işlemiyle başlayan ve sonlandırma işlemiyle biten yaşam süreçlerini görmüştük. */ dizi. örneğin geçerli olduğu kapsamdan çıkıldığı an sona erer. http://ddili. // ← bütün nesneler tarafından // paylaşılır this() { /* * Her nesne bellekte çok yer tutsun diye bu diziyi * çok sayıda int'lik hale getiriyoruz. bazen kaynakların sisteme geri verilmeleri gecikebilir ve yeni nesneler için kaynak kalmayabilir. /* * Bir nesne daha kurulmuş olduğu için nesne sayacını * bir arttırıyoruz. // ← her nesnenin kendisine aittir static int sayaç.1 Sınıf sonlandırıcı işlevlerinin geç işletilmesini gösteren bir örnek Sınıfların sonlandırıcı işlevlerinin ilerideki belirsiz bir zamanda işletildiklerini göstermek için bir sınıf tanımlayalım. Artık sonlanmakta olduğu için zaten o kaynağı kullanması söz konusu değildir. Sınıflarda ve başka referans türlerinde ise çöp toplayıcı tarafından sonraki bir zamanda işletilir.stdio.org 334 . Bu sınıfın kurucu işlevi sınıfın static bir sayacını arttırsın ve sonlandırıcı işlevi de o sayacı azaltsın. */ ++sayaç. sonlandırılması sırasında gereken işlemlerin de ~this isimli sonlandırıcı işlevde tanımlandıklarını öğrenmiştik. Sonlandırıcı işlevi ise çöp toplayıcı tarafından belirsiz bir zaman sonra otomatik olarak işletilir. class YaşamıGözlenen { int[] dizi. Nesnelerin * böylece büyük olmaları nedeniyle.File yapısı. Sınıfların sonlandırıcılarının çöp toplayıcı tarafından tam olarak ne zaman çağrılacakları belli olmadığı için. yapılarda ve başka değer türlerinde nesnenin yaşamı sona ererken hemen işletilir. Nesnenin yaşamı. static üyelerden bir tane bulunur: Sınıfın bütün nesneleri o tek üyeyi ortaklaşa kullanırlar. } ~this() Copyright © 2009-2011 Ali Çehreli. çöp * toplayıcının bellek açmak için onları daha sık * sonlandıracağını umuyoruz.

YaşamıGözlenen. 20) { auto değişken = new YaşamıGözlenen..stdio. yaşamlarının sona erdiği an işletilmediklerini sayacın değerine bakarak görebiliyoruz: 1 2 3 4 5 6 7 1 2 3 4 5 6 7 1 2 3 4 5 6 Çöp toplayıcının bellek ayırma algoritması.2 Nesnenin sonlandırıcısını işletmek için clear() "Temizle.sayaç. 0 .) 59. Değerinin yazdırıldığı satırdan hemen sonraki clear() satırında da sonlandırıcı işlev tarafından tekrar sıfıra indirilir. ' ').sayaç. // ← baş O programda oluşan her YaşamıGözlenen nesnesinin yaşamı aslında çok kısadır: new anahtar sözcüğüyle başlar. http://ddili. O sınıfın nesnelerini bir döngü içinde oluşturan bir program: import std.org 335 . Programdaki baş ve son açıklamaları her nesnenin yaşamının başladığı ve sona erdiği noktayı gösteriyor. (Not: Bu çıktı çöp toplayıcının yürüttüğü algoritmaya.. boşalt" anlamına gelen clear() .clear ve scoped { } } /* * Bir nesne daha sonlandırılmış olduğu için nesne * sayacını bir azaltıyoruz. } } writeln(). 0 . write(YaşamıGözlenen. void main() { foreach (i. O yüzden yazdırıldığı satırda hep 1 olduğunu görüyoruz: 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 Copyright © 2009-2011 Ali Çehreli. ve foreach döngüsünün kapama parantezinde son bulur. ' '). nesnenin sonlandırıcı işlevini çağırır: void main() { foreach (i. */ --sayaç. bu deneyde bu sınıfın nesnelerinden en fazla 7 adet bulunmasına izin veriyor. clear(değişken). boş bellek miktarına ve başka etkenlere bağlı olarak farklı da olabilir.sayaç 'ın değeri new satırında kurucu işlevin işletilmesi sonucunda arttırılır ve 1 olur. Yaşamları sona eren bu nesneler çöp toplayıcının sorumluluğuna girerler. write(YaşamıGözlenen. } // ← son } writeln(). 20) { auto değişken = new YaşamıGözlenen. Nesnelerin sonlandırıcı işlevlerinin.

http://ddili. daha çok bunun tümleyeni olan "kaynaklar sonlandırıcı işlevlerde geri verilmelidir" ilkesini temsil eder. "</". "resource acquisition is initialization"ın kısaltmasıdır. } } O yapıyı kullanan bir programla aşağıdaki çıktıyı elde etmiştik. O yapı. ~this() { writeln(girinti.. sonlandırıcılardaki kodların nesnelerin yaşamlarının sona erdiği anda işletilmeleri beklentisi üzerine kuruludur. Aşağıdaki XmlElemanı . Başka bir nedeni. Örneğin RAII yöntemi. RAII. Aynı düzeyde bulunan açma ve kapama etiketlerini aynı renkte gösteriyorum: Copyright © 2009-2011 Ali Çehreli. XML elemanlarını <etiket>değer</etiket> şeklinde yazdırmak için kullanılıyordu.3 Ne zaman kullanmalı Yukarıdaki örnekte gördüğümüz gibi.4 Örnek Kurucu ve Diğer Özel İşlevler dersinde XmlElemanı isminde bir yapı tanımlamıştık. XML elemanlarının kapama etiketlerinin yazdırılması sonlandırıcı işlevin göreviydi: struct XmlElemanı { // .clear ve scoped 59.. Tam çevirisi "kaynak ayırmak ilklemektir" olsa da. 59. '>').org 336 . sonlandırıcı işlevindeki etiket kapama işlemi nedeniyle RAII yönteminin bir örneği olarak görülebilir. isim. kaynakların çöp toplayıcının kararına kalmadan hemen geri verilmesi gerektiğinde kullanılır. nesnelerin belirli bir sırada sonlandırılıyor olmalarının programın akışı açısından önemli olduğu durumlardır.

istenen çıktıyı elde etmek için yeterlidir: void main() { auto dersler = XmlElemanı("dersler". 3) { auto not = XmlElemanı("not". nesneleri uygun kapsamlara yerleştirmek. writeln(girintiDizgisi(3).stdio. foreach (dersNumarası.clear ve scoped <dersler> <ders0> <not> 72 </not> <not> 97 </not> <not> 90 </not> </ders0> <ders1> <not> 77 </not> <not> 87 </not> <not> 56 </not> </ders1> </dersler> ← Kapama etiketleri doğru satırlarda beliriyor ← ← ← ← ← ← ← ← O çıktının doğru çalışmasının nedeni. http://ddili. import std. foreach (i. 2) { auto ders = XmlElemanı("ders" ~ to!string(dersNumarası). 0 . const int rasgeleNot = uniform(50..org 337 . Copyright © 2009-2011 Ali Çehreli. 0)..array. XmlElemanı 'nın bir yapı olmasıdır. } // ← not sonlanır } // ← ders sonlanır } // ← dersler sonlanır Nesneler açıklama satırları ile belirtilen noktalarda sonlandıkça XML kapama etiketlerini de çıkışa yazdırırlar. Sınıfların farkını görmek için aynı programı bu sefer XmlElemanı bir sınıf olacak şekilde yazalım: import std. 2). 0 . rasgeleNot). Yapıların sonlandırıcıları hemen çağrıldığı için.random. import std. 1). 101).

3) { auto not = new XmlElemanı("not". string girinti. '<'. this. } } } Referans türleri olan sınıfların sonlandırıcı işlevleri çöp toplayıcıya bırakılmış olduğu için programın çıktısı artık istenen düzende değildir: Copyright © 2009-2011 Ali Çehreli. 0 . isim.isim = isim..conv. '>'). rasgeleNot). } class XmlElemanı { string isim. "</". '>'). writeln(girintiDizgisi(3). } ~this() { writeln(girinti. girintiAdımı * 2). foreach (i. const int rasgeleNot = uniform(50. http://ddili. in int düzey) { this. string girintiDizgisi(in int girintiAdımı) { return replicate(" ". } void main() { auto dersler = new XmlElemanı("dersler". isim. 2) { auto ders = new XmlElemanı( "ders" ~ to!string(dersNumarası).org 338 . } writeln(girinti. this(in string isim. 2). 101).. 0).clear ve scoped import std.girinti = girintiDizgisi(düzey). foreach (dersNumarası. 0 . 1).

rasgeleNot). const int rasgeleNot = uniform(50. clear(ders). Örneğin programın çıktısında hiçbir kapama parantezi bulunmayabilir. 3) { auto not = new XmlElemanı("not". Sonuçta. 2) { auto ders = new XmlElemanı( "ders" ~ to!string(dersNumarası). 0 . writeln(girintiDizgisi(3). (Not: Aslında çöp toplayıcı bütün nesnelerin sonlandırılacakları garantisini vermez. } } } clear(not). foreach (i. http://ddili. nesneler kapsamlardan çıkılırken sonlandırıldıkları için programın çıktısı yapı tanımında olduğu gibi düzgündür: Copyright © 2009-2011 Ali Çehreli.. foreach (dersNumarası.org 339 ..clear ve scoped <dersler> <ders0> <not> 57 <not> 98 <not> 87 <ders1> <not> 84 <not> 60 <not> 99 </not> </not> </not> </ders1> </not> </not> </not> </ders0> </dersler> ← Kapama etiketlerinin hepsi en sonda beliriyor ← ← ← ← ← ← ← ← Bütün sonlandırıcı işlevler işletilmişlerdir ama kapama etiketleri beklenen yerlerde değildir. 0). 0 . 101). clear(dersler). 1).) XmlElemanı 'nın sonlandırıcı işlevinin doğru noktalarda işletilmesini sağlamak için clear() çağrılır: void main() { auto dersler = new XmlElemanı("dersler". 2).

http://ddili. // . // ...5 Sonlandırıcı işlevi otomatik olarak çağırmak için scoped Yukarıdaki programın bir yetersizliği vardır: Kapsamlardan daha clear() satırlarına gelinemeden atılmış olan bir hata nedeniyle çıkılmış olabilir.clear ve scoped <dersler> <ders0> <not> 66 </not> <not> 75 </not> <not> 68 </not> </ders0> <ders1> <not> 73 </not> <not> 62 </not> <not> 100 </not> </ders1> </dersler> ← Kapama etiketleri doğru satırlarda belirmiş ← ← ← ← ← ← ← ← 59. Programın yalnızca değişen satırlarını gösteriyorum: import std. bunun bir çözümü Hatalar dersinde gördüğümüz scope ve diğer olanaklardan yararlanmaktır. 1). scoped 'un etkisi. auto dersler = scoped!XmlElemanı("dersler".org 340 . auto ders = scoped!XmlElemanı( "ders" ~ to!string(dersNumarası). Copyright © 2009-2011 Ali Çehreli. // . yaşam süreçleri açısından sınıf nesnelerini yapı nesnelerine benzetmesidir.scoped ile kurmaktır.. sınıf değişkenini perde arkasında bir yapı nesnesi ile sarmalar. auto not = scoped!XmlElemanı("not". 0). sınıf nesnesini new yerine std.. Bu değişikliklerden sonra programın çıktısı yine istenen düzende olur.typecons. Başka bir yöntem.. 2).typecons. scoped() . Eğer clear() satırlarının kesinlikle işletilmeleri gerekiyorsa.. O yapı nesnesinin sonlandırıcısı kapsamdan çıkılırken otomatik olarak çağrıldığında sınıf nesnesinin sonlandırıcısını da çağırır.

• scoped ile kurulan sınıf nesnelerinin sonlandırıcıları kapsamdan çıkılırken otomatik olarak çağrılır.org 341 . http://ddili.clear ve scoped 59.6 Özet • Bir sınıf nesnesinin sonlandırıcı işlevinin istenen bir anda çağrılması için clear() işlevi kullanılır. Copyright © 2009-2011 Ali Çehreli.

Bu tanıma göre.Modüller ve Kütüphaneler 60 Modüller ve Kütüphaneler D programlarını ve kütüphanelerini oluşturan en alt yapısal birimler modüllerdir. D'nin modül kavramı çok basit bir temel üzerine kuruludur: Her kaynak dosya bir modüldür.. Pakedi içeren klasörün ismi de modül isimlerinin baş tarafını oluşturur.d uzantısından önceki bölümü ile aynıdır. class Köpek { // .kedi. ve kaynak dosyanın en başına yazılan module anahtar sözcüğü ile belirtilir. programlarınızın taşınabilir olmaları için dosya isimlerinde yalnızca ASCII küçük harfler kullanmanızı öneririm. dosya isimlerinde kullanılabilecek karakterler konusunda kısıtlamalar getirir. Örneğin bir kaynak dosyanın isminin "kedi. class Kedi { // . otomatik olarak dosyanın isminin . modül isimlerinin başına klasör ismini yazmak. http://ddili. 60. Örneğin yukarıdaki Kedi sınıfı ile birlikte kullanılacak olan bir Köpek sınıfının modülünün dosya ismini "kopek.. bu. Ayrıca çoğu dosya sistemi. Her modülün ismi.d 'den önceki bölümü kullanılır. hangi ortamda olursa olsun geçerlidir. Örneğin yukarıdaki "kedi.d" ve "kopek. şimdiye kadar deneme programlarımızı yazdığımız tek kaynak dosya bile bir modüldür.2 Paketler Modüllerin bir araya gelerek oluşturdukları yapıya paket denir. Linux sistemlerinde büyük/küçük harfler farklıdır. Ancak. O yüzden. dosya sistemleri konusunda aynı serbesti bulunmaz..org 342 . class Kedi { Copyright © 2009-2011 Ali Çehreli.d" olduğunu varsayarsak: module kedi. } // ASCII harflerden oluşan modül ismi // Unicode harflerden oluşan program kodu 60.d" olarak seçebiliriz. onları aynı pakedin modülleri yapmaya yeter: module hayvan. D'nin paket kavramı da çok basittir: Dosya sisteminde aynı klasörde bulunan bütün modüller aynı pakedin parçası olarak kabul edilirler. } module satırı isteğe bağlıdır. Bu yüzden modülün ismi de ASCII harflerden oluşur: module kopek.d" dosyalarının "hayvan" isminde bir klasörde bulunduklarını düşünürsek.1 Dosya ve modül isimleri D programlarını Unicode olarak oluşturma konusunda şanslıyız. Örneğin Windows işletim sistemlerinin standart dosya sistemleri dosya isimlerinde büyük/küçük harf ayrımı gözetmezken. Yazılmadığı zaman. dosya isminin ..

3 Modüllerin programda kullanılmaları Şimdiye kadar çok kullandığımız import anahtar sözcüğü. } Not: Aşağıda anlatıldığı gibi. iç içe klasörlerde bulunan modüllerin paket isimleri de o klasör yapısının eşdeğeridir. Copyright © 2009-2011 Ali Çehreli. . hayvan. Yukarıdaki koddaki std. Örneğin "hayvan" klasörünün altında bir de "omurgalilar" klasörü olsa. class Köpek { // . auto köpek = new Köpek.omurgalilar.kedi. import hayvan. import hayvan.stdio. http://ddili. bir modülün başka bir modüle tanıtılmasını. import 'tan sonra yazılan modül ismi. paket bilgisini de içerir. // bu modülün ismi // kullandığı bir modül // kullandığı başka bir modül void main() { auto kedi = new Kedi. standart kütüphaneyi oluşturan modüllerin std isimli pakette bulunduklarını gösterir.. } Paket isimleri dosya sistemi klasörlerine karşılık geldiği için. derleyici. Öte yandan. Aynı şekilde kopek modülü için de: module hayvan..kedi.kopek modülleri bir "deneme.kopek. ve o modül içinde kullanılabilmesini sağlar: import std.Modüller ve Kütüphaneler } // . bir modül dosyasının nerede bulunduğunu modül ismini klasör ve dosya isimlerine dönüştürerek bulur. eğer varsa.org 343 . Küçük bir programın bütün dosyalarının tek bir klasörde bulunmasında bir sakınca yoktur. oradaki bir modülün paket ismi bu klasörü de içerir: module hayvan..d" dosyasında şu şekilde bildirilir: module deneme. 60. Kaynak dosyaların ne derece dallanacağı programın büyüklüğüne ve tasarımına bağlıdır. dosyaları belirli bir düzen altına almak için klasörleri gerektiği kadar dallandırmak da mümkündür.kopek.kedi ve hayvan. 60.4 Modüllerin dosya sistemindeki yerleri Modül isimleri dosya sistemindeki dosyalara bire bir karşılık geldiği için. Benzer şekilde.. yukarıdaki programın derlenip oluşturulabilmesi için o modül dosyalarının da bağlayıcıya derleme satırında bildirilmeleri gerekir.

Bunu. "hayvan/kedi. ve program. // . auto karışıklık = new Jaguar.6 Modüllerdeki tanımların programa dahil edilmesi import anahtar sözcüğü. Dolayısıyla.jaguar. O kadarı.Kedi. derleyici hangi modüldekinden bahsettiğimizi anlayamaz.Modüller ve Kütüphaneler Örneğin yukarıdaki programın kullandığı iki modül. Hem hayvan modülünde hem de arabalar modülünde bulunabilecek Jaguar isimli iki sınıftan hangisinden bahsettiğimizi uzun ismiyle şöyle belirtmek zorunda kalırız: import hayvan.d" dosyasını kullanarak oluşturmaya çalışmak yetmez: $ dmd deneme.d -w deneme.. http://ddili. // üsttekinin aynısı Normalde uzun isimleri kullanmak gerekmez.Jaguar. Kedi sınıfının tür ismini kısa ve uzun yazarak şöyle gösterebiliriz: auto kedi0 = new Kedi.5 Kısa ve uzun isimler Programda kullanılan isimler.d" dosyalarıdır.org 344 .Jaguar. import arabalar. Her ne kadar anlaşılmaz isimler içeriyor olsalar da.text._Dmain+0x4): undefined reference to `_D6hayvan4kedi4Kedi7__ClassZ' deneme.o: In function `_Dmain': deneme. Örneğin iki modülde birden tanımlanmış olan bir ismi kısa olarak yazdığımızda. // ← derleme HATASI // ← derlenir // ← derlenir 60. yalnızca o modüldeki olanakların bu kaynak kod içinde kullanılabilmelerini sağlar. programda kullanılan bazı tanımların bulunamadıklarını bildirir. paket ve modül bilgilerini de içeren uzun halde de yazılabilirler._Dmain+0xf): undefined reference to `_D6hayvan5kopek6Köpek7__ClassZ' collect2: ld returned 1 exit status --. yukarıdaki hata mesajları. yukarıdaki dosyayı da sayarsak bu programı oluşturmak için üç modül kullanılmaktadır. bağlayıcının bir araya getirdiği parçalardan oluşturulur. O hata mesajları bağlayıcıdan gelir.text. import ..jaguar.d" ve "hayvan/kopek. Copyright © 2009-2011 Ali Çehreli.d:(.jaguar. 60. Derleyici tarafından derlenen modüller bağlayıcıya verilir. Onları yalnızca olası karışıklıkları gidermek için kullanırız.jaguar. belirtilen modülün programın parçası haline gelmesi için yeterli değildir. auto hayvanım = new hayvan. auto kedi1 = new hayvan. Programın oluşturulması. perde arkasında çağrılan bağlayıcının görevidir.kedi. Yukarıdaki programı yalnızca "deneme.d:(. ancak kaynak kodun derlenebilmesi için gereklidir. auto arabam = new arabalar.errorlevel 1 Not: Derleyici uyarılarını etkinleştiren -w ayarını da her zaman için kullanmanızı öneririm.

d" dosyaları da derleme satırında bildirilmelidir: $ dmd deneme. Çalıştığınız ortama bağlı olarak kütüphane dosyasının ismi farklı olacaktır. programların başlangıç işlevi olan main .d" ve "kopek. kullandığı "hayvan/kedi.d"nin ayrı ayrı bildirilmelerine gerek kalmaz.d hayvan/kedi. onları kütüphaneler içinden de kullanabiliriz.a Program oluşturulurken artık "hayvan/kedi.a -w Not: Phobos kütüphane dosyasının yeri ve ismi sizin ortamınızda farklı olabilir. Daha sonra program oluşturulurken programın diğer modülleriyle bağlanırlar.d hayvan. Yoksa normalde onu da örneğin şu şekilde belirtmemiz gerekirdi: $ dmd deneme. Copyright © 2009-2011 Ali Çehreli.d dosyalarını derler ve bir kütüphane dosyası olarak bir araya getirir. Oluşturulan kütüphanenin isminin hayvan olmasını sağlamak için -of seçeneğini de kullanırsak. belirtilen . Kütüphaneler kendileri program olmadıkları için. Örneğin Linux ortamlarında kütüphane dosyalarının uzantıları .d hayvan.Modüller ve Kütüphaneler Bu yüzden.a /usr/lib/libphobos2.d" ve "hayvan/kopek.d hayvan/kopek. Kütüphaneler yalnızca işlev.d hayvan/kopek. http://ddili. 60. vs. Yukarıdaki programın oluşturulabilmesi için.org 345 .d" ve "hayvan/kopek.d" modüllerini içeren bir kütüphane şu şekilde oluşturulabilir: $ dmd hayvan/kedi. kütüphanelerde bulunmaz.d hayvan/kedi.d hayvan/kopek. programa otomatik olarak dahil edilir.a -w Yani o komut.d -lib -ofhayvan -w Konsoldan çalıştırılan o komut. şimdiye kadar çok yararlandığımız Phobos modüllerini içeren standart kütüphanenin açıkça bildirilmesi gerekmez. Kütüphane oluşturmak için dmd'nin -lib seçeneği kullanılır.7 Kütüphaneler Modül tanımlarının derlendikten sonra bir araya getirilmelerine kütüphane adı verilir. Onları içeren kütüphane dosyası tek başına yeterlidir: $ dmd deneme. yapı. O kütüphane. tanımlarını bir araya getirirler. sınıf.a olur: hayvan. programı oluşturan bütün parçaların derleme satırında belirtilmeleri gerekir. daha önce kullandığımız şu komutun eşdeğeridir: $ dmd deneme.d -w Modülleri derleme satırında her program için ayrı ayrı belirtmek yerine. yukarıdaki "kedi.d -w Bir istisna olarak.

erkek } struct Öğrenci { string isim.kız: ++kızToplamı. to!string(öğrenci. Üyelere böyle serbestçe erişebilmek. okuldaki kız ve erkek öğrencilerin sayılarını ayrı olarak tutuyor olsun: class Okul { Öğrenci[] öğrenciler. üye erişiminin bu kadar serbest olması sakıncalar da doğurabilir. final switch (öğrenci. " öğrencidir"). Cinsiyet. öğrencinin yalnızca ismini değiştirdiğimizi düşünelim: öğrenci. Cinsiyet cinsiyet.cinsiyet) { case Cinsiyet. bir erkek öğrencidir Başka bir örnek olarak. int kızToplamı.erkek). Örneğin belki de yanlışlıkla.org 346 .cinsiyet). int erkekToplamı.Sarma ve Erişim Hakları 61 Sarma ve Erişim Hakları Şimdiye kadar tasarladığımız bütün yapı ve sınıf türlerinin bütün üyeleri dışarıdan erişime açıktı. break. O atama sonucunda artık nesnenin geçerliliği bozulmuş olabilir: Ayşe. writeln(öğrenci. O kod. ".erkek: ++erkekToplamı.isim = "Ayşe". void ekle(in Öğrenci öğrenci) { öğrenciler ~= öğrenci.isim. bir ". break. bir grup öğrenciyi barındıran Okul isminde bir sınıfa bakalım. öğrenci hakkında bilgiyi çıkışa şöyle yazdırır: Tolga. Bu sınıf. } O yapının nesnelerinin üyelerine istediğimiz gibi erişebiliyorduk: auto öğrenci = Öğrenci("Tolga". case Cinsiyet. http://ddili. Hatırlamak için şöyle bir öğrenci yapısı düşünelim. bir erkek öğrencidir Ancak. o üyeleri programda istediğimiz gibi kullanma olanağı sağladığı için yararlıdır. } } override string toString() { Copyright © 2009-2011 Ali Çehreli. enum Cinsiyet { kız.

toplam 2 öğrenci Oysa bu sınıfın üyelerine serbestçe erişebiliyor olmak. vs. toplam 3 öğrenci 61. Cinsiyet. toplam %s öğrenci". Yeni öğrenci. %s erkek. writeln(okul). 61.kız). Kullanıcıların sınıfın üyelerine doğrudan erişememeleri. ekle işlevini kullanarak o sınıfın nesnelerine yeni öğrenciler ekleyebiliriz: auto okul = new Okul. kredi kartı numarası veya şifre gibi değerli veya gizli verilere erişimi kısıtlamak için değildir ve bu amaçla kullanılamaz. onun nesnelerini de tutarsız hale getirebilir. işlevler. program geliştirme konusunda yararlı bir olanaktır: tanımlarımızın kolay ve doğru kullanılmalarını ve kolayca değiştirilebilmelerini sağlar.1 Sarma İşte sarma.Sarma ve Erişim Hakları } } return format( "%s kız. yapılar.erkek)). içinin yapısı istendiği gibi değiştirilebilir.org 347 . ve tutarlı bir çıktı elde ederiz: 1 kız. bu tür durumları önlemek için üyelere erişimi kısıtlayan bir olanaktır. Örneğin öğrenciler üyesine doğrudan yeni bir öğrenci eklediğimizi düşünelim: okul. 1 erkek. Sarma. Varsayılan erişim public 'tir. enum'lar. Copyright © 2009-2011 Ali Çehreli.2 Erişim hakları D'de erişim hakları iki bağlamda belirtilebilir: 1. aşağıdaki dört özellikten birisi olarak belirtilebilir. Cinsiyet. sarma yoluyla bir anlamda bir kara kutu haline gelir ve ancak arayüzünü belirleyen işlevler aracılığıyla kullanılabilir. Sarma. toplamları sayan ekle işlevi çağrılmadan eklendiği için bu Okul nesnesi artık tutarsızdır: 1 kız. öğrenciler. Sınıf. Başka bir yararı. Cinsiyet.ekle(Öğrenci("Metin". okul. http://ddili. Sınıfın arayüzündeki işlevlerin tanımına dokunulmadığı sürece. yapı veya sınıf düzeyinde: her yapı veya sınıf üyesinin erişim hakkı ayrı olarak belirtilebilir 2.length). kızToplamı. ayrıca sınıfın iç tasarımının ileride rahatça değiştirilebilmesini de sağlar. modül düzeyinde: modül içinde tanımlanmış olan her tür olanağın erişim hakkı ayrı olarak belirtilebilir: sınıflar. Üyelerin veya modül tanımlarının erişim hakları.kız)).ekle(Öğrenci("Leyla". kullanıcıların sınıfın iç yapısını bilmek zorunda kalmamalarıdır. 1 erkek. erkekToplamı. okul.öğrenciler ~= Öğrenci("Nimet".

protected: // . korumalı: türetilen sınıf tarafından da erişilebilmeyi ifade eder private erişimi genişleten bir erişimdir: bu şekilde işaretlenmiş olan üyeye sınıf tarafından erişilmek yanında.. // ....org 348 . kedi modülünün kendisinden başka. Tek bir tanımın önüne yazıldığında yalnızca o tanımın erişim haklarını belirler. Bu... veya o sınıfı barındıran modüldeki kodlar tarafından erişilebilir. aynı şekilde yeni bir erişim hakkı yazılana kadarki bütün tanımları etkiler. // . // ... Örneğin hayvan. buradakiler korumalı .. hiçbir erişim kısıtlaması yoktur.. pakede açık: paketteki modüller tarafından erişilebilmeyi ifade eder Bu şekilde işaretlenmiş olan bir tanım.. genel: Programın her tarafından erişilebilmeyi ifade eder..kedi isimli bir modül içinde package olarak işaretlenmiş olan bir tanım. private void birİşlev() { // . C++'daki gibidir: private: // . o sınıftan türetilen sınıflardan da erişilebilir.. onu barındıran paketteki bütün kodlara açıktır. • private . • protected . 61. // . Copyright © 2009-2011 Ali Çehreli. özel: özel erişimi ifade eder Bu şekilde tanımlanan üyelere içinde tanımlı oldukları sınıfın kodları tarafından..omurgalilar.. http://ddili.stdio modülünün import ile eklenmesi.. Bu erişim hakkı yalnızca modülü içeren en içteki pakede verilir...3 Belirtilmesi Erişim hakları üç şekilde belirtilebilir. // ..... Bu. stdout 'un serbestçe kullanılabilmesi için yeterlidir.. • package . Blok söz dizimiyle yazıldığında bütün bloğun içini etkiler: private { // .. Bunun bir örneği olarak stdout standart akımını düşünebilirsiniz. } Bu üç yöntemin etkisi aynıdır. buradakilerin hepsi özel . } İki nokta üst üste karakteriyle yazıldığında. // .Sarma ve Erişim Hakları • public . buradaki bütün tanımlar özel .. Java ve bazı başka dillerdeki gibidir: private int birSayı. Hangisini kullanacağınıza tasarımınıza uygun olduğunu düşündüğünüz şekilde serbestçe karar verebilirsiniz. omurgalilar pakedindeki bütün modüllere de açıktır.. Onu bildiren std.

stdio 'yu asıl modülün ayrıca eklemesi gerekir: import okul. import std.stdio.stdio.Sarma ve Erişim Hakları 61. std. Örneğin okul modülü şöyle başlıyor olsun: module okul.okul. Onu kullanan şu program derlenemez: import okul. 61..5 Sarmayı ne zaman kullanmalı Sarma.okul. http://ddili. Copyright © 2009-2011 Ali Çehreli.ogrenci. Örneğin okul modülü std. Örneğin okul isimli bir modülün eklenmesinin. eklenen bir modülün başka modülleri de otomatik ve dolaylı olarak sunması istenebilir. o modülü dolaylı olarak ekleyen başka modüller tarafından görülemezler. } // kendi işi için eklenmiş. ogrenci modülünü de otomatik olarak eklemesi istenebilir. // ← derleme HATASI O yüzden. void main() { auto öğrenci = Öğrenci("Tolga". Bu.org 349 . public import okul. // .. import std. } // şimdi derlenir Bazen.okul. import okul..stdio modülünü eklese.okul.. void main() { writeln("merhaba").erkek). import işlemi public olarak işaretlenerek sağlanır: module okul. giriş bölümünde gösterdiğim sorunları önlemek ve sınıf tasarımlarını serbest bırakmak için çok etkili bir olanaktır. O program yalnızca okul modülünü eklediği halde Öğrenci yapısını da kullanabilmektedir... Cinsiyet.stdio 'dan otomatik olarak yararlanamazlar.. void main() { writeln("merhaba").okul..4 import 'lar normalde modüle özeldir import ile eklenen modüller. Artık okul modülünü ekleyen modüller ogrenci modülünü açıkça eklemek zorunda kalmadan. okul modülünü ekleyen başka modüller std. // .stdio. onun içindeki Öğrenci yapısını kullanabilirler: import std. // .

nesnelerin tutarlılıkları denetim altına alınmış olur.d" dosyasına bakalım: module okul.6 Örnek Yukarıdaki Öğrenci yapısını ve Okul sınıfını sarmaya uygun olacak şekilde tanımlayalım ve küçük bir deneme programında kullanalım. sizin o diziyi daha sonradan örneğin bir eşleme tablosu olarak değiştirmenizi güçleştirir. public import okul. Çünkü bu değişiklik kullanıcı kodlarını da etkileyecektir. Bu sefer de. package Cinsiyet cinsiyet. başka kodlar yapı ve sınıf gerçekleştirmelerine bağımlı kalmamış olurlar. Öğrenci ve Okul 'un birbirlerinin üyelerine doğrudan erişebilecek kadar yakın tanımlar oldukları düşünülebilir. yukarıdaki ekle işlevinde olduğu gibi. Ayrıca. o modülden yararlanan "okul/okul. nesne yönelimli programlamanın en yararlı olanakları arasındadır.d": Öğrenci yapısını içeren ogrenci modülü • "okul/okul.öğrenciler üyesine erişilebiliyor olması. Önceki dersten de hatırlayacağınız gibi. import std.ogrenci.d": Okul sınıfını tanımlayan okul modülü • "deneme.string. Böylece. Sarma.conv.d" dosyasını görelim: module okul.ogrenci.org 350 . to!string(cinsiyet)). 61. class Okul { // 1 Copyright © 2009-2011 Ali Çehreli. "okul" isminde bir klasör içinde tanımlandıkları için ilk ikisi okul pakedinin parçaları olacaklar: • "okul/ogrenci. Yine de.Sarma ve Erişim Hakları Üyelerin ve başka değişkenlerin ilgisiz kodlar tarafından serbestçe değiştirilmeleri önlenmiş olur. Bu örnekte toplam üç dosya tanımlayacağız. string toString() { return format("%s bir %s öğrencidir". http://ddili. enum Cinsiyet { kız.okul. aslında temelde sarmaya karşıdır. isim. Aynı pakedin parçası olsa bile başka bir modül tarafından yapının üyelerine erişilmesi. erkek } struct Öğrenci { package string isim. import std. çünkü biraz sonra göreceğimiz Okul sınıfının bu yapının üyelerine doğrudan erişmesini istedim.d": küçük bir deneme programı İlk önce "okul/ogrenci. Örneğin Okul.string. import std. } } Bu yapının üyelerini yalnızca kendi pakedindeki kodlara açmak için package olarak belirledim.

Cinsiyet. Bu sayede sınıf nesnelerinin tutarlılığı güvence altına alınmış oluyor. kullanılabilmeleri için public olarak işaretleniyorlar.Sarma ve Erişim Hakları private: Öğrenci[] öğrenciler. Bu sınıfın herhangi bir şekilde kullanışlı olabilmesi için üye işlevler sunması gerekir. writeln(öğrenci. int kızToplamı. 2. public: void ekle(in Öğrenci öğrenci) { öğrenciler ~= öğrenci. import okul. Cinsiyet.length.org 351 . final switch (öğrenci. break. Son olarak bu iki modülü kullanan program dosyasına da bakalım: import std. 4. ekle ve toString işlevleri.erkek)). Öğrenci yapısının her iki üyesine de bu modüldeki kodlar tarafından erişilebiliyor. } // 2 // 3 // 4a } override string toString() { string sonuç = format( "%s kız. break.kız: ++kızToplamı.kız)).ekle(Öğrenci("Leyla". toplam %s öğrenci". Bir anlamda. öğrenciler.toString()). // 4b } } return sonuç. 3. } writeln(okul). for (int i = 0. genele açılıyor.kız)).isim.ekle(Öğrenci("Nimet". okul. case Cinsiyet. kızToplamı. int erkekToplamı. Önceki dosyada package olarak işaretlendikleri için. erkekToplamı. auto okul = new Okul. Cinsiyet. okul. sonuç ~= öğrenciler[i]. ". Okul sınıfının bütün üyeleri özel olarak işaretleniyor. ++i) { sonuç ~= (i == 0) ? ": " : ". i != öğrenciler.ekle(Öğrenci("Metin". http://ddili. okul.cinsiyet) { case Cinsiyet. Bu modülü ekleyen programlar ogrenci modülünü de ayrıca eklemek zorunda kalmasınlar diye.length). Copyright © 2009-2011 Ali Çehreli. public olarak ekleniyor. bu "ekleme". } 1. Cinsiyet. void main() { auto öğrenci = Öğrenci("Tolga".erkek: ++erkekToplamı.stdio. %s erkek.erkek).okul.

http://ddili. Metin. o program bu tanımları yalnızca Okul.toString işlevleri aracılığıyla kullanıyor.ekle ve Öğrenci. Öğrenci 'nin ve Okul 'un tanımlarında yapılan hiçbir değişiklik bu programı etkilemez. Öğrenci ve Okul 'u ancak genel erişime açık olan arayüzleri aracılığıyla kullanabilir.org 352 . Nimet Dikkat ederseniz.Sarma ve Erişim Hakları Bu program. ne de Okul 'un üyelerine erişemez ve bu yüzden nesneler her zaman için tutarlıdır: Tolga bir erkek öğrencidir 2 kız. toplam 3 öğrenci: Leyla. Ne Öğrenci 'nin. O işlevlerin kullanımları değiştirilmediği sürece. 1 erkek. Copyright © 2009-2011 Ali Çehreli.

Çünkü dinamik dizilerde length niteliğine yeni bir değer atamak.length == 3). 9 ]. Nitelikler. yapı ve sınıf işlevlerinin sanki üye değişkenlermiş gibi çağrılmalarını sağlayan bir olanaktır. 20). double boy. bu niteliğin diğer kullanımı bunun doğru olamayacağını gösterir. yani uzunluğu bildirmesine bakarsak. Ancak. Bu olanağı dinamik dizilerden tanıyorsunuz.alan).1 Değer üreten nitelik işlevleri Çok basit bir örnek olarak yalnızca en ve boy üyeleri bulunan bir dikdörtgen yapısına bakalım: struct Dikdörtgen { double en. length 'in bir üye değişken olarak tasarlandığını düşünebiliriz: struct BirDiziGerçekleştirmesi { int length.. Copyright © 2009-2011 Ali Çehreli. 62. assert(dizi. bu dikdörtgenin alanını bildiren bir üyesinin olmasını da isteyelim: auto bahçe = Dikdörtgen(10. http://ddili. Dizilerin length niteliği. dizi uzunluğunu belki de yeni elemanlar ekleyecek biçimde değiştirir: dizi. length 'e yapılan o atamanın arkasında üye değeri değiştirmekten çok daha karmaşık başka işlemler gizlidir: • dizinin kapasitesinin yeni elemanlar için yeterli olup olmadığına bakılması • gerekiyorsa daha büyük yeni bir yer ayrılması • eski elemanların o yeni yerin baş tarafına kopyalanmaları Bu açıdan bakınca. 8. Yalnızca bu kullanıma. duruma göre belki de çok karmaşık işlemleri olan işlevlerdir. } Daha sonradan.length == 5).length = 5. // şimdi 5 eleman var Not: Sabit uzunluklu dizilerde length niteliği değiştirilemez. writeln(bahçe. dizideki eleman adedini bildirir: int[] dizi = [ 7. Bu işlevler @property belirteciyle işaretlenerek tanımlanırlar. üye değişken gibi kullanılmalarına rağmen.Nitelikler 62 Nitelikler Nitelikler. assert(dizi. } // . length 'e yapılan atamanın aslında bir işlev gibi çalışması gerektiği görülebilir. Yukarıdaki atama yalnızca bir değer değişikliği değildir..org 353 .

} Bu tasarımın sakıncası. Bu olanak sayesinde. Artık o yapıyı sanki üçüncü bir üyesi varmış gibi kullanabiliriz: auto bahçe = Dikdörtgen(10. bu yapı nesnelerinin tutarsız durumlara düşebilecek olmalarıdır: aralarında her zaman için "en * boy == alan" gibi bir ilişkinin bulunması gerekirken. alan bilgisini D'nin nitelik olanağından yararlanarak sunmaktır.alan = 50. Bu işlevin dönüş değeri. onun değeri. double boy. nesne kurulurken tamamen ilgisiz değerler kullanılabilir: // Tutarsız nesne: alanı 10 * 20 == 200 değil.org 354 . @property double alan() const { return en * boy. writeln("Bahçenin alanı: ". 20).2 Atama işleci ile kullanılan nitelik işlevleri Dizilerin length niteliğinde olduğu gibi. 1111). Bir örnek olarak. kendi tanımladığımız nitelikleri de atama işlemlerinde kullanabiliriz. yani bu durumda alan . Örneğin Dikdörtgen yapısının alanının böyle bir atama ile değiştirilebilmesini isteyelim: bahçe. double alan. @property olarak işaretlenmiş olan bir işlevin sonucu olarak hesaplanır. } } Not: İşlev bildiriminin sonundaki const . 20. İşte böyle durumları önlemenin bir yolu. bahçe. niteliğin değeri haline gelir: struct Dikdörtgen { double en. üye değişken gibi kullanılacak olan isimdir. bunu o söz dizimiyle gerçekleştirmek için bir üçüncü üye eklememiz gerekir: struct Dikdörtgen { double en. 1111 auto bahçe = Dikdörtgen(10. bu nesnenin bu işlev içinde değiştirilmediğini bildirir. alan niteliğinin değeri işlevde enin ve boyun çarpımı olarak hesaplandığı için her zaman tutarlı olacaktır: Bahçenin alanı: 200 62. double boy. bu yapının üyeleri kullanıcılar tarafından serbestçe değiştirilebildikleri için bu ilişki kullanım sırasında bozulabilir. const ref Parametreler ve const Üye İşlevler dersinden hatırlayacağınız gibi. Copyright © 2009-2011 Ali Çehreli. http://ddili.alan). Bu durumda yapıya yeni üye eklenmez. İşlevin ismi.Nitelikler Şimdiye kadarki derslerde öğrendiğimiz kadarıyla.

kenarların uzunlukları yarıya iner: Bahçenin alanı: 200 Yeni durum: 5 x 10 = 50 62. } Atama işlemi ile kullanılan işlevde std. double boy. Dikdörtgenin hem eni hem de boyu. en *= büyültme.math. import std. İşlevin ismi yine niteliğin isminin aynısıdır. Bunu sağlamak için dikdörtgenimizin esnek olduğunu kabul edebiliriz: "en * boy == alan" ilişkisini koruyabilmek için kenar uzunluklarının uygun şekilde değişmeleri gerekir.alan = 50. oranın karekökü kadar değişince alan da yeni değere gelmiş olur.boy. aynı işi üye işlevler yoluyla da gerçekleştirebiliriz: import std. } } void main() { auto bahçe = Dikdörtgen(10. yani 200 yerine 50 atandığında. import std. Ancak bu hiçbir zaman kesinlikle gerekmez.alan). @property double alan() const { return en * boy.Nitelikler O atamanın sonucunda alanın gerçekten değişmesi için dikdörtgenin üyelerinin. Atama işleminin sağ tarafında kullanılan değer. bahçe. Niteliklerin atama işleminde kullanılmalarını sağlayan işlev de @property ile işaretlenir. bu işlevin tek parametresinin değeri olarak gelir. bahçe. bahçe. yani eninin veya boyunun değişmesi gerekir. boy *= büyültme. çünkü değişik şekilde yazılıyor olsa da. } @property void alan(double yeniAlan) { const double büyültme = sqrt(yeniAlan / alan). Copyright © 2009-2011 Ali Çehreli. bahçe. 20).math modülünün karekök almaya yarayan işlevi olan sqrt 'u kullandım. bahçe. http://ddili.math. struct Dikdörtgen { double en. writeln("Bahçenin alanı: ".alan).stdio. writefln("Yeni durum: %s x %s = %s". Örneğin alan niteliğine dörtte biri kadar bir değer atandığında.3 Nitelikler şart değildir Yukarıdaki örnekteki yapının nasıl sanki üçüncü bir üyesi varmış gibi kullanılabildiğini gördük.en.stdio. alan niteliğine değer atamayı sağlayan bir sınıf şöyle yazılabilir: import std.org 355 .

Nitelikler

struct Dikdörtgen { double en; double boy; double alan() const { return en * boy; } void alanDeğiştir(double yeniAlan) { const double büyültme = sqrt(yeniAlan / alan); en *= büyültme; boy *= büyültme;

}

}

void main() { auto bahçe = Dikdörtgen(10, 20); writeln("Bahçenin alanı: ", bahçe.alan()); bahçe.alanDeğiştir(50); writefln("Yeni durum: %s x %s = %s", bahçe.en, bahçe.boy, bahçe.alan());

}

Hatta, İşlev Yükleme dersinde de anlatıldığı gibi, bu iki işlevin ismini aynı da seçebiliriz: double alan() const { // ... } void alan(double yeniAlan) { // ... }

62.4

Ne zaman kullanmalı
Bu derste anlatılan nitelik işlevleri ile daha önceki derslerde gördüğümüz erişim işlevleri arasında seçim yapmak, her zaman kolay olmayabilir. Bazen erişim işlevleri, bazen nitelikler, bazen de ikisi birden doğal gelecektir. Niteliklerin kullanılmamaları da bir kayıp değildir. Örneğin C++ gibi başka bazı dillerde nitelik olanağı bulunmaz. Ancak ne olursa olsun, Sarma ve Erişim Hakları dersinde gördüğümüz gibi, üyelere doğrudan erişimin engellenmesi önemlidir. Yapı ve sınıf tasarımları zamanla geliştikçe, üyelerin kullanıcı kodları tarafından doğrudan değiştirilmeleri sorun haline gelebilir. O yüzden, üye erişimlerini mutlaka nitelikler veya erişim işlevleri yoluyla sağlamanızı öneririm. Örneğin yukarıdaki Dikdörtgen yapısının en ve boy üyelerinin erişime açık bırakılmaları, yani public olmaları, ancak çok basit yapılarda kabul edilir bir davranıştır. Normalde bunun yerine ya üye işlevler, ya da nitelikler kullanılmalıdır: struct Dikdörtgen { private: double en_; double boy_;

Copyright © 2009-2011 Ali Çehreli, http://ddili.org

356

Nitelikler

public: @property double alan() const { return en * boy; } @property void alan(double yeniAlan) { const double büyültme = sqrt(yeniAlan / alan); en_ *= büyültme; boy_ *= büyültme;

}

@property double en() const { return en_; } @property double boy() const { return boy_; }

}

Üyelerin private olarak işaretlendiklerine ve o sayede değerlerine yalnızca nitelik işlevleri yoluyla erişilebildiklerine dikkat edin. Ayrıca aynı isimdeki nitelik işlevleriyle karışmasınlar diye üyelerin isimlerinin sonlarına _ karakteri eklediğime dikkat edin. Üye isimlerinin bu şekilde farklılaştırılmaları, nesneye yönelik programlamada oldukça sık karşılaşılan bir uygulamadır. Yukarıda da gördüğümüz gibi; üyelere erişimin nitelik işlevleri yoluyla sağlanması, kullanım açısından hiçbir fark getirmez. en ve boy yine sanki nesnenin üyeleriymiş gibi kullanılabilir: auto bahçe = Dikdörtgen(10, 20); writeln("en: ", bahçe.en, " boy: ", bahçe.boy); Hatta; atama işleci ile kullanılan nitelik işlevini bu üyeler için bilerek tanımlamadığımız için, enin ve boyun dışarıdan değiştirilmeleri de artık olanaksızdır: bahçe.en = 100; // ← derleme HATASI

Bu da, üyelere yapılan değişikliklerin kendi denetimimiz altında olması açısından çok önemlidir. Bu üyeler ancak bu sınıfın kendi işlevleri tarafından değiştirilebilirler. Nesnelerin tutarlılıkları bu sayede bu yapının veya sınıfın işlevleri tarafından sağlanabilir. Dışarıdan değiştirilmelerinin yine de uygun olduğu üyeler varsa, atamayı sağlayan nitelik işlevi onlar için özel olarak tanımlanabilir.

Copyright © 2009-2011 Ali Çehreli, http://ddili.org

357

Yapı ve Sınıflarda Sözleşmeli Programlama

63

Yapı ve Sınıflarda Sözleşmeli Programlama
Sözleşmeli programlama, kod hatalarını azaltmaya yarayan çok etkili bir olanaktır. D'nin sözleşmeli programlama olanaklarından ikisini Sözleşmeli Programlama dersinde görmüştük. in ve out blokları, işlevlerin giriş ve çıkış koşullarını denetlemek için kullanılıyordu. Hatırlamak amacıyla, üçgen alanını Heron formülünü kullanarak kenar uzunluklarından hesaplayan bir işlev yazalım. Üçgenin alanının doğru olarak hesaplanabilmesi için üç kenar uzunluğunun da sıfır veya daha büyük olması gerekir. Ek olarak, bir üçgenin hiçbir kenarının diğer ikisinin toplamından uzun olmaması da gerekir. O giriş koşulları sağlandığında, üçgenin alanı da sıfır veya daha büyük olacaktır. Bu koşulları ve bu garantiyi sağlayan bir işlev şöyle yazılabilir: private import std.math; double üçgenAlanı(in double a, in double b, in double c) in { // Kenarlar sıfırdan küçük olamaz assert(a >= 0); assert(b >= 0); assert(c >= 0); // Hiçbir kenar diğer ikisinin toplamından uzun olamaz assert(a <= (b + c)); assert(b <= (a + c)); assert(c <= (a + b));

} out (sonuç) { assert(sonuç >= 0); } body { const double yarıÇevre = return sqrt(yarıÇevre * (yarıÇevre * (yarıÇevre * (yarıÇevre }

(a + b + c) / 2; - a) - b) - c));

63.1

Üye işlevlerin in ve out blokları
in ve out blokları üye işlevlerle de kullanılabilir ve aynı şekilde, işlevin giriş koşullarını ve çıkış garantisini denetler. Yukarıdaki alan hesabı işlevini bir üye işlev olarak yazalım: import std.stdio; import std.math; struct Üçgen { private: double a_; double b_; double c_; public: @property double alan() const out (sonuç)

Copyright © 2009-2011 Ali Çehreli, http://ddili.org

358

Yapı ve Sınıflarda Sözleşmeli Programlama

{ } body {

assert(sonuç >= 0);

}

}

const double yarıÇevre = return sqrt(yarıÇevre * (yarıÇevre * (yarıÇevre * (yarıÇevre

(a_ + b_ + c_) / 2; - a_) - b_) - c_));

void main() { auto üçDörtBeşÜçgeni = Üçgen(3, 4, 5); writeln(üçDörtBeşÜçgeni.alan); } Üçgenin kenarları zaten yapının üye değişkenleri oldukları için, bu işlevin parametreleri yok. O yüzden bu işlevin in bloğunu yazmadım. Üye değişkenlerin tutarlılıkları için aşağıdaki bilgileri kullanmanız gerekir.

63.2

Nesnelerin geçerliliği için in ve out blokları
Yukarıdaki üye işlev parametre almadığı için in bloğunu yazmadık. İşlevdeki hesabı da nesnenin üyelerini kullanarak yaptık. Yani bir anlamda üyelerin geçerli değerlere sahip olduklarını varsaydık. Bu varsayımın doğru olmasını sağlamanın bir yolu, sınıfın kurucu işlevine in bloğu eklemektir. Böylece kurucunun aldığı parametrelerin geçerli olduklarını en başından, daha nesne kurulmadan denetleyebiliriz. in bloğunu da yazabilmek için, kurucu işlevi açıkça bizim tanımlamamız gerekir; derleyicinin otomatik olarak sunduğu kurucuyu kullanamayız: class Üçgen { // ... this(in double a, in double b, in double c) in { // Kenarlar sıfırdan küçük olamaz assert(a >= 0); assert(b >= 0); assert(c >= 0); // Hiçbir kenar diğer ikisinin toplamından uzun olamaz assert(a <= (b + c)); assert(b <= (a + c)); assert(c <= (a + b));

} body {

} // ... }

a_ = a; b_ = b; c_ = c;

Bu sayede, üçgen nesnelerinin geçersiz değerlerle oluşturulmaları en başından engellenmiş olur. Artık programın geçersiz değerlerle kurulmuş olan bir üçgen nesnesi kullanması olanaksızdır:

Copyright © 2009-2011 Ali Çehreli, http://ddili.org

359

Yapı ve Sınıflarda Sözleşmeli Programlama

auto eksiKenarUzunluklu = Üçgen(-1, 1, 1); auto birKenarıFazlaUzun = Üçgen(1, 1, 10); Kurucu işlevin in bloğu, yukarıdaki geçersiz nesnelerin oluşturulmalarına izin vermez: core.exception.AssertError@deneme.d: Assertion failure Bu sefer de out bloğunu yazmadığıma dikkat edin. Eğer gerekirse, daha karmaşık türlerde kurucu işlevin out bloğu da yazılabilir. O da nesnenin üyeleri kurulduktan sonra gerekebilecek denetimler için kullanılabilir.

63.3

Nesnelerin tutarlılığı için invariant bloğu
Kurucuya eklenen in ve out blokları, nesnenin yaşamının geçerli değerlerle başlayacağını; üyelere eklenen in ve out blokları da işlevlerin doğru şekilde çalışacaklarını garanti eder. Ancak bu denetimler, nesnenin üyelerinin her zaman için geçerli veya tutarlı olacaklarını garanti etmeye elverişli değillerdir. Nesnenin üyeleri, üye işlevler içinde programcı hataları sonucunda tutarsız değerler edinebilirler. Nesnenin tutarlılığını tarif eden koşullara "mutlak değişmez" denir. Örneğin bir müşteri takip sınıfında her siparişe karşılık bir fatura bulunacağını varsayarsak; fatura adedinin sipariş adedinden fazla olamayacağı, bu sınıfın bir mutlak değişmezidir. Eğer bu koşulun geçerli olmadığı bir müşteri takip nesnesi varsa, o nesnenin tutarlı durumda olduğunu söyleyemeyiz. Bunun bir örneği olarak Sarma ve Erişim Hakları dersinde kullandığımız Okul sınıfını ele alalım: class Okul { private: Öğrenci[] öğrenciler; int kızToplamı; int erkekToplamı; // ... } Bu sınıftan olan nesnelerin tutarlı olarak kabul edilmeleri için, üç üyesi arasındaki bir mutlak değişmezin sağlanması gerekir. Öğrenci dizisinin uzunluğu, her zaman için kız öğrencilerin toplamı ile erkek öğrencilerin toplamına eşit olmalıdır: assert(öğrenciler.length == kızToplamı + erkekToplamı); O koşulun bozulmuş olması, bu sınıf kodlarında yapılan bir hatanın göstergesidir. Yapı ve sınıf nesnelerinin tutarlılıkları, o türün invariant bloğunda denetlenir. Bu blok, yapı veya sınıf tanımı içine yazılır ve sınıf nesnelerinin tutarlılık koşullarını içerir. in ve out bloklarında olduğu gibi, burada da assert ifadeleri kullanılır: class Okul { private: Öğrenci[] öğrenciler; int kızToplamı; int erkekToplamı;

Copyright © 2009-2011 Ali Çehreli, http://ddili.org

360

Yapı ve Sınıflarda Sözleşmeli Programlama

invariant() { assert( öğrenciler.length == kızToplamı + erkekToplamı); } // ... } Yapı ve sınıf tanımlarında yalnızca bir tane invariant bloğu bulunabilir. invariant bloklarındaki kodlar aşağıdaki zamanlarda otomatik olarak işletilir, ve bu sayede programın yanlış verilerle devam etmesi önlenmiş olur: • kurucu işlev sonunda; böylece nesnenin yaşamına tutarlı olarak başladığı garanti edilir • sonlandırıcı işlev çağrılmadan önce; böylece sonlandırma işlemlerinin tutarlı üyeler üzerinde yapılacakları garanti edilir • public bir işlevden önce ve sonra; böylece üye işlevlerdeki kodların nesneyi bozmadıkları garanti edilir Not: Yukarıdaki listede public işlevler için söylenenler export işlevler için de geçerlidir. export işlevleri kısaca "dinamik kütüphalerin sundukları işlevler" olarak tanımlayacağım, fakat ayrıntılarına burada girmeyeceğim. invariant bloğundaki denetimlerin başarısız olmaları da in ve out bloklarında olduğu gibi AssertError atılmasına neden olur. Bu sayede programın tutarsız nesnelerle devam etmesi önlenmiş olur. in ve out bloklarında olduğu gibi, invariant blokları da -release seçeneği ile iptal edilebilir: dmd deneme.d -w -release

63.4

Özet
in ve out bloklarını üye işlevlerle de kullanabilirsiniz; kurucu işleve ekleyerek nesnelerin geçersiz parametrelerle kurulmalarını önleyebilirsiniz. Nesnelerin yaşamları boyunca her zaman için tutarlı olmalarını garantilemek için invariant bloklarını kullanabilirsiniz.

Copyright © 2009-2011 Ali Çehreli, http://ddili.org

361

Şablonlar

64

Şablonlar
Şablonlar, derleyicinin belirli bir kalıba uygun olarak kod üretmesini sağlayan olanaktır. Herhangi bir kod parçasının bazı bölümleri sonraya bırakılabilir; ve derleyici o kod bölümlerini gereken türler, değerler, vs. için kendisi oluşturur. Şablonlar algoritmaların ve veri yapılarının türden bağımsız olarak yazılabilmelerini sağlarlar; ve bu yüzden özellikle kütüphanelerde çok yararlıdırlar. D'nin şablon olanağı bazı başka dillerdekilerle karşılaştırıldığında çok güçlü ve çok kapsamlıdır. Bu yüzden şablonların bütün ayrıntılarına bu derste giremeyeceğim. Burada; gündelik kullanımda en çok karşılaşılan işlev, yapı, ve sınıf şablonlarının türlerle nasıl kullanıldıklarını göstereceğim. Şablonların yararlarını anlamak için, kendisine verilen değeri parantez içinde yazdıran basit bir işleve bakalım: void parantezliYazdır(int değer) { writefln("(%s)", değer); } Parametresi int olarak tanımlandığı için, o işlev yalnızca int türüyle veya otomatik olarak int 'e dönüşebilen türlerle kullanılabilir. Derleyici, örneğin kesirli sayı türleriyle çağrılmasına izin vermez. O işlevi kullanan programın geliştiğini, ve artık başka türlerden olan değerleri de parantez içinde yazdırmaya ihtiyaç duyulduğunu düşünelim. Bunun için bir çözüm, D'nin işlev yükleme olanağıdır; aynı işlevi örneğin double için de tanımlayabiliriz: void parantezliYazdır(double değer) { writefln("(%s)", değer); } Bu da ancak belirli bir noktaya kadar yeterlidir; çünkü bu işlevi bu sefer de örneğin real türüyle, veya kendi tanımlamış olabileceğimiz başka türlerle kullanamayız. Tabii işlevi o türler için de yüklemeyi düşünebiliriz, ama her tür için ayrı ayrı yazılmasının çok külfetli olacağı açıktır. Burada dikkatinizi çekmek istediğim nokta; tür ne olursa olsun, işlevin içeriğinin hep aynı olduğudur. Türler için yüklenen bu işlevdeki işlemler, türden bağımsız olarak hepsinde aynıdır. Benzer durumlar özellikle algoritmalarda ve veri yapılarında karşımıza çıkarlar. Örneğin ikili arama algoritması türden bağımsızdır: o algoritma, yalnızca işlemlerle ilgilidir. Aynı şekilde, örneğin bağlı liste veri yapısı da türden bağımsızdır: yalnızca topluluktaki elemanların nasıl bir arada tutulduklarını belirler. İşte şablonlar bu gibi durumlarda yararlıdır: kod, bir kalıp halinde tarif edilir; ve derleyici, programda kullanılan türler için kodu gerektikçe kendisi üretir.

64.1

İşlev şablonları
İşlevi bir kalıp olarak tarif etmek, içinde kullanılan bir veya daha fazla türün belirsiz olarak sonraya bırakılması anlamına gelir.

Copyright © 2009-2011 Ali Çehreli, http://ddili.org

362

Şablonlar

İşlevdeki hangi türlerin sonraya bırakıldıkları, işlev parametrelerinden hemen önce yazılan şablon parametrelerinde belirtilir. Bu yüzden işlev şablonlarında iki adet parametre parantezi bulunur; birincisi şablon parametreleridir, ikincisi de işlev parametreleri: void parantezliYazdır(T)(T değer) { writefln("(%s)", değer); } Yukarıda şablon parametresi olarak kullanılan T , "bu işlevde T yazdığımız yerlerde asıl hangi türün kullanılacağına derleyici gerektikçe kendisi karar versin" anlamındadır. T yerine herhangi başka bir isim de yazılabilir; ancak, "type"ın baş harfi olduğu için T harfi gelenekleşmiştir. "Tür"ün baş harfine de uyduğu için aksine bir neden olmadığı sürece T kullanmak yerinde olacaktır. O şablonu öyle bir kere yazmış olmak; kendi türlerimiz de dahil olmak üzere, onu çeşitli türlerle çağırma olanağı sağlar: import std.stdio; void parantezliYazdır(T)(T değer) { writefln("(%s)", değer); } void main() { parantezliYazdır(42); parantezliYazdır(1.2); auto birDeğer = BirYapı(); parantezliYazdır(birDeğer);

// int ile // double ile // BirYapı nesnesi ile

}

struct BirYapı { string toString() const { return "merhaba"; } } Derleyici; programdaki kullanımlarına bakarak, yukarıdaki işlev şablonunu gereken her tür için ayrı ayrı üretir. Program, sanki o işlev T 'nin kullanıldığı üç farklı tür için, yani int , double , ve BirYapı için ayrı ayrı yazılmış gibi derlenir: /* * Not: Bu işlevlerin hiçbirisi programa dahil değildir. * Derleyicinin kendi ürettiği işlevlerin eşdeğerleri * olarak gösteriyorum. */ void parantezliYazdır(int değer) { writefln("(%s)", değer); } void parantezliYazdır(double değer) { writefln("(%s)", değer); } void parantezliYazdır(BirYapı değer) {

Copyright © 2009-2011 Ali Çehreli, http://ddili.org

363

Şablonlar

}

writefln("(%s)", değer);

Programın çıktısı da o üç farklı işlevin etkisini gösterecek şekilde, her tür için farklıdır: (42) (1.2) (merhaba)

64.2

Birden fazla şablon parametresi kullanılabilir
Aynı işlevi, açma ve kapama parantezlerini de kullanıcıdan alacak şekilde değiştirdiğimizi düşünelim: void parantezliYazdır(T)(T değer, char açma, char kapama) { writeln(açma, değer, kapama); } Artık o işlevi, istediğimiz parantez karakterleri ile çağırabiliriz: parantezliYazdır(42, '<', '>'); Parantezleri belirleyebiliyor olmak, işlevin kullanışlılığını arttırmış olsa da; parantezlerin türünün char olarak sabitlenmiş olmaları, işlevin kullanışlılığını tür açısından düşürmüştür. İşlevi örneğin ancak wchar ile ifade edilebilen Unicode karakterleri arasında yazdırmaya çalışsak, wchar 'ın char 'a dönüştürülemeyeceği ile ilgili bir derleme hatası alırız: parantezliYazdır(42, '→', '←'); // ← derleme HATASI

Error: template deneme.parantezliYazdır(T) cannot deduce template function from argument types !()(int,wchar,wchar) Bunun bir çözümü; parantez karakterlerini, her karakteri ifade edebilen dchar olarak tanımlamaktır. Bu da yetersiz olacaktır, çünkü bu sefer de örneğin string ile veya kendi özel türlerimizle kullanılamaz. Başka bir çözüm, yine şablon olanağından yararlanmak ve parantezin türünü de derleyiciye bırakmaktır. Yapmamız gereken, işlev parametresi olarak char yerine yeni bir şablon parametresi kullanmak; ve onu da şablon parametre listesinde belirtmektir: void parantezliYazdır(T, ParantezTürü)(T değer, ParantezTürü açma, ParantezTürü kapama) { writeln(açma, değer, kapama); } Yeni şablon parametresinin anlamı da T 'ninki gibidir: "bu işlev tanımında ParantezTürü geçen yerlerde, aslında hangi tür gerekiyorsa o kullanılsın". Artık parantez olarak herhangi bir tür kullanılabilir. Örneğin wchar ve string türleriyle: parantezliYazdır(42, '→', '←'); parantezliYazdır(1.2, "-=", "=-");

Copyright © 2009-2011 Ali Çehreli, http://ddili.org

364

Şablonlar

→42← -=1.2=Bu şablonun yararı; tek bir işlev tanımlamış olduğumuz halde T ve ParantezTürü şablon parametrelerinin otomatik olarak belirlenebilmeleridir.

64.3

Tür çıkarsama
Derleyici yukarıdaki iki kullanımda şu türleri otomatik olarak seçer: • 42'nin yazdırıldığı satırda int ve wchar • 1.2'nin yazdırıldığı satırda double ve string İşlevin çağrıldığı noktalarda hangi türlerin gerektiği, işlevin parametrelerinden kolayca anlaşılabilmektedir. Derleyicinin türü işlev çağrılırken kullanılan parametrelerden anlamasına tür çıkarsaması denir. Derleyici, şablon parametrelerini ancak ve ancak işlev çağrılırken kullanılan türlerden çıkarsayabilir.

64.4

Türün açıkça belirtilmesi
Bazı durumlarda ise şablon parametreleri çıkarsanamazlar, çünkü örneğin işlevin parametresi olarak geçmiyorlardır. Onların derleyici tarafından çıkarsanmaları olanaksızdır. Örnek olarak kullanıcıya bir soru soran ve o soru karşılığında girişten bir değer okuyan bir işlev düşünelim. Bu işlev, okuduğu değeri döndürüyor olsun. Ayrıca, bütün türler için kullanılabilmesi için de dönüş türünü sabitlemeyelim ve bir şablon parametresi olarak tanımlayalım: T giriştenOku(T)(string soru) { writef("%s (%s): ", soru, T.stringof); T cevap; readf(" %s", &cevap); } return cevap;

O işlev, girişten okuma işini türden bağımsız olarak gerçekleştirdiği için programda çok yararlı olacaktır. Örneğin kullanıcı bilgilerini edinmek için şu şekilde çağırmayı düşünebiliriz: giriştenOku("Yaşınız?"); Ancak, o çağırma sırasında T 'nin hangi türden olacağını belirten hiçbir ipucu yoktur. Soru işleve string olarak gitmektedir, ama derleyici dönüş türü için hangi türü istediğimizi bilemez ve T 'yi çıkarsayamadığını bildiren bir hata verir: Error: template deneme.giriştenOku(T) cannot deduce template function from argument types !()(string) Bu gibi durumlarda şablon parametrelerinin ne oldukları programcı tarafından açıkça belirtilmek zorundadır. Şablonun hangi türlerle üretileceği, yani şablon parametreleri, işlev isminden sonraki ünlem işareti ve hemen ardından gelen şablon parametre listesi ile bildirilir:

Copyright © 2009-2011 Ali Çehreli, http://ddili.org

365

Şablonlar

giriştenOku!(int)("Yaşınız?"); O kod artık derlenir ve yukarıdaki şablon, T yerine int yazılmış gibi derlenir. Tek bir şablon parametresinin belirtildiği durumlarda, bir kolaylık olarak şablon parantezleri yazılmayabilir: giriştenOku!int("Yaşınız?"); // üsttekiyle aynı şey

O yazılışı şimdiye kadar çok kullandığımız to!string 'den tanıyorsunuz. to bir işlev şablonudur. Ona verdiğimiz değerin hangi türe dönüştürüleceğini bir şablon parametresi olarak alır. Tek bir şablon parametresi gerektiği için de to!(string) yerine, onun kısası olan to!string yazılır.

64.5

Şablon özellemeleri
giriştenOku işlevini başka türlerle de kullanabiliriz. Ancak, derleyicinin ürettiği kod her tür için geçerli olmayabilir. Örneğin iki boyutlu düzlemdeki bir noktayı ifade eden bir yapımız olsun: struct Nokta { int x; int y; } Her ne kadar yasal olarak derlenebilse de, giriştenOku şablonunu bu yapı ile kullanırsak, şablon içindeki readf işlevi doğru çalışmaz. Şablon içinde Nokta türüne karşılık olarak üretilen kod şöyle olacaktır: Nokta cevap; readf(" %s", &cevap);

// YANLIŞ

Doğrusu, Nokta 'yı oluşturacak olan x ve y değerlerinin girişten ayrı ayrı okunmaları ve nesnenin bu değerlerle kurulmasıdır. Böyle durumlarda, şablonun belirli bir tür için özel olarak tanımlanmasına özelleme denir. Özelleme, şablon parametre listesinde, hangi tür için özellendiği : karakterinden sonra yazılarak belirtilir: T giriştenOku(T : Nokta)(string soru) { writefln("%s (Nokta)", soru); auto x = giriştenOku!int(" auto y = giriştenOku!int(" } return Nokta(x, y); x"); y");

giriştenOku işlevi bir Nokta için çağrıldığında, derleyici o özel tanımı kullanır: auto merkez = giriştenOku!Nokta("Merkez?"); O işlev de kendi içinde giriştenOku!int 'i iki kere çağırarak x ve y değerlerini ayrı ayrı okur:

Copyright © 2009-2011 Ali Çehreli, http://ddili.org

366

Şablonlar

Merkez? (Nokta) x (int): 11 y (int): 22 Başka bir örnek olarak, şablonu string ile kullanmayı da düşünebiliriz. Ne yazık ki şablonun genel tanımı, bu durumda dizginin girişin sonuna kadar okunmasına neden olur: // bütün girişi okur: auto isim = giriştenOku!string("İsminiz?"); Eğer string 'lerin tek satır olarak okunmalarının uygun olduğunu kabul edersek, bu durumda da çözüm şablonu string için özel olarak tanımlamaktır: T giriştenOku(T : string)(string soru) { writef("%s (string): ", soru); return chomp(readln()); }

64.6

Yapı ve sınıf şablonları
Yukarıdaki Nokta sınıfının iki üyesi int olarak tanımlanmış oldukları için, işlev şablonlarında karşılaştığımız yetersizlikler onda da vardır. Nokta yapısının daha kapsamlı olduğunu düşünelim. Örneğin kendisine verilen başka bir noktaya olan uzaklığını hesaplayabilsin: import std.math; // ... struct Nokta { int x; int y; int uzaklık(in Nokta diğerNokta) const { const real xFarkı = x - diğerNokta.x; const real yFarkı = y - diğerNokta.y; const auto uzaklık = sqrt((xFarkı * xFarkı) + (yFarkı * yFarkı)); } return cast(int)uzaklık;

}

O yapı, örneğin kilometre duyarlığındaki uygulamalarda yeterlidir: auto merkez = giriştenOku!Nokta("Merkez?"); auto şube = giriştenOku!Nokta("Şube?"); writeln("Uzaklık: ", merkez.uzaklık(şube)); Ancak, kesirli değerler gerektiren daha hassas uygulamalarda kullanışsızdır. Yapı ve sınıf şablonları, onları da belirli bir kalıba uygun olarak tanımlama olanağı sağlarlar. Bu durumda yapının tanımındaki int 'ler yerine T kullanmak, bu tanımın bir şablon haline gelmesi ve üyelerin türlerinin derleyici tarafından belirlenmesi için yeterlidir:

Copyright © 2009-2011 Ali Çehreli, http://ddili.org

367

Şablonlar

struct Nokta(T) { T x; T y; T uzaklık(in Nokta diğerNokta) const { const real xFarkı = x - diğerNokta.x; const real yFarkı = y - diğerNokta.y; const auto uzaklık = sqrt((xFarkı * xFarkı) + (yFarkı * yFarkı)); } return cast(T)uzaklık;

}

Yapı ve sınıflar işlev olmadıklarından, çağrılmaları söz konusu değildir. Bu yüzden, derleyicinin şablon parametrelerini çıkarsaması olanaksızdır; türleri açıkça belirtmemiz gerekir: auto merkez = Nokta!int(0, 0); auto şube = Nokta!int(100, 100); writeln("Uzaklık: ", merkez.uzaklık(şube)); Yukarıdaki kullanım, derleyicinin Nokta şablonunu T yerine int gelecek şekilde üretmesini sağlar. Doğal olarak, başka türler de kullanabiliriz. Örneğin virgülden sonrasının önemli olduğu bir uygulamada: auto nokta1 = Nokta!double(1.2, 3.4); auto nokta2 = Nokta!double(5.6, 7.8); writeln(nokta1.uzaklık(nokta2)); Yapı ve sınıf şablonları, veri yapılarını böyle türden bağımsız olarak tanımlama olanağı sunar. Dikkat ederseniz, Nokta şablonundaki üyeler ve işlemler tamamen T 'nin asıl türünden bağımsız olarak yazılmışlardır. Nokta yapısını bir yapı şablonu olarak yazmak, giriştenOku işlev şablonunun daha önce yazmış olduğumuz özellemesinde bir sorun oluşturur: T giriştenOku(T : Nokta)(string soru) { writefln("%s (Nokta)", soru); auto x = giriştenOku!int(" auto y = giriştenOku!int(" } return Nokta(x, y); x"); y"); // ← derleme HATASI

Hatanın nedeni, artık Nokta diye bir tür bulunmamasıdır. Nokta ; artık bir tür değil, bir yapı şablonudur. Bir tür olarak kabul edilebilmesi için, mutlaka şablon parametresinin de belirtilmesi gerekir. giriştenOku işlev şablonunu bütün Nokta kullanımları için özellemek için aşağıdaki değişiklikleri yapabiliriz. Açıklamalarını koddan sonra yapacağım: Nokta!T giriştenOku(T : Nokta!T)(string soru) { writefln("%s (Nokta!%s)", soru, T.stringof); auto x = giriştenOku!T(" x"); // 2, 1 // 5 // 3a

Copyright © 2009-2011 Ali Çehreli, http://ddili.org

368

7 Varsayılan şablon parametreleri Şablonların getirdiği bu esneklik çok kullanışlı olsa da. döndürdüğümüz değer de bir Nokta!T olmak zorundadır 5. 1 ve 2 numaralı maddelere benzer şekilde. T2 = char)() { // . bu özellemenin Nokta!T türleri için olduğu belirtilmektedir 2. Ek olarak. } // . şablon parametrelerinin her sefer belirtilmeleri bazen gereksiz olabilir. giriştenOku şablonunu o türden değer okuyacak şekilde. okumakta olduğumuz türün "(Nokta)" yerine örneğin "(Nokta!double)" olarak bildirilmesi için şablon türünün ismini T. http://ddili. şablon parametre listesinde Nokta!T yazılması gerekir. çünkü Nokta 'nın üyeleri herhangi bir tür olabilir.. ama birincisi şarttır: birŞablon!(string).Şablonlar auto y = giriştenOku!T(" } return Nokta!T(x. Copyright © 2009-2011 Ali Çehreli. bu işlevin önceki tanımında olduğu gibi giriştenOku!int 'i çağıramayız. yani giriştenOku!T şeklinde çağırmamız gerekir 4. varsayılan şablon parametreleri ya bütün parametreler için. } O şablonun son iki parametresinin belirtilmesi gerekmez. Yukarıdaki işlev çağrısında şablon parametresi belirtilmediği için int varsayılır ve o çağrı giriştenOku!int ile aynıdır.. üçüncü parametre de char olur.. Varsayılan şablon parametre türleri = karakterinden sonra belirtilir: T giriştenOku(T = int)(string soru) { // . auto yaş = giriştenOku("Yaşınız?"). Örneğin giriştenOku işlev şablonu programda hemen hemen her yerde int ile kullanılıyordur. Yapı ve sınıf şablonlarında parametre değerlerinin açıkça belirtilmesi şarttır. T ne olursa olsun. Varsayılan şablon parametreleri yalnızca işlev şablonlarıyla kullanılabilirler. dönüş türünün de Nokta!T olarak belirtilmesi gerekir 3. bu yüzden. y")... ya da yalnızca sondaki parametreler için belirtilebilir: void birŞablon(T0.org 369 . bu işlev şablonu özellemesinin Nokta 'nın bütün kullanımlarını desteklemesi için. T1 = int.stringof 'tan ediniyoruz 64.. T ne ise. Parametre Serbestliği dersinde işlev parametreleri için anlatılana benzer şekilde. bir anlamda. Böyle durumlarda şablon parametrelerine varsayılan türler verilebilir ve açıkça belirtilmediğinde o türler kullanılır. belki de yalnızca bir kaç noktada örneğin double ile de kullanılıyordur. O kullanımda ikinci parametre int . // 3b // 4 1. okuduğumuz türe uyması için. y).

public: void ekle(T eleman) { elemanlar_ ~= eleman. Bunun bir örneğini görmek için bir yığın topluluğu (stack container) tanımlayalım. 0. 64. bu veri yapısını her türle kullanabilecek şekilde şöyle tanımlayabiliriz: class Yığın(T) { T[] elemanlar_. veri yapılarının en basit olanlarındandır: Elemanların üst üste durdukları düşünülür. http://ddili. yalnızca en üstteki eleman çıkartılabilir. // ← derleme HATASI Türlerin uyumsuz olduklarını gösteren bir derleme hatası alınır: Error: cannot implicitly convert expression (Nokta(0.8 Her şablon gerçekleştirmesi farklı bir türdür Bir şablonun belirli bir tür veya türler için üretilmesi yepyeni bir tür oluşturur.9 Derleme zamanı olanağıdır Şablon olanağı bütünüyle derleme zamanında işleyen ve derleyici tarafından işletilen bir olanaktır. Dizinin sonuncu elemanı. Kullanışlı olsun diye topluluktaki eleman sayısını veren bir nitelik de tasarlarsak.25. Copyright © 2009-2011 Ali Çehreli.length. Topluluktan eleman çıkartılmak istendiğinde. en üste yerleştirilir ve yalnızca bu üstteki elemana erişilebilir.25. } void çıkart() { --elemanlar_. program çalışmaya başladığında şablonların koda çevrilmeleri ve derlenmeleri çoktan tamamlanmıştır. Bu türler birbirlerinden farklıdırlar: Nokta!int nokta3 = Nokta!double(0.org 370 . Derleyicinin kod üretmesiyle ilgili olduğu için.0. Aynı şekilde. Dizi elemanı türünü de sabit bir tür olarak yazmak yerine şablon parametresi olarak belirlersek. Yığın topluluğu. Nokta!double da başlıbaşına bir türdür.75). Örneğin Nokta!int başlıbaşına bir türdür. bu basit veri yapısının işlemlerini şöyle sıralayabiliriz: • • • • eleman eklemek eleman çıkartmak üsttekine eriştirmek eleman adedini bildirmek Bu veri yapısını gerçekleştirmek için D'nin iç olanaklarından olan dizilerden yararlanabiliriz. Eklenen her eleman.75)) of type Nokta!(double) to Nokta!(int) 64.10 Sınıf şablonu örneği: yığın veri yapısı Yapı ve sınıf şablonları veri yapılarında çok kullanılırlar.Şablonlar 64. yığın topluluğunun üstteki elemanı olarak kullanılabilir.

assert(yığın. string toString() const { return format("(%s.uzunluk == 1). Çünkü ikisi de bu veri yapısıyla ilgili basit bir bilgi sunuyorlar.üstteki == 42). y). assert(yığın.org 371 . Örneğin yukarıdaki Nokta şablonunun bir benzeri olsa: struct Nokta(T) { T x.çıkart().%s)". Bu sınıf için bir unittest bloğu tanımlayarak beklediğimiz şekilde çalıştığından emin olabiliriz: unittest { auto yığın = new Yığın!int. } Bu veri yapısını bir şablon olarak tanımlamış olmanın yararını görmek için. x. onu kendi türlerimizle de kullanmayı deneyebiliriz.uzunluk == 1). http://ddili. } } Ekleme ve çıkartma işlemlerinin üye işlevler olmaları doğaldır. // Eklenen eleman üstte görünmeli yığın.ekle(42). // Tek eleman çıkartılınca boş kalmalı yığın. assert(yığın.1].çıkart(). } @property int uzunluk() const { return elemanlar_.length. assert(yığın.uzunluk == 2).uzunluk == 0). assert(yığın. } } Copyright © 2009-2011 Ali Çehreli.üstteki == 100). üstteki ve uzunluk işlevlerini ise nitelik olarak tanımlamayı daha uygun buldum. /* * Aynı işlemler tekrarlanabilmeli ve veri yapısında bir * değişikliğe yol açmamalı: */ assert(yığın. // Yeni eklenen eleman üstte görünmeli yığın. assert(yığın. assert(yığın.Şablonlar } @property T üstteki() const { return elemanlar_[$ .üstteki == 42). assert(yığın. // Eleman çıkartılınca önceki görünmeli yığın.uzunluk == 1).ekle(100).üstteki == 42). T y.

random. nokta). } // Belirtilen sayıda rasgele Nokta!double içeren bir Yığın // döndürür Yığın!(Nokta!double) rasgeleNoktalar(int adet) in { assert(adet >= 0).string. string toString() const { return format("(%s. void main() { auto üstÜsteNoktalar = rasgeleNoktalar(10). x.stdio. noktalar. rasgele_double()). import std.50 ile 0. while (üstÜsteNoktalar. http://ddili.org 372 . } } Copyright © 2009-2011 Ali Çehreli. foreach (i. 0 . struct Nokta(T) { T x.50) / 100. } } // -0. adet) { const auto nokta = Nokta!double(rasgele_double(). 100) .50 arasında rasgele bir değer döndürür double rasgele_double() out (sonuç) { assert((sonuç >= -0.çıkart().üstteki). ve sonra onları teker teker çıkartan bir deneme programı şöyle yazılabilir: import std.. } } return noktalar.50) && (sonuç < 0.uzunluk == adet).%s)". } body { return (cast(double)uniform(0. Bu veri yapısına on tane rasgele değerli nokta ekleyen. üstÜsteNoktalar. y). writeln("ekliyorum : ". T y.Şablonlar double türünde üyeleri bulunan Nokta 'ları içeren bir Yığın şablonu şöyle oluşturulabilir: auto noktalar = new Yığın!(Nokta!double).uzunluk) { writeln("çıkartıyorum: ". } body { auto noktalar = new Yığın!(Nokta!double).50)).ekle(nokta). import std. üstÜsteNoktalar. } out (sonuç) { assert(sonuç.

25.47) (0. bir dizi halinde yan yana ve sıralı olarak bulunan değerler arasında arama yapan en hızlı algoritmadır.25.32. http://ddili.0.02) (0. in int değer) { // Dizi boşsa.25.0.-0.0. ondan sonra algoritmada kullanılan int 'leri T yaparak onu bir şablona dönüştüreceğim.23.35) (0.47.25.-0.39. if (değer == değerler[ortaNokta]) { // Bulduk return ortaNokta.05) (0.24) (0.02.0.39. değer bulunmuş demektir. Eğer değilse. bulamadık demektir if (değerler.11 İşlev şablonu örneği: ikili arama algoritması İkili arama algoritması. bulamazsa -1 döndürür int ikiliAra(const int[] değerler.27) (0. // Aranan değeri bulursa.47.-0.03.1) 64.35) (-0. Çok basit bir algoritmadır: sıralı olarak bulunan değerlerin en ortadakine bakılır.0.Şablonlar Programın çıktısından anlaşılacağı gibi.0.27) (0. Ben bu işlevi yukarıdaki tanımına da çok uyduğu için kendisini çağıran bir işlev olarak yazacağım.02.-0.32.31) (0. değerin dizide hangi indekste // bulunduğunu.03.02. Copyright © 2009-2011 Ali Çehreli.length / 2.0.34) (0. İşlevi şablon olarak yazmak yerine.39) (0.24) (0.02) (0. önce int için gerçekleştireceğim.1) (0.23.47) (-0.0.-0.org 373 . Bu algoritmanın bir diğer adı "ikiye bölerek arama".0. } const auto ortaNokta = değerler.0.01.-0.34) (0. ortaNokta].01.0. Böyle kendisini tekrarlayarak tarif edilen algoritmalar özyinelemeli olarak da programlanabilirler.05) (0.-0.31) (0. değer). o orta değerin aranan değerden daha küçük veya büyük olmasına göre ya sol yarıda ya da sağ yarıda aynı algoritma tekrarlanır.0.-0.length == 0) { return -1. } else if (değer < değerler[ortaNokta]) { // İlk yarıda aramaya devam etmeliyiz return ikiliAra(değerler[0 . Eğer aranan değere eşitse.39) (0. İngilizcesi de "binary search"tür. eklenenlerle çıkartılanlar ters sırada olmaktadır: ekliyorum : ekliyorum : ekliyorum : ekliyorum : ekliyorum : ekliyorum : ekliyorum : ekliyorum : ekliyorum : ekliyorum : çıkartıyorum: çıkartıyorum: çıkartıyorum: çıkartıyorum: çıkartıyorum: çıkartıyorum: çıkartıyorum: çıkartıyorum: çıkartıyorum: çıkartıyorum: (0..02.

. sayılar). $]. indeks. } } } foreach (i. 1000) { const auto adet = uniform(1.. } Copyright © 2009-2011 Ali Çehreli. const auto indeks = ikiliAra(sayılar.random.stdio. %s var: %s".Şablonlar } else { // Son yarıda aramaya devam etmeliyiz return (ortaNokta + 1 + ikiliAra(değerler[ortaNokta + 1 . Yukarıdaki işlev. adet) { sayılar ~= i..sort. } return sayılar. int aranacak = sayılar[0]. sayılar[indeks]. import std. aynı işlevi soldaki yarıda devam ettir değilse...org 374 . } } assert(false. assert(false). dene(adet). 0 . değer)). unittest { int[] karışıkSayılar(in int adet) { int[] sayılar. } if (sayılar[indeks] != aranacak) { writefln( "Hata! %s indeksinde %s değil. aranacak. adet]. 100). // . "Buraya hiç gelinmemeliydi"). sayılar). bu basit algoritmayı şu dört adım halinde gerçekleştiriyor: • • • • dizi boşsa. sayılar = sayılar[0 . if (indeks == -1) { writefln( "Hata! %s değeri %s dizisinde bulunamadı". assert(false). http://ddili. aranacak. bulamadığımızı bildirmek için -1 döndür ortadaki değer aranan değere eşitse ortadaki değerin indeksini döndür aranan değer ortadaki değerden önceyse. 0 . void dene(in int adet) { int[] sayılar = karışıkSayılar(adet * 4). aynı işlevi sağdaki yarıda devam ettir O işlevi deneyen bir kod da şöyle yazılabilir: import std. foreach (i. } randomShuffle(sayılar). aranacak)..

İşlev içerisinde.random modülünde tanımlanmış olan ve ismi "rasgele karıştır"dan gelen randomShuffle işlevinden de yararlandım. x.. burada hiç int bulunmuyor . yukarıda tanımladığımız Nokta şablonu bu türle kullanılmak için henüz hazır değildir: struct Nokta(T) { T x. Bir derleme hatası alırız: Error: need member function opCmp() for struct const(Nokta!(int)) to compare O hata. in int değer) { // . Onları şablon parametreleri olarak tanımlamak. } O işlevi. Nokta!int(10...org 375 .. 0 ..%s)". Dikkat ederseniz... in T değer) { // . string toString() const { return format("(%s.. Nokta!int 'in bir karşılaştırma işleminde kullanılabilmesi için opCmp işlevinin tanımlanmış olması gerektiğini bildirir.Şablonlar O kodda std. T y. } Dönüş değeri olan int . } } void main() { Nokta!int[] noktalar. y). O yüzden. İşleç Yükleme dersinde gösterildiği şekilde bir opCmp Copyright © 2009-2011 Ali Çehreli. artık bir şablon haline getirebiliriz.. O işlevi int için yazıp doğru çalıştığından emin olduktan sonra. bu işlevin bir şablon haline gelmesi ve dolayısıyla başka türlerle de kullanılabilmesi için yeterlidir: int ikiliAra(T)(const T[] değerler. Parametrelerde geçen int 'ler ise bu işlevin kullanılabildiği değerlerin türünü belirliyor.. işlevin tanımında yalnızca üç yerde int geçiyor: int ikiliAra(const int[] değerler. 10)) == 10).. 15) { noktalar ~= Nokta!int(i. } else if (değer < değerler[ortaNokta]) { // . elemanlar yalnızca == ve < işleçleriyle kullanılıyorlar: if (değer == değerler[ortaNokta]) { // . i). bir indeks bilgisi olduğu için onun öyle kalmasını isteriz. foreach (i. http://ddili. içindeki kullanımlara uyan her türle kullanabiliriz. } } assert(ikiliAra(noktalar.

ve sınıfların şablon haline gelmeleri için yeterlidir void işlevŞablonu(T)(T işlevParametresi) { // . } } 64. http://ddili. • şablon parametreleri yalnızca işlev şablonlarında çıkarsanabilirler işlevŞablonu(42).org 376 . } • şablon parametreleri ünlem işaretinden sonra açıkça belirtilebilirler.. Devamını sonraya bırakarak bu dersin özeti: • şablonlar. tek parametre için parantez kullanmaya gerek yoktur auto nesne1 = new SınıfŞablonu!(double). programın derlenmesini ve ikili arama işlevinin Nokta şablonu için de kullanılabilmesini sağlar: struct Nokta(T) { // .Şablonlar tanımlamak.sağdaki. yapıların.sağdaki..12 Özet Şablonlar bu derste gösterdiklerimden çok daha kapsamlıdır.. işlevlerin... • şablonun farklı türlerle her kullanımı farklı bir türdür assert(typeid(SınıfŞablonu!int) != typeid(SınıfŞablonu!uint)).x).y : x . auto nesne2 = new SınıfŞablonu!double. int opCmp(const ref Nokta sağdaki) const { return (x == sağdaki. // işlevŞablonu!int çağrılır // aynı şey • şablonlar.. } class SınıfŞablonu(T) { // . kodun kalıp halinde tarif edilmesini ve derleyici tarafından her tür için üretilmesini sağlayan olanaktır • şablonlar bütünüyle derleme zamanında işleyen bir olanaktır • tanımlarken isimlerinden sonra şablon parametresi de belirtmek. } • işlev şablonlarında varsayılan şablon parametre türleri = karakterinden sonra belirtilebilir Copyright © 2009-2011 Ali Çehreli.. : karakterinden sonra belirtilen tür için özellenebilirler class SınıfŞablonu(T : dchar) { // .x ? y ..

org 377 . http://ddili..Şablonlar void işlevŞablonu(T = long)(T işlevParametresi) { // . } Copyright © 2009-2011 Ali Çehreli..

Takma isimler vermenin çeşitli yararları vardır. Takma isim ve daha önceden var olan isim birbirinin eşdeğeridir ve aynı şekilde kullanılır. Bir adım daha ileri giderek yukarıdaki alias 'ı iki parça halinde de tanımlayabiliriz: alias Nokta!double HassasNokta. türün açıkça yazıldığı her yerde değişiklik yapılması gerekecektir Bu sakıncalar. Noktalar rasgeleNoktalar(int adet) { auto noktalar = new Noktalar.1. Copyright © 2009-2011 Ali Çehreli. } // . Yığın!(Nokta!double) ismine tek bir yerde yeni bir isim vererek giderilebilir: alias Yığın!(Nokta!double) Noktalar... Programda açıkça Yığın!(Nokta!double) yazmanın bir kaç sakıncası görülebilir: • okumayı güçleştirecek derecede karmaşıktır • onun bir yığın veri yapısı olduğunun ve elemanlarının Nokta şablonunun double türü ile kullanılmalarından oluştuğunun her noktada görülmesi. 65. 65. alias Yığın!HassasNokta Noktalar.. Yine de ileride daha uygun bir ders olmadığını bildiğimden.. gereksiz bir bilgi olarak kabul edilebilir • programın ihtiyaçlarının gelişmesi durumunda örneğin double yerine artık real kullanılması gerektiğinde. alias 'ın söz dizimi şöyledir: alias var_olan_isim takma_isim. } // . // . programda geçen bir isme yeni bir takma isim vermek için kullanılır. veya yığın veri yapısı yerine bir ikili ağaç veri yapısı gerektiğinde.org 378 ..1 Uzun bir ismi kısaltmak Önceki derste gördüğümüz şablonlarda olduğu gibi. Daha önce tanımladığımız şu işlevi hatırlayalım: Yığın!(Nokta!double) rasgeleNoktalar(int adet) { auto noktalar = new Yığın!(Nokta!double). programda geçen bazı isimler kullanışsız derecede uzun olabilirler.alias ve alias this 65 alias ve alias this Not: alias ve alias this ayrı bölümlerde anlatılacak kadar bağımsız olanaklardır.. isim benzerliği nedeniyle ve çok kısa olduğu için alias this 'i de burada anlatmaya karar verdim.1 alias alias anahtar sözcüğü. http://ddili.

eğer o yapının kullanıcıları her zaman için MüşteriNumarası ve Şirketİsmi yazarlarsa.Müdür at [.. alias string Şirketİsmi. struct Müşteri { MüşteriNumarası numara. kod okunurken o değerin anlamı konusunda hiçbir şüphe bırakmaz.2 Tasarım esnekliği Bazı durumlarda.Müdür at [. import firma. Bazı durumlarda böyle tür isimleri bir yapı veya sınıfın içinde de tanımlanabilir... Örnek olarak ağırlık niteliğine sahip bir sınıfa bakalım: Copyright © 2009-2011 Ali Çehreli. her ne kadar ileride değişmeyecek olduğundan emin bile olunsa. tasarımın esnek olması için int gibi temel türlere bile anlamlı yeni isimler verilebilir: alias int MüşteriNumarası. 65. // . programda kullanmak istediğimiz Müdür 'e bir takma isim verebiliriz. Böylece her seferinde uzun uzun örneğin okul.d(1) Bunun önüne geçmek için.1. Örneğin okul ve firma isimli iki modülde Müdür isminde iki farklı tür tanımlı olduğunu varsayalım. Bu yöntem.Müdür yazmak zorunda kalmadan birden fazla yerde kullanabiliriz: import std. Böylece o yapının veya sınıfın arayüzünde bu takma isimleriyle kullanılırlar. kodun anlaşılır olmasına da yardım eder. http://ddili. yapı tanımında int veya string yerine başka bir tür kullanıldığında daha az satırda değişiklik gerekmiş olur..]/okul..Müdür OkulMüdürü.stdio.alias ve alias this Türlerin isimlerini modülleriyle birlikte uzun uzun yazmak yerine de alias 'tan yararlanabiliriz. // ... Bu iki modülün de programa eklendikleri bir durumda yalnızca Müdür yazıldığında program derlenemez: import okul. // . alias okul.. void main() { OkulMüdürü kişi. Bir değerin türünün int yerine MüşteriNumarası olması... Şirketİsmi şirket. Müdür kişi.d(1) conflicts with firma..]/firma.. } OkulMüdürü başkaKişi. // ← derleme HATASI Derleyici hangisini kasdettiğimizi anlayamaz: Error: okul. } Her ne kadar sırasıyla int 'in ve string 'in aynısı olsalar da. // .org 379 .

. foreach (kutu. alt sınıfta görünmezler. } Bunun karşıtı olarak. kutular) { toplamAğırlık += kutu.org 380 . Kullanıcı kodu da doğrudan double yazmak yerine. üst sınıfın işlevlerinin isimleri gizlenirler ve alt sınıf arayüzünde görünmezler: Copyright © 2009-2011 Ali Çehreli.. Bu sınıfın üyesinin ve niteliğinin açıkça double yazılarak tanımlanmış olması.. } } // . Kutu. public: @property double ağırlık() const { return ağırlık_. } Bu sayede. kodda değiştirilmesi gereken yerlerin sayısı da az olur. onun kullanıcılarının da ağırlığı double olarak kullanmalarını neden olacaktır: double toplamAğırlık = 0...1. 65.3 Üst sınıfın gizlenen isimlerini alt sınıfta görünür yapmak Üst sınıfta da aynı isimde bulunan isimler.. http://ddili.Ağırlık toplamAğırlık = 0. kutular) { toplamAğırlık += kutu. public: alias double Ağırlık. sınıfın arayüzüne bağlı kalarak Ağırlık yazacaktır: // . Alt sınıfta aynı isimde tek bir işlev bile bulunsa. Kutu sınıfının tasarımcısı Ağırlık 'ı daha sonradan başka şekilde tanımlarsa.alias ve alias this class Kutu { private: double ağırlık_.ağırlık.ağırlık. ağırlığın türünün sınıf içindeki bir alias ile tanımlandığı düşünürsek: class Kutu { private: Ağırlık ağırlık_. foreach (kutu. @property Ağırlık ağırlık() const { return ağırlık_. } } // .

alias ve alias this class GenelHesap { void hesapla(int x) { // . Not: Burada üst sınıfın işlevini alt sınıfta değişik olarak yeniden tanımlamaktan bahsetmediğimize dikkat edin.. İsim gizleme. } // ← derleme HATASI O çağrıda 42 değeri kullanıldığı için. http://ddili.hesapla 'yı bu gizleme nedeniyle hiç dikkate bile almadığı için ÖzelHesap. Öyle olsaydı. parametre listesini üst sınıftakiyle aynı yapar ve override anahtar sözcüğünü kullanırdık. alt sınıfa eklenen yeni bir işlev isminin.hesapla 'nın bir int ile çağrılamayacağını belirten bir hata verir: Error: function deneme. Gizlenen isimlerin alt sınıf arayüzünde görünmeleri de alias ile sağlanır: class GenelHesap { void hesapla(int x) { // .. } } class ÖzelHesap : GenelHesap { void hesapla() { // .. Burada. GenelHesap.hesapla işlevinin çağrılacağını bekleyebiliriz.. Türeme dersinde anlatıldığı gibi. } } class ÖzelHesap : GenelHesap { void hesapla() { // .org 381 . üst sınıftaki bir isimle aynı olduğu durumla ilgileniyoruz.hesapla işlevi. Copyright © 2009-2011 Ali Çehreli..hesapla işlevini gizler ve program derlenmez. ÖzelHesap nesnesinin kalıtım yoluyla edindiği GenelHesap. nesneye yönelik programlamayı destekleyen başka dillerde de bulunan ve bu tür hataları önleyen bir olanaktır. hesap.hesapla(42)..ÖzelHesap. her ne kadar parametre listeleri farklı olsa da ÖzelHesap. aynı isme sahip olduğu için GenelHesap.hesapla hesapla. } } alias GenelHesap. Derleyici. hiçbir uyarı verilmeden kodun istenenden farklı bir işlevi çağırmasına neden olabilirler.. } } void main() { auto hesap = new ÖzelHesap.hesapla () is not callable using argument types (int) Bunun geçerli bir nedeni vardır: İsim gizleme olmasa. Oysa.. ileride bu sınıflara eklenen veya onlardan çıkartılan hesapla işlevleri.

} İsim gizleme üye değişkenler için de geçerlidir. void main() { auto hesap = new ÖzelHesap..alias ve alias this Yukarıdaki alias . hesap. class ÜstSınıf { int şehir.. üst sınıfın aynı isimdeki üyesini gizler ve bu yüzden aşağıdaki kod derlenemez: void main() { auto nesne = new AltSınıf. Yeni bir isimle getiren bir örnek: class ÜstSınıf { int şehir.. } class AltSınıf : ÜstSınıf { string şehir() const { return "Kayseri". } // ← derleme HATASI Üst sınıfın üye değişkeni alias ile alt sınıf arayüzüne getirildiğinde kod artık derlenir. } } alias GenelHesap.genelHesapla(42). ve beklendiği gibi üst sınıfın hesapla işlevi çağrılır. http://ddili. alt sınıftaki şehir . İstendiğinde onların alt sınıf arayüzünde görünmeleri de alias ile sağlanır..org 382 . O eklemeden sonra kod artık derlenir. diğeri üye işlev olsa da. Eğer daha uygun olduğu düşünülürse. üst sınıftaki hesapla ismini alt sınıf arayüzüne getirir. üst sınıfın işlevi farklı bir isimle bile görünür hale getirilebilir: class GenelHesap { void hesapla(int x) { // .hesapla genelHesapla. } Copyright © 2009-2011 Ali Çehreli. } } class ÖzelHesap : GenelHesap { void hesapla() { // . nesne. } } Her ne kadar birisi üye değişken. ve böylece gizlenmesini önlemiş olur.şehir = 42.

şehirKodu = 42. } } alias ÜstSınıf. long kuruş. Bu yüzden. Bu iki sözcüğün arasına yapının veya sınıfın bir üyesi yazılır: struct Para { long lira. nesne. // ← derlenir. 25). Copyright © 2009-2011 Ali Çehreli. void main() { auto nesne = new AltSınıf. long kuruşsuz = miktar.2 alias this Başka bağlamlarda kendi özel anlamları bulunan alias ve this anahtar sözcükleri. lira üyesinin türü olan long yerine. alias this .org 383 . } Yukarıdaki koddaki Para nesnesi. bir yapının veya sınıfın otomatik tür dönüşümü yoluyla başka bir tür yerine kullanılabilmesini sağlar. Para nesnelerinin otomatik olarak lira 'nın türü olan long yerine kullanılabilmelerini sağlar: void main() { auto miktar = Para(10.alias ve alias this class AltSınıf : ÜstSınıf { string şehir() const { return "Kayseri". Tür dönüşümü için başka bir seçenek de opCast işlecidir. http://ddili. Bir yapı veya sınıf tanımında birden fazla alias this bulunamaz. } 65. ve otomatik olarak // long'a dönüşür assert(kuruşsuz == 10). ikisi bir arada kullanıldığında tek bir anahtar sözcük olarak kabul edilmelidir. } alias lira this. ve onun değeri ile kullanılabilmiştir. O tanım. bir arada kullanıldıklarında tamamen farklı bir anlamdadırlar.şehir şehirKodu.

dizi). her seferinde dizi elemanlarından birisinin kopyasıdır. göstergelerin anlaşılmalarını da kolaylaştırır. Bu bölümde özellikle basit olarak seçtiğim örnekler.Göstergeler 66 Göstergeler Göstergeler başka değişkenlere erişim sağlamak için kullanılırlar. Ben bu derste kısa olsun diye.1 foreach 'in ref değişkenleri foreach Döngüsü dersinde gördüğümüz gibi. Göstergeler mikro işlemcilerin en temel olanaklarındandır ve sistem programcılığının önemli bir parçasıdır.1 Referans kavramı Göstergelere geçmeden önce. void main() { int[] dizi = [ 1. dizi) { sayı = 0.1. foreach (sayı. bunların hepsinin yerine değişken sözünü kullanacağım. Yukarıdaki döngü içinde sıfırlanmakta olan sayı değişkeni. göstergelerin amaçlarından bazılarının D'nin başka olanakları tarafından zaten karşılanıyor olmasıdır. 66. örneklerde basitçe gösterge diye seçtiğim isimlerin kullanışsız olduklarını aklınızda bulundurun. nesneyi. // ← kopya değişir. Kendi programlarınızda her ismi anlamlı ve açıklayıcı olarak seçmeye özen gösterin. göstergelerin kullanım amaçlarını anlatma konusunda yetersiz kalabilirler. Bu yüzden.org 384 . asıl eleman değişmez } } writeln("Döngüden sonra elemanlar: ". 11. Yazımlarını ve kullanımlarını öğrenirken bunu gözardı edebilirsiniz. erişim sağladıkları değişkenlerin adresleridir. Bunun nedeni. Onun değiştirilmesi. D'de göstergelerin çok daha kolay öğrenileceğini düşünüyorum. D'nin gösterge kavramı ve kullanımı C'den geçmiştir. En sonda vereceğim örneklerin daha anlamlı olacaklarını düşünüyorum. C öğrenenlerin anlamakta en çok zorlandıkları olanak göstergeler olduğu halde. 66. http://ddili.stdio. Değerleri. döngü değişkenleri normalde elemanların kopyalarıdır: import std. Göstergeler her türden değişkeni. 111 ]. ve hatta başka göstergeleri de gösterebilirler. hem bir çok durumda gösterge kullanılması gerekmez. Ek olarak. dizideki asıl elemanı etkilemez: Döngüden sonra elemanlar: 1 11 111 Dizideki elemanların kendilerinin değişmeleri istendiğinde foreach değişkeni ref olarak tanımlanır: Copyright © 2009-2011 Ali Çehreli. hem de başka D olanaklarının zaten anlaşılmış olması. göstergelerin temel amacı olan referans kavramını şimdiye kadarki derslerden tanıdığımız D olanakları ile kısaca hatırlayalım.

http://ddili. o derse dahil etmediğim sınıflar üzerinde bir örnek göstermek istiyorum: Copyright © 2009-2011 Ali Çehreli. değer türünden olan işlev parametreleri normalde başka değişkenlerin kopyalarıdır: import std.1. değer). Burada.5.3 Referans türleri D'de bazı türler referans türleridir.1.stdio.Göstergeler foreach (ref sayı.5. dizi) { sayı = 0. bu sefer dizideki asıl elemanın takma ismi gibi işlem görür ve dizideki asıl elemanlar değişir: Döngüden sonra elemanlar: 0 0 0 66.org 385 .5 İşlev parametresinin. void yarımEkle(double değer) { değer += 0. işlevin çağrıldığı yerdeki değişkenin takma ismi olması için ref anahtar sözcüğü kullanılır: void yarımEkle(ref double değer) { değer += 0. } writeln("İşlevden sonraki değer: ". yarımEkle(değer). main 'deki değer değişmez: İşlevden sonraki değer: 1. } Şimdi main içindeki değer etkilenmiş olur: İşlevden sonraki değer: 2 66. // ← main'deki değer değişmez } void main() { double değer = 1.5. // ← asıl eleman değişir } sayı . kendileri sahip olmadıkları başka değerlere erişim sağlarlar: • sınıflar • dinamik diziler • erişim tabloları Referans kavramını Değerler ve Referanslar dersinde görmüştük. Bu türlerden olan değişkenler. işlev içindeki atama yalnızca işlevin yerel değişkeni olan değer 'i etkiler. İşlev parametresi ref olarak tanımlanmadığı için.2 ref işlev parametreleri İşlev Parametreleri dersinde gördüğümüz gibi.

hem de açıkça gösterge kullanmaya gerek bırakmazlar.mürekkep).org 386 .mürekkep.---+---+--. farklı sınıf değişkenleri olan kalem ve başkaKalem .---+-|-+--. http://ddili. kalem. } } void main() { auto kalem = new TükenmezKalem. başkaKalem. } void kullan(double miktar) { mürekkep -= miktar. kalem. başkaKalem. // ← şimdi ikisi de aynı nesneye // erişim sağlarlar writefln("Önce : %s %s". başkaKalem. D'nin yukarıda hatırlattığım üst düzey olanakları da perde arkasında göstergelerle gerçekleştirilmiştir.Göstergeler import std.stdio.mürekkep). asıl değişkenleri gösterirler.kullan(1). new ile oluşturulmuş olan tek TükenmezKalem nesnesine erişim sağlamaktadır. iki değişkenin kullanılması da aynı nesneyi etkiler: Önce : 15 15 Sonra: 12 12 Bu sınıf nesnesinin ve ona erişim sağlayan iki sınıf değişkeninin bellekte şu şekilde durduklarını düşünebiliriz: (TükenmezKalem nesnesi) kalem başkaKalem ---+-------------------+--. başka sistem programlama dillerinde de olduğu gibi. auto başkaKalem = kalem. göstergelerin D programcılığında da mutlaka bilinmeleri gerekir. // ← aynı nesne kullanılır // ← aynı nesne kullanılır } writefln("Sonra: %s %s". Referanslar. Sonuçta. Sınıflar referans türleri oldukları için. Buna rağmen. this() { mürekkep = 15. class TükenmezKalem { double mürekkep.mürekkep.---+---+--| mürekkep | | o | | o | ---+-------------------+--.kullan(2). Programlama dillerindeki referans ve gösterge kavramları perde arkasında mikro işlemcilerin gösterme amacıyla kullanılan yazmaçları ile gerçekleştirilir. Copyright © 2009-2011 Ali Çehreli.---+-|-+--▲ | | | | | +--------------------+------------+ Referans kavramı her zaman için bu şekilde düşünülebilir. kalem. Bu yüzden hem zaten çok etkin çalışırlar.

Örneğin bir int göstergesi yalnızca int türünden olan değişkenleri gösterebilir. bir int 'i gösteren bir gösterge şöyle tanımlanabilir: int * benim_göstergem. Bu. C bilen programcılar için bir kolaylık olarak görülse de. Başka bir deyişle. http://ddili. * karakterinden önceki ve sonraki boşlukların yazılmaları isteğe bağlıdır ve aşağıdaki gibi kullanımlar da çok yaygındır: int* benim_göstergem. Böyle bir tanımda * karakterini "göstergesi" diye okuyabilirsiniz. Göstergenin değeri çıkışa yazdırıldığında beygirGücü 'nün adresi ile aynı olur: writeln("beygirGücü'nün adresi : ". göstergenin değer olarak o değişkenin adresini taşıması ile sağlanır. benim_göstergem 'in türü bir int* 'dır. önce hangi türden değer göstereceği. göstergenin o adresteki değişkeni göstermesi anlamına gelir. Buna göre. benim_göstergem 'in beygirGücü 'nü göstermesini sağlar. Yukarıdaki ifadede göstergenin beygirGücü 'nün adresi ile ilklenmesi. hiçbir değişkene erişim sağlamama değeri olan null 'dır. benim_göstergem).3 Göstergenin değeri ve adres alma işleci & Göstergeler de değişkendir ve her değişkenin olduğu gibi onların da değerleri vardır. yani bir "int göstergesidir". int * benim_göstergem = &beygirGücü. &beygirGücü). değer olarak bir adres taşıması. Bu işleç. Bir göstergenin hangi değişkeni gösterdiği (erişim sağladığı). C'de olduğu gibi D'de de öğrenmeyi güçleştirebilir. int *benim_göstergem. Tek başına tür ismi olarak "int göstergesi" anlamında kullanıldığında. writeln("benim_göstergem'in değeri: ". Bu adres değeri. Şimdiye kadar readf işlevi ile çok kullandığımız & işlecini Değerler ve Referanslar dersinden de hatırlayacaksınız. önüne yazıldığı değişkenin adresini alır.Göstergeler 66. beygirGücü'nün adresi : BFDB9830 benim_göstergem'in değeri: BFDB9830 Copyright © 2009-2011 Ali Çehreli. Değer atanmayan göstergelerin varsayılan değeri. Biraz aşağıda anlatacağım her türü gösterebilen gösterge dışındaki göstergeler ancak belirli türden bir değişkeni gösterebilirler. gösterge değeri olarak kullanılabilir: int beygirGücü = 180. özellikle * işlecinin farklı anlamlara sahip olması.2 Tanımlanması D'nin gösterge söz dizimi aynı C'de olduğu gibidir. boşluksuz olarak int* olarak yazılması da çok yaygındır. Bir gösterge tanımlanırken. sonra da bir * karakteri yazılır: göstereceği_tür * göstergenin_ismi. 66.org 387 .

bir şerit gibi soldan sağa doğru uzadığını hayal ettiğimiz belleği şimdi şöyle düşünebiliriz: BFDB9830 BFDB9834 BFDB9838 : : : ---+----------------+----------------+--| 180 | BFDB9830 | ---+----------------+----------------+--- Kaynak kodda geçen değişken ismi. gibi isimler. program içinde mikro işlemcinin anladığı adreslere ve değerlere dönüşürler. Bu yer.4 Erişim işleci * Çarpma işleminden tanıdığımız * karakterinin gösterge tanımlarken tür isminden sonra yazıldığını yukarıda gördük. beygirGücü . erişim sağladığı değişkenin adresi olmasını. programın her çalıştırılışında büyük olasılıkla farklı bir adreste bulunacaktır.Göstergeler Not: Adres değeri siz denediğinizde farklı olacaktır. Gösterme kavramını belirtmek için kullandığım oku da kaldırırsak. bu iki değişkenin bellekte yan yana bulundukları sonucunu çıkartabiliriz. Örneğin programcının isim vererek tanımladığı ve kullandığı değişkenler. D gibi derlemeli diller ile oluşturulan programların içinde bulunmazlar. *benim_göstergem). ama o isimlerin programın işleyişiyle ilgileri yoktur. onların adreslerini de & işleci ile öğrenebiliriz: writeln("benim_göstergem'in adresi: ". işlev ismi. göstergenin erişim sağladığı değer anlamına gelir: writeln("Gösterdiği değer: ". anahtar sözcük. Copyright © 2009-2011 Ali Çehreli. programın işletim sisteminden aldığı daha büyük bir belleğin küçük bir yerinde bulunur. benim_göstergem 'in değeri de beygirGücü 'nün adresidir. Göstergeleri öğrenirken karşılaşılan bir güçlük. 66. Not: Programda kullanılan isimler hata ayıklayıcıda yararlanılmak üzere programın debug halinde de bulunurlar. http://ddili. Göstergeler de değişken olduklarından.org 388 . referanslara benzer şekilde şöyle düşünebiliriz: BFDB9830 adresindeki başka bir adresteki beygirGücü benim_göstergem ---+-------------------+-----+---------------+--| 180 | | BFDB9830 | ---+-------------------+-----+-------|-------+--▲ | | | +-----------------------------+ beygirGücü 'nün değeri 180. vs. Bir göstergenin isminden önce yazıldığında. ve böylece o değişkeni göstermesini. &benim_göstergem). benim_göstergem'in adresi: BFDB9834 beygirGücü ile benim_göstergem 'in adreslerinin arasındaki farkın bu örnekte 4 olduğuna bakarak ve beygirGücü 'nün türü olan int 'in büyüklüğünün 4 bayt olduğunu hatırlayarak. Bir göstergenin değerinin. bu karakterin göstergenin gösterdiği değişkene erişmek için de kullanılmasıdır.

bu işleç C'deki -> işleci ile aynıdır. string toString() const { return format("(%s. O ifade. bu yazım sıkıntılı hale gelir. Copyright © 2009-2011 Ali Çehreli. Gösterilen değişken yapı veya sınıf nesnesi olduğunda ise. (nokta) işleci Not: Eğer göstergeleri C'den biliyorsanız. y). int y. yani merkez 'in kendisi • (*gösterge) : nokta karakteri gösterge'ye değil. Bunu şu adımlarla açıklayabiliriz: • gösterge : merkez 'i gösteren gösterge • *gösterge : nesneye erişim. gösterilen nesnenin bir üyesine erişmek için * işleci kullanıldığında yazım karmaşıklaşır: // 10 birim sağa ötele (*gösterge). http://ddili. merkez nesnesinin x üyesinin değerini değiştirmektedir. * işlecinin gösterilen değişkene erişim için kullanıldığını gördük. writeln(merkez). writeln(*gösterge). Yukarıdaki ifadeyi çok daha kısa olarak şöyle yazabiliriz: gösterge. Örnek olarak x ve y üyeleri ile iki boyutlu düzlemdeki bir noktayı ifade eden bir yapıya bakalım: struct Konum { int x. (nokta) işleci göstergenin kendisine uygulanır ama gösterdiğinin üyesine erişim sağlar.0) Ancak.x += 10. toString işlevi tanımlanmış olduğu için Konum nesnesini yazdırmak için yeterlidir: (0. // tanım // erişim O kullanım. onun gösterdiğine uygulansın diye gereken parantezler • (*gösterge). . temel türleri gösteren göstergeler için yeterli derecede kullanışlıdır: *benim_göstergem yazılarak gösterilen değere kolayca erişilir.Göstergeler Gösterdiği değer: 180 66.org 389 . } } O türden bir değişkeni gösteren bir göstergeyi aşağıdaki gibi tanımlayabiliriz ve gösterdiğine erişebiliriz: auto merkez = Konum(0.%s)". 0). Bu.x += 10.x : gösterdiği nesnenin x üyesi Gösterilen nesnenin üyesine erişim böyle karışık bir şekilde yazılmak zorunda kalınmasın diye. Konum * gösterge = &merkez. x.5 Gösterdiğinin üyesine erişim için .

bu işlemler göstergenin değerini belirtilen miktar kadar arttırmazlar. o göstergenin bellekte bir sonra bulunan değişkeni göstermesini sağlar: ++birGösterge. (nokta) işleci. Aritmetik işlemlerden alıştığımızdan farklı olarak. belirtilen miktar kadar sonraki (veya önceki) değişkeni gösterecek şekilde değişir. yukarıdaki koddaki nesne.üye = 42. (nokta) işleci. o nesneye erişim sağlayan bir sınıf değişkenidir. Sınıflarda da sınıf değişkenlerine doğrudan uygulanan .) Copyright © 2009-2011 Ali Çehreli. Göstergenin değeri.Göstergeler Daha basit olan gösterge. birGösterge -= 2. aslında asıl nesnenin üyesine erişim sağlar. bir dizinin sonuncu elemanından sonraki hayali elemanın gösterilebilmesidir.. Uyarı: Göstergelerin programa ait olmayan adresleri göstermeleri tanımsız davranıştır. // Değişkene uygulanır. // Solda değişken. ++ işlemi sonucunda 4 artar. --birGösterge. (Not: Bunun tek istisnası. Sınıflar dersinden hatırlayacağınız gibi. ve toplama ve çıkarma işlemlerinde kullanılabilir: ++birGösterge. writeln(birGösterge + 3). birGösterge += 2. Örneğin göstergenin değerinin ++ işleciyle arttırılması. Erişmek için kullanılmasa bile.org 390 . Aynı durumun göstergelerde de bulunması. nesnenin üyesine erişir değişken. Örneğin int* türündeki bir göstergenin değeri. // daha önce gösterdiğinden bir sonraki // değişkeni göstermeye başlar Derleyici.x ifadesi. 66. writeln(birGösterge . int 'in büyüklüğü 4 olduğu için. } // .3).0) Bunun sınıflarla aynı olduğuna dikkat edin. yapı değişkenleri ile göstergelerin temelde benzer şekilde gerçekleştirildiklerini ortaya koyar. sağda nesne SınıfTürü değişken = new SınıfTürü. değişken . azaltılabilir. bir göstergenin var olmayan bir değişkeni göstermesi hatalıdır. Değişkene uygulanan . http://ddili.6 Gösterge değerinin değiştirilmesi Göstergelerin değerleri arttırılabilir. new ile sağda isimsiz olarak oluşturulur. Bunu aşağıda açıklıyorum.. bunu sağlayabilmek için göstergenin değerini türün büyüklüğü kadar arttırır. aslında sınıf nesnesinin üyesine erişim sağlar: class SınıfTürü { int üye. merkez 'in x üyesini değiştirmiştir: (10.

to!string(renk)). o işlemin sonucunda ne olacağının belirsiz olması anlamına gelir. 2. beygirGücü 'nden bir sonraki adreste benim_göstergem 'in kendisi bulunduğu için. " bayt"). KurşunKalem. ++i) { writeln("gösterge değeri: ". KurşunKalem * gösterge = &kalemler[0]. // ← tanımsız davranış Öyle bir işlemde programın nasıl davranacağının tanımlanmamış olması. Değişkenlerin yan yana bulunmaları. mavi } struct KurşunKalem { Renk renk.sarı. göstergelerin değerlerinin arttırılması veya azaltılması ancak yan yana bulundukları bilinen değişkenler gösterildiğinde kullanılmalıdır. O işlem sonucunda programın çökeceği sistemler bulunabilir. KurşunKalem(Renk. import std. 13) ]. string toString() const { return format("%s santimlik %s bir kalem". i != kalemler.kırmızı.) O yüzden. uzunluk. ve zaten karakter dizileri olan dizgilerin tanımları gereğidir. 11). writeln("kalem: ". enum Renk { kırmızı. gösterdiği elemanın adresidir gösterdiği nesneye erişim bir sonraki nesneyi göstermesi Bu programın çıktısı: Copyright © 2009-2011 Ali Çehreli. dizilerin. 3. } } void main() { writeln("KurşunKalem nesnelerinin büyüklüğü: ". http://ddili.string.mavi.stdio. import std. göstergenin değeri 4 sonraki bellek adresine sahip olacaktır. gösterge). Günlük kullanımdaki bilgisayarlardaki mikro işlemcilerde ise. Dizi içindeki bir elemanı gösteren göstergenin değerinin ++ işleci ile artırılması. double uzunluk. ++gösterge. } tanımlanması. *gösterge). (Not: İlginç bir gözlem olarak. değeri. 4.conv.sizeof. gösterge kendisini sanki bir int 'miş gibi gösterecektir. dizinin ilk elemanının adresi ile ilklenmektedir değerinin kullanılması. 12). KurşunKalem(Renk. onun bir sonraki elemanı göstermesini sağlar: import std. // (1) // (2) // (3) // (4) } 1.org 391 . yukarıdaki programdaki adreslerden anlaşıldığı gibi. for (int i = 0. sarı.length.Göstergeler Örneğin yukarıda tek bir int olarak tanımlanmış olan beygirGücü değişkenini gösteren göstergeyi arttırmak yasal değildir: ++benim_göstergem. KurşunKalem[] kalemler = [ KurşunKalem(Renk.

writeln(sayılar[1 .length kere tekrarlandığı için. 3 ]. 1. dilim aralıklarının ikinci indeksi. Başlangıç göstergesinin ilk elemanı göstermesi. bir dizinin sonuncu elemanından hemen sonraki hayali elemanı göstermeleri yasaldır. http://ddili. // ondan iki sonrakinin adresi writeln(sayılar). 2. 66.7 Göstergeler risklidir Göstergelerin doğru olarak kullanılıp kullanılmadıkları konusunda denetim sağlanamaz. 3 hariç Bu yöntem göstergelerle de kullanılabilir. 3 ]. Ne derleyici. yukarıdaki döngü kalemler.. } } void main() { int[] sayılar = [ 0.org 392 . ++baş. baş + 2). ve bitiş göstergesinin son elemandan sonraki elemanı göstermesi oldukça yaygın bir işlev tasarımıdır. ne de çalışma zamanındaki denetimler bunu garantileyebilirler. // ikinci elemanın adresi onKatı(baş. Bir göstergenin değerinin her zaman için geçerli olması programcının sorumluluğundadır. } Copyright © 2009-2011 Ali Çehreli.Göstergeler KurşunKalem nesnelerinin büyüklüğü: 12 bayt gösterge değeri: 114FC0 kalem: 11 santimlik kırmızı bir kalem gösterge değeri: 114FCC kalem: 12 santimlik sarı bir kalem gösterge değeri: 114FD8 kalem: 13 santimlik mavi bir kalem Dikkat ederseniz. 66. 2. Bu. dilimlerden alışık olduğumuz aralık kavramına benzeyen yöntemlerde kullanışlıdır. // Kendisine verilen aralıktaki değerleri 10 katına çıkartır void onKatı(int * baş. Bunu bir işlevin parametrelerinde görelim: import std.8 Dizinin son elemanından bir sonrası Göstergelerin. Hatırlarsanız. gösterge hiçbir zaman var olmayan bir nesneye erişmemektedir. int * son) { while (baş != son) { *baş *= 10. int * baş = &sayılar[1]. // 1 ve 2 dahil. 1.stdio. işlem yapılacak olan elemanlardan bir sonrasını gösterir: int[] sayılar = [ 0. O yüzden. göstergeleri kullanmayı düşünmeden önce D'nin üst düzey ve güvenli olanaklarının yeterli olup olmadıklarına bakmanızı öneririm. 3]).

göstergenin göstermekte olduğu değişken sanki bir dizinin ilk elemanıymış gibi düşünülür ve [] işleci o hayali dizinin belirtilen elemanına erişim sağlar. gösterge[1] = -200. for döngüsünün hazırlık bölümü boş bırakılmıştır. göstergeler bir dizinin elemanlarına erişir gibi de kullanılabilirler: double[] kesirliler = [ 0. int * son) { for ( . baş + sayılar. doğrudan baş parametresini arttırmaktadır.. kesirliler dizisinin 2 indeksli elemanını göstermektedir.2. ikinci göstergenin dizinin sonuncu elemanından bir sonrayı göstermesi gerekir: // ikinci gösterge. 3. 4. double * gösterge = &kesirliler[2].org 393 . son 'un gösterdiği elemanı kullanmadığı için de dizinin yalnızca 1 ve 2 numaralı indeksli elemanları değişmiştir: 0 10 20 3 Yukarıdaki gibi işlevlerin for döngüleriyle gerçekleştirilenlerine de rastlayabilirsiniz: void onKatı(int * baş. writeln(kesirliler).1 -100 -200 4. dizinin sonuncu elemanından sonraki // hayali bir elemanı gösteriyor: onKatı(baş. ++baş) { *baş *= 10. 66. yani indeksi 3 olan elemanın adresi anlamına gelir.4 ]. } Bu gibi bir yöntemde bir dizinin elemanlarının hepsinin birden kullanılabilmesi için. } } Dikkat ederseniz.Göstergeler baş + 2 değeri. Bu işlev yeni bir gösterge kullanmak yerine.0.3. işlem yapacağı int 'lerin dışını belirten bir değer olarak kullanmaktadır. iki gösterge almakta ve bunlardan ilkinin gösterdiği int 'i kullanmakta. baş != son. *gösterge = -100. Yukarıdaki onKatı işlevi. http://ddili. İkinci göstergeyi. 2. baş . ama ikincisinin gösterdiği int 'e hiçbir zaman erişmemektedir. Yukarıdaki programdaki gösterge .9 Dizi erişim işleci [] ile kullanımı D'de hiç gerekmese de. son) { *gösterge *= 10. 1. Çıktısı: 0 1. // gösterdiğine erişim // dizi gibi erişim Copyright © 2009-2011 Ali Çehreli.1. baş 'ın gösterdiğinden 2 sonraki elemanın. dizilerin son elemanlarından sonraki aslında var olmayan bir elemanın gösterilmesi yasaldır. Aralık bildiren çift göstergeler foreach deyimi ile de uyumlu olarak kullanılabilir: foreach (gösterge.length). İşte bunun için.4 Böyle bir kullanımda.

bu kullanımın geçerli bir değişkeni gösterip göstermediği denetlenemez. Karışık gibi görülse de. Güvenli olabilmesi için bunun yerine dilim kullanılmalıdır: double[] dilim = kesirliler[2 .10 Her türü gösterebilen void* D'de hiç gerekmese de. *(gösterge + 1) = -200. Derleyici. Bunlar void göstergesi olarak tanımlanırlar: int tamsayı = 42. void* türünden olan göstergeler çok kısıtlıdırlar: ne onların değerlerini sonraki (veya önceki) değişkeni gösterecek şekilde değiştirebiliriz. getirdiği esnekliğin bir sonucudur: gösterilen türün ne olduğu bilinmediği için. void * herTürüGösterebilen = &tamsayı. o dizinin 1 indeksli elemanına. o satırların ikisi de yasaldır ve hatasız olarak derlenir. void * herTürüGösterebilen. yani asıl dizinin 3 indeksli elemanına erişim sağlar. 4]. // dizi gibi erişim // üsttekiyle aynı elemana erişim Yukarıda da belirttiğim gibi. gösterdiği elemanın kaç baytlık olduğu da bilinemez: *herTürüGösterebilen = 43. gösterge[indeks] gibi bir yazımı perde arkasında *(gösterge + indeks) olarak dönüştürür: gösterge[1] = -200. herTürüGösterebilen = &kesirli.. Asıl dizinin 2 ve 3 indeksli elemanlarına erişim sağlamaktadır. // HATA: dilimin dışına erişim Dilimin 2 indeksli elemanı bulunmadığı için bir hata atılır ve böylece programın yanlış sonuçlarla devam etmesi önlenmiş olur: core. İndeksi 4 olan eleman dilimin dışındadır. herTürüGösterebilen = &tamsayı. ne de gösterdikleri değişkenlere erişebiliriz.exception.RangeError@deneme(8391): Range violation 66. double kesirli = 1.Göstergeler gösterge[1] kullanımı. eleman erişimi hataları çalışma zamanında yakalanır: dilim[2] = -300. sanki orada hayali bir dizi varmış gibi. bu kullanımın temelinde çok basit bir dönüşüm yatar. dilim[1] = -200. Dilimler güvenlidir.org 394 . O dilimin yalnızca iki elemanı bulunduğuna dikkat edin. // (1) // (2) Copyright © 2009-2011 Ali Çehreli. void* 'nin değerinin önce doğru türü gösteren bir göstergeye aktarılması gerekir: int tamsayı = 42. http://ddili. herhangi türden değişkenleri gösterebilen göstergelerdir.25. Yukarıdaki koddaki void* türünden olan gösterge hem bir int 'i hem de bir double 'ı gösterebilmektedir. dilim[0] = -100. // ← derleme HATASI Böyle işlemlerde kullanılabilmesi için. Bu. yine C'den gelen bir olanak.

Göstergeler // . vs..11 Mantıksal ifadelerde kullanılmaları Göstergeler otomatik olarak bool türüne dönüşebilirler. 66. new ile oluşturulan değişkenlere dinamik değişken denir. if (baytAdedi) { *baytAdedi = bilgi.mavi. kaç bayt yazdığını da bir çıkış parametresi ile bildiriyor olsun. kalem). işleve gönderilen gösterge değerinin null olup olmaması ile gerçekleştirilebilir: void bilgiVer(const ref KurşunKalem kalem. Copyright © 2009-2011 Ali Çehreli. http://ddili. int * baytAdedi) { const string bilgi = format("Kalem: %s". Bu. ve temel tür değişkenleri için de kullanabiliriz. // (4) Yukarıdaki örnek kodu şu adımlarla açıklayabiliriz: 1. baytAdedi. Bu işlev. 4. null değere sahip olan göstergeler mantıksal ifadelerde false değerini alırlar. Çıkışa nesne yazdıran bir işlev düşünelim. 8). &baytAdedi). writeln(bilgi). Bayt adedinin önemli olduğu durumlarda ise null olmayan bir değer: int baytAdedi.12 new bazı türler için adres döndürür Şimdiye kadar sınıf nesneleri oluştururken karşılaştığımız new 'ü yapı nesneleri. gibi üst düzey olanakları bulunmayan C kütüphanelerinde void* türleri çok kullanılır. asıl değişken değişkenin değerinin bir void* içinde saklanması daha sonra o değerin doğru türü gösteren bir göstergeye aktarılması değişkenin değerinin doğru türü gösteren gösterge ile erişilerek değiştirilmesi interface . Ama o işi ancak özellikle istendiğinde yapıyor olsun. şablon. Bunun isteğe bağlı olması. 3.sarı. 7).length. 66. int * tamsayıGöstergesi = cast(int*)herTürüGösterebilen. bilgiVer(KurşunKalem(Renk. writeln("Çıkışa ".. diğerleri de true değerini. onların değerlerinin mantıksal ifadelerde kullanılabilmesini sağlar. // (3) *tamsayıGöstergesi = 43. Böyle kütüphaneler kullandığımızda bizim de void* türünde göstergeler kullanmamız gerekebilir. " bayt yazılmış"). sınıf. Yani hiçbir değişkeni göstermeyen göstergeler false 'tur. 2. } } Kaç bayt yazıldığı bilgisinin gerekmediği durumlarda gösterge olarak null değeri gönderilebilir: bilgiVer(KurşunKalem(Renk.org 395 . null). diziler.

Yapı ).org 396 . onlara ancak new 'ün döndürmüş olduğu referans ile erişilir.Göstergeler new . küçük bir programla şöyle görülebilir: import std. • sınıf nesnelerinde. dizinin eleman türünü gösteren bir göstergedir: Copyright © 2009-2011 Ali Çehreli. Bu değişkenlerin kendi isimleri bulunmadığı için. Bu referans. struct Yapı {} class Sınıf {} void main() { writeln(typeof(new writeln(typeof(new writeln(typeof(new writeln(typeof(new } int ). auto ve typeof dersinden hatırlayacağınız gibi. dinamikDizi = new int[100].13 Dizilerin . • dizilerde ise bir dinamik dizidir: int[] dinamikDizi = new int[100]. bellekten değişken için gereken sayıda baytlık bir yer ayırır.stringof yöntemiyle yazdırılabildiğini hatırlarsanız.stringof). intGöstergesi = new int.stringof). Sınıf ).ptr niteliği. new .ptr niteliği Dizilerin . new 'ün değişik türler için ne döndürdüğü. • yapı nesnelerinde ve temel türlerde bir göstergedir: Yapı * yapıGöstergesi = new Yapı. yapıGöstergesi = new Yapı.stringof). sol taraftaki tür isimleri yerine normalde auto anahtar sözcüğü kullanıldığı için çoğunlukla bu ayrıma dikkat etmemiz gerekmez: auto auto auto auto sınıfDeğişkeni = new Sınıf.stdio. int * intGöstergesi = new int.stringof). dizideki ilk elemanın adresini döndürür. Ondan sonra bu yerde bir değişken kurar. temel tür ve yapılar için gösterge türünde bir değer döndürmektedir: int* int[] Yapı* Sınıf 66. şimdiye kadar çok gördüğümüz gibi bir sınıf değişkenidir: Sınıf sınıfDeğişkeni = new Sınıf. Herhangi bir ifadenin tür isminin typeof(Tür). http://ddili. int[5]). Bu değerin türü. Çıktıdan anlaşıldığı gibi.

. Örneğin ş harfi bir char[] veya string içinde iki tane char olarak bulunur.ptr.stdio. 66. Hatırlarsanız.14 Eşleme tablolarının in işleci Göstergeleri aslında Eşleme Tabloları dersinde gördüğümüz in işleci ile kullandık. Orada henüz göstergeleri anlatmamış olduğum için. } in işleci. Bazı C işlevleri. 2 : "iki". Bunu örnekler bölümünde göstereceğim.org 397 . in 'in dönüş değerini bir göstergeye atarsak. yokmuş.ptr niteliği de ilk karakterlerinin adresini verir. 12 ]. int * ilkElemanınAdresi = sayılar. değeri null olan bir göstergenin gösterdiğine erişilemez. Copyright © 2009-2011 Ali Çehreli. Burada dikkat edilmesi gereken bir konu. Yukarıdaki koşul da bu değerin false 'a veya true 'ya dönüşmesine göre işler. *ilkElemanınAdresi). Unicode kodlamasında kullanılan karakterler ayrı ayrı gözlemlenebilirler. bulunmuyorsa null değerini döndürür. 3 : "üç" ]. in işlecinin dönüş türünü geçiştirmiş ve o değeri üstü kapalı olarak bir mantıksal ifadede kullanmıştım: if ("mor" in renkKodları) { // evet. auto eleman = sayı in sayılar. o harflerin Unicode kodlamasındaki karşılıkları olduklarıdır. aslında tabloda bulunuyorsa elemanın adresini. elemanın tabloda bulunduğu durumlarda ona etkin bir şekilde erişebiliriz: import std. writeln("İlk eleman: ".. 1 : "bir". null değerinin gösterdiği geçerli bir nesne olmadığı için. renkKodları'nda "mor" indeksli eleman varmış } else { // hayır. http://ddili. Değeri null olmadığında da gösterdiği değişkene erişilmektedir (3). Dizgilerin de dizi olduklarını hatırlarsanız. if (eleman) { writeln("Biliyorum: ". *eleman). " sayısının yazılışını bilmiyorum"). // (1) // (2) // (3) } } else { writeln(sayı. bellekte art arda bulunan elemanların ilkinin adresini alırlar. dizgi elemanlarının harf değil. } Yukarıdaki koddaki eleman göstergesi in işleci ile ilklenmekte (1) ve değeri bir mantıksal ifadede kullanılmaktadır (2).Göstergeler int[] sayılar = [ 7. void main() { // Tamsayıdan string'e dönüşüm tablosu string[int] sayılar = [ 0 : "sıfır". Bu değer C kütüphanelerini kullanırken yararlı olabilir..ptr niteliğinin döndürdüğü adres ile erişildiğinde. int sayı = 2. onların .

15.. bu tür işlevlerle karşılaştığımızda onlara istedikleri türde gösterge göndermemiz gerekir. Örnek olarak yazı-tura deneyi yapan bir programa bakalım: import std. 100) { int * hangisi = (uniform(0. eşleme tablosunun değer türünde bir göstergedir. import std.. turaAdedi).random.. boyutlar nesnesinin üyelerinin kurulması . http://ddili. Dolayısıyla. foreach (i. Her ne kadar D kütüphanelerinde az sayıda olacaklarını düşünsek de. 2)) { ++yazıAdedi. } } writefln("yazı: %s tura: %s". int turaAdedi. void main() { int yazıAdedi.15 Ne zaman kullanmalı Kütüphaneler gerektirdiğinde readf işlevinde de gördüğümüz gibi.org 398 .15. 66. 0 . } else { ++turaAdedi. kullandığımız bir kütüphane bizden bir gösterge bekliyor olabilir.1 66... // .. Tabii aynı işlemi gösterge kullanmadan da gerçekleştirebiliriz: uniform(0. /* . *hangisi += 1.. Bu tablodaki değerler string oldukları için. auto yerine türü açık olarak yazmak istesek: string * eleman = sayı in sayılar. */). &boyutlar. in 'in dönüş türü string* 'dir.. değer türünden olan bir değişkenin hangisiyle işlem yapılacağını bir gösterge ile belirleyebiliriz.stdio. Veya bir if koşuluyla: if (uniform(0. pencere. 2) == 1 ? &yazıAdedi : &turaAdedi).. } Copyright © 2009-2011 Ali Çehreli. */. 66. 2) ? ++yazıAdedi : ++turaAdedi. yazıAdedi.setGeometryHints(/* .2 Değer türünden değişkenleri göstermek için Yine kesinlikle gerekmese de. Örneğin bir C kütüphanesi olan gtk'den uyarlanmış olan gtkD'nin bazı işlevlerinin bazı parametreleri göstergedir: GdkGeometry boyutlar.Göstergeler Orada eleman 'ın türü.

http://ddili. 66. göstergeler bazı durumlarda daha hızlı veya daha doğal olabilirler. Başka veri yapılarında da gösterge kullanımına çok rastlanır. her düğümün kendisinden bir sonraki düğümü göstermesi düşüncesi üzerine kuruludur. } // ..3 Veri yapılarında Bazı veri yapılarının temeli göstergelere dayanır.1 Yukarıdaki şekil yanıltıcı olabilir: burada düğümlerin bellekte art arda bulundukları sanılmasın. her düğümün kendisinden bir sonraki düğümü gösteriyor olmasıdır. Liste.org 399 .16.15. Not: Kendi türünden nesneleri gösterdiği için bunun özyinelemeli bir yapı olduğunu söyleyebiliriz. Sonuncu düğüm hiçbir düğümü göstermez (değeri null 'dır): ilk düğüm +--------+---+ | eleman | o----▶ +--------+---+ düğüm +--------+---+ | eleman | o----▶ +--------+---+ düğüm +--------+---+ | eleman | o----▶ .. Böyle veri yapıları. Hataya açık olduklarını akılda tutmak gerekir. Copyright © 2009-2011 Ali Çehreli. 66. } // . İkili ağaç veri yapısının düğümleri de sol ve sağ dallardaki düğümleri gösterirler. D'de veri yapıları referans türleri kullanarak da gerçekleştirilebilseler de. Dizilerin elemanlarının yan yana bulunmalarının aksine. elemanların veri yapısına farklı zamanlarda eklenmeleri olabilir. Ek olarak. kendisinden bir sonraki düğümü gösterir.4 Belleğe doğrudan erişmek gerektiğinde Göstergeler belleğe doğrudan ve bayt düzeyinde erişim sağlarlar. Düğümler normalde belleğin herhangi bir yerinde bulunabilirler.Göstergeler 66... Bu şekle uygun olarak. programa ait olmayan belleğe erişmek tanımsız davranıştır. Bütün düğümlerin bir liste olarak düşünülmesi de yalnızca başlangıç düğümünü gösteren bir gösterge ile sağlanabilir: struct Liste { Düğüm * baş. Bunun bir nedeni. +--------+---+ son düğüm +--------+------+ | eleman | null | +--------+------+ 66. bir int listesinin düğümünü şöyle tanımlayabiliriz: struct Düğüm { int eleman. Önemli olan.. bazı veri yapılarının elemanları bellekte birbirlerinden ayrı olarak dururlar. elemanların birbirlerini göstermeleri temeli üzerine kuruludur.16 Örnekler Basit bir bağlı liste Bağlı liste veri yapısının elemanları düğümler halinde tutulurlar.15. Düğüm * sonraki. Örneğin bağlı liste veri yapısının her düğümü..

Bunu problemler bölümünde soruyorum. } } return sonuç. (Not: Aslında sonuna ekleme işlemi daha doğal ve kullanışlıdır. this.conv.eleman = eleman. sonraki üyesi olarak listenin şu andaki başı kullanılıyor. void başınaEkle(int eleman) { baş = new Düğüm(eleman. } } // . Düğüm * sonraki) { this. Bu yeni nesne kurulurken.Göstergeler Dersin amacından fazla uzaklaşmamak için. baş). if (sonraki) { sonuç ~= " -> " ~ to!string(*sonraki). Listenin yeni başı olarak da bu yeni düğümün adresi kullanılınca. burada yalnızca listenin başına eleman ekleyen işlevi göstermek istiyorum: struct Liste { Düğüm * baş. import std.org 400 . Düğüm * sonraki. struct Düğüm { int eleman. O satır. Bu küçük veri yapısını deneyen küçük bir program: import std. } string toString() const { string sonuç = to!string(eleman). import std.) Yukarıdaki satırda sağ tarafta dinamik bir Düğüm nesnesi oluşturuluyor. listenin başına eleman eklenmiş oluyor. } } Copyright © 2009-2011 Ali Çehreli. Bu kodun en önemli noktası başınaEkle işlevini oluşturan satırdır.. } string toString() const { return format("(%s)". void başınaEkle(int eleman) { baş = new Düğüm(eleman.string. yeni elemanı listenin başına ekler ve böylece bu yapının bir bağlı liste olmasını sağlar.. baş).stdio. http://ddili. this(int eleman. baş ? to!string(*baş) : "").sonraki = sonraki. } struct Liste { Düğüm * baş.

0 . sayılar). Göstergeler.16. writeln(baytGöstergesi[3]).. Eğer sizin mikro işlemciniz de benimki gibi küçük soncul ise. Göstergenin değerini tür dönüşümü ile bir ubyte göstergesine atayabiliriz: ubyte * baytGöstergesi = cast(ubyte*)adresi. kendi türü için gereken sayıda bayt üzerinde kurulur. http://ddili.org 401 .başınaEkle(sayı). } } writeln("sonra: ".Göstergeler void main() { Liste sayılar. writeln(baytGöstergesi[2]). Örneğin bir değişkenin adresi bir ubyte göstergesine atanır ve bu gösterge ilerletilerek o değişkeni oluşturan baytların tümü gözlemlenebilir. int 'i oluşturan baytların bellekte ters sırada durduklarını görebilirsiniz: 4 3 2 1 Değişkenleri oluşturan baytları gözlemleme işini kolaylaştırmak için bir işlev şablonu yazabiliriz: Copyright © 2009-2011 Ali Çehreli. Bu adresteki int 'i oluşturan 4 baytı şöyle yazdırabiliriz: writeln(baytGöstergesi[0]).2 ubyte göstergesi ile belleğin incelenmesi Belleğin adresleme birimi bayttır. Örnek olarak. writeln("önce : ". Belleğe bayt olarak erişmek için en uygun tür ubyte* 'dir. O göstergenin değeri. Her adreste tek baytlık bilgi bulunur. değerini açıklayıcı olsun diye onaltılı düzende yazdığım bir tamsayı olsun: int birSayı = 0x01020304. foreach (sayı. birSayı 'nın bellekte bulunduğu yerin adresidir. bize belleğe bayt bayt erişme olanağı sunarlar. Her değişken. Çıktısı: önce : () sonra: (9 -> 8 -> 7 -> 6 -> 5 -> 4 -> 3 -> 2 -> 1 -> 0) 66. sayılar). 10) { sayılar. writeln(baytGöstergesi[1]). Bu değişkeni gösteren bir göstergenin şu şekilde tanımlandığını gördük: int * adresi = &birSayı.

yani değişkenin başlangıç adresinin yazdırılması Türün büyüklüğünün .sizeof değerinin aralık dışında kaldığına ve ona hiçbir zaman erişilmediğine dikkat edin. this.sizeof aralığında değişir. baş . writefln("tür : writefln("değer : writefln("adres : writef( "baytlar: %s". 2. T. int j. baş). // (1) // (2) // (3) // (4) foreach (i.sizeof) { writef("%02x ". } 1. %s". baş + T. } writeln(). baş + T. this(int i.. 4.j = j. } } void main() { int tamsayı = 0x11223344. writeln()...sizeof) { writef("%02x ". "). baytlarınıGöster(dizi). 3.Göstergeler void baytlarınıGöster(T)(ref T değişken) { const ubyte * baş = cast(ubyte*)&değişken. O işlev şablonunu değişik türlerle çağırabiliriz: struct Yapı { int birinci. int j) { this. 0 .nan. } O döngüde bayt göstergesininin değeri. 2. double kesirli = double. baytlarınıGöster(kesirli). *bayt). int ikinci. http://ddili. 3 ]. } class Sınıf { int i. string dizgi = "merhaba dünya". değişken). baş . baş[i]).stringof). baytlarınıGöster(dizgi). Değişkenin adresinin bir ubyte göstergesine atanması Göstergenin değerinin.i = i. T. Copyright © 2009-2011 Ali Çehreli. %s". int[3] dizi = [ 1. baytlarınıGöster(tamsayı). baş + T.sizeof niteliği ile edinilmesi Göstergenin gösterdiği bayta [] işleci ile erişilmesi Yukarıdaki döngüyü * işleci ile erişecek şekilde şöyle de yazabilirdik: foreach (bayt.org 402 .

nan özel değerini oluşturan baytları ters sırada düşününce. 2). } // asıl karakterler Copyright © 2009-2011 Ali Çehreli. baytlarınıGöster(yapıNesnesi). sabit uzunluklu dizinin (int[3u] ). auto sınıfNesnesi = new Sınıf(1. ve yapı nesnesinin değerlerinin baytları bellekte ters sırada bulunmaktadır 2. Derleyicinin bir iç türü olduğunu vurgulamak için ismini __ ile başlatarak. onun değeri olan "merhaba dünya"nın o kadar küçük bir alana sığması olanaksızdır. 0xbb).org 403 . örneğin şöyle bir yapı olduğunu düşünebiliriz: struct __string { int uzunluk.Sınıf BFFD6D3C c0 ec be 00 (1) (2) (3) (1) (1) (4) Gözlemler: 1. Bazı türlerin baytları beklediğimiz gibidir: int 'in. 187) BFFD6D34 aa 00 00 00 bb 00 00 00 Sınıf deneme.Göstergeler auto yapıNesnesi = Yapı(0xaa. } Çıktısı aydınlatıcı olabilir: tür : değer : adres : baytlar: tür : değer : adres : baytlar: tür : değer : adres : baytlar: tür : değer : adres : baytlar: tür : değer : adres : baytlar: tür : değer : adres : baytlar: int 287454020 BFFD6D0C 44 33 22 11 double nan BFFD6D14 00 00 00 00 00 00 f8 7f string merhaba dünya BFFD6D1C 0e 00 00 00 e8 c0 06 08 int[3u] 1 2 3 BFFD6D24 01 00 00 00 02 00 00 00 03 00 00 00 Yapı Yapı(170. string 8 bayttan oluşmaktadır. string türünün perde arkasında bir yapı gibi tanımlanmış olmasından gelir. baytlarınıGöster(sınıfNesnesi). http://ddili. double. char * ptr. Bu. bu değerin 0x7ff8000000000000 özel bit dizisi ile ifade edildiğini öğreniyoruz 3.

karakter). string 'in .org 404 . Çıktıdan anlaşıldığına göre ü harfi için gerçekten de iki bayt kullanılmaktadır: Copyright © 2009-2011 Ali Çehreli. dizgi. sınıf değişkenlerinin sınıf nesnesini gösterecek şekilde tek bir göstergeden oluştuğu şüphesini uyandırır: struct __Sınıf_DeğişkenTürü { __Sınıf_AsılNesneTürü * nesne. void belleğiGöster(T)(T * bellek. "isprint olmayan" karakterler yerine '.ptr... Bu işlevi. 0 .'. // . } std. belleğiGöster(dizgi. const char karakter = isprint(*adres) ? *adres : '. uzunluk) { const ubyte * adres = baş + i. } writefln("%s: %02x %s". adres. belirli bir adresteki belirli sayıdaki baytı gösteren bir işlev yazalım: import std.. Benzer şekilde. bu tahmini güçlendiriyor. kendisine verilen bayt değerinin ASCII tablosunun görüntülenebilen bir karakteri olup olmadığını bildirir. foreach (i.length). http://ddili. iki int için 8 bayt gerektiğini biliyoruz. Belirli bir değişkenin baytları yerine. *adres. } Şimdi biraz daha esnek bir işlev düşünelim. 4.ctype modülünde tanımlı olan isprint işlevi.ptr niteliğinin gösterdiği karakterlere erişmek için kullanabiliriz: string dizgi = "merhaba dünya". içlerindeki ü'nün UTF-8 kodlamasında iki baytla ifade edilmesi nedeniyle 14 bayttan oluşur. sınıf nesnesini oluşturan i ve j üyelerinin 4 bayta sığmaları olanaksızdır. int uzunluk) { const ubyte * baş = cast(ubyte*)bellek. "merhaba dünya" dizgisindeki toplam 13 harf.ctype. O çıktı.' karakterini yazdırıyoruz. string 'in yukarıda görülen ilk 4 baytı olan 0x0000000e'nin değerinin onlu sistemde 14 olması. Bazı bayt değerlerinin tesadüfen uç birimin kontrol karakterlerine karşılık gelerek uç birimin çalışmasını bozmalarını önlemek için.Göstergeler Bu tahmini destekleyen bulguyu string 'i oluşturan baytlarda görüyoruz: dikkat ederseniz.

assert(j == 1). Yeni elemanların listenin başına değil. çözümler Copyright © 2009-2011 Ali Çehreli. Kendisine verilen iki int 'in değerlerini değiş tokuş etmeye çalışan şu işlevi parametrelerinde ref kullanmadan düzeltin: void değişTokuş(int birinci. Bu derste gösterilen liste yapısını şablona dönüştürün ve böylece int 'ten başka türlerle de kullanılabilmesini sağlayın. // Değerleri değişsin assert(i == 2). Bu çözümde ref parametre kullanmak yasak! :o) 2.. . Bunun için listenin sonuncu elemanını gösteren bir gösterge yararlı olabilir. j). } void main() { int i = 1.17 Problemler 1. Bağlı listede yeni elemanların sona eklenmeleri daha doğal bir işlemdir. ikinci = geçici. birinci = ikinci. sonuna eklenmelerini sağlayın.. değişTokuş(i. } O programı çalıştırdığınızda assert denetimlerinin başarısız olduklarını göreceksiniz. Ben daha kısa olduğu için bu derste başına eklemeyi seçtim.org 405 . . int ikinci) { int geçici = birinci. http://ddili. 3. n y a 66.Göstergeler 8067F18: 8067F19: 8067F1A: 8067F1B: 8067F1C: 8067F1D: 8067F1E: 8067F1F: 8067F20: 8067F21: 8067F22: 8067F23: 8067F24: 8067F25: 6d 65 72 68 61 62 61 20 64 c3 bc 6e 79 61 m e r h a b a d . int j = 2.

Bilgisayarlarda adreslenebilen.1 Transistör Modern elektronik aletlerin işlem yapma yetenekleri büyük ölçüde transistör denen elektronik devre elemanı ile sağlanır. Depoladığı veriyi.2 Bit Bilgisayarlarda en küçük bilgi birimi bittir. bool. Programlama dili aracılığıyla ifade ettiğimiz işlemleri ve verileri en alt düzeyde gerçekleştiren elemanlardır. elektronik devrenin kendi durumundan haberinin olmasını ve kendi durumunu değiştirebilmesini sağlar. Bilgisayarlar. veri olarak iki farklı değerden birisini depolayabilir: 0 veya 1. Transistörün bir özelliği. Bir anlamda.sizeof. Böyle işlevlere özellikle C kütüphanelerinden uyarlanmış olan D kütüphanelerinde rastlayabilirsiniz. Copyright © 2009-2011 Ali Çehreli. D dili ile ifade ettiğimiz kavramların en alt düzeyde elektronik devre elemanlarına nasıl bağlı olduklarını anlamak önemlidir. Transistörler hem mikro işlemcinin içinde hem de bilgisayarın ana belleğinde çok büyük sayılarda bulunurlar.3 Bayt Bayt. Bu yüzden. Yine de. birbirleriyle ilişkilendirilen 8 bitin bir araya gelmesinden oluşur.stringof.org 406 . Program içinde tanımladığımız Öğrenci gibi bir kullanıcı türünün bilgisayarın iç yapısı ile doğrudan bir ilgisi yoktur. Bu konularda başka kaynaklarda çok miktarda bilgi bulabileceğinizi bildiğim için bu başlığı olabildiğince kısa tutacağım. yani ayrı ayrı erişilebilen en küçük veri bayttır. 67. devrenin başka tarafındaki sinyallerle kontrol edilebilmesidir. Bit. hem de parametre olarak bayrak alan işlevler için gereklidir.sizeof değerine bakarak kolayca görebiliriz: writeln(bool.Bit İşlemleri 67 Bit İşlemleri Bu bölümde mikro işlemcinin en küçük bilgi birimi olan bitlerle yapılan işlemleri tanıyacağız. Bunun nedeni. veriye bit düzeyinde doğrudan erişim sağlamazlar. yalnızca false ve true diye iki farklı değer alan ve bu yüzden de tek bitlik bilgi taşıyan bool türü bile 1 bayt olarak gerçekleştirilir.1. tekrar değiştirilene kadar veya enerji kaynağı tükenene kadar korur. bool.1. Bu işlemler hem alt düzey programcılık açısından bilinmelidir. 67. ' '. Bunu. her bitin adreslenebilmesinin bilgisayarın karmaşıklığını ve maliyetini çok arttıracak olması. en alt düzeyde bir kaç tane transistörün belirli bir düzende bir araya getirilmesi ile gerçekleştirilir. ve zaten tek bitlik kavramların desteklenmeye değmeyecek kadar nadir olmalarıdır. Bellekten tek seferde en az bir bayt veri okunabilir ve belleğe en az bir bayt veri yazılabilir. 67.1. Bit işlemleri mikro işlemcinin en temel olanaklarındandır. Bir programlama dilinin amaçlarından birisi. donanımın anladığı dil ile insanın anladığı dil arasında aracılık yapmaktır. Bit.1 Verinin en alt düzeyde gerçekleştirilmesi D gibi bir programlama dili aslında bir soyutlamadır. http://ddili. " bayttır"). 67.

0 adet 100. bitin iki değer alabilmesinden gelir. vs. (Not: Bitler örneğin üç farklı değer alabilseler. Programlama dili aracılığıyla gerçekleştirilen her iş eninde sonunda bir veya daha fazla yazmaç tarafından halledilir. 10. binler. Bu. şeklinde gitmesi gerektiğini görürüz. onlar. ve 3 adet 1 Dikkat ederseniz. ikili sayı sisteminde yazılmış olan 32 bitlik işaretsiz bir değerin bütün basamaklarını ve basamak değerlerini şöyle gösterebiliriz: Copyright © 2009-2011 Ali Çehreli. yüzler.) Günlük hayatta kullandığımız sayıların birler. 0 adet 4. Örneğin 1023 gibi bir sayı şöyle ifade edilebilir: 1023 == 1 adet 1000. vs. dörtler. Yani sola doğru ilerlendiğinde her basamağın değeri 2 kat artmalıdır: 1. Yazmaçlar oldukça kısıtlı ama çok hızlı işlemler sunarlar. ikili sistemde yazılmış olan sayıların basamaklarının da birler.1. Buna göre. vs. 1000. ikiler. 2. Örneğin 32 bitlik işlemcilerde yazmaçlar 4 bayttan. yani en düşük değerli olan basamağa 0 numaralı basamak denir. Aynı tanımı ikili sayı sistemine taşıyınca. Örneğin 32 bitlik bir işlemci en etkin olarak int ve uint türlerini. Yazmaç genişliği.4 Yazmaç Mikro işlemcinin kendi içinde bulunan depolama ve işlem birimleri yazmaçlardır. 2 adet 10. 64 bitlik bir işlemci de long ve ulong türlerini işleyebilir. 1 adet 1 Basamaklar numaralanırken. mikro işlemcinin en etkin olarak işleyebildiği bilgi miktarını belirler.org 407 . 1 adet 2.2 İkili sayı sistemi Günlük hayatta kullandığımız onlu sayı sisteminde 10 rakam vardır: 0123456789. sola doğru ilerlendiğinde her basamağın değeri 10 kat artmaktadır: 1. basamakları vardır. 64 bitlik işlemcilerde de 8 bayttan oluşur. 67. 100. 4. 8. Yazmaçlar her işlemcinin bit genişliğine bağlı olan sayıda bayttan oluşurlar. http://ddili.Bit İşlemleri bool 1 bayttır 67. vs. en sağdaki. Örneğin 1011 gibi bir ikili sayı şöyle ifade edilebilir: 1011 == 1 adet 8. İkili sayı sistemlerinde de iki rakam vardır: 0 ve 1. bilgisayarlar üçlü sayı sistemini kullanırlardı. sekizler.

912 268.435.576 524.096 2.741.483. void main() { // 1073741824 4 1 // ↓ ↓ ↓ int sayı = 0b_01000000_00000000_00000000_00000101.097.554.048 1.stdio.108.Bit İşlemleri Basamak 31 30 29 28 27 26 25 24 23 22 21 20 19 18 17 16 15 14 13 12 11 10 9 8 7 6 5 4 3 2 1 0 Değeri 2.384 8.072 65. } Çıktısı: Copyright © 2009-2011 Ali Çehreli.608 4.456 134. İkili sistemde yazılan değerlerin 0b ile başladıklarını Hazır Değerler dersinde görmüştük.192 4.777. Okumayı kolaylaştırmak için alt çizgi karakterlerinden de yararlanarak: import std.728 67. http://ddili. writeln(sayı).536 32.217.864 33.304 2.147.648 1.388.768 16.870. düşük değerli bitlere alt bit denir. İkili sistemde değerler yazarak bu tabloya nasıl uyduklarına bakabiliriz.048.144 131.152 1.288 262.194.024 512 256 128 64 32 16 8 4 2 1 Yüksek değerli bitlere üst bit.824 536.432 16.073.org 408 .216 8.

4096. c 12. Burada önemli olan.2. 256. aksine bir neden olmadığı sürece aşağıda gösterilenler gibi bit işlemlerinde her zaman için işaretsiz türler kullanılır: ubyte . Aynı deneyi uint ile yaptığımızda tablodaki değeri görürüz: uint sayı = 0b_10000000_00000000_00000000_00000000. d 13. Hem çok yer kaplar. On altılı sayı sisteminde toplam 16 rakam vardır. 16. a 10.1 İşaretli türlerin işaret biti En üst bit. Latin alfabesinden 6 harf ödünç alınarak bu sistemin rakamları olarak 0123456789abcdef kabul edilmiştir. O sıralamadan bekleneceği gibi. b 11. ikili sayı sistemi görüntülenmeye uygun değildir.org 409 . http://ddili. -2147483648 En üst bitin diğerlerinden bağımsız olduğunu düşünmeyin. diğer bitlerinin 0 olmalarına bakarak -0 değeri olarak düşünülmemelidir (zaten tamsayılarda -0 diye bir değer yoktur). 2147483648 Bu yüzden. Bunun ayrıntısına burada girmeyeceğim ve bunun D'nin de kullandığı ikiye tümleyen sayı gösterimi ile ilgili olduğunu söylemekle yetineceğim. hem de yalnızca 0 ve 1'lerden oluştuğu için okunması ve anlaşılması zordur. uint . ve ulong . 67. bu sistemde sola doğru ilerlendiğinde her basamağın değeri 16 kat artar: 1.Bit İşlemleri 1073741829 Dikkat ederseniz. Bu basamakların yukarıdaki tablodaki basamak değerlerini toplayınca bu değere eşit olduğunu görürüz: 1073741824 + 4 + 1 == 1073741829. writeln(sayı). Örneğin yukarıdaki sayı.147. on altılı sistemdeki 8 basamaklı bir sayının basamak değerleri şöyledir: Copyright © 2009-2011 Ali Çehreli. vs. işaretli türlerde sayının artı veya eksi olduğunu bildirmek için kullanılır: int sayı = 0b_10000000_00000000_00000000_00000000. o hazır değerin içinde rakamı 1 olan yalnızca 3 adet basamak vardır.648 değerinin yalnızca işaretsiz türlerde geçerli olduğunu bilmenizdir. Yukarıdaki sayı sistemlerine benzer şekilde. abcdef harfleri yerine isteğe bağlı olarak ABCDEF harfleri de kullanılabilir. e 14.3 On altılı sayı sistemi Yukarıdaki hazır değerlerden de görülebileceği gibi. yukarıdaki tabloda gösterilen en yüksek 2. 67.483. Alfabelerde 10'dan fazla rakam bulunmadığı için. ve f 15 değerindedir. Daha kullanışlı olduğu için on altılı sayı sistemi yaygınlaşmıştır. Örneğin. writeln(sayı).

048.777. Yukarıda ilk kullandığımız ikili değer için: Copyright © 2009-2011 Ali Çehreli.576 65.435. a'nın 10 ve f'nin 15 olduklarını hatırlayarak hesaplarsak: 3145728 + 40960 + 15 == 3186703. Birbirlerine karşılık gelen değerler şöyledir: On altılı 0 1 2 3 4 5 6 7 8 9 a b c d e f İkili 0000 0001 0010 0011 0100 0101 0110 0111 1000 1001 1010 1011 1100 1101 1110 1111 Onlu 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 Örneğin yukarıda kullandığımız 0x0030a00f on altılı değerini ikili olarak şöyle yazabiliriz: // on altılı: 0 0 3 0 a 0 0 f uint ikili = 0b_0000_0000_0011_0000_1010_0000_0000_1111.536 4. writeln(sayı). 3186703 Bunun nedenini sayı içindeki sıfır olmayan basamakların katkılarına bakarak anlayabiliriz: 3 adet 1048576. a adet 4096. On altılı ve ikili sistemde yazılan sayılar kolayca birbirlerine dönüştürülebilirler.Bit İşlemleri Basamak 7 6 5 4 3 2 1 0 Değeri 268. ve f adet 1.216 1. İkili sistemden on altılı sisteme dönüştürmek için de ikili sayının her dört basamağı on altılı sistemde tek basamak olarak yazılır. On altılı sistemdeki bir sayıyı ikili sisteme dönüştürmek için.org 410 .096 256 16 1 On altılı hazır değerlerin 0x ile yazıldıklarını hatırlayarak bir deneme: // 1048576 4096 1 // ↓ ↓ ↓ uint sayı = 0x_0030_a00f.456 16. http://ddili. sayının her basamağı ikili sistemde dört basamak olarak yazılır.

} void main() { göster(123456789). void göster(uint sayı) { writefln("%032b %08x %10s". bu işlemler en az 8 biti birden etkilemek zorundadır. write(" "). 1 olan bitler 0.stdio. Siz buradaki işlemleri ubyte ve ulong türleri ile. on altılı. write("~ "). ve onlu: 00000111010110111100110100010101 075bcd15 123456789 67. diğerleri için 0 değeri üretilir. aşağıdaki işlemleri açıklamada yardımcı olacak bir işlev yazalım. Kendisine verilen sayıyı ikili. sayı. bitlere doğrudan erişilemediği için.2 Ve işleci & İki ifadenin arasına yazılır. Her ne kadar bit düzeyindeki işlemlerden bahsediyor olsak da. ve işaret bitinin önemini hatırlamak şartıyla byte . Copyright © 2009-2011 Ali Çehreli. int . her iki ifadede de 1 olan bitler için 1 değeri. 0 olanlar 1 olur: uint değer = 123456789. Bu işlecin etkisi ikili gösteriminde çok kolay anlaşılıyor. sayı. göster(değer). ve long türleri ile de deneyebilirsiniz. göster(~değer).4.1 Tersini alma işleci ~ Bu işleç. 67. Ben üst bitin özel anlamı nedeniyle işaretli türleri gözardı edeceğim ve bu örneklerde uint türünü kullanacağım. İki ifadenin aynı numaralı bitlerine sırayla bakılır. sayı).Bit İşlemleri // ikili: 0100 0000 0000 0000 0000 0000 0000 0101 uint onAltılı = 0x___4____0____0____0____0____0____0____5. değerleri bit düzeyinde değiştiren işlemlere geçebiliriz. Şimdi. ikili.4 Bit işlemleri Değerlerin bitlerle nasıl ifade edildiklerini ve ikili veya onaltılı olarak nasıl yazıldıklarını gördük. on altılı. http://ddili. } Sırasıyla. Önce. Her bit tersine dönmüştür: 00000111010110111100110100010101 075bcd15 123456789 ~ 11111000101001000011001011101010 f8a432ea 4171510506 Bu işlecin bit düzeyindeki etkisini şöyle özetleyebiliriz: ~0 → 1 ~1 → 0 67. önüne yazıldığı ifadenin bitleri ters olanını üretir.4. Örneğin ubyte türündeki bir ifadenin 8 bitinin hepsi de.org 411 . ve onlu sistemde göstersin: import std. ama ayrı ayrı olarak bit işlemine dahil edilir. Sonuç olarak.

göster(soldaki & sağdaki).org 412 . Çıktıda önce soldaki ifadeyi. sonra da sağdaki ifadeyi görüyoruz. 0 ile "ve"lemek. 30. writeln("| --------------------------------"). write(" "). Her iki ifadede de 0 olan bitlere karşılık 0 değeri üretilir. diğerlerinin sonucu 1 olur: uint soldaki = 123456789. write(" "). 00000111010110111100110100010101 075bcd15 123456789 00111010110111100110100010110001 3ade68b1 987654321 | -------------------------------00111111110111111110110110110101 3fdfedb5 1071639989 Dikkat ederseniz. write(" "). write(" "). write(" "). uint sağdaki = 987654321. numaralı bitlerini ayrı ayrı kullanır. sonucun da 1 olması için yeterlidir: Copyright © 2009-2011 Ali Çehreli. göster(soldaki). uint sağdaki = 987654321. İki bitin 0 ve 1 oldukları dört farklı durumda ancak iki bitin de 1 oldukları durum 1 sonucunu verir: 0 0 1 1 & & & & 0 1 0 1 → → → → 0 0 0 1 Gözlemler: • Bir taraf 0 ise. sonuçta 0 olan bitler. Bunu bir tablo ile gösterebiliriz. 1 ile "ve"lemek etkisizdir 67.3 Veya işleci | İki ifadenin arasına yazılır. sonuç diğerinin değeridir. Mikro işlemci bu işlemde her iki ifadenin 31. diğer taraftan bağımsız olarak sonuç 0'dır. writeln("& --------------------------------"). İki ifadenin aynı numaralı bitlerine sırayla bakılır. sıfırlamak anlamına gelir • Bir taraf 1 ise.Bit İşlemleri uint soldaki = 123456789. Kesikli çizginin altında da bit işleminin sonucu yazdırılıyor: 00000111010110111100110100010101 075bcd15 00111010110111100110100010110001 3ade68b1 & -------------------------------00000010010110100100100000010001 025a4811 123456789 987654321 39471121 Dikkat ederseniz. göster(soldaki | sağdaki). 29. Bitin soldaki veya sağdaki ifadede 1 olması. çizginin üstündeki her iki ifadede de 1 değerine sahip olan bitlerdir. vs. göster(soldaki). Bu işleç bu yüzden ve işleci olarak isimlendirilmiştir: soldaki ve sağdaki bit 1 olduğunda 1 değerini üretir. write(" ").4. göster(sağdaki). her iki ifadede de 0 olan bitlerdir. http://ddili. kesikli çizginin altına yazdırdığım sonuç değerde 1 olan bitler. göster(sağdaki).

org 413 . writeln("^ --------------------------------"). göster(soldaki ^ sağdaki). 00000000000000000000000000000000 00000000 0 67.5 Sağa kaydırma işleci >> İfadenin değerini oluşturan bitleri belirtilen sayıda basamak kadar sağa kaydırır. uint sağdaki = 987654321. soldaki ve sağdaki ifadelerde farklı olan bitlerdir. sonuç diğerinin değeridir. sonuçta 1 olan bitler. İki ifadede farklı olan bitlere karşılık 1 değeri üretilir. İkisinde de 0 veya ikisinde de 1 olan bitlere karşılık 0 üretilir. göster(soldaki). Bu örnek. 0 ile "veya"lamak etkisizdir • Bir taraf 1 ise. İki ifadenin aynı numaralı bitlerine sırayla bakılır.Bit İşlemleri 0 0 1 1 | | | | 0 1 0 1 → → → → 0 1 1 1 Gözlemler: • Bir taraf 0 ise. 0 0 1 1 ^ ^ ^ ^ 0 1 0 1 → → → → 0 1 1 0 Gözlem: • Kendisiyle "ya da"lamak sıfırlamak anlamına gelir Değeri ne olursa olsun. aynı değişkenin kendisiyle "ya da"lanması 0 sonucunu üretir: uint değer = 123456789. göster(değer ^ değer). diğer taraftan bağımsız olarak sonuç 1'dir. Sol taraftan yeni gelen bitler işaretsiz türlerde 0 olur. Kaydırılacak yerleri olmayan en sağdaki bitler düşerler ve değerleri kaybedilir.4. write(" "). 00000111010110111100110100010101 075bcd15 123456789 00111010110111100110100010110001 3ade68b1 987654321 ^ -------------------------------00111101100001011010010110100100 3d85a5a4 1032168868 Dikkat ederseniz. göster(sağdaki). write(" "). 1 ile "veya"lamak 1 yapmak anlamına gelir 67. diğerlerinin sonucu 0 olur: uint soldaki = 123456789. http://ddili. bitleri 2 basamak kaydırıyor: Copyright © 2009-2011 Ali Çehreli. write(" ").4.4 Ya da işleci ^ İki ifadenin arasına yazılır.

6 İşaretsiz sağa kaydırma işleci >>> Bu işleç sağa kaydırma işlecine benzer şekilde çalışır. göster(değer >> 3). Sağdan kaybedilecek olan bitleri griyle.7 Sola kaydırma işleci << Sağa kaydırma işlecinin tersi olarak.org 414 . göster(değer). göster(değer << 4).Bit İşlemleri uint değer = 123456789.4. http://ddili. Tek farkı. göster(değer). Bu. soldan yeni gelen bitleri de maviyle işaretliyorum: 00000111010110111100110100010101 075bcd15 00000001110101101111001101000101 01d6f345 123456789 30864197 Dikkat ederseniz. göster(değer >> 2).4. göster(değer >>> 3). Bu etkiyi göstermek için int türünde ve özellikle üst biti 1 olan bir değer seçelim: int değer = 0x80010300. alt satırdaki bitler üst satırdaki bitlerin iki bit sağa kaydırılması ile elde edilmiştir. bitleri belirtilen basamak kadar sola kaydırır: uint değer = 123456789 . İşaretli türlerde ise işaret genişletilmesi (sign extension) denen bir yöntem uygulanır ve sayının en soldaki biti ne ise. işaretsiz türlerde böyledir. 01000000000000010000001100000000 40010300 1073808128 00001000000000000010000001100000 08002060 134226016 67. soldan hep o bitin değerinde bitler gelir. göster(değer >> 3). göster(değer). Bitler sağa kaydırılırken sol tarafa yeni gelenlerin 0 olduklarını gördünüz. işaret genişletilmesinin kullanılmamasıdır. Türden ve en soldaki bitten bağımsız olarak soldan her zaman için 0 gelir: int değer = 0x80010300. 10000000000000010000001100000000 80010300 2147549952 00010000000000000010000001100000 10002060 268443744 67. göster(değer). Asıl sayıda üst bit 1 olduğu için yeni gelen bitler de 1 olur: 10000000000000010000001100000000 80010300 2147549952 11110000000000000010000001100000 f0002060 4026540128 Üst bitin 0 olduğu bir değerde yeni gelen bitler de 0 olur: int değer = 0x40010300. Copyright © 2009-2011 Ali Çehreli. göster(değer).

göster(sağdaki). sonucun bütün bitlerinin 1 olmasını sağlar: uint soldaki = 0xaaaaaaaa.2 & işleci. Örnek olarak &= işlecini kullanırsak: değer = değer & 123. write(" "). write(" "). değer &= 123. >>>= . write(" "). 67. Burada bu anlamlara dikkat çekmek istiyorum. write(" "). Uç bir örnek olarak.8 Atamalı bit işleçleri Bütün bu işleçlerin atamalı olanları da vardır: ~= . ^= .1 | işleci. kesişim kümesidir Her iki ifadede de 1 olan bitlerin kesişimini verir. writeln("| --------------------------------"). http://ddili. writeln("& --------------------------------"). uint sağdaki = 0x55555555. &= .5.org 415 . >>= . göster(sağdaki). Tamsayılar ve Aritmetik İşlemler dersinde gördüğümüz atamalı aritmetik işleçlerine benzer şekilde.5 Anlamları Bu işleçlerin bit düzeyinde nasıl işledikleri.Bit İşlemleri En soldaki bit değerleri kaybedilir ve sağ taraftan 0 değerli bitler gelir: 00000111010110111100110100010101 075bcd15 123456789 01110101101111001101000101010000 75bcd150 1975308624 67. göster(soldaki | sağdaki). |= . göster(soldaki). yukarıdaki iki ifadenin 1 olan hiçbir biti diğerini tutmadığı için. write(" "). birleşim kümesidir İki ifadenin 1 olan bitlerinin birleşimini verir. göster(soldaki & sağdaki). // üsttekiyle aynı şey 67.5.4. write(" "). Uç bir örnek olarak. ve <<= . bunlar işlemi gerçekleştirdikten sonra sonucu soldaki ifadeye atarlar. kesişimlerinin bütün bitleri 0'dır: uint soldaki = 0xaaaaaaaa. işlemlerin hangi anlamlarda görülmeleri gerektiği konusunda yeterli olmayabilir. uint sağdaki = 0x55555555. Copyright © 2009-2011 Ali Çehreli. göster(soldaki). bitleri birer basamak atlayarak 1 olan ve birbirlerini tutmayan iki ifadenin birleşimi. 10101010101010101010101010101010 aaaaaaaa 2863311530 01010101010101010101010101010101 55555555 1431655765 | -------------------------------11111111111111111111111111111111 ffffffff 4294967295 67.

uint sıfırYapılacakBitler = 0xffefffef. Etkilenen bitlerin önceki ve sonraki durumlarını yine maviyle işaretleyerek: önce : silinecekler: sonra : 00000000111111110000000011111111 00ff00ff 16711935 11111111111011111111111111101111 ffefffef 4293918703 00000000111011110000000011101111 00ef00ef 15663343 sıfırYapılacakBitler değeri. bir anlamda hangi bitlerin 1 yapılacakları bilgisini taşımış ve asıl ifadenin o bitlerini 1 yapmış ve diğerlerine dokunmamıştır. belirli bitleri siler İfadelerden bir taraftakini asıl değişken olarak düşünürsek. write("önce : write("silinecekler: "). göster(ifade).Bit İşlemleri 10101010101010101010101010101010 aaaaaaaa 2863311530 01010101010101010101010101010101 55555555 1431655765 & -------------------------------00000000000000000000000000000000 00000000 0 67. "). göster(sıfırYapılacakBitler). diğer ifadede o bitin 1 olup olmadığı sorgulanabilir: Copyright © 2009-2011 Ali Çehreli.5. 67. diğer ifadeyi de 1 yapılacak olan bitleri seçen ifade olarak görebiliriz: uint ifade = 0x00ff00ff. http://ddili. 67. göster(birYapılacakBitler). göster(ifade). ifade &= sıfırYapılacakBitler.org 416 . ifade |= birYapılacakBitler. Etkilenen bitlerin önceki ve sonraki durumlarını maviyle işaretledim: önce : 1 olacaklar: sonra : 00000000111111110000000011111111 00ff00ff 00010000000000000001000000000000 10001000 00010000111111110001000011111111 10ff10ff 16711935 268439552 285151487 birYapılacakBitler değeri. hangi bitlerin sıfırlanacakları bilgisini taşımış ve asıl ifadenin o bitlerini sıfırlamıştır. diğer ifadeyi de silinecek olan bitleri seçen ifade olarak görebiliriz: uint ifade = 0x00ff00ff. belirli bitleri 1 yapar İfadelerden bir taraftakini asıl değişken olarak düşünürsek. uint birYapılacakBitler = 0x10001000. göster(ifade). write("sonra : "). belirli bir bitin 1 olup olmadığını sorgular Eğer ifadelerden birisinin tek bir biti 1 ise. write("önce : write("1 olacaklar: ").5 & işleci.5. ").5.3 |= işleci. göster(ifade). write("sonra : ").4 &= işleci.

-1000 67.6 Sağa kaydırmak ikiye bölmektir Sağa bir bit kaydırmak. writeln(değer >> 3). Örneğin 3 bit kaydırmak. 1 Başka bir bitini sorgulayalım: uint sorgulananBit = 0x00001000.5. writeln(ifade & sorgulananBit ? "evet. sağa kaydırmak işaretli türlerde de ikiye bölmektir: int değer = -8000. bir sağındakinin iki katı olması nedeniyle. bir bit sola kaydırmak 2 ile çarpmak anlamına gelir: Copyright © 2009-2011 Ali Çehreli. uint sorgulananBit = 0x00010000. o kadar sayıda yarıya bölmek anlamına gelir.5.Bit İşlemleri uint ifade = 123456789. göster(sorgulananBit). http://ddili. 1000 Ayrıntısına girmediğim ikiye tümleyen sistemi nedeniyle. 1" : "1 değil"). writeln(değer >> 3).7 Sola kaydırmak iki katını almaktır Basamaklar tablosundaki her bitin. Sağa birden fazla sayıda kaydırmak. 123456789 65536 00000111010110111100110100010101 075bcd15 00000000000000000001000000000000 00001000 1 değil 123456789 4096 Sorgulama ifadesinde birden fazla 1 kullanarak o bitlerin hepsinin birden asıl ifadede 1 olup olmadıkları da sorgulanabilir. göster(ifade). 67. Asıl ifadenin hangi bitinin sorgulandığını mavi ile işaretliyorum: 00000111010110111100110100010101 075bcd15 00000000000000010000000000000000 00010000 evet. Bunu yukarıdaki basamak değerleri tablosunda görebilirsiniz: bir sağdaki bit her zaman için soldakinin yarısı değerdedir. yani sonuçta 8'e bölmek anlamına gelir: uint değer = 8000. 3 kere 2'ye bölmek. değerin yarıya inmesine neden olur.org 417 .

geçerli/geçersiz gibi iki değerli kavramları ifade ederler. Her değer.6. araba yarışıyla ilgili bir oyun programı düşünelim. writeln(değer << 5). Bayraklar özellikle C kütüphanelerinde yaygındır. bazen bir arada kullanılırlar. Bayraklar bir enum türünün birbirleriyle örtüşmeyen tek bitlik değerleri olarak tanımlanırlar. aynı değer içinde yan yana bulunurlar: Copyright © 2009-2011 Ali Çehreli. Bir örnek olarak.1 Oyun sırasında bunlardan hangilerinin etkin olacakları bayrak değerleriyle belirtilebilir: enum Gerçekçilik { benzinBiter hasarOluşur lastiklerEskir lastikİzleriOluşur } = = = = 1 1 1 1 << << << << 0.lastikİzleriOluşur.0010 .org 418 . 2. Her ne kadar böyle tek bitlik bilgilerin yaygın olmadıklarını söylemiş olsam da.. olsun/olmasın.. 1'in farklı sayıda sola ötelenmesi ile elde edilmiştir. bu değerler | ile birleştirilebilir ve hep birden tek bir değişkende bulundurulabilir. Beş kere 2 ile çarpmak.. Bu iki bayrağın bitleri. o enum değerlerinin hepsi de tek bir bitten oluşmakta ve birbirleriyle çakışmamaktadır.. kullanıma göre azalabilsin çarpışmalar hasar bırakabilsin lastikler kullanıma göre eskiyebilsin lastikler yolda iz bırakabilsin 67. Tek bitlik oldukları için var/yok. 32 ile çarpmanın eşdeğeridir: 320 67. 3 Dikkat ederseniz. http://ddili. ayarlar). değer şöyle kurulabilir: Gerçekçilik ayarlar = Gerçekçilik.0100 .. Bit değerlerinin şöyle olduklarını görebiliriz: benzinBiter hasarOluşur lastiklerEskir lastikİzleriOluşur : : : : . Bu programın gerçekçiliği kullanıcı seçimlerine göre belirlensin: • • • • benzin.0001 . C'den uyarlanan D kütüphanelerinde bulunmaları da beklenebilir.6 Bazı kullanımları Bayraklar Bayraklar.lastiklerEskir | Gerçekçilik.1000 Hiçbir bit diğerlerininkiyle çakışmadığı için.. writefln("%b". birbirlerinden bağımsız olarak bir arada tutulan tek bitlik verilerdir. Örneğin yalnızca lastiklerle ilgili ayarların etkin olmaları istendiğinde.Bit İşlemleri uint değer = 10. 1...

bu işlemcinin yetenekleri ile ilgili bilgi verir... ancak belirtilen bayrak ayarlar içinde de 1 ise 1 sonucunu verir. if koşuluyla kullanılabilmesinin bir nedeninin de Tür Dönüşümleri dersinden hatırlayacağınız gibi. cpuid. SSE_BIT = 1<<25.toString()). gibi tek bitlik olarak içerir: import std. } & işlecinin sonucu. Listelenmediği için bilemediğimiz yetenekler bu işlemcide bulunmaz. SSE2_BIT = 1<<26.16GHz Family=6 Model=15 Stepping=6 MMX FXSR SSE SSE2 SSE3 SSSE3 AMD64 HTT 2 threads / 2 cores O çıktıdaki Features satırı. bu bayrakların etkin olup olmadıkları & işleci ile denetlenir: if (ayarlar & Gerçekçilik.stdio.lastiklerEskir) { // . void main() { writeln(std. Phobos'un std.6. O satırdaki her sözcük.. benzinin azalmasıyla ilgili kodlar .cpuid. bu ortamda listelenmemektedir. http://ddili. import std.Bit İşlemleri 1100 Daha sonradan.benzinBiter) { // . lastiklerin eskimesiyle ilgili kodlar .cpuid modülünün kaynak kodlarına bakalım.d dosyasında şöyle tanımlanmıştır: MMX_BIT = 1<<23.2 Bayrak örneği Örnek olarak. mikro işlemci ile ilgili çok sayıda bilgiyi öyledir/değildir.d dosyasının içine bakarak haberdar olduğum IA64 yeteneği. farklı bir değer olduğunda da true değerine dönüşür.. programın asıl işleyişi sırasında.. IA64_BIT = 1<<30 Copyright © 2009-2011 Ali Çehreli. } Bu dersi yazdığım ortamda aldığım çıktı şöyle: Vendor string: Processor string: Signature: Features: Multithreading: GenuineIntel Intel(R) Core(TM)2 CPU T7400 @ 2. FXSR_BIT = 1<<24. Örneğin. Bu modül. dmd/src/phobos/std/cpuid. vs. 67. Mikro işlemcinin yeteneklerini belirleyen bayraklardan bazıları. 1 değerinin otomatik olarak true 'ya dönüşmesidir. ve bayrağın etkin olup olmadığı böylece anlaşılmış olur.. & işlecinin sonucu 0 olduğunda false . } if (ayarlar & Gerçekçilik..cpuid. o yeteneğin bu işlemcide var olduğunu belirtir.. destekler/desteklemez.org 419 . HTT_BIT = 1<<28.

http://ddili. diğer bitlerini sıfırlar: uint değer = 123456789. IP adresleri ağ paketleri içinde 32 bitlik tek bir değer olarak bulunurlar. write("sonuç: "). uint maske = 0xff000000. diğer ifadenin alt 8 bitini olduğu gibi korur.Bit İşlemleri Bu modüldeki bazı işlevler. değerin de üst bitleri seçilmiş olur: Copyright © 2009-2011 Ali Çehreli. ilgilenilen veri ile örtüşen sayıda 1'lerden oluşur. göster(değer & maske). göster(değer).168. Bu 32 bitin 8'er bitlik 4 parçası. İki yöntem de doğrudur ve aynı etkiye sahiptir.3 Maskeleme Bazı kütüphanelerde ve sistemlerde.} İlginç bir gözlem olarak. write("sonuç: ").org 420 . & işlecinin sonucu bool 'a otomatik olarak değil. Bu veriler. açıkça !=0 karşılaştırmasıyla dönüştürülüyor. 67. yani & işleci ile kullanıldığında verinin değerleri elde edilir. Örneğin 192. write("değer: "). maskeleme yöntemiyle birbirlerinden ayrılabilirler. 32 bit olarak 0xc0a80102 değeridir: c0 a8 01 02 == 12 * 16 + == 10 * 16 + == 0 * 16 + == 0 * 16 + 0 8 1 2 = 192 = 168 = 1 = 2 Maske. Örneğin 0x000000ff gibi bir maske değeri. write("maske: "). göster(maske). Bunun bir örneğini IP adreslerinde görebiliriz. Maskenin üst bitleri 1 olduğundan. göster(değer & maske).1. belirli bayrakların etkin olup olmadıklarını & işlecinin sonucuna bakarak bildirir: bool mmx() {return (flags&MMX_BIT)!=0. Asıl değişken bu maske ile "ve"lendiğinde. günlük kullanımdan alışık olduğumuz noktalı adres gösterimi değerleridir.6. write("değer: "). uint maske = 0x000000ff. Diğer bütün bitler sıfırlanmıştır: değer: 00000111010110111100110100010101 075bcd15 maske: 00000000000000000000000011111111 000000ff sonuç: 00000000000000000000000000010101 00000015 123456789 255 21 Bu yöntemi 0xc0a80102 IP adresine ve en üst 8 biti seçecek bir maskeyle uyguladığımızda noktalı gösterimin ilk adres değerini elde ederiz: uint değer = 0xc0a80102.2 gibi bir adres. göster(değer). Maskenin seçerek koruduğu bitleri mavi ile işaretliyorum. Örneğin 32 bitlik bir değerin üst 3 bitinin belirli bir anlamı ve alt 29 bitinin başka bir anlamı bulunabilir. write("maske: "). göster(maske). belirli bir tamsayı değer içine birden fazla bilgi yerleştirilmiş olabilir.

. 1.2"). 5) == 0b_0000_0000_0000_0000_0000_0000_0011_1110)..168. write("maske: "). maskelenen 8 bitin değerin en sağ tarafına kaydırılmalarının da gerekmesidir. bayt1. Verilen 4 değeri 32 bitlik IP adresine dönüştüren bir işlev yazın: uint ipAdresi(ubyte ubyte ubyte ubyte { // . göster((değer & maske) >> 24). write("değer: ").7 Problemler 1. Belirtilen bit ile başlayan ve belirtilen uzunlukta olan maske oluştursun: uint maskeYap(int düşükBit. Bunun nedeni. } 2.1. 3221225472 olmuştur. http://ddili.org 421 . } unittest { assert(maskeYap(1.. göster(maske). } unittest { assert(noktalıOlarak(0xc0a80102) == "192. bayt0) // en yüksek değerli bayt // en düşük değerli bayt unittest { assert(ipAdresi(192.. } bayt3. uint maske = 0xff000000. Maske oluşturan bir işlev yazın.. // ↑ Copyright © 2009-2011 Ali Çehreli. } 3. O 8 biti 24 bit sağa kaydırırsak değerlerini doğru olarak elde ederiz: uint değer = 0xc0a80102.Bit İşlemleri değer: 11000000101010000000000100000010 c0a80102 3232235778 maske: 11111111000000000000000000000000 ff000000 4278190080 sonuç: 11000000000000000000000000000000 c0000000 3221225472 Ancak. değer: 11000000101010000000000100000010 c0a80102 3232235778 maske: 11111111000000000000000000000000 ff000000 4278190080 sonuç: 00000000000000000000000011000000 000000c0 192 67. 168. göster(değer). int uzunluk) { // . sonucun onlu gösterimi beklediğimiz gibi 192 değil. bayt2. 2) == 0xc0a80102). Verilen IP adresini noktalı gösteriminde yazdıran bir işlev yazın: string noktalıOlarak(uint ipAdresi) { // . write("sonuç: ")..

ve 5 bitten oluşuyor Copyright © 2009-2011 Ali Çehreli..org 422 . http://ddili.Bit İşlemleri } . çözümler // // başlangıç biti 1..

auto iki = 'ş'. bu koşullar yalnızca derleme zamanında değerlendirilir. • Şablon özellemeleri. } unittest { auto bir = 'ğ'. özelleme kodu derlenmez ve programa dahil edilmez: void değişTokuş(T)(ref T birinci. Birim testi de yazılmış olsa. Çalışma zamanında etkili olan if . birim testlerinin önemini gösteren bir başka örnek olarak da görebilirsiniz. http://ddili. daha hızlı olacağı düşünüldüğü için bit işleci ^ (ya da işlemi) kullanılarak yazılmış. // DİKKAT: sondaki i harfi unutulmuş! } void main() {} Yukarıdaki işlevin uint özellemesi. ve invariant blokları -release seçeneği kullanılmadığı zaman etkindir Yukarıdakiler. } void değişTokuş(T : uint)(ref T birinci. değişTokuş işlevinin uint türü ile çağrılmamış olması. O programdaki hata. birinci ^= ikinc. assert(bir == 'ş'). Bunu. Derleyici seçeneklerine bağlı olarak kullanılıp kullanılmamaları. işlev yazıldığı sırada ortaya çıkar: Copyright © 2009-2011 Ali Çehreli. ikinci ^= birinci.org 423 . assert(iki == 'ğ'). yalnızca belirtilen türler için etkilidir. programın çalışması sırasında etkileri yoktur. ikinci = geçici. o özellemedeki hata. Derleme ile ilgili oldukları için. O türler programda kullanılmadığında. programın doğruluğunu arttırma amacına yönelik yardımcı olanaklar olarak görülebilir. Sonundaki yazım hatasına rağmen. değişTokuş(bir. birinci = ikinci. ve bu yüzden uint özellemesinin derlenmemiş olmasıdır. ref T ikinci) { birinci ^= ikinci.Koşullu Derleme 68 Koşullu Derleme Programın bazı bölümlerinin belirli koşullara bağlı olarak farklı derlenmesi. veya hiç derlenmemesi istenebilir. iki). for . out . ref T ikinci) { T geçici = birinci. D'nin koşullu derleme olanakları bu konuda kullanılır. yukarıdaki program derlenir ve çalışır. Aslında önceki derslerde koşullu derleme olarak kabul edilebilecek başka olanaklar görmüştük: • Birim testi bloklarındaki kodlar yalnızca -unittest derleyici seçeneği kullanıldığında derlenir ve çalıştırılır • Sözleşmeli programlama olanakları olan in . Bunun nedeni. işlev uint türü ile çağrılana kadar ortaya çıkmaz. while gibi D olanakları koşullu derleme olanakları değildir. programın asıl davranışını zaten değiştirmemelidir.

Bu belirteçle işaretlenmiş olan ifadeler ve bloklar yalnızca derleyicinin -debug seçeneği kullanıldığında etkilidir: debug koşullu_derlenen_bir_ifade.. assert(j == 42).Koşullu Derleme unittest { uint i = 42. } Görüldüğü gibi.1 debug Program geliştirme aşamasında yararlı olan bir olanak debug belirtecidir. Şimdiye kadarki programların hemen hemen hepsinde programın nasıl işlediğini gösteren "ekliyorum". } Yukarıdaki tek ifade de.. } const auto ortaNokta = değerler. http://ddili. in int değer) { if (değerler. } else { Copyright © 2009-2011 Ali Çehreli. O algoritmanın açıklama satırlarını çıkartıyorum ve bilerek hatalı olarak yazıyorum: import std.. debug { // .length == 0) { return -1. blok içindeki ifadeler de ancak -debug derleyici seçeneği etkin olduğunda derlenir. "debug". assert(i == 7). D'nin koşullu derlemeyi destekleyen ve bütünüyle bu amaç için tasarlanmış üç olanağı daha vardır: • debug • version • static if 68.. gibi satırlar kullandık.stdio. değer).length / 2. ortaNokta]. koşullu derlenen ifadeler . j). if (değer == değerler[ortaNokta]) { return ortaNokta.. uint j = 7.org 424 . Böylece algoritmaların işleyişlerini görsel bir hale getirerek olası hatalarını bulabiliyorduk. değişTokuş(i. hata gidermek anlamına gelir ve bu konuda yararlıdır. "çıkartıyorum". Bunun bir örneği olarak Şablonlar dersinde gördüğümüz ikiliAra işlevine bakalım. // DİKKAT! Bu algoritma hatalıdır int ikiliAra(const int[] değerler. } else if (değer < değerler[ortaNokta]) { return ikiliAra(değerler[0 . şablon özellemelerinin de koşullu olarak derlendiklerini kabul edebiliriz.

1000 ].1000] içinde 42 arıyorum bakılan konum: 4 son yarıda olması gerek [10. indeks). } } Programın şimdiki çıktısı.length / 2. $]. değer). 42. değer).1. 365. in int değer) { writeln(değerler.365. " bulunamadı").365. $]. ortaNokta]. if (değerler. void main() { int[] sayılar = [ -100. 2.7.1000] içinde 42 arıyorum bakılan konum: 2 ilk yarıda olması gerek [10. değer.42] içinde 42 arıyorum bakılan konum: 1 42. " konumunda bulundu").. Buna Copyright © 2009-2011 Ali Çehreli. ortaNokta).Koşullu Derleme } } return ikiliAra(değerler[ortaNokta + 1 .2.. return ikiliAra(değerler[ortaNokta + 1 .42.42. 1. işlevin önemli noktalarına işlemler hakkında bilgiler veren satırlar eklemektir: int ikiliAra(const int[] değerler.10. writeln("Konum: ". 1 konumunda bulundu Konum: 1 Hatanın bu çıktıdan yararlanılarak bulunduğunu ve giderildiğini varsayalım. writeln("bakılan konum: ".org 425 . 0. int indeks = ikiliAra(sayılar. 7. 42). " içinde ". algoritmanın işleyiş adımlarını gösterir: [-100.length == 0) { writeln(değer.. ortaNokta. } const auto ortaNokta = değerler. return ikiliAra(değerler[0 . return -1. " arıyorum"). return ortaNokta. } Yukarıdaki programı çalıştırdığımızda.0. http://ddili. ". Hata giderildikten sonra artık writefln satırlarına gerek kalmaz ve hatta silinmeleri gerekir. } else { writeln("son yarıda olması gerek"). 42'nin 6 olarak bildirilmesi gereken konumunun yanlış bildirildiğini görürüz: Konum: 1 Bu hatayı bulmanın bir yolu. } else if (değer < değerler[ortaNokta]) { writeln("ilk yarıda olması gerek"). 10. if (değer == değerler[ortaNokta]) { writeln(değer. ". değer).

int[] sayılar) { debug(1) writeln("birİşlev işlevine girildi"). çünkü bugün silinirlerse...d -ofdeneme -w -debug=ikili_arama -debug=yigin_yapisi O durumda hem ikili_arama . 68. dosyaİsmi). Böyle durumlarda debug belirteçlerine isimler verebilir ve onların yalnızca komut satırında belirtilenlerinin etkinleşmelerini sağlayabiliriz: debug(ikili_arama) writeln(değer. İsimli debug belirteçlerini etkinleştirmek için komut satırında -debug=isim yazılır: dmd deneme. } Aynı anda birden çok isimli debug belirteci de kullanılabilir: $ dmd deneme. o satırları silmek de bir israf olarak görülebilir.stdio. hata ayıklama düzeylerini belirleyen sayısal değerler verilebilir. foreach (i.. belki de ileride tekrar gerekebilirler. Onun yerine. daha derinlemesine bilgi verir: debug import std. } } Copyright © 2009-2011 Ali Çehreli.Koşullu Derleme rağmen. " bulunamadı"). sayı). http://ddili.d -ofdeneme -w -debug=ikili_arama İsimli debug belirteçleri de birden fazla ifade için kullanılabilir: debug(ikili_arama) { // .1 debug(isim) debug belirtecinin çok yerde kullanılması durumunda programın çıktısı çok kalabalıklaşabilir.. Artan her düzey. 68.d -ofdeneme -w -debug Böylece. " bulunamadı").org 426 . hem de yigin_yapisi isimli debug blokları etkin olur. i. O satırlar artık yalnızca -debug derleyici seçeneği kullanıldığında etkin olacaktır: dmd deneme. void birİşlev(string dosyaİsmi. bu satırların başına debug anahtar sözcüğü yazılabilir: debug writeln(değer.2 debug(düzey) Bazen debug belirteçlerine isimler vermek yerine. sayı. sayılar) { writefln(" %4s: %s". koşullu derlenen ifadeler . programın normal işleyişi sırasında çıktıya hiçbir bilgi yazdırılmayacak.1. bir hata görüldüğünde ise -debug kullanılarak algoritmanın işleyişi hakkında bilgi alınabilecektir. debug(2) { writeln("işlev parametreleri: ").1. writeln(" isim: ".

..2 version(isim) .. o düzey ve daha düşük olanlarının etkinleşmesini sağlar: $ dmd deneme...org 427 .. aynı anda birden fazla version bloğu etkinleştirilebilir: $ dmd deneme. program için farklı sürümler oluşturma amacıyla kullanılmasıdır./deneme birİşlev işlevine girildi işlev parametreleri: isim: deneme. ve version(düzey) version .... Yine debug 'da olduğu gibi.. bir ifade . version(2) { // ...txt 0: 10 1: 4 2: 100 68. version (okulSürümü) { // . debug olanağına çok benzer ve kod içinde aynı şekilde kullanılır: version (denemeSürümü) /* . } Bütünüyle aynı şekilde çalışıyor olsa da. sürüm 2 ile ilgili bir olanak .. } version(1) birDeğişken = 5. Derleyiciye belirtilen debug düzeyi. programlarımızda yararlanabilmemiz için hazır olarak tanımlıdır: Copyright © 2009-2011 Ali Çehreli. */.d -ofdeneme -w -version=kayit -version=hassas_hesap Bazı version isimleri.Koşullu Derleme } // . http://ddili.d -ofdeneme -w -debug=2 $ . okullara satılan sürümle ilgili ifadeler .d -ofdeneme -w -debug=1 $ ./deneme birİşlev işlevine girildi Daha derinlemesine bilgi almak istendiğinde: $ dmd deneme. debug 'dan farkı. asıl işlemler ....

Copyright © 2009-2011 Ali Çehreli. -cov derleyici seçeneği Ddoc belgeleri üretiliyor (-D derleyici seçeneği) X86 inline assembler'ı mevcut Göstergeler 64 bitlik (-m64 derleyici seçeneği) Position Independent Code üretiliyor (-fPIC derleyici seçeneği) Birim testleri etkin (-unittest derleyici seçeneği) Bir D2 derleyicisi ile derlenmekte Hiçbir zaman tanımlı değildir.org 428 . debug(hepsi) { debug = 1. import std. Satır sonu anlamına gelen kodları belirleyen newline dizisi. 68. yüksek değerlinin önce olduğu durum İşletilen bütün kod satırlarını belirleyecek şekilde derleniyor (Code coverage analysis. debug = 2.string modülünde tanımlı olan newline 'a bakalım: version (Windows) immutable char[2] newline = "\r\n". version = okulSürümü. } void main() { debug(1) writeln("debug düzeyi 1"). http://ddili.stdio. Digital Mars'ınki Intel veya AMD 32 bit işlemcisi AMD veya Intel 64 bit işlemcisi Microsoft Windows sistemi Microsoft 32 bit Windows sistemi Microsoft 64 bit Windows sistemi Bütün Linux sistemleri Bütün Linux sistemleri Bayt sırası. değer olarak belirtilen debug veya version isminin de etkinleşmesini sağlar. version = denemeSürümü. kod bloğunu etkisizleştirir Her zaman için tanımlıdır. Değişkenlerden farklı olarak.Koşullu Derleme Öntanımlı version belirteçleri DigitalMars X86 X86_64 Windows Win32 Win64 linux Posix LittleEndian BigEndian D_Coverage D_Ddoc D_InlineAsm_X86 D_LP64 D_PIC unittest D_Version2 none all Derleyici. else version (Posix) immutable char[1] newline = "\n".3 debug 'a ve version 'a isim atamak debug ve version 'a sanki bir değişkenmiş gibi isim atanabilir. Kullanım örneği olarak std. üzerinde derlenmekte olduğu işletim sistemine göre farklı olarak tanımlanmaktadır. düşük değerlinin önce olduğu durum Bayt sırası. none'ın tersidir D_InlineAsm_X86_64 X86-64 inline assembler'ı mevcut O tanımlardan yararlanarak programınızın farklı olanaklarla derlenmesini sağlayabilirsiniz. atama işlemi değer değiştirmez.

/deneme debug düzeyi 1 debug düzeyi 2 deneme sürümü okul sürümü 68.Koşullu Derleme debug(2) writeln("debug düzeyi 2"). ve okulSürümü sanki komut satırında bildirilmişler gibi etkinleşir: $ dmd deneme. her satırda belirtilen ismin de etkinleşmesini sağlar. false olduğunda ise dahil edilmez. blok içindeki kodlar yazıldıkları satırda programa dahil edilirler. } else static if (char. void main() { Copyright © 2009-2011 Ali Çehreli. Böylece bu program için derleme satırında dört debug ve version seçeneği farklı olarak seçilebileceği gibi. 2. vs. sınıf. else static if ve else blokları da bulunabilir. yapı. 1. http://ddili. static if koşulu da bir mantıksal ifade ile kullanılır.4 static if Programın çalışması sırasındaki kararlarda çok kullandığımız if koşulunun derleme zamanındaki eşdeğeri static if 'tir. -debug=hepsi kullanıldığında. Yine if 'e benzer şekilde. Koşul sağlandığında.sizeof == 2) { writeln("Selam dünya").stdio.sizeof == 1) { writeln("Merhaba dünya").d -ofdeneme -w -debug=hepsi $ . } Yukarıdaki koddaki debug(hepsi) bloğu içindeki atamalar. Bir örnek olarak main içinde kullanalım: import std. void main() { static if (char. version(denemeSürümü) writeln("deneme sürümü"). şablon. denemeSürümü. static if her kapsamda kullanılabilir: modül dosyasında en üst düzeyde.org 429 . version(okulSürümü) writeln("okul sürümü"). işlev.. static if bloğundaki kodlar bu mantıksal ifade true olduğunda derlenir ve programa dahil edilir. if 'ten farklı olarak. kapsamlarında. if koşulunda olduğu gibi. } else { writeln("Zaten beraberiz").. mantıksal ifadenin sonucunun derleme zamanında bilinebiliyor olması şarttır.stdio. o program yalnızca "Merhaba dünya"lı satır yazılmış gibi derlenir: import std. } } char tek baytlık bir tür olduğu için.

. koşul geçerli olduğunda 1. Çalışma zamanında kullanmaya alıştığımız assert 'le aynı şekilde. 68. Örneğin if deyimiyle. Copyright © 2009-2011 Ali Çehreli. */) // daha önce gördüğümüz is işleci // is ifadesi Bu bölümün konusu olan is ifadesi derleme zamanında işletilir. http://ddili. static if 'e benzerliği nedeniyle burada anlatmaya karar verdim. belirli bir algoritmanın yalnızca belirli büyüklükteki türlerle doğru olarak çalışabildiğini varsayalım. O işlev şablonu örneğin char ile çağrıldığında.org 430 . ama is 'in kendisinin değeri bir mantıksal ifadede kullanılmaya elverişlidir. static if gibi. Örnek olarak. Bunu bir static assert ile şöyle denetleyebiliriz: void birAlgoritma(T)(T değer) { // Bu algoritma.5 static assert Aslında bir koşullu derleme olanağı olarak kabul edilmese de. programın derlenmesi bir hata ile sonlanır: Error: static assert (1u == 0u) is false Böylece algoritmanın uygunsuz bir türle kullanılmasının ve olasılıkla hatalı çalışmasının önüne geçilmektedir. static if . Yanlışlıkla else if yazılmışsa. daha önce null değeri ve is işleci dersinde gördüğümüz is işlecinden anlam ve yazım açısından farklıdır: a is b is (/* . } // .. büyüklüğü dördün katı olan türlerle // çalışabilir static assert((T. Mantıksal ifadesi false olduğunda derlemenin bir hata ile sonlandırılmasını sağlar.Koşullu Derleme } writeln("Merhaba dünya"). static if zincirleri oluştururken else static if yazmak gerektiğine dikkat edin. static if 'in else bloğu olarak if kullanılacak demektir ve if de doğal olarak çalışma zamanında işletilecektir. is 'in aldığı koşul bir mantıksal ifade değildir. Ürettiği değerin türü int 'tir. 68.sizeof % 4) == 0). Çalışma zamanında hiçbir karşılaştırma yapılmaz.6 is ifadesi Bu ifade. static assert de aşağıdaki is ifadesinden yararlanabilir. ve parantez içindeki koşula bağlı olarak bir değer üretir.. geçersiz olduğunda 0 değerini alır. static assert de programda herhangi bir kapsamda bulunabilir. çoğunlukla biraz aşağıda göreceğimiz is ifadesi ile kullanılır. ama derleme zamanında işletilir.. ve derleme zamanında işletildiği için daha da uygun olarak static if deyimiyle kullanılabilir.

} geçersiz 68. interface Saat { void zamanıOku().stdio.Koşullu Derleme Aldığı koşul türlerle ilgilidir ve bir kaç özel biçimden birisi olarak yazılmak zorundadır. } Yukarıdaki koşulda kullanılan int . En çok şablon parametrelerini denetlemede ve şablon parametre türleriyle ilgili bilgi toplamada yararlıdır.org 431 . http://ddili. Yeniİsim değişken = 42. Ek olarak. is 'in bu kullanımı için tek başına örnekler bulmak oldukça zor.6. 68. Bunun şablon parametrelerinde yararlı olacağını düşünebilirsiniz: static if (is (int)) { writeln("geçerli"). Tür Dönüşümleri dersinde gördüğümüz temel tür dönüşümlerini.3 is (Tür : ÖzelTür) Tür'ün belirtilen özel türe otomatik olarak dönüşüp dönüşemediğini denetler.1 is (Tür) Tür'ün anlamsal olarak geçerli bir tür olup olmadığını denetler. koşul geçerli olduğunda Takmaİsim'i türün yeni takma ismi olarak tanımlar: static if (is (int Yeniİsim)) { writeln("geçerli"). geçerli bir türdür: geçerli Başka bir örnek olarak. veya Türeme dersinde gördüğümüz "bu alt sınıf. import std. 68. eşleme tablosu indeks türü olarak void kullanmak geçersiz olduğu için.6. } else { writeln("geçersiz").6.2 is (Tür Takmaİsim) Yukarıdaki ile aynı şekilde çalışır. } Takma ismin bu şekilde is ifadesinin içinde tanımlanabilmesi. daha sonra göreceğimiz karmaşık is ifadelerinde yararlıdır. o üst sınıfın türündendir" ilişkilerini denetlemede kullanılır. // int ve Yeniİsim aynı şey } else { writeln("geçersiz"). } else { writeln("geçersiz"). Copyright © 2009-2011 Ali Çehreli. bu örnekte else bloğu işletilir: static if (is (string[void])) { writeln("geçerli").

68. Tür int olduğunda ise else bloğu işletilmektedir: bu bir Saat. bu sefer ÇalarSaat için de geçersiz olacaktır: static if (is (T == Saat)) { writeln("bu bir Saat.org 432 . Ek olarak. } } void main() { auto değişken = new ÇalarSaat. } ÇalarSaat Saat 'ten türediği için bir Saat 'tir. O yüzden koşul hem ÇalarSaat için. zamanı söyleyebiliriz"). } else { writeln("bu bir Saat değil").4 is (Tür Takmaİsim : ÖzelTür) Yukarıdakiyle aynı şekilde çalışır.5 is (Tür == ÖzelTür) Tür'ün belirtilen özel türün aynısı olup olmadığını.zamanıOku(). } } void birİşlev(T)(T nesne) { static if (is (T : Saat)) { // Eğer buraya geldiysek. ama Saat 'in aynısı değildir. veya aynı belirtece sahip olup olmadığını denetler. nesne 'nin zamanıOku işlevini de çağırmaktadır. zamanı söyleyebiliriz"). birİşlev(değişken). birİşlev şablonu Saat 'e dönüşebilen bir tür ile çağrıldığında.6.zamanıOku(). Aynı tür anlamında: Yukarıdaki örnek kodu değiştirsek ve : yerine == kullansak.6. nesne. şablon parametresi olan T. zamanı söyleyebiliriz 10:00 bu bir Saat değil ← ÇalarSaat için ← ÇalarSaat için ← int için 68. http://ddili.Koşullu Derleme } class ÇalarSaat : Saat { override void zamanıOku() { writeln("10:00"). } else { writeln("bu bir Saat değil"). // Saat'ten türemiş bir türdür writeln("bu bir Saat. } O kod. birİşlev(42). nesne. koşul geçerli olduğunda Takmaİsim'i koşulu sağlayan türün yeni takma ismi olarak tanımlar. hem de int için geçersizdir: Copyright © 2009-2011 Ali Çehreli.

Koşulun değişik bloklarının etkinleştiğini göstermek için şöyle deneyebiliriz: auto değişken = new ÇalarSaat. o işlev şablonu. koşul geçerli olduğunda isim'i duruma göre farklı anlamlarda tanımlar.6 is (Tür isim == Belirteç) Yukarıdaki ile aynı şekilde çalışır. çağrıldığı türe göre değişik davranacak şekilde kodlanabilir. } else static if (is (T == const)) { writeln("bu 'const' bir tür"). isim.2. Bu kullanımda. } } Böylece. } else { writeln("bu başka bir tür"). birİşlev(HaftaGünleri. bu bu bu bu bir sınıf türü bir enum 'const' bir tür başka bir tür 68. belirteç olarak şu anahtar sözcükler kullanılabilir (bu anahtar sözcüklerden bazılarını daha sonraki derslerde anlatacağım): • • • • • • • • • • struct union class interface enum function delegate const immutable shared void birİşlev(T)(T nesne) { static if (is (T == class)) { writeln("bu bir sınıf türü").org 433 . const double sayı = 1. birİşlev(değişken).Koşullu Derleme bu bir Saat değil bu bir Saat değil Aynı belirtece sahip anlamında: ÖzelTür yerine bir belirteç kullanıldığında. türün o belirtece uyup uymadığını denetler.6. http://ddili.Pazartesi). birİşlev(42). yukarıdaki takma isimli kullanımlardaki gibi doğrudan türün takma ismi olabileceği gibi. birİşlev(sayı). belirtece bağlı olarak başka bir bilgi de olabilir: Copyright © 2009-2011 Ali Çehreli. Ek olarak. } else static if (is (T == enum)) { writeln("bu bir enum").

ve isim'in ne anlamlara geldiklerini açıklama satırları olarak yazdım: void birİşlev(T)(T nesne) { static if (is (T YerelTür == struct)) { writefln("\n--. nesnelerin. İşlevin çağrıldığı türlerin. } enum HaftaGünleri { Pazartesi.. Salı. } class ÇalarSaat : Saat { // .. } interface Saat { // .. Çarşamba.. } static if (is (T üstTürler == super)) { writeln("\n--. // T ve YerelTür aynı anlamdadır. 'nesne'. delegate 'in.org 434 .stringof. Cumartesi. http://ddili. Pazar } char foo(double kesirli. } is ifadesinin bu değişik türlerle kullanımlarını göstermek için aşağıdaki gibi bir işlev şablonu yazılabilir. // 'nesne'. Perşembe. veya işlev göstergesinin dönüş türü koşulu sağlayan tür koşulu sağlayan tür koşulu sağlayan tür Bu olanağın nasıl çalıştığını göstermek için önce bazı türler tanımlayalım: struct Nokta { // .Koşullu Derleme Belirteç struct union class interface super enum function delegate return const immutable shared koşulu sağlayan tür koşulu sağlayan tür koşulu sağlayan tür koşulu sağlayan tür isim'in tanımı üst tür ve arayüzlerden oluşan çokuzlu enum 'un gerçekleştirildiği temel tür işlev parametrelerinden oluşan çokuzlu delegate 'in türü işlevin. Cuma. // 'üstTürler' çokuzlusu bütün üst türleri içerir. Saat saat) { return 'a'..super ---").struct ---"). bu işleve // gelen yapı nesnesidir writeln("Yeni bir ". bu işleve gelen sınıf nesnesidir Copyright © 2009-2011 Ali Çehreli. " nesnesini kopyalayarak oluşturuyorum"). int tamsayı. YerelTür. YerelTür yeniNesne = nesne..

// İşlev göstergesiyle birİşlev(&foo). bu işleve // parametre olarak gelen 'nesne'. " enum türü.. AsılTür. "). dönüş türü ".org 435 .stringof).Pazartesi). // Sınıf nesnesiyle birİşlev(new ÇalarSaat). 'nesne'. // 'AsılTür'. bu işleve gelen // enum değeridir writeln(T.enum ---"). Çıktısı: Copyright © 2009-2011 Ali Çehreli. üstTürler[0]. DönüşTürü. işlevin dönüş türüdür. " sınıfının ". " olarak gerçekleştirilmiştir"). } } O işlevi yukarıdaki farklı türlerle şöyle çağırabiliriz: // Yapı nesnesiyle birİşlev(Nokta()). " olan bir işlev:"). writeln("en üstteki: ". " adet üst türü var"). new ÇalarSaat).stringof. 42.length. enum değerlerini gerçekleştirmek için // kullanılan asıl türdür. } static if (is (T AsılTür == enum)) { writeln("\n--.Koşullu Derleme writeln(T.stringof. perde arkasında ". writeln(" ".stringof. writeln("ve sonuç: ". bir işlev // göstergesidir writeln("Bu. // 'DönüşTürü'. // Not: İşlev göstergeleri işlev gibi çağrılabilirler DönüşTürü sonuç = nesne(1. write("çağırıyoruz.5. sonuç).stringof).stringof. T. } static if (is (T DönüşTürü == return)) { writeln("\n--.return ---"). üstTürler.. üstTürler. http://ddili. writeln("hepsi birden: ". // enum değerle birİşlev(HaftaGünleri.stringof).

stringof). İndeks. T. Değer. çok daha karmaşık ifadeler yazmaya olanak verir. eşleme tablosunun indeks türünün de özellikle string olması gerekmektedir. void birİşlev(T)(T nesne) { writeln("\n--. o türe dönüşebilme ve aynı türden olma koşulunu denetlerler.Koşullu Derleme --. ŞablonParametreListesi. Dolayısıyla. O koşulu kullanan ve dört farklı türle çağrılan bir program: import std..7 is (Tür isim : Belirteç. Örnek olarak.super --ÇalarSaat sınıfının 2 adet üst türü var hepsi birden: (in Object. ŞablonParametreListesi) Bu iki kullanım.return --Bu. hem koşulun parçası olarak çalışır. koşul sağlanmadı"). "T .enum --HaftaGünleri enum türü.". Yalnızca böyle türlere uymaya çalışan bir is ifadesi şöyle yazılabilir: static if (is (T Değer : Değer[İndeks]. ve sonuç: a 68. koşul sağlandı"). İndeks : string)) { writeln("Evet. Ek olarak. perde arkasında int olarak gerçekleştirilmiştir --.org 436 . Saat saat) çağırıyoruz. } else { writeln("Hayır. şablonlardaki tür çıkarsaması ile aynı şekilde çalışır.6. Bu. in Saat) en üstteki: Object --.stdio. static if (is (T Değer : Değer[İndeks]. dönüş türü char olan bir işlev: char function(double kesirli. şablon parametresi olan T 'nin bir eşleme tablosu türü olmasını gerektirir. writeln("indeks türü: ". indeks türü string olan bir eşleme tablosu ise" anlamına gelmektedir. http://ddili. writeln("değer türü : ". yukarıdaki is ifadesi.struct --Yeni bir Nokta nesnesini kopyalayarak oluşturuyorum --. " ile çağrıldık ---").stringof. int tamsayı. İndeks : string)) { Belirteç olarak Değer[İndeks] kullanılmış olması. indeks değeri string olan eşleme tabloları kullanıldığında bazı özel işlemler yapmak isteyelim.. isim'den sonra kullanılan : ve == işleçleri. yukarıda gösterilen kullanımlarıyla aynı anlamdadır. hem de bütün koşul sağlandığında otomatik olarak uygun tür isimleri tanımlar. ŞablonParametreListesi) is (Tür isim == Belirteç. Sırasıyla. } Copyright © 2009-2011 Ali Çehreli.stringof).

Koşullu Derleme } void main() { int sayı. birİşlev(sayı).double) ile çağrıldık --Evet. } Koşul.int) ile çağrıldık --Evet. http://ddili. birİşlev(dcharTablosu). koşul sağlanmadı --. koşul sağlandı değer türü : double indeks türü: string --.AssociativeArray!(string. birİşlev(doubleTablosu). double[string] doubleTablosu.dchar) ile çağrıldık --Hayır. koşul sağlanmadı Copyright © 2009-2011 Ali Çehreli. int[string] intTablosu. yalnızca indeks türü string olan eşleme tabloları için sağlanmaktadır: --.AssociativeArray!(string. dchar[long] dcharTablosu.org 437 .AssociativeArray!(long. birİşlev(intTablosu). koşul sağlandı değer türü : int indeks türü: string --.int ile çağrıldık --Hayır.

1 İşlev göstergeleri Bundan önceki derste is ifadesini denerken & işleci ile işlevlerin adreslerinin de alınabildiğini görmüştük. double d) değeri: 80495B4 69. double d) { return 42. Şablonların çeşitli türlerle çağrılabilmelerinden ve türlerin .1 Tanımlanması İşlev göstergeleri function anahtar sözcüğü ile tanımlanır. işlevlerin adreslerinin saklanabilmelerini ve daha sonraki bir zamanda bu göstergeler yoluyla çağrılabilmelerini sağlar.stdio. yukarıdakı işlev isimli işlevi gösteren bir değişkeni şöyle tanımlayabiliriz: int function(char. hem işlev göstergelerini. hem de o işlevlerin kullandıkları kapsamları bir arada saklayan olanaklardır.1. 69. bir işlev şablonuna parametre olarak göndermiştik. İşlev göstergeleri D'ye C'den geçmiştir. double) gösterge = &işlev. Bir örnek olarak. } void main() { şablon(&işlev). Bu tanımda parametrelere isim verilmesi gerekmez. yukarıdaki çıktıda gördüğümüz parametre isimleri olan c 'nin ve d 'nin yazılmaları isteğe bağlıdır. double) Hesapİşlevi. Kapamalar. sonra da işlevin aldığı parametreler yazılır: dönüş_türü function(aldığı_parametreler) gösterge.stringof). İşlev göstergelerinin yazımı oldukça karmaşık olduğundan. parametre). T. O adresi.İşlev Göstergeleri ve Kapamalar 69 İşlev Göstergeleri ve Kapamalar İşlev göstergeleri. } O program çalıştırıldığında. Bu sözcükten önce işlevin dönüş türü. işlev isimli işlevin adresinin türü konusunda bir fikir sahibi olabiliyoruz: türü : int function(char c.org 438 . int işlev(char c. http://ddili. } // adresinin alınması ve // parametre olarak gönderilmesi void şablon(T)(T parametre) { writeln("türü : ". o türe alias ile yeni bir isim vermek kodun okunaklılığını arttırır: alias int function(char. Copyright © 2009-2011 Ali Çehreli.stringof niteliğinden yararlanarak. writeln("değeri: ". işlev göstergelerinin türleri hakkında bilgi edinebiliriz: import std.

bu yapının bir enum değeri ile belirlenebilir: final switch (çalışan. kütüphane içindeki ilgili switch deyimlerinin hepsinin yeni türü de içerecek şekilde değiştirilmeleri gerekir. break. sanki kendisi bir işlevmiş gibi isminden sonraki parametre listesiyle çağrılır ve dönüş değeri kullanılabilir: int sonuç = gösterge('a'. } class MaaşlıÇalışan : Çalışan { double ücretHesabı() { double sonuç.org 439 . http://ddili. 69. } } class SaatliÇalışan : Çalışan { double ücretHesabı() { double sonuç. değerlerin saklanmalarına benzer şekilde. // . assert(sonuç == 42). işlemlerin de saklanabilmelerini sağlar. case ÇalışanTürü. Çalışan diye bir arayüz tanımlanabilir ve ücret hesabı ondan türeyen alt sınıflara yaptırılabilir: interface Çalışan { double ücretHesabı().. Aslında davranış farklılıklarının D'nin başka olanakları ile de sağlanabildiğini biliyorsunuz. o kod bir kütüphane içinde bulunduğu zaman ortaya çıkar: bütün enum değerlerinin ve onlara karşılık gelen bütün işlevlerin kütüphane kodu yazıldığı sırada biliniyor olması gerekmektedir. } O yöntemin bir yetersizliği. 5.3 Ne zaman kullanmalı İşlev göstergeleri. return sonuç.. Saklanan göstergeler programda daha sonradan işlev gibi kullanılabilirler. Farklı bir ücret hesabı gerektiğinde.saatli: saatliÜcretHesabı().1.maaşlı: maaşlıÜcretHesabı(). Örneğin Çalışan gibi bir yapının ücretinin hesaplanması sırasında hangi işlevin çağrılacağı..tür) { case ÇalışanTürü.67). 69. Hesapİşlevi gösterge = &işlev. return sonuç. Davranış farkı konusunda başka bir yöntem. nesne yönelimli programlama olanaklarından yararlanmak olabilir. break.2 Çağrılması İşlev göstergesi olarak tanımlanan değişken.İşlev Göstergeleri ve Kapamalar Artık function 'lı uzun yazım yerine kısaca Hesapİşlevi yazmak yeterlidir.1.. Copyright © 2009-2011 Ali Çehreli. // .

. 10) . nesne yönelimli programlama dillerine uygun olan bir yöntemdir. Bu.stdio. sonuç ~= yeniDeğer.. Dikkat ederseniz.4 Parametre örneği Kendisine verilen bir dizi sayı ile işlem yapan bir işlev tasarlayalım. foreach (sayı. sonuç yalnızca sıfırdan büyük olanların on katlarını içermektedir: giriş: -2 0 3 2 4 -3 2 -4 4 2 2 4 2 1 -2 -1 0 2 -2 4 sonuç: 30 20 40 20 40 20 20 40 20 10 20 40 süz_ve_dönüştür işlevinin bu haliyle fazla kullanışlı olduğunu düşünemeyiz.ücretHesabı(). sayı dönüştürme işlemi de int 'ten yine int 'e bir dönüşümdür: Copyright © 2009-2011 Ali Çehreli. Oysa süzme ve dönüştürme işlemlerini dışarıdan alabilse çok daha kullanışlı olur.random. süzme işlemi int 'ten bool 'a bir dönüşüm. davranış farklılığı konusunda kullanılan başkaca bir yöntemdir. sayılar). çünkü her zaman için sıfırdan büyük değerlerin on katlarını üretmektedir. // ve dönüştürme O işlevi şöyle bir programla deneyebiliriz: import std. İşlev göstergeleri. Bu. MaaşlıÇalışan çalışan.1. http://ddili. süz_ve_dönüştür(sayılar)). Bu işlev.İşlev Göstergeleri ve Kapamalar } } // . import std.org 440 . double ücret = çalışan.. } Çıktısından görüldüğü gibi. } } } return sonuç. } writeln("giriş: ". 0 . özellikle nesne yönelimli olanakları bulunmayan C dilinde yazılmış olan kütüphaneler kullanırken gerekebilir. 20) { sayılar ~= uniform(0. writeln("sonuç: ". // süzme. sayılar) { if (sayı > 0) { int yeniDeğer = sayı * 10.5. // Rasgele 20 sayı foreach (i. void main() { int[] sayılar. sayıların yalnızca sıfırdan büyük olanlarının on katlarını içeren bir dizi döndürsün: int[] süz_ve_dönüştür(const int[] sayılar) { int[] sonuç. 69.

"int alan ve bool döndüren" işlev göstergesi. } } } return sonuç. bu dönüşümleri sağlayacak olan işlev gösterge türlerini şöyle tanımlayabiliriz: alias bool function(int) Süzmeİşlemi. Süzmeİşlemi . Örneğin "çift olanlarının ters işaretlilerini" elde etmek için: bool çift_mi(int sayı) { return (sayı % 2) == 0. Bu türlerden olan işlev göstergelerini süz_ve_dönüştür işlevine dışarıdan parametre olarak verirsek.İşlev Göstergeleri ve Kapamalar sayı > 0 : int olan sayıya bakarak bool sonuç elde ediliyor sayı * 10 : int olan sayı kullanılarak yine int üretiliyor Bu işlemleri işlev göstergeleri yoluyla yapmaya geçmeden önce. süz_ve_dönüştür işlevinin artık bambaşka süzücü ve dönüştürücü işlevleriyle de serbestçe çağrılacak hale gelmiş olmasıdır.. Bunun yararı. süzme ve dönüştürme işlemlerini o işlev göstergelerine yaptırabiliriz.org 441 . o işlemleri. Böylece işlev daha kullanışlı hale gelmiş olur: int[] süz_ve_dönüştür(const int[] sayılar. &sıfırdanBüyük_mü. Süzmeİşlemi süzücü. } int onKatı(int sayı) { return sayı * 10. http://ddili. sonuç ~= yeniDeğer. } Copyright © 2009-2011 Ali Çehreli.. foreach (sayı. Dönüşümİşlemi de "int alan ve int döndüren" işlev göstergesi anlamındadır. kendisine verilen işlev göstergelerine yaptırmaktadır. Bu işlev artık asıl süzme ve dönüştürme işlemlerinden bağımsız bir hale gelmiş olur. } // . alias int function(int) Dönüşümİşlemi. süz_ve_dönüştür(sayılar. Yukarıdaki gibi "sıfırdan büyük olanlarının on katlarını" üretebilmesi için şöyle iki küçük işlev tanımlayabiliriz ve süz_ve_dönüştür işlevini onların adresleri ile çağırabiliriz: bool sıfırdanBüyük_mü(int sayı) { return sayı > 0. &onKatı)). sayılar) { if (süzücü(sayı)) { int yeniDeğer = dönüştürücü(sayı). writeln("sonuç: ". Dönüşümİşlemi dönüştürücü) { int[] sonuç.

süzme ve dönüştürme işlemlerini kurucu parametreleri olarak alan bir sınıf da yazabiliriz: class SüzücüDönüştürücü { Süzmeİşlemi süzücü. sonuç ~= yeniDeğer.2 İsimsiz işlevler Yukarıdaki örnek programlarda süz_ve_dönüştür işlevinin esnekliğinden yararlanmak için küçük işlevler tanımlandığını ve süz_ve_dönüştür çağrılırken bu küçük işlevlerin adreslerinin gönderildiğini gördük. http://ddili. Copyright © 2009-2011 Ali Çehreli. &çift_mi. 69. } } } return sonuç. &tersİşaretlisi). writeln("sonuç: ".org 442 .5 Üye örneği İşlev göstergeleri değişken olarak kullanılabildikleri için yapı ve sınıf üyeleri de olabilirler. Yukarıdaki süz_ve_dönüştür işlevi yerine. Dönüşümİşlemi dönüştürücü.dönüştürücü = dönüştürücü. this (Süzmeİşlemi süzücü. &tersİşaretlisi)). Çıktısı: giriş: 2 -3 -3 -2 4 4 3 1 4 3 -4 -1 -2 1 1 -5 0 2 -3 2 sonuç: -2 2 -4 -4 -4 4 2 0 -2 -2 69.. işlemci.işlemYap(sayılar)). writeln("sonuç: ". sayılar) { if (süzücü(sayı)) { int yeniDeğer = dönüştürücü(sayı). } // . this. } int[] işlemYap(const int[] sayılar) { int[] sonuç. Dönüşümİşlemi dönüştürücü) { this. süz_ve_dönüştür(sayılar.İşlev Göstergeleri ve Kapamalar int tersİşaretlisi(int sayı) { return -sayı.. foreach (sayı.süzücü = süzücü.1. } Daha sonra o türden bir nesne oluşturabiliriz ve yukarıdaki sonuçları elde etmek için şu şekilde kullanabiliriz: auto işlemci = new SüzücüDönüştürücü(&çift_mi.

// uzun olarak birİşlev(function () { return 42. işlev göstergesi kullanılabilen her yerde şu söz dizimiyle tanımlanabilirler: function dönüş_türü(parametreleri) { /* işlemleri */ }. */) { // .42. yukarıdaki örnekte tanımladığımız sınıftan olan bir nesneyi "ikiden büyük olanlarının yedi katlarını" üretecek şekilde şöyle kullanabiliriz: new SüzücüDönüştürücü( function bool(int sayı) { return sayı > 2..1 Kısa söz dizimi İsimsiz işlevlerin yazımlarında bazı kolaylıklar da vardır.. 69. Böyle bir durumda.İşlev Göstergeleri ve Kapamalar Yukarıdaki örneklerde de görüldüğü gibi.. // parametresiz Birincisi hiçbir kısaltmaya başvurmadan yazılmıştır.2. İsimsiz işlevin parametre almadığı durumlarda da parametre listesi yazılmayabilir. ikincisi. o göstergenin parametre almayan bir işlev göstergesi olduğunu ifade eder. // dönüş türsüz birİşlev(function { return 42.. function int(int sayı) { return sayı * 7. function (int sayı) { return sayı * 7.42. dönüş türünün return satırından çıkarsanmasından yararlanmıştır. başka ifadelerin arasında küçük işlevler tanımlamaya yarar. Böylece. Bunu görmek için işlev göstergesi alan bir işlev düşünelim: void birİşlev(/* . } O parametrenin tanımındaki function 'dan sonraki parantezin boş olması.42. }). Copyright © 2009-2011 Ali Çehreli. } O işlevin aldığı parametre. Bu gereksizlik özellikle sayı > 0 ve sayı * 10 gibi küçük işlemler kullanırken hissedilir... }). double döndüren ama parametre almayan bir işlev göstergesi olsun: void birİşlev(double function() gösterge) { // . }. İsimsiz işlevler. hem de istediğimiz davranışı tam da gereken noktada belirtmiş oluruz. üçüncüsü de gereksiz olan boş parametre listesini yazmamıştır. isimsiz işlevin oluşturulduğu noktada boş parantez yazmaya da gerek yoktur..org 443 . hem bu kadar küçük işlemler için ayrıca işlevler tanımlamak zorunda kalmamış oluruz. }. İşlevin dönüş türünün return satırından anlaşılabildiği durumlarda dönüş türü yazılmayabilir: new SüzücüDönüştürücü( function (int sayı) { return sayı > 2. http://ddili.. işlev göstergesi alsın . Örneğin. }). Şu üç isimsiz işlev tanımı birbirlerinin eşdeğeridir: birİşlev(function double() { return 42. }). bazı durumlarda başlı başına işlevler yazmak gereksiz gelebilir. İşlev hazır değeri olarak da adlandırabileceğimiz isimsiz işlev olanağı. }).

org 444 . Bu işlevin döndürdüğü isimsiz işlev. çıkışa yazdır. Kapama.1 Kullanılması Kapamaların kullanımı işlev göstergelerine çok benzer: tek farkları function yerine delegate anahtar sözcüğünün kullanılmasıdır. Kapamalar işte bu gibi durumlarda kullanılırlar: işlev göstergesi yanında. } // ← artış'ın yaşamı burada son bulur İşlevlerden artış gibi yerel değişkenlerin adreslerinin döndürülmesi de zaten bu yüzden tanımsız davranıştır. Böylece o kapsamlardaki değişkenlerin yaşamları uzamış olur..İşlev Göstergeleri ve Kapamalar Bazı durumlarda bir adım daha atabilir ve function da yazmayabiliriz. daha çok fonksiyonel programlama dillerinde görülen bir olanaktır. Hesapİşlevi hesapçı() { int artış = 10. yerel değişkeni kullanmaya çalıştığı için o kod hatalıdır. Ama o zaman bir isimsiz kapama kullanıldığı varsayılır. bu yerel değişkeni de kullanıyor olsun: alias int function(int) Hesapİşlevi. emirli bir dildir. 69. notlarını topla. sistem dilleri arasında bu konuda bir istisnadır. Yukarıdaki kodun derlenip doğru olarak çalışması için o kadarı yeterlidir: alias int delegate(int) Hesapİşlevi. yaşamı çoktan sona ermiş olan artış değişkenine erişmeye çalışacaktır.hesapçı. vs. isimsiz işlev daha sonradan işletildiği sırada. Yaşam Süreçleri ve Temel İşlemler dersinde gördüğümüz gibi. // ↓ derleme HATASI return function int(int sayı) { return artış + sayı.3 Kapamalar Kapama. 69. değişkenlerin yaşamları. Bu yüzden derleyici hata verir: Error: function deneme. Bu gibi dillerde programın işlemleri adım adım programcı tarafından emir verir gibi tarif edilir: bütün öğrenci nesnelerine eriş. Hesapİşlevi hesapçı() { Copyright © 2009-2011 Ali Çehreli. // . onun içinde tanımlandığı kapsamın da saklanmasından oluşur.3.__funcliteral1 cannot access frame of function hesapçı O kodun derlenip doğru olarak çalışabilmesi için yerel değişkenlerin yaşam süreçlerinin isimsiz işlev yaşadığı sürece uzatılması gerekir. } Döndürülen isimsiz işlev. onun kullandığı kapsamları da saklarlar. Bunu biraz aşağıda göstereceğim. artış 'ın işlev göstergesi döndüren bir işlev içinde tanımlanmış olan yerel bir değişken olduğunu düşünelim. C ailesine bağlı olan D. D. Kapamalar genelde böyle dillerde bulunmazlar.. öğrenci sayısına böl. }. http://ddili. tanımlı oldukları kapsamdan çıkıldığında son bulur: { int artış = 10. Derlenmesine izin verilmiş olsa. işlev göstergesine ek olarak.

org 445 . ve boş olduğu için parametre listesi de yazılmamıştır. ilk ve son sayıları -1 olan bir dizi sayı oluşturmaktadır. Bu iki özel sayının arasına kaç adet başka sayı geleceğini ve bu sayıların nasıl üretileceklerini ise parametre olarak almaktadır.2 Kısa söz dizimi İsimsiz işlevlerde olduğu gibi. Bütün bu kısaltmaları görmek için. } } return sonuç ~ -1. bir de yerel bir değişken kullanan bir kapama ile çağıralım: Copyright © 2009-2011 Ali Çehreli. Gördüğünüz gibi.İşlev Göstergeleri ve Kapamalar } int artış = 10. O kapamanın kullandığı yerel kapsamdaki artış gibi değişkenlerin yaşamları. ve işlev(3) yazımıyla çağırmaktadır. Çıktısı: -1 42 42 42 -1 Aynı işlevi. parametresiz bir kapama kullanan bir koda bakalım: int[] özelSayılarOluştur(int adet. kapama yaşadığı sürece devam edecektir. function veya delegate yazılmadığında delegate varsayılır. Bu isimsiz kapamayı belirleyen kapsam parantezlerini sarı ile işaretliyorum: writeln(özelSayılarOluştur(3. 0 . Bunun örneklerini daha sonraki bir derste öğreneceğimiz yapı ve sınıfların opApply üye işlevlerinde göreceğiz. int delegate() sayıÜretici) { int[] sonuç = [ -1 ]. isimsiz kapama için delegate anahtar sözcüğü kullanılmamış. return satırından çıkarsanabildiği için dönüş türü belirtilmemiş. })). isimsiz kapamaların dönüş türleri yazılmayabilir. işlev(3)). O işlevi. Bu yüzden kapamalar her çağrıldıklarında yerel değişkenleri değiştirebilirler. Kapamanın işi de kendisine verilen sayı ile artış 'ın toplamını döndürmek olduğu için. her çağrıldığında aynı sabit değeri döndüren aşağıdaki gibi bir kapamayla çağırabiliriz. { return 42. return delegate int(int sayı) { return artış + sayı. çıkışa 3 ve 10'un toplamı yazdırılacaktır: hesap: 13 69. hesapçı . adet) { sonuç ~= sayıÜretici(). eğer çıkarsanabiliyorsa. Yukarıdaki kapamayı şöyle bir kodla deneyebiliriz: auto işlev = hesapçı(). o kapamayı işlev isimli bir değişkenin değeri olarak kullanmakta.3. eğer boşsa parametre listeleri belirtilmeyebilir. foreach (i. http://ddili. O işlev. ve ek olarak delegate sözcüğü de yazılmayabilir. }.. writeln("hesap: ". isimsiz bir kapama döndürmektedir. Yukarıdaki kod.

3). { sonSayı += uniform(0.İşlev Göstergeleri ve Kapamalar int sonSayı.4 Özet • function anahtar sözcüğü ile işlev göstergeleri tanımlanabilir ve bu göstergeler daha sonra işlev gibi kullanılabilir • delegate anahtar sözcüğü. rasgele sayıların değerleri hep artan yönde gitmektedir. O kapama.org 446 . return sonSayı. işlev göstergesine ek olarak o işlev göstergesinin kullandığı kapsamı da barındırır • İşlev göstergesi veya kapama gereken yerlerde isimsiz işlevler veya isimsiz kapamalar tanımlanabilir • İsimsiz işlev ve kapama tanımlarında kısa söz dizimleri kullanılabilir Copyright © 2009-2011 Ali Çehreli. kapama. ama her zaman için son sayıya eklediği için. kapama tanımlar. Yerel değişkenin kapamanın işletilmesi sırasında nasıl değişmiş olduğunu da çıktının son satırında görüyoruz: -1 0 2 3 4 6 6 8 9 9 9 10 12 14 15 17 -1 son üretilen sayı: 17 69. rasgele bir değer üretmekte. sonSayı). writeln(özelSayılarOluştur(15. writeln("son üretilen sayı: ". http://ddili. })).

Öte yandan. 70.1 foreach desteğini aralık işlevleri ile sağlamak foreach 'in for 'un daha kullanışlısı olduğunu biliyoruz. bu döngü uygulandığı türe göre değişik şekillerde işler. Bu iki yöntemden opApply işlevleri önceliklidir: eğer tanımlanmışlarsa. } // . foreach 'in her türe uygun olamayacağını vurgulamak istiyorum. !aralık.. Bu yöntemlere geçmeden önce. o türe özel bir şekilde. eşleme tablolarında. indeksli veya indekssiz olarak tablo elemanlarına. aralık. aralık işlevlerine başvurur.. ifadeler . Bu üç işlev.. kütüphane türlerinde.Yapı ve Sınıflarda foreach 70 Yapı ve Sınıflarda foreach foreach Döngüsü dersinden hatırlayacağınız gibi. O yüzden Öğrenci sınıfının böyle bir konuda destek vermesi beklenmeyebilir. foreach 'in nasıl işleyeceğini kendi türlerimiz için de belirleyebiliriz. Derleyicinin arka planda ürettiği kod bu üye işlevleri kullanır: for ( . örneğin File için dosya satırlarına.. sonrakine geçmek (aralığı baş tarafından daraltmak).. ifadeler . döngünün sonunu belirlemek. } O döngü. derleyici o üye işlevleri kullanır. derleyici tarafından arka planda bir for döngüsü olarak şöyle gerçekleştirilir: for ( . Bunun için iki yöntem kullanılabilir: türümüzün aralık algoritmalarıyla da kullanılmasına olanak veren aralık işlevleri tanımlamak. Örneğin Öğrenci gibi bir sınıfın foreach ile kullanılmasında ne tür değişkenlere erişileceği açık değildir. tanımlanmamışlarsa.. Bu üç üye işlevin isimleri sırasıyla empty . sayaçlı veya sayaçsız olarak dizi elemanlarına.front(). Kendi türlerinizin foreach desteği verip vermeyeceklerine ve vereceklerse ne tür değişkenlere erişim sağlayacaklarına siz karar vermelisiniz. Öte yandan... Bir nesne üzerinde foreach ile ilerlemek.org 447 . popFront . /* bitmediği sürece */. foreach döngüsünün Öğrenci nesnesinin notlarına erişmek için kullanılacağı da düşünülebilir.empty(). veya opApply üye işlevlerini tanımlamak.. aralık) { // .. Şöyle bir foreach döngüsü olsun: foreach (eleman. ancak o tür herhangi bir şekilde bir topluluk olarak kabul edilebiliyorsa anlamlı olabilir. değerlere. aralık işlevlerini kullanmak çoğu durumda daha basit ve yeterli olabilir. Nasıl kullanıldığına bağlı olarak farklı elemanlara erişim sağlar: dizilerde. sayı aralıklarında. Copyright © 2009-2011 Ali Çehreli. http://ddili. ve en baştakine erişim sağlamak için kullanılır. başka bir bakış açısı ile. foreach 'in kendi türlerimizle de çalışabilmesi için yukarıdaki üç özel bölümde kullanılacak olan üç özel üye işlev tanımlamak gerekir. /* başından daralt */) { auto eleman = /* aralığın başındaki */.popFront()) { auto eleman = aralık. ve front 'tur.

. } bool empty() const { // baş.empty() : aralık tükenmişse true . } int front() const { // Aralığın başındaki değer. o işlevlerin doğru olarak çalışabilmesi için ayrıca aralığın boş olmaması gerekir...1. int son. ++baş.baş = baş. ifadeler . } invariant() { // baş'ın hiçbir zaman son'dan büyük olmaması gerekir assert(baş <= son). Bir anlamda. O yapının nesnelerini artık foreach ile şöyle kullanabiliriz: Copyright © 2009-2011 Ali Çehreli.son şeklinde yazılan aralıklarının eşdeğeri olarak çalışan bir tür tanımlayalım: struct Aralık { int baş.son = son.. } } Not: Ben güvenlik olarak yalnızca invariant bloğundan yararlandım. popFront ve front işlevleri için in blokları da düşünülebilirdi. } void popFront() { // Bir sonrakine geçmek. Ona ek olarak. Aralığın başını ve sonunu belirleyen değerler. Bu // işlem. türün foreach ile kullanılabilmesi için yeterlidir.Yapı ve Sınıflarda foreach } // .1 Örnek Belirli aralıkta değerler üreten bir yapı tasarlayalım. nesne kurulurken belirlensinler. http://ddili. son'a eşit olduğunda aralık tükenmiş demektir return baş == son.org 448 .popFront() : bir sonrakine geçer (aralığı baş tarafından daraltır) • . 70. son değer aralığın dışında kabul edilsin. this(int baş. değilse false döndürür • . int son) { this. this. baş'ın kendisidir return baş.front() : baştaki elemanı döndürür O şekilde işleyen böyle üç üye işleve sahip olması.. Bu üç işlev aşağıdaki gibi işlemelidir: • . Geleneklere uygun olarak. baş'ı bir arttırmaktır. D'nin baş. bir anlamda aralığı baş tarafından kısaltır.

son'u bir azaltmaktır.Yapı ve Sınıflarda foreach foreach (eleman.. aralıklarla ilgili çeşitli olanaklar sunar. kendisine verilen aralığı ters sırada kullanır: import std.range. ' '). çünkü gelenek olarak aralığın sonu. bir anlamda aralığı son tarafından kısaltır.. yani empty 'nin dönüş değeri true olana kadar kullanır: 3 4 5 6 70. değilse false döndürür • . aralık tükenmişse true . // . nesnelerin foreach ile tek bir şekilde kullanılmaları durumuna daha uygundur. Örneğin Copyright © 2009-2011 Ali Çehreli. http://ddili.. foreach (eleman. 7))) { write(eleman. retro yukarıdaki üye işlevlerden yararlanarak bu aralığı ters sırada kullanır: 6 5 4 3 70. void popBack() { // Bir öncekine geçmek.retro std. 7)) { write(eleman.back() : sondaki elemanı döndürür Bu iki yeni işlevi Aralık için şöyle tanımlayabiliriz: struct Aralık { // . nesneyi sanki bir aralıkmış gibi kullanmayı sağlarlar. } Türün retro ile kullanılabilmesi için empty yanında iki üye işlev daha gerekir: • . retro(Aralık(3. ' '). return son . Aralık(3.2 Ters sırada ilerlemek için std.org 449 . Bunlar arasından retro .1. son'dan bir önceki // değerdir. // aralığa dahil değildir.. } foreach . } int back() const { // Aralığın sonundaki değer.empty() : aynı anlamdadır.range modülü. Bu // işlem. o üç işlevden yararlanarak aralıktaki değerleri sonuna kadar.popBack() : bir öncekine geçer (aralığı son tarafından daraltır) • . --son.1. O yöntem. } } Kodun çıktısından anlaşıldığı gibi.range.2 foreach desteğini opApply işlevleri ile sağlamak Yukarıdaki üye işlevler.

yukarıdaki döngü derleyici tarafından arka planda aşağıdaki koda dönüştürülür. } foreach (ingilizcesi. opApply işlevinin yazımı konusunda bazı zorunluluklar getirir. öğrencilere foreach ile teker teker erişim sağlaması. opApply tarafından çağrılmalıdır 2..Yapı ve Sınıflarda foreach Öğrenciler gibi bir türün nesnelerinin. yalnızca elemanlar .. Programın işleyişi. Bu dönüşümü ve uyulması gereken zorunlulukları şu maddelerle açıklayabiliriz: 1. kapamanın parametreleri haline gelirler.. o yöntemle kolayca gerçekleştirilebilir.. }).. onun yerine nesnenin opApply işlevi derleyicinin oluşturduğu bir kapama ile çağrılır. bu kapama.. kendi türlerimizi de foreach ile birden fazla şekilde kullanma olanağı sağlarlar.. nesne) { // . } Eğer döngü değişkenlerine uyan bir opApply işlevi tanımlanmışsa. işlemler .. opApply 'ın nasıl tanımlanması gerektiğini görmeden önce opApply 'ın nasıl çağrıldığını anlamamız gerekiyor. indeksler ve elemanlar . ya da hem elemanlara hem de indekslere erişilebiliyordu: string[string] ingilizcedenTürkçeye. döngü değişkenleri. foreach 'in işlemleri. foreach döngüsü ortadan kalkar. bazen bir nesne üzerinde farklı şekillerde ilerlemek istenebilir. Kapamayı oluşturan kapsam parantezlerini sarı ile işaretliyorum: // Derleyicinin arka planda kullandığı kod: nesne. Bu anlaşmayı açıklamadan önce foreach döngüsünün yapısını tekrar hatırlatmak istiyorum: // Programcının yazdığı döngü: foreach (/* döngü değişkenleri */. işlemler . bu parametrelerin opApply 'ın tanımında ref olarak işaretlenmeleri gerekir Copyright © 2009-2011 Ali Çehreli.. ingilizcedenTürkçeye) { // .. Buna göre.. kapamayı oluşturan işlemler haline gelirler. foreach (türkçesi. türkçesi. http://ddili.. Yani. ve bu karşılıklı gidiş geliş döngü sonuna kadar devam eder.opApply(delegate int(/* döngü değişkenleri */) { // . Bunun örneklerini eşleme tablolarından biliyoruz: döngü değişkenlerinin tanımına bağlı olarak ya yalnızca elemanlara.. ingilizcedenTürkçeye) { // . Derleyicinin oluşturduğu bir kapamanın kullanılıyor olması. Önce opApply 'ın içi işletilir. foreach 'in kapsamına yazılan işlemler ile opApply işlevinin işlemleri arasında.. belirli bir anlaşmaya uygun olarak gider gelir. derleyici.. Öte yandan. // .org 450 . return sonlanmaBilgisi.. opApply kendi işi sırasında foreach 'in işlemlerini çağırır... döngü değişkenlerini ve döngü kapsamını kullanarak bir kapama oluşturur ve nesnenin opApply işlevini o kapama ile çağırır. } opApply işlevleri.

son = son. // (5) // (4) } Bu sınıfı da foreach ile aynı şekilde kullanabiliriz: foreach (eleman. eşleme tablolarının hem indekslerine hem de elemanlarına foreach ile erişildiği duruma benzer. eğer sıfır ise döngü devam etmelidir.'. aralık işlevleri kullanıldığı zamanki çıktının aynısı olacaktır: 3 4 5 6 70. ikinci. Yukarıdaki maddeleri. buna uygun olarak.org 451 . 15)) { write(birinci. int son.1 Farklı biçimlerde ilerlemek için opApply 'ın yüklenmesi Nesne üzerinde farklı şekillerde ilerleyebilmek. } Çıktısı. Derleyici. this. Aralık nesnelerinin iki foreach değişkeni ile de kullanılabilmelerini isteyelim: foreach (birinci. for (int sayı = baş. opApply 'ın değişik türlerdeki kapamalarla yüklenmesi ile sağlanır. int son) { this. Örneğin.2. sıfırdan farklı ise döngü sonlanmalıdır 4. döngünün örneğin break ile sonlanıp sonlanmadığını anlamak için kullanılır. asıl döngü opApply 'ın içinde programcı tarafından gerçekleştirilir 5. // (3) } } } return sonuç. opApply .baş = baş. Copyright © 2009-2011 Ali Çehreli. http://ddili. kapamanın dönüş türü int 'tir. foreach değişkenlerinin uyduğu bir opApply yüklemesi bulur ve onu çağırır. } // (3) (2) (1) int opApply(int delegate(ref int) işlemler) const { int sonuç. Aralık(0. ilgili oldukları yerlerde açıklama satırları olarak belirtiyorum: struct Aralık { int baş. return 'ün döndürdüğü bilgi. this(int baş. ikinci. } O kullanım. 7)) { write(eleman.Yapı ve Sınıflarda foreach 3. // (1) if (sonuç) { break. ++sayı) { sonuç = işlemler(sayı). kapamanın döndürmüş olduğu sonlanmaBilgisi 'ni döndürmelidir Aralık sınıfını bu anlaşmaya uygun olarak aşağıdaki gibi tanımlayabiliriz. kapamanın sonuna derleyici tarafından bir return satırı eklenir. Aralık(3. ' '). '. sayı != son. ' ').

..6 10. ref int) işlemler) const { int sonuç. okul) { // . foreach döngüsünün hem öğrencilere hem de öğretmenlere erişmek için kullanılabileceği bir Okul sınıfı şöyle tanımlanabilir: class Okul { int opApply(int delegate(ref Öğrenci) işlemler) const { // . Hangi opApply işlevinin seçileceği döngü değişkenlerinin adedi yanında. } } Bu Okul türünü kullanan programlar.Yapı ve Sınıflarda foreach Bu örnekte. Buna göre. } Copyright © 2009-2011 Ali Çehreli. opApply tarafından ve bu kullanıma uygun olan iki değerle çağrılmalıdır: int opApply(int delegate(ref int. Aralık yukarıdaki gibi iki değişkenle kullanıldığında art arda iki değere erişiliyor olsun. if (sonuç) { break. } int opApply(int delegate(ref Öğretmen) işlemler) const { // ..11 Bunu sağlamak için iki değişkenli bir kapama ile çalışan yeni bir opApply tanımlamak gerekir. ikinci).. hangi elemanlar üzerinde ilerleneceğini döngü değişkenini açık olarak yazarak seçebilirler: foreach (Öğrenci öğrenci.. Tür için anlamlı olduğu sürece başka opApply işlevleri de tanımlanabilir.. } foreach (Öğretmen öğretmen. okul) { // .. i += 5) { int birinci = i. i + 1 < son. İki değişkenli döngü kullanıldığında üretilen kapama bu opApply yüklemesine uyduğu için. http://ddili. sonuç = işlemler(birinci.. türleri ile de belirlenebilir. Değişkenlerin türleri foreach döngüsünde açıkça yazılabilir ve böylece ne tür elemanlar üzerinde ilerlenmek istendiği açıkça belirtilebilir.org 452 . for (int i = baş. O kapama. derleyici bu tanımı kullanır. ve döngünün her ilerletilişinde değerler beşer beşer artsın. Yani yukarıdaki döngünün çıktısı şöyle olsun: 0. } } } return sonuç.1 5. int ikinci = i + 1.

. 70. ' '). foreach 'in döngü değişkenlerine göre öğrencilere veya öğretmenlere erişim sağlayacak şekilde yazın. http://ddili. 70.3 Uyarı: foreach 'in işleyişi sırasında topluluk değişmemelidir Hangi yöntemle olursa olsun. 10. 2)) { write(sayı. ve o kapamaya uyan opApply işlevini çağıracaktır. Bu kurala uyulmaması tanımsız davranıştır. çözümler Copyright © 2009-2011 Ali Çehreli. ama aralıktaki değerleri birer birer değil. foreach desteği veren bir tür. Aralık(0. Yazı içinde geçen Okul sınıfını. .4 Problemler 1.. belirtilen adım kadar ilerleten bir sınıf tanımlayın.Yapı ve Sınıflarda foreach Derleyici. döngünün işleyişi sırasında sunduğu topluluk kavramında bir değişiklik yapmamalıdır: döngünün işleyişi sırasında yeni elemanlar eklememeli ve var olan elemanları silmemelidir. Adım bilgisini kurucu işlevinin üçüncü parametresi olarak alsın: foreach (sayı. } Sıfırdan 10'a kadar ikişer ikişer ilerlemesi beklenen o Aralık nesnesinin çıktısı şöyle olsun: 0 2 4 6 8 2.org 453 . Yukarıdaki Aralık gibi çalışan. değişkenin türüne uyan bir kapama üretecek.

Dört baytlık int ve sekiz baytlık double aynı alanı paylaştıkları için bu birliğin büyüklüğü en büyük üye için gereken yer kadardır: 8 Bunun bellek kazancı sağlayan bir olanak olduğunu düşünmeyin. Birliklerin yararı. D'ye C dilinden geçmiş olan alt düzey bir olanaktır. her ortamda aynı şekilde çalışmasa da. http://ddili. bilginin parçalarına diğer üyeler yoluyla erişilebilmesidir. birbirlerinden bağımsız değillerdir Şimdiye kadar çok karşılaştığımız yapı türlerinin kullandıkları bellek alanı..org 454 . aynı bölgenin farklı zamanlarda farklı türden bilgiler için kullanılabilmesidir.double için 8 bayt ---------------> | ---+------+------+------+------+------+------+------+------+--- Copyright © 2009-2011 Ali Çehreli. üyeleri aynı bellek bölgesini paylaştıkları için. Yukarıdaki birliği oluşturan sekiz baytın bellekte nasıl durduklarını. ve üyeler için nasıl kullanıldıklarını şöyle gösterebiliriz: 0 1 2 3 4 5 6 7 ---+------+------+------+------+------+------+------+------+--| <-. bütün üyelerini barındıracak kadar büyüktü: struct Yapı { int i. Aynı bellek alanına birden fazla bilgi sığdırmak olanaksızdır. writeln(Birlik. writeln(Yapı. Dört baytlık int 'ten ve sekiz baytlık double 'dan oluşan o yapının büyüklüğü 12'dir: 12 Aynı şekilde tanımlanan bir birliğin büyüklüğü ise. double d.int için 4 bayt --> | | | <-------------. Belirli bir anda tek bir üye saklanabilir.Birlikler 71 Birlikler Birlikler. } // .sizeof). double d. üyelerden en büyüğü için gereken yer kadardır: union Birlik { int i.. birden fazla üyenin aynı bellek alanını paylaşmalarını sağlarlar.. } // .. Birliklerin yararlarından birisi. İki fark dışında yapılarla aynı şekilde kullanılır: • struct yerine union anahtar sözcüğü ile tanımlanır • üyeleri aynı bellek alanını paylaşırlar.sizeof).

Aynı bellek bölgesinin kullanılıyor olması ilginç sonuçlar doğurabilir. türleri oluşturan baytlara teker teker erişmek için kullanılabilirler. } } // . writeln(birlik. Örneğin aslında 32 bitten oluşan IP adreslerinin 4 bölümünü elde etmek için bu 32 biti paylaşan 4 baytlık bir dizi kullanılabilir. 42|0|0|0. Üyelerin hepsi aynı alanı paylaşırlar. Yukarıdaki yapının son iki üyesi aynı alanı paylaşırlar ve bu yüzden yapı.2 Başka bir türün baytlarını ayrıştırmak Birlikler. toplam iki int 'in büyüklüğü kadar yer tutar. writeln(BirYapı. Birlik üyesi olmayan birinci için gereken 4 bayt.1 İsimsiz birlikler İsimsiz birlikler. // int üyenin ilklenmesi // double üyenin kullanılması int üyeyi oluşturan dört baytın 42 değerini taşıyacak şekilde kurulmaları. Adres değerini oluşturan üye ve dört bayt bir birlik olarak şöyle bir araya getirilebilir: union IpAdresi { Copyright © 2009-2011 Ali Çehreli.org 455 .Birlikler Ya sekiz baytın hepsi birden double üye için kullanılır. 71.. Ben örnek olarak iki üye kullandım. ya da ilk dört bayt int üye için kullanılır ve gerisine dokunulmaz.d). birliğin bir int ile ilklenmesi ama bir double olarak kullanılması. içinde bulundukları bir yapının hangi üyelerinin paylaşımlı olarak kullanıldıklarını belirlerler: struct BirYapı { int birinci. int üçüncü. union { int ikinci.9547e-270 Mikro işlemcinin bayt sıralarına bağlı olarak int üyeyi oluşturan dört bayt bellekte 0|0|0|42. baştan kestirilemeyecek double değerleri verebilir: auto birlik = Birlik(42). http://ddili. birlikleri istediğiniz kadar üye ile tanımlayabilirsiniz.. veya daha başka bir düzende bulunabilir. Bu yüzden yukarıdaki double üyenin değeri başka ortamlarda daha farklı da olabilir.sizeof). ve ikinci ile üçüncü 'nün paylaştıkları 4 bayt: 8 71. double üyenin değerini de etkiler: 4. Örneğin.

std. Başka ortamlarda başka sırada da çıkabilir. baytlar dizisinin elemanları teker teker adresin dört bölümüne karşılık gelirler: void main() { auto adres = IpAdresi(0xc0a80102). birlik üyelerinin değerlerinin belirsiz olabilecekleridir. O birliği oluşturan iki üye.LittleEndian değeri sistemin küçük soncul olduğunu.baytlar[i]. Yukarıdaki dönüşüm sonucunda IP adresinin bölümleri alışık olunan sırada çıkacaktır: Copyright © 2009-2011 Ali Çehreli. küçük soncul bir ortamda olduğumuzu şöyle belirleyebilir ve yukarıdaki IP adresini oluşturan baytları tersine çevirebiliriz: import std.Birlikler } uint değer. bu programı denediğim ortamda alışık olunduğundan ters sırada çıkmaktadır: 2 1 168 192 Bu. O üye dışındaki üyelere erişildiğinde ne tür değerlerle karşılaşılacağı ortamdan ortama farklılık gösterebilir.BigEndian değeri de büyük soncul olduğunu belirtir.2 adresinin değeri olarak karşılaştığımız 0xc0a80102 ile ilklendiğinde. if (endian == Endian. bswap . ancak ve ancak tek bir üyeleri ile kullanıldıklarında beklendiği gibi çalışırlar.system. Hangi üyesi ile kurulmuşsa.IP adresini oluşturan 32 bit ---------> | | baytlar[0] | baytlar[1] | baytlar[2] | baytlar[3] | ---+------------+------------+------------+------------+--Bu birlik.. Endian.168. } } writeln().LittleEndian) { adres. import std. // . http://ddili. Adresin bölümleri. Bu örnekte asıl göstermek istediğim..intrinsic modülünün bswap işlevinin bu konuda yararlı olabileceğini belirtmek istiyorum. birlik nesnesinin yaşamı boyunca o üyesi ile kullanılması gerekir.değer).org 456 . 4) { write(adres. Birlikler.system modülündeki endian değerinden de yararlanırsak. 0 . std. foreach (i. kendisine verilen uint 'in baytları ters sırada olanını döndürür.intrinsic.. aynı belleği şu şekilde paylaşırlar: 0 1 2 3 ---+------------+------------+------------+------------+--| <-----. } Endian.değer = bswap(adres. Bu dersle ilgisi olmasa da. daha önceki derslerde 192. programı çalıştıran mikro işlemcinin küçük soncul olduğunu gösterir. ubyte[4] baytlar. ' ').1.

o üyenin değerine göre farklı bir şekilde kullanılıyor olabilir: struct Adres { // . örneğin ağ protokollerinde. union { BirProtokol birProtokol... Yukarıdaki AğPakedi yapısında hangi protokol üyesinin geçerli olduğu tür 'ün değerinden anlaşılabilir. Ağ pakedinin daha sonraki bir bölümü. } enum ProtokolTürü { birTür.3 Protokol örneği Bazı protokollerde.. 71.. http://ddili.. Normalde IP adresleriyle böyle doğrudan ilgilenmek yerine.Birlikler 192 168 1 2 Bunu yalnızca birliklerle ilgili bir kullanım örneği olarak gösterdim. BaşkaProtokol başkaProtokol.4 Ne zaman kullanmalı Bayt paylaşmak gibi kavramlar oldukça alt düzey kabul edilecekleri için. programın geri kalanı da yapıyı o değere göre kullanır.. Birliklerle C kütüphanelerinde de karşılaşılabilir. } struct BaşkaProtokol { // . başkaTür } struct AğPakedi { Adres hedef. } struct BirProtokol { // . 71. Adres kaynak. ProtokolTürü tür.org 457 . bazı baytların anlamı başka bir üye tarafından belirleniyor olabilir. Copyright © 2009-2011 Ali Çehreli. o iş için kullanılan bir kütüphanenin olanaklarından yararlanmak daha doğru olur. } } ubyte[] geriKalanı. birlikler günlük tasarımlarımızda hemen hemen hiç kullanılmazlar.

koşul 'un true olduğu durumlarda doğrudan bitiş isimli satıra gider. if (!koşul) { writeln("ikinci"). vs.Etiketler 72 Etiketler Etiketler. sonlandırıcı bölge goto 'nun C'deki geçerli bir kullanımı.1. } writeln("ikinci"). kod satırlarına isimler vermeye ve program akışını bu isimli satırlara yöneltmeye yararlar. goto . Buna rağmen goto 'nun C dilinde iki tane geçerli kullanımı vardır.1 goto İngilizce'de "git" anlamına gelen goto . while . 72. Örneğin yukarıdaki kodun eşdeğeri.org 458 . program akışını ismi belirtilen satıra yönlendirir: void birİşlev(bool koşul) { writeln("birinci"). 72. yapısal programlamaya aykırı olduğu için C'de bile kaçınılması önerilen bir olanaktır. for . şimdiye kadar çoğu kodda gördüğümüz gibi.1 D'de gerekmeyen. Bu kullanımların ikisi de D'de gereksizdir. Etiketin isminden ve : karakterinden oluşurlar: bitiş: // ← bir etiket Yukarıdaki etiket. işlevlerin sonlarına yazılan ve o işlevde ayrılmış olan kaynakların geri verilmesi gibi işlemleri içeren sonlandırıcı bölgedir: Copyright © 2009-2011 Ali Çehreli. bitiş: } writeln("üçüncü"). Doğrudan belirli satırlara yönlendiren goto 'lar yerine. goto kullanmadan şöyle yazılabilir: void birİşlev(bool koşul) { writeln("birinci"). Etiketler ve goto D'ye C'den geçmiştir. ve "ikinci" yazdırılmaz. gibi yapısal deyimlerin kullanılması önerilir. Yukarıdaki işlev. if (koşul) { goto bitiş. http://ddili. } } writeln("üçüncü"). tanımlandığı satıra bitiş ismini verir.

hata atma düzeneğinin catch ve finally blokları.. iç içe döngülerde kullanımı goto 'nun C'deki diğer geçerli kullanımı. D'de sonlandırma işlemleri. Döngüyü kırmak için kullanılan break ... vs. C'de ve C++'da dıştaki döngüyü kırmanın bir yolu.. bu kullanım D'de gereksizdir. yalnızca en içteki döngüyü etkiler. gibi olanaklarla sağlanır.. sonlandirma islemleri buraya yazilir . D'de goto 'nun bu kullanımına da gerek yoktur.org 459 . } return hata. Aynı durum. dıştaki döngüyü ilerletmenin bir yolu da. ve döngüyü hemen ilerletmek için kullanılan continue . 72. // distaki icin 'continue' gibi calisir goto distakiniIlerlet. Not: Bu kullanıma C++'da da gerek yoktur. iç içe bulunan switch deyimlerinde de vardır.. biraz aşağıda göstereceğim döngü etiketleri kullanılır.1. } distakindenCik: Not: Bu kullanıma C++ programlarında da rastlanabilir.. Copyright © 2009-2011 Ali Çehreli.. break yalnızca içteki switch 'i etkilediği için. dıştakinden de çıkmak için goto kullanılabilir. çöp toplayıcı. döngüden sonraki bir etikete gitmektir. iç içe döngülerin daha dışta olanlarını etkilemektir. // distaki icin 'break' gibi calisir goto distakindenCik. onun hemen içindeki bir etikete gitmektir: // --. D'de kaynak yönetimi için başka olanaklar bulunduğu için. if (hata) { goto bitis.C kodu --while (birKosul) { while (baskaKosul) { // yalnizca icteki donguyu etkiler continue. // yalnizca icteki donguyu etkiler break. sonlandırıcı işlevler. bitis: // .Etiketler // --. http://ddili. } // . Onun yerine. } distakiniIlerlet: .C kodu --int birIslev() { // .2 D'de gerekmeyen.

Nesnelerin kuruldukları satırlar goto ile atlanabilse. Bunun nedenlerinden birisi. goto 'nun bu tür kullanımı D'de hatalıdır ve yukarıdaki kod bir derleme hatasına neden olur: Error: cannot goto forward into different try block level Not: Aynı sorun C++'da da vardır. // dıştaki döngüden çıkar break dışDöngü. // dıştaki döngüyü ilerletir continue dışDöngü. 72. } // kurucu işlevi atlar auto nesne = Yapı(42).Etiketler 72. belki de hiç kullanılmayacak olan bir nesneyi kurmak için gereksizce zaman ve kaynak harcamamaktır. Copyright © 2009-2011 Ali Çehreli.1. ve o döngülerin etkilenmeleri sağlanabilir: dışDöngü: while (birKoşul) { while (başkaKoşul) { // içteki döngüyü ilerletir continue. // içteki döngüden çıkar break. nesnelerin kuruldukları satırlarda çağrılır. 72. ama bu konu. continue ve break anahtar sözcüklerinde de etiket belirtilebilir. nesnenin kurulması için gereken bilginin henüz mevcut olmaması olabilir. } } Aynısı switch deyimleri için de geçerlidir.3 Özet D'de goto 'yu kullanmak için hiçbir neden yoktur. Bir başka neden. // nesnenin kurulduğu satır birEtiket: nesne. Bu yüzden. // HATA: belki de kurulmamış olan // nesneyi kullanır Yukarıdaki koddaki goto ile birEtiket satırına gidilebilse.3 goto 'nun kurucu işlevleri atlama sorunu Kurucu işlevler.org 460 . kurulamamış bir nesne üzerinde işletilirdi. http://ddili.birİşlem(). C++'da programcının dikkatine bırakılmıştır. break deyimlerinin dıştaki bir switch 'i etkilemesi için o switch deyiminden önce de etiket tanımlanabilir. birİşlem() işlevi. kurulamamış oldukları için hatalı sonuçlar doğuran nesnelerle karşılaşılabilir: if (koşul) { goto birEtiket.2 Döngü etiketleri D'de döngülerden hemen önce etiketler tanımlanabilir.

http://ddili. Copyright © 2009-2011 Ali Çehreli.Etiketler İç içe döngülerden veya switch deyimlerinden hangisinin etkileneceğini belirtmek için break ve continue deyimlerinde etiket belirtilebilir.org 461 .

73. veya sınıf şablonu tanımlamak. } } template Kesirli(T) { class Kesirli { T pay. } class Kesirli(T) { T pay.. Bu derste. } // . o derste anlatılanları burada tekrarlamamaya çalışacağım. T payda. Daha önce de görmüş olduğunuz yukarıdaki tanımlar. Yukarıdaki söz dizimleri. hem birlik ve arayüz şablonlarını da tanıyacağız.1 Kestirme ve uzun söz dizimi C++ gibi başka dillerde de olduğu gibi D'nin şablonları çok güçlü olanaklardır.Ayrıntılı Şablonlar 73 Ayrıntılı Şablonlar Şablonların ne kadar kullanışlı olduklarını Şablonlar dersinde görmüştük. Devam etmeden önce en azından o dersin sonundaki özeti bir kere daha gözden geçirmenizi öneririm. Bu derste şablon olanağını daha ayrıntılı olarak göreceğiz. en çok yararlanılan kullanımlarının olabildiğince rahat ve anlaşılır olmasına çalışılmıştır. alias . } Derleyicinin her zaman için uzun tanımı kullandığını. this . hem de şablon parametrelerinin değer.. Aslında şablonlar daha uzun olarak template anahtar sözcüğü ile tanımlanırlar. Daha önce işlev. Algoritmaların veya veri yapılarının tek tanımını yazarak birden çok türle çalışmalarını sağlayabiliyorduk. aşağıdaki tanımların kısa eşdeğerleridir: template ikiKatı(T) { T ikiKatı(T değer) { return 2 * değer.. yapı. ve çokuzlu çeşitleri olduğunu da göreceğiz. Buna rağmen. D'nin kestirme şablon tanımlarıdır. İşlev. T payda. O derste şablonların en çok karşılaşılan kullanımlarını göstermiştim. } // . ve sınıf şablonlarını tanımıştık ve şablon parametrelerinin türler konusunda serbestlik getirdiklerini görmüştük.org 462 . yapı.. isminden sonra şablon parametre listesi eklemek kadar kolaydır: T ikiKatı(T)(T değer) { return 2 * değer. ve kestirme söz dizimini arka planda şu şekilde uzun tanıma dönüştürdüğünü düşünebiliriz: Copyright © 2009-2011 Ali Çehreli. http://ddili.

Ayrıntılı Şablonlar

1. tanımladığımız şablonu bir template kapsamı içine alır 2. o kapsama da aynı ismi verir 3. şablon parametre listesini bizim tanımladığımız şablondan alır ve o kapsama verir Kestirme tanım; biraz aşağıda göreceğimiz tek tanım içeren şablon olanağı ile ilgilidir.
73.1.1

Şablon isim alanı
template bloğu, aslında bir seferde birden çok şablon tanımlanmasına da olanak verir: template ŞablonBloğu(T) { T birİşlev(T değer) { return değer / 3; } struct BirYapı { T üye; }

}

Yukarıdaki blokta bir işlev, bir de yapı şablonu tanımlamaktadır. O şablonları örneğin int ve double türleri için, ve uzun isimleriyle şöyle kullanabiliriz: auto sonuç = ŞablonBloğu!int.birİşlev(42); writeln(sonuç); auto nesne = ŞablonBloğu!double.BirYapı(5.6); writeln(nesne.üye); Şablonun belirli bir türle kullanımı, bir isim alanı tanımlar. Bloğun içindeki isimler, o isim alanı açıkça belirtilerek kullanılabilirler. Bu isimler fazla uzun olabileceklerinden, onlara alias ve alias this dersinde gördüğümüz alias anahtar sözcüğü ile kısa takma isimler verilebilir: alias ŞablonBloğu!dchar.BirYapı KarakterYapısı; // ... auto nesne = KarakterYapısı('ğ'); writeln(nesne.üye);

73.1.2

Tek tanım içeren template blokları
İçinde tek tanım bulunan bir şablon, eğer şablon bloğunun ismi o tek tanımın ismi ile aynı ise, doğrudan o tek tanım yerine kullanılabilir. Bu, şimdiye kadarki şablonlarda kullandığımız kestirme söz dizimini sağlayan olanaktır. Örnek olarak, büyüklüğü 20 bayttan fazla olan türlerin büyük olarak kabul edildiği bir program olsun. Bir türün büyük olup olmadığının kararı şöyle bir şablonun içindeki bir bool değişken ile belirlenebilir: template büyük_mü(T) { const bool büyük_mü = T.sizeof > 20; }

Copyright © 2009-2011 Ali Çehreli, http://ddili.org

463

Ayrıntılı Şablonlar

Dikkat ederseniz, hem şablonun hem de içindeki tek tanımın isimleri aynıdır. Öyle olduğunda, bu uzun şablon tanımının isim alanı ve içindeki tek tanım açıkça büyük_mü!int.büyük_mü diye yazılmaz; kısaca, yalnızca şablonun isim alanı yazılır: writeln(büyük_mü!int); Yukarıdaki işaretli bölüm, şablon içindeki aynı isimli bool yerine geçer. O kod, çıktıya false yazar; çünkü büyük_mü!int , şablon içindeki bool değişkendir. int 'in uzunluğu 4 bayt olduğu için o bool değişkenin değeri false 'tur.

73.2

Şablon çeşitleri
İşlev, sınıf, ve yapı şablonları
Bu alt başlığı bütünlük amacıyla yazdım. Yukarıda da görüldüğü gibi, bu tür şablonlarla hem Şablonlar dersinde hem de daha sonraki örneklerde çok karşılaştık.

73.2.1

73.2.2

Birlik şablonları
Birlik şablonları, yapı şablonları ile aynı şekilde tanımlanırlar. Birlik şablonları için de kestirme şablon söz dizimi kullanılabilir. Bir örnek olarak, Birlikler dersinde tanımladığımız IpAdresi birliğinin daha genel ve daha kullanışlı olanını tasarlamaya çalışalım. O dersteki birlik; değer olarak uint türünü kullanıyordu. O değerin parçalarına erişmek için kullanılan dizinin elemanlarının türü de ubyte idi: union IpAdresi { uint değer; ubyte[4] baytlar; } O birlik, hem IP adresi değeri tutuyordu, hem de o değerin parçalarına ayrı ayrı erişme olanağı veriyordu. Aynı kavramı, daha genel isimler de kullanarak bir şablon olarak şöyle tanımlayabiliriz: union ParçalıDeğer(AsılTür, ParçaTürü) { AsılTür değer; ParçaTürü[/* gereken eleman adedi */] parçalar; } Bu birlik şablonu, asıl değerin ve alt parçalarının türünü serbestçe tanımlama olanağı verir. Asıl tür ve parça türü, birbirlerinden bağımsız olarak seçilebilirler. Burada gereken bir işlem, parça dizisinin uzunluğunun kullanılan türlere bağlı olarak hesaplanmasıdır. IpAdresi birliğinde, uint 'in dört adet ubyte parçası olduğunu bildiğimiz için sabit olarak 4 yazabilmiştik. Bu şablonda ise dizinin uzunluğu, kullanılan türlere göre otomatik olarak hesaplanmalıdır. Türlerin bayt olarak uzunlukları .sizeof niteliğinden öğrenilebildiği için; bu bilgiden yararlanırsak, uzunluk hesabını bir işlev şablonuna yaptırabiliriz:

Copyright © 2009-2011 Ali Çehreli, http://ddili.org

464

Ayrıntılı Şablonlar

int elemanAdedi(AsılTür, ParçaTürü)() { return (AsılTür.sizeof + (ParçaTürü.sizeof - 1)) / ParçaTürü.sizeof; } Not: O hesaptaki (ParçaTürü.sizeof - 1) ifadesi, türlerin uzunluklarının birbirlerine tam olarak bölünemediği durumlarda gerekir. Asıl türün 5 bayt, parça türünün 2 bayt olduğunu düşünün. O ifadeyi eklemezsek; gerçekte 3 adet parça gerektiği halde, 5/2 hesabının sonucu tamsayı kırpılması nedeniyle 2 çıkardı. Artık parça dizisinin eleman adedi olarak o işlevin döndürdüğü değeri kullanabiliriz ve böylece birliğin tanımı tamamlanmış olur: union ParçalıDeğer(AsılTür, ParçaTürü) { AsılTür değer; ParçaTürü[elemanAdedi!(AsılTür, ParçaTürü)()] parçalar; } Daha önce tanımladığımız IpAdresi birliğinin eşdeğeri olarak bu şablonu kullanmak istesek, türleri IpAdresi 'nde olduğu gibi sırasıyla uint ve ubyte olarak belirtmemiz gerekir: auto adres = ParçalıDeğer!(uint, ubyte)(0xc0a80102); foreach (eleman; adres.parçalar) { write(eleman, ' '); } Birlikler dersinde gördüğümüz çıktının aynısını elde ederiz: 2 1 168 192 Bu şablonun getirdiği esnekliği görmek için IP adresinin parçalarını iki adet ushort olarak edinmek istediğimizi düşünelim. Bu sefer, ParçalıDeğer şablonunun ParçaTürü parametresi olarak ushort yazmak yeterlidir: auto adres = ParçalıDeğer!(uint, ushort)(0xc0a80102); Alışık olmadığımız bir düzende olsa da, bu seferki çıktı iki ushort 'tan oluşmaktadır: 258 49320 Bir sonraki başlığa geçmeden önce, burada başka bir tasarımın da kullanılabileceğini göstermek istiyorum. Kaç adet parça gerektiğini yukarıdaki elemanAdedi işlev şablonu ile hesapladığımız için, o işlevi çağırmak için sonuna () karakterleri yazmıştık: ParçaTürü[elemanAdedi!(AsılTür, ParçaTürü)()] parçalar; Aynı hesabın sonucunu, tek tanımlı bir şablon içindeki değişken değeri olarak da saklayabilirdik: template elemanAdedi(AsılTür, ParçaTürü) { const int elemanAdedi = (AsılTür.sizeof + (ParçaTürü.sizeof - 1))

Copyright © 2009-2011 Ali Çehreli, http://ddili.org

465

Ayrıntılı Şablonlar

}

/ ParçaTürü.sizeof;

O tek tanımlı şablon, içindeki int değişken yerine geçtiği için, doğrudan o değişkenin değerinden yararlanabilirdik: ParçaTürü[elemanAdedi!(AsılTür, ParçaTürü)] parçalar;

73.2.3

Arayüz şablonları
Arayüz şablonları, arayüzde kullanılan türler konusunda serbestlik getirirler. Arayüz şablonlarında da kestirme tanım kullanılabilir. Örnek olarak, renkli nesnelerin arayüzünü tanımlayan, ama renk olarak hangi türün kullanılacağını serbest bırakan bir arayüz tasarlayalım: interface RenkliNesne(RenkTürü) { void renklendir(RenkTürü renk); } O arayüz, kendisinden türeyen sınıfların renklendir işlevini tanımlamalarını gerektirir; ama renk olarak ne tür kullanılacağı konusunu serbest bırakır. Bir sitedeki bir çerçeveyi temsil eden bir sınıf; renk olarak kırmızı, yeşil, ve maviden oluşan üçlü bir yapı kullanabilir: struct KırmızıYeşilMavi { ubyte kırmızı; ubyte yeşil; ubyte mavi; } class SiteÇerçevesi : RenkliNesne!KırmızıYeşilMavi { override void renklendir(KırmızıYeşilMavi renk) { // ... } } Öte yandan, renk olarak ışığın frekansını kullanmak isteyen bir sınıf, renk için frekans değerine uygun olan başka bir türden yararlanabilir: alias double Frekans; class Lamba : RenkliNesne!Frekans { override void renklendir(Frekans renk) { // ... } } Yine Şablonlar dersinden hatırlayacağınız gibi, "her şablon gerçekleştirmesi farklı bir türdür". Buna göre, RenkliNesne!KırmızıYeşilMavi ve RenkliNesne!Frekans arayüzleri, farklı arayüzlerdir. Bu yüzden, onlardan türeyen sınıflar da birbirlerinden bağımsız sıradüzenlerin parçaları olurlar; SiteÇerçevesi ve Lamba , birbirlerinden bağımsızdır.

Copyright © 2009-2011 Ali Çehreli, http://ddili.org

466

Ayrıntılı Şablonlar

73.3

Şablon parametre çeşitleri
Şimdiye kadar gördüğümüz şablonlar, hep türler konusunda serbestlik getiriyorlardı. Yukarıdaki örneklerde de kullandığımız T ve RenkTürü gibi şablon parametreleri, hep türleri temsil ediyorlardı. Örneğin T 'nin anlamı, şablonun kod içindeki kullanımına bağlı olarak int , double , Öğrenci , vs. gibi bir tür olabiliyordu. Şablon parametreleri; değer, this , alias , ve çokuzlu da olabilirler.

73.3.1

Tür parametreleri
Bu alt başlığı bütünlük amacıyla yazdım. Şimdiye kadar gördüğümüz bütün şablon parametreleri zaten hep tür parametreleriydi.

73.3.2

Değer parametreleri
Şablon parametresi olarak değerler de kullanılabilir. Bu, şablonun tanımı ile ilgili bir değerin serbest bırakılmasını sağlar. Şablonlar derleme zamanı olanakları oldukları için, değer olarak kullanılan şablon parametresinin derleme zamanında hesaplanabilmesi şarttır. Bu yüzden, programın çalışması sırasında hesaplanan, örneğin girişten okunan bir değer kullanılamaz. Bir örnek olarak, belirli sayıda köşeden oluşan şekilleri temsil eden yapılar tanımlayalım: struct Üçgen { Nokta[3] köşeler; // ... } struct Dörtgen { Nokta[4] köşeler; // ... } struct Beşgen { Nokta[5] köşeler; // ... } Örnek kısa olsun diye başka üyelerini göstermedim. Normalde, o türlerin başka üyelerinin ve işlevlerinin de bulunduğunu ve hepsinde tamamen aynı şekilde tanımlandıklarını varsayalım. Sonuçta, dizi uzunluğunu belirleyen değer dışında, o yapıların tanımları aynı olsun. Değer şablon parametreleri böyle durumlarda yararlıdır. Yukarıdaki tanımlar yerine tek yapı şablonu tanımlanabilir. Yeni tanım genel amaçlı olduğu için, ismini de o şekillerin genel ismi olan poligon koyarak şöyle tanımlayabiliriz: struct Poligon(int köşeAdedi) { Nokta[köşeAdedi] köşeler; // ... } O yapı şablonu, şablon parametresi olarak int türünde ve köşeAdedi isminde bir şablon parametresi almaktadır. O parametre değeri, yapının tanımında herhangi bir yerde kullanılabilir.

Copyright © 2009-2011 Ali Çehreli, http://ddili.org

467

Ayrıntılı Şablonlar

Artık o şablonu istediğimiz sayıda köşesi olan poligonları ifade etmek için kullanabiliriz: Poligon!100 yüzKöşeli; Yine alias 'tan yararlanarak kullanışlı isimler verebiliriz: alias Poligon!3 Üçgen; alias Poligon!4 Dörtgen; alias Poligon!5 Beşgen; // ... Üçgen üçgen; Dörtgen dörtgen; Beşgen beşgen; Böylece; hem başlangıçtaki üç farklı yapı yerine tek şablon tanımlamış oluruz, hem de herhangi sayıda köşesi olan şekiller için kullanabiliriz. Yukarıdaki değer şablon parametresinin türü int 'ti. Derleme zamanında bilinebildiği sürece, değer olarak başka türler de kullanılabilir. Başka bir örnek olarak, basit XML elemanları oluşturmakta kullanılan bir sınıf şablonu tasarlayalım. Bu basit XML tanımı, çok basitçe şu çıktıyı üretmek için kullanılsın: • önce < > karakterleri arasında elemanın ismi: <isim> • sonra elemanın değeri • en sonunda da </ > karakterleri arasında yine elemanın ismi: </isim> Örneğin değeri 42 olan bir elemanın <isim>42</isim> şeklinde görünmesini isteyelim. Eleman isimlerini bir sınıf şablonunun string türündeki bir değer parametresi olarak belirleyebiliriz: class XmlElemanı(string isim) { double değer; this(double değer) { this.değer = değer; } override string toString() const { return format("<%s>%s</%s>", isim, değer, isim); }

}

Bu örnekteki şablon parametresi, şablonda kullanılan bir türle değil, bir string değeriyle ilgilidir. O string 'in değeri de şablon içinde gereken her yerde kullanılabilir. alias 'tan yararlanarak kullanışlı tür isimleri de tanımlayarak: alias XmlElemanı!"konum" Konum; alias XmlElemanı!"sıcaklık" Sıcaklık; alias XmlElemanı!"ağırlık" Ağırlık; void main() { Object[] elemanlar; elemanlar ~= new Konum(1); elemanlar ~= new Sıcaklık(23);

Copyright © 2009-2011 Ali Çehreli, http://ddili.org

468

Ayrıntılı Şablonlar

elemanlar ~= new Ağırlık(78); } writeln(elemanlar);

Not: Ben bu örnekte kısa olsun diye ve nasıl olsa bütün sınıf sıradüzenlerinin en üstünde bulunduğu için bir Object dizisi kullandım. O sınıf şablonu aslında daha uygun bir arayüz sınıfından da türetilebilirdi. Yukarıdaki kodun çıktısı: <konum>1</konum> <sıcaklık>23</sıcaklık> <ağırlık>78</ağırlık> Değer parametrelerinin de varsayılan değerleri olabilir. Örneğin, herhangi boyutlu bir uzaydaki noktaları temsil eden bir yapı tasarlayalım. Noktaların koordinat değerleri için kullanılan tür ve uzayın kaç boyutlu olduğu, şablon parametreleri ile belirlensin: struct Konum(T, int boyut = 3) { T[boyut] koordinatlar; } boyut parametresinin varsayılan bir değerinin bulunması, bu şablonun o parametre belirtilmeden de kullanılabilmesini sağlar: Konum!double merkez; // üç boyutlu uzayda bir nokta

Gerektiğinde farklı bir değer de belirtilebilir: Konum!(int, 2) nokta; // iki boyutlu düzlemde bir nokta

73.3.3

Üye işlev şablonları için this parametreleri
Üye işlevler de şablon olarak tanımlanabilirler. Üye işlev şablonlarının da tür ve değer parametreleri bulunabilir, ve normal işlev şablonlarından beklendiği gibi çalışırlar. Ek olarak; üye işlev şablonlarının parametreleri, this anahtar sözcüğü ile de tanımlanabilir. Bu durumda, o anahtar sözcükten sonra yazılan isim, o nesnenin this referansının türü haline gelir. (Not: Burada, çoğunlukla kurucu işlevler içinde gördüğümüz this.üye = değer kullanımındaki this referansından, başka bir deyişle nesnenin kendisini ifade eden referanstan bahsediyorum.) struct BirYapı { void birİşlev(this KendiTürüm)() const { writeln("Bu nesnenin türü: ", typeid(KendiTürüm)); } } O üye işlev şablonunun KendiTürüm parametresi, nesnenin asıl türüdür: auto değişebilen = BirYapı(); const sabit = BirYapı(); immutable değişmez = BirYapı(); değişebilen.birİşlev(); sabit.birİşlev(); değişmez.birİşlev();

Copyright © 2009-2011 Ali Çehreli, http://ddili.org

469

Ayrıntılı Şablonlar

KendiTürüm ; nesnenin const , immutable , vs. tür belirteçlerine de sahiptir: Bu nesnenin türü: deneme.BirYapı Bu nesnenin türü: const(deneme.BirYapı) Bu nesnenin türü: immutable(deneme.BirYapı)

73.3.4

alias parametreleri
alias şablon parametrelerine karşılık olarak D programlarında geçebilen bütün yasal sözcükler kullanılabilir. Bu sözcükler evrensel isimler, yerel isimler, modül isimleri, başka şablon isimleri, vs. olabilirler. Tek koşul, o parametrenin şablon içindeki kullanımının o parametreye uygun olmasıdır. Örnek olarak, hangi yerel değişkeni değiştireceği kendisine bir alias parametre olarak bildirilen bir yapıya bakalım: struct BirYapı(alias değişken) { void birİşlev(int değer) { değişken = değer; } } O yapının üye işlevi, değişken isminde bir değişkene bir atama yapmaktadır. O değişkenin programdaki hangi değişken olduğu; bu şablon tanımlandığı zaman değil, şablon kullanıldığı zaman belirlenir: int x = 1; int y = 2; auto nesne = BirYapı!x(); nesne.birİşlev(10); writeln("x: ", x, ", y: ", y); Yapı şablonunun yukarıdaki kullanımında yerel x değişkeni belirtildiği için, birİşlev içindeki atama, onu etkiler: x: 10, y: 2 Şablon parametresi olarak y belirtildiğinde de o değişken değişir: auto nesne = BirYapı!y(); nesne.birİşlev(10); writeln("x: ", x, ", y: ", y); Bu sefer y değişmiştir: x: 1, y: 10 Başka bir örnek olarak, alias parametresini işlev olarak kullanan bir işlev şablonuna bakalım: void çağıran(alias işlev)() { write("çağırıyorum: "); işlev(); }

Copyright © 2009-2011 Ali Çehreli, http://ddili.org

470

Ayrıntılı Şablonlar

() parantezlerinden anlaşıldığı gibi, çağıran ismindeki işlev şablonu, kendisine verilen parametreyi bir işlev olarak kullanmaktadır. Ayrıca; parantezlerin içinin boş olmasından anlaşıldığı gibi, o işlev parametre göndermeden çağrılmaktadır. Parametre almadıkları için o kullanıma uyan iki de işlev bulunduğunu varsayalım: void birinci() { writeln("birinci"); } void ikinci() { writeln("ikinci"); } O işlevler, çağıran şablonu içindeki kullanıma uydukları için o şablonun alias parametresinin değeri olabilirler: çağıran!birinci(); çağıran!ikinci(); Belirtilen işlevin çağrıldığını görürüz: çağırıyorum: birinci çağırıyorum: ikinci alias şablon parametrelerini her çeşit şablonla kullanabilirsiniz. Önemli olan, o parametrenin şablon içindeki kullanıma uymasıdır. Örneğin, yukarıdaki alias parametresi yerine bir değişken kullanılması derleme hatasına neden olacaktır: int değişken; çağıran!değişken();

// ← derleme HATASI

Aldığımız hata, () karakterlerinden önce bir işlev beklendiğini, int türündeki değişken 'in uygun olmadığını belirtir: Error: function expected before (), not değişken of type int Her ne kadar işaretlediğim satır nedeniyle olsa da, derleme hatası aslında çağıran işlevinin içindeki işlev() satırı için verilir. Derleyicinin gözünde hatalı olan; şablona gönderilen parametre değil, o parametrenin şablondaki kullanılışıdır. Uygunsuz şablon parametrelerini önlemenin bir yolu, şablon kısıtlamaları tanımlamaktır. Bunu aşağıda göreceğiz.
73.3.5

Çokuzlu parametreleri
İşlevlerin belirsiz sayıda parametre alacak şekilde tanımlanabildiklerini biliyoruz. Örneğin writeln işlevini istediğimiz sayıda parametre ile çağırabiliriz. Bu tür işlevlerin nasıl tanımlandıklarını Parametre Serbestliği dersinde görmüştük. Aynı serbestlik şablon parametrelerinde de bulunur. Şablon parametrelerinin sayısını ve çeşitlerini serbest bırakmak, şablon parametre listesinin en sonuna bir çokuzlu ismi ve ... karakterleri yazmak kadar basittir. Bunun bir örneğini, bir işlev şablonunda görelim:

Copyright © 2009-2011 Ali Çehreli, http://ddili.org

471

Ayrıntılı Şablonlar

void bilgiVer(alias işlev, Çokuzlu ...)(Çokuzlu parametreler) { // ... } Yukarıdaki işlev şablonunun ilk parametresini alias olarak seçtim. Onun yerine başka çeşit bir şablon parametresi de olabilirdi, ve yanında başka şablon parametreleri de bulunabilirdi. İsminden anlaşıldığı gibi, o alias parametresinin işlev olarak kullanılacağı düşünülmektedir. O parametreden sonra yazılan Çokuzlu ... , bilgiVer işlev şablonunun belirsiz sayıda parametre ile çağrılabilmesini sağlar. O çokuzlu, belirsiz işlev parametrelerinin hepsini birden içerir. Örneğin o işlev şablonunun şu şekilde çağrıldığını düşünelim: bilgiVer!writeln(1, "abc", 2.3); Şablona açıkça verilen writeln parametresi, şablonun tanımında kullanılan alias 'ın değeri olur. İşleve gönderilen bütün parametreler de parametreler isimli çokuzluyu oluştururlar. O çokuzlunun parçalarına da şablon içinde örneğin bir foreach ile erişilebilir: void bilgiVer(alias işlev, Çokuzlu ...)(Çokuzlu parametreler) { foreach (i, parametre; parametreler) { işlev(i, ": ", typeid(parametre), " türünde ", parametre); } } Çıktısı: 0: int türünde 1 1: immutable(char)[] türünde abc 2: double türünde 2.3 alias olan ilk şablon parametresi, bilgiVer 'in kendi işini yaparken hangi işlevden yararlanacağını belirtmektedir. Örneği devam ettirmek için, writeln yerine kendi yazdığımız bir işlevden yararlanalım. işlev 'in şablon içinde beş parametre ile çağrıldığını görüyoruz. O kullanıma uyduğu sürece, alias parametresi olarak herhangi bir işlevi belirtebiliriz. O beş parametreden bazılarını gözardı eden, ve yalnızca türü ve değeri küme parantezleri arasında yazdıran şu işlev şablonuna bakalım: void yalnızcaTürVeDeğer(TürBilgisi, Tür)(int sayaç, string dizgi1, TürBilgisi türİsmi, string dizgi2, Tür değer) { write('{', türİsmi, ' ', değer, '}'); } bilgiVer içindeki işlev çağrısına uyduğu için, bilgiVer 'i bu yeni işlevle de kullanabiliriz: bilgiVer!yalnızcaTürVeDeğer(1, "abc", 2.3); Sonuçta da o üç parametrenin yalnızca türlerini ve değerlerini küme parantezleri arasında elde ederiz:

Copyright © 2009-2011 Ali Çehreli, http://ddili.org

472

Ayrıntılı Şablonlar

{int 1} {immutable(char)[] abc} {double 2.3} Aynı amaca ulaşmak için başka yollar bulunduğunu biliyorsunuz. Örneğin bilgiVer yerine türVeDeğerBildir gibi daha kısa bir şablon da yazabilirdik. Ben, çokuzlu parametrelerinden önceki parametrelerin nasıl kullanıldıklarını göstermek için bir alias parametresi seçtim.

73.4

Şablon özellemeleri
Özellemeleri de Şablonlar dersinde anlatmıştım. Aşağıdaki meta programlama başlığında da özelleme örnekleri göreceksiniz. Tür parametrelerinde olduğu gibi, başka çeşit şablon parametreleri de özellenebilir. Örneğin değer parametreleri: void birİşlev(int birDeğer)() { // ... genel tanımı ... } void birİşlev(int birDeğer : 0)() { // ... sıfıra özel tanımı ... }

73.5

Meta programlama
Kod üretme ile ilgili olmaları nedeniyle, şablonlar diğer D olanaklarından daha üst düzeyde programlama araçları olarak kabul edilirler. Şablonlar bir anlamda, kod oluşturan kodlardır. Kodların daha üst düzey kodlarla oluşturulmaları kavramına meta programlama denir. Şablonların derleme zamanı olanakları olmaları, normalde çalışma zamanında yapılan işlemlerin derleme zamanına taşınmalarına olanak verir. (Not: Aynı amaçla Derleme Zamanında İşlev İşletme (CTFE) olanağı da kullanılabilir. Bu konuyu daha sonraki bir derste göstereceğim.) Şablonların bu amaçla derleme zamanında işletilmeleri, çoğunlukla özyineleme üzerine kuruludur. Bunun bir örneğini görmek için 0'dan başlayarak belirli bir sayıya kadar olan bütün sayıların toplamını hesaplayan normal bir işlev düşünelim. Bu işlev, parametre olarak örneğin 4 aldığında 0+1+2+3+4'ün toplamını döndürsün: int topla(int sonDeğer) { int sonuç; foreach (değer; 0 .. sonDeğer + 1) { sonuç += değer; } } return sonuç;

Aynı işlevi özyinelemeli olarak da yazabiliriz:

Copyright © 2009-2011 Ali Çehreli, http://ddili.org

473

Ayrıntılı Şablonlar

int topla(int sonDeğer) { return (sonDeğer == 0 ? sonDeğer : sonDeğer + topla(sonDeğer - 1)); } Özyinelemeli işlev; kendi düzeyindeki değeri, bir eksik değerli hesaba eklemektedir. İşlevde 0 değerinin özel olarak kullanıldığını görüyorsunuz; özyineleme zaten onun sayesinde sonlanmaktadır. İşlevlerin normalde çalışma zamanı olanakları olduklarını biliyoruz. topla 'yı çalışma zamanında gerektikçe çağırabilir ve sonucunu kullanabiliriz: writeln(topla(4)); Aynı sonucun yalnızca derleme zamanında gerektiği durumlarda ise, o hesap bir işlev şablonuyla da gerçekleştirilebilir. Yapılması gereken; değerin işlev parametresi olarak değil, şablon parametresi olarak kullanılmasıdır: // Uyarı: Bu kod yanlıştır int topla(int sonDeğer)() { return (sonDeğer == 0 ? sonDeğer : sonDeğer + topla!(sonDeğer - 1)()); } Bu şablon da hesap sırasında kendisinden yararlanmaktadır. Kendisini, sonDeğer 'in bir eksiği ile kullanmakta ve hesabı yine özyinelemeli olarak elde etmeye çalışmaktadır. Ne yazık ki o kod yazıldığı şekilde çalışamaz. Derleyici, ?: işlecini çalışma zamanında işleteceği için, yukarıdaki özyineleme derleme zamanında sonlanamaz: writeln(topla!4()); // ← derleme HATASI

Derleyici, aynı şablonun sonsuz kere dallandığını anlar ve bir hata ile sonlanır: Error: template instance deneme.topla!(-296) recursive expansion Şablon parametresi olarak verdiğimiz 4'ten geriye doğru -296'ya kadar saydığına bakılırsa, derleyici şablonların özyineleme sayısını 300 ile sınırlamaktadır. Meta programlamada özyinelemeyi kırmanın yolu, şablon özellemeleri kullanmaktır. Bu durumda, aynı şablonu 0 değeri için özelleyebilir ve özyinelemenin kırılmasını bu sayede sağlayabiliriz: // Sıfırdan farklı değerler için kullanılan tanım int topla(int sonDeğer)() { return sonDeğer + topla!(sonDeğer - 1)(); } // Sıfır değeri için özellemesi int topla(int sonDeğer : 0)() { return 0; }

Copyright © 2009-2011 Ali Çehreli, http://ddili.org

474

Ayrıntılı Şablonlar

Derleyici, sonDeğer 'in sıfırdan farklı değerleri için hep genel tanımı kullanır ve en sonunda 0 değeri için özel tanıma geçer. O tanım da basitçe 0 değerini döndürdüğü için özyineleme sonlanmış olur. O işlev şablonunu şöyle bir programla deneyebiliriz: import std.stdio; int topla(int sonDeğer)() { return sonDeğer + topla!(sonDeğer - 1)(); } int topla(int sonDeğer : 0)() { return 0; } void main() { writeln(topla!4()); } Şimdi hatasız olarak derlenecek ve 4+3+2+1+0'ın toplamını üretecektir: 10 Burada dikkatinizi çekmek istediğim önemli nokta, topla!4() işlevinin bütünüyle derleme zamanında işletiliyor olmasıdır. Sonuçta derleyicinin ürettiği kod, writeln 'e doğrudan 10 hazır değerini göndermenin eşdeğeridir: writeln(10); // topla!4()'lü kodun eşdeğeri

Derleyicinin ürettiği kod, 10 hazır değerini doğrudan programa yazmak kadar hızlı ve basittir. O 10 hazır değeri, yine de 4+3+2+1+0 hesabının sonucu olarak bulunmaktadır; ancak o hesap, şablonların özyinelemeli olarak kullanılmalarının sonucunda derleme zamanında işletilmektedir. Burada görüldüğü gibi, meta programlamanın yararlarından birisi, şablonların derleme zamanında işletilmelerinden yararlanarak normalde çalışma zamanında yapılmasına alıştığımız hesapların derleme zamanına taşınabilmesidir. Yukarıda da söylediğim gibi, daha sonraki bir derste göstereceğim CTFE olanağı, bazı meta programlama yöntemlerini D'de gereksiz hale getirir.

73.6

Derleme zamanı çok şekilliliği
Bu kavram, İngilizce'de "compile time polymorphism" olarak geçer. Nesne yönelimli programlamada çok şekilliliğin sınıf türetme ile sağlandığını biliyorsunuz. Örneğin bir işlev parametresinin bir arayüz olması, o parametre yerine o arayüzden türemiş her sınıfın kullanılabileceği anlamına gelir. Daha önce gördüğümüz bir örneği hatırlayalım: import std.stdio; interface SesliAlet { string ses();

Copyright © 2009-2011 Ali Çehreli, http://ddili.org

475

yukarıdaki sesliAletKullan işlevi bir şablon olarak yazıldığında. Parametresi SesliAlet olduğu için. bazı işlemler ..org 476 ...Ayrıntılı Şablonlar } class Keman : SesliAlet { string ses() { return "♩♪♪".. başka işlemler .. Şablonlar bütünüyle derleyicinin derleme zamanındaki kod üretmesiyle ilgili olduklarından. her iki çok şekillilik de bütün türlerle kullanılamaz. http://ddili. Yukarıdaki son cümlede geçen bütün türlerle kullanılabilme kavramını şablonlardan da tanıyoruz. Çalışma zamanı çok şekilliliği. (Not: Eğer tanımlanmışsa. bazı işlemler .. Şablon parametresi. Şablon kısıtlamalarını biraz aşağıda anlatacağım. Derleme zamanı çok şekilliliği ise şablon içindeki kullanıma uyma ile kısıtlıdır.. sesliAletKullan(new Çan). belirli bir arayüzden türeme ile kısıtlıdır. } } Copyright © 2009-2011 Ali Çehreli. Doğrusu... ses üye işlevi bulunan bütün türlerle kullanılabilir: void sesliAletKullan(T)(T alet) { // .. şablon kısıtlamalarına da uyması gerekir. } Yukarıdaki sesliAletKullan işlevi çok şekillilikten yararlanmaktadır... } class Araba { string ses() { return "düt düt".. } void main() { sesliAletKullan(new Keman).) Örneğin. } } class Çan : SesliAlet { string ses() { return "çın". o şablonla kullanılabilir. Böyle düşününce... şablon içindeki kullanımda derleme hatasına neden olmuyorsa. writeln(alet. şablonların sundukları çok şekilliliğe derleme zamanı çok şekilliliği denir.ses()).. Her ikisinde de türlerin uymaları gereken bazı koşullar vardır. şablonların da bir çeşit çok şekillilik sunduklarını görürüz. ondan türemiş olan bütün türlerle kullanılabilir. } } void sesliAletKullan(SesliAlet alet) { // .ses()). // . başka işlemler . // . writeln(alet.

Yukarıdaki şablon. Programda yüz farklı tür ile çağrıldığını düşünürsek. şablonun farklı parametrelerle her kullanımı için farklı kod üretir.Ayrıntılı Şablonlar // . şablon içindeki kullanıma işaret eder: void sesliAletKullan(T)(T alet) { Copyright © 2009-2011 Ali Çehreli.. sesliAletKullan(new Keman).. derleyici tarafından işlev göstergeleriyle sağlanır. İşlev tek olduğu halde her hayvanın kendisine özel olarak davranabilmesi.7 Kod şişmesi Şablonlar kod üretme ile ilgilidirler. şablon bloğuna işaret eder. şablonların bu özelliğinin akılda tutulması gerekir... toplam yüz kere oluşturacaktır.. sesliAletKullan işlevinin ilk yazdığımız SesliAlet alan tanımında. böyle bir kod tekrarı yoktur. o işlevi bir kere derler ve her SesliAlet türü için aynı işlevi çağırır. yani şablon olmayan tanımında.. Öte yandan. Burada sözünü ettiğim hız etkilerini tasarımlarınızda fazla ön planda tutmayın. 73. Çalışma zamanında çok küçük bir hız kaybına yol açsa da. // ← uyumsuz bir tür Oradaki hata. bu uyumsuzluk ancak şablonun kendi kodları derlenirken farkedilebilir. http://ddili. sesliAletKullan(new Çan). Uyumsuz bir parametre kullanıldığında. Derleyici.org 477 . çalışma zamanında fazladan işlemler yapılması da hızı düşürecektir. Çoğu programda sorun oluşturmasa da. Belirli bir programda hangisinin etkisinin daha fazla olduğuna ancak o program denenerek karar verilebilir. sesliAletKullan(new Fincan). diğerleriyle kalıtım ilişkisi bulunmayan Araba türü ile de kullanılabilmiştir. 73. her tür için ayrı ayrı. Oysa derleme hatası. Bu yüzden. işlev göstergeleri kullanmanın çoğu programda önemi yoktur ve zaten bu çözümü sunan en hızlı gerçekleştirmedir.. Program boyutunun artması da. şablonun uyumsuz bir türle çağrılıyor olmasıdır. derleyici o işlev şablonunun tanımını.8 Şablon kısıtlamaları Şablonların her tür ve değerdeki şablon parametresi ile çağrılabiliyor olmalarının getirdiği bir sorun vardır. Örneğin yukarıda en son yazdığımız sesliAletKullan işlev şablonu. } // .. ses() işlevi yok . Derleyici. programda kullanıldığı her tür için ayrı ayrı üretilir ve derlenir. sesliAletKullan(new Araba). Yukarıdaki sesliAletKullan şablonunu ses isminde üye işlevi bulunmayan bir türle çağıralım: class Fincan { // . Programın boyutunun büyümesine neden olduğu için bu etkiye kod şişmesi (code bloat) denir. derleme hatasında belirtilen satır numarası. Derleyici her tür için farklı bir işlev göstergesi kullanır ve böylece her tür için farklı üye işlev çağrılır.

max: %s)".org 478 . writeln(). // ← derleme HATASI Bunun bir sakıncası.. (Not: Aynı işin şablon kullanmadan. Böyle bir sorunun arayüzlerde bulunmadığına dikkat edin.ses()). şablon bloğunun hemen öncesine yazılan if deyiminin içindeki mantıksal ifadelerdir: void birŞablon(T)() if (/* . kısıtlama koşulu .. Bunun bir örneği olarak meta programlama yoluyla faktöriyel hesaplayan bir şablona ve onun 0 değeri için özellemesine bakalım: import std. } Derleyici bu şablon tanımını ancak kısıtlama koşulu true olduğunda göze alır. ancak o arayüzden türemiş olan türlerle çağrılabilir. normal işlevlerle de gerçekleştirilebileceğini hatırlatmak istiyorum. } Yukarıdaki program.Ayrıntılı Şablonlar } // . */) { // . belki de bir kütüphane modülünde tanımlı olan bir şablona işaret edilmesinin. writefln("21! == %25s". Yukarıdaki şablonlar. ulong faktöriyel(ulong değer)() { return değer * faktöriyel!(değer . ulong. hatanın o kütüphanede olduğu yanılgısını uyandırabileceğidir. Daha önemlisi.. başka işlemler . asıl hatanın hangi satırda olduğunun hiç bildirilmiyor olmasıdır. faktöriyel!19())... } void main() { writefln("19! == %25s".... O durumda derleme hatası.. işlevin uyumsuz bir türle çağrılması olanaksızdır. faktöriyel!21()). writefln("(ulong.. 19.. 21 ve daha büyük değerlerin faktöriyelini yanlış hesaplar: Copyright © 2009-2011 Ali Çehreli.max).. Parametre olarak arayüz alacak şekilde yazılmış olan bir işlev. faktöriyel!20()). Yukarıdaki kod. writefln("(doğru 21!: 51090942171709440000)").. } ulong faktöriyel(ulong değer : 0)() { return 1. ve 21 değerlerinin faktöriyelini hesaplamaktadır. sonuçların derleme zamanında hesaplanmalarını ve sanki kaynak koda örneğin doğrudan 121645100408832000 yazılmış gibi derlenmelerini sağlarlar. Türeyen her tür arayüzün işlevlerini gerçekleştirmek zorunda olduğu için. Şablon kısıtlamaları. http://ddili.. çünkü 21'in faktöriyelinin ulong 'a sığmadığını biliyorum.stdio.. Koşulun false olduğu durumda ise bu şablon tanımını gözardı eder. bazı işlemler . // .) O programdaki değerleri özel olarak seçtim. şablonun tanımı . writefln("20! == %25s".1)(). Şablonların yalnızca belirli koşulları sağlayan türlerle kullanılmaları şablon kısıtlamaları ile sağlanır. işlevi uygunsuz türle çağıran satıra işaret eder. writeln(alet. 20..

1 İsimli kısıtlama yöntemi Şablon kısıtlamaları bazı durumlarda yukarıdakinden çok daha karmaşık olabilirler. Bunun örneklerini aşağıda göstereceğim. O değer ulong 'a sığmadığı için taşmıştır..709. nesne. Şablonlar derleme zamanı olanakları oldukları için.org 479 . şablonu ancak o koşula uyan değerler için göze alacaktır: writefln("21! == %25s". // .kon(). Koşullu Derleme dersinde gördüğümüz ve derleme zamanında işletildiğini öğrendiğimiz is ifadesi ile de çok kullanılırlar. http://ddili.Ayrıntılı Şablonlar 19! == 20! == 21! == 121645100408832000 2432902008176640000 14197454024290336768 (ulong. faktöriyel şablonunun geçersiz değerlerle kullanılması ve bu yüzden yanlış sonuçlar üretmesi bir şablon kısıtlaması ile engellenebilir: ulong faktöriyel(ulong değer)() if (değer <= 20) { return değer * faktöriyel!(değer ..171.. // .. 73. typeof .. } Derleyici. faktöriyel!21()). is ifadesi. // . ve tek tanım içeren şablonlardır...hazırlan(). isimsiz kapama.uç(42). benim isimli kısıtlama olarak adlandırdığım bir yöntemdir. Bu yüzden.1)()..max: 18446744073709551615) (doğru 21!: 51090942171709440000) 21'in faktöriyeli. o kullanıma karşılık bir şablon tanımı bulunmadığını bildirir: Error: template instance faktöriyel!(21) does not match any template declaration Böylece amacımıza ulaşmış oluruz. Bu yöntemi burada daha çok bir kalıp olarak göstereceğim ve her ayrıntısına girmemeye çalışacağım. Bu dört olanak. Parametresini belirli şekilde kullanan bir işlev şablonu olsun: void kullan(T)(T nesne) { // .000 değeridir. nesne. şablon kısıtlamaları da derleme zamanında işletilirler. 51.440.942.8. Bunun üstesinden gelmenin bir yolu. Artık derleme hatası şablonun yanlış parametre ile kullanıldığı satıra işaret etmektedir.090. } Copyright © 2009-2011 Ali Çehreli. nesne. Bu yöntem D'nin dört olanağından yararlanarak kısıtlamaya anlaşılır bir isim verir. // ← derleme HATASI Derleme hatası.

O işlevlerden uç 'un ayrıca int türünde bir de parametresi olmalıdır. Şimdilik. isimsiz bir kapama tanımlar.Ayrıntılı Şablonlar Şablon içindeki kullanımından anlaşıldığı gibi.hazırlan(). uçan. Copyright © 2009-2011 Ali Çehreli.kon().. uç . } O tanımı aşağıda biraz daha açmaya çalışacağım. Onun yerine.hazırlan(). is (typeof(T. } 1. Bu kısıtlamayı is ve typeof ifadelerinden yararlanarak şöyle yazabiliriz: void kullan(T)(T nesne) if (is (typeof(T. } // ← isimli kısıtlama Bu kısıtlama bir öncekinden daha açıktır.kon))) { // . Yukarıdaki gibi isimli kısıtlamalar şu kalıba uygun olarak tanımlanırlar: template uçabilir_mi(T) { const bool uçabilir_mi { T uçan.. Bu şablonun uçabilen türlerle çalıştığını okunaklı bir şekilde belgeler. uçan. Yukarıdaki gibi bir kısıtlama istendiği gibi çalışıyor olsa da.uç(1))) şeklinde bir de parametre verildiğinde ise. ve kon isminde üç üye işlevinin bulunması gerekir.hazırlan)) && is (typeof(T. bu şablonun kullanıldığı türlerin hazırlan . İsimsiz kapama: İsimsiz kapamaları İşlev Göstergeleri ve Kapamalar dersinde görmüştük.. uçan. } = is (typeof( // uçmaya hazırlanabilmeli // belirli mesafe uçabilmeli // istendiğinde konabilmeli O yöntemde kullanılan D olanaklarını ve birbirleriyle nasıl etkileştiklerini çok kısaca göstermek istiyorum: template uçabilir_mi(T) { // (6) (5) (4) const bool uçabilir_mi = is (typeof( { // (1) T uçan. // (3) }())). "o işlev ek olarak o türden bir parametre de alıyorsa" diye kabul edebilirsiniz. http://ddili.uç(1).org 480 . İşleve is (typeof(T. Yukarıdaki küme parantezleri. her zaman için tam açık olmayabilir.hazırlan)) kullanımını bir kalıp olarak "eğer o türün hazırlan isminde bir üye işlevi varsa" diye kabul edebilirsiniz. o şablon kısıtlamasının ne anlama geldiğini daha iyi açıklayan bir isim verilebilir: void kullan(T)(T nesne) if (uçabilir_mi!T) { // .uç(1))) && is (typeof(T. }()))..kon().uç(1). // (2) uçan. uçan. uçan.

bir ifadenin eğer işletilse ne türden bir değer üreteceğini bildirir: int i = 42. Programın çıktısı ikinci satır için false değerini içerir: true false Bunun nedeni. diğeri uyumsuz iki türle çağırmayı deneyelim: Copyright © 2009-2011 Ali Çehreli. typeof(++i) j. typeof geçersiz bir tür döndürür. kendisine verilen türün anlamlı olduğu durumda true değerini üretiyordu: int i. asıl şablonda kullanıldığı gibi kullanır. // 'int j. yukarıdaki işletme bir typeof içinde olduğu için. 5. türünü ürettiği ifadeyi hiç bir şekilde işletmemesidir. bu şablon. Buradaki is (Tür) şeklindeki kullanımı. ve o tanımın ismi şablonun ismiyle aynı olduğu için. typeof ifadesi: typeof . typeof 'un ikinci kullanım için geçersiz bir tür üretmiş olmasıdır. yukarıdaki kullan işlev şablonunun kısıtlaması.) 3. ve o türün sahip olması gereken üç üye işlevi çağrılmaktadır. 6. Tek tanım içeren şablon: Bu derste anlatıldığı gibi. bütün bu olanaklar sayesinde kullanışlı bir isim edinmiş olur: void kullan(T)(T nesne) if (uçabilir_mi!T) { // . } O şablonu birisi uyumlu. typeof geçerli bir tür üretir. 4. yalnızca o ifadenin türünü üretmiş ve böylece j de int olarak tanımlanmıştır. Eğer uçabilir_mi şablonuna gönderilen tür. typeof void bile olmayan geçersiz bir tür döndürür.org 481 . is ifadesi: Koşullu Derleme dersinde is ifadesinin birden fazla kullanımını görmüştük. typeof 'un önemli bir özelliği. Kapama bloğu: Kapama bloğu.' yazmakla aynı şey // ++i işletilmemiştir Yukarıdaki assert 'ten de anlaşıldığı gibi. kendisine verilen ifadenin türünü üretir. içindeki tek tanım yerine geçebilir.Ayrıntılı Şablonlar 2. (Not: Bu kodlar typeof tarafından kullanılırlar ama hiçbir zaman işletilmezler. writeln(is (typeof(i))). derleyici hata vermez. normalde o kapamayı işletir. writeln(is (typeof(varOlmayanBirİsim))). Ancak. uçabilir_mi şablonunun içinde tek tanım bulunduğu için. Yukarıdaki blokta önce bu türden bir nesne oluşturulmakta. o isimsiz kapama içindeki kodlarda gösterildiği gibi derlenebiliyorsa. bu kapama hiçbir zaman işletilmez. İşte. Eğer o tür kapama içindeki kodlardaki gibi derlenemiyorsa. şimdiye kadarki örneklerde çok kullandığımız gibi. assert(i == 42).. Kapamanın işletilmesi: Bir kapamanın sonuna yazılan () parantezleri. kısıtlaması tanımlanmakta olan türü. typeof . http://ddili.. Eğer typeof 'a verilen ifadenin geçerli bir türü yoksa. typeof . ++i ifadesi işletilmemiştir. // true // false Yukarıdaki ikinci ifadede bilinmeyen bir isim kullanıldığı halde.

ve arayüz şablonları tanımlanabildiği gibi. yapı. alias . bu derleme hatası artık şablonun içine değil. http://ddili.9 Özet Önceki şablonlar dersinin sonunda şunları hatırlatmıştım: • şablonlar.Ayrıntılı Şablonlar // Şablondaki kullanıma uyan bir tür class ModelUçak { void hazırlan() {} void uç(int mesafe) {} void kon() {} } // Şablondaki kullanıma uymayan bir tür class Güvercin { void uç(int mesafe) {} } // . 73. şablonun her farklı parametreli kullanımı için ayrı kod üretir. şablonun uyumsuz türle kullanıldığı satıra işaret eder.org 482 . birlik. bir şablon kısıtlaması tanımlanmış olduğu için. kodun kalıp halinde tarif edilmesini sağlarlar • bütünüyle derleme zamanında işleyen bir olanaktır • şablon parametreleri ünlem işaretinden sonra açıkça belirtilebilir. şablon kapsamı içinde her tür tanım da bulunabilir şablon parametrelerinin tür. aşırı olduğu durumda buna kod şişmesi denir olası derleme hatalarının şablonun yanlış kullanıldığı satıra işaret edebilmesi için şablon kısıtlamaları tanımlanabilir isimli kısıtlama yöntemi. ve çokuzlu çeşitleri vardır şablonlar. sınıf. // ← derlenir // ← derleme HATASI İsimli veya isimsiz. değer. parametrelerinin herhangi bir kullanımı için özellenebilirler meta programlama. işlemlerin derleme zamanında yapılmalarını sağlar şablonlar derleme zamanı çok şekilliliği olanaklarıdır derleyici. kullan(new ModelUçak). o tanım yerine geçer işlev. kullan(new Güvercin). kısıtlamalara okunaklı isimler vermeye yarar Copyright © 2009-2011 Ali Çehreli... tek parametre için parantez kullanmaya gerek yoktur • şablon parametreleri yalnızca işlev şablonlarında çıkarsanabilirler • şablon özellemeleri : karakteri ile belirtilir • varsayılan şablon parametre değerleri = karakteri ile belirtilir Bu derste de şu kavramları gördük: • • • • • • • • • • • şablonlar kestirme veya uzun söz dizimleriyle tanımlanabilirler şablon kapsamı bir isim alanı belirler içinde tek tanım bulunan şablon. this .

b: 2. // ← ne a ne de b etkilenir writefln("a: %s. sonuç += 10. b: %s. o değişkenin arttırılması. sonuç). a. normalde işlevi çağıran tarafa kopyalanırlar. } Derleyici dönüş türünü return satırından otomatik olarak çıkarsar. b). kendisine verilen iki parametreden büyük olanını döndürmektedir: int büyüğü(int birinci. yalnızca sonuç isimli kopyayı etkiler. int sonuç = büyüğü(a.1. sonuç: %s". } O işlevin hem parametreleri hem de dönüş değeri normalde kopyalanır: int a = 1.1. inout . Bunlar. Örneğin aşağıdaki işlev. ve auto ref olarak bildirilebilirler.org 483 . double ikinci) { double sonuç = birinci + ikinci.1 auto işlevler auto olarak bildirilen işlevlerin dönüş türlerinin açıkça yazılması gerekmez: auto topla(int birinci. int b = 2. 74.1 Dönüş türü olanakları İşlevler auto . büyüğü işlevinin dönüş değeri sonuç değişkenine kopyalandığı için. http://ddili. önceki derslerde yer almayan başka işlev olanaklarını anlatacağım. İşleve kendileri de zaten kopyalanarak geçirilmiş olan a ve b değişmezler: a: 1. ref belirteci. return sonuç.Diğer İşlev Olanakları 74 Diğer İşlev Olanakları İşlevleri daha önce aşağıdaki derslerde görmüştük: • • • • İşlevler İşlev Parametreleri İşlev Yükleme İşlev Göstergeleri ve Kapamalar Bu derste. b. o işlev sanki dönüş türü double yazılmış gibi derlenir. işlevlerin dönüş türleriyle ilgilidir. sonuç: 12 Copyright © 2009-2011 Ali Çehreli. 74. Yukarıdaki işlevin return ile döndürdüğü sonuç double olduğu için. int ikinci) { return (birinci > ikinci) ? birinci : ikinci. ref .2 ref işlevler İşlevlerin döndürdükleri değerler. 74. dönüş değerinin kopyalanmak yerine referans olarak döndürülmesini sağlar.

Bir başka deyişle. writefln("a: %s. sonuç += 10. http://ddili. b: 12 Yerel referans için gösterge gerekir: Burada bir noktaya dikkatinizi çekmek istiyorum. o ikisinin büyük olanını etkilerdi: a: 1. b: %s. b. a ve b 'den büyük olanını etkiler: a: 1. ref kullanılmış olmasına rağmen. O işlem. o dönüş değeri hep işlevin çağrıldığı noktada zaten yaşamakta olan a 'nın veya b 'nin referansıdır. b: 12. bir gösterge olarak tanımlanması gerekirdi: int * sonuç = &büyüğü(a. Aynı sözcük dönüş türü için de kullanılabilir ve işlevin dönüş değerinin de referans olarak döndürülmesini sağlar: ref int büyüğü(ref int birinci. a.org 484 . a. onun aracılığıyla yapılan değişiklik. daha işlev çağrılmadan önce yaşamaya başlayan iki değişkenden birisinin takma ismi gibi çalışmaktadır. birinci de döndürülse. büyüğü(a. b). // ← ya a ya b etkilenir writefln("a: %s. b: %s". *sonuç += 10. a veya b 'yi yine de değiştirmez: int sonuç = büyüğü(a. b). } Döndürülen referans. Dikkat ederseniz. Yaşam süresi işlevden çıkılırken sona erecek olan bir değişkene referans döndürülemez: ref string parantezİçinde(string söz) { string sonuç = '(' ~ söz ~ ')'. sonuç: 12 sonuç 'un a 'nın veya b 'nin referansı olması istenseydi. işlevin döndürdüğü referansı sonuç diye bir değişken kullanmadan doğrudan arttırıyoruz. b). b) += 10. *sonuç). o referans sonuç ismindeki yerel değişkene kopyalandığı için a veya b değişmez: a: 1. sonuç artık ya a 'ya ya da b 'ye erişim sağladığı için. ref int ikinci) { return (birinci > ikinci) ? birinci : ikinci. ikinci de döndürülse. sonuç: %s". // ← yine yalnızca 'sonuç' değişir büyüğü işlevi a 'ya veya b 'ye referans döndürüyor olsa da. parametrelerden birisinin takma ismi yerine geçecek ve onda yapılan değişiklik artık ya a 'yı ya da b 'yi değiştirecektir: int a = 1. b: 2. int b = 2. Copyright © 2009-2011 Ali Çehreli. o dönüş değerinin yerel bir değişkene atanması. sonuç: 12 Yerel değişkene referans döndürülemez: Yukarıdaki ref dönüş değeri.Diğer İşlev Olanakları Parametrelerin kopyalanmak yerine referans olarak gönderilmeleri için ref anahtar sözcüğünün kullanıldığını biliyorsunuz.

immutable olmayan bir dizgi ile çağrılamaz: char[] dizgi. yani immutable(char)[] olduğu için. kopyalanarak döndürülür.4 inout işlevler Bu belirteç işlev parametrelerinde ve dönüş türünde kullanılır.1. auto işlevlerde olduğu gibi otomatik olarak çıkarsanır. O yüzden.org 485 . Yukarıdaki işlevi string alacak ve string döndürecek şekilde tekrar yazalım: string parantezİçinde(string söz) { return '(' ~ söz ~ ')'. Aynı işlevi auto ref olarak yazdığımızda program derlenir: auto ref parantezİçinde(string söz) { string sonuç = '(' ~ söz ~ ')'. auto ref . http://ddili. // derlenir } Döndürülen değerin referans olabildiği durumlarda ise o değişkene referans döndürülür. } O işleve string türünde parametre verilmesi gerektiğini. writeln(parantezİçinde(dizgi)). o kod derlenir ve çalışır: (merhaba) Burada kullanışsız bir durum vardır. referans olamayacak bir değer döndürüldüğünde. Derleme. const . 74. Ek olarak. O işlev string türüne bağlı olarak yazıldığı için. // elemanları değişebilir dizgi ~= "selam". o değer referans olarak değil. veya immutable anlamına gelir. return sonuç. ve sonucunun da string olduğunu biliyoruz: writeln(parantezİçinde("merhaba")). yerel değişkene referans döndürüldüğünü bildiren bir hata ile sonlanır: Error: escaping reference to local variable sonuç 74.1. Copyright © 2009-2011 Ali Çehreli. auto ref olarak bildirilmiş olan bir işlevin dönüş türü.Diğer İşlev Olanakları return sonuç. o işlevden çıkıldığında sona erer. öyle durumlarda yararlıdır. ve o işlevin parametrelerine bağlı olarak değişebilen. "merhaba" hazır değerinin türü string . } // ← sonuç'un yaşamı burada sona erer İşlev kapsamında tanımlanmış olan sonuç 'un yaşamı. o değişkenin takma ismi gibi kullanılacak bir dönüş değeri olamaz.3 auto ref işlevler Yukarıdaki parantezİçinde işlevinin yaşam süresi sona eren yerel değişken nedeniyle derlenemediğini gördük.

const(char)[] sabit. o değişikliklerin üçünde de yapılmasının unutulmaması gerekecektir. } O çözüm şablon içindeki kullanıma uyan her tür ile kullanılabilir. işlevi şablon olarak tanımlamaktır: T parantezİçinde(T)(T söz) { return '(' ~ söz ~ ')'. Bunun bazen fazla esnek olabileceğini ve şablon kısıtlamaları kullanılmasının gerekebileceğini de önceki derste görmüştük. Örneğin parametrelerin daha doğru olarak inout(char)[] olarak yazılmaları derleme hatasına neden oluyor. writeln(typeid(parantezİçinde(değişebilen))).Diğer İşlev Olanakları Derleme hatası. işlevin parametrelerinden otomatik olarak çıkarsar: inout(char[]) parantezİçinde(inout(char[]) söz) { return '(' ~ söz ~ ')'. işlevin dönüş türünü yazdırarak görebiliriz: char[] değişebilen. O özelliği. Başka bir yöntem. inout . değişebilen karakterlerden oluşan char[] türünün string 'e dönüştürülemeyeceğini bildirir: Error: cannot implicitly convert expression (dizgi) of type char[] to string Aynı sorun. http://ddili. O yüzden bu riskli bir tasarımdır. } const(char)[] parantezİçinde(const(char)[] söz) { return '(' ~ söz ~ ')'. Copyright © 2009-2011 Ali Çehreli. veya immutable özelliğini esnek bırakır. işlevi değişebilen ve const karakter dizileri için de yüklemektir: char[] parantezİçinde(char[] söz) { return '(' ~ söz ~ ')'. Bu sorunu çözmek için bir kaç yöntem düşünülebilir. } Bunun programcılıkta kaçınılması gereken kod tekrarı anlamına geldiğini görüyoruz. O işlev char[] türüyle çağrıldığında. Bir yöntem. Bunu. ama bütün türü değil. immutable(char)[] veya const(char)[] türleriyle çağrıldığında ise. inout . sırasıyla immutable veya const yerine geçer. yalnızca türün değişebilen. inout yöntemi şablon çözümüne çok benzer. const(char)[] dizgilerinde de vardır. hiç inout yazılmamış gibi derlenir. const .org 486 . } Not: dmd'nin bu dersi yazdığım sırada kullandığım 2.047 sürümünde inout ile ilgili hatalar var. parametreden otomatik olarak çıkarsanan özelliği dönüş türüne de aktarır. Bu işlevlerin ileride gelişebileceklerini veya olası hatalarının giderilebileceklerini düşünürsek.

74. Öyle bir işlev. const . http://ddili. Örneğin yukarıdaki gibi bir writeln ifadesi programdaki bir hatanın giderilmesi sırasında geçici olarak eklenmiş olabilir. Programcılıkta değer üretmenin yan etki oluşturmaktan bir çok açıdan daha iyi olduğu kabul edilir. inout . veya immutable özelliğini dönüş türüne aktarır. belirli parametre değerlerine karşılık hep aynı dönüş değerini üretecektir. pure olarak bildirilmiş olan topla 'nın da yan etkili olmasına neden olacaktır. 74. Yan etki üretmeyen böyle işlevler daha kolay yazılırlar ve test edilirler. İşlevlerin sonuçlarının programın belirli bir andaki genel durumundan bağımsız olması ve programın genel durumunu değiştirmemesi yeğlenir. pure olmayan writeln 'in çağrılamayacağını bildirir: Error: pure function 'topla' cannot call impure function 'writeln' Öte yandan. bazı fonksiyonel programlama dillerinde işlevlerin yan etki üretmeleri olanaksızdır. Derleme hatası. o işlevin tutarlı ve güvenli olarak çalışmasına yardım eder. parametre türünün değişebilme.1 pure işlevler İşlevlerin değer üretebildiklerini veya yan etki oluşturabildiklerini biliyoruz.Diğer İşlev Olanakları writeln(typeid(parantezİçinde(sabit))). Üç çağrının farklı dönüş türleri: char[] const(const(char)[]) immutable(immutable(char)[]) Özetle. Bu çok önemli bir konu olarak görüldüğü için. Çağrılabilse. immutable(char)[] değişmez. Bu gibi durumlarda yararlı olmak amacıyla.2. debug olarak işaretlenmiş olan ifadeler ve bloklar saf olmasalar bile pure işlevlerde kullanılabilirler: Copyright © 2009-2011 Ali Çehreli. Bir işlevin sonucunun programın genel durumundan bağımsız ve bütünüyle giriş parametrelerine bağlı olması.2 Davranış olanakları pure ve nothrow . pure işlevlerin bu kadar kısıtlı olmaları bazen istenmez. } writeln yan etkisi olan bir işlevdir çünkü programın standart çıkışında değişikliğe neden olur. Bunları İşlevler dersinde görmüştük. Böyle programlama dillerinin "saf" anlamına gelen "pure" olduğu söylenir. işlevlerin genel davranışlarıyla ilgilidir. onları pure olarak bildirmektir. pure işlevlerin programda evrensel değişiklikler yapmalarına izin vermez: pure int topla(int birinci. writeln(typeid(parantezİçinde(değişmez))).org 487 . İşlevlerin saflığı konusunda derleyiciden yararlanmanın yolu. // ← derleme HATASI return birinci + ikinci. O yüzden yukarıdaki işlev derlenemez. Derleyici. int ikinci) { writeln("topluyorum").

kesinlikle hata atmadıklarını bilmek isteriz. nothrow . topla 'nın hata atabileceğini bildirir: Copyright © 2009-2011 Ali Çehreli. } O işlev ne kendisi hata atabilir ne de hata atabilen bir işlevi çağırabilir: nothrow int topla(int birinci. Genel bir kural olarak. pure olarak bildirilmiş olan bütün işlevlerin uyması gereken koşul şudur: • Programın evrensel durumu ile ilgili hiçbir bilgi edinemez.2 nothrow işlevler D'nin hata düzeneğini Hata Atma ve Yakalama dersinde görmüştük.) Bazı durumlarda ise çağırdığımız işlevlerin ne tür hatalar attıklarını değil..Diğer İşlev Olanakları pure int topla(int birinci. o adımlar sırasında hata atılmadığından emin olmak zorundadırlar. bütün hataların dolaylı da olsa Exception 'dan türemiş oldukları varsayılabilir ve tek catch bloğu ile bütün hatalar yakalanabilir. İşlevin programın genel durumuna bağlı olması veya o durumu değiştirmesi bu sayede önlenir. } Yukarıdaki garantilerin sağlanabilmesi için derleyici pure işlevlerden pure olmayan işlevlerin çağrılmalarına izin vermez. Yarı saf işlevler ise parametrelerinde değişiklik yapabilirler: // yarı saf (weakly pure) pure void eksilt(ref int değer) { --değer. int ikinci) { // .. parametrelerinde değişiklik yaparak yan etki oluşturmaları da böylece önlenmiş olur. Her işlevin hangi durumlarda hangi hataları atabileceği o işlevin belgesinde belirtilmelidir. // ← derleme HATASI return birinci + ikinci. Belirli adımlarının kesintisiz olarak devam etmesi gereken bazı algoritmalar. aşağıdaki koşulun sağlanıp sağlanmamasına göre işlev tam saf (strongly pure) veya yarı saf (weakly pure) olarak anılır: • Tam saf işlevlerin bütün parametreleri ya immutable 'dır ya da otomatik olarak immutable 'a dönüşebilir. int ikinci) { debug writeln("topluyorum"). // ← şimdi derlenir return birinci + ikinci. int ikinci) { writeln("topluyorum"). Ek olarak. (Not: Error sıradüzeninden türemiş olan hataların ve en genel hata türü olan Throwable türünün yakalanmasının önerilmediğini yine o derste görmüştük. veya o durumu değiştiremez. } Not: Bu sefer programın -debug seçeneği ile derlenmesi gerekir. işlevin hata atmadığını garanti eder: nothrow int topla(int birinci.org 488 . 74.2. http://ddili. Tam saf işlevlerin. } Derleme hatası.

"Güvenli" anlamına gelen @safe işlevler. Copyright © 2009-2011 Ali Çehreli. derleyici @safe işlevlerde şu işlemlere ve olanaklara izin vermez: • • • • • • • • • • • • • göstergeler void* dışındaki gösterge türlerine dönüştürülemezler gösterge olmayan bir tür. işlevlerin kesinlikle hata atmayacaklarını da anlayabilir. int ikinci) { int sonuç. Bu hatalar genellikle göstergelerin yanlış kullanılmaları ve güvensiz tür dönüşümleri sonucunda oluşur.3 Güvenlik olanakları @safe . topla 'nın aşağıdaki tanımında her tür hata yakalandığı için. http://ddili..Diğer İşlev Olanakları Error: function deneme.topla 'topla' is nothrow yet may throw Bunun nedeni. ve @system belirteçleri işlevlerin garanti ettikleri güvenliklerle ilgilidir. @trusted . ve işlev nothrow olmayan işlevleri bile çağırabilir: nothrow int topla(int birinci. 74. gösterge türüne dönüştürülemez göstergelerin değerleri değiştirilemez gösterge veya referans üyeleri bulunan birlikler kullanılamaz @system olarak bildirilmiş olan işlevler çağrılamaz Exception sınıfından türemiş olmayan bir hata yakalanamaz inline assembler kullanılamaz değişebilen değişkenler immutable 'a dönüştürülemezler immutable değişkenler değişebilen türlere dönüştürülemezler işletim dizilerinin yerel değişkenleri shared 'e dönüştürülemezler shared değişkenler iş parçacığının yerel değişkeni olacak şekilde dönüştürülemezler işlevlerin yerel değişkenlerinin veya parametrelerinin adresleri alınamaz __gshared değişkenlere erişilemez 74. nothrow 'un getirdiği garantiler geçerliliğini sürdürür. } catch (Exception hata) { // .org 489 . // derlenir sonuç = birinci + ikinci. writeln 'in nothrow olarak bildirilmiş bir işlev olmamasıdır.2 @trusted işlevler "Güvenilir" anlamına gelen @trusted olarak bildirilmiş olan işlevler.1 @safe işlevler Programcı hatalarının önemli bir bölümü. @safe olarak bildirilemeyecek oldukları halde tanımsız davranışa neden olmayan işlevlerdir.3. try { writeln("topluyorum"). } } return sonuç. Derleyici. // bütün hataları yakalar 74. Bazılarına bu derslerde hiç değinmemiş olsam da.3. belleği bozmayacağını garanti eden işlevlerdir. farkında olmadan belleğin yanlış yerlerine yazılması ve o yerlerdeki bilgilerin bu yüzden bozulmaları ile ilgilidir..

org 490 . çıktıya bir menü yazdıran bir işleve bakalım: import std. string[] seçenekler.3 @system işlevler @safe veya @trusted olarak bildirilmiş olmayan bütün işlevlerin @system oldukları varsayılır. Programcının derleyiciye "bu işleve güvenebilirsin" demesi gibidir. ve @trusted işlevlerin @safe işlevlerden çağrılmalarına izin verir.yazı. seçenek. Hem başlık satırını hem de seçenekleri belirli ölçüde işledikten sonra onları ortalanmışSatır isimli başka bir işleve göndermektedir. Yukarıdaki 1 + 2 işlemi derleme zamanında işletilir ve program doğrudan 3 yazılmış gibi derlenir. foreach (i. } } } return girinti ~ yazı ~ '\n'. Copyright © 2009-2011 Ali Çehreli. (genişlik . genişlik). int genişlik) { string sonuç = ortalanmışSatır("-= " ~ başlık ~ " =-". normal olarak çalışma zamanında işletildiklerini düşüneceğimiz işlevlerin bile derleme zamanında işletilmelerine olanak sağlar. http://ddili.4 Derleme zamanında işlev işletme Derleme zamanında yapılabilen hesaplar çoğu programlama dilinde oldukça kısıtlıdır. 0 . D'nin "compile time function execution (CTFE)" denen özelliği ise. Bu hesaplar genellikle sabit uzunluklu dizilerin uzunluklarını hesaplamak. 74.length < genişlik) { foreach (i. O işlevin oldukça karmaşık olduğunu düşünebiliriz. veya hazır değerler kullanan aritmetik işlemler yapmak kadar basittir: writeln(1 + 2).3.Diğer İşlev Olanakları Böyle işlevler. Derleyici programcının sözüne güvenir. @safe işlevlerin yasakladığı işlemleri yapıyor oldukları halde hatalı olmadıkları programcı tarafından garantilenen işlevlerdir.length) / 2) { girinti ~= ' '. } } return sonuç. if (yazı.". string menü(string başlık. int genişlik) { string girinti. seçenekler) { sonuç ~= ortalanmışSatır(". Derleyici böyle işlevlerin doğru veya güvenilir olduklarını düşünemez. 74.. Benzer karmaşıklığı ortalanmışSatır işlevinde de görüyoruz: string ortalanmışSatır(string yazı. genişlik). Örnek olarak. " ~ seçenek ~ " .stdio. o hesap için çalışma zamanında hiç zaman harcanmaz.

org 491 . işlevlerin gerçekten derleme zamanında işletilip işletilmediklerini görmek o kadar kolay olmayabilir. Bu. tatlıMenüsü değişkeni static olarak bildirildiği içindir. ortalanmışSatır işlevinden ve kendi işlemlerinden yararlanarak şu menüyü oluşturur: -= Tatlılar =. static değişkenler programda yalnızca bir kere ilklenirler. menü işlevi bütünüyle derleme zamanında işletilebilir. http://ddili. O dizginin tamamını görmenin başka bir yolu. derleyicinin davranışı ile ilgili olduğundan. Derleme zamanında oluşturulmuşsa. Bu olanak. [ "Baklava". bir dosya içindeki bütün okunaklı dizgileri bulur ve standart çıkışına gönderir.\n". Muhallebi . Dizginin gerçekten derleme zamanında oluşturulduğunu görmenin bir yolu. ve sonrasındaki 4 satırı gösterir: Copyright © 2009-2011 Ali Çehreli.\n" .\n" . içinde "Tatl" geçen satırı. } Yukarıdaki programdaki menü işlevi. Yukarıdaki programdaki işlevlerin derleme zamanında işletilmelerinin sonucunda. o dizginin oluşturulması için çalışma zamanında hiç zaman harcanmaz. 30). writeln(tatlıMenüsü). Baklava . menü gibi bir işlev çağrıldığı halde ve onun sonucunda işlemler yapıldığı halde. yukarıdaki sonucun bütünüyle derleme zamanında oluşturulmuş olmasıdır. Kadayıf . strings . bütün menü dizgisi. static değişkenlerin ilk değerlerinin derleme zamanında bilinmeleri gerekir. "Kadayıf". Unix ortamlarında bulunan strings ve grep programlarından yararlanmaktır. Baklava . Burada önemli olan. programı bir hex editör'de açmaktır. . "Muhallebi" ]. Örneğin static bir değişkenin ilklenmesi sırasında: void main() { static string tatlıMenüsü = menü("Tatlılar". grep ise. .Diğer İşlev Olanakları Bütün o karmaşıklığa rağmen. tatlıMenüsü değişkeni sanki o hesapların sonunda oluşan dizgi programa açıkça yazılmış gibi derlenir: // Yukarıdaki kodun eşdeğeri: static string tatlıMenüsü = " " " " -= Tatlılar =-\n" . derlenmiş olan programın içinde gömülü olarak görülür. Muhallebi . kullanımına bağlı olarak. Programın isminin deneme olduğunu varsayarsak. aşağıdaki komut. girişine gelen satırlar arasından belirli bir düzene uyanlarını seçer ve standart çıkışına gönderir. Kadayıf .

işlevin parametrelerinin uyması gereken koşullar: ◦ ◦ ◦ ◦ ◦ ◦ ◦ ◦ ◦ ◦ ◦ ◦ tamsayı hazır değerleri kesirli sayı hazır değerleri karakter hazır değerleri dizgi hazır değerleri bu listedeki elemanlardan oluşan dizi hazır değerleri bu listedeki elemanlardan oluşan eşleme tablosu hazır değerleri bu listedeki elemanlardan oluşan yapı hazır değerleri bu listedeki elemanlarla ilklenmiş olan sabit değişkenler kapamalar işlev göstergeleri isimsiz kapamalar isimsiz işlevler 2. belirsiz sayıda parametre alan işlevlerin C dilindeki gibi tanımlanmış olmaları (Parametre Serbestliği dersini anlatırken bu olanağı bilerek atlamıştım) 3. derleme zamanında işletilmiştir. Kaday . Muhallebi . buradaki kullanımında kesinlikle değişmeyen değer anlamına gelen enum da yazılabilir enum string tatlıMenüsü = /* . eş zamanlı çalışacak şekilde tanımlanmış olmamalıdır 4. örneğin. aşağıdaki koşulların sağlanmaları da gerekir: 1. işlev. Yukarıdaki çıktıdaki ı harflerinin benim ortamımda sorun çıkartıyor olmaları önemli değildir. işlevdeki ifadeler ◦ ◦ ◦ ◦ ◦ ◦ hata atamazlar gösterge veya sınıf kullanamazlar evrensel değişkenlere erişemezler yerel static değişkenlere erişemezler delete 'i çağıramazlar derleme zamanında işletilemeyen işlevler çağıramazlar 5. menü işlevi. Baklava . yukarıdaki programdaki static yerine. Burada göstermek istediğim. Her işlev derleme zamanında işletilemez. http://ddili.Diğer İşlev Olanakları $ strings deneme | grep -A 4 Tatl -= Tatl lar =. şu deyimler kullanılamaz: ◦ eş zamanlı çalışma deyimleri Copyright © 2009-2011 Ali Çehreli.org 492 .. sonuç dizginin programın içinde gömülü olarak bulunduğudur. */ • sabit uzunluklu bir dizinin uzunluğunun hesaplanması • bir değer şablon parametresinin değerinin hesaplanması Ek olarak. Bir işlevin derleme zamanında işletilebilmesi için öncelikle bunun mümkün olduğu bir ifadede kullanılmış olması gerekir: • static bir değişkenin ilklenmesi • enum bir değişkenin ilklenmesi. ..

dup . şu nitelikler derleme zamanında işletilebilirler: ◦ ◦ ◦ ◦ .length .values Copyright © 2009-2011 Ali Çehreli. http://ddili.keys . bir istisna olarak.Diğer İşlev Olanakları ◦ ◦ ◦ ◦ ◦ throw with scope try-catch-finally etiketli break veya continue 6.org 493 .

köşeler) { write(i. Tür ve eleman adedi. mixin anahtar sözcüğü. köşe. oldukları gibi mixin satırının bulunduğu noktaya yerleştirilirler. şablonun içindeki kodları kullanarak iki elemanlı int dizisini ve o diziyi kullanan iki işlevi oluşturur. şablonun belirli parametre değerleri için bir kullanımı yazılır: mixin bir_şablon!(şablon_parametreleri) O şablonun o parametrelerle kullanımı için üretilen kodlar. O şablonun int türüyle ve 2 değeriyle kullanılmasını istediğimizi bir mixin 'e şöyle belirtebiliriz: mixin KöşeDizisiOlanağı!(int. } O şablon.org 494 . yapı. birlik. } void köşeleriGöster() { writeln("Bütün köşelerim:"). C ve C++ dillerindeki makrolar gibi işledikleri düşünülebilir. köşe. "Katmak". sanki oraya açıkça elle yazılmış gibi eklenmelerini sağlarlar. http://ddili. foreach (i. ":". Şablonlardan yararlanarak farklı parametre değerleri için işlev. Örnek olarak bir köşe dizisini. onları örneğin bir yapının üyeleri haline getirebiliriz: Copyright © 2009-2011 Ali Çehreli. Bu açıdan. sınıf. veya yasal olduğu sürece her tür D kodunu oluşturabiliyorduk. bir şablon içinde tanımlanmış olan bütün kodların programın belirli bir noktasına. mixin anahtar sözcüğünden sonra. derleme zamanında şablonlar veya dizgiler tarafından üretilen kodların programın istenen noktalarına eklenmelerine yararlar. arayüz. 75. Yukarıdaki mixin . Şablon katmaları. void köşeDeğiştir(int indeks. dizi elemanlarının türü ve eleman adedi konusunda esneklik getirmektedir. Böylece.1 Şablon katmaları Şablonların belirli kalıplara göre kod üreten olanaklar olduklarını Şablonlar ve Ayrıntılı Şablonlar derslerinde görmüştük. kullanıma göre artık serbestçe seçilebilir.Katmalar 75 Katmalar Katmalar. 2). int adet) { T[adet] köşeler. ve o köşeler üzerindeki işlemleri tanımlayan bir şablon düşünelim: template KöşeDizisiOlanağı(T. T köşe) { köşeler[indeks] = köşe. ' '). } } writeln(). şablonun belirli bir kullanımını programın herhangi bir noktasına yerleştirir. "içine karıştırmak" anlamına gelen "mix in"den türemiştir.

2) 4:(0. T 'ye karşılık int ve adet 'e karşılık 2 olacak şekilde üretilirler ve mixin anahtar sözcüğünün bulunduğu yere yerleştirilirler. köşeleriGöster(). 2). çizgi.0) 75. http://ddili. } Şablon içindeki kodlar.2 Dizgi katmaları D'nin çok güçlü bir olanağı.köşeDeğiştir(0.string. Bu kullanımda dizginin parantez içinde belirtilmesi gerekir: Copyright © 2009-2011 Ali Çehreli. y). 5). Böylece Çizgi yapısının 2 elemanlı bir dizisi ve o dizi ile işleyen iki işlevi olmuş olur: auto çizgi = Çizgi(). main 'in içine yerel bir dizi ve yerel iki işlev yerleştirir. } } Çıktısı: Bütün köşelerim: 0:(0. } O mixin . struct Nokta { double x = 0. 2. çizgi.0) 3:(1.köşeDeğiştir(1. 100).0) 1:(0. string toString() const { return format("(%s.2. köşeDeğiştir(3.2)). çizgi. mixin 'e verilen Nokta 'nın da şöyle basit bir yapı olduğunu düşünebiliriz: import std.köşeleriGöster(). double y = 0. Nokta(1.1. değerleri derleme sırasında bilinebilen dizgilerin kod olarak programın içine yerleştirilebilmeleridir. x.1.0) 2:(0.Katmalar struct Çizgi { mixin KöşeDizisiOlanağı!(int.org 495 . İçinde yasal D kodları bulunan her dizgi. 200). mixin anahtar sözcüğü ile programa eklenebilir. Program şu çıktıyı üretir: Bütün köşelerim: 0:100 1:200 Aynı şablonu örneğin bir işlev içinde ve başka parametre değerleri ile de kullanabiliriz: void main() { mixin KöşeDizisiOlanağı!(Nokta.%s)".

stdio. yalnızca içinde "writeln" geçen dizgiler döndürmektir. Aynı çıktı elde edilir: merhaba dünya Bu örneklerdeki mixin 'lere gerek olmadığı açıktır. mixin(yazdırmaDeyimi("selam dünya")). mixin 'e verilecek olan kod dizgilerini bir işleve oluşturtmaktadır: import std. ve beklediğimiz çıktıyı verir: merhaba dünya Bunun etkisini göstermek için biraz daha ileri gidebilir ve bütün programı bile bir dizgi katması olarak yazabiliriz: mixin( `import std. mixin 'lerin bulundukları satırlara kod olarak yerleştirilirler. void main() { mixin(`writeln("merhaba dünya"). } void main() { mixin(yazdırmaDeyimi("merhaba dünya")).stdio. Dizgi katmalarının gücü. kodun derleme zamanında otomatik olarak oluşturulabilmesinden gelir. Aşağıdaki örnek.`). O kodların şimdiye kadar hep yaptığımız gibi programa açıkça yazılmaları daha mantıklıdır. void main() { writeln("merhaba dünya"). } Yukarıdaki program. Copyright © 2009-2011 Ali Çehreli. Derleme zamanında oluşturulabildiği sürece. string yazdırmaDeyimi(string mesaj) { return `writeln("` ~ mesaj ~ `"). writeln işlevlerinin yazdırmaDeyimi 'nin içinde çağrılmadıklarıdır. O dizgiler. şunun eşdeğeridir: import std. void main() { writeln("merhaba dünya"). yazdırmaDeyimi 'nin yaptığı.`.stdio.Katmalar mixin(derleme_zamanında_oluşturulan_dizgi) Örneğin "merhaba dünya" programını bir dizgi katması ile şöyle yazabiliriz: import std. mixin ifadesi işlevlerin döndürdüğü dizgilerden bile yararlanabilir. Burada dikkatinizi çekmek istediğim nokta. }` ). } Dizgi içindeki kod mixin satırına eklenir. yazdırmaDeyimi 'nin oluşturduğu iki dizgiyi mixin satırlarının yerlerine yerleştirir ve program o kodlarla derlenir. program derlenir.org 496 . http://ddili. Sonuçta derlenen program.stdio.

2 gibi kullanımlar için Süre opBinary(string işleç)(int miktar) if (işleç == "-") { return Süre(dakika .işleçleriyle kullanılırken int kabul edebilmeleri için aşağıdaki üye işlevleri tanımlanmış olsun: struct Süre { int dakika. mixin 'li program.miktar). Bu türün nesnelerinin + ve . } // süre + 2 gibi kullanımlar için Süre opBinary(string işleç)(int miktar) if (işleç == "+") { return Süre(dakika + miktar). Bunu görmek için tekrar Süre yapısına bakalım.3 İşleç yüklemedeki kullanımı İşleç Yükleme dersinde işleçlerin şablon söz dizimi ile tanımlandıklarını görmüştük. opOpAssign 'ın ve opBinary 'nin + ve . işleçleri belirleyen şablon parametrelerinin string türünde olmaları ve bu yüzden dizgi katmalarından yararlanabilmeleridir.Katmalar } writeln("selam dünya"). O söz dizimlerini bir kalıp olarak kabul etmenizi rica etmiş ve onların şablonlarla ilgili derslerden sonra açıklığa kavuşacaklarını söylemiştim.işleçleri için tanımları "+" ve "-" karakterleri dışında aynıdır. sanki o iki writeln satırı varmış gibi derlenir ve çalışır: merhaba dünya selam dünya 75.org 497 . } // süre . http://ddili. } // süre -= 2 gibi kullanımlar için void opOpAssign(string işleç)(int miktar) if (işleç == "-") { dakika -= miktar. İşleç yüklemeyle ilgili olan üye işlevlerin şablonlar olarak tanımlanmalarının nedeni. Buradaki kod tekrarını ortadan kaldırmak için mixin 'lerden yararlanılabilir: struct Süre { int dakika. // süre += 2 gibi kullanımlar için void opOpAssign(string işleç)(int miktar) if (işleç == "+") { dakika += miktar. } } Görüldüğü gibi. Copyright © 2009-2011 Ali Çehreli.

org 498 . sonuç1). } } void main() { auto süre = Süre(10). 4. süre). } string toString() { return format("%s dakika". } } Üstelik bu sefer şablon kısıtlamaları da kullanılmadığı için başka aritmetik işlemler de desteklenmektedir: import std. http://ddili.Katmalar void opOpAssign(string işleç)(int miktar) { mixin("dakika " ~ işleç ~ "= miktar.stdio. } Süre opBinary(string işleç)(int miktar) { mixin("return Süre(dakika ") ~ işleç ~ " miktar. writeln("süre auto auto auto auto sonuç1 sonuç2 sonuç3 sonuç4 = = = = : ".string. struct Süre { int dakika.").")."). void opOpAssign(string işleç)(int miktar) { mixin("dakika " ~ işleç ~ "= miktar. ". süre süre süre süre + / % 3. ". Çıktısı: süre : sonuç1: sonuç2: sonuç3: sonuç4: 3 dakika 6 dakika -1 dakika 0 dakika 3 dakika Copyright © 2009-2011 Ali Çehreli. sonuç3). ". sonuç2). import std. süre /= 3. süre %= 4. sonuç4). 4."). süre += 1. 4. süre -= 2. } writeln("sonuç1: writeln("sonuç2: writeln("sonuç3: writeln("sonuç4: ". dakika). } Süre opBinary(string işleç)(int miktar) { mixin("return Süre(dakika ") ~ işleç ~ " miktar.

4 Örnek Kendisine verilen sayılardan belirli bir koşula uyanlarını seçen ve bir dizi olarak döndüren bir işleve bakalım: int[] seç(string koşul)(in int[] sayılar) { int[] sonuç. seçme koşulunu bir şablon parametresi olarak almakta. Konumuzla ilgisi olmayan bölümlerini göstermeden. seç şablonuna parametre olarak verilen dizginin içinde kullanılan değişken isminin. Oradaki a ve b . -2. a. sort şablonu.. bize o koşulu programda bir dizgi olarak bildirme olanağı verir: int[] sayılar = [ 1. O işlev. 10 ]. Bunun bir örneği olarak std. 6. seç işlevinin belgelerinde belirtilmek zorundadır. Benim seçtiğim "eleman" gibi uzun bir isim değil. } } } return sonuç. o parametre bildirilmeden kullanılabilir: Copyright © 2009-2011 Ali Çehreli.. b. sort 'un tanımı şöyledir. c diye tek harflik isimler kullanılır. sort işlevi. sort 'un her sıralama kararında kullandığı soldaki ve sağdaki değişkenlerdir. öyle bir varsayılan değere sahip olduğu için. a ve b değişkenlerini kullanır.org 499 . */)(/* . O değişken isminin ne olduğu. less 'in varsayılan sıralaması küçükten büyüğe doğrudur. int[] seçilenler = seç!"eleman < 7"(sayılar). "Sırala" anlamına gelen sort .. http://ddili. foreach (eleman. void sort(alias less = "a < b". O işlevi kullanan programcılar da o isme uymak zorundadırlar. O ifadenin örneğin elemanların 7'den küçük olanlarını seçmesi için if deyimi içine şöyle bir ifadenin yazılması gerekir: if (eleman < 7) { İşte. yukarıdaki seç işlevi. ve if deyiminin parantezinin içine o koşulu olduğu gibi kod olarak yerleştirmektedir. */) less isimli şablon parametresinin varsayılan değeri. 75. Bu amaçla kullanılan değişken isimleri konusunda Phobos'ta bir standart gelişmeye başlamıştır.algorithm modülündeki sort işlevine bakabiliriz. O koşul soldakinin sağdakinden küçük olmasına baktığı için. 8. sıralama koşulunda kullanılan değişkenlerin a ve b olmalarını şart koşar. /* . kendisine verilen aralığı sıralamaya yarar..Katmalar Gerektiğinde yine de şablon kısıtlamaları eklenebilir ve uygun olmayan işlemler o sayede elenebilir. seç işlevi içinde tanımlananla aynı olması şarttır. sayılar) { if (mixin(koşul)) { sonuç ~= eleman. Burada önemli bir noktaya dikkat çekmek istiyorum.

import std. writeln(sayılar). sort(sayılar). 1.sort niteliğinin de aynı amaçla kullanılabileceğini hatırlayın. 1. Örneğin İşlev Göstergeleri ve Kapamalar dersinde gördüğümüz gibi bir delegate parametre de kullanılabilir. 3 ]. dizi elemanları sıralanmışlardır: 1 2 3 4 5 Elemanların ters sırada sıralanmaları için less parametresini açıkça yazmak ve < yerine > belirtmek yeter: int[] sayılar = [ 5. writeln(sayılar). 4. Copyright © 2009-2011 Ali Çehreli. sort!"a > b"(sayılar). http://ddili. 2.org 500 .algorithm.Katmalar import std. 4. her karşılaştırmada soldaki elemanın sağdaki elemandan büyük olması kıstası kullanıldığından dizi büyükten küçüğe doğru sıralanır: 5 4 3 2 1 Aslında aynı amaca ulaşmak için başka yollar da bulunduğunu hatırlayın. 2.stdio. Her zaman olduğu gibi. 3 ]. } Not: Dizilerin . hangi durumda hangi yöntemin daha uygun olacağına kendiniz karar vermelisiniz. Bu sefer. void main() { int[] sayılar = [ 5. Beklendiği gibi.

Veri yapılarının nasıl gerçekleştirilmiş oldukları önemsizleşir. vs. Phobos. eşleme tabloları. Ben bu derste topluluk ve veri yapısı deyimlerini aynı anlamda kullanacağım. D'deki topluluklar. foreach döngüsü ile kullanılabiliyordu. O üç işlev. Topluluk (veri yapısı): Topluluk. Değişkenler belirli amaçlarla bir araya getirilirler ve sonradan bir topluluğun elemanları olarak kullanılırlar. aralıkları kullanan ilk ve bilinen tek kütüphanedir.1 Tarihçe Algoritmalarla veri yapılarını birbirlerinden başarıyla soyutlayan bir kütüphane. neredeyse bütün programlarda karşılaşılan çok yararlı bir kavramdır. topluluk türü olarak da int[] kullanacağım. Eleman Erişimi Üzerine isimli makalesinde aralıkları tanıtır ve aralıkların erişicilerden neden daha üstün olduklarını gösterir.Aralıklar 76 Aralıklar Aralıklar. ikili arama algoritması. Her veri yapısı türü. elemanlarına nasıl erişildiği ön plana çıkar. elemanları o veri yapısına özel biçimde barındırır ve elemanlara o veri yapısına özel biçimde eriştirir. Aralıklar. Her topluluk belirli bir veri yapısı olarak gerçekleştirilir. ikili ağaç veri yapısında düğümler kendilerinden sıralamada önceki ve sonraki elemanlara farklı dallar yoluyla erişim sağlarlar.container modülünde tanımlanmış olan topluluk türleridir. Örneğin sıralı arama algoritması. Örneğin eşleme tabloları bir hash table veri yapısı gerçekleştirmesidir. ve std. Aslında aralıkların gücü şablonlarla birlikte kullanıldıklarında ortaya çıkar. Aralıklarla ilgili kavramları en basit aralık çeşidi olan InputRange ile göstereceğim. erişicilerin bu zayıflıklarını gidermeye yönelik olarak Andrei Alexandrescu tarafından tasarlanmıştır. Andrei Alexandrescu. çok sayıdaki veri yapısının çok sayıdaki algoritma ile uyumlu olarak kullanılmasını sağlar. diziler. Diğer aralıkların farkları. STL bu soyutlamayı C++'ın şablon olanağından yararlanarak gerçekleştirdiği erişici (iterator) kavramı ile sağlar. C++ dilinin standart kütüphanesinin de bir parçası olan STL'dir (Standard Template Library). Algoritma (işlev): Veri yapılarının belirli amaçlarla ve belirli adımlar halinde işlenmelerine algoritma denir. Aralıkların birbirlerine uydurduğu çoğu topluluk ve çoğu algoritma şablondur. 76. Çok güçlü bir soyutlama olmasına rağmen erişici kavramının bazı zayıflıkları da vardır. InputRange aralık çeşidinin gerektirdiği işlevlerdir. başka işlevler de gerektirmeleridir.org 501 . topluluk elemanlarına erişim işlemini soyutlarlar. Copyright © 2009-2011 Ali Çehreli. her adımda elemanların yarısını eleyerek arayan bir algoritmadır. bağlı liste yapısında elemanlar düğümlerde saklanırlar ve bu düğümler aracılığıyla erişilirler. Bu soyutlama. Daha ileri gitmeden önce aralıklarla doğrudan ilgili olan topluluk ve algoritma tanımlarını hatırlatmak istiyorum. vs. Aşağıdaki çoğu örnekte eleman türü olarak int . http://ddili. türlerin belirli isimdeki işlevleri sunmaları ilkesi üzerine kurulu olan aslında çok basit bir kavramdır. Ben bu derste algoritma ve işlev deyimlerini aynı anlamda kullanacağım. Aralıklar. front ve popFront() üye işlevlerini tanımlayan her tür. Örneğin dizi veri yapısında elemanlar yan yana dururlar ve sıra numarası ile erişilirler. aranan değeri topluluktaki bütün elemanları başından sonuna kadar ilerleyerek arayan bir algoritmadır. Bunların örneklerini bir sonraki derse bırakacağım. Bu kavramla daha önce Yapı ve Sınıflarda foreach dersinde de karşılaşmıştık: empty .

Örneğin elemanların 10'dan büyük olanlarını seçmek için kullanılan aşağıdaki filter() dizi değil. void main() { int[] sayılar = [ 1. belirtilen kıstasa uyan elemanlar yazdırılırlar: [20. kullandığı bağlı listenin düğümlerinin eleman ve sonraki isminde iki üyesi bulunduğunu bilmek zorundadır: struct Düğüm { int eleman. b gibi harflerle ifade edildikleri şablon parametreleriyle daha önce Katmalar dersinde kullandığımız sort() işlevinde karşılaşmıştık. import std. kullanımları sırasında farkedilmese bile aslında geçici aralık nesneleri döndürürler. filter() 'ın döndürmüş olduğu aralık nesnesini gerektikçe tembel olarak kullanır. Çoğu programda kendi aralık türlerimizi veya aralık işlevlerimizi yazmamız gerekmez.algorithm. Phobos'taki çok sayıda algoritma. } Not: Değişkenlerin a . Döndürülen nesne bir dizi olmadığı için örneğin aşağıdaki satır derlenemez: int[] seçilenler = filter!"a > 10"(sayılar).Aralıklar 76.stdio.org 502 . 11] O sonuca bakarak filter() 'ın int dizisi döndürdüğü düşünülebilir. } void yazdır(const(Düğüm) * liste) { Copyright © 2009-2011 Ali Çehreli. Düğüm * sonraki. 7. O geçici aralık nesnesinin istendiğinde bir diziye de dönüştürülebileceğini aşağıda göstereceğim. sizin denediğiniz Phobos sürümünde farklı olabilir. writeln(filter!"a > 10"(sayılar)). aralıklarla ilgili çok sayıda olanak içerir. aralık nesnesi döndürür: import std. writeln . 20. Örneğin bir bağlı listenin elemanlarını sırayla çıkışa yazdıran aşağıdaki işlev. ancak bu doğru değildir. 11 ].3 Algoritmaların geleneksel gerçekleştirmeleri Geleneksel olarak algoritmalar işlemekte oldukları veri yapılarının nasıl gerçekleştirildiklerini bilmek zorundadırlar. http://ddili. Sonuçta.2 Aralıklar D'de kaçınılmazdır Aralıklar D'ye özgü bir kavramdır. // ← derleme HATASI Döndürülen nesnenin türünü hata mesajında görüyoruz: Error: cannot implicitly convert expression (filter(sayılar)) of type Result to int[] Not: O tür. Buna rağmen aralıkların Phobos'ta nasıl kullanıldığını bilmek önemlidir. Diziler en işlevsel aralık çeşidi olan RandomAccessRange 'e uyarlar ve Phobos. 76.

.eleman).son biçiminde yazılan sayı aralıklarından farklıdır. onların her veri yapısı için özel olarak yazılmalarını gerektirir. Bunun sonucunda da A adet algoritmanın V adet veri yapısı ile kullanılabilmesi için gereken işlev sayısı A çarpı V'dir. bağlı liste. vs. i != dizi. vs. liste.Aralıklar } for ( . Aralık çeşitleri. sırala().10]. foreach (sayı. 3.. bir diziyi yazdıran işlev de dizilerin length isminde niteliklerinin bulunduğunu ve elemanlarına [] işleci ile erişildiğini bilmek zorundadır: void yazdır(const int[] dizi) { for (int i = 0. çift uçlu aralık: ek olarak back ve popBack() işlevleri RandomAccessRange . en temelden en işlevsele doğru ve gerektirdikleri işlevlerle birlikte şunlardır: • • • • InputRange . Diğer aralıklar. } Benzer şekilde. for 'un gerçekten gerektiği bir durum olduğunu kabul edelim. ++i) { write(' '.7) { // sayı aralığı. http://ddili. Örneğin eşleme tabloları sıralanamazlar. onun sunduğu aralık çeşidini destekleyen bütün algoritmalarla kullanılmaya hazırdır. giriş aralığı: empty . liste = liste. Amacım algoritmaların geleneksel olarak veri yapılarına doğrudan bağlı olduklarını göstermek olduğu için. Bu sıradüzen en basit aralık olan InputRange ile başlar. // Phobos aralığı DEĞİL Ben bu derste aralık yazdığım yerlerde Phobos aralıklarını kastedeceğim. yeni yazılan bir algoritma da onun gerektirdiği aralık çeşidine uyan bütün veri yapıları ile işlemeye hazırdır.org 503 . dizi[i]). gibi algoritmaların ayrı ayrı yazılmaları gerekir. (Not: Her algoritma her veri yapısı ile kullanılamadığı için gerçekte bu sayı daha düşüktür.length.4 Phobos aralıkları Bu dersin konusu olan aralıklar.sonraki) { write(' '. Algoritmaların veri yapılarına bu biçimde bağlı olmaları. Aralıklar bir aralık sıradüzeni oluştururlar. // Phobos aralığı DEĞİL // sayı aralığı. front ve popFront() işlevleri ForwardRange . ikili ağaç. 76. Sayı aralıklarını foreach döngüsündeki ve dilimlerdeki kullanımlarından tanıyoruz: int[] dilim = dizi[5. Yeni yazılan bir veri yapısı. liste. dizi.. } } Not: Dizilerde ilerlerken foreach 'in daha uygun olduğunu biliyoruz. ortakOlanlarınıBul().) Öte yandan. temel aldıkları aralığın gerektirdiği işlevlere ek olarak başka işlevler de gerektirirler. rastgele erişimli aralık: ek olarak [] işleci (sonlu veya sonsuz olmasına göre başka koşullar da gerektirir) Copyright © 2009-2011 Ali Çehreli. eşleme tablosu. değiştir(). baş. Örneğin. ilerleme aralığı: ek olarak save() işlevi BidirectionalRange . yığın. aralıklar veri yapılarıyla algoritmaları birbirlerinden soyutladıkları için yalnızca A adet algoritma ve V adet veri yapısı yazmak yeterli olur. gibi veri yapılarının her birisi için ara().

aralığı bu şekilde başından daraltma düşüncesi üzerine kuruludur. // ← hep ilk elemana erişilir } Yukarıdaki döngünün ilerlemesi.length == 3). salt ilerleme işleminin dizide bir değişiklik oluşturmadığını belirtmek istiyorum. algoritmaların veri yapılarından soyutlanmaları için yeterlidir. Copyright © 2009-2011 Ali Çehreli. sonlu ve sonsuz olarak iki çeşittir: InputRange (giriş aralığı) ↑ ForwardRange (ilerleme aralığı) ↗ ↖ BidirectionalRange RandomAccessRange (sonsuz) (çift uçlu aralık) (rastgele erişimli aralık) ↑ RandomAccessRange (sonlu) (rastgele erişimli aralık) Yukarıdaki aralıklar eleman erişimine yönelik aralıklardır.Aralıklar Bu sıradüzeni aşağıdaki gibi gösterebiliriz. her seferinde baştaki eleman çıkartılarak sağlanır: for ( .length. Farklı bir bakış açısı getiren bir yöntem. dizi = dizi[1. İlerleme. 11. http://ddili. i != dizi.. } assert(dizi. dizi[0]). ++i) { write(' '.$]) { write(' '. 11. 12 ].. dizi. 12 [ 12 [ ] ] ] ] İşte Phobos aralıklarındaki ilerleme kavramı. aralığı başından daraltarak ilerlemektir. Bu yöntemde aralığın hep ilk elemanına erişilir.4.org 504 .) O örneği yalnızca bu tür ilerleme kavramını göstermek için verdim.$] ifadesinin baştaki elemanı diziden çıkartması ile sağlanmaktadır. dizi[i]). (BidirectionalRange ve sonlu RandomAccessRange aralıkları son taraftan da daralabilirler. for (int i = 0. 12 [ 11. Dizi. dizi = dizi[1. Onlara ek olarak eleman çıkışı ile ilgili olan bir aralık daha vardır: • OutputRange . RandomAccessRange . Örneğin bir dizide foreach veya for ile ilerlendiğinde dizinin kendisi değişmez: int[] dizi = [ 10. for döngülerinin o şekilde yazılması normal kabul edilmemelidir. 76.length.1 Aralığı daraltarak ilerlemek Şimdiye kadar çoğu örnekte kullandığımız ilerleme yönteminde aralığın kendi durumunda değişiklik olmaz. // ← uzunluğu değişmez Burada. o ifadenin etkisiyle aşağıdaki aşamalardan geçerek daralır ve sonunda tükenir: [ 10. çıkış aralığı: put(aralık. eleman) işlemini desteklemek Bu beş aralık.

Programların standart girişlerinde olduğu gibi. yazdır() böylece topluluğun asıl türünden de bağımsız hale gelir ve InputRange 'in gerektirdiği üç işlevi sunan her toplulukla kullanılabilir.. asıl topluluğun kendisi değil. dilim[0]). dilim = dilim[1. giriş aralığı Bu çeşit aralık. Copyright © 2009-2011 Ali Çehreli. aralık. yazdır() işlevini bir kere de bu üç işlevden yararlanacak şekilde gerçekleştirelim: void yazdır(T)(T aralık) { for ( . !aralık.length. 76. değilse false döndürmelidir • front : "öndeki" anlamına gelir ve aralığın başındaki elemana erişim sağlar • popFront() : "öndekini çıkart" anlamına gelir ve aralığın başındaki elemanı çıkartarak aralığı baş tarafından daraltır Not: empty ve front işlevlerini nitelik olarak kullanılmaya uygun oldukları için parantezsiz. aralık boş kabul edildiğinde true . // ← dilim boşalır // ← dizi değişmez Phobos işlevleri de asıl topluluğun değişmemesi için özel aralık nesneleri döndürürler. aralık.popFront()) { write(' '. Bu erişim hep ileri yöndedir. yukarıdaki geleneksel yazdır() işlevlerinde de olduğu gibi elemanların art arda erişildikleri aralık çeşidini ifade eder.length == 3). popFront() işlevini ise yan etkisi olan bir işlev olduğu için parametre listesi ile yazmaya karar verdim. dilim. InputRange aralıklarının gerektirdiği üç işlevi bütünlük amacıyla bir kere daha hatırlatıyorum: • empty : "boş mu" anlamına gelir ve aralığın sonuna gelinip gelinmediğini bildirir. Bu örnekteki asıl diziyi korumak için örneğin bir dilimden yararlanılabilir: int[] dizi = [ 10.Aralıklar Salt ilerlemiş olmak için elemanların diziden bu şekilde çıkartılmaları çoğu durumda istenmeyeceğinden. çok sayıda algoritma yalnızca InputRange kullanarak yazılabilir. Aralığın elemanlarının türü konusunda bir kısıtlama getirmiş olmamak için işlevi ayrıca şablon olarak tanımladığıma dikkat edin.org 505 .1 Bir InputRange örneği Daha önce de karşılaşmış olduğumuz Okul türünü InputRange tanımına uygun olarak tekrar tasarlayalım. Buna rağmen.$]) { write(' '.length == 0). Okul 'u bir Öğrenci topluluğu olarak düşünelim ve onu elemanlarının türü Öğrenci olan bir aralık olarak tanımlamaya çalışalım. http://ddili.5 InputRange .front). } assert(dilim. 76.empty.5. 11. assert(dizi. tekrar başa dönülemez. int[] dilim = dizi. okundukça elemanların tüketildikleri akımlar da bu tür aralık tanımına girerler. 12 ]. yalnızca ilerlemek için oluşturulan başka bir aralık tüketilir. } } writeln(). çünkü çoğu algoritma yalnızca ileri yönde ilerleme üzerine kuruludur. for ( .

@property bool empty() { return öğrenciler.Aralıklar Örneği kısa tutmuş olmak için bazı önemli konularla ilgilenmeyeceğim: • • • • yalnızca bu bölümü ilgilendiren üyeleri yazacağım bütün türleri yapı olarak tasarlayacağım private ..length == 0. numara). dizinin ilk elemanı döndürülerek sağlanabilir: struct Okul { // . 1)..string. } } Copyright © 2009-2011 Ali Çehreli. } void main() { auto okul = Okul( [ Öğrenci("Ebru".. public . const gibi aslında yararlı olan belirteçler kullanmayacağım sözleşmeli programlama veya birim testi olanaklarından yararlanmayacağım import std.. http://ddili. } } Programda kullanırken okul. string toString() { return format("%s(%s)". int numara. @property ref Öğrenci front() { return öğrenciler[0]. struct Öğrenci { string isim. isim.empty biçiminde parantezsiz olarak yazabilmek için işlevi @property belirteci ile tanımladım. Öğrenci("Damla". } } struct Okul { Öğrenci[] öğrenciler. InputRange 'in gerektirdiği üç üye işlevi tanımlamamız gerekiyor. empty işlevinin aralık boş olduğunda true döndürmesini sağlamak için doğrudan öğrenciler dizisinin uzunluğunu kullanabiliriz. Öğrenci("Derya". } Okul türünü bir InputRange olarak kullanabilmek için.org 506 . 2) . 3) ] ). front işlevinin aralıktaki ilk elemanı döndürmesi. Dizinin uzunluğu 0 olduğunda aralık da boş kabul edilmelidir: struct Okul { // .

Öğrenci bir yapı türü olduğu için ilk elemanın kopyası döndürülürdü. void popFront() { öğrenciler = öğrenciler[1 . en işlevsel aralık çeşidi olan RandomAccessRange olarak bile kullanılabilirler. öğrenciler dizisini başında daraltarak sağlanabilir: struct Okul { // ..array. empty . her elemanı kopyalar. 4 ]). Bu üç işlev Okul türünün InputRange olarak kullanılması için yeterlidir. 76. Bunun için std. popFront() ve diğer aralık işlevlerini diziler için özel olarak tanımlar. Phobos veya başka kütüphanelerin InputRange alan algoritmalarıyla da kullanılmaya hazırdır.array modülü En sık kullanılan topluluk çeşidi olan diziler. yazdır() . InputRange tanımına uyan Okul 'u aralık işlevleri aracılığıyla kullanır. her aralık dizi olarak kullanılamaz. popFront() işlevinin aralığı başından daraltması.array. yazdır([ 1. 3.array işlevi kullanılabilir. front . Bunu biraz aşağıda göreceğiz. Okul . array() .Aralıklar Not: Dizideki asıl elemana erişim sağlamış olmak için ref dönüş türü kullandığımıza dikkat edin..array.org 507 . Copyright © 2009-2011 Ali Çehreli. ve yeni bir dizi döndürür: import std. 2. InputRange aralığını başından sonuna kadar ilerler.. Not: Biraz aşağıda göreceğimiz std. // .. Bu sorunu daha sonra özel bir aralık türü yardımıyla gidereceğiz. std. salt ilerlemiş olmak için aralıktan öğrenci çıkartılıyor olması çoğu duruma uygun değildir. Böylece diziler örneğin yazdır() işlevine gönderilmeye hazırdırlar: import std.range modülü eklendiğinde std.array modülü. http://ddili.array 'in ayrıca eklenmesine gerek yoktur. Okul nesnelerini artık başka hiçbir şey gerekmeden örneğin yazdır() şablonuna gönderebiliriz: yazdır(okul). Aralık elemanlarından dizi oluşturmak gerektiğinde elemanlar teker teker açıkça kopyalanmalıdır. Sonuçta aralığın elemanları teker teker çıkışa yazdırılırlar: Ebru(1) Derya(2) Damla(3) Böylece kendi yazdığımız bir türü InputRange tanımına uydurmuş ve InputRange 'lerle işleyen bir işleve gönderebilmiş olduk. Öyle yazmasaydık. Her dizinin aralık olarak kullanılabilmesinin aksine.2 Dizileri aralık olarak kullanabilmek için std.array modülünün eklenmesi yeterlidir.5. $]. } } Not: Yukarıda da değindiğim gibi. Bunun için std..

http://ddili. dizgi[i]). // . yazdır("abcçdefgğ?"c). Aşağıdaki dizgilerde char 'a sığmadıklarını bildiğimiz ç ve ğ harflerinden başka wchar 'a sığmayan gotik ahsa harfi (?) de bulunuyor.) Bunu hatırlamak için dizgileri elemanlarına tek tek erişerek yazdıralım: void elemanlarınıyazdır(T)(T dizgi) { for (int i = 0. Buna rağmen. yazdır("abcçdefgğ?"w). Derya(2).array. Damla(3)] 76.org 508 . Bu ortamda desteklenmiyorsa bir soru işareti olarak görünüyor olabilir: import std. Bunun anlamı. writeln(öğrencilerinKopyaları).. yazdır("abcçdefgğ?"d).. Copyright © 2009-2011 Ali Çehreli. ne tür dizgi olursa olsun dizgi elemanlarının harf harf ilerlenmesidir.. Ancak. ++i) { write(' '.Aralıklar // .5. Bunun istisnaları.array modülünün dizgilere özel önemli bir yararı daha vardır: Dizgilerde ileri veya geri yönde ilerlendiğinde elemanlara UTF kod birimleri olarak değil. elemanlarınıYazdır("abcçdefgğ?"c). std. auto öğrencilerinKopyaları = array(okul). elemanlarınıYazdır("abcçdefgğ?"d). elemanlarınıYazdır("abcçdefgğ?"w). (Not: aslında dchar da UTF-32 kod birimidir ama UTF-32 kod birimleri Unicode karakterlerine bire bir karşılık gelirler.. i != dizgi. Çıktısı: [Ebru(1). programın çıktısı çoğu durumda zaten istemiş olacağımız gibidir: a b c ç d e f g ğ ? a b c ç d e f g ğ ? a b c ç d e f g ğ ? Bu çıktının Karakterler ve Dizgiler derslerinde gördüğümüz davranışlara uymadığına dikkat edin..array modülü sayesinde hemen hemen bütün aralık çeşitleri olarak kullanılabilirler.length.array modülünün dizgilere özel yararı Tanım gereği olarak zaten karakter dizisi olan dizgiler de std. } } writeln(). Hatırlarsanız. char ve wchar dizgilerinin elemanları UTF kod birimleridir. char ve wchar dizgilerinin RandomAccessRange tanımına giremiyor olmalarıdır..3 std. Unicode karakterleri olarak erişilir. // .

O dchar . böyle aralıklar yalnızca başka aralıkların elemanlarına erişim sağlamak için kullanılırlar. } struct ÖğrenciAralığı { Öğrenci[] öğrenciler.5. dizgi içindeki iki UTF kod biriminden oluşmuştur ama kendisi dizginin elemanı değildir. Oysa.array. Unicode karakterlerine erişildiğini gördük.öğrenciler. char ve wchar Unicode karakteri ifade edemeyeceklerinden. UTF kod birimlerine erişilmiş olur. this(Okul okul) { this. dizgideki UTF kod birimlerinin bir araya getirilmelerinden oluşturulan bir dchar 'dır: import std.front . Örneğin Okul. var olan bir Öğrenci nesnesine referans döndürüyordu. http://ddili. Daha önce Okul içinde tanımlamış olduğumuz bütün aralık işlevlerini yeni ÖğrenciAralığı türüne taşıyalım: struct Okul { Öğrenci[] öğrenciler. tek amacı okuldaki öğrencilere erişim sağlamak olan özel bir tür InputRange olarak tanımlanır.Aralıklar Doğrudan dizgi elemanlarına erişildiği için harflere değil. yukarıda Okul aralığında ilerlerken karşılaştığımız eleman kaybedilmesi sorununu da ortadan kaldırır. front 'un döndürdüğü karakter.front. bazı aralıkların ise hiç elemanları yoktur. bu konuda da esneklik getirmeleridir: front 'un döndürdüğü elemanın bir topluluğun gerçek bir elemanı olması gerekmez.4 Kendi elemanları bulunmayan aralıklar Yukarıda aralık örneği olarak kullandığımız dizilerde ve Okul nesnelerinde hep gerçek elemanlar bulunuyordu. } Copyright © 2009-2011 Ali Çehreli. aralık olarak kullandığımızda elde edilen Unicode karakterleri o dizgilerin gerçek elemanları olamazlar. Bunun için örneğin Okul türünün kendisi değil.öğrenciler = okul. örneğin popFront() her çağrıldığında hesaplanarak oluşturulabilir ve front her çağrıldığında döndürülebilir. Buna benzer olarak. // front'un döndürdüğü dchar.org 509 . Programın benim ortamımdaki çıktısı aşağıdaki gibi oluyor: a b c � � d e f g � � � � � � a b c ç d e f g ğ ��� ��� a b c ç d e f g ğ ? 76. Gerçek elemanları bulunmayan bir aralık örneğiyle aslında biraz yukarıda da karşılaştık: Dizgiler aralık olarak kullanıldıklarında UTF kod birimlerine değil. Bu. Aralıkların bir üstünlüğü. void main() { dchar harf = "şu". O sözde eleman. // ş'yi oluşturan iki char'ın // bileşimidir } Dizginin eleman türü char olduğu halde yukarıdaki front 'un dönüş türü dchar 'dır.

. // ← sonsuz aralık Copyright © 2009-2011 Ali Çehreli.Aralıklar @property bool empty() { return öğrenciler.length == 3). } @property ref Öğrenci front() { return öğrenciler[0].5 Sonsuz aralıklar Kendi elemanları bulunmayan aralıkların başka bir yararı. baştaki = sonraki.length == 0.5. } void popFront() { öğrenciler = öğrenciler[1 . yalnızca iki adet int üyesi bulunmasına rağmen sonsuz uzunluktaki Fibonacci serisi olarak kullanılabilir: struct FibonacciSerisi { int baştaki = 0. } void popFront() { int ikiSonraki = baştaki + sonraki. http://ddili. int sonraki = 1. öğrenciler dizisi Okul 'un özel bir üyesi de olabilirdi ve ÖğrenciAralığı en azından o özel üyeye erişim sağlamak için yararlı olabilirdi.öğrenciler dizisinin bir dilimini kendimiz de doğrudan kullanabilirdik. $]. sonraki = ikiSonraki. Öğrenci("Damla". static immutable bool empty = false. Öte yandan. yazdır(ÖğrenciAralığı(okul)). Bunun sonucunda da asıl dizi değişmemiş olur: auto okul = Okul( [ Öğrenci("Ebru". assert(okul. // asıl dizi değişmez Not: Bütün işlerini doğrudan üyesi olan dilime yaptırdığı için ÖğrenciAralığı 'nın iyi bir örnek olmadığını düşünebiliriz. 76.org 510 . Bunun bir örneğini görmek için Fibonacci serisini üreten bir aralık düşünelim.öğrenciler. kendisine verilen Okul 'un öğrencilerini gösteren bir dilim oluşturur ve popFront() içinde o dilimi tüketir. 2) . Bir aralığın hiç sonlanmaması. Her zaman için false değerinde olan empty 'nin işlev olması da gerekmeyeceğinden static bir üye olarak tanımlanır. Aşağıdaki aralık. 1). } } Yeni aralık. sonsuz uzunlukta aralıklar oluşturabilmektir. empty işlevinin her zaman için false değerinde olması ile sağlanır. Çünkü nasıl olsa Okul. Öğrenci("Derya". @property int front() const { return baştaki. 3) ] ).

max 'tan daha büyük değerlere gelindiğinde FibonacciSerisi yanlış çalışır. 2) . Bazı durumlarda ise ÖğrenciAralığı gibi türleri açıkça yazmak yerine. yazdır() da take() 'in döndürmüş olduğu bu geçici aralık nesnesini kullanır: Ebru(1) Derya(2) Copyright © 2009-2011 Ali Çehreli.take işlevinde görebiliriz.. Böylece kullanıcılar bazı durumlarda çok karmaşık olabilen özel aralık türlerinin isimlerini ve şablon parametrelerini bilmek ve açıkça yazmak yerine. Öğrenci("Damla". 2)). Aslında bu işlem take() işlevi tarafından değil. kendisine verilen bir aralığın başındaki belirli adet elemana teker teker erişim sağlar.Aralıklar } } Not: Her ne kadar sonsuz olsa da. Bunun bir örneğini çok basit olan std. sayı türü olarak int kullandığı için int. yazdır(öğrencileri(okul)). "Al" anlamına gelen take() .. } // . Yukarıdaki kullanımda take() . o türün nesnelerini döndüren işlevlerden yararlanılır. okul nesnesinin başındaki 2 elemana erişim sağlayacak olan geçici bir aralık nesnesi döndürür. auto okul = Okul( [ Öğrenci("Ebru". yazdır(take(öğrencileri(okul). // ... parametre olarak gönderildiğinde yazdır() 'ın içindeki for döngüsü hiç sonlanmaz: yazdır(FibonacciSerisi()). kodlamayı kolaylaştırabilir: ÖğrenciAralığı öğrencileri(ref Okul okul) { return ÖğrenciAralığı(okul).5. FibonacciSerisi nesneleri için empty 'nin değeri hep false olduğundan.org 511 .range.range. 76. 1). 3) ] ). Öğrenci("Derya". FibonacciSerisi 'nin yalnızca belirli adet elemanının nasıl kullanılabildiğini aşağıda göreceğiz. onları döndüren işlevlerin kısa isimlerini hatırlayabilirler. onun döndürmüş olduğu özel bir aralık türü tarafından gerçekleştirilir. // hiç sonlanmaz Sonsuz aralıklar ancak sonuna kadar ilerlemenin gerekmediği durumlarda kullanılabilirler. http://ddili. Örneğin bütün işi bir ÖğrenciAralığı nesnesi döndürmek olan aşağıdaki işlev.6 Aralık döndüren işlevler Bir ÖğrenciAralığı nesnesini yukarıda açıkça ÖğrenciAralığı(okul) yazarak oluşturmuş ve kullanmıştık. Yine de biz take() 'i kullanırken bunu bilmek zorunda değilizdir: import std.

. std. Öğrenci("Derya". Öğrenci("Jane". import std.swapFront algoritmasını Okul türü ile kullanalım. kendisine verilen iki InputRange aralığının ilk elemanlarını değiş tokuş eder. yazdır(öğrencileri(amerikanOkulu)). std.algorithm. Çıktısı. onları yalnızca kendi işlevlerimizle değil. 20) ] ).org 512 . veya daha önce de yararlanmış olduğumuz typeof ve stringof ile kendimiz de yazdırabiliriz: writeln(typeof(take(öğrencileri(okul).algorithm modülleri Kendi türlerimizi aralık olarak tanımlamanın çok büyük bir yararı. 2)). filter() . öğrencileri(amerikanOkulu)). 10).stringof).7 std. Phobos ve başka kütüphanelerin aralık algoritmalarıyla da kullanabilmemizdir. 2) . filter() 'a şablon parametresi olarak verilen kıstası bildirmenin bir kaç yolu Co