You are on page 1of 649

srm: r250, 2011-07-08

Programlama Dili

D2 programlama dili ile bilgisayar programcl

Ali ehreli

D.ershane Serisi

0.1

Copyright 2009-2011 Ali ehreli


Forum ve Wiki blmlerinin ierikleri genele aittir. Her dersin haklar dersin yazarna aittir. Kiisel kullanm iin olmak artyla istediiniz ekilde yararlanabilirsiniz. Bu sitenin geri kalannn haklar Ali ehreli'ye aittir. Kaynak gstermek artyla istediiniz blmlerini baka sitelere kopyalayabilirsiniz. Kiisel kullanm iin olmak artyla, istediiniz blmn istediiniz 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 dosyasnn geliimi


r250, 2011-07-08: le yklemenin yeni sz dizimine dntrld, baz derslerin problemleri eklendi, vs. r243, 2011-07-05: 'Bellek Ynetimi' dersine realloc() eklendi r240, 2011-07-04: 'Bellek Ynetimi' dersi r235, 2011-06-26: Emekliye ayrlan 'scope' belirtecinin yerine 'clear' ve 'scoped' r233, 2011-06-18: 'Tr Nitelikleri' dersi r231, 2011-06-18: 'E Zamanl Programlama' dersi r206, 2011-06-04: 'Kout lemler' dersi r178, 2011-05-19: 'okuzlular' dersi r175, 2011-05-18: 'Baka Aralk Olanaklar' dersi r169, 2011-05-06: 'Aralklar' dersi r155, 2011-03-21: std.cstream yerine std.stdio ve ok sayda dzeltme r149, 2011-02-04: Dilimlerin gsterildii ders 'Baka Dizi Olanaklar' yeni balyla geniletildi r132, 2010-07-21: eitli dzeltmeler: '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: eitli dzeltmeler r118, 2010-05-31: 'Katmalar' dersi r114, 2010-05-28: 'Dier lev Olanaklar' dersi r109, 2010-05-25: 'Ayrntl ablonlar' dersi r107, 2010-05-07: 'Etiketler' dersi r104, 2010-05-05: 'Birlikler' dersi

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

r101, 2010-05-04: 'Yap ve Snflarda foreach' dersi r99, 2010-05-01: 'lev Gstergeleri ve Kapamalar' dersi r96, 2010-04-30: 'Koullu Derleme' dersi r81, 2010-04-06: 'Bit lemleri' dersi r75, 2010-04-02: 'Gstergeler' dersi ve ncesi

Srmlerdeki farkllklar ddili.org'un proje sayfasnda grebilirsiniz.

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

Teekkr

Teekkr
Bu derslerin yazm srasnda beni bir sre btnyle kaybedeceklerini bandan bilen, bunu anlayla karlayan, ve heyecanm paylaan eime ve kzma ok teekkr ederim. Bu derslerin doruluu ve anlalrl byk lde Ddili Forum yelerinin katklarna baldr. Yeni dersler hep nce o forumda duyuruldular, ve ancak yelerin nerileri dikkate alndktan sonra genel kullanma sunuldular. Bu dersleri yazma srecinde heyecanm uyank tuttuklar iin Ddili Forum yelerine teekkr ederim. zellikle Mert Ataol, kitabn her satrn byk bir titizlikle gzden geirdi ve dzeltmeler nerdi. Can Alpay ifti'nin ve Faruk Erdem ncel'in kitabn ve ddili.org'un geliiminde byk katklar oldu.

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

Tantm

Tantm
Derslere balamadan nce baz noktalarda anlamamz gerekiyor. Siz: Her ne kadar D iin C, C++, ve Java gibi dillerdeki deneyimler yararl olsa da, buradaki dersler D dilini ve genel olarak programcl hibir deneyim varsaymadan, temelden anlatrlar. Eer programcl Trke kaynaklardan renmeyi seviyorsanz, buras size gre... Hogeldiniz! :o) Hazrlk: Bu dersleri izleyebilmek iin D programlar yazacak bir ortama ihtiyacnz olacak. Bunun iin en azndan unlar gerekir: bir metin dzenleyici bir D 2.0 derleyicisi Trke harfleri destekleyen bir ortam Metin dzenleyiciyi ve derleyiciyi ayr ayr kurmak yerine, onlarn ikisini ve baka aralar da ieren bir gelitirme ortam da kurabilirsiniz. Bu konuda kurulum sayfasndan yararlanabilirsiniz. Eer sizin bu programlar kuracak deneyiminiz yoksa, bu aamada birisinden yardm almanz gerekiyor. D dilini metin dzenleyici veya derleyici olmadan renemezsiniz. Dersler btnyle Trke programlardan olutuklar iin, altnz ortamda Trke harflerin doru olarak grnmeleri gerekir. Bu konuda Windows ortam iin yardm almak iin Trke harflerin DOS pencerelerinde gsterilmesini anlatan forum konusundan yararlanabilirsiniz. (zellikle 2-a ve 2-b maddeleri.) Derslerin yaps: Her ders, olabildiince az sayda kavram rneklerle anlatyor. Her sayfann sonunda rencinin o zamana kadar rendikleriyle programlayabilecei problemler bulunuyor. renci kendisininkiyle karlatrabilsin diye her problemin zm de veriliyor. (Yerinizi kaybetmemeniz iin zmler ayr bir tarayc sayfasnda alrlar.) Bir derste anlatlan kavramlar anlamadan bir sonrakine gememenizi neririm. Eer anlalmayan kavramlar kalyorsa; bu sizden deil, derslerdeki eksikliklerden kaynaklanyor olabilir. O zaman ltfen sesinizi Ders Aras forumunda duyurun ve derslerin geliimine yardmc olun. Derslerde pencereli programlar anlatlmyor: Pencereli (grsel) programlar ok daha ho ve baz durumlarda ok daha kullanldrlar. Buna ramen, bu dersler grsel program yazmay anlatmazlar. nk dili renmek iin pencereler kullanmak gerekmez. Hatta grsel program yazmak iin kullanlan ktphanelerin tasarmlar dilin kavramlaryla karabilir ve dilin renimini gletirebilir. Bu yzden, bu dersler D dilini btnyle komut satrndan alan programlarla anlatrlar. D'yi ve standart ktphanesi Phobos'u bir kere rendikten sonra, zaten siz istediiniz grsel ktphane ile istediiniz pencereli program yazabileceksiniz. Ders RSS beslemesi: Bu blm hl yazm aamasnda olduu iin, eklenen yeni ders sayfalarndan haberdar olmak iin RSS beslemesinden yararlanabilirsiniz. Szlk: Her sayfada, o derste kullanlan terimleri ieren bir mini szlk bulunuyor. Bylece derste geen terimleri btn szl amanz gerekmeden hzlca hatrlayabileceksiniz. (Yerinizi kaybetmemeniz iin btn szlk ayr bir tarayc sayfasnda alr.) Ders Aras forumu: Programlama dilleri bakalaryla paylanca ok daha zevklidir. D dilini renenlerin forumu olan Ders Aras'na katln ve varsa sorularnz sorun, veya yeni balayanlara siz yardmc olun.

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

Tantm

Bir rica: Bu derslerin yararl ve kullanl olabilmesi sizin katknza bal. Yazarn kafasnda kurgulad anlatm yntemi renmeye uygun olmayabilir. Ltfen grdnz her trl yanl ve eksii en ksa zamanda yazara bildirin. Teekkrler! Yazar: Ali ehreli CV: http://acehreli.org/resume.html Kendisiyle bir sylei Kendisine Ceviz.net'in C ve C++ forumunda veya Ddili Forum'da rastlayabilirsiniz acehreli@yahoo.com adresinden ulaabilirsiniz

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

Programclk

Programclk
Programcln doru tanmnn ne olduu konusunda tam bir anlama yoktur. Bu yazara sorarsanz, programclk en ok zanaattr. Rastgele bir ka fikir: Bilgisayarn istediimiz gibi davranmasn salayan programlar oluturma iidir Aralar renmeyi, ve bu aralar nceki ustalarn deneyimleri dorultusunda kullanmay gerektirdii iin bir zanaattr Problem zd ve bunu belirli kstlamalar altnda yapt iin mhendisliktir ok zevkli ve tatmin edicidir Gzel sanat deildir ama insann oluturduu her eyde olduu gibi programlarn da gzelliklerinden sz edilebilir Kendisi bilim deildir ama kulland yntemler bilgisayar bilimi tarafndan gelitirilir

3.1

renmek ve retmek ok zordur


Programclk 1950'lerden beri retilen bir uratr. O zamandan bu yana henz programlamay etkin bir ekilde retecek bir yntem gelitirilememitir. Ne yazk ki, insanlarn kabaca yarsna yakn bir blmnn programlama renmeye yatkn olmadklar grlyor. Bu konuda yaplm aratrmalardan birisiyle ilgili olarak Ddili Forum'da alan bir konuya gz atmak isteyebilirsiniz. O aratrmadan anlaldna gre, programcla yatkn olanlar; anlamadklar bir durumla karlatklarnda kafalarnda doru veya yanl, ama tutarl bir model kurabilenlerdir. Bence yatkn olan kiiler iin programlamann zorluu, gerektirdii bilgi miktarndadr.

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

Atama ve lem Sralar

Atama ve lem Sralar


Eer programclk bir nceki blmde szedildii gibi baz insanlar iin gerekten ok zorsa; daha ileriye gitmeden nce bu insanlarn karlaacaklar engellerden ilk ikisini hemen burada grelim.

4.1

Atama ilemi
Program iinde a = 10 gibi bir satr grdnzde bu, "a'nn deeri 10 olsun" demektir. Benzer ekilde, b = 20 grdnzde de "b'nin deeri 20 olsun" demektir. imdi bu bilgilere dayanarak, o iki satrdan sonra unu grdmzde ne dnebiliriz? a = b Ne yazk ki matematikten altmz kural burada uygulayamayz. O ifade, "a ile b eittir" demek deildir! Bataki iki ifadeyle ayn mant yrtnce, o ifadenin "a'nn deeri b olsun" demek olduunu grrz. "a'nn b olmas" demek, "b'nin deeri ne ise, a'nn deeri de o olsun" demektir. Fark gryor musunuz? Matematikten altmz = iareti programclkta bambaka bir anlamda kullanlmaktadr: sa tarafn deeri ne ise, sol tarafn deerini de o yapmak...

4.2

lem sralar
Programlarda ilemler adm adm ve belirli bir srada uygulanrlar. Yukardaki ifadeyi program iinde alt alta a = 10 b = 20 a = b eklinde grdnzde, onlarn anlam udur: "a'nn deeri 10 olsun, sonra b'nin deeri 20 olsun, sonra a'nn deeri b'nin deeri olsun" demektir. Yani oradaki ilem admndan sonra hem a'nn hem de b'nin deerleri 20 olur. Programclk renirken karlalan bu iki nemli kavram anladnz m? yleyse devam...

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

Derleyici

Derleyici
D programclnda belki de en ok kullanlan iki ara metin dzenleyici ve derleyicidir. Metin dzenleyiciyi herkesin bildiini varsayarak ksaca "yaz yazma program" olarak hatrlatabiliriz. Programlar metin dzenleyicilerde yazlrlar. D gibi dillerde derleme kavram ve derleyicinin ilevi de hi olmazsa kaba hatlaryla mutlaka bilinmelidir.

5.1

Makine Kodu
Bilgisayarn beyni CPU denen mikro ilemcidir. Mikro ilemciye ne iler yapacan bildirmeye kodlama, bu bildirimlere de kod denir. Her mikro ilemci modelinin kendisine has kodlar vardr. Her mikro ilemcinin nasl kodlanacana mimari tasarm aamasnda ve donanm kstlamalar gzetilerek karar verilir. Bu kodlar ok alt dzeyde elektronik sinyaller olarak gerekletirilirler. Bu tasarmda kodlama kolayl geri planda kald iin, dorudan mikro ilemciyi kodlayarak program yazmak ok g bir itir. Mikro ilemcinin adnn paras olan ilem kavram, zel saylar olarak belirlenmitir. rnein kodlar 8 bit olan hayal bir ilemcide 4 says ykleme ilemini, 5 says depolama ilemini, 6 says da arttrma ilemini gsteriyor olabilir. Bu hayal ilemcide soldaki 3 bitin ilem says ve sadaki 5 bitin de o ilemde kullanlacak deer olduunu dnrsek, bu mikro ilemcinin makine kodu yle olabilir: lem 100 101 110 000 Deer 11110 10100 10100 00000 Anlam YKLE 11110 DEPOLA 10100 ARTTIR 10100 BEKLE

Makine kodunun donanma bu kadar yakn olmas, oyun kad veya renci kaytlar gibi st dzey kavramlarn bilgisayarda temsil edilmelerini son derece g hale getirir.

5.2

Programlama Dili
Mikro ilemcileri kullanmann daha etkin yollar aranm, ve zm olarak st dzey kavramlar ifade etmeye elverili programlama dilleri gelitirilmitir. Bu dillerin donanm kayglar olmad iin, zellikle kullanm kolayl ve ifade gc gzetilerek tasarlanmlardr. Programlama dilleri insanlara uygun dillerdir ve ok kabaca konuma dillerine benzerler: if (ortaya_kat_atlm()) { oyun_kadn_gster(); } Programlama dillerinin bir sorunu, anahtar szcklerinin geleneksel olarak ngilizce olmasdr. Neyse ki bunlar kolayca renebilecek kadar az saydadr. rnein if 'in "eer" anlamna geldiini bir kere renmek yeter.

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

Derleyici

5.3

Derleyici
Derleyicinin grevi araclktr: insanlarn anlad programlama dilini mikro ilemcinin anlad kodlara evirir. Bu ileme derleme denir. Her derleyici belirli bir programlama dilini bilir ve o dilin derleyicisi olarak anlr: "D derleyicisi" gibi...

5.4

Derlemeli Dil
Bu gibi dillerde yazlan programn altrlr hale gelmeden nce derlenmesi gerekir. Bu yntem ok hzl alan programlar retir; ama program yazmann yannda bir de derlemek gerektii iin, program gelitirme aamas daha klfetlidir. D, derlemeli bir dildir.

5.5

Yorumlamal Dil
Baz programlama dilleri derleyici gerektirmezler. Bu gibi dillere yorumlamal dil denir. Yorumlamal dillerde yazlan programlar derlenmeleri gerekmeden hemen altrlabilirler. Bu dillere rnek olarak Python, Ruby, ve Perl' gsterebiliriz... Derleme aamas olmad iin bu diller program gelitirmeyi abuklatrrlar. Bir sakncalar, her altrldklarnda program metninin batan taranmasnn ve makine kodu karlklarnn alma zamannda bulunmasnn gerekmesidir. Bu yzden, yorumlamal dillerde yazlan programlar derlemeli dillerde yazlan edeerlerinden genel olarak daha yava alrlar.

5.6

Derleme Hatas
Derleyiciler program dilin kurallarna uygun olarak derledikleri iin, kural d kodlar grdklerinde bir hata mesaj vererek sonlanrlar. rnein kapanmam bir parantez, unutulmu bir noktal virgl, yanl yazlm bir anahtar szck, vs. derleme hatasna yol aar. Derleyici bazen de kod aka kural d olmad halde, programcnn yanl yapmasndan phelenebilir ve bu konuda uyar mesaj verebilir. Program derlenmi bile olsa, her zaman iin uyarlar da hata gibi kabul edip, uyarya neden olan kodu deitirmek iyi olur.

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

10

"Merhaba Dnya" Program

"Merhaba Dnya" Program


Her programlama dilinde gsterilen ilk program, merhaba dnya programdr. Doal olarak son derece basit olmas gereken bu program, o dilde program yazabilmek iin mutlaka bilinmesi gereken kavramlar da ierdii iin nemlidir. imdilik bu kavramlar bir kenara brakalm ve nce bu program grelim: import std.stdio; void main() { writeln("Merhaba dnya!"); } Bu kadar kk bir programda bile deinilmesi gereken ok sayda kavram vardr. Bu aamada fazla ayrntya girmeden yle tantabiliriz: olanaklar: Her programlama dili kendi sz dizimini, temel trlerini, anahtar szcklerini, kurallarn, vs. tanmlar. Bunlar o dilin i olanaklarn olutururlar. Bu programda grlen parantezler, noktal virgller, main ve void gibi szckler; hep D dilinin kurallar dahilindedirler. Bunlar Trke gibi bir dilin yazm kurallarna benzetebiliriz: zne, yklem, noktalama iaretleri, vs... Anahtar szckler: Dilin i olanaklarn belirleyen zel szcklere anahtar szck denir. Bu programda tane anahtar szck var: Programa modl eklemeye yarayan import , "hibir tr" anlamna gelen void , ve programn balang noktasn belirleyen main . Ktphaneler ve ilevler: Dilin i olanaklar yalnzca dilin yapsn belirler. Bu olanaklar kullanlarak oluturulan ilevlerin bir araya getirilmelerine ktphane ad verilir. Bunu yine Trke'ye benzetecek olursak; dil; cmle yaps, ses uyumu, vs. gibi kavramlar ierir. Trke'yi kullanarak yaplabilecek sohbetlerin snr yoktur: bilimsel sohbet, futbol sohbeti, vs. Bunlar hep Trke olsalar da, kendilerine has zelliklere, anlamalara, ve kullanmlara sahiptirler. te bu deiik sohbetleri biraz olsun programlama dili ktphanelerine benzetebiliriz. Bu programdaki writeln ilevi, standart D ktphanesinde ka satr yazdrmak iin kullanlan bir ilevdir. smi, "satr yaz"n karl olan "write line"dan gelir. Modller: D'de ktphaneler programlara modller halinde tantlrlar. Bu programda kullanlan tek modl olan std.stdio 'nun ismi, "standart ktphanenin standart giri/k modl" olarak evirebileceimiz "standard input/output"tan tremitir. Karakterler ve dizgiler: Bu programdaki "Merhaba dnya!" gibi bilgilere dizgi, dizgileri oluturan elemanlara da karakter ad verilir. rnein bu programdaki dizgiyi oluturan karakterlerden bazlar M, e, ve ! karakterleridir. lem sras: Program, iini belirli admlar belirli srada tekrarlayarak yapar. Bu srann en banda main isimli ilevin iindeki ilemler vardr; programn ileyii, main 'le balar. Bu kk programda tek bir ilem bulunuyor: writeln 'li satrdaki ilem. Byk/Kk harf ayrm: Programda deiiklik yaparken dizgilerin iinde istediiniz karakterleri kullanabilirsiniz, ama dier isimleri grldkleri gibi kk harfle yazmaya dikkat edin, nk D dilinde byk/kk harf ayrm nemlidir. rnein writeln ile Writeln D dilinde farkl isimlerdir. Grld gibi, en kk D programnda bile sz edilmesi gereken ok sayda kavram bulunuyor. Bunlar ayrntlaryla renecek ok zamanmz olacak.

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

11

"Merhaba Dnya" Program

6.1

Kaynak dosya
Programcnn D dili kurallarna gre yazd ve derleyiciye derlemesi iin verdii dosyaya kaynak dosya denir. D derlemeli bir dil olduu iin, kaynak dosyann kendisi altrlabilir bir program deildir. Kaynak dosya, ancak derleyici tarafndan derlendikten sonra altrlabilen program haline gelir. Her tr dosyann olduu gibi, kaynak dosyann da diske kaydedilirken bir isminin olmas gerekir. Kaynak dosya isminde sisteminizin izin verdii her harfi kullanabilirsiniz. Ancak, D kaynak dosyalarnn dosya isim uzantsnn .d olmas geleneklemitir. Gelitirme ortamlar ve baka ara programlar da bunu beklerler. rnek olarak: deneme.d , tavla.d , fatura.d , vs.

6.2

Problemler
1. Yukardaki program gelitirme ortamnza kopyalayn, derleyin, ve altrn. Programn kts yle olmaldr: Merhaba dnya! 2. Programa istediiniz baka bir ey yazdrn 3. Program birden fazla satr yazacak ekilde deitirin 4. Programn baka yerlerinde deiiklikler yapn ve derlemeye aln; rnein writeln satrnn sonundaki noktal virgl olmadnda derleme hatalaryla karlatnz grn ... zmler

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

12

writeln ve write

writeln ve write
Bundan nceki derste, yazdrlmak istenen dizginin writeln 'e parantez iinde verildiini grdk. Programda writeln gibi i yapan birimlere ilev, o ilevlerin ilerini yaparken kullanacaklar bilgilere de parametre ad verilir. Parametreler ilevlere parantez iinde verilirler. writeln satra yazdrmak iin bir seferde birden fazla parametre alabilir. Parametrelerin birbirleriyle karmalarn nlemek iin aralarnda virgl kullanlr. import std.stdio; void main() { writeln("Merhaba dnya!", "Merhaba balklar!"); } Bazen, satra yazdrlacak olan btn bilgi writeln 'e hep birden parametre olarak geirilebilecek ekilde hazr bulunmayabilir. Byle durumlarda satrn ba taraflar write ile para para oluturulabilir ve satrdaki en sonuncu bilgi writeln ile yazdrlabilir. writeln yazdklarnn sonunda yeni bir satra geer, write ayn satrda kalr: import std.stdio; void main() { // nce elimizde hazr bulunan bilgiyi yazdryor olalm: write("Merhaba"); // ... arada baka ilemlerin yapldn varsayalm ... write("dnya!"); // ve en sonunda: writeln();

writeln 'i parametresiz kullanmak, satrn sonlanmasn salar. Balarnda // karakterleri bulunan satrlara aklama satr ad verilir. Bu satrlar programa dahil deildirler; programn bilgisayara yaptraca ileri etkilemezler. Tek amalar, belirli noktalarda ne yaplmak istendiini program daha sonra okuyacak olan kiilere aklamaktr.

7.1

Problemler
1. Buradaki programlarn ikisi de dizgileri aralarnda boluk olmadan birbirlerine yapk olarak yazdryorlar; bu sorunu giderin 2. write ' da birden fazla parametreyle armay deneyin ... zmler

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

13

Temel Trler

Temel Trler
Bilgisayarlarn beyni mikro ilemcidir. Bir programdaki ilemlerin ounu mikro ilemci yapar. Kendi yapmad ileri de bilgisayarn yan birimlerine devreder. Bilgisayarlarda en kk bilgi miktar, 0 veya 1 deerini tutabilen ve bit ad verilen yapdr. Bit, donanm dzeyinde bir ka tane transistrden oluur. Yalnzca 0 ve 1 deerini tutabilen bir veri trnn kullanm ok kstl olduu iin, mikro ilemciler birden fazla bitin yan yana getirilmesinden oluan daha kullanl veri trleri tanmlamlardr: rnein 8 bitten oluan bayt veya 32, 64, vs. bitten oluan daha byk veri trleri... Her mikro ilemcinin mimari tasarm gerei en etkin olarak kulland veri tr, o mikro ilemcinin o kadar bitlik olduunu gsterir: "32 bitlik ilemci", "64 bitlik ilemci", gibi... Mikro ilemcinin tanmlad veri trleri de kendi balarna yeterli deillerdir; nk rnein renci ismi gibi veya oyun kad gibi zel bilgileri tutamazlar. Mikro ilemcinin sunduu bu genel amal veri trlerini daha kullanl trlere evirmek programlama dillerinin grevidir. D'nin temel trleri bile tek balarna kullanldklarnda oyun kad gibi bir kavram destekleyemezler. O tr kavramlar ileride anlatlacak olan yaplarla ve snflarla ifade edilirler. D'nin temel trleri ounlukla dier dillerdeki temel trlere benzerler. Ek olarak, D'de belki de ancak bilimsel hesaplarda ie yarayan baz ek trler de bulunur. Tabloda kullanlan terimlerin aklamalarn aada bulacaksnz.

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

14

Temel Trler

D'nin Temel Veri Trleri Tr bool byte ubyte short int uint long ulong cent ucent float real Bool deeri iaretli 8 bit iaretsiz 8 bit iaretli 16 bit iaretli 32 bit iaretsiz 32 bit iaretli 64 bit iaretsiz 64 bit iaretli 128 bit (ilerisi iin dnlmtr; henz geerli bir tr deildir) Aklama lk Deeri false 0 0 0 0 0 0 0L 0L 0

ushort iaretsiz 16 bit

iaretsiz 128 bit (ilerisi iin dnlmtr; henz geerli 0 bir tr deildir) 32 bit kayan noktal say ya donanmn (mikro ilemcinin) tanmlad en byk kayan noktal say trdr (rnein, x86 mikro ilemcilerinde 80 bit), ya da double'dr; hangisi daha bykse... float.nan double.nan real.nan

double 64 bit kayan noktal say

ifloat sanal float deer idouble sanal double deer ireal sanal real deer

float.nan * 1.0i double.nan * 1.0i real.nan * 1.0i float.nan + float.nan * 1.0i double.nan + double.nan * 1.0i real.nan + real.nan * 1.0i 0xFF 0xFFFF 0x0000FFFF

cfloat iki float'tan oluan karmak say

cdouble iki double'dan oluan karmak say

creal

iki real'den oluan karmak say

char wchar dchar

iaretsiz 8 bit UTF-8 kod birimi iaretsiz 16 bit UTF-16 kod birimi iaretsiz 32 bit UTF-32 kod birimi ve Unicode kod noktas

Bunlara ek olarak hibir trden olmama kavramn ifade eden void anahtar szc de vardr. Aksine bir neden yoksa, genel bir kural olarak tamsay deerler iin int kullanlr.

8.1

Tablodaki Terimlerin Aklamalar


Bool deer: Mantksal ifadelerde kullanlan ve "doruluk" durumunda true , "doru olmama" durumunda false deerini alan trdr

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

15

Temel Trler

aretli tr: Hem eksi hem art deerler alabilen trdr; rnein -128'den 127'ye kadar deer alabilen byte . simleri eksi iaretinden gelir. aretsiz tr: Yalnzca art deerler alabilen trdr; rnein 0'dan 255'e kadar deer alabilen ubyte . Bu trlerin bandaki u harfi, "iaretsiz" anlamna gelen "unsigned"n ba harfidir. Kayan noktal say: Kabaca, 1.23 gibi kesirli deerleri tutabilen trdr; hesaplarn hassasiyeti trlerin bit saysyla doru orantldr (yksek bit says yksek hassasiyet salar); bunlarn dndaki trler kesirli deerler alamazlar; rnein int , yalnzca tamsay deerler alabilir Karmak say: Matematikte geen karmak say deerlerini alabilen trdr Sanal deer: Karmak saylarn salt sanal deerlerini tayabilen trdr; tabloda lk Deer stununda geen i , matematikte -1'in kare kk olan saydr nan: "Not a number"n ksaltmasdr ve geersiz kesirli say deeri anlamna gelir

8.2

Tr Nitelikleri
D'de trlerin nitelikleri vardr. Niteliklere trn isminden sonra bir nokta ve nitelik ismiyle eriilir. rnein int 'in .sizeof niteliine int.sizeof diye eriilir. Burada niteliklerden yalnzca drt tanesini greceiz; gerisini sonraki derslere brakacaz: .stringof trn okunakl ismidir .sizeof trn bayt olarak uzunluudur; trn ka bitten olutuunu hesaplamak iin bu deeri bir bayttaki bit says olan 8 ile arpmak gerekir .min "en az" anlamna gelen "minimum"un ksaltmasdr; trn alabilecei en kk deerdir .max "en ok" anlamna gelen "maximum"un ksaltmasdr; trn alabilecei en byk deerdir int 'in ka bayttan olutuunu, en kk deerini, ve en byk deerini yle yazdrabiliriz: import std.stdio; void main() { writeln("Tr : writeln("Bayt olarak uzunluu: writeln("En kk deeri : writeln("En byk deeri : }

", ", ", ",

int.stringof); int.sizeof); int.min); int.max);

8.3

Problem
Dier trlerin de niteliklerini yazdrn. Not: lerisi iin dnldkleri iin geersiz olan cent ve ucent trlerini hibir durumda, hibir trden olmamay temsil eden void 'i ise bu problemdeki gibi durumlarda kullanamazsnz. ... zm

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

16

Deikenler

Deikenler
Programda kullanlan kavramlar temsil eden yaplara deiken denir. rnek olarak hava scakl gibi bir deeri veya yar arabas motoru gibi karmak bir nesneyi dnebilirsiniz. Her deiken belirli bir trdendir ve her deikenin bir deeri vardr. Deikenlerin ounun isimleri de olur, ama programda aka anlmalar gerekmeyen deikenlerin isimleri olmayabilir de... rnek olarak bir okuldaki renci says kavramn ifade eden bir deiken dnebiliriz. renci says bir tamsay olduu iin, trn int olarak seebiliriz. Aklayc bir isim olarak da renci_says uygun olur. D'nin yazm kurallar gerei, deikenler nce trleri sonra isimleri yazlarak tantlrlar. Bir deikenin bu ekilde tantlmasna, o deikenin tanm, ve bu eyleme o deikenin tanmlanmas denir. Deikenin ismi, programda getii her yerde deerine dnr. import std.stdio; void main() { // Deikenin tanmlanmas; renci_says'nn int // trnde bir deiken olduunu belirtir: int renci_says; // Deikenin isminin kullanld yerde deerine // dnmesi: writeln("Bu okulda ", renci_says, " renci var.");

Bu programn kts udur: Bu okulda 0 renci var. Programn ktsndan anlaldna gre, renci_says 'nn deeri 0'dr. Bunun nedeni, int 'in ilk deerinin temel trler tablosundan hatrlayacanz gibi 0 olmasdr. Dikkat ederseniz, renci_says ktda ismi olarak deil, deeri olarak belirmitir; yani programn kts Bu okulda renci_says renci var. eklinde olmamtr. Deikenlerin deerleri = ileci ile deitirilir. Yapt i deer atamak olduu iin, bu ilece atama ileci denir: import std.stdio; void main() { int renci_says; writeln("Bu okulda ", renci_says, " renci var."); // renci_says'na 200 deerinin atanmas: renci_says = 200; writeln("Bu okulda imdi ", renci_says, " renci var.");

Bu okulda 0 renci var. Bu okulda imdi 200 renci var. Eer deikenin deeri tanmland srada biliniyorsa, tanmlanmasyla deerinin atanmas ayn anda yaplabilir, ve hata riskini azaltt iin de nerilen bir yntemdir:

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

17

Deikenler

import std.stdio; void main() { // Hem tanm, hem atama: int renci_says = 100; } writeln("Bu okulda ", renci_says, " renci var.");

Bu okulda 100 renci var.

9.1

Problem
ki deiken kullanarak ekrana "2.11 kurundan 20 avro bozdurdum." yazdrn. Deikenlerden kesirli say olan iin double trn kullanabilirsiniz. ... zm

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

18

Standart Giri ve k Akmlar

10

Standart Giri ve k Akmlar


Bizim ekran olarak algladmz k, aslnda D programnn standart kdr. Standart k karakter temellidir: yazdrlan btn bilgi nce karakter karlna dntrlr ve ondan sonra art arda karakterler olarak standart ka gnderilir. nceki blmlerde ka gnderilen tamsaylar, rnein renci says olan 100 deeri, ekrana aslnda tamsay 100 deeri olarak deil; 1 , 0 , ve 0 eklinde karakter olarak gnderilmitir. Normalde klavye olarak algladmz standart giri de bunun tersi olarak alr: bilgi art arda karakterler olarak gelir ve ondan sonra programda kullanlacak deerlere dntrlr. rnein giriten okunan 42 gibi bir deer, aslnda 4 ve 2 karakterleri olarak okunur. Bu dnmler bizim zel bir ey yapmamza gerek olmadan, otomatik olarak gerekleirler. Art arda gelen karakterler kavramna karakter akm ad verilir. Bu tanma uyduklar iin D programlarnn standart girii ve k birer karakter akmdr. Standart giri akmnn ismi stdin , standart k akmnn ismi de stdout 'tur. Akmlar kullanrken normalde akmn ismi, bir nokta, ve o akmla yaplacak ilem yazlr: akm.ilem gibi. Buna ramen, ok kullanlan akmlar olduklar iin, stdin ve stdout 'un zellikle belirtilmeleri gerekmez. nceki blmlerde kullandmz writeln , aslnda stdout.writeln 'in ksaltmasdr. Benzer ekilde, write da stdout.write 'in ksaltmasdr. Merhaba dnya programn byle bir ksaltma kullanmadan yle de yazabiliriz: import std.stdio; void main() { stdout.writeln("Merhaba dnya!"); }

10.1

Problem
Yukardaki programda stdout 'u yine writeln ilemiyle kullann ama bir seferde birden fazla deiken yazdrn. ... zm

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

19

Giriten Bilgi Almak

11

Giriten Bilgi Almak


Girilen bilginin daha sonradan kullanlabilmesi iin bir deikende saklanmas gerekir. rnein okulda ka tane renci bulunduu bilgisini alan bir program, bu bilgiyi int trnde bir deikende tutabilir. Yazdrma ilemi srasnda dolayl olarak stdout akmnn kullanldn bir nceki derste grdk. Bu, bilginin nereye gideceini aklamaya yetiyordu; nk stdout , standart k demektir. ka ne yazdrlacan da parametre olarak veriyorduk. rnein write(renci_says); yazmak, ka renci_says deikeninin deerinin yazdrlacan sylemeye yetiyordu. zetlersek: akm: ilem: yazdrlan: hedef: stdout write renci_says deikeninin deeri normalde ekran

write 'n karl readf 'tir. smindeki "f", "belirli bir dzende"nin ngilizcesi olan "formatted"dan gelir. Standart giriin de stdin olduunu grmtk. Okuma durumunda bundan bakaca nemli bir ayrnt vardr: okunan bilginin nerede depolanacann da belirtilmesi gerekir. Okuma ilemini de zetlersek: akm: ilem: okunan: hedef: stdin readf bir bilgi ?

Bilginin depolanaca hedef belirtilirken bir deikenin adresi kullanlr. D'de isimlerden nce kullanlan & karakteri, o isimle belirtilen eyin adresi anlamna gelir. readf 'e okuduu bilgiyi yerletirecei yer bu ekilde bildirilir: &renci_says . Burada &renci_says , "renci_says deikenine" diye okunabilir. Bu kullanm, yukardaki soru iaretini ortadan kaldrr: akm: ilem: okunan: hedef: din readf bir bilgi renci_says deikeni

simlerin bana & karakteri koymak o ismin belirttii eyin gsterilmesi anlamna gelir. Bu gsterme kavram, sonraki derslerde karlaacamz referanslarn ve gstergelerin de zn oluturur. readf konusunda bir noktay ilerideki derslere brakacaz ve imdilik ilk parametresi olarak "%s" kullanmak gerektiini kabul edeceiz: readf("%s", &renci_says); (Not: ou durumda aslnda boluk karakteri ile " %s" yazmak gerekeceini aada gsteriyorum.) "%s", verinin deikene uygun olan dzende dntrleceini belirtir. rnein giriten gelen '4' ve '2' karakterleri, bir int 'e okunduklarnda 42 tamsay deerini oluturacak ekilde okunurlar.

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

20

Giriten Bilgi Almak

Bu anlatlanlar gsteren programda nce sizden renci saysn bildirmeniz isteniyor. stediiniz deeri yazdktan sonra Enter'a basmanz gerekir. import std.stdio; void main() { write("Okulda ka renci var? "); // renci saysnn tutulaca deikenin tanmlanmas int renci_says; /* Giriten gelen bilginin renci_says deikenine atanmas */ readf("%s", &renci_says); writeln( "Anladm: okulda ", renci_says, " renci varm.");

11.1

Boluklarn gzard edilmelerinin gerekmesi


Yukardaki gibi programlarda deerleri yazdktan sonra Enter tuuna baslmas gerektiini biliyorsunuz. Kullancnn Enter tuuna basm olmas da zel bir kod olarak ifade edilir ve o bile programn standart giriinde belirir. Programlar bylece bilgilerin tek satr olarak m yoksa farkl satrlarda m girildiklerini alglayabilirler. Baz durumlarda ise girite bekleyen o zel kodlarn hibir nemi yoktur; szlp gzard edilmeleri gerekir. Yoksa standart girii tkarlar ve baka bilgilerin girilmesini engellerler. Bunun bir rneini grmek iin yukardaki programda ayrca retmen saysnn da girilmesini isteyelim. Program dzenini biraz deitirerek ve aklamalar kaldrarak: import std.stdio; void main() { write("Okulda ka renci var? "); int renci_says; readf("%s", &renci_says); write("Ka retmen var? "); int retmen_says; readf("%s", &retmen_says); writeln("Anladm: okulda ", renci_says, " renci", " ve ", retmen_says, " retmen varm.");

Ne yazk ki o program ikinci int 'in okunduu noktada taklr: Okulda ka renci var? 100 Ka retmen var? 200 Program burada taklr retmen says olarak 200 yazlm olsa da bir nceki 100'n sonunda baslm olan Enter'n kodlar girii tkamtr ve o yzden retmen_says deikeninin deeri olan 200 okunamamaktadr. Programn giriine gelen kodlar u ekilde ifade edebiliriz: 100[EnterKodu]200[EnterKodu]

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

21

Giriten Bilgi Almak

Giriin tkand noktay krmzyla belirttim. Bu durumda zm, retmen saysndan nce gelen Enter kodunun nemli olmadn belirtmek iin %s belirtecinden nce bir boluk karakteri kullanmaktr: " %s". Dzen dizgisi iinde geen boluk karakterleri sfr veya daha fazla saydaki grnmez kodu okuyup gzard etmeye yararlar. O tek boluk karakteri btn grnmez karakter kodlarn okuyup gzard eder: normal boluk karakteri, Enter'la girilen satr sonu karakteri, Tab karakteri vs. Genel bir kural olarak, okunan her deer iin " %s" kullanabilirsiniz. Yukardaki program o deiiklikle artk istendii gibi alr. Yalnzca deien satrlarn gstererek: // ... readf(" %s", &renci_says); // ... readf(" %s", &retmen_says); // ... kts: Okulda ka renci var? 100 Ka retmen var? 200 Anladm: okulda 100 renci ve 200 retmen varm.

11.2

Ek bilgiler
Daha nce grdmz // karakterleri tek bir satr aklama yazmaya elverilidir. Birden fazla satrda blok halinde aklama yazmak iin aklamay /* ve */ belirteleri arasna alabilirsiniz. Baka aklama belirtelerini de ierebilmek iin /+ ve +/ belirteleri kullanlr: /+ // Tek satrlk aklama /* Birden fazla satrlk aklama */

Yukardaki belirteleri bile ierebilen aklama blou +/ Kaynak kodlardaki boluk karakterlerinin ou nemsizdir. O yzden fazla uzayan satrlar blebiliriz veya daha anlalr olacan dndmz boluklar ekleyebiliriz. Hatta yazm hatasna yol amad srece hi boluk kullanmayabiliriz bile: import std.stdio;void main(){writeln("Okumas zor!");} Fazla skk kodu okumak gtr.

11.3

Problemler
1. Dviz kurunu ve avro adedini kullancdan bilgi olarak aln ve bu bilgiyi ka yazdrn. 2. Ayn programda giriten say beklenen durumda harfler girin ve programn yanl altn gzlemleyin. ... zmler

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

22

Mantksal fadeler

12

Mantksal fadeler
Programda asl ileri ifadeler yaparlar. Programda deer veya yan etki reten her eye ifade denir. Aslnda olduka geni bir kavramdr, nk 42 gibi bir tamsay sabiti bile 42 deerini rettii iin bir ifadedir. "merhaba" gibi bir dizgi de bir ifadedir, nk "merhaba" sabit dizgisini retir. (Not: Buradaki retme kavramn deiken tanmlama ile kartrmayn. Burada yalnzca deer retmekten sz ediliyor; deiken retmekten deil. Her deerin bir deikene ait olmas gerekmez.) writeln gibi kullanmlar da ifadedirler, nk yan etkileri vardr: k akmna karakter yerletirdikleri iin k akmn etkilemi olurlar. imdiye kadar grdkleriniz arasndan atama ilecini de bir ifade rnei olarak verebiliriz. fadelerin deer retiyor olmalar, onlarn baka ifadelerde deer olarak kullanlmalarn salar. Bylece basit ifadeler kullanlarak daha karmak ifadeler elde edilebilir. rnein hava scakln veren bir hava_scakl() ilevi olduunu dnrsek, onu kullanarak yle bir kt oluturabiliriz: writeln("u anda hava ", hava_scakl(), " derece"); O satr toplam drt ifadeden oluur: 1. 2. 3. 4. "u anda hava " ifadesi hava_scakl() ifadesi " derece" ifadesi ve o ifadeyi kullanan writeln 'li ifade

Bu blmde mantksal ifadeleri greceiz ama daha ileri gitmeden nce en temel ilemlerden olan atama ilecini hatrlayalm. = (atama ileci): Sa tarafndaki ifadenin deerini sol tarafndaki ifadeye (rnein bir deikene) atar. hava_scakl = 23 // hava_scakl'nn deeri 23 olur

12.1

Mantksal fadeler
Mantksal ifadeler Bool aritmetiinde geen ifadelerdir. Karar verme dzeneinin paras olduklar iin bilgisayarlar akll gsteren davranlarn da temelidirler. rnein bir programn "eer girilen yant Evet ise dosyay kaydedeceim" gibi bir kararnda bir mantksal ifade kullanlr. Mantksal ifadelerde yalnzca iki deer vardr: "doru olmama" anlamn tayan false ve "doruluk" anlamn tayan true . rnein Trke "bakent stanbul'dur" ifadesinin deeri false , "bakent Ankara'dr" ifadesinin deeri true 'dur. Aadaki rneklerde bir soru ile kullanlan writeln ifadelerini yle anlamanz gerekiyor: Eer sorunun karsna "true" yazlmsa evet, "false" yazlmsa hayr... rnein programn kts Tatl var: true olduunda "evet, tatl var" demektir. Ayn ekilde Tatl var: false

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

23

Mantksal fadeler

olduunda "hayr, tatl yok" demektir. Yani ktda "var" grnd iin "var olduunu" dnmeyin; ktdaki "... var: false", "yok" anlamna geliyor. Aadaki program paralarn hep yle okumanz gerekiyor. Mantksal ifadeleri daha ileride greceimiz koullarda, dnglerde, parametrelerde, vs. ok kullanacaz. Programlarda bu kadar ok kullanldklar iin mantksal ifadeleri ok iyi anlamak gerekir. Tanmlar son derece ksa olduu iin ok da kolaydrlar. Mantksal ifadelerde kullanlan mantksal ileler unlardr: == "Eit midir" sorusunu yantlar. ki tarafndaki ifadelerin deerlerini karlatrr ve eit olduklarnda "doruluk" anlamna gelen true deerini, eit olmadklarnda da "doru olmama" anlamna gelen false deerini retir. rettii deerin tr de doal olarak bool 'dur. rnein yle iki deikenimiz olsun: int haftadaki_gn_says = 7; int yldaki_ay_says = 12; Onlar kullanan iki eitlik ileci ifadesi ve sonular yle gsterilebilir: haftadaki_gn_says == 7 yldaki_ay_says == 11 // true // false

!= "Eit deil midir" sorusunu yantlar. ki tarafndaki ifadeleri karlatrr ve == ilecinin tersi sonu retir. haftadaki_gn_says != 7 yldaki_ay_says != 11 // false // true

|| "Veya" anlamndadr. Sol tarafndaki ifadenin deeri true ise hi sa taraftaki ifadeyi iletmeden true deerini retir. Sol taraf false ise sa taraftakinin deerini retir. Bu ilem Trke veya ifadesine benzer: birincisi, ikincisi, veya ikisi birden true olduunda true retir. Bu ilece verilen iki ifadenin alabilecei olas deerler ve sonular yledir: Sol ifade false false true true false true false (baklmaz) true (baklmaz) Sa ifade Sonu false true true true

import std.stdio; void main() { /* false, "doru olmama" anlamna geldii iin burada "yok"u temsil etsin true, "doru olma" anlamna geldii iin burada "var" temsil etsin */ bool baklava_var = false; bool kadayf_var = true; } writeln("Tatl var: ", baklava_var || kadayf_var);

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

24

Mantksal fadeler

Yukardaki programdaki || ilecini kullanan ifade, en az bir true deer olduu iin true deerini retir. && "Ve" anlamndadr. Sol tarafndaki ifadenin deeri false ise hi sa taraftaki ifadeyi iletmeden false deerini retir. Sol taraf true ise sa taraftakinin deerini retir. Bu ilem Trke ve ifadesine benzer: birincisi ve ikincisi true olduunda true retir. Sol ifade false false true true Sa ifade false (baklmaz) true (baklmaz) false true Sonu false false false true

writeln("Baklava yiyeceim: ", baklava_yemek_istiyorum && baklava_var);

Not: || ve && ilelerinin bu "sa taraf ancak gerektiinde" iletme davranlar ileler arasnda ok nadirdir, ve bir de imdilik sonraya brakacamz ?: ilecinde vardr. Dier ileler btn ifadelerinin deerlerini her zaman iin hesaplarlar ve kullanrlar. ^ "Yalnzca birisi mi" sorusunu yantlar. ki ifadeden ya biri ya br true olduunda (ama ikisi birden deil) true deerini retir. Sol ifade false false true true Sa ifade false true false true Sonu false true true false

rnein ancak ve ancak bir arkadamn geldiinde tavla oynayacam, aksi taktirde onlarla baka bir ey yapacam dnrsek; onlarn gelip gelmeme durumlarna gre tavla oynayp oynamayacam yle hesaplayabiliriz: writeln("Tavla oynayacam: ", ahmet_burada ^ bar_burada); < "Kk mdr" sorusunu yantlar. Sol taraf sa taraftan kkse (veya sralamada nceyse) true , deilse false deerini retir. writeln("Yendik: ", yediimiz_gol < attmz_gol); > "Byk mdr" sorusunu yantlar. Sol taraf sa taraftan bykse (veya sralamada sonraysa) true , deilse false deerini retir. writeln("Yenildik: ", yediimiz_gol > attmz_gol); <= "Kk veya eit midir" sorusunu yantlar. Sol taraf sa taraftan kk (veya sralamada daha nce) veya ona eit olduunda true retir. > ilecinin tersidir. writeln("Yenilmedik: ", yediimiz_gol <= attmz_gol); >= "Byk veya eit midir" sorusunu yantlar. Sol taraf sa taraftan byk (veya sralamada daha sonra) veya ona eit olduunda true retir. < ilecinin tersidir.

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

25

Mantksal fadeler

writeln("Yenmedik: ", yediimiz_gol >= attmz_gol); ! "Tersi" anlamndadr. Dier mantksal ilelerden farkl olarak tek bir ifade ile alr ve sa tarafndaki ifadenin deerinin tersini retir: true ise false , false ise true ... writeln("Bakkala gideceim: ", !ekmek_var);

12.2

fadeleri Gruplamak
fadelerin hangi srada iletilecekleri, gerektiinde parantezlerle belirtilir. Karmak ifadelerde nce parantez iindeki ifadeler iletilir ve onlarn deeri dtaki ilele kullanlr. rnein "kahve veya ay varsa ve yannda da baklava veya kadayf varsa keyfim yerinde" gibi bir ifadeyi yle hesaplayabiliriz: writeln("Keyfim yerinde: ", (kahve_var || ay_var) && (baklava_var || kadayf_var)); Kendimiz parantezlerle gruplamazsak, ifadeler D dilinin kurallar ile belirlenmi olan nceliklere uygun olarak iletilirler. && ilecinin ncelii de || ilecinden daha yksek olduu iin, yukardaki mantksal ifadeyi gruplamadan yle yazarsak writeln("Keyfim yerinde: ", kahve_var || ay_var && baklava_var || kadayf_var); ilem ncelikleri nedeniyle unun edeeri olarak iletilir writeln("Keyfim yerinde: ", kahve_var || (ay_var && baklava_var) || kadayf_var); ve tamamen farkl anlamda bir ifadeye dnr: "kahve varsa, veya ay ve baklava varsa, veya kadayf varsa; keyfim yerinde". Btn ilelerin ilem nceliklerini hemen hemen hibir programc ezbere bilmez. O yzden, gerekmese bile parantezler kullanarak hangi ifadeyi kurmak istediinizi aka belirtmek kodun anlalrl asndan ok yararldr.

12.3

Giriten bool Okumak


Yukardaki rneklerdeki btn bool ifadeler ka "false" veya "true" dizgileri olarak yazdrlrlar. Bunun kart doru deildir: giriten okunan "false" ve "true" dizgileri otomatik olarak false ve true deerlerine dntrlmezler. O yzden, giriten gelen kelimeyi nce bir dizgi olarak okumamz, ve sonra ona bakarak bir bool deere dntrmemiz gerekir. Aadaki program denerken giriten "false" ve "true" girebilmenizi istiyorum. Bylece bu derste anlatlanlar aynen deneyebileceksiniz. Bunun olabilmesi iin, derslerin banda planladm yoldan sapacak ve istemeyerek henz anlatmadm kodlar gstereceim. Giriten gelen "false" gibi bir dizgiyi false deerine dntren bir ilev kullanacam. Aadaki programlarda main 'ler iinde anlalmaz bir taraf olmadn umuyorum. Ama giriten_bool_oku() ilevinde henz grmediiniz olanaklar kullanlyor; iine aklamalar koydum ama isterseniz o ilevi imdilik tamamen gzard da edebilirsiniz.

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

26

Mantksal fadeler

12.4

Problemler
1. Saylarn bykl ve kkl ile ilgili olan < , > vs. ileleri bu derste tandk. Bu ileler iinde "arasnda mdr" sorusunu yantlayan ile bulunmaz. Yani verilen bir saynn iki deer arasnda olup olmadn hesaplayan ile yoktur. Bir arkadanz bunun stesinden gelmek iin yle bir program yazm olsun. Bu program derlemeye aln ve derlenemediini grn: import std.stdio; void main() { int say = 15; } writeln("Arasnda: ", 10 < say > 20);

Derleme hatasn gidermek iin btn ifadenin etrafnda parantez kullanmay deneyin: writeln("Arasnda: ", (10 < say > 20)); Yine derlenemediini grn... 2. Ayn arkadanz hatay gidermek iin bir eyler denerken derleme hatasnn gruplama ile giderildiini farketsin: writeln("Arasnda: ", (10 < say) > 20); Bu sefer de programn beklendii gibi almadn grn: 15 deerinin 10 ile 20 arasnda olduunu bildiimiz halde sonu "false" kmaktadr. Neden? pucu: Mantksal ifadelerin deerlerinin bool trnde olduklarn hatrlayn. Bildiiniz bool deerlerin 20 gibi bir saydan byk olmas gibi bir kavram tanmadk. 3. D'de "arasnda mdr" sorusunu yantlayan mantksal ifadeyi u ekilde kodlamamz gerekir: alt snrdan byktr ve st snrdan kktr. Programdaki ifadeyi o ekilde deitirin ve artk ktnn beklendii gibi "true" olduunu grn. Ayrca, yazdnz ifadenin say 'nn baka deerleri iin de doru altn denetleyin. rnein, say 50 veya 1 olduunda sonu "false" ksn; 12 olduunda "true" ksn. 4. Plaja ancak iki kouldan birisi gerekletiinde gidiyor olalm: Mesafe 10'dan az (kilometre varsayalm) ve yeterince bisiklet var Kii says 5 veya daha az, arabamz var, ve ehliyetli birisi var Aadaki programdaki mantksal ifadeyi bu koullar salandnda "true" yazdracak ekilde kurun. Program denerken "... var m?" sorularna "false" veya "true" diye yant verin: import std.stdio; void main() { write("Ka kiiyiz? "); int kii_says; readf("%s\n", &kii_says); write("Ka bisiklet var? "); int bisiklet_says; readf("%s\n", &bisiklet_says);

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

27

Mantksal fadeler

write("Mesafe? "); int mesafe; readf("%s\n", &mesafe); bool araba_var = giriten_bool_oku("Araba var m? "); bool ehliyet_var = giriten_bool_oku("Ehliyet var m? "); // Buradaki true'yu silin ve yerine sorudaki koullardan // birisi gerekletiinde true reten bir mantksal // ifade yazn: writeln("Plaja gidiyoruz: ", true);

} /*

UYARI: Henz bu ilevi anlamanz beklenmiyor. indeki baz olanaklar daha sonra anlatlacak. */ bool giriten_bool_oku(string mesaj) { // Mesaj yazdr writef(mesaj); // Girilen kelimeyi bir dizgiye oku char[] giri; readf("%s\n", &giri); /* imdi girilen dizgi "true" ise true deerini, "false" ise false deerini dndrmek istiyoruz. Yani burada bir dnm yapyoruz: rnein "true" dizgisini grnce true deerini dndryoruz. Not: "true" dizgisi ile true deerinin ayn ey olmadklarna dikkat edin: Birisi 't', 'r' gibi karakterlerden oluur, dieri ise bir bool deeridir.

*/

// Sonu olarak dndreceimiz deiken: bool dntrlen_deer; // "false"tan false'a ve "true"dan true'ya dnm if (giri == "false") { dntrlen_deer = false; } else if (giri == "true") { dntrlen_deer = true; } else { /* Bilmediimiz bir kelime geldi. Bu durumda ne yapacamz belli olmad iin ilemden vazgeip hata atmaktan baka hibir aremiz yok. */ throw new Exception( ("Bu kelimeyi anlamadm: " ~ giri ~ ". Ltfen false veya true yazn.").idup); } } return dntrlen_deer;

eitli deerler girin, ve yazdnz ifadenin her durumda doru altn denetleyin: koullara uyduunda "true", uymadnda "false" yazsn. ... zmler

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

28

if Koulu

13

if Koulu
Programda asl ilerin ifadeler tarafndan yapldn rendik. imdiye kadar grdmz programlarda ilemler main isimli ilev iinde batan sona doru ve yazldklar srada iletiliyorlard. D'de deyimler, ifadelerin iletilme kararlarn veren ve ifadelerin iletilme sralarn etkileyen program yaplardr. Kendileri deer retmezler ve yan etkileri yoktur. Deyimler, ifadelerin iletilip iletilmeyeceklerini ve bu ifadelerin hangi srada iletileceklerini belirlerler. Bu kararlar verirken de yine ifadelerin deerlerinden yararlanrlar. Not: fade ve deyim kavramlarnn burada rendiiniz tanmlar D dilindeki tanmlardr. Baka dillerdeki tanmlar farkllklar gsterir ve hatta baz dillerde byle bir ayrm yoktur.

13.1

if blou ve kapsam
if deyimi, ifadelerin iletilip iletilmeyeceine belirli bir mantksal ifadenin sonucuna bakarak karar veren yapdr. "if", ngilizce'de "eer" anlamndadr; "eer tatl varsa" kullanmnda olduu gibi... Parantez iinde bir mantksal ifade alr, eer o ifade doruysa (yani deeri true ise), kme parantezleri iindeki ifadeleri iletir. Bunun tersi olarak, mantksal ifade doru deilse kme parantezleri iindeki ifadeleri iletmez. Sz dizimi yledir: if (bir_mantksal_ifade) { // iletilecek bir ifade // iletilecek baka bir ifade // vs... } rnein "eer baklava varsa baklava ye ve sonra taba kaldr" gibi bir program yapsn yle yazabiliriz: import std.stdio; void main() { bool baklava_var = true; if (baklava_var) { writeln("Baklava yiyorum..."); writeln("Taba kaldryorum..."); }

O programda baklava_var 'n deeri false yaplrsa ka hibir ey yazdrlmaz, nk if deyimi kendisine verilen mantksal ifade false olduunda kme parantezi iindeki ifadeleri iletmez. Kme parantezleriyle gruplanm ifadelerin tmne blok, o blgeye de kapsam ad verilir. Yazm yanllarna yol amad srece, okumay kolaylatrmak iin programda istediiniz gibi boluklar kullanabilirsiniz. Ben kme ama parantezini, yani { karakterini, if 'le ayn satra yazmay seviyorum; siz alt satra da yazabilirsiniz. kisi de olduka yaygn kullanmlardr.

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

29

if Koulu

13.2

else blou ve kapsam


ou zaman if 'e verilen mantksal ifadenin doru olmad durumda da baz ilemler yapmak isteriz. rnein "eer ekmek varsa yemek ye, yoksa bakkala git" gibi bir kararda ekmek olsa da olmasa da bir eylem vardr. D'de ifade false olduunda yaplacak iler else anahtar szcnden sonraki kme parantezleri iinde belirtilir. "else", "deilse" demektir. Sz dizimi yledir: if (bir_mantksal_ifade) { // doru olduunda iletilen ifadeler } else { // doru olMAdnda iletilen ifadeler } rnek olarak: if (ekmek_var) { writeln("Yemek yiyorum"); } else { writeln("Bakkala yryorum"); } O rnekte ekmek_var 'n deerine gre ya birinci dizgi ya da ikinci dizgi yazdrlr. Kme parantezlerinin yerlerinin yine tercihe bal olduunu gryorsunuz. else kendisi bir deyim deildir, if deyiminin seime bal bir parasdr; tek bana kullanlamaz.

13.3

Kapsam parantezlerini hep kullann


Hi tavsiye edilmez ama bunu bilmenizde yarar var: Eer if 'in veya else 'in altndaki ifade tekse, kme parantezleri gerekmez. Yukardaki ifade kme parantezleri kullanlmadan aadaki gibi de yazlabilir: if (ekmek_var) writeln("Yemek yiyorum"); else writeln("Bakkala yryorum"); ou deneyimli programc tek ifadelerde bile kme parantezi kullanr. (Bununla ilgili bir hatay problemler blmnde greceksiniz.) Mutlaka kme parantezleri kullanmanz bu noktada nermemin bir nedeni var: Bu neriye hemen hemen hibir zaman uyulmayan tek durumu da imdi anlatacam.

13.4

"if, else if, else" zinciri


Dilin bize verdii glerden birisi, ifade ve deyimleri serbeste kartrarak kullanma imkandr. fade ve deyimleri kapsamlar iinde de kullanabiliriz. rnein bir else kapsamnda bile if deyimi bulunabilir. Programlarn akll olarak alglanmalar, hep bizim ifade ve deyimleri doru sonular verecek ekilde birbirlerine balamamzdan doar. Bisiklete binmeyi yrmekten daha ok sevdiimizi varsayarsak:

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

30

if Koulu

if (ekmek_var) { writeln("Yemek yiyorum"); } else { if (bisiklet_var) { writeln("Uzaktaki frna gidiyorum"); } else { writeln("Yakndaki bakkala yryorum"); } } Oradaki if deyimlerinin anlam udur: "eer ekmek varsa: yemek yiyorum; eer ekmek yoksa: bisiklet varsa frna gidiyorum, yoksa bakkala yryorum". Biraz daha ileri gidelim ve bisiklet olmadnda hemen bakkala yrmek yerine, komunun evde olup olmamasna gre davranalm: if (ekmek_var) { writeln("Yemek yiyorum"); } else { if (bisiklet_var) { writeln("Uzaktaki frna gidiyorum"); } else { if (komu_evde) { writeln("Komudan istiyorum"); } else{ writeln("Yakndaki bakkala yryorum"); }

Burada grld gibi "eer byleyse bunu yap, deilse ama yleyse onu yap, o da deilse ama yleyse unu yap, vs." gibi yaplar programclkta ok kullanlr. Ne yazk ki byle yaznca kodda fazla boluklar oluur: buradaki 3 if deyimi ve 4 writeln ifadesi iin toplam 13 satr yazm olduk (bo satrlar saymadan). Sk karlalan bu yapy daha dzenli olarak yazmak iin, byle zincirleme kararlarda bir istisna olarak ilerinde tek bir if deyimi bulunan else 'lerin kapsam parantezlerini yazmayz. Hibir zaman kodu aadaki gibi irkin brakmayn! Ben bir sonraki adma geme aamas olarak gsteriyorum (o yzden de renksiz braktm). lerinde tek bir if olan else 'lerin kme parantezlerini kaldrnca: if (ekmek_var) { writeln("Yemek yiyorum"); } else if (bisiklet_var) { writeln("Uzaktaki frna gidiyorum"); } else if (komu_evde) { writeln("Komudan istiyorum"); } else{ writeln("Yakndaki bakkala yryorum"); }

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

31

if Koulu

Bir adm daha ileri giderek if anahtar szcklerini de stlerindeki else satrlarna eker ve biraz da hizalarsak, son derece okunakl bir yap oluur: if (ekmek_var) { writeln("Yemek yiyorum"); } else if (bisiklet_var) { writeln("Uzaktaki frna gidiyorum"); } else if (komu_evde) { writeln("Komudan istiyorum"); } else{ writeln("Yakndaki bakkala yryorum"); } Bylece hem satr says azalm olur, hem de kararlara gre iletilecek olan btn ifadeler alt alta gelmi olurlar. Drt koulun hangi srada denetlendii ve her koulda ne yapld bir bakta anlalr. ok sk karlalan bu kod yapsna "if, else if, else" denir.

13.5

Problemler
1. Aadaki programdaki mantksal ifadenin true olduunu gryoruz. Dolaysyla programn limonata iip barda ykamasn bekleriz: import std.stdio; void main() { bool limonata_var = true; if (limonata_var) { writeln("Limonata iiyorum"); writeln("Barda ykyorum"); } else writeln("Baklava yiyorum"); writeln("Taba kaldryorum");

Oysa program altrrsanz, ktsnda bir de tabak kaldrldn greceksiniz: Limonata iiyorum Barda ykyorum Taba kaldryorum Neden? Program dzelterek beklenen kty vermesini salayn. 2. Kullancyla oyun oynayan (ve ona fazlasyla gvenen) bir program yazn. Kullanc att zarn deerini girsin. Zarn deerine gre ya kullanc kazansn, ya da program: Zar Deeri 1 2 3 4 5 6 Baka bir deer Program kts Siz kazandnz Siz kazandnz Siz kazandnz Ben kazandm Ben kazandm Ben kazandm HATA: Geersiz deer

Ek puan: Hatal giri olutuunda deeri de yazsn. rnein:

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

32

if Koulu

HATA: Geersiz deer: 7 3. Ayn oyunu yle deitirelim: Kullanc 1'den 1000'e kadar bir say girsin ve 1-500 aralnda siz kazann, 501-1000 aralnda bilgisayar kazansn. Hl bir nceki problemdeki zmleri uygulayabilir misiniz? ... zmler

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

33

while Dngs

14

while Dngs
while dngs if kouluna ok benzer ve onun tekrar tekrar iletilmesidir. Mantksal bir ifade alr, bu ifade doru ise kapsamdaki ifadeleri iletir. Kapsamdaki ilemler tamamlannca mantksal ifadeye tekrar bakar ve doru olduu srece bu dngde devam eder. "while", "olduu srece" demektir. Sz dizimi yledir: while (bir_mantksal_ifade) { // iletilecek bir ifade // iletilecek baka bir ifade // vs... } rnein "baklava olduu srece baklava ye" gibi bir ifade yle programlanabilir: import std.stdio; void main() { bool hl_baklava_var = true; while (hl_baklava_var) { writeln("Tabaa baklava koyuyorum"); writeln("Baklava yiyorum"); }

O program sonsuza kadar o dng iinde kalacaktr, nk hl_baklava_var deikeninin deeri hi deimemekte ve hep true olarak kalmaktadr. while 'n gc, mantksal ifadenin programn almas srasnda deitii durumlarda daha iyi anlalr. Bunu grmek iin kullancdan bir say alan ve bu say "0 veya daha byk" olduu srece dngde kalan bir program dnelim. Hatrlarsanz, int trndeki deikenlerin ilk deeri 0 olduu iin bu programda da say 'nn ilk deeri 0'dr: import std.stdio; void main() { int say; while (say >= 0) { write("Bir say girin: "); readf(" %s", &say); } } writeln(say, " iin teekkrler!");

writeln("Dngden ktm");

O program girilen say iin teekkr eder ve eksi bir say girildiinde dngden kar.

14.1

continue deyimi
"continue", "devam et" demektir. Bu deyim, dngnn geri kalanndaki ifadelerin iletilmeleri yerine, hemen dngnn bana dnlmesini salar.

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

34

while Dngs

Yukardaki programda girilen her say iin teekkr etmek yerine, biraz seici olalm ve 13 deeri girildiinde beenmeyip tekrar dngnn bana dnelim. Bu programda 13'e teekkr edilmez, nk say 13 olduunda continue ile hemen dngnn bana gidilir: import std.stdio; void main() { int say; while (say >= 0) { write("Bir say girin: "); readf(" %s", &say); if (say == 13) { writeln("Uursuz say kabul etmiyorum..."); continue; } } } writeln(say, " iin teekkrler!");

writeln("Dngden ktm");

O programn davrann yle zetleyebiliriz: girilen say 0 veya daha byk olduu srece say al, ama 13 deerini kullanma.

14.2

break deyimi
Bir ok szlk anlam olan "break" D'de "dngy kr" anlamndadr. Bazen artk dngyle iimiz kalmadn anladmzda dngden hemen kmak isteriz; break bunu salar. Bu programn arad zel saynn 42 olduunu varsayalm ve o sayy bulduu an dngyle ii bitsin: import std.stdio; void main() { int say; while (say >= 0) { write("Bir say girin: "); readf(" %s", &say); if (say == 42) { writeln("ARADIIMI BULDUM!"); break; } } } writeln(say, " iin teekkrler!");

writeln("Dngden ktm");

imdiki davrann da yle zetleyebiliriz: girilen say 0 veya daha byk olduu srece say al, 42 gelirse hemen k.

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

35

while Dngs

14.3

Sonsuz dng
break deyiminin kullanld baz durumlarda bilerek sonsuz dng oluturulur ve break deyimi o dngnn tek k olur. Sonsuz dng oluturmak iin while 'a sabit true deeri verilir. rnein kullancya bir men gstererek ondan bir komut bekleyen aadaki program, ancak kullanc zellikle istediinde bu dngden kmaktadr: import std.stdio; void main() { // Sonsuz dng, nk mantksal ifade hep true: while (true) { write("0:k, 1:Trke, 2:ngilizce - Seiminiz? "); int seim; readf(" %s", &seim); if (seim == 0) { writeln("Tamam, sonra grrz..."); break; // Bu dngnn tek k } else if (seim == 1) { writeln("Merhaba!"); } else if (seim == 2) { writeln("Hello!"); } else { writeln("Onu bilmiyorum... :/"); }

14.4

Problemler
1. u program giriten 3 geldii srece dngde kalmak iin programlanm ama bir hata var: kullancdan bir kere bile say istemiyor: import std.stdio; void main() { int say; while (say == 3) { write("Say? "); readf(" %s", &say); }

Neden? O programdaki hatay giderin ve beklendii gibi almasn salayn: kullancdan say alsn ve say 3 olduu srece dngde kalsn. 2. Bilgisayar iki kiiye (Aye ve Bar) yle bir oyun oynatsn: en bata Aye'den 1-10 aralnda bir say alsn. Aye'nin bu aralk dnda say girmesini kabul etmesin ve doru say girene kadar Aye'den say almaya devam etsin. Ondan sonra Bar'tan teker teker say almaya balasn ve Bar'n girdii say Aye'nin batan girdii sayya eit olunca oyunu sonlandrsn. Not: Aye'nin girdii say ekranda grnd iin tabii ki Bar tarafndan hemen bilinir. :o) Bu aamada bunun bir nemi yok; burada amacmz dngleri renmek...

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

36

while Dngs

... zmler

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

37

Tamsaylar ve Aritmetik lemler

15

Tamsaylar ve Aritmetik lemler


D'nin karar verme ile ilgili yaplarndan if 'i ve while ' grdk. Bu blmde temel trlerin saysal olanlaryla yaplan aritmetik ilemlere bakacaz. Bylece bundan sonraki blmlerde ok daha becerikli ve ilgin programlar yazabileceksiniz. Aritmetik ilemler aslnda son derece basittirler nk zaten gnlk hayatmzda her zaman karmza karlar. Buna ramen, temel trlerle ilgilenirken mutlaka bilinmesi gereken ok nemli kavramlar da vardr. Tr uzunluu, tama, ve krplma kavramlarn anlyorsanz btn konuyu bu tabloya bakarak geebilirsiniz: le ++ -+ * / % ^^ Etkisi deerini bir arttrr deerini bir azaltr iki deeri toplar ikinciyi birinciden kartr iki deeri arpar birinciyi ikinciye bler birincinin ikinciye blmnnden kalan verir ssn alr rnek kullanm ++deiken --deiken birinci + ikinci birinci - ikinci birinci * ikinci birinci / ikinci birinci % ikinci birinci ^^ ikinci

Tablodaki ikili ilelerin yanna = karakteri gelenleri de vardr: += , -= , *= , /= , %= , ve ^^= . Bunlar ilemin sonucunu soldaki deikene atarlar. rnein: say += 10; O ifade say 'ya 10 ekler ve sonucu yine say 'ya atar; yani deerini 10 arttrm olur. Tama: Her deer her tre smaz ve taabilir. rnein 0 ile 255 arasnda deerler tutabilen ubyte 'a 260 deeri verilmeye kalklrsa deeri 4 olur. Krplma: Tamsaylar virglden sonrasn tutamazlar. rnein 3/2 ifadesinin deeri 1 olur. Eer bu kavramlar rnein baka dillerden biliyorsanz, bu kadar yetebilir. sterseniz geri kalann okumayabilirsiniz, ama yine de sondaki problemleri atlamayn.

15.1

Ayrntl Bilgi
Bu blm ilgisiz bilgiler veriyor gibi gelebilir; nk aritmetik ilemler hepimizin gnlk hayatta srekli olarak karlatmz kavramlardr: Tanesi 10 lira olan bir eyden iki tane alrsak 20 lira veririz, veya 45 lira olan eylerin tanesi 15 liradr... Ne yazk ki iler bilgisayarda bu kadar basit olmayabilir. Saylarn bilgisayarda nasl saklandklarn bilmezsek, rnein 3 milyar borcu olan bir firmann 3 milyar daha bor almas sonucunda borcunun 1.7 milyara dtn grebiliriz. Baka bir rnek olarak, 1 kutusu 4 ocua yeten dondurmadan 11 ocuk iin 2 tane yetecek diye hesaplayabiliriz. Bu blm size ncekilerden daha teknik gelebilir ama tamsaylarn bilgisayarda nasl ifade edildiklerinin bir programc tarafndan mutlaka bilinmesi gerekir.

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

38

Tamsaylar ve Aritmetik lemler

15.1.1

Tamsaylar
Tamsaylar ancak tam deerler alabilen trlerdir: -2, 0, 10, vs. Bu trler 2.5 gibi kesirli deerler tutamazlar. Daha nce temel trler tablosunda da grdnz tamsay trleri unlardr: Bit Uzunluu 8 8 16 16 32 32 64 64 lk Deeri 0 0 0 0 0 0 0L 0L

Tr byte ubyte short ushort int uint long ulong

Hatrlarsanz, tr isimlerinin bandaki u karakteri "unsigned"dan geliyordu ve "iaretsiz" demekti. O trler eksi iareti olmayan trlerdir ve yalnzca sfr ve daha byk deerler alabilirler.
15.1.2

Bitler ve tr uzunluklar
Gnmzdeki bilgisayar sistemlerinde en kk bilgi paras bittir. Bit, elektronik dzeyde ve devrelerin belirli noktalarnda elektrik geriliminin var olup olmamas kavramyla belirlendii iin, ancak iki durumdan birisinde bulunabilir. Bu durumlar 0 ve 1 deerleri olarak kabul edilmilerdir. Yani sonuta bir bit, iki deiik deer saklayabilir. Yalnzca iki durumla ifade edilebilen kavramlarla fazla karlamadmz iin bitin kullanll da azdr: yaz veya tura, odada klarn ak olup olmad, vs. gibi iki durumu olan kavramlar... Biraz ileri giderek iki biti bir araya getirirsek, ikisinin birlikte saklayabilecekleri toplam deer adedi artar. kisinin ayr ayr 0 veya 1 durumunda olmalarna gre toplam 4 olaslk vardr. Soldaki rakam birinci biti, sadaki rakam da ikinci biti gsteriyor olsun: 00, 01, 10, ve 11. Yani bir bit eklemeyle toplam durum says ikiye katlanm olur. Bit eklemenin etkisini daha iyi grebilmek iin bir adm daha atabiliriz: bit, toplam 8 deiik durumda bulunabilir: 000, 001, 010, 011, 100, 101, 110, 111. Bu sekiz durumun hangi tamsay deerlerine karlk gelecekleri tamamen anlamalara ve geleneklere kalmtr. Yoksa rnein 000 durumu 42 deerini, 001 durumu 123 deerini, vs. gsteriyor da olabilirdi. Tabii bu kadar ilgisiz deerler kullanl olmayacaklarndan, 3 bitlik bir tr rnek alrsak, bu 8 durumun iaretli ve iaretsiz olarak kullanlmasnda ald deerler u tablodakine benzer:

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

39

Tamsaylar ve Aritmetik lemler

Bitlerin Durumu 000 001 010 011 100 101 110 111

aretsiz Deer 0 1 2 3 4 5 6 7

aretli Deer 0 1 2 3 -4 -3 -2 -1

Burada grmenizi istediim, 3 bitten nasl 8 farkl deer elde edilebildiidir. Grld gibi, eklenen her bit, saklanabilecek bilgi miktarn iki katna kartmaktadr. Bunu devam ettirirsek; bitlerin bir araya getirilmelerinden oluturulan deiik uzunluktaki trlerin saklayabildikleri farkl deer miktarlarn, bir nceki bit uzunluunun saklayabilecei deer miktarn 2 ile arparak yle grebiliriz: Bit Adedi 1 2 3 4 5 6 7 8 ... 16 ... 32 ... Saklanabilecek Farkl Deer Adedi D Tr 2 4 8 16 32 64 128 256 ... 65,536 short ushort ... 4,294,967,296 ... long -9223372036854775808 9223372036854775807 ulong 0 18446744073709551615 int uint -2147483648 0 2147483647 4294967295 -32768 0 32767 65535 byte ubyte -128 0 127 255 En Kk Deeri En Byk Deeri

64 18,446,744,073,709,551,616 ...
15.1.3

...

Hangi durumda hangi tr


bitlik bir tr toplam 8 deer tayabildii iin rnein ancak atlan zarn sonucu veya haftann gn says gibi kavramlar ifade etmek iin kullanlabilir. (D'de 3 bitlik tr yoktur; rnek olarak kullanyorum.) te yandan, uint ok byk bir tr olsa da, dnyadaki btn insanlar kapsayacak bir kimlik kart numaras gibi bir kavram iin kullanlamaz, nk uint dnyadaki insan nfusu olan 6 ksur milyardan daha az sayda deer saklayabilir. long ve ulong 'un Trke'de nasl okunduunu bile bilemeyeceim toplam deer adedi ise ou kavram iin fazlasyla yeterlidir. Temel bir kural olarak, zel bir neden yoksa, tamsaylar iin ncelikle int 'i dnebilirsiniz.

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

40

Tamsaylar ve Aritmetik lemler

15.1.4

Tama
Trlerin bit saylaryla belirlenen bu kstlamalar, onlarla yaplan ilemlerde garip sonulara neden olur. rnein deerleri 3 milyar olan iki uint 'in toplam gerekte 6 milyar olsa da, en fazla 4 milyar kadar deer saklayabilen uint 'e smaz. Bu durumda sonu uint 'ten tam olur; programda hibir uyar verilmeden 6 milyarn ancak 4 milyardan geri kalan, yani 2 milyar kadar sonu deikeninde kalr. (Aslnda 6 milyar eksi 4.3 milyar, yani yaklak olarak 1.7 milyar...)

15.1.5

Krplma
Tamsaylar kesirli deerler tutamadklar iin ne kadar nemli olsa da, virglden sonraki bilgiyi kaybederler. rnein 1 kutusu 4 ocua yeten dondurmadan 11 ocuk iin 2.75 kutu gerekiyor olsa bile, bu deer bir tamsay tr iinde ancak 2 olarak saklanabilir. Tamaya ve krplmaya kar alabileceiniz baz nlemleri ilemlerin tantmndan sonra vereceim. nce aritmetik ilemleri tanyalm.

15.1.6

Tr nitelikleri hatrlatmas
Temel trlerin tantld derste tr niteliklerini grmtk: .min , trn alabilecei en kk deeri; .max da en byk deeri veriyordu.

15.1.7

Arttrma: ++
Tek bir deikenle kullanlr. Deikenin isminden nce yazlr ve o deikenin deerini 1 arttrr: import std.stdio; void main() { int say = 10; ++say; writeln("Yeni deeri: ", say); }

Yeni deeri: 11 Arttrma ileci, biraz aada greceiniz atamal toplama ilecinin 1 deeri ile kullanlmasnn edeeridir: say += 1; // ++say ifadesinin ayns

Arttrma ileminin sonucu; eer trn tayabilecei en yksek deeri ayorsa, o zaman taar ve trn alabildii en dk deere dnr. Bunu denemek iin nceki deeri int.max olan bir deikeni arttrrsak, yeni deerinin int.min olduunu grrz: import std.stdio; void main() { writeln("en dk int deeri writeln("en yksek int deeri

: ", int.min); : ", int.max);

int say = int.max; writeln("saynn nceki deeri : ", say); ++say;

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

41

Tamsaylar ve Aritmetik lemler

writeln("saynn sonraki deeri: ", say);

en dk int deeri : en yksek int deeri : saynn nceki deeri : saynn sonraki deeri:

-2147483648 2147483647 2147483647 -2147483648

Bu ok nemli bir konudur; nk say hibir uyar verilmeden, en yksek deerinden en dk deerine gemektedir; hem de arttrma ilemi sonucunda! Buna stten tama denir. Benzer tama davranlarn azaltma, toplama, ve karma ilemlerinde de greceiz.
15.1.8

Azaltma: -Tek bir deikenle kullanlr. Deikenin isminden nce yazlr ve o deikenin deerini 1 azaltr: --say; // deeri bir azalr

Azaltma ileci, biraz aada greceiniz atamal karma ilecinin 1 deeri ile kullanlmasnn edeeridir: say -= 1; // --say ifadesinin ayns

++ ilecine benzer ekilde, eer deikenin deeri batan o trn en dk deerindeyse, yeni deeri o trn en yksek deeri olur. Buna da alttan tama ad verilir.
15.1.9

Toplama: +
ki ifadeyle kullanlr ve aralarna yazld iki ifadenin toplamn verir: import std.stdio; void main() { int birinci = 12; int ikinci = 100; writeln("Sonu: ", birinci + ikinci); writeln("Sabit ifadeyle: ", 1000 + ikinci);

Eer iki ifadenin deeri o trde saklanabilecek en yksek deerden fazlaysa, yine tama oluur ve deerlerin ikisinden de daha kk bir sonu elde edilir: import std.stdio; void main() { // ki tane 3 milyar uint birinci = 3000000000; uint ikinci = 3000000000; writeln("uint'in en yksek deeri: ", uint.max); writeln(" birinci: ", birinci); writeln(" ikinci: ", ikinci); writeln(" toplam: ", birinci + ikinci);

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

42

Tamsaylar ve Aritmetik lemler

writeln("TAMA! Sonu 6 milyar olmad!");

uint'in en yksek deeri: 4294967295 birinci: 3000000000 ikinci: 3000000000 toplam: 1705032704 TAMA! Sonu 6 milyar olmad!

15.1.10

karma: ki ifadeyle kullanlr ve birinci ile ikincinin farkn verir: import std.stdio; void main() { int say_1 = 10; int say_2 = 20; writeln(say_1 - say_2); writeln(say_2 - say_1);

-10 10 Eer sonucu tutan deer iaretsizse ve sonu eksi bir deer alrsa, yine garip sonular doar. Yukardaki program uint iin tekrar yazarsak: import std.stdio; void main() { uint say_1 = 10; uint say_2 = 20; writeln("SORUN! uint eksi deer tutamaz:"); writeln(say_1 - say_2); writeln(say_2 - say_1);

SORUN! uint eksi deer tutamaz: 4294967286 10 Eninde sonunda farklar alnacak kavramlar iin hep iaretli trlerden semek iyi bir karardr. Yine, zel bir neden yoksa normalde int 'i seebilirsiniz.
15.1.11

arpma: *
ki ifadenin deerlerini arpar. Yine tamaya maruzdur: import std.stdio; void main() { uint say_1 = 6;

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

43

Tamsaylar ve Aritmetik lemler

uint say_2 = 7; } writeln(say_1 * say_2);

42

15.1.12

Blme: /
Birinci ifadeyi ikinci ifadeye bler. Tamsaylar kesirli say tutamayacaklar iin, eer varsa sonucun kesirli ksm atlr. Buna krplma denir. rnein bu yzden aadaki program 3.5 deil, 3 yazmaktadr: import std.stdio; void main() { writeln(7 / 2); }

15.1.13

Kalan: %
Birinci ifadeyi ikinci ifadeye bler ve kalann verir. rnein 10'un 6'ya blmnden kalan 4 olduu iin, aadaki program 4 yazmaktadr: import std.stdio; void main() { writeln(10 % 6); }

4 Bu ile bir saynn tek veya ift olduunu anlamada kullanlr. Tek saylarn ikiye blmnden kalan her zaman iin 1 olduundan, kalann 0 olup olmadna bakarak saynn tek veya ift olduu kolayca anlalr: if ((say % 2) == 0) { writeln("ift say"); } else { writeln("tek say"); }

15.1.14

s alma: ^^
Birinci ifadenin ikinci ifade ile belirtilen ssn alr. rnein 3 ss 4, 3'n 4 kere kendisiyle arpmdr: import std.stdio; void main() { writeln(3 ^^ 4); }

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

44

Tamsaylar ve Aritmetik lemler

81

15.1.15

Atamal aritmetik ileleri


Yukarda gsterilen ve iki ifade alan aritmetik ilelerin atamal olanlar da vardr. Bunlar ilemi gerekletirdikten sonra ek olarak sonucu sol taraftaki deikene atarlar: import std.stdio; void main() { int say = 10; say say say say say say } += 20; -= 5; *= 2; /= 3; %= 7; ^^= 6; // // // // // // say say say say say say = = = = = = say say say say say say + 20 - 5 * 2 / 3 % 7 ^^ 6 ile ile ile ile ile ile ayn ayn ayn ayn ayn ayn ey; ey; ey; ey; ey; ey; imdi imdi imdi imdi imdi imdi 30 25 50 16 2 64

writeln(say);

64

15.1.16

Eksi iareti: nne yazld ifadenin deerini artysa eksi, eksiyse art yapar: import std.stdio; void main() { int say_1 = 1; int say_2 = -2; writeln(-say_1); writeln(-say_2);

-1 2 Bu ilecin sonucunun tr, deikenin tr ile ayndr. uint gibi iaretsiz trler eksi deerler tutamadklar iin, bu ilecin onlarla kullanlmas artc sonular dourabilir: uint say = 1; writeln("eksi iaretlisi: ", -say); -say ifadesinin tr de uint 'tir ve o yzden eksi deer alamaz: eksi iaretlisi: 4294967295

15.1.17

Art iareti: +
Matematikte saylarn nne yazlan + iareti gibi bunun da hibir etkisi yoktur. fadenin deeri eksiyse yine eksi, artysa yine art kalr:

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

45

Tamsaylar ve Aritmetik lemler

import std.stdio; void main() { int say_1 = 1; int say_2 = -2; writeln(+say_1); writeln(+say_2);

1 -2

15.1.18

nceki deerli arttrma: ++


Not: zel bir nedeni yoksa normal arttrma ilecini kullanmanz neririm. Normal arttrma ilecinden farkl olarak ifadeden sonra yazlr. Yukarda anlatlan ++ ilecinde olduu gibi ifadenin deerini bir arttrr, ama iinde getii ifadede nceki deeri olarak kullanlr. Bunun etkisini grmek iin normal ++ ileciyle karlatralm: import std.stdio; void main() { int normal_arttrlan = 1; writeln(++normal_arttrlan); writeln(normal_arttrlan); int nceki_deerli_arttrlan = 1; // Deeri arttrlr ama ifadede nceki deeri kullanlr: writeln(nceki_deerli_arttrlan++); // 1 yazlr writeln(nceki_deerli_arttrlan); // 2 yazlr

// 2 yazlr // 2 yazlr

2 2 1 2 Yukardaki arttrma ileminin olduu satr unun edeeridir: int nceki_deeri = nceki_deerli_arttrlan; ++nceki_deerli_arttrlan; writeln(nceki_deeri); // 1 yazlr Yani bir anlamda, say arttrlmtr, ama iinde bulunduu ifadede nceki deeri kullanlmtr.
15.1.19

nceki deerli azaltma: -Not: zel bir nedeni yoksa normal azaltma ilecini kullanmanz neririm. nceki deerli arttrma ++ ile ayn ekilde davranr ama arttrmak yerine azaltr.

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

46

Tamsaylar ve Aritmetik lemler

15.1.20

lem ncelikleri
Yukardaki ilemleri hep tek balarna ve bir veya iki ifade ile grdk. Oysa mantksal ifadelerde olduu gibi, birden fazla aritmetik ilemi bir arada kullanarak daha karmak ilemler oluturabiliriz: int say = 77; int sonu = (((say + 8) * 3) / (say - 1)) % 5; Mantksal ifadelerde olduu gibi, bu ilelerin de D tarafndan belirlenmi olan ncelikleri vardr. rnein * ilecinin ncelii + ilecininkinden yksek olduu iin, parantezler kullanlmadnda, say + 8 * 3 ifadesi, nce * ilemi uygulanaca iin say + 24 olarak hesaplanr. Bu da yukardakinden farkl bir ilemdir. O yzden, parantezler kullanarak hem ilemleri doru srada uygulatm olursunuz, hem de kodu okuyan kiilere kodu anlamalarnda yardmc olmu olursunuz.

15.1.21

Tamaya kar nlemler


Eer bir ilemin sonucu seilen tre smyorsa, zaten yaplacak bir ey yoktur. Ama bazen sonu sacak olsa da ara ilemler srasnda oluabilecek tamalar nedeniyle yanl sonular elde edilebilir. Bir rnee bakalm: kenarlar 40'a 60 kilometre olan bir alann her 1000 metre karesine bir elma aac dikmek istiyoruz. Ka aa gerekir? Bu problemi kat kalemle znce sonucun 40000 arp 60000 bl 1000 olarak 2.4 milyon olduunu grrz. Bunu hesaplayan bir programa bakalm: import std.stdio; void main() { int en = 40000; int boy = 60000; int aa_bana_yer = 1000; int gereken_aa = en * boy / aa_bana_yer; } writeln("Gereken elma aac: ", gereken_aa);

Gereken elma aac: -1894967 Brakn yakn olmay, bu sonu sfrdan bile kktr! Bu sorunun nedeni, programdaki en * boy alt ileminin bir int 'e samayacak kadar byk olduu iin tamas, ve bu yzden de geri kalan / aa_bana_yer ileminin de yanl kmasdr. Buradaki ara ilem srasnda oluan tamay deiken sralarn deitirerek giderebiliriz: int gereken_aa = en / aa_bana_yer * boy; imdi hesap doru kar: Gereken elma aac: 2400000 Bu ifadenin doru almasnn nedeni, imdiki ara ilem olan en / aa_bana_yer ifadesinin deerinin 40 olduu iin artk int 'ten tamamasdr.

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

47

Tamsaylar ve Aritmetik lemler

Aslnda byle bir durumda en doru zm; bir tamsay tr deil, kesirli say trlerinden birisini kullanmaktr: float , double , veya real .
15.1.22

Krplmaya kar nlemler


Benzer ekilde, ara ilemlerin srasn deitirerek krplmann da etkisini azaltabiliriz. Bunun ilgin bir rneini, ayn sayya blp yine ayn sayyla arptmzda grebiliriz: 10/9*9 ileminin sonucunun 10 kmasn bekleriz. Oysa: import std.stdio; void main() { writeln(10 / 9 * 9); }

9 Yine, ilemlerin srasn deitirince krplma olmayaca iin sonu doru kar: writeln(10 * 9 / 9);

10 Burada da en iyi zm belki de bir kesirli say tr kullanmaktr.

15.2

Problemler
1. Yazacanz program kullancdan iki tamsay alsn ve birincinin iinde ikinciden ka tane bulunduunu ve artann versin. rnein 7 ve 3 deerleri girilince ka unu yazsn: 7 = 3 * 2 + 1 2. Ayn program, kalan 0 olduunda daha ksa sonu verecek ekilde deitirin. rnein 10 ve 5 verince gereksizce "10 = 5 * 2 + 0" yazmak yerine, yalnzca yeterli bilgiyi versin: 10 = 5 * 2 3. Drt ilemi destekleyen basit bir hesap makinesi yazn. lemi bir menden setirsin ve girilen iki deere o ilemi uygulasn. Bu programda tama ve krplma sorunlarn gzard edebilirsiniz. 4. Yazacanz program 1'den 10'a kadar btn saylar ayr satrlarda olacak ekilde yazdrsn. Ama, bir istisna olarak 7 deerini yazdrmasn. Programda u ekilde tekrarlanan writeln ifadeleri kullanmayn: import std.stdio; void main() { // Byle yapmayn! writeln(1); writeln(2); writeln(3); writeln(4); writeln(5); writeln(6); writeln(8);

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

48

Tamsaylar ve Aritmetik lemler

writeln(9); writeln(10);

Onun yerine, bir dng iinde deeri arttrlan bir deiken dnn ve 7'yi yazdrmama kouluna da dikkat edin. Burada herhalde eit olmama koulunu denetleyen != ilecini kullanmak zorunda kalacaksnz. ... zmler

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

49

Kesirli Saylar

16

Kesirli Saylar
Tamsaylarn ve aritmetik ilemlerin olduka kolay olduklarn, buna ramen yaplarndan kaynaklanan tama ve krplma gibi zellikleri olduunu grdk. Bu blmde de biraz ayrntya girmek zorundayz. Eer aadaki listedeki hereyi bildiinizi dnyorsanz, ayrntl bilgileri okumayp dorudan problemlere geebilirsiniz: Bin kere 0.001 eklemek 1 eklemekle ayn ey deildir == veya != mantksal ifadelerini kesirli say trleriyle kullanmak ou durumda hataldr Kesirli saylarn ilk deerleri 0 deil, .nan 'dr; .nan deeriyle ilem yaplamaz; baka bir deerle karlatrldnda .nan ne kktr ne de byk stten tama deeri .infinity , alttan tama deeri -.infinity 'dir Kesirli say trleri ok daha kullanldrlar ama onlarn da mutlaka bilinmesi gereken zellikleri vardr. Krplma konusunda ok iyidirler, nk zaten zellikle virglden sonras iin tasarlanmlardr. Belirli sayda bitle snrl olduklar iin tama bu trlerde de vardr ancak alabildikleri deer aral tamsaylarla karlatrldnda olaanst genitir. Ek olarak, tamsay trlerinin tama durumunda sessiz kalmalarnn aksine, kesirli saylar "sonsuzluk" deerini alrlar. nce kesirli say trlerini hatrlayalm: Tr float double real Bit Uzunluu 32 64 en az 64, veya donanm salyorsa daha fazla (rnein 80) lk Deeri float.nan double.nan real.nan

16.1

Kesirli tr nitelikleri
Kesirli trlerin nitelikleri tamsaylardan daha fazladr: .stringof trn okunakl ismidir .sizeof trn bayt olarak uzunluudur; trn ka bitten olutuunu hesaplamak iin bu deeri bir bayttaki bit says olan 8 ile arpmak gerekir .max "en ok" anlamna gelen "maximum"un ksaltmasdr; trn alabilecei en byk deerdir; bu deerin eksi iaretlisi de trn alabilecei en dk deer olur .min_normal "ifade edebildii sfra en yakn normalize deer" anlamndadr (tr aslnda bundan daha kk deerler de ifade edebilir ama o deerlerin duyarl trn normal duyarlnn altndadr) .dig "basamak says" anlamna gelen "digits"in ksaltmasdr; trn ka basamak duyarl olduunu belirtir .infinity "sonsuz" anlamna gelir; tama durumunda kullanlan deerdir Not: "Trn alabilecei en kk deer", .min deildir; .max 'n eksi iaretlisidir: rnein double.max . Dier niteliklere bu aamada gerek olduunu dnmyorum; btn nitelikleri Properties for Floating Point Types bal altnda bulabilirsiniz. Yukardaki nitelikleri, birbirleriyle olan ilikilerini grmek iin bir say izgisine yle yerletirebiliriz:

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

50

Kesirli Saylar

+ +-----------+------------+ .. + .. +----------+----------+ + | -max -1 -min 0 min 1 max | -infinity infinity

Yukardaki izginin leinin doru olduunu vurgulamak istiyorum: min ile 1 arasnda ne kadar deer ifade edilebiliyorsa, 1 ile max arasnda da ayn sayda deer ifade edilir. Bu da, min ile 1 arasndaki deerlerin son derece yksek dorulukta olduklar anlamna gelir. (Ayn durum eksi taraf iin de geerlidir.)

16.2

.nan
Aka ilk deeri verilmeyen kesirli saylarn ilk deerlerinin .nan olduunu grdk. .nan deeri baz anlamsz ilemler sonucunda da ortaya kabilir. rnein u programdaki ifadelerin hepsi .nan sonucunu verir: import std.stdio; void main() { double sfr = 0; double sonsuz = double.infinity; writeln("nan kullanan her ilem: writeln("sfr bl sfr : writeln("sfr kere sonsuz : writeln("sonsuz bl sonsuz : writeln("sonsuz eksi sonsuz : ", ", ", ", ", double.nan + 1); sfr / sfr); sfr * sonsuz); sonsuz / sonsuz); sonsuz - sonsuz);

16.3

Kesirli saylarn yazmlar


Bu trn niteliklerine bakmadan nce kesirli saylarn nasl yazldklarn grelim. Kesirli saylar 123 gibi tamsay eklinde veya 12.3 gibi noktal olarak yazabiliriz. Ek olarak, 1.23e+4 gibi bir yazmdaki e+ , "arp 10 zeri" anlamna gelir. Yani bu rnek 1.23x104'tr, bir baka deyile "1.23 arp 10000"dir ve ifadenin deeri 12300'dr. Eer e 'den sonra gelen deer eksi ise, yani rnein 5.67e-3 gibi yazlmsa, o zaman "10 zeri o kadar deere blnecek" demektir. Yani bu rnek 5.67/103'tr, bir baka deyile "5.67 bl 1000"dir ve ifadenin deeri 0.00567'dir. Kesirli saylarn bu gsterimlerini, trlerin niteliklerini yazdran u programn ktsnda greceksiniz: import std.stdio; void main() { writeln("Tr ismi writeln("Duyarlk writeln("En kk writeln("En byk writeln(); writeln("Tr ismi writeln("Duyarlk writeln("En kk writeln("En byk writeln(); writeln("Tr ismi writeln("Duyarlk writeln("En kk writeln("En byk }

: : normalize deeri: deeri : : : normalize deeri: deeri : : : normalize deeri: deeri :

", ", ", ", ", ", ", ", ", ", ", ",

float.stringof); float.dig); float.min_normal); float.max); double.stringof); double.dig); double.min_normal); double.max); real.stringof); real.dig); real.min_normal); real.max);

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

51

Kesirli Saylar

Benim ortammdaki kts aadaki gibi oldu; real tr donanma bal olduu iin bu kt sizin ortamnzda farkl olabilir: Tr ismi : float Duyarlk : 6 En kk normalize deeri: 1.17549e-38 En byk deeri : 3.40282e+38 Tr ismi : double Duyarlk : 15 En kk normalize deeri: 2.22507e-308 En byk deeri : 1.79769e+308 Tr ismi : real Duyarlk : 18 En kk normalize deeri: 3.3621e-4932 En byk deeri : 1.18973e+4932

16.4

Gzlemler
ulong trnn tutabilecei en yksek deerin ne kadar ok basama olduunu hatrlyor musunuz: 18,446,744,073,709,551,616 says 20 basamaktan oluur. Buna karn, en kk kesirli say tr olan float 'un bile tutabilecei en yksek deer 1038 mertebesindedir. Yani unun gibi bir deer: 340,282,000,000,000,000,000,000,000,000,000,000,000. real 'in en byk deeri ise 104932 mertebesinde. Yani 4900'den fazla basama olan bir say! Baka bir gzlem olarak double 'n 15 duyarlkla ifade edebilecei en dk deere bakalm: 0.000...burada 300 tane daha 0 var...0000222507.

16.5

Tama gzard edilmez


Ne kadar byk deerler tutuyor olsalar da kesirli saylarda da tama olabilir. Kesirli say trlerinin iyi taraf, tama olutuunda tamsaylardaki tamann tersine bundan haberimizin olabilmesidir: taan saynn deeri "art sonsuz" iin .infinity , "eksi sonsuz" iin .infinity haline gelir. Bunu grmek iin u programda .max 'n deerini %10 arttrmaya alalm. Say zaten en byk deerinde olduu iin, %10 arttrnca taacak ve yarya blnse bile deeri "sonsuz" olacaktr: import std.stdio; void main() { real say = real.max; writeln("nce: ", say); // 1.1 ile arpmak, %110 haline getirmektir: say *= 1.1; writeln("%10 arttrnca: ", say); // kiye blerek kltmeye alalm: say /= 2; writeln("Yarya blnce: ", say);

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

52

Kesirli Saylar

O programda say bir kere real.infinity deerini alnca yarya blnse bile sonsuz deerinde kalr: nce: 1.18973e+4932 %10 arttrnca: inf Yarya blnce: inf

16.6

Duyarlk (Hassasiyet)
Duyarlk, yine gnlk hayatta ok karlatmz ama fazla szn etmediimiz bir kavramdr. Duyarlk, bir deeri belirtirken kullandmz basamak saysdr. rnein 100 lirann te birinin 33 lira olduunu sylersek, duyarlk 2 basamaktr. nk 33 deeri sadece iki basamaktan ibarettir. Daha hassas deerler gereken bir durumda 33.33 dersek, bu sefer drt basamak kullanm olduumuz iin duyarlk 4 basamaktr. Kesirli say trlerinin bit olarak uzunluklar yalnzca alabilecekleri en dk ve en yksek deerleri deil; deerlerin duyarlklarn da etkiler. Bit olarak uzunluklar ne kadar fazlaysa, duyarlklar da o kadar fazladr.

16.7

Krplma yoktur
Kesirli saylarda virglden sonrasn atmak anlamnda krplma yoktur. Saynn doruluu duyarlyla ilgilidir: virglden sonraki basamaklarn ne kadar doru olduklarn duyarlk belirler.

16.8

Hangi durumda hangi tr


zel bir neden yoksa her zaman iin real 'i kullanmak doruluk asndan yararldr. float 'un duyarl ok dktr, ama kk olmasnn yarar salayaca nadir programlardan birisini yazyorsanz, o zaman dnerek ve lerek karar verebilirsiniz. te yandan real 'in duyarl her ortamda ayn olmad iin, tanabilirlik asndan double kullanmak isteyebilirsiniz.

16.9

Her deeri ifade etmek olanakszdr


Her deerin ifade edilememesi kavramn nce gnlk hayatmzda gstermek istiyorum. Kullandmz onlu say sisteminde virglden nceki basamaklar birler, onlar, yzler, vs. basamaklardr; virglden sonrakiler de onda birler, yzde birler, binde birler, vs... Eer ifade etmek istediimiz deer bu basamaklarn bir karm ise, deeri tam olarak ifade edebiliriz. rnein 0.23 deeri 2 adet onda bir deerinden ve 3 adet yzde bir deerinden olutuu iin tam olarak ifade edilebilir. te yandan, 1/3 deerini onlu sistemimizde tam olarak ifade edemeyiz nk virglden sonra ne kadar uzatrsak uzatalm yeterli olmaz: 0.33333... Benzer durum kesirli saylarda da vardr. Trlerin bit saylar snrl olduu iin, her deer tam olarak ifade edilemez. Bilgisayarlarda kullanlan ikili say sistemlerinin bir fark, virglden ncesinin birler, ikiler, drtler, vs. diye; virglden sonrasnn da yarmlar, drtte birler, sekizde birler, vs. diye gitmesidir. Eer deer bunlarn bir karm ise tam olarak ifade edilebilir; deilse edilemez. Bilgisayarlarda tam olarak ifade edilemeyen bir deer 0.1'dir (10 kuru gibi). Onlu sistemde tam olarak 0.1 eklinde ifade edilebilen bu deer, ikili sistemde 0.0001100110011... diye

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

53

Kesirli Saylar

tekrarlar ve kesirli saynn duyarlna bal olarak belirli bir yerden sonra hataldr. (Tekrarladn sylediim o son sayy ikili sistemde yazdm, onlu deil...) Bunu gsteren aadaki rnei ilgin bulabilirsiniz. Bir deikenin deerini bir dng iinde her seferinde 0.001 arttralm. Dngnn 1000 kere tekrarlanmasnn ardndan sonucun 1 olmasn bekleriz. Oysa yle kmaz: import std.stdio; void main() { float sonu = 0; // Bu dngnn 1000 kere tekrarlandktan sonra 1 deerine // ulaacan dnrz: while (sonu < 1) { sonu += 0.001; } // Bakalm doru mu... if (sonu == 1) { writeln("Beklendii gibi 1"); } else { writeln("FARKLI: ", sonu); }

FARKLI: 1.00099 Bunun nedeni; 0.001 deerinin de tam olarak ifade edilemeyen bir deer olmas, ve bu deerdeki hata miktarnn sonucu 1000 kere etkilemesidir. Sonutan da anlalaca gibi, dngden klabilmesi iin 1001 kere tekrarlanmas gerekmitir.

16.10

Kesirli say karlatrmalar


Tamsaylarda u karlatrma ilelerini kullanyorduk: eitlik (== ), eit olmama (!= ), kklk (< ), byklk (> ), kk veya eit olma (<= ), byk veya eit olma (>= ). Kesirli saylarda geersiz deeri gsteren .nan da bulunduu iin, onun dier deerlerle kk byk olarak karlatrlmas anlamszdr. rnein .nan 'n m yoksa 1'in mi daha byk olduu gibi bir soru yantlanamaz. Bu yzden kesirli saylarda baka bir karlatrma kavram daha vardr: sraszlk. Sraszlk, deerlerden en az birisinin .nan olmas demektir. Aadaki tablo kesirli say karlatrma ilelerini gsteriyor. lelerin hepsi ikilidir ve rnein soldaki == sadaki eklinde kullanlr. false ve true ieren stunlar, ilelerin hangi durumda ne sonu verdiini gsterir. Sonuncu stun, ifadelerden birisinin .nan olmas durumunda o ilecin kullanmnn anlaml olup olmadn gsterir. rnein 1.2 < real.nan ifadesinin sonucu false ksa bile, ifadelerden birisi real.nan olduu iin bu sonucun bir anlam yoktur nk bunun tersi olan real.nan < 1.2 ifadesi de false verir.

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

54

Kesirli Saylar

le == != > >= < <= !<>= <> <>= !<= !< !>= !> !<>

Anlam eittir eit deildir byktr byktr veya eittir kktr kktr veya eittir kk, byk, eit deildir kktr veya byktr kktr, byktr, veya eittir kk deildir ve eit deildir kk deildir byk deildir ve eit deildir byk deildir kk deildir ve byk deildir

Soldaki Soldaki kisi Bykse Kkse Eitse 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 .nan ise false true false false false false true false false true true true true true

.nan ile Anlaml evet evet hayr hayr hayr hayr evet hayr hayr evet evet evet evet evet

Dikkat ederseniz; ! karakteri ieren, yani anlamnda "deildir" sz bulunan btn ilelerin .nan ile kullanlmas anlamldr ve sonu hep true 'dur. .nan 'n geerli bir deeri gstermiyor olmas, ou karlatrmaya "deil" sonucunu verdirir.

16.11

Problemler
1. nceki blmdeki hesap makinesini kesirli bir tr kullanacak ekilde deitirin. Bylece hesap makineniz ok daha doru sonular verecektir. Denerken deerleri girmek iin 1000, 1.23, veya 1.23e4 eklinde yazabilirsiniz. 2. Giriten 5 tane kesirli say alan bir program yazn. Bu saylarn nce iki katlarn yazsn, sonra da bee blmlerini. Bu problemi bir sonra anlatlacak olan dizilere hazrlk olarak soruyorum. Eer bu program imdiye kadar rendiklerinizle yazarsanz, dizileri anlamanz daha kolay olacak. ... zmler

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

55

Diziler

17

Diziler
Bir nceki dersin problemlerinden birisinde 5 tane deiken tanmlam ve onlarla belirli ilemler yapmtk: nce iki katlarn almtk, sonra da bee blmtk. O deikenleri ayr ayr yle tanmlamtk: double double double double double say_1; say_2; say_3; say_4; say_5;

Bu yntem her duruma uygun deildir, nk deiken says arttnda onlar teker teker tanmlamak, iinden klmaz bir hal alr. Bin tane sayyla ilem yapmak gerektiini dnn... Bin tane deikeni ayr ayr say_1, say_2, ..., say_1000 diye tanmlamak hemen hemen olanaksz bir itir. Dizilerin bir yarar byle durumlarda ortaya kar: diziler bir seferde birden fazla deiken tanmlamaya yarayan olanaklardr. Birden fazla deikeni bir araya getirmek iin en ok kullanlan veri yaps da dizidir.

17.1

Tanmlanmas
Dizi tanm deiken tanmna ok benzer. Tek fark, dizide ka deiken bulunacann, yani bir seferde ka deiken tanmlanmakta olduunun, trn isminden sonraki keli parantezler iinde belirtilmesidir. Tek bir deikenin tanmlanmas ile bir dizinin tanmlanmasn yle karlatrabiliriz: int tekDeiken; int[10] onDeikenliDizi; O iki tanmdan birincisi, imdiye kadarki kodlarda grdklerimiz gibi tek deiken tanmdr; ikincisi ise 10 deikenden oluan bir dizidir. Yukarda sz geen problemdeki 5 ayr deikeni 5 elemanl bir dizi halinde hep birden tanmlamak iin u sz dizimi kullanlr: double[5] saylar; Bu tanm, "double trnde 5 tane say" diye okunabilir. Daha sonra kod iinde kullanldnda tek bir say deikeni sanlmasn diye ismini de oul olarak setiime dikkat edin. zetle; dizi tanm, tr isminin yanna keli parantezler iinde yazlan dizi uzunluundan ve bunlar izleyen dizi isminden oluur: tr_ismi[dizi_uzunluu] dizi_ismi; Tr ismi olarak temel trler kullanlabilecei gibi, programcnn tanmlad daha karmak trler de kullanlabilir (bunlar daha sonra greceiz). rnekler: // Btn ehirlerdeki hava durumlarn tutan bir dizi // Burada rnein // false: "kapal hava" // true : "ak hava" // anlamnda kullanlabilir bool[ehirAdedi] havaDurumlar;

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

56

Diziler

// Yz kutunun arlklarn ayr ayr tutan bir dizi double[100] kutuArlklar; // Bir okuldaki btn rencilerin kaytlar renciBilgisi[renciAdedi] renciKaytlar;

17.2

Topluluklar ve elemanlar
Ayn trden deikenleri bir araya getiren veri yaplarna topluluk ad verilir. Bu tanma uyduklar iin diziler de toplulukturlar. rnein Temmuz ayndaki gnlk hava scaklklarn tutmak iin kullanlacak bir dizi, 31 tane double deikenini bir araya getirebilir ve bir hava scakl topluluu oluturur. Topluluk deikenlerinin her birisine eleman denir. Dizilerin barndrdklar eleman adedine dizilerin uzunluu denir. "Eleman adedi" ve "dizi uzunluu" ifadelerinin ikisi de sk kullanlr.

17.3

Eleman eriimi
Problemdeki deikenleri ayrt etmek iin isimlerinin sonuna bir alt izgi karakteri ve bir sra numaras eklemitik: say_1 gibi... Saylar hep birden bir dizi halinde ve saylar isminde tanmlaynca elemanlara farkl isimler verme ansmz kalmaz. Onun yerine, elemanlara dizinin eriim ileci olan [] ile ve bir sra numarasyla eriilir: saylar[0] O yazm, "saylarn 0 numaral eleman" diye okunabilir. Bu ekilde yaznca say_1 ifadesinin yerini saylar[0] ifadesi alm olur. Burada dikkat edilmesi gereken iki nokta vardr: Numara sfrdan balar: Biz insanlar nesneleri 1'den balayacak ekilde numaralamaya alk olduumuz halde, dizilerde numaralar 0'dan balar. Bizim 1, 2, 3, 4, ve 5 olarak numaraladmz saylar dizi iinde 0, 1, 2, 3, ve 4 olarak numaralanrlar. Bu uyumsuzlua zellikle dikkat etmek gerekir, nk baz programc hatalarnn kayna bu uyumsuzluktur. [] karakterlerinin iki farkl kullanm: Dizi tanmlarken kullanlan [] karakterleri ile eriim ileci olarak kullanlan [] karakterlerini kartrmayn. Dizi tanmlarken kullanlan [] karakterleri elemanlarn trnden sonra yazlr ve dizide ka eleman bulunduunu belirler; eriim iin kullanlan [] karakterleri ise dizinin isminden sonra yazlr ve elemann sra numarasn belirler: // Bu bir tanmdr... 12 tane int'ten olumaktadr ve her // ayda ka gn bulunduu bilgisini tutmaktadr int[12] ayGnleri; // Bu bir eriimdir... Aralk ayna karlk gelen elemana // eriir ve deerini 31 olarak belirler ayGnleri[11] = 31; // Bu da bir eriimdir... Ocak ayndaki gn saysn // yazdrmaktadr writeln("Ocak'ta ", ayGnleri[0], " gn var"); Hatrlatma: Ocak aynn sra numarasnn 0, Aralk aynn sra numarasnn 11 olduuna dikkat edin.

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

57

Diziler

17.4

ndeks
Elemanlara eriirken kullanlan sra numaralarna indeks, elemanlara erime iine de indeksleme denir. ndeks sabit bir deer olmak zorunda deildir; indeks olarak deiken deerleri de kullanlabilir. Bu olanak dizilerin kullanlln byk lde arttrr. rnein aadaki kodda hangi aydaki gn saysnn yazdrlacan ayNumaras deikeni belirlemektedir: writeln("Bu ay ", ayGnleri[ayNumaras], " gn eker"); ayNumaras 'nn 2 olduu bir durumda yukardaki ifadede ayGnleri[2] 'nin deeri, yani Mart ayndaki gn adedi yazdrlr. ayNumaras 'nn baka bir deerinde de o aydaki gn says yazdrlr. Yasal olan indeksler, 0'dan dizinin uzunluundan bir eksiine kadar olan deerlerdir. rnein 3 elemanl bir dizide yalnzca 0, 1, ve 2 indeksleri yasaldr. Bunun dnda indeks kullanldnda program bir hata ile sonlanr. Dizileri, elemanlar yan yana duran bir topluluk olarak dnebilirsiniz. rnein aylarn gnlerini tutan bir dizinin elemanlar ve indeksleri u ekilde gsterilebilir (ubat'n 28 gn ektiini 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 elemann indeksi 0, ve Ocak ayndaki gn says olan 31 deerine sahip; ikinci elemann indeksi 1, ve ubat ayndaki gn says olan 28 deerine sahip; vs.

17.5

Sabit uzunluklu diziler ve dinamik diziler


Ka eleman barndracaklar programn yazld srada bilinen dizilere sabit uzunluklu dizi; elemanlarnn says programn almas srasnda deiebilen dizilere dinamik dizi denir. Yukarda 5 say tanmlamak iin kullandmz saylar dizisi ve 12 aydaki gn saylarn tutmak iin kullandmz ayGnleri dizileri sabit uzunluklu dizilerdir; nk eleman saylar batan belirlenmitir. O dizilerin uzunluklar programn almas srasnda deitirilemez. Uzunluklarnn deimesi gerekse, bu ancak kaynak koddaki sabit olan deerin elle deitirilmesi ve programn tekrar derlenmesi ile mmkndr. Dinamik dizi tanmlamak belki de sabit uzunluklu dizi tanmlamaktan daha kolaydr, nk dizinin uzunluunu bo brakmak diziyi dinamik yapmaya yeter: int[] dinamikDizi; Byle dizilerin uzunluklar programn almas srasnda gerektike arttrlabilir veya azaltlabilir.

17.6

Dizi nitelikleri
Trlerin olduu gibi dizilerin de nitelikleri vardr. Burada yalnzca tanesini tanyacaz. .length niteliinin dinamik dizilerde farkl olduuna dikkat edin: .length : Dizinin uzunluunu, yani iindeki eleman saysn verir: writeln("Dizide ", dizi.length, " tane eleman var");

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

58

Diziler

Ek olarak, dinamik dizilerde dizinin uzunluunu deitirmeye de yarar: int[] dizi; dizi.length = 5; // botur // uzunluu 5 olur

.sort : Elemanlar kkten bye doru sralar: dizi.sort; .reverse : Elemanlar ters srada sralar: dizi.reverse; Bu bilgiler nda 5 deikenli probleme dnelim ve onu dizi kullanacak ekilde tekrar yazalm: import std.stdio; void main() { // Bu deikeni dngleri ka kere tekrarladmz saymak // iin kullanacaz int saya; // double trndeki be elemandan oluan sabit uzunluklu // bir dizi tanmlyoruz double[5] saylar; // Saylar bir dng iinde giriten alyoruz while (saya < saylar.length) { write("Say ", saya + 1, ": "); readf(" %s", &saylar[saya]); ++saya; } writeln("ki katlar:"); saya = 0; while (saya < saylar.length) { writeln(saylar[saya] * 2); ++saya; } // Bete birlerini hesaplayan dng de bir nceki // dngnn benzeridir...

Gzlemler: Dnglerin ka kere tekrarlanacaklarn saya belirliyor: dngleri, o deikenin deeri saylar.length 'ten kk olduu srece tekrarlyoruz. Sayacn deeri her tekrarda bir arttka, saylar[saya] ifadesi de srayla dizinin elemanlarn gstermi oluyor: saylar[0] , saylar[1] , vs. Bu programn yararn grmek iin giriten 5 yerine rnein 20 say alnacan dnn... Dizi kullanan bu programda tek bir yerde kk bir deiiklik yapmak yeter: 5 deerini 20 olarak deitirmek... Oysa dizi kullanmayan programda 15 tane daha deiken tanmlamak ve kullanldklar kod satrlarn 15 deiken iin tekrarlamak gerekirdi.

17.7

Elemanlar ilklemek
D'de her trde olduu gibi dizi elemanlar da otomatik olarak ilklenirler. Elemanlar iin kullanlan ilk deer, elemanlarn trne baldr: int iin 0, double iin double.nan , vs.

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

59

Diziler

Yukardaki programdaki saylar dizisinin be eleman da dizi tanmland zaman double.nan deerine sahiptir: double[5] saylar; // dizinin btn elemanlarnn // ilk deeri double.nan olur

Elemanlarn bu ilk deerleri dizi kullanldka deiebilir. Bunun rneklerini yukardaki programlarda grdk. rnein ayGnleri dizisinin 11 indeksli elemanna 31 deerini atadk: ayGnleri[11] = 31; Daha sonra da giriten gelen deeri, saylar isimli dizinin saya indeksli elemannn deeri olarak okuduk: readf(" %s", &saylar[saya]); Bazen elemanlarn deerleri, dizi kurulduu anda bilinir. yle durumlarda dizi, atama sz dizimiyle ve elemanlarn ilk deerleri sa tarafta belirtilerek tanmlanr. Kullancdan ay numarasn alan ve o ayn ka gn ektiini yazan bir program dnelim: import std.stdio; void main() { // ubat'n 28 gn ektiini varsayyoruz int ayGnleri[12] = [ 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 ]; write("Kanc ay? "); int ayNumaras; readf(" %s", &ayNumaras); int indeks = ayNumaras - 1; writeln(ayNumaras, ". ay ", ayGnleri[indeks], " gn eker");

O programda ayGnleri dizisinin elemanlarnn dizinin tanmland anda ilklendiklerini gryorsunuz. Ayrca, kullancdan alnan ve deeri 1-12 aralnda olan ay numarasnn indekse nasl dntrldne dikkat edin. Bylece kullancnn 1-12 aralnda verdii numara, programda 0-11 aralna dntrlm olur. Kullanc 1-12 aralnn dnda bir deer girdiinde, program dizinin dna eriildiini bildiren bir hata ile sonlanr. Dizileri ilklerken sa tarafta tek bir eleman deeri de kullanlabilir. Bu durumda dizinin btn elemanlar o deeri alr: int[10] hepsiBir = 1; // Btn elemanlar 1 olur

17.8

Basit dizi ilemleri


Diziler, btn elemanlarn ilgilendiren baz ilemlerde byk kolaylk salarlar. Kopyalama: Atama ileci, sadaki dizinin elemanlarnn hepsini birden soldaki diziye kopyalar: int[5] kaynak = [ 10, 20, 30, 40, 50 ]; int[5] hedef; hedef = kaynak;

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

60

Diziler

Eleman ekleme: ~= ileci, dinamik dizinin sonuna yeni bir eleman veya yeni bir dizi ekler: int[] dizi; dizi ~= 7; dizi ~= 360; dizi ~= [ 30, 40 ]; // // // // dizi botur dizide tek eleman vardr dizide iki eleman olur dizide drt eleman olur

Sabit uzunluklu dizilerde dizinin uzunluu deitirilemez: int[10] dizi; dizi ~= 7;

// derleme HATASI

Birletirme: ~ ileci iki diziyi u uca birletirerek yeni bir dizi oluturur. Ayn ilecin atamal olan da vardr (~= ) ve sadaki diziyi soldaki dizinin sonuna ekler: import std.stdio; void main() { int[10] birinci = 1; int[10] ikinci = 2; int[] sonu; sonu = birinci ~ ikinci; writeln(sonu.length); sonu ~= birinci; writeln(sonu.length); // 20 yazar // 30 yazar

Eer sol tarafta sabit uzunluklu bir dizi varsa, dizinin uzunluu deitirilemeyecei iin ~= ileci kullanlamaz: int[20] sonu; // ... sonu ~= birinci;

// derleme HATASI

Atama ileminde de, sa tarafn uzunluu sol tarafa uymazsa program ker: int[10] birinci = 1; int[10] ikinci = 2; int[21] sonu; sonu = birinci ~ ikinci; O kod, programn "dizi kopyas srasnda uzunluklar ayn deil" gibi bir hatayla kmesine neden olur: object.Exception: lengths don't match for array copy

17.9

Problemler
1. Yazacanz program nce kullancdan ka tane say girileceini rensin ve giriten o kadar kesirli say alsn. Daha sonra bu saylar nce kkten bye, sonra da bykten ke doru sralasn. Burada dizi niteliklerinden .sort 'u ve .reverse ' kullanabilirsiniz.

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

61

Diziler

2. Baka bir program yazn: giriten ald saylarn nce tek olanlarn srayla, sonra da ift olanlarn srayla yazdrsn. zel olarak -1 deeri girii sonlandrmak iin kullanlsn: bu deer geldiinde artk giriten yeni say alnmasn. rnein giriten 1 4 7 2 3 8 11 -1 geldiinde ka unlar yazdrsn: 1 3 7 11 2 4 8 pucu: Saylar iki ayr diziye yerletirmek iinize yarayabilir. Girilen saylarn tek veya ift olduklarn da aritmetik ilemler sayfasnda rendiiniz % (kalan) ilecinin sonucuna bakarak anlayabilirsiniz. 3. Bir arkadanz yazd bir programn doru almadn sylyor. Giriten be tane say alan, bu saylarn karelerini bir diziye yerletiren, ve sonunda da dizinin elemanlarn ka yazdran bir program yazmaya alm ama program doru almyor. Bu programn hatalarn giderin ve beklendii gibi almasn salayn: import std.stdio; void main() { int[5] kareler; writeln("5 tane say giriniz"); int i = 0; while (i <= 5) { int say; write(i + 1, ". say: "); readf(" %s", &say); kareler[i] = say * say; ++i;

writeln("=== Saylarn Kareleri ==="); while (i <= kareler.length) { write(kareler[i], " "); ++i; } } ... zmler writeln();

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

62

Karakterler

18

Karakterler
Karakterler yazlar oluturan en alt birimlerdir: harfler, rakamlar, noktalama iaretleri, boluk karakteri, vs. nceki dersteki dizilere ek olarak bu derste de karakterleri tanynca iki ders sonra anlatlacak olan dizgi yaps ok kolay anlalacak. Bilgisayarlar temelde bitlerden olutuklar iin, karakterler de bitlerin birleimlerinden oluan tamsay deerler olarak ifade edilirler. rnein kk harf a 'nn tamsay deeri 97'dir veya 1 rakamnn tamsay deeri 49'dur. Bu deerler tamamen anlamalara bal olarak atanmlardr ve kkleri ASCII kod tablosuna dayanr. Karakterler baz dillerde geleneksel olarak 256 farkl deer tutabilen char tryle gsterilirler. Eer char trn baka dillerden tanyorsanz onun her harfi barndracak kadar byk bir tr olmadn biliyorsunuzdur. D'de farkl karakter tr bulunur. Buna aklk getirmek iin nce bu konunun ksa tarihine bakmamz gerekiyor.

18.1

Tarihe
ASCII: Donanmn ok kstl olduu gnlerde tasarlanan ilk ASCII tablosu 7 bitlik deerlerden oluuyordu ve bu yzden ancak 128 karakter deeri barndrabiliyordu. Bu deerler ngiliz alfabesini oluturan 26 harfin kk ve byk olanlarn, rakamlar, sk kullanlan noktalama iaretlerini, programlarn ktlarn u birimlerde gsterirken kullanlan kontrol karakterlerini, vs. ifade etmek iin yeterliydi. rnek olarak, "merhaba" metnindeki karakterlerin ASCII kodlar srasyla yledir (bu gsterimlerde okumay kolaylatrmak iin bayt deerleri arasnda virgl kullanyorum): 109, 101, 114, 104, 97, 98, 97 Her bir deer bir harfe karlk gelir. rnein iki 'a' harfi iin iki tane 97 deeri kullanlmtr. Not: Bu baytlarn gerekte hangi srada durduklar platforma ve hatta baytlarn iinde bulunduklar belgeye gre farkllklar gsterir. Burada yalnzca karakterleri ifade etmek iin kullanlan kod deerlerini gryoruz. Donanmdaki gelimeler dorultusunda ASCII tablosundaki kodlar daha sonra 8 bite kartlarak 256 karakter destekleyen Geniletilmi [Extended] ASCII tablosu tanmlanmtr. IBM Kod Tablolar: IBM firmas, ASCII tablosuna dayanan ve 128 ve daha byk karakter deerlerini dnya dillerine ayran bir dizi kod tablosu tanmlad. Bu kod tablolar sayesinde ngiliz alfabesinden baka alfabelerin de desteklenmeleri salanm oldu. rnein Trk alfabesine zg karakterler IBM'in 857 numaral kod tablosunda yer aldlar. Her ne kadar ASCII'den ok daha yararl olsalar da, kod tablolarnn nemli sorunlar vardr: Yaznn doru olarak grntlenebilmesi iin yazld zaman hangi kod tablosunun kullanldnn bilinmesi gerekir, nk farkl kod tablolarndaki kodlar farkl karakterlere karlk gelirler. rnein 857 numaral kod tablosunda olan karakter, 437 numaral kod tablosu ile grntlendiinde karakteri olarak belirir. Baka bir sorun; yaz iinde birden fazla dilin karakteri kullanldnda kod tablolarnn yetersiz kalmalardr. Ayrca, 128'den fazla zel karakteri olan diller zaten 8 bitlik bir tabloda ifade edilemezler. ISO/IEC 8859 Kod Tablolar: Uluslararas standartlama almalar sonucunda ISO/IEC 8859 standart karakter kodlar km, ve rnein Trk alfabesinin zel harfleri 8859-9 tablosunda yer almlardr. Yapsal olarak IBM'in tablolarnn edeeri olduu iin, IBM'in kod tablolarnn sorunlar bu standartta da vardr. Hatta Felemenke'nin karakteri gibi baz karakterler bu tablolarda yer bulamamlardr.

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

63

Karakterler

Unicode: Btn bu sorunlar kknden zen standart, Unicode'dur. Unicode; dnya dillerindeki ve yaz sistemlerindeki harflerin, karakterlerin, ve yazm iaretlerinin 100 binden fazlasn tanmlar ve her birisine farkl bir kod verir. Bylece, Unicode'un tanmlad kodlar kullanan metinler btn dnya karakterlerini, hibir karklk ve kstlama yaamadan; bir arada bulundurabilirler.

18.2

Unicode kodlama eitleri


Unicode, her bir karaktere bir kod deeri verir. rnek olarak, harfinin Unicode'daki deeri 286'dr. Unicode'un destekledii karakter says o kadar ok olunca, karakterleri ifade eden deerler de doal olarak artk 8 bitle ifade edilemezler. rnein kod deeri 255'ten byk olduu iin 'nin en az 2 baytla gsterilmesi gerekir. Not: Bayt deerlerinin nasl hesapland konumuz dnda kalyor. Aadaki rneklerde kullanld iin bu iki baytn deerlerinin 1 ve 30 olduklarn sylemekle yetineceim. Karakterlerin elektronik ortamda nasl ifade edildiklerine karakter kodlamas denir. Yukarda "merhaba"nn karakterlerinin ASCII kodlaryla nasl ifade edildiklerini grmtk. imdi Unicode karakterlerinin standart kodlamalarndan n greceiz. UTF-32: Bu kodlama her Unicode karakteri iin 32 bit kullanr; yani 4 bayt. "merhaba"nn UTF-32 kodlamas da ASCII kodlamasyla ayndr; fark, her karakter iin 4 bayt kullanlmasdr. Bu yzden UTF-32 kodlamasnn yle olacan dnebilirsiniz (okumay kolaylatrmak iin iki satr halinde gsteriyorum): 0, 0, 0, 109, 0, 0, 0, 101, 0, 0, 0, 114, 0, 0, 0, 97, 0, 0, 0, 98, 0, 0, 0, 97 0, 0, 0, 104,

Baka bir rnek olarak, ifade edilecek metnin rnein "a" olduunu dnrsek: 0, 0, 0, 97, 0, 0, 1, 30

Yani a'da 1, ve 'de 2 tane anlaml bayt olduu iin; 5 tane de sfr bulunmaktadr. Bu sfrlar, her karaktere 4 bayt verebilmek iin gereken doldurma baytlar olarak dnebiliriz. Not: Baytlarn sralar farkl olabilir ve bazlarnn gerek deerleri farkldr. Dikkat ederseniz, bu kodlama her zaman iin ASCII kodlamasnn 4 kat yer harcamaktadr. Metin iindeki karakterlerin byk bir blmnn ngiliz alfabesindeki karakterlerden olutuu durumlarda, ou karakter iin 3 tane de 0 kullanld iin bu kodlama, duruma gre ok savurgan olabilir. te yandan, karakterlerin her birisinin tam olarak 4 bayt yer tutuyor olmasnn getirdii yararlar da vardr. UTF-16: Bu kodlama, Unicode karakterlerinin ounu 16 bitle, yani 2 baytla gsterir. ki bayt yaklak olarak 65 bin deer tutabildii iin, yaklak yz bin Unicode karakterinin geri kalan 30 bin kadar iin daha fazla bayt kullanmak gerekir. rnek olarak "a" UTF-16'da 4 bayt olarak kodlanr: 0, 97, 1, 30

Not: Baytlarn sralar farkl olabilir ve bazlarnn gerek deerleri farkldr. Bu kodlama ou belgede UTF-32'den daha az yer tutar ama karakterine gre deiik sayda bayt kullanld iin ilenmesi daha karmaktr.

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

64

Karakterler

UTF-8: Bu kodlama, karakterleri 1 veya daha fazla baytla ifade eder. Eer karakter ASCII tablosundaki karakterlerden birisiyse, tek baytla ve aynen ASCII tablosundaki deeriyle ifade edilir. Bunlarn dndaki karakterlerin bazlar 2, bazlar 3, dierleri de 4 bayt olarak ifade edilirler. Trk alfabesinin ngiliz alfabesinde bulunmayan zel karakterleri 2 baytlk gruptadrlar. ou belge iin UTF-8 btn kodlamalar iinde en az yer tutan kodlamadr. Baka bir yarar, ASCII tablosundaki kodlara aynen karlk geldii iin, ASCII kodlanarak yazlm ve ngiliz alfabesini kullanan belgeler de otomatik olarak UTF-8 dzenine uyarlar. Bu kodlamada hi savurganlk yoktur; btn karakterler gerekten gereken sayda baytla ifade edilirler. rnein "a" iin: 97, 1, 30

Not: Baytlarn sralar farkl olabilir ve bazlarnn gerek deerleri farkldr.

18.3

D'nin karakter trleri


D'de karakterleri ifade etmek iin 3 deiik tr vardr. Bunlar yukarda anlatlan Unicode kodlama yntemlerine karlk gelirler. Temel trlerin tantld sayfada gsterildikleri gibi: Tr char Aklama iaretsiz 8 bit UTF-8 karakter deeri lk Deeri 0xFF 0xFFFF 0x0000FFFF

wchar iaretsiz 16 bit UTF-16 karakter deeri dchar iaretsiz 32 bit UTF-32 karakter deeri

Baka baz programlama dillerinden farkl olarak, D'de her karakter ayn uzunlukta deildir. rnein harfi Unicode'da 2 baytla gsterildii iin 8 bitlik char trne smaz. te yandan, dchar 4 bayttan olutuu iin her Unicode karakterini tutabilir. Buna ramen D'nin Unicode destei tam deildir. Bunu biraz aada anlatacam.

18.4

Karakter sabitleri
Karakterleri program iinde tek olarak belirtmek gerektiinde etraflarna tek trnak iaretleri koyulur: char a_harfi = 'a'; wchar byk_yumuak_g = ''; Karakter sabitleri iin ift trnak kullanlamaz, nk o zaman iki ders sonra greceimiz dizgi sabiti anlamna gelir: 'a' karakter deeridir, "a" tek karakterli bir dizgidir. Trk alfabesindeki baz harflerin Unicode kodlar 2 bayttan olutuklar iin char trndeki deikenlere atanamazlar. Karakterleri sabit olarak program iine yazmann bir ok yolu vardr: En doal olarak, klavyeden dorudan karakterin tuuna basmak alma ortamndaki baka bir programdan veya bir metinden kopyalamak. rnein bir internet sitesinden, veya alma ortamnda karakter semeye yarayan bir programdan kopyalanabilir (u anda altm Linux ortamnda bu programn ismi Character Map.) Karakterlerin bazlarn standart ksa isimleriyle yazmak. Bunun sz dizimi \&karakter_ismi; eklindedir. rnein avro karakterinin ismi euro 'dur ve programda deeri yle yazlabilir:

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

65

Karakterler

wchar para_sembol = '\&euro;'; simleri olan baka karakterleri D'nin isimli karakterler listesinde bulabilirsiniz. ASCII tablosundaki karakterleri deerleriyle \sekizli_dzende_kod veya \xonaltl_dzende_kod sz dizimleriyle yazmak: char soru_iareti_sekizli = '\77'; char soru_iareti_onaltl = '\x3f'; Karakterleri Unicode deerleriyle yazmak. wchar iin \udrt_haneli_kod sz dizimini, dchar iin de \Usekiz_haneli_kod sz dizimini kullanabilirsiniz. Bu yazmda karakterin kodunun onaltl say sisteminde (hexadecimal) yazlmas gerekir: wchar _w = '\u011e'; dchar _d = '\U0000011e'; Karakterleri tamsay gibi Unicode deerleriyle belirlemek: char a = 97; wchar = 286; Bu yntemler karakterleri ift trnak iinde bir arada yazdnz durumlarda da geerlidir. rnein u iki satr ayn kty verirler: writeln("A fiyat: 10.25"); writeln("\x41\u011f fiyat: 10.25\&euro;");

18.5

Kontrol karakterleri
Baz karakterler yalnzca metin dzeniyle ilgilidirler; kendilerine zg bir grnmleri yoktur. rnein u birime yeni bir satra geileceini bildiren yeni satr karakterinin gsterilecek bir ekli yoktur; yalnzca yeni bir satra geilmesini salar. Byle karakterlere kontrol karakteri denir. Kontrol karakterleri \zel_harf sz dizimiyle ifade edilirler. Yazm \n \r \t sim yeni satr satr ba sekme Yeni satra geirir Satrn bana gtrr Bir sonraki sekme noktasna kadar boluk brakr Aklama

rnein \n karakterini kullanrsak, ktda yeni satr amadn bildiimiz write 'la bile yeni satr atrabiliriz. Yazdrlacak metnin iinde istediimiz noktalara \n karakterleri yerletirmek, o noktalarda yeni satr almasn salar: write("birinci satr\nikinci satr\nnc satr\n"); kts: birinci satr ikinci satr nc satr

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

66

Karakterler

18.6

Tek trnak ve ters bl


Tek trnak karakterinin kendisini tek trnaklar arasnda yazamayz, nk derleyici ikinci trna grdnde trnaklar kapattmz dnr: ''' . lk ikisi ama ve kapama trnaklar olarak alglanrlar, ncs de tek bana alglanr ve yazm hatasna neden olur. Ters bl karakteri de baka zel karakterleri ifade etmek iin kullanld iin, derleyici onu bir zel karakterin balangc olarak alglar: '\' . Derleyici \' yazmn bir zel karakter olarak alglar ve bataki tek trnakla elemek iin bir tane daha tek trnak arar ve bulamaz. Bu iki karakteri sabit olarak yazmak iin balarna bir ters bl daha yazlr: Yazm \' \\ sim Aklama

tek trnak Tek trnan karakter olarak tanmlanmasna olanak verir: '\'' ters bl Ters bl karakterinin yazlmasna olanak verir: '\\' veya "\\"

18.7

std.uni modl
std.uni modl Unicode karakterleriyle ilgili yardmc ilevler ierir. Bu modldeki ilevleri Ddili Wiki'nin std.uni sayfasnda grebilirsiniz. Banda is olan ilevler karakterle ilgili soru cevaplarlar ve cevap yanlsa 0, doruysa sfrdan farkl bir deer dndrrler. Bylece bu ilevler mantksal ifadelerde kullanldrlar: isUniLower : kk harf mi? isUniUpper : byk harf mi? isUniAlpha : bir harf mi? Banda to olan ilevler, verilen karakteri kullanarak yeni bir karakter retirler: toUniLower : kk harfini retir toUniUpper : byk harfini retir Bu ilevleri kullanan bir program: import std.stdio; import std.uni; void main() { writeln(" kk mdr? ", isUniLower('')); writeln(" kk mdr? ", isUniLower('')); writeln(" byk mdr? ", isUniUpper('')); writeln(" byk mdr? ", isUniUpper('')); writeln("z harf midir? ", isUniAlpha('z')); writeln("\&euro; harf midir? ", isUniAlpha('\&euro;')); writeln("'nin k: ", toUniLower('')); writeln("'nin k: ", toUniLower('')); writeln("'nin by: ", toUniUpper('')); writeln("'nn by: ", toUniUpper(''));

kts:

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

67

Karakterler

kk mdr? 1 kk mdr? 0 byk mdr? 1 byk mdr? 0 z harf midir? 1 harf midir? 0 'nin k: 'nin k: i 'nin by: 'nn by: I

18.8

Trk alfabesinin anssz harfleri: ve i


ve i harflerinin kk ve byk biimleri Trk alfabesinde tutarldr: noktalysa noktal, noktaszsa noktasz. Oysa ou yabanc alfabede bu konuda bir tutarszlk vardr: noktal i'nin by noktasz I'dr. Bilgisayar sistemlerinin temelleri ngiliz alfabesiyle balad iin ne yazk ki i'nin by I, I'nn k de i olur. Bu yzden bu iki harf iin zel dikkat gstermemiz gerekir. te bu sorunu gsteren bir program: import std.stdio; import std.uni; void main() { writeln("i'nin by: ", toUniUpper('i')); writeln("I'nn k: ", toUniLower('I')); } ve yanl kts: i'nin by: I I'nn k: i

18.8.1

Her alfabe sorunludur


Karakter kodlar kullanlarak yaplan kk-byk dnmleri ve harf sralamalar, aslnda btn alfabeler iin sorunludur. rnein I'nn knn i olarak dntrlmesi Azeri ve Kelt alfabeleri iin de yanltr. Benzer sorunlar harflerin sralanmalarnda da bulunur. rnein gibi Trk alfabesine zg harfler z'den sonra sralandklar gibi, gibi aksanl harfler ngiliz alfabesinde bile z'den sonra gelirler.

18.8.2

trileri ktphanesi
Alfabelerle ilgili bu gibi sorunlara zm getiren bir proje, Ddili Forum yeleri tarafndan gelitirilen trileri ktphanesidir. trileri, bu yaznn yazld srada ngiliz, Trk, Azeri, Trkmen, Krm Tatar, Gagavuz, Kumuk, Hakas, Krt, ve rlanda alfabelerini desteklemekte ve bu alfabelerin kk-byk dnm ve sralama sorunlarn zmektedir. trileri ktphanesini u sayfada deneyebilirsiniz: http://www.ddili.org/cgi-bin/trileri_deneme

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

68

Karakterler

18.9

Giriten karakter okumadaki sorunlar


D'nin Unicode konusunda getirdii g ve esneklik, giriten karakter okurken beklenmedik sonular dourabilir. Bu eliki, karakter ile ne kastedildiinin ak olmamas nedeniyledir. Daha ileriye gitmeden nce, bu sorunu yaayan bir program gstermek istiyorum. import std.stdio; void main() { char harf; write("Ltfen bir harf girin: "); readf(" %s", &harf); writeln("Okuduum harf: ", harf); } Yukardaki program Unicode kodlamas kullanlmayan bir ortamda altrrsanz, programn giriinden ald Trke harfleri belki de doru olarak yazdrdn grebilirsiniz. te yandan ayn program ou Linux konsol penceresinde olduu gibi bir Unicode ortamnda altrrsanz, yazdrlan harfin sizin yazdnzla ayn olmadn grrsnz. rnein UTF-8 kodlamas kullanan bir konsolda ASCII tablosunda bulunmayan bir harf girildiinde: Ltfen bir harf girin: Okuduum harf: girilen harf grnmyor Bunun nedeni, UTF-8 kodlamas kullanan konsolun ASCII tablosunda bulunmayan gibi harfleri birden fazla kod ile temsil etmesi, ve readf 'in char okurken bu kodlardan yalnzca ilkini alyor olmasdr. O char asl karakteri temsil etmeye yetmedii iin de writeln 'n yazdrd eksik kodlanm olan harf konsolda gsterilememektedir. char olarak okunduunda harfin kendisinin deil, onu oluturan kodlarn okunmakta olduklarn harfi iki farkl char olarak okuyarak grebiliriz: import std.stdio; void main() { char birinci_kod; char ikinci_kod; write("Ltfen bir harf girin: "); readf(" %s", &birinci_kod); readf(" %s", &ikinci_kod); } writeln("Okuduum harf: ", birinci_kod, ikinci_kod);

Program giriten iki char okumakta ve onlar ayn srada ka yazdrmaktadr. O char deerlerinin art arda konsola gnderilmi olmalar, bu sefer harfin UTF-8 kodlamasn standart k tarafnda tamamlamakta ve karakter doru olarak gsterilmektedir: Ltfen bir harf girin: Okuduum harf: iki char 'n oluturmu olduu harf Bu sonular standart giri ve kn char akmlar olmalarndan kaynaklanr. Karakterlerin iki ders sonra greceimiz dizgiler araclyla aslnda ok daha rahat okunduklarn greceksiniz.

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

69

Karakterler

18.10

D'nin Unicode destei


Unicode ok byk ve karmak bir standarttr. D, Unicode'un tamamn deil ama olduka kullanl bir alt kmesini destekler. Unicode ile kodlanm olan bir metin en aadan en yukarya doru u dzeylerden oluur: kod birimi (code unit): UTF kodlamalarn oluturan kod deerleridir. Unicode karakterleri, kodlamaya ve karakterin kendisine bal olarak bir veya daha fazla kod biriminden oluabilirler. rnein UTF-8 kodlamasnda a karakteri tek kod biriminden, karakteri ise iki kod biriminden oluur. D'nin char , wchar , ve dchar trleri srasyla UTF-8, UTF-16, ve UTF-32 kod birimlerini ifade ederler. kod noktas (code point): Unicode'un tanmlam olduu her harf, im, vs. bir kod noktasdr. rnein a ve iki farkl kod noktasdr. Bu kod noktalar kodlamaya bal olarak bir veya daha fazla kod birimi ile ifade edilirler. Yukarda da deindiim gibi, UTF-8 kodlamasnda a tek kod birimi ile, ise iki kod birimi ile ifade edilir. te yandan, her ikisi de UTF-16 ve UTF-32 kodlamalarnda tek kod birimi ile ifade edilirler. D'de kod noktalarn tam olarak destekleyen tr dchar 'dr. char ve wchar ise yalnzca kod birimi tr olarak kullanlmaya elverilidirler. karakter (character): yaz sistemlerinde kullanlmak zere Unicode'un tanmlam olduu btn ekiller, imler, ve konuma dilinde "karakter" dediimiz her ey bu tanma girer. Bu konuda Unicode'un getirdii bir karklk, baz karakterlerin birden fazla kod noktasndan oluabilmeleridir. rnein harfini ifade etmenin iki yolu vardr: tek bana kod noktas olarak art arda gelen g ve kod noktalar olarak (g ve sonrasnda gelen birletirici (combining) breve apkas) D, kod noktalarnn bileimlerinden oluan karakter kavramn desteklemez. D'nin gznde tek kod noktas olan ile art arda gelen g ve karakterlerinin ilgileri yoktur.

18.11

zet
Unicode, dnya yaz sistemlerindeki btn karakterleri destekler char UTF-8 kodlamas iindir; karakterleri ifade etmeye genelde elverili olmasa da ASCII tablosunu destekler wchar UTF-16 kodlamas iindir; karakterleri ifade etmeye genelde elverili olmasa da zel durumlarda birden fazla alfabe karakterini destekler dchar UTF-32 kodlamas iindir; 32 bit olmas nedeniyle btn Unicode karakterlerini destekler ve kod noktas olarak kullanlabilir

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

70

Baka Dizi Olanaklar

19

Baka Dizi Olanaklar


Elemanlar bir araya getirmeye yarayan dizileri Diziler dersinde grmtk. O dersi ksa tutmak iin zellikle sonraya braktm baka dizi olanaklarn burada gstereceim. Ama nce karkla neden olabileceini dndm baz terimleri listelemek istiyorum: dizi: sra numarasyla eriilen eleman topluluudur; bundan bakaca anlam tamaz sabit uzunluklu dizi: eleman adedi deitirilemeyen dizidir; kendi elemanlarna sahiptir dinamik dizi: eleman adedi deitirilebilen dizidir; kendi elemanlar yoktur, sahip olmad elemanlara eriim salar dilim: dinamik dizilerin ok yaygn baka bir ismidir Bu derste zellikle dilim dediim zaman dilimleri (yani dinamik dizileri), yalnzca dizi dediim zaman da fark gzetmeden dilimleri ve sabit uzunluklu dizileri kasdetmi olacam.

19.1

Dilimler
Dilimler aslnda dinamik dizilerle ayn olanaktr. Bu olanaa; dizi gibi kullanlabilme zellii nedeniyle bazen dinamik dizi, baka dizinin bir parasna eriim salama zellii nedeniyle de bazen dilim denir. Var olan baka bir dizinin elemanlarnn bir blmn sanki daha kk farkl bir diziymi gibi kullandrmaya yarar. Dilimler, elemanlar bir balang indeksinden bir biti indeksine kadar belirlemeye yarayan aralk sz dizimiyle tanmlanrlar: araln_ba .. araln_sonundan_bir_sonras Balang indeksi arala dahildir; biti indeksi araln dndadr: /* ... */ = ayGnleri[0 .. 3]; // 0, 1, ve 2 dahil; 3 hari

Not: Burada anlatlan aralklar Phobos ktphanesinin aralk kavramndan farkldr. Snf ve yap arayzleriyle ilgili olan Phobos aralklarn daha ilerideki bir derste gstereceim. rnek olarak ayGnleri dizisini drde dilimleyerek birbirinden farkl drt eyrek diziymi gibi yle kullanabiliriz: int ayGnleri[12] = [ 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 ]; int int int int ilkeyrek[] ikincieyrek[] nceyrek[] soneyrek[] = = = = ayGnleri[0 ayGnleri[3 ayGnleri[6 ayGnleri[9 .. .. .. .. 3]; 6]; 9]; 12];

O kodda sol taraftaki yaplar dilimdirler ve asl dizinin drt deiik blgesine eriim salamaktadrlar. Buradaki nemli nokta, o dilimlerin kendilerine ait elemanlarnn bulunmuyor olmasdr. Onlar asl dizinin elemanlarna eriim salarlar. Bir dilimdeki bir elemann deitirilmesi asl dizideki asl eleman etkiler. Bunu grmek iin drt eyrein ilk elemanlarna drt farkl deer verelim ve asl diziyi yazdralm: ilkeyrek[0] = 1; ikincieyrek[0] = 2; nceyrek[0] = 3; soneyrek[0] = 4;

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

71

Baka Dizi Olanaklar

writeln(ayGnleri); Deien elemanlar saryla gsteriyorum: [1, 28, 31, 2, 31, 30, 3, 31, 30, 4, 30, 31] Dikkat ederseniz, her dilim kendisinin 0 numaral elemann deitirdiinde o dilimin asl dizide eriim salad ilk eleman deimitir. Dizi indekslerinin 0'dan baladklarn ve dizinin uzunluundan bir eksiine kadar olduklarn daha nce grmtk. rnein 3 elemanl bir dizinin yasal indeksleri 0, 1, ve 2'dir. Dilim sz diziminde biti indeksi araln sonundan bir sonras anlamna gelir. Bu yzden, dizinin son elemann da arala dahil etmek gerektiinde ikinci indeks olarak dizinin uzunluu kullanlr. rnein uzunluu 3 olan bir dizinin btn elemanlarna eriim salamak iin dizi[0..3] yazlr. Aralk sz dizimindeki doal bir kstlama, balang indeksinin biti indeksinden byk olamayacadr: int[] dizi = [ 0, 1, 2 ]; int[] dilim = dizi[2 .. 1];

// alma zaman HATASI

Balang indeksinin biti indeksine eit olmas ise yasaldr ve bo dilim anlamna gelir: int[] dilim = birDizi[indeks .. indeks]; writeln("Dilimin uzunluu: ", dilim.length); indeks 'in yasal bir indeks deeri olduunu kabul edersek, kts: Dilimin uzunluu: 0

19.2

dizi.length yerine $
Dizi elemanlarn [] ileci ile indekslerken bazen dizinin uzunluundan da yararlanmak gerekebilir. Bu konuda kolaylk olarak ve yalnzca [] ileci iindeyken, dizi.length yazmak yerine ksaca $ karakteri kullanlabilir: writeln(dizi[dizi.length - 1]); writeln(dizi[$ - 1]); // dizinin son eleman // ayn ey

19.3

Kopyasn almak iin .dup


smi "kopyala" anlamna gelen "duplicate"in ksas olan .dup nitelii, var olan bir dizinin elemanlarnn kopyasndan oluan yeni bir dizi retir: double[] dizi = [ 1.25, 3.75 ]; double[] kopyas = dizi.dup; Bir rnek olarak ubat'n 29 gn ektii senelerdeki aylarn gn saylarn tutan bir dizi oluturmak isteyelim. Bir yntem, nce normal senelerdeki ayGnleri 'nin bir kopyasn almak ve o kopya dizideki ubat'n gn saysn bir arttrmaktr:

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

72

Baka Dizi Olanaklar

import std.stdio; void main() { int ayGnleri[12] = [ 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 ]; int[] artkYl = ayGnleri.dup; ++artkYl[1]; // yeni dizideki ubat'n gn saysn // arttrr

writeln("Normal: ", ayGnleri); writeln("Artk : ", artkYl);

kts: Normal: [31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31] Artk : [31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31]

19.4

Atama ilemi
Deerini deitirme olarak bildiimiz atama ilemi, sabit uzunluklu dizilerde de ayn anlamdadr; elemanlarn deerleri deiir: int[3] a = [ 1, 1, 1 ]; int[3] b = [ 2, 2, 2 ]; a = b; writeln(a); kts: [2, 2, 2] Dilimlerle kullanldnda ise atama ileminin anlam ok farkldr: Dilimin, eriim salamakta olduu elemanlar brakmasna ve yeni elemanlara eriim salamaya balamasna neden olur: int[] tekler = [ 1, 3, 5, 7, 9, 11 ]; int[] iftler = [ 2, 4, 6, 8, 10 ]; int[] dilim; // henz hibir elemana eriim salamyor // a'nn elemanlar da 2 olur

dilim = tekler[2 .. $ - 2]; writeln(dilim); dilim = iftler[1 .. $ - 1]; writeln(dilim); Yukardaki koddaki dilim balangta hibir dizinin elemanna eriim salamazken nce tekler 'in baz elemanlarna, sonra da iftler 'in baz elemanlarna eriim salar: [5, 7] [4, 6, 8]

19.5

Uzunluun artmas paylam sonlandrr


Sabit dizilerin uzunluklar deiemedii iin bu konu yalnzca dilimlerle ilgilidir.

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

73

Baka Dizi Olanaklar

Ayn elemana ayn anda birden fazla dilimle eriilebilir. rnein aadaki sekiz elemann ilk ikisi dilim tarafndan paylalmaktadr: import std.stdio; void main() { int[] dilim = [ 1, 3, 5, 7, 9, 11, 13, 15 ]; int[] yars = dilim[0 .. $ / 2]; int[] eyrei = dilim[0 .. $ / 4]; eyrei[1] = 0; writeln(eyrei); writeln(yars); writeln(dilim);

eyrei diliminin ikinci elemannda yaplan deiiklik asl eleman deitirdii iin, bu etki dilimlerin hepsi tarafndan grlr: [1, 0] [1, 0, 5, 7] [1, 0, 5, 7, 9, 11, 13, 15] Bu adan baklnca dilimlerin elemanlara paylaml olarak eriim saladklar sylenebilir. Bu paylam sonlandran bir etki, dilimlerden birisinin uzunluunun artmasdr; uzunluu artan dilim paylamdan ayrlr. Bu ilem srasnda o dilimin eriim salamakta olduu btn elemanlar otomatik olarak kopyalanrlar ve uzayan dilim artk bu yeni elemanlara eriim salamaya balar. Bunu grmek iin yukardaki programdaki eyrei diliminin elemann deitirmeden nce ona yeni bir eleman ekleyelim: eyrei ~= 42; eyrei[1] = 0; // uzunluu artt iin bu dilim // bu noktada paylamdan ayrlr // o yzden bu ilem yalnzca kendi // elemann etkiler

Eklenen eleman dilimin uzunluunu arttrd iin dilim artk kopyalanan yeni elemanlara eriim salamaya balar. eyrei 'nin elemannda yaplan deiikliin dilim ve yars dilimlerini artk etkilemedii programn imdiki ktsnda grlyor: [1, 0, 42] [1, 3, 5, 7] [1, 3, 5, 7, 9, 11, 13, 15] Dilimin uzunluunun aka arttrlmas da eleman paylamndan ayrlmasna neden olur: ++eyrei.length; veya eyrei.length += 5; // paylamdan ayrlr // paylamdan ayrlr

te yandan, bir dilimin uzunluunun ksaltlmas eleman paylamn sonlandrmaz. Uzunluun ksaltlmas, yalnzca artk daha az elemana eriim salama anlamna gelir:

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

74

Baka Dizi Olanaklar

int[] a = [ 1, 11, 111 ]; int[] d = a; d = d[1 .. $]; d[0] = 42; writeln(a); // bandan ksaltyoruz // eleman dilim yoluyla deitiriyoruz // dier dilimi yazdryoruz

ktsndan grld gibi, d yoluyla yaplan deiiklik a 'nn eritirdii eleman da etkilemitir; yani paylam devam etmektedir: [1, 42, 111] Uzunluun baka ifadeler yoluyla azaltlmas da paylam sonlandrmaz: d = d[0 .. $ - 1]; --d.length; d.length = d.length - 1; Eleman paylam devam eder. // sonundan ksaltmak // ayn ey // ayn ey

19.6

Btn elemanlar zerindeki ilemler


Bu olanak hem sabit uzunluklu dizilerle hem de dilimlerle kullanlabilir. Dizi isminden sonra yazlan ii bo [] karakterleri btn elemanlar anlamna gelir. Bu olanak, elemanlarn her birisiyle yaplmas istenen ilemlerde byk kolaylk salar. Not: Bu dersi yazdm srada kullandm dmd 2.051 bu ilemleri henz dilimler iin desteklemiyor. O yzden bu balk altndaki baz rneklerde sabit uzunluklu diziler kullanmak zorunda kaldm. import std.stdio; void main() { double[3] a = [ 10, 20, 30 ]; double[3] b = [ 2, 3, 4 ]; double[3] sonu = a[] + b[]; } writeln(sonu);

kts: [12, 23, 34] O programdaki toplama ilemi, a ve b dizilerinin birbirlerine karlk gelen elemanlarn ayr ayr toplar: nce ilk elemanlar kendi aralarnda, sonra ikinci elemanlar kendi aralarnda, vs. O yzden byle ilemlerde kullanlan dizilerin uzunluklarnn eit olmalar arttr. Yukardaki programdaki + ileci yerine; daha nce grdnz + , - , * , / , ve % aritmetik ilelerini; ilerideki derslerde karlaacanz ^ , & , ve | ikili bit ilelerini; ve bir dizinin nne yazlan tekli - ve ~ ilelerini kullanabilirsiniz. Bu ilelerin atamal olanlar da kullanlabilir: = , += , -= , *= , /= , %= , ^= , &= , ve |= .

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

75

Baka Dizi Olanaklar

Bu olanak yalnzca iki diziyi ilgilendiren ilemler iin deildir; bir dizi yannda onun elemanlaryla uyumlu olan bir ifade de kullanlabilir. rnein bir dizinin btn elemanlarn drde blmek iin: double[3] a = [ 10, 20, 30 ]; a[] /= 4; writeln(a); kts: [2.5, 5, 7.5] Btn elemanlarn belirli bir deere eitlemek iin: a[] = 42; writeln(a); kts: [42, 42, 42] Bu olanan dilimlerle kullanmnda hataya ak bir durum vardr. Sonuta eleman deerlerinde bir fark grlmese bile aadaki iki ifade aslnda anlamsal adan ok farkldr: dilim2 = dilim1; dilim3[] = dilim1; // dilim1'in elemanlarna eriim // salamaya balar // zaten eriim salamakta olduu // elemanlarn deerleri deiir

dilim2 'nin dorudan atama ileciyle kullanlyor olmas, onun artk dilim1 'in elemanlarna eriim salamaya balamasna neden olur. Oysa dilim3[] ifadesi dilim3'n btn elemanlar anlamn tad iin, onun btn elemanlarnn deerleri dilim1 'in elemanlarnn deerlerini alrlar. Bu yzden, unutulan bir [] ilecinin etkisi ok byk olabilir. Bunu aadaki programda grebiliriz: import std.stdio; void main() { double[] dilim1 = [ 1, 1, 1 ]; double[] dilim2 = [ 2, 2, 2 ]; double[] dilim3 = [ 3, 3, 3 ]; dilim2 = dilim1; dilim3[] = dilim1; // dilim1'in elemanlarna eriim // salamaya balar // zaten eriim salamakta olduu // elemanlarn deerleri deiir

writeln("dilim1 nce : ", dilim1); writeln("dilim2 nce : ", dilim2); writeln("dilim3 nce : ", dilim3); dilim2[0] = 42; dilim3[0] = 43; // eriimini dilim1'le paylamakta // olduu eleman deiir // kendi eleman deiir

writeln("dilim1 sonra: ", dilim1);

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

76

Baka Dizi Olanaklar

writeln("dilim2 sonra: ", dilim2); writeln("dilim3 sonra: ", dilim3);

dilim2 'de yaplan deiiklik dilim1 'i de etkilemitir: dilim1 dilim2 dilim3 dilim1 dilim2 dilim3 nce : nce : nce : sonra: sonra: sonra: [1, 1, 1] [1, 1, 1] [1, 1, 1] [42, 1, 1] [42, 1, 1] [43, 1, 1]

Buradaki tehlike; dilim2 atanrken [] ilecinin belki de unutulmu olmasnn etkisinin, belki de o yzden istenmeden paylalmaya balanm olan eleman deiene kadar farkedilememi olmasdr. Bu gibi tehlikeler yznden bu ilemleri dilimlerle kullanrken dikkatli olmak gerekir.

19.7

ok boyutlu diziler
imdiye kadar grdmz dizi ilemlerinde eleman tr olarak hep int ve double gibi temel trler kullandk. Eleman tr olarak aslnda baka trler, rnein diziler de kullanlabilir. Bylece dizi dizisi gibi daha karmak topluluklar tanmlayabiliriz. Elemanlarnn tr dizi olan dizilere ok boyutlu dizi denir. imdiye kadar grdmz dizilerin elemanlarn hep soldan saa doru yazmtk. ki boyutlu dizi kavramn anlamay kolaylatrmak iin bir diziyi bir kere de yukardan aaya doru yazalm: int[] dizi = [

];

10, 20, 30, 40

Kodu gzelletirmek iin kullanlan boluklarn ve fazladan satrlarn derleyicinin gznde etkisiz olduklarn biliyorsunuz. Yukardaki dizi nceden olduu gibi tek satrda da yazlabilirdi ve ayn anlama gelirdi. imdi o dizinin her bir elemann int[] trnde bir deerle deitirelim: /* ... */ dizi = [

];

[ [ [ [

10, 20, 30, 40,

11, 21, 31, 41,

12 22 32 42

], ], ], ]

Yaptmz tek deiiklik, int yerine int[] trnde elemanlar yazmak oldu. Kodun yasal olmas iin eleman trn artk int olarak deil, int[] olarak belirlememiz gerekir: int[][] dizi = [

[ 10, 11, 12 ], [ 20, 21, 22 ], [ 30, 31, 32 ],

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

77

Baka Dizi Olanaklar

];

[ 40, 41, 42 ]

Satr ve stunlardan olutuklar iin yukardaki gibi dizilere iki boyutlu dizi denir. Elemanlar int dizisi olan yukardaki dizinin kullanm imdiye kadar rendiklerimizden farkl deildir. Her bir elemannn int[] trnde olduunu hatrlamak ve int[] trne uyan ilemlerde kullanmak yeter: dizi ~= [ 50, 51 ]; // yeni bir eleman (yani dilim) ekler dizi[0] ~= 13; // ilk elemanna (yani ilk dilimine) ekler Ayn dizinin imdiki hali: [[10, 11, 12, 13], [20, 21, 22], [30, 31, 32], [40, 41, 42], [50, 51]] Dizinin kendisi veya elemanlar sabit uzunluklu da olabilir: int[2][3][4] dizi; // 2 stun, 3 satr, 4 dzlem

Yukardaki tanm iki stunlu satrdan oluan drt dzlem diye dnebilirsiniz. yle bir dizi, rnein bir macera oyununda ve her katnda 2x3=6 oda bulunan 4 katl bir bina ile ilgili bir kavram iin kullanlyor olabilir. rnein yle bir binann ikinci katnn ilk odasnda bulunan eyalarn says yle arttrlabilir: // ikinci katn indeksi 1'dir ve o katn ilk odasna // [0][0] ile eriilir ++eyaSaylar[1][0][0];

19.8

zet
sabit uzunluklu dizilerin kendi elemanlar vardr; dilimler, kendilerine ait olmayan elemanlara eriim salarlar [] ileci iindeyken dizi_ismi.length yazmak yerine ksaca $ yazlabilir .dup nitelii, elemanlarn kopyalarndan oluan yeni bir dizi retir atama ilemi, sabit dizilerde eleman deerlerini deitirir; dilimlerde ise baka elemanlara eriim salanmasna neden olur uzayan dilim paylamdan ayrlr ve yeni kopyalanm olan elemanlara eriim salamaya balar dizi[] yazm dizinin btn elemanlar anlamna gelir; kendisine uygulanan ilem her bir elemanna ayr ayr uygulanr elemanlar dizi olan dizilere ok boyutlu dizi denir

19.9

Problem
Bir double dizisini bandan sonuna doru ilerleyin ve deerleri 10'dan byk olanlarn deerlerini yarya indirin. rnein elinizde u dizi varsa: double[] dizi = [ 1, 20, 2, 30, 7, 11 ]; elemanlarnn deerleri una dnsn:

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

78

Baka Dizi Olanaklar

[1, 10, 2, 15, 7, 5.5] eitli zmleri olsa da, bunu yalnzca dilim olanaklar ile baarmaya aln. e btn diziye eriim salayan bir dilimle balayabilirsiniz. Ondan sonra o dilimi her seferinde ba tarafndan tek eleman ksaltabilir ve dilimin hep ilk elemann kullanabilirsiniz. u ifade dilimi bandan tek eleman ksaltr: dilim = dilim[1 .. $]; ... zm

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

79

Dizgiler

20

Dizgiler
"merhaba" gibi metin paralarnn dizgi olduklarn zaten renmi ve imdiye kadarki kodlarda ok yerde kullanmtk. Dizgileri anlamaya yarayan iki olana da bundan nceki derslerde grdk: diziler ve karakterler... Dizgiler o iki olanan bileiminden baka bir ey deildir: elemanlarnn tr karakter olan dizilere dizgi denir. rnein char[] bir dizgi trdr. Ancak, karakterler dersinde grdmz gibi, D'de deiik karakter tr olduu iin, deiik dizgi trnden ve bunlarn bazen artc olabilecek etkileimlerinden sz etmek gerekir.

20.1

readf yerine readln ve chomp


Konsoldan satr okuma ile ilgili baz karklklara burada deinmek istiyorum. Dizgiler karakter dizileri olduklar iin satr sonu anlamna gelen '\n' gibi kontrol karakterlerini de barndrabilirler. O yzden, girdiimiz bilgilerin sonunda bastmz Enter tuunu temsil eden kodlar da okunurlar ve dizginin paras haline gelirler. Dahas, giriten ka karakter okunmak istendii de bilinmedii iin readf giri tkenene kadar gelen btn karakterleri dizginin iine okur. Bunun sonucunda da imdiye kadar kullanmaya altmz readf istediimiz gibi ilemez: import std.stdio; void main() { char[] isim; write("sminiz nedir? "); readf(" %s", &isim); } writeln("ok memnun oldum ", isim, "!");

Yazlan isimden sonra baslan Enter girii sonlandrmaz, ve readf dizgiye eklemek iin karakter beklemeye devam eder: sminiz nedir? Mert Enter'a basld halde giri sonlanmaz (bir kere daha basldn varsayalm) Konsolda girii sonlandrmak iin Linux ortamlarnda Ctrl-D'ye, Windows ortamlarnda da Ctrl-Z'ye baslr. Girii o ekilde sonlandrdnzda Enter'lar nedeniyle oluan satr sonu kodlarnn bile dizginin paras haline geldiklerini grrsnz: ok memnun oldum Mert isimden sonra "satr sonu" var ! (bir tane daha) simden hemen sonra yazdrlmak istenen nlem iareti satr sonu kodlarndan sonra belirmitir. Bu yzden readf ou durumda giriten dizgi okumaya uygun deildir. Onun yerine ismi "satr oku" anlamndaki "read line"dan tremi olan readln kullanlabilir. readln 'n kullanm readf 'ten farkldr; " %s" dzen dizgisini ve & ilecini gerektirmez:

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

80

Dizgiler

import std.stdio; void main() { char[] isim; write("sminiz nedir? "); readln(isim); } writeln("ok memnun oldum ", isim, "!");

Buna ramen satr sonunu belirleyen kodu o da barndrr: sminiz nedir? Mert ok memnun oldum Mert ! isimden sonra yine "satr sonu" var Dizgilerin sonundaki satr sonu kodlar std.string modlnde tanmlanm olan chomp ilevi ile silinebilir: import std.stdio; import std.string; void main() { char[] isim; write("sminiz nedir? "); readln(isim); isim = chomp(isim); } writeln("ok memnun oldum ", isim, "!");

Yukardaki chomp ifadesi isim 'in sonundaki satr sonu kodlarnn silinmi halini dndrr. O halinin tekrar isim 'e atanmas da isim 'i deitirmi olur: sminiz nedir? Mert ok memnun oldum Mert!

"satr sonu" kodlarndan arnm olarak

readln ve chomp zincirleme biimde daha ksa olarak da yazlabilirler: string isim = chomp(readln()); O yazm string trn tanttktan sonra kullanmaya balayacam.

20.2

Tek trnak deil, ift trnak


Tek trnaklarn karakter sabiti tanmlarken kullanldklarn grmtk. Dizgi sabitleri iin ise ift trnaklar kullanlr: 'a' karakterdir, "a" tek karakterli bir dizgidir.

20.3

string , wstring , ve dstring deimezdirler


D'de karakter trne karlk gelen farkl karakter dizisi tr vardr: char[] , wchar[] , ve dchar[] .

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

81

Dizgiler

Bu dizi trnn deimez olanlarn gstermek iin tane de takma isim vardr: string , wstring , ve dstring . Bu takma isimler kullanlarak tanmlanan deikenler deimezdirler. Bir rnek olarak, wchar[] deikendir, wstring deimezdir. (D'nin deimezlik kavramnn ayrntlarn daha sonraki derslerde greceiz.) rnein bir string 'in ba harfini bytmeye alan u kodda bir derleme hatas vardr: string deimez = "merhaba"; deimez[0] = 'M';

// derleme HATASI

Buna bakarak, deitirilmesi istenen dizgilerin dizi yazmyla yazlabileceklerini dnebiliriz ama o da derlenemez. Sol taraf dizi yazmyla yazarsak: char[] bir_dilim = "merhaba"; // derleme HATASI

O kod da derlenemez. Bunun iki nedeni vardr: 1. "merhaba" gibi kodun iine hazr olarak yazlan dizgilerin tr string 'dir ve bu yzden deimezdirler 2. Tr char[] olan sol taraf, sa tarafn bir dilimidir Bir nceki dersten hatrlayacanz gibi, sol taraf sa taraf gsteren bir dilim olarak alglanr. char[] deiebilir ve string deimez olduu iin de burada bir uyumsuzluk oluur: derleyici, deiebilen bir dilim ile deimez bir diziye eriilmesine izin vermemektedir. Bu durumda yaplmas gereken, deimez dizinin bir kopyasn almaktr. Bir nceki derste grdmz .dup niteliini kullanarak: import std.stdio; void main() { char[] dizgi = "merhaba".dup; dizgi[0] = 'M'; writeln(dizgi); } Derlenir ve dizginin ba harfi deiir: Merhaba Benzer ekilde, rnein string gereken yerde de char[] kullanlamaz. Deiebilen char[] trn, deitirilemeyen string trne dntrmek iin de .idup niteliini kullanmak gerekir. char[] ad_soyad = ad ~ soyad; writeln(tolower(ad_soyad.idup)); Bu, aada gsterilecek olan std.string modlnn string alan ilevlerini arrken karmza kacak.

20.4

string 'in artc olabilen uzunluu


Unicode karakterlerinin bazlarnn birden fazla baytla gsterildiklerini ve Trk alfabesine zg harflerin iki baytlk olduklarn grmtk. Bu bazen artc olabilir: writeln("a".length);

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

82

Dizgiler

"a" dizgisi 2 harf ierdii halde dizinin uzunluu 3'tr: 3 Bunun nedeni, "merhaba" eklinde yazlan hazr dizgilerin eleman trnn char olmasdr. char da UTF-8 kod birimi olduu iin, o dizginin uzunluu 3'tr (a iin tek, iin iki kod birimi). Bunun grnr bir etkisi, iki baytlk bir harfi tek baytlk bir harfle deitirmeye altmzda karmza kar: char[] d = "a".dup; writeln("nce: ", d); d[1] = 't'; writeln("Sonra:", d);

nce: a Sonra:at

YANLI

O kodda dizginin '' harfinin 't' harfi ile deitirilmesi istenmi, ancak 't' harfi tek bayttan olutuu iin ''yi oluturan baytlardan ancak birincisinin yerine gemi ve ikinci bayt ktda belirsiz bir karaktere dnmtr. O yzden, baz baka programlama dillerinin normal karakter tr olan char ' D'de bu amala kullanamayz. (Ayn saknca wchar 'da da vardr.) Unicode'un tanmlad anlamda harflerle, imlerle, ve dier simgelerle ilgilendiimiz durumlarda dchar trn kullanmamz gerekir: dchar[] d = "a"d.dup; writeln("nce: ", d); d[1] = 't'; writeln("Sonra:", d);

nce: a Sonra:at Doru alan kodda iki deiiklik yapldna dikkat edin: 1. dizginin tr dchar[] olarak belirlenmitir 2. "a"d hazr dizgisinin sonunda d belirteci kullanlmtr (o belirtecin anlam bir sonraki balkta aklanyor)

20.5

Hazr dizgiler
Hazr dizgilerin zellikle belirli bir karakter trnden olmasn salamak iin sonlarna belirleyici karakterler eklenir: import std.stdio; void main() { string s = "a"c; wstring w = "a"w; dstring d = "a"d; writeln(s.length); writeln(w.length);

// bu, "a" ile ayn eydir

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

83

Dizgiler

writeln(d.length);

3 2 2 a ve harflerinin her ikisi de wchar ve dchar trlerinden tek bir elemana sabildikleri iin, son iki dizginin uzunluklar 2 olmaktadr.

20.6

Dizgi birletirmek
Dizgiler aslnda dizi olduklarndan, dizi ilemleri onlar iin de geerlidir. ki dizgiyi birletirmek iin ~ ileci, bir dizginin sonuna baka dizgi eklemek iin de ~= ileci kullanlr: import std.stdio; import std.string; void main() { char[] isim; write("sminiz? "); readln(isim); isim = chomp(isim); // Birletirme rnei: char[] selam = "Merhaba " ~ isim; // Sonuna ekleme rnei: selam ~= "! Hogeldin..."; } writeln(selam);

sminiz? Can Merhaba Can! Hogeldin...

20.7

Dizgileri karlatrmak
Not: Unicode btn yaz sistemlerindeki harfleri tanmlasa da onlarn nasl sralanacaklarn belirlemez. Aadaki ilevleri kullanrken bu konuda beklenmedik sonularla karlaabilirsiniz. Sralamann nemli olduu durumlarda trileri gibi bir ktphaneden yararlanmak gerekebilir. Daha nce saylarn kklk byklk karlatrmalarnda kullanlan < , >= , vs. ilelerini grmtk. Ayn ileleri dizgilerle de kullanabiliriz. Bu ilelerin kklk kavram dizgilerde alfabetik srada nce anlamndadr. Benzer ekilde, byklk de alfabetik srada sonra demektir: import std.stdio; import std.string; void main() { write(" Bir dizgi giriniz: "); char[] dizgi_1; readln(dizgi_1); dizgi_1 = chomp(dizgi_1);

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

84

Dizgiler

write("Bir dizgi daha giriniz: "); char[] dizgi_2; readln(dizgi_2); dizgi_2 = chomp(dizgi_2); if (dizgi_1 == dizgi_2) { writeln("kisi ayn!"); } else { char[] nce_olan; char[] sonra_olan; if (dizgi_1 < dizgi_2) { nce_olan = dizgi_1; sonra_olan = dizgi_2; } else { nce_olan = dizgi_2; sonra_olan = dizgi_1; } writeln("Sralamada nce '", nce_olan, "', sonra '", sonra_olan, "' gelir.");

20.8

Byk kk harfler farkldr


Harflerin byk ve kk hallerinin farkl karakter kodlarna sahip olmalar onlarn birbirlerinden farkl olduklar gereini de getirir. rnein 'A' ile 'a' farkl harflerdir. Ek olarak, ASCII tablosundaki kodlarnn bir yansmas olarak, byk harflerin hepsi, sralamada kk harflerin hepsinden nce gelir. rnein byk olduu iin 'B', sralamada 'a'dan nce gelir. Ayn ekilde, "aT" dizgisi, 'T' harfi ''den nce olduu iin "a" dizgisinden nce sralanr. Bazen dizgileri harflerin kk veya byk olmalarna bakmakszn karlatrmak isteriz. Byle durumlarda yukarda gsterilen aritmetik ileler yerine, aada gsterilecek olan icmp ilevini kullanmak gerekir.

20.9

std.string modl
std.string modl dizgilerle ilgili ilevler ierir. Bu ilevlerin tam listesini Ddili Wiki'nin std.string sayfasnda bulabilirsiniz. Oradaki ilevler arasndan bir ka tanesi: indexOf : Verilen karakteri bir dizgi iinde batan sona doru arar ve bulursa bulduu yerin indeksini, bulamazsa -1 deerini dndrr. Seime bal olarak bildirilebilen nc parametre, kk byk harf ayrm olmadan aranmasn salar lastIndexOf : indexOf 'a benzer ekilde alr. Fark, sondan baa doru aramasdr countchars : Birinci dizgi iinde ikinci dizgiden ka tane bulunduunu sayar tolower : Verilen dizginin, btn harfleri kk olan edeerini dndrr toupper : tolower 'a benzer ekilde alr. Fark, byk harf kullanmasdr strip : Dizginin bandaki ve sonundaki boluklar siler insert : Dizginin iine baka dizgi yerletirir Dizgiler de aslnda dizi olduklarndan, diziler iin yararl ilevler ieren std.array ve std.algorithm modllerindeki ilevler de dizgilerle kullanlabilir.

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

85

Dizgiler

20.10

Problemler
1. std.string modlnn belgesine hzlca da olsa bir gz atn. 2. ~ ilecini de kullanan bir program yazn: kullanc, btnyle kk harflerden oluan ad ve soyad girsin; program nce bu iki kelimeyi aralarnda boluk olacak ekilde birletirsin ve sonra ba harflerini bytsn. rnein "ebru" ve "domates" girildiinde programn kts "Ebru Domates" olsun. Bu programda alrken toupper , capitalize , ve capwords ilevlerinin n de deneyin ve farkllklarn grn. 3. Kullancdan bir satr aln. Satrn iindeki ilk 'a' harfinden, satrn iindeki son 'a' harfine kadar olan blm yazdrsn. rnein kullanc "balkadam" dizgisini girdiinde ekrana "alkada" yazdrlsn. Bu programda indexOf ve lastIndexOf ilevlerini kullanarak iki deiik indeks bulmanz, ve bu indekslerle bir dilim oluturmanz ie yarayabilir. indexOf ve lastIndexOf ilevlerinin dn trleri int deil, sizediff_t 'dir. lk 'a' harfini bulmak iin yle bir satr kullanabilirsiniz: sizediff_t ilk_a = indexOf(satr, 'a'); Bir ka ders sonra greceimiz auto anahtar szc ile daha da ksa olabilir: auto ilk_a = indexOf(satr, 'a'); ... zmler

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

86

Standart Akmlar Dosyalara Balamak

21

Standart Akmlar Dosyalara Balamak


nceki blmlerdeki programlar hep standart giri ve k akmlar ile etkileiyorlard. D'nin standart akmlarnn stdin ve stdout olduklarn grmtk, ve aka akm bildirmeden arlan writeln gibi ilevlerin de arka planda bu akmlar kullandklarn renmitik. Ek olarak, standart giriin hep klavye olduu, ve standart kn da hep ekran olduu durumlarda almtk. Bundan sonraki blmde programlar dosyalarla etkileecek ekilde yazmay reneceiz. Dosyalarn da karakter akm olduklarn, ve bu yzden standart giri ve kla etkilemekten bir farklar olmadklarn greceksiniz. Dosya akmlarna gemeden nce, programclk hayatnzda ok iinize yarayacak baka bir bilgiyi bu blmde vermek istiyorum: programnzn standart giri ve kn, sizin kodunuzda hibir deiiklik gerekmeden dosyalara balayabilirsiniz. Programnz ekran yerine bir dosyaya yazabilir, ve klavye yerine bir dosyadan veya bir programdan okuyabilir. Bu, btn modern u birimlerin hepsinde bulunan ve programlama dilinden bamsz bir olanaktr.

21.1

Standart k > ile bir dosyaya balamak


Programnz bir u birimden balatyorsanz, program altrmak iin yazdnz komutun sonuna > karakterinden sonra bir dosya ismi yazmanz, programn standart k akmnn o dosyaya balanmas iin yeterlidir. Bu durumda, programn standart kna yazd herey o dosyaya yazlr. Standart giriinden bir say alan, o sayy 2 ile arpan, ve sonucu standart kna yazdran bir program dnelim: import std.stdio; void main() { double say; readf(" %s", &say); } writeln(say * 2);

O programn isminin iki_kat olduunu varsayarsak, program komut satrndan ./iki_kat > iki_kat_sonucu.txt eklinde balatr ve giriine rnein 1.2 yazarsanz, programn kts olan 2.4'n ekrana deil, iki_kat_sonucu.txt ismindeki bir dosyaya yazldn grrsnz. Not: Bu program batan "Ltfen bir say giriniz: " gibi bir mesaj yazmad halde, siz yine de sayy klavyeden yazp Enter'a basmalsnz.

21.2

Standart girii < ile bir dosyaya balamak


kn > karakteriyle bir dosyaya balanmasna benzer ekilde, giri de < karakteriyle bir dosyaya balanabilir. Bu durumda da giriinden bilgi bekleyen bir program klavyeden okumak yerine, belirtilen dosyadan okur. Bu sefer de elimizde giriinden ald saynn onda birini hesaplayan bir program olsun:

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

87

Standart Akmlar Dosyalara Balamak

import std.stdio; void main() { double say; readf(" %s", &say); } writeln(say / 10);

Eer iki kat alan programn oluturduu dosya hl klasrde duruyorsa, ve bu programn isminin de onda_bir olduunu varsayarsak, program komut satrndan ./onda_bir < iki_kat_sonucu.txt eklinde balatrsanz, giriini daha nce oluturulan iki_kat_sonucu.txt dosyasndan aldn ve ka 0.24 yazdrdn grrsnz. Not: iki_kat_sonucu.txt dosyasnda 2.4 olduunu varsayyorum. onda_bir program; ihtiyac olan sayy artk klavyeden deil, bir dosyadan okumaktadr.

21.3

Giri ve k akmlarnn ikisini birden dosyalara balamak


> ve < karakterlerini ayn anda kullanabilirsiniz: ./onda_bir < iki_kat_sonucu.txt > butun_sonuc.txt Bu sefer giri iki_kat_sonucu.txt dosyasndan okunur, ve k da butun_sonuc.txt dosyasna yazlr.

21.4

Programlar | ile birbirlerine balamak


Yukarda kullanlan iki_kat_sonucu.txt dosyasnn iki program arasnda araclk yaptna dikkat edin: iki_kat program, hesaplad sonucu iki_kat_sonucu.txt dosyasna yazmaktadr, ve onda_bir program da ihtiya duyduu sayy iki_kat_sonucu.txt dosyasndan okumaktadr. | karakteri, programlar byle bir arac dosyaya gerek olmadan birbirlerine balar. | karakteri, solundaki programn standart kn sandaki programn standart giriine balar. rnein komut satrnda birbirine u ekilde balanan iki program, toplu olarak "bete bir" hesaplayan bir komut haline dnr: ./iki_kat | ./onda_bir nce iki_kat program alr ve giriinden bir say alr. Not: O programn "Ltfen bir say giriniz: " gibi bir mesaj yazmadn hatrlayn; siz yine de sayy klavyeden yazp Enter'a basmalsnz. Sonra, iki_kat programnn k onda_bir programnn giriine verilir ve iki kat alnm olan saynn onda biri, yani bataki saynn "bete biri" ka yazlr.

21.5

Problem
kiden fazla program art arda balamay deneyin:

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

88

Standart Akmlar Dosyalara Balamak

./birinci | ./ikinci | ./ucuncu ... zm

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

89

Dosyalar

22

Dosyalar
Ne kadar gl olsalar da; nceki blmde grdmz > , < , ve | her duruma uygun deildir. nk her program iini yalnzca standart giri ve kla etkileerek yapamaz. rnein renci kaytlar ile ilgilenen bir program, standart kn kullancya bir komut mens gstermek iin kullanyor olabilir. Standart giriini de kullancdan komut almak iin kullandn dnrsek, byle bir programn kaytlarn tuttuu renci bilgilerini yazmak iin bir dosyaya ihtiyac olacaktr. Bu blmde iletim sisteminin klasrlerde barndrd dosyalara yazmay ve dosyalardan okumay reneceiz.

22.1

Temel kavramlar
Dosya ilemleri iin yine std.stdio modlnde tanmlanm olan File yaps kullanlr. Henz yaplar gstermediim iin File nesnelerinin kurulma sz dizimi konusunda baz kabuller yapmanz bekleyeceim. rneklere gemeden nce dosyalarla ilgili genel kavramlar tantacam.

22.1.1

Kar taraf
Bu blmdeki bilgilerle oluturulan dosyalarn her ortamda rahata okunabileceklerini dnmeyin. Benzer ekilde, baka ortamlarda oluturulmu dosyalarn da bu blmdeki bilgilerle alabileceklerini beklemeyin. Bir dosya oluturup iine bilgiler yazmak, o dosyann baka bir ortamda alp okunmas iin yeterli olmayabilir. Dosyay yazan tarafla okuyan tarafn belirli konularda anlam olmalar gerekir. rnein dosyaya char[] olarak yazlm olan bir bilginin wchar[] olarak okunmas yanl sonu dourur. Bu blmde greceiniz File 'n olanaklarn, u durumlarda kullanl olacaklarn dnerek renin: yalnzca programnz tarafndan yazlacak olan ve yine programnz tarafndan okunacak olan dosyalar; rnein programnzn ayarlarnn ve verilerinin sakland dosyalar BOM'suz UTF-8 okuyabilen bir ortama gnderilen UTF-8 dosyalar; eer kar taraf BOM'suz UTF-8 dosyalar doru olarak aabiliyorsa, bu blmdeki olanaklar kullanabilirsiniz baka bir ortamdan gelen BOM'suz UTF-8 dosyalar; bu blmdeki olanaklar kullanarak bu tr dosyalar doru olarak aabilirsiniz

22.1.2

Dosya eriim haklar


letim sistemi, dosyalar programlara eitli eriim haklaryla sunar. Eriim haklar hem performans, hem de dosya sal asndan nemlidir. Konu dosyadan okumak olunca; ayn dosyadan okumak isteyen birden fazla programa ayn anda okuma izni verilmesi, programlar birbirlerini beklemeyecekleri iin hz kazanc salar. te yandan, konu dosyaya yazmak olunca; dosyann ieriinin tutarll asndan dosyaya belirli bir anda ancak tek bir programn yazmasna izin verilmelidir; yoksa iki programn birbirlerinden habersiz olarak yazmalar sonucunda dosyann ierii tutarsz hale gelebilir. D dosyalar, eriim haklarndan temelde iki tanesi ile ilgilidir: okuma ve yazma eriimi.

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

90

Dosyalar

22.1.3

Dosya amak
Programn standart giri ve k akmlar olan stdin ve stdout , program baladnda zaten alm ve kullanma hazr olarak gelirler; onlar kullanmaya balamadan nce zel bir ilem yapmamz gerekmez. Dosyalarn ise diskteki isimleri ve istenen eriim haklar bildirilerek program tarafndan almalar gerekir.

22.1.4

Dosya kapatmak
Alan dosyalarn mutlaka kapatlmalar da gerekir; ancak, File nesneleri kendileri sonlanrken dosyalarn da kapattklar iin, normalde bu iin programda aka yaplmas gerekmez. Dosya, File nesnesinin iinde bulunduu kapsamdan klrken kendiliinden kapatlr. rnek: if (bir_koul) { // dosya burada oluturulmu ve kullanlm olsun // ... } // dosya bu kapsamdan klrken otomatik olarak kapatlr Bazen, ayn dosyann ayn kapsam iinde ama deiik eriim haklaryla almas gerekebilir. rnein balangta yazma eriimi ile alarak iine bilgi yazlm olan bir dosya; yine ayn kapsam iinde, ama bu sefer okuma eriimi ile alabilir. Byle bir durumda, dosyay nce kapatmak, sonra yeni eriim hakkyla tekrar amak gerekir.

22.1.5

Dosyaya yazmak ve dosyadan okumak


Dosyalar da karakter akmlar olduklar iin, alk olduunuz writeln ve readf gibi ilevleri onlarla da kullanabilirsiniz. Farkl olarak yapmanz gereken ey, dosya nesnesinin ismini ve nokta ilecini de kullanmaktr: writeln("merhaba"); // standart ka yazar stdout.writeln("merhaba"); // yukardakinin uzun yazmdr dosya.writeln("merhaba"); // dosyaya yazar

22.1.6

Dosya sonu: eof()


Bir dosyadan okurken dosyann sonuna gelinip gelinmedii, "dosya sonu" anlamna gelen "end of file"n ksaltmas olan eof() ye ileviyle denetlenir. Bu ilev, dosya sonuna gelindiinde true dndrr. Bir dosyann sonuna gelene kadar ilem yapmak iin yle bir dng yazlabilir: while (!dosya.eof()) { /* ... */ }

22.1.7

Klasr ilemleri iin std.file modl


Klasr ilemleri ile ilgili olan std.file modlnn Ddili Wiki'deki sayfasnda iinize yarayacak ilevler bulabilirsiniz. rnein exists , belirtilen isimde bir dosyann klasrde zaten bulunup bulunmadn bildirir: if (exists(dosya_ismi)) { // dosya mevcut

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

91

Dosyalar

} else { // dosya mevcut deil }

22.2

std.stdio.File yaps
File , C dilindeki standart fopen ilevinin kulland eriim belirtelerini kullanr: Belirte r r+ w Anlam okuma eriimi dosya, bandan okunacak ekilde hazrlanr okuma ve yazma eriimi dosya, bandan okunacak ve bana yazlacak ekilde hazrlanr yazma eriimi dosya yoksa: bo olarak oluturulur dosya zaten varsa: ii boaltlr okuma ve yazma eriimi dosya yoksa: bo olarak oluturulur dosya zaten varsa: ii boaltlr sonuna yazma eriimi dosya yoksa: bo olarak oluturulur dosya zaten varsa: ierii korunur ve sonuna yazlacak ekilde hazrlanr okuma ve sonuna yazma eriimi dosya yoksa: bo olarak oluturulur dosya zaten varsa: ierii korunur; bandan okunacak ve sonuna yazlacak ekilde hazrlanr

w+

a+

Yukardaki eriim haklarnn "rb" gibi sonuna 'b' karakteri gelenleri de vardr. O durumda, dosyaya yazma ve dosyadan okuma ilemleri srasnda karakter dnmleri yaplmaz. Baz zel kodlarn deiik ortamlardaki alt dzey gsterimleriyle ilgili olan bu konuyu bu derste anlatmayacam. Bu yapnn belgesini std.stdio wiki sayfasnda bulabilirsiniz.
22.2.1

Yazma rnei
import std.stdio; void main() { File dosya = File("ogrenci_bilgisi", "w"); dosya.writeln("sim : ", "Zafer"); dosya.writeln("Numara: ", 123); dosya.writeln("Snf : ", "1A");

Dizgiler dersinden hatrlayacanz gibi, "ogrenci_bilgisi" gibi bir dizginin tr string 'dir ve deimezdir. Yani File , dosya ismini ve eriim hakkn string tr olarak kabul eder. Bu yzden, yine dizgiler blmnden hatrlayacanz gibi, File ' rnein char[] trnde bir dizgi ile kuramazsnz; kurmanz gerektiinde o dizginin .idup niteliini armanz gerekir. Bu bilgiyi problemlerden birisinde hatrlamanz gerekecek.

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

92

Dosyalar

Yukardaki program, altrld klasr iinde ismi ogrenci_bilgisi olan bir dosya oluturur. Not: Dosya ismi olarak dosya sisteminin izin verdii her karakteri kullanabilirsiniz. Ben bu derslerde dosya isimlerinde yalnzca ASCII harfler kullanacam.
22.2.2

Okuma rnei
import std.stdio; import std.string; void main() { File dosya = File("ogrenci_bilgisi", "r"); while (!dosya.eof()) { string satr = chomp(dosya.readln()); writeln("Okuduum satr -> |", satr); }

Yukardaki program, altrld klasr iindeki ogrenci_bilgisi isimli dosyann iindeki satrlar bandan sonuna kadar okur ve standart ka yazdrr.

22.3

Problem
Yazacanz program kullancdan bir dosya ismi alsn, o dosyann iindeki bo olmayan btn satrlar, dosyann ismine .bak eklenmi baka bir dosyaya yazsn. rnein, verilen dosyann ismi deneme.txt ise, bo olmayan satrlarn deneme.txt.bak dosyasna yazsn. inize yarayacak bilgiler: std.stdio modlnn belgelerinde grld gibi, File'n kurucu ilevi, dosya ismini string trnde alr. Eer elinizde deiebilen trde bir dizgi varsa, rnein bir char[] varsa, dosya ismi olarak onun .idup ile alnm bir kopyasn kullanmanz gerekecektir Satrn bo olup olmadn .length niteliinin deerine bakarak anlayabilirsiniz ... zm

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

93

auto ve typeof

23
23.1

auto ve typeof
auto
Bazen ayn ismin iki veya daha fazla modlde birden tanml olduu durumlarla karlalabilir. rnein birbirlerinden farkl iki ktphanenin iki modlnde de File isminde bir tr bulunabilir. O ismi tanmlayan iki modln birden eklenmesi durumunda da yalnzca File yazmak karkla neden olur; derleyici hangi trn kullanlacan bilemez. Byle durumlarda hangi modldeki ismin kastedildiini belirtmek iin modln ismini de yazmak gerekir. rnein File tr ile ilgili byle bir isim akmas olduunu varsayarsak: std.stdio.File dosya = std.stdio.File("ogrenci_bilgisi", "r"); O kullanmda uzun ismin hem de iki kere yazlmas gerekmitir: sol tarafta dosya nesnesinin trn belirtmek iin, sa tarafta ise File nesnesini kurmak iin. Oysa derleyiciler ou durumda sol tarafn trn sa tarafn trne bakarak anlayabilirler. rnein 42 gibi bir tamsay deerle ilklenen bir deikenin int olduu, veya std.stdio.File kurularak oluturulan bir nesnenin yine std.stdio.File trnden olduu kolayca anlalabilir. D'nin auto anahtar szc, sol tarafn trnn sa taraftan anlalabildii durumlarda sol tarafn yazmn kolaylatrmak iin kullanlr: auto dosya = std.stdio.File("ogrenci_bilgisi", "r"); auto 'yu her trle kullanabilirsiniz: auto auto auto auto say = 42; kesirliSay = 1.2; selam = "Merhaba"; vida = BisikletVitesDzeneininAyarVidas(10);

"auto", otomatik anlamna gelen "automatic"in ksaltmasdr. Buna ramen trn otomatik olarak anlalmas kavram ile ilgili deildir. Aslnda deikenlerin yaam sreleri ile ilgili olan auto , tanm srasnda baka belirte bulunmad zaman kullanlr. Baka belirteler de trn otomatik olarak anlalmas iin yeterlidir: immutable i = 42; Zaten immutable yazlm olduu iin trn deimez bir int olduu o yazmdan da otomatik olarak anlalr. (immutable anahtar szcn daha sonra greceiz.)

23.2

typeof
Bu anahtar szck, "tr" anlamna gelen "type of" deyiminden tremitir. Kendisine verilen deikenin, nesnenin, hazr deerin, vs. trn retir. rnein zaten tanmlanm olan int trnde say isminde bir deiken olduunu varsayarsak: int say = 100; // bu zaten 'int' olarak tanmlanm

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

94

auto ve typeof

typeof(say) say2; typeof(100) say3;

// "say'nn tr" anlamnda // "100 hazr deerinin tr" anlamnda

Yukardaki son iki ifade, u ikisinin edeeridir: int say2; int say3; Trlerin zaten bilindii yukardaki gibi durumlarda typeof 'un kullanlmasna gerek olmad aktr. Bu anahtar szck daha sonra anlatlacak olan ablonlar konusunda, ve bu dersin probleminde yararl olacak.

23.3

Problem
42 gibi bir hazr deerin D'nin tamsay trlerinden int trnde olduunu yukarda okudunuz. (Yani short , long , vs. deil.) Bir program yazarak 1.2 gibi bir hazr deerin trnn D'nin kesirli say trlerinden hangisinden olduunu bulun: float mu, double m, yoksa real mi? Yeni rendiiniz typeof ve temel trler dersinde rendiiniz .stringof iinize yarayabilir. ... zm

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

95

sim Alan

24

sim Alan
D'de her isim, tanmland noktadan balayarak hem iinde tanmland kapsamda, hem de o kapsamn iindeki kapsamlarda geerlidir. Her kapsam bir isim alan tanmlar. inde tanmland kapsamdan kldnda, isim artk geersiz hale gelir ve derleyici tarafndan tannmaz: void main() { int dSay; if (birKoul) { int iSay = 1; dSay = 2; } iSay = 3; } // yeni bir kapsam balatr // alr; ieride de geerlidir

// iSay'nn geerlilii burada son bulur // derleme HATASI // iSay'nn geerli olduu kapsamdan // klmtr

if dngsnn kapsam iinde tanmlanan iSay , o kapsamn dnda geersizdir. te yandan, dSay hem dardaki hem de ierideki kapsamda geerlidir. Bir kapsamda tanmlanm bir ismin ierdeki bir kapsamda tekrar tanmlanmas yasal deildir: int uzunluk = tekSaylar.length; if (birKoul) { int uzunluk = asalSaylar.length; } // derleme hatas

Not: C dilinden beri yasal olan bu kullanm D'de artk emekliye ayrlmaktadr.

24.1

simleri kullanldklar ilk noktada tanmlamak


imdiye kadarki rneklerde de grdnz gibi, isimlerin kullanldklar ilk noktadan daha nce tanmlanm olmalar gerekir: writeln(say); int say = 42; // derleme HATASI; say henz bilinmiyor

O kodun alabilmesi iin say 'nn writeln ileminden daha nce tanmlanm olmas gerekir. Ka satr nce tanmlanaca programcya bal olsa da, her ismin kullanld ilk noktaya en yakn yerde tanmlanmas programclk asndan daha iyi kabul edilir. Bunu kullancdan ald saylarn ortalamalarn yazdran bir programn main ilevinde grelim. zellikle C dilinden gelen programclar, kullanlan btn isimleri kapsamlarn en banda tanmlamaya almlardr: int adet; int[] saylar; double ortalamaDeer; write("Ka say gireceksiniz? "); readf(" %s", &adet); // BURADA // BURADA // BURADA

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

96

sim Alan

if (adet >= 1) { saylar.length = adet; // ... burada asl ilemler yaplyor olsun... } else { writeln("HATA: En az bir say girmelisiniz!"); } Bunun kart olarak, isimleri olabildiince ge tanmlamak nerilir. Ayn program bu tavsiyeye uyarak yle yazabiliriz: write("Ka say gireceksiniz? "); int adet; readf(" %s", &adet); if (adet >= 1) { int[] saylar; saylar.length = adet; // BURADA

// BURADA

double ortalamaDeer; // BURADA // ... burada asl ilemler yaplyor olsun... } else { writeln("HATA: En az bir say girmelisiniz!"); } Btn deikenleri bir arada en bata tanmlamak yapsal olarak daha iyi olsa da, deikenleri ge tanmlamann da bir ka nemli yarar vardr: Hz: her deiken tanmnn program hz asndan bir bedeli vardr. D'de btn deikenler ilklendikleri iin, belki de hi kullanlmayacak olan deikenleri en batan ilklemek, o ilem iin geen zamann boa gitmesine neden olabilir Hata riski: Deikenlerin tanmlar ile kullanmlar arasna giren her satr, program hatalar asndan ufak da olsa bir risk tar: bir rnek olarak, uzunluk gibi genel bir ismi olan bir deiken aradaki satrlarda yanllkla baka bir uzunluk kavram iin kullanlm, ve asl kullanlaca yere gelindiinde deeri oktan deimi olabilir Okuma kolayl: Kapsamdaki satrlar oaldka, alttaki satrlarda kullanlan bir deikenin tanmnn programn yazld ekrann dnda kalma olasl artar; deikenlerin tanmlarn grmek veya hatrlamak iin sk sk metnin st tarafna gitmek ve tekrar geri gelmek gerekebilir Kod deiiklii: Program kodlar srekli olarak geliim halindedirler: programa ekler yaplr, programn baz olanaklar silinir, farkedilen hatalar giderilir, vs. Bu ilemler srasnda ou zaman bir grup satrn hep birden baka bir ilev olarak tanmlanmas istenebilir. Byle durumlarda, o kod satrlarnda kullanlan btn deikenlerin kullanldklar ilk yerde tanmlanm olmalar, hepsinin birden baka bir yere tanmalarna olanak salar. rnein yukardaki bu tavsiyeye uyan programn if kapsamndaki btn satrlar hep birden programn baka bir noktasna tanabilirler. Oysa deikenlerini C'deki gibi tanmlayan bir programda, tanacak olan kod satrlarnda kullanlan deikenlerin de teker teker seilerek ayr ayr tanmalar gerekir.

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

97

for Dngs

25

for Dngs
while dngs ile ayn ie yarar. Yarar, dng ile ilgili btn tanmlarn tek satrda yaplmasdr. ou duruma daha uygun olduu iin programlarda while 'dan daha ok kullanlr.

25.1

while 'n blmleri


Hatrlarsak, while dngs tek bir koul denetler ve o koul doru olduu srece dngye devam eder. rnein 1'den 10'a kadar olan btn tamsaylar yazdran bir dng "say 11'den kk olduu srece" eklinde kodlanabilir: while (say < 11) O dngnn ilerletilmesi, say 'nn dng iinde bir arttrlmas ile salanabilir: ++say; Kodun derlenebilmesi iin say 'nn while 'dan nce tanmlanm olmas gerekir: int say = 1; Dngnn asl ilemlerini de sayarsak, btn blmlerine deinmi oluruz: writeln(say); Bu drt ilemi dngnn hazrl, devam etme koulunun denetimi, asl ilemleri, ve ilerletilmesi olarak aklayabiliriz: int say = 1; while (say < 11) { writeln(say); ++say; } // hazrlk // devam koulu // asl ilemler // dngnn ilerletilmesi

while dngs srasnda bu blmler u srada iletilirler: hazrlk koul denetimi asl ilemler ilerletilmesi koul denetimi asl ilemler ilerletilmesi ...

25.2

for 'un blmleri


for dngs bu drt ilemden n tek bir tanma indirgeyen deyimdir. Bu ilemlerin de for deyiminin parantezi iinde, ve aralarnda noktal virgl olacak ekilde yazlrlar. Asl ilemler ise kapsam iindedir:

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

98

for Dngs

for (/* hazrlk */; /* devam koulu */; /* ilerletilmesi */) { /* asl ilemler */ } Yukardaki while dngs for ile yazldnda ok daha dzenli bir hale gelir: for (int say = 1; say < 11; ++say) { writeln(say); } Bu, zellikle dng kapsamnn kalabalk olduu durumlarda ok yararldr: dngy ilerleten ilem, kapsam iindeki dier ifadeler arasnda kaybolmak yerine, for ile ayn satrda durur ve kolayca grlr. for dngsnn blmleri de while 'n blmleriyle ayn srada iletilirler. break ve continue deyimleri for dngsnde de ayn ekilde alrlar. ok sk olarak, dngy ilerletmek iin bir tamsay kullanlr, ama yle olmas gerekmez. rnein belirli bir deer aralndaki kesirli saylarn srekli olarak yarlarn gsteren bir dng yle yazlabilir: for (double say = 1; say > 0.001; say /= 2) { writeln(say); }

25.3

Dngnn blm de bo braklabilir


Gereken durumlarda istee bal olarak, bu blmler bo braklabilir: Bazen hazrlk iin bir deiken tanmlamak gerekmez nk zaten tanmlanm olan bir deiken kullanlacaktr Bazen dngy sonlandrmak iin dng koulu yerine dng iindeki break satrlarndan yararlanlr Bazen dngy ilerletme adm belirli koullara bal olarak dng iinde yaplabilir Btn blmler bo brakldnda, for dngs sonsuza kadar anlamna gelir: for ( ; ; ) { // ... } yle bir dng, rnein ya hi klmayacak ekilde, veya belirli bir koul gerekletiinde break ile klacak ekilde tasarlanm olabilir.

25.4

Dng deikeninin geerli olduu kapsam


for ile while 'n tek fark, dng hazrl srasnda tanmlanan ismin geerlilik alandr: for dngsnn hazrlk blgesinde tanmlanan isim, yalnzca dng iindeki kapsamda geerlidir (ve onun iindekilerde), dardaki kapsamda deil: for (int i = 0; i < 5; ++i) { // ... } writeln(i); // derleme HATASI // i burada geerli deildir

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

99

for Dngs

while dngsnde ise, isim while 'n da iinde bulunduu kapsamda tanmlanm olduu iin, while 'dan kldnda da geerliliini korur: int i = 0; while (i < 5) { // ... ++i; } writeln(i); // alr; i burada hl geerlidir

for dngsnn bu ismin geerlilik alann kk tutuyor olmas, bir nceki dersin sonunda anlatlanlara benzer ekilde, programclk hatas risklerini de azaltr.

25.5

Problemler
1. ie iki for dngs kullanarak, ekrana satr ve stun numaralarn gsteren 9'a 9'luk bir tablo yazdrn: 0,0 0,1 0,2 0,3 0,4 0,5 0,6 0,7 0,8 1,0 2,0 3,0 4,0 5,0 6,0 7,0 8,0 1,1 2,1 3,1 4,1 5,1 6,1 7,1 8,1 1,2 2,2 3,2 4,2 5,2 6,2 7,2 8,2 1,3 2,3 3,3 4,3 5,3 6,3 7,3 8,3 1,4 2,4 3,4 4,4 5,4 6,4 7,4 8,4 1,5 2,5 3,5 4,5 5,5 6,5 7,5 8,5 1,6 2,6 3,6 4,6 5,6 6,6 7,6 8,6 1,7 2,7 3,7 4,7 5,7 6,7 7,7 8,7 1,8 2,8 3,8 4,8 5,8 6,8 7,8 8,8

2. Bir veya daha fazla for dngs kullanarak ve * karakterini gereken sayda yazdrarak geometrik ekiller izdirin: * ** *** **** ***** ****** ******* ******** *********

******** ******** ******** ******** ******** ******** ******** ******** ******** vs.

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

100

for Dngs

... zmler

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

101

l le ?:

26

l le ?:
?: ileci, temelde bir if-else deyimi gibi alr: if (/* /* } else /* } koul */) { doruluk ilemleri */ { doru olmama ilemleri */

if deyimi, koul doru olduunda doruluk ilemlerini, aksi durumda dier ilemleri iletir. Hatrlarsanz, if bir deyimdir ve bu yzden kendi deeri yoktur; tek etkisi, programn ileyiini etkilemesidir. ?: ileci ise bir ifadedir ve if-else ile ayn ii, ama bir deer retecek ekilde gerekletirir. Yukardaki kodu ?: kullanarak yle yazabiliriz (blm aklamalarn ksaltarak gsteriyorum): /* koul */ ? /* doruluk ilemi */ : /* doru olmama ilemi */ ?: ileci blmndeki ifade yznden l ile olarak adlandrlr. Bu ilecin deeri; koula bal olarak ya doruluk ileminin, ya da doru olmama ileminin deeridir. fade olduu iin, ifadelerin kullanlabildii her yerde kullanlabilir. Aadaki rneklerde ayn ii hem ?: ileci ile, hem de if deyimi ile gerekletireceim. ?: ilecinin bu rneklerdeki gibi durumlarda ok daha ksa olduunu greceksiniz. Deer atama Artk yl olduunda 366, olmadnda 365 deerini atamak iin: int gnAdedi = artkYl ? 366 : 365; Ayn ii if ile yapmak istesek; bir yol, batan hi ilklememek ve deeri sonra vermektir: int gnAdedi; if (artkYl) { gnAdedi = 366; } else { gnAdedi = 365; } if ile baka bir yol; batan artk yl deilmi gibi ilklemek ve adedi sonra bir arttrmak olabilir: int gnAdedi = 365; if (artkYl) { ++gnAdedi; } Yazdrma Yazdrlan bir mesajn bir parasn ?: ile duruma gre farkl yazdrmak: writeln("Bardan yars ", iyimser ? "dolu" : "bo");

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

102

l le ?:

Ayn ii yapmak iin mesajn ba tarafn nce yazdrabilir ve gerisini sonra if ile seebiliriz: write("Bardan yars "); if (iyimser) { writeln("dolu"); } else { writeln("bo"); } if ile baka bir yol, btn mesaj farkl olarak yazdrmaktr: if (iyimser) { writeln("Bardan yars dolu"); } else { writeln("Bardan yars bo"); } Hesap Tavla puann mars olup olmama durumuna gre ?: ile 2 veya 1 arttrmak: tavlaPuan += marsOldu ? 2 : 1; if ile puan duruma gre 2 veya 1 arttrmak: if (marsOldu) { tavlaPuan += 2; } else { tavlaPuan += 1; } if ile baka bir yol; batan bir arttrmak ve mars ise bir kere daha arttrmak olabilir: ++tavlaPuan; if (marsOldu) { ++tavlaPuan; } Bu rneklerden grld gibi kod ?: ileci ile ok daha ksa olmaktadr.

26.1

Seilecek ifadeler ayn trden olmaldr


?: ilecinin ifade deeri, denetlenen koula bal olarak ya doru ifadesinin deeridir, ya da doru olmama ifadesinin. lecin kullanmnn D kurallarna gre geerli olarak kabul edilebilmesi iin bu iki ifadenin trlerinin ayn olmas gerekir. rnein yukardaki gnAdedi deikeni ilklenirken seilen deer, artkYl kouluna bal olarak ya 366'dr ya da 365. Bu deerlerin ikisinin de tr int olduu iin ifade geerlidir. Geerli olmayan bir rnek olarak Ddili Forum'un mesajlarndan birisine bakalm. Bal olan kullanc says 1 olduunda mesaj "tek" kelimesi ile yazlr: "u an tek kullanc bal" Kullanc says 1'den fazla olduunda ise rakamla gsterilir. rnein: "u an 3 kullanc bal"

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

103

l le ?:

O mesaj kodlarken "tek" ve 3 arasndaki seimi ?: ileciyle kolayca gerekletirebileceimizi dnebiliriz: writeln("u an ", (adet == 1) ? "tek" : adet, " kullanc bal");

// derleme HATASI

Ne yazk ki o kod yasal deildir, nk koul sonucunda seilecek iki ifadenin trleri farkldr: "tek" bir string olduu halde, adet bir int 'tir. zm olarak adet 'i de string 'e dntrebiliriz. std.conv modlnde bulunan to!string ilevi, kendisine verilen ifadenin string karln retir: import std.conv; // ... writeln("u an ", (adet == 1) ? "tek" : to!string(adet), " kullanc bal"); Artk ?: ilecinin her iki ifadesi de ayn trden (string ) olduu iin kod hatasz olarak derlenir ve alr.

26.2

Problem
Program kullancdan sfr dnda tek bir tamsay deer alsn; bu deerin sfrdan kk olmas zararda olmak, sfrdan byk olmas da kazanl olmak anlamna gelsin. Program verilen deere gre sonu "zarardasnz" veya "kazanlsnz" ile biten bir mesaj yazdrsn. rnein "100 lira zarardasnz" veya "70 lira kazanlsnz". Bu dersle ilgili olabilmesi iin if koulunu kullanmak yasak! :o) ... zm

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

104

Hazr Deerler

27

Hazr Deerler
Programlar ilerini deikenlerin ve nesnelerin deerlerini kullanarak yaparlar. Deikenleri ve nesneleri ilelerle ve ilevlerle kullanarak yeni deerler retirler ve yeni nesneler olutururlar. Baz deerlerin ise hesaplanmalar gerekmez; onlar kaynak kod iine dorudan hazr olarak yazlrlar. rnein u kod parasndaki ilemler srasnda kullanlan 0.75 kesirli say deeri ve "Toplam fiyat: " sabit dizgisi kod iine programc tarafndan hazr olarak yazlmtr: renciFiyat = biletFiyat * 0.75; fiyat += renciSays * renciFiyat; writeln("Toplam fiyat: ", fiyat); Bu tr deerlere hazr deer denir. imdiye kadar grdmz programlarda zaten ok sayda hazr deer kullandk. Bu derste hazr deerlerin btn eitlerini ve sz dizimlerini greceiz.

27.1

Tamsaylar
Tamsaylar drt deiik say sisteminde yazabilirsiniz: Gnlk hayatmzda kullandmz onlu say sisteminde, baz durumlarda daha uygun olan onaltl veya ikili say sistemlerinde, ve nadir olarak sekizli say sisteminde. Btn tamsay deerlerinin rakamlarnn aralarna, istediiniz sayda, istediiniz yerlerine, ve herhangi amala; rnein okumay kolaylatrmak iin _ karakterleri yerletirebilirsiniz. rnein, rakamlar er er ayrmak iin: 1_234_567. Bu karakterler tamamen programcnn isteine baldr ve derleyici tarafndan gzard edilirler. Ondalk say sisteminde: Gnlk hayatmzda kullandmz gibi, onlu rakamlarla yazlr. rnek: 12 . Ondalk deerlerin ilk rakam 0 olamaz. Bunun nedeni, 0 ile balayan hazr deerlerin ou baka dilde sekizli say sistemine ayrlm olmasdr. Bu konudaki karklklardan doabilecek olan hatalar nlemek iin D'de tamsay hazr deerleri 0 ile balayamaz. Onaltl say sisteminde: 0x veya 0X ile balayarak ve onaltl say sisteminin rakamlar olan "0123456789abcdef" ve "ABCDEF" ile yazlr. rnek: 0x12ab00fe . Sekizli say sisteminde: std.conv modlndeki octal ile ve sekizli say sisteminin rakamlar olan "01234567" ile yazlr. rnek: octal!576 . kili say sisteminde: 0b veya 0B ile balayarak ve ikili say sisteminin rakamlar olan 0 ve 1 ile yazlr. rnek: 0b01100011 .

27.1.1

Tamsay deerlerin trleri


Her deikenin olduu gibi, D'de hazr deerlerin de trleri vardr. Hazr deerlerin trleri int , double , vs. gibi aka yazlmaz; derleyici, tr hazr deerin yazmndan anlar. Hazr deerlerin trlerinin aslnda programc asndan ok byk bir nemi yoktur. Bazen tr, hazr deerin iinde kullanld ifadeye uymayabilir ve derleyici uyar verir. yle durumlarda aadaki bilgilerden yararlanarak hazr deerin trn aka belirtmeniz gerekebilir. Tamsay hazr deerlerin ncelikle int trnde olduklar varsaylr. Eer deer bir int 'e smayacak kadar bykse, derleyici u ekilde karar verir:

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

105

Hazr Deerler

int 'e smayacak kadar byk olan deer onlu sistemde yazlmsa, long 'dur int 'e smayacak kadar byk olan deer baka bir say sisteminde yazlmsa, ncelikle uint 'tir, ona da smyorsa long 'dur, ona da smyorsa ulong 'dur Bunu grmek iin daha nce rendiimiz typeof 'tan ve stringof 'tan yararlanan u program kullanabiliriz: import std.stdio; void main() { writeln("\n--- bunlar onlu olarak yazldlar ---"); // int'e sd iin int writeln( 2_147_483_647, "\t\t", typeof(2_147_483_647).stringof); // int'e smad ve onlu olarak yazld iin long writeln( 2_147_483_648, "\t\t", typeof(2_147_483_648).stringof); writeln("\n--- bunlar onlu olarak yazlMAdlar ---"); // int'e sd iin int writeln( 0x7FFF_FFFF, "\t\t", typeof(0x7FFF_FFFF).stringof); // int'e smad ve onlu olarak yazlmad iin uint writeln( 0x8000_0000, "\t\t", typeof(0x8000_0000).stringof); // uint'e smad ve onlu olarak yazlmad iin long writeln( 0x1_0000_0000, "\t\t", typeof(0x1_0000_0000).stringof); // long'a smad ve onlu olarak yazlmad iin ulong writeln( 0x8000_0000_0000_0000, "\t\t", typeof(0x8000_0000_0000_0000).stringof);

--- bunlar onlu olarak yazldlar --2147483647 int 2147483648 long --- bunlar onlu olarak yazlMAdlar --2147483647 int 2147483648 uint 4294967296 long 9223372036854775808 ulong

27.1.2

L son eki
Deerin byklnden bamsz olarak, eer deerin sonunda bir L karakteri varsa, tr long 'dur. rnek: 10L

27.1.3

U son eki
Deerin byklnden bamsz olarak, eer deerin sonunda bir U karakteri varsa, iaretsiz bir trdr. rnek: 10U'nun tr uint 'tir. Kk harf u da kullanlabilir. L ve U karakterleri birlikte ve sralarnn nemi olmadan da kullanlabilirler. rnein 7UL'nin ve 8LU'nun ikisinin de trleri ulong 'dur.

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

106

Hazr Deerler

27.2

Kesirli saylar
Kesirli saylar onlu say sisteminde veya onaltl say sisteminde yazlabilirler. rnein ondalk olarak 1.234 veya onaltl olarak 0x9a.bc Ondalk say sisteminde: Saynn yanna, e veya E belirtecinden sonra "arp 10 zeri" anlamna gelen bir arpan eklenebilir. rnein 3.4e5, "3.4 arp 10 zeri 5" anlamndadr. Bu belirteten sonra bir + karakteri de yazlabilir ama onun bir etkisi yoktur. rnein, 5.6e2 ile 5.6e+2 ayn anlamdadr. Belirteten sonra gelen - karakterinin etkisi vardr ve "10 zeri o kadar deere blnecek" anlamna gelir. rnein 7.8e-3, "7.8 bl 10 zeri 3" anlamndadr. Onaltl say sisteminde: Say 0x veya 0X ile balar; noktadan nceki ve sonraki blmleri onaltl say sisteminin rakamlaryla yazlr. e ve E de onaltl sistemde geerli rakamlar olduklarndan, s belirteci olarak baka bir harf kullanlr: p (veya P ). Baka bir fark, bu belirteten sonra gelen deerin "10 zeri" deil, "2 zeri" anlamna gelmesidir. rnein 0xabc.defP4'n sonundaki belirte, "2 zeri 4 ile arplacak" anlamna gelir. Kesirli say deerler hemen hemen her zaman iin bir nokta ierirler, ama belirte varsa noktaya gerek yoktur. rnein 2e3, 2000 deerinde bir kesirli saydr. Noktadan nceki deer 0 ise, yazlmayabilir. rnein .25, "eyrek" anlamnda bir kesirli say deeridir. Gzard edilen _ karakterlerini kesirli saylarla da kullanabilirsiniz: 1_000.5

27.2.1

Kesirli say deerlerin trleri


Kesirli deerler zellikle belirtilmemise double trndedir. Sonlarna f veya F eklenirse, float ; L eklenirse real olurlar. rnein 1.2 double 'dr, 3.4f float 'tur, ve 5.6L real 'dir. Deerin sonuna eklenen i , o deerin trn bir sanal tr haline getirir. rnein 7.8i idouble 'dr, 9.1fi ifloat 'tur, ve 11.12Li ireal 'dir.

27.3

Karakterler
Karakter trndeki hazr deerler her zaman iin tek trnaklar arasnda yazlrlar. rnein 'a' , '\n' , '\x21' . Karakterin kendisi olarak: Tek trnaklar arasna karakterin kendisi klavyeden yazlabilir veya baka bir metinden kopyalanabilir: 'a', '', vs. Karakter belirteci olarak: Ters bl iaretinden sonra bir karakter belirteci kullanlabilir. rnein ters bl karakterinin kendisi '\\' eklinde yazlr. Karakter belirteleri unlardr:

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

107

Hazr Deerler

Yazm \' \" \? \\ \a \b \f \n \r \t \v

Anlam tek trnak ift trnak soru iareti ters bl uyar karakteri (baz ortamlarda zil sesi) silme karakteri sayfa sonu satr sonu ayn satrn bana gtrr bir sonraki sekme admna gtrr bir sonraki dey sekme admna gtrr

ASCII karakter kodu olarak: Deerleri 256'dan kk olan karakterleri, kodunu vererek yazabilirsiniz. Yukarda tamsaylar balnda anlatlanlara uygun olarak, kodu \x ile balayan 2 haneli onaltl say olarak veya 3 haneye kadar sekizli say olarak yazabilirsiniz. rnein '\x21' ve '\41' nlem iareti karakterinin iki farkl yazmdr. Unicode karakter kodu olarak: u karakterinden sonra 4 onaltl rakam olarak yazlrsa tr wchar olur; U karakterinden sonra 8 onaltl rakam olarak yazlrsa tr dchar olur. rnein '\u011e' ve '\U0000011e' karakterinin srasyla wchar ve dchar trnde olan deeridir. simli karakter olarak: simleri olan karakterleri D'nin isimli karakterler listesinde gsterilen isimleriyle ve '\&karakter_ismi;' sz dizimiyle yazabilirsiniz. rnein '\&euro;' , '\&hearts;' , ve '\&copy;' karakteridir.

27.4

Dizgiler
Hazr dizgiler sabit karakterlerin bileimlerinden oluurlar ve ok sayda farkl sz dizimiyle yazlabilirler.

27.4.1

ift trnaklar arasnda yazlan dizgiler


Dizgilerin baka dillerde de bulunan en yaygn yazm, ift trnaklar arasnda yazlmalardr: rnein "merhaba"... Bu ekilde yazldnda, iindeki karakterler yukardaki karakter yazmlarna uygun olarak yazlrlar. rnein, gstermek amacyla yukardaki karakter sabitlerinden bazlarn ieren "A4 ka\u011ft: 3\&frac12;TL" dizgisi, "A4 kat: 3TL"nin edeeridir.

27.4.2

Grnd gibi kan dizgiler


Ters trnak iaretleri arasnda yazlan dizgilerin iindeki karakterler, yukarda karakter sabitleriyle ilgili olarak anlatlan kurallar iletilmeden, grldkleri anlama gelirler. rnein `c:\nurten` eklinde yazlan dizgi, Windows iletim sisteminde bir klasr ismi olabilir. Oysa ift trnaklar arasnda yazlm olsa, dizginin iinde geen '\n', satr sonu anlamna gelirdi: writeln(`c:\nurten`); writeln("c:\nurten");

c:\nurten c: urten

grnd gibi satr sonu olarak anlalan karakter

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

108

Hazr Deerler

Grnd gibi kan dizgilerin dier bir yazm ekli, ift trnaklar kullanmak, ama ncesine bir r belirteci eklemektir: r"c:\nurten" de grnd gibi anlalr.
27.4.3

Onaltl sistemde yazlan dizgiler


Karakterlerinin kodlar onaltl say sisteminde yazlacak olan dizgilerin her karakterinin bana \x yazmak yerine, bana x belirteci gelen dizgiler kullanlabilir. Hatta bu dizgilerin iine okumay kolaylatrmak amacyla boluklar da yazlabilir. Bu boluklar derleyici tarafndan gzard edilirler. rnein "\x44\x64\x69\x6c\x69" yerine x"44 64 69 6c 69" yazlabilir.

27.4.4

Ayral dizgiler
ift trnaklarn hemen iine gelmek kouluyla, dizginin paras olmayan ayralar yerletirebilirsiniz. Ayral dizgilerde ift trnaklardan nce q karakteri gelir: q"{merhaba}" dizgisinin deeri "merhaba"dr. Hemen sonras satr sonuna gelmek kouluyla, ayralar szckler olarak da belirleyebilirsiniz: writeln(q"AYRA birinci satr ikinci satr AYRA"); Bu rnekteki AYRA szc dizginin paras deildir: birinci satr ikinci satr

27.4.5

D kodu dizgileri
Yine banda q karakteri olmak zere, { ve } karakterleri arasnda yasal D kodu ieren dizgiler yazlabilir: auto dizgi = q{int say = 42; ++say;}; writeln(dizgi);

int say = 42; ++say;

27.4.6

Dizgi deerlerin trleri


Dizgiler zellikle belirtilmediinde immutable(char)[] trndedirler. Sonlarna eklenen c , w , ve d karakterleri dizginin trn srasyla immutable(char)[] , immutable(wchar)[] , ve immutable(dchar)[] olarak belirler. rnein "merhaba"d dizgisinin karakterleri immutable(dchar) trndedirler. Bu trn srasyla string , wstring , ve dstring olan takma isimlerini Dizgiler dersinde renmitiniz.

27.5

Hazr deerler derleme zamannda hesaplanrlar


Hazr deerleri ilem halinde de yazabilirsiniz. rnein Ocak ayndaki toplam saniye deerini 2678400 veya 2_678_400 olarak yazmak yerine, deerin doruluundan emin olmamza yarayan 60 * 60 * 24 * 31 eklinde de yazabilirsiniz. inde arpma ileleri olsa da, o

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

109

Hazr Deerler

ilem programnzn alma hzn drmez; hazr deer, derleme zamannda yine de 2678400 olarak hesaplanr ve sanki siz yle yazmsnz gibi derlenir. Ayn durum dizgi hazr deerleri iin de geerlidir. rnein "merhaba " ~ "dnya" yazmndaki dizgi birletirme ilemi alma zamannda deil, derleme zamannda yapld iin programnz sanki "merhaba dnya" yazlm gibi derlenir ve alr.

27.6

Problemler
1. Bir bankaclk oyun programnda oyuncularn balangta ellerinde bulunan para 10 milyar olsun. Bu amala programa yazlan u satr yznden program derlenemiyor: int kasadakiPara = 10_000_000_000; O satrn derlenmesini salayn. 2. Bir tamsaynn deerini sonsuz bir dngde arttran ve bunu ekrana yazdran bir program yazn. Dngnn her tekrarnda saynn deeri ekrana yazdrld halde, yazlan deer hep ayn satrda ksn: Say: 25774 hep ayn satrn stne yazlsn

Bunun iin yukardaki zel karakter belirtelerinden birisi iinize yarayacak; '\n' olmad ak herhalde... :o) ... zmler

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

110

kt Dzeni

28

kt Dzeni
Dier derslerden farkl olarak, bu blm D dilinin i olanaklaryla ilgili deil. Burada anlatlanlar tamamen ktphane olanaklar olarak gerekletirilmilerdir ve Phobos'un kt dzeni iin kulland std.format modlnn olanaklardr. Hazr deerler dersinde anlatlanlarla yakndan ilgili olduu iin bu konuyu burada anlatmaya karar verdim. D'nin giri ve k iin kulland dzen belirtelerinin temelde C dilindekiler gibi olduunu ama baz farklar bulunduunu greceksiniz. Bir n hatrlatma olarak :o), btn dzen dizgisi karakterleri: Ayar Karakterleri (birden fazla kullanlabilir) sola dayal + iaretli # dier ekilde 0 solda 0'l boluk solda boluklu Dzen Karakterleri s belirtesiz gibi b ikili d onlu o sekizli x,X onaltl e,E on zerili f,F virgll g,G e veya f gibi a,A onaltl kesirli imdiye kadar kt iin writeln gibi ilevleri, gerektiinde birden fazla parametreyle kullanmtk. Bu parametreler otomatik olarak karakter edeerlerine dntrlerek srayla ka gnderiliyorlard. ou zaman bu yeterli olmaz. ktnn belirli bir dzene uymas gerekebilir. rnein bir faturann maddelerini yazdran u koda bakalm: faturaMaddeleri ~= 1.23; faturaMaddeleri ~= 45.6; for (int i = 0; i != faturaMaddeleri.length; ++i) { writeln("Madde ", i + 1, ": ", faturaMaddeleri[i]); } kts: Madde 1: 1.23 Madde 2: 45.6 Oysa faturadaki deerlerin belirli bir dzende, rnein her zaman iin virglden sonra iki haneyle ve geni bir alanda saa dayal olarak yazlmalar okuma asndan nemli olabilir. (Not: Ben bu derste gnlk kullanma uygun olarak "virgl" diyeceim; ama kesirli saylarda virgl yerine nokta karakteri kullanlr.): Madde 1: Madde 2: 1.23 45.60

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

111

kt Dzeni

te kt dzeni, byle konularda yarar salar. imdiye kadar kullandmz ve isminde f harfi geen kt ilevlerinin ilk parametresi bir dzen dizgisi olarak verilebilir. Bu ilevlerin isimlerindeki f harfi, "dzen, biim" anlamna gelen "format"n ba harfinden gelmitir. rnein yukardaki kty veren bir dzen dizgisi writefln ile yle yazlabilir: writefln("Madde %d:%9.02f", i + 1, faturaMaddeleri[i]); Dzen dizgisi, normal karakterlerden ve zel dzen belirtelerinden oluur. Her dzen belirteci % karakteriyle balar ve bir dzen karakteri ile biter. Yukardaki dizgide iki tane dzen belirteci var: %d ve %9.02f . Her belirte, dzen dizgisinden sonra verilen parametrelerle sra ile eleir. rnein %d ile i + 1 , ve %9.02f ile faturaMaddeleri[i] ... Her belirte, eletii parametrenin kt dzenini belirler. Dzen dizgisi iinde bulunan ve belirtelere ait olmayan karakterler, olduklar gibi yazdrlrlar. Yukardaki dizgi iindeki normal karakterleri krmz renkle yle gsterebiliriz: "Madde %d:%9.02f" . Dzen belirteci be blmden oluur (Not: okumay kolaylatrmak iin aralarnda boluk kullanyorum; bu blmler aslnda bitiik olarak yazlrlar): % ayar_karakterleri genilik duyarlk dzen_karakteri

Bataki % karakterinin ve sondaki dzen karakterinin yazlmas arttr, dierleri ise istee baldr. % karakterinin byle zel bir anlam olduu iin, ktda % karakterinin kendisi yazdrlmak istendiinde %% eklinde ift olarak yazlr.

28.1

dzen_karakteri
b : Tamsay, ikili say dzeninde yazdrlr. o : Tamsay, sekizli say dzeninde yazdrlr. x ve X : Tamsay, onaltl say dzeninde yazdrlr; x iin kk harfler, X iin byk harfler kullanlr. d : Tamsay, onlu sistemde yazdrlr; eer iaretsiz bir trse ve deeri sfrdan kkse, bana eksi iareti gelir; aksi durumda iaretsiz bir tr gibi yazdrlr. int deer = 12; writefln("kili : writefln("Sekizli : writefln("Onaltl: writefln("Ondalk : %b", %o", %x", %d", deer); deer); deer); deer);

kili : Sekizli : Onaltl: Ondalk :

1100 14 c 12

e : Kesirli say, aadaki blmlerden oluacak ekilde yazdrlr. virglden nce tek hane

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

112

kt Dzeni

duyarlk 0 deilse virgl virglden sonra duyarlk adet hane (varsaylan duyarlk 6'dr) e karakteri en az iki hane olarak "10 zeri" anlamnda s deeri

E : e ile ayn dzende, ama ktda E harfiyle f ve F : Kesirli say, onlu sistemde yazdrlr; virglden nce en az bir hane bulunur; varsaylan duyarlk 6'dr. g : Kesirli say, eer s deeri -5 ile duyarlk arasnda olacaksa, f gibi; deilse e gibi yazdrlr; duyarlk virglden sonrasn deil, belirgin hane saysn belirtir; virglden sonra belirgin hane yoksa virgl de yazdrlmaz; virglden sonra en sadaki sfrlar yazdrlmazlar. G : g ile ayn dzende, ama E veya F kullanlm gibi yazdrlr a : Kesirli say, onaltl sistemde ve aadaki blmlerden oluacak ekilde yazdrlr: 0x karakterleri tek onaltl hane duyarlk 0 deilse virgl virglden sonra duyarlk adet hane, veya duyarlk belirtilmemise gerektii kadar hane

p karakteri ssn deerine gre - veya + karakteri en az bir hane olarak "2 zeri" anlamnda s deeri; (0 deerinin s deeri 0'dr) A : a ile ayn dzende, ama ktda 0X ve P karakterleriyle double deer = 123.456789; writefln("e writefln("f writefln("g writefln("a ile: ile: ile: ile: %e", %f", %g", %a", deer); deer); deer); deer);

e f g a

ile: ile: ile: ile:

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

s : Parametrenin deeri; dzen dizgisi kullanlmad zamandaki gibi, trne uygun olan ekilde yazdrlr: bool trler true veya false olarak tamsaylar %d gibi kesirli saylar %g gibi dizgiler UTF-8 kodlamasyla; duyarlk, en fazla ka bayt kullanlacan belirler (UTF-8 kodlamasnda karakter saysyla bayt saysnn eit olmayabileceklerini hatrlayn; rnein "a" dizgisi toplam 3 bayt uzunluunda 2 karakterden oluur) yap ve snf nesneleri, trn toString() ye ilevinin rettii dizgi olarak; duyarlk, en fazla ka bayt kullanlacan belirler diziler, elemanlar yan yana sralanarak bool b = true; int tamsay = 365; double kesirli = 9.87; string dizgi = "dzenli"; auto nesne = File("deneme_dosyasi", "r");

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

113

kt Dzeni

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 dzenli File [2, 4, 6, 8]

28.2

genilik
Deer iin ktda ayrlan alann geniliini belirler. Eer genilik olarak * kullanlmsa, genilik deeri bir sonraki parametrenin deeri olarak alnr. Eer eksi bir sayysa, - ayar karakteri kullanlm gibi alr. int deer = 100; writefln("On karakterlik alanda :%10s", deer); writefln("Be karakterlik alanda:%5s", deer);

On karakterlik alanda : Be karakterlik alanda:

100 100

28.3

duyarlk
Eer belirtilmise, nokta karakterinden sonra yazlr. Kesirli say trnden olan deerlerin ktda kullanlacak olan duyarln belirler. Eer duyarlk olarak * kullanlmsa, duyarlk deeri bir sonraki parametrenin deeri olarak alnr (o deer int olmak zorundadr). Duyarlk eksi bir sayysa gzard 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

114

kt Dzeni

28.4

ayar_karakterleri
Birden fazla ayar karakteri kullanabilirsiniz. - : parametre deeri; kendisine ayrlan alanda sola dayal olarak yazdrlr; bu ayar, 0 ayar karakterini geersiz klar int deer = 123; writefln("normalde saa dayal:|%10d|", deer); writefln("sola dayal :|%-10d|", deer);

normalde saa dayal:| sola dayal :|123

123| |

+ : deer art ise bana + karakteri yazdrlr; bu ayar, boluk ayar karakterini geersiz klar writefln("eksi deerde etkili deil: %+d", -50); writefln("art deer, + ile : %+d", 50); writefln("art deer, + olmadan : %d", 50);

eksi deerde etkili deil: -50 art deer, + ile : +50 art deer, + olmadan : 50 # : kullanlan dzen_karakteri'ne bal olarak, deeri baka ekilde yazdrr o iin: sekizli saynn ilk karakteri her zaman iin 0 olarak yazdrlr x ve X iin: say sfr deilse, bana 0x veya 0X gelir kesirli saylarda: virglden sonra hane olmasa da virgl yazdrlr g ve G iin: virglden sonra sadaki sfrlar atlmaz writefln("Sekizli sfrla balar writefln("Onaltlnn bana 0x gelir : %#o", 1000); : %#x", 1000);

writefln("Gerekmese de virgll olur : %#g", 1f); writefln("Sadaki sfrlar da yazdrlr: %#g", 1.2);

Sekizli sfrla balar Onaltlnn bana 0x gelir Gerekmese de virgll olur

: 01750 : 0x3e8 : 1.00000

Sadaki sfrlar da yazdrlr: 1.20000 0 : saylarda (deer nan veya infinity deilse), sol tarafa deer iin ayrlan alan dolacak kadar 0 yazdrlr; duyarlk da belirtilmise bu ayar etkisizdir writefln("Sekiz genilikte: %08d", 42);

Sekiz genilikte: 00000042 boluk karakteri: deer art ise, eksi deerlerle alt alta dzgn dursun diye bana tek bir boluk karakteri yazdrlr

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

115

kt Dzeni

writefln("Eksi deerde etkisi yok: % d", -34); writefln("Art deer, boluklu : % d", 56); writefln("Art deer, boluksuz : %d", 56);

Eksi deerde etkisi yok: -34 Art deer, boluklu : 56 Art deer, boluksuz : 56

28.5

Problemler
1. Girilen tamsayy onaltl dzende yazdran bir program yazn. 2. Girilen kesirli sayy bir yzde deeri olarak ve virglden sonra 2 haneyle yazdran bir program yazn. rnein 1.2345 girildiinde ekrana yalnzca %1.23 yazsn. ... zmler

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

116

Giri Dzeni

29

Giri Dzeni
kt dzeni dersinde anlatlanlara benzer ekilde, giriten gelen verilerin dzeni de belirtilebilir. Bu dzen; hem okunmas istenen bilgiyi, hem de gzard edilmesi istenen bilgiyi belirtebilir. Dzen dizgisi olarak imdiye kadar yaptmz gibi " %s" kullanldnda, okunmakta olan deikenin trne en uygun olan dzende okunur. rnein aadaki readf arlarnn ikisi de ayn ekilde alr ve ikisi de giriten bir kesirli say okur: double say; readf(" %s", &say); readf(" %f", &say); Giri iin kullanlan dzen dizgisi, C'deki scanf ilevinin dzen dizgisine ok benzer. Dzen dizgisi iinde tr bilgi bulunabilir: boluk karakteri: giriteki sfr veya daha fazla boluk karakteri anlamna gelir ve onlarn okunup gzard edilmelerini salar herhangi bir karakter: girite aynen bulunmas beklenen bir karakteri ifade eder ve onun okunup gzard edilmesini salar dzen belirteci: nceki derstekilere benzer ekilde % karakteriyle balar ve giriten gelen karakterlerin hangi trde okunacaklarn belirler O bilgiler sayesinde, giriten gelen veri ierisinden bizim iin nemli olanlarn seip kartmak ve geri kalann gzard etmek son derece kolaydr. Ayrntya girmeden nce, bu tr bilgiyi kullanan bir rnee bakalm. Girite tek satr halinde yle bir bilgi bulunsun: numara:123 not:90 O satr ierisinden bizim iin nemli olan iki bilgi, rencinin numaras ve notu olsun; yani giriteki numara: ve not: gibi karakterlerin bizim iin bir nemi bulunmasn. te o satr iinden rencinin numarasn ve notunu seen ve geri kalann gzard eden bir dzen dizgisi yle yazlabilir: int numara; int not; readf("numara:%s not:%s", &numara, &not); "numara:%s not:%s" dzen dizgisi iinde maviyle gsterdiim btn karakterler girite aynen bulunmaldrlar; onlar readf tarafndan giriten okunup gzard edilirler. O dzen dizgisinde kullanlan tek boluk karakteri, girite o noktada bulunan btn boluk karakterlerinin gzard edilmelerine neden olur. % karakterinin zel anlam nedeniyle, girite % karakterinin kendisinin gzard edilmesi istendiinde %% eklinde ift olarak yazlr.

29.1

Dzen karakterleri
d : onlu sistemde tamsay o : sekizli sistemde tamsay

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

117

Giri Dzeni

x : onaltl sistemde tamsay f : kesirli say s : tre uygun olan dzende c : tek bir karakter; bu belirte boluklar da okur (gzard edilmelerini nler) rnein girite 3 tane "23" bulunduunu varsayarsak; deer, dzen belirtecine gre farkl olarak okunur: int say_d; int say_o; int say_x; readf(" %d %o %x", &say_d, &say_o, &say_x); writeln("onlu olarak okununca : ", say_d); writeln("sekizli olarak okununca : ", say_o); writeln("onaltl olarak okununca: ", say_x); Giriine 3 defa "23" girdiimiz halde, deerler farkl okunur: onlu olarak okununca : 23 sekizli olarak okununca : 19 onaltl olarak okununca: 35 Not: ok ksaca; "23", sekizli dzende 2x8+3=19 deerinde, ve onaltl dzende 2x16+3=35 deerindedir.

29.2

Problem
Giriten yl.ay.gn dzeninde bir tarih bilgisi gelsin. Ekrana kanc ay olduunu yazdrn. rnein 2009.09.30 geldiinde 9 yazlsn. ... zm

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

118

do-while Dngs

30

do-while Dngs
for dngs dersinde while 'n ileyi admlarn da grmtk: hazrlk koul denetimi asl ilemler ilerletilmesi koul denetimi asl ilemler ilerletilmesi ... do-while 'n while 'dan fark, koul denetiminin sonda olmas ve bu sayede ilemlerin en az bir kere iletilmeleridir: hazrlk (while'dan daha az durumda gerekir) asl ilemler ilerletilmesi koul denetimi asl ilemler ilerletilmesi koul denetimi ... rnein, tuttuu saynn tahmin edilmesini bekleyen bir programda do-while dngs daha doal gelebilir: import std.stdio; import std.random; void main() { int say = uniform(1, 101); writeln("1'den 100'e kadar bir say tuttum!..."); int tahmin; do {

write("Tahmininiz nedir? "); readf(" %s", &tahmin); if (say < tahmin) { write("tuttuum say daha kk; "); } else if (say > tahmin) { write("tuttuum say daha byk; "); }

} while (tahmin != say); } writeln("Doru!");

Daha doal gelmeyebilir de... :o) Seim sizin...

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

119

do-while Dngs

uniform , std.random modlnde bulunan bir ilevdir. Belirtilen aralkta eit dalml rasgele saylar retir. Yukardaki kullanmnda; aral belirleyen ikinci deer, kacak saylar arasnda deildir. Dier kullanmlarn renmek iin std.random modlnn belgesine bakabilirsiniz.

30.1

Problem
Ayn oyunu bilgisayara oynatn; tuttuunuz sayy en fazla 7 tahminde bulacaktr. ... zm

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

120

Eleme Tablolar

31

Eleme Tablolar
Eleme tablolar, st dzey dillerin hepsinde bulunan veri yaplardr. Onlar, program iine gmlen minik veri tabanlar olarak dnlebilirsiniz. Programlarda ok kullanlan ve ok hzl veri yaplardr. Dizileri, "elemanlar yan yana duran topluluk" olarak tanmlam ve elemanlarna indeksle eriildiini grmtk. rnein haftann gnlerinin isimlerini tutan bir dizi yle tanmlanabilir: string[] gnsimleri = [ "Pazartesi", "Sal", "aramba", "Perembe", "Cuma", "Cumartesi", "Pazar" ]; ve, belirli bir gnn ismi o diziyi kullanarak yle yazdrlabilir: writeln(gnsimleri[1]); // "Sal" yazar

Diziler elemanlara numara ile (indeksle) erimeye ok uygundurlar, ama bunun tersini yapamazlar. rnein elimizde "Sal" dizgisi bulunduunda onun haftann 1 numaral gn olduunu syleyemezler (indekslerin 0'la baladklarn hatrlayn): writeln(gnSralar["Sal"]); // derleme HATASI

O satrda "1" yazmay umuyoruz ama derleme hatas oluuyor; nk dizilerde indeks olarak yalnzca tamsay trler kullanlabilir; "Sal" gibi dizgiler indeks olamazlar. Eleme tablolarnn kullanll ite bu gibi durumlarda ortaya kar. Eleme tablolar, elemanlara yalnzca numara ile deil, herhangi bir trle eriilen veri yaplardr. Grevleri; herhangi bir indeks trndeki bir deeri, herhangi baka bir trdeki deere elemektir.

31.1

ok hzl ama srasz


Eleme tablolar arka planda hash table veri yapsn kullandklar iin algoritma karmakl asndan dizilerden geri kalmazlar: son derece hzl topluluklardr. Bunun anlam, ilerindeki eleman saysndan bamsz olarak, hemen her zaman iin sabit zamanda eriim salamalardr. Bu kadar hzl almalarnn bedeli, ilerindeki elemanlarn sralar konusunda bir ey bilinemiyor olmasdr. Elemanlarn ne dizilerdeki gibi yan yana olduklarn, ne de rnein kkten bye doru sralandklarn syleyebiliriz.

31.2

Tanmlama
Eleme tablosu tanm, dizi tanmna ok benzer. Tek fark, keli parantezler iine dizinin uzunluu yerine, dizinin indeks trnn gelmesidir. Sz dizimi, eleman_tr[indeks_tr] eklindedir. rnein tr string olan gn isminden, tr int olan gn sra numarasna eleme yapan bir eleme tablosu yle tanmlanr: int[string] gnSralar; Eleme tablolarnn en kullanl taraflarndan birisi; indeks ve eleman tr olarak, daha sonra reneceimiz yap ve snf trleri de dahil olmak zere, her trn kullanlabilmesidir.

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

121

Eleme Tablolar

Dinamik dizilerinki gibi, eleme tablolarnn boylar da tanmlandklar zaman belirlenmez; tablo, otomatik olarak byr.

31.3

Atama
Eleman indeks deeri ile atamak; eleme bilgisinin kurulmas ve tablonun otomatik olarak bymesi iin yeterlidir: gnSralar["Pazartesi"] = 0; gnSralar["Sal"] = 1;

31.4

lkleme
Gn sralar kavramnda olduu gibi, eleme bilgisi bazen tablo kurulduu srada bilinir. Elemeleri teker teker atayarak kurmak yerine, bu bilgiyi tabloyu tanmladmz zaman da verebiliriz. Tablo, dizi sz diziminde olduu gibi ilklenir; indeks deerleri ile eleman deerleri arasna : karakteri yazlr: int[string] gnSralar = [ "Pazartesi":0, "Sal":1, "aramba":2, "Perembe":3, "Cuma":4, "Cumartesi":5, "Pazar":6 ]; writeln(gnSralar["Sal"]); // "1" yazar

31.5

Tablodan eleman kartma


Elemanlar, buradaki kullanmnda "kart, at" anlamna gelen .remove ile kartlrlar: gnSralar.remove("Sal"); writeln(gnSralar["Sal"]);

// alma zaman HATASI

Son satr, tabloda bulunmayan bir elemana eritii iin alma zamannda bir hata atlmasna neden olur.

31.6

Eleman sorgulama
Tabloda bulunmayan bir elemana erimek bir hata atlmasna neden olacandan; sorgulamak iin in ileci kullanlr. Bu kullanm, "iinde var m?" sorusunu yantlar: if ("mor" in renkKodlar) { // evet, renkKodlar'nda "mor" indeksli eleman varm } else { // hayr, yokmu... }

31.7

Nitelikler
.length tablodaki eleman says .keys tabloda bulunan btn indeksler, dinamik dizi olarak .values tabloda bulunan btn elemanlar, dinamik dizi olarak .rehash ancak gerektii durumda, tablonun daha etkin almasn salayabilir; tablo eriimini hzlandrmak iin, rnein ok sayda eleman giri ileminin sonunda, tablonun asl kullanm balamadan nce bu nitelik arlabilir

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

122

Eleme Tablolar

.sizeof tablonun referansnn bykldr (tablonun kullanmyla dorudan bir ilgisi yoktur ve ou ortamda 8'dir)

31.8

rnek
Girilen rengin ngilizcesini veren bir program yle yazlabilir: import std.stdio; import std.string; void main() { string[string] renkler = [ "siyah" "beyaz" "krmz" "yeil" "mavi" ];

: : : : :

"black", "white", "red", "green", "blue",

writeln("Ben bu ", renkler.length, " rengin ngilizcelerini rendim: ", renkler.keys); write("Haydi sorun: "); string trkesi = chomp(readln()); if (trkesi in renkler) { writefln("ngilizcesi \"%s\"", renkler[trkesi]); } else { writeln("Onu bilmiyorum..."); }

31.9

Problemler
1. Kullanmakta olduunuz bir eleme tablosunu nasl btnyle boaltabilirsiniz? En az yntem dnlebilir: elemanlar bir dng iinde teker teker tablodan kartmak bo bir eleme tablosu atamak bir ncekine benzer ekilde, tablonun .init niteliini atamak Not: Bu nitelii ilk defa duyuyorsunuz: her trn .init nitelii, o trn ilk deeri olarak kullanlan deerdir; rnein: say = int.init; // int iin 0 olur

2. Dizilerde olduu gibi, eleme tablolarnda da her indekse karlk tek bir eleman bulunabilir. Bu da baz durumlarda kstlaycdr. Her renci iin birden fazla not tutmak istiyor olalm. rnein "emre" iin 90, 85, 95, vs. notlarn barndrmak isteyelim. Bir eleme tablosu kullanmak, notlara notlar["emre"] eklinde rencinin ismiyle erime konusunda yardmc olur. Ancak, notlar tabloya aadaki ekilde yerletirmek ie yaramaz: int[string] notlar; notlar["emre"] = 90; notlar["emre"] = 85;

// Olmaz: ncekinin stne yazar

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

123

Eleme Tablolar

Ne yapabilirsiniz? Her renci iin birden fazla not tutabilen bir eleme tablosu tanmlayn. ... zmler

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

124

foreach Dngs

32

foreach Dngs
foreach , D'nin en kullanl deyimlerinden birisidir. "Her birisi" anlamna gelir. Belirli ilemleri bir topluluktaki (veya bir aralktaki) elemanlarn her birisi ile yapmay salar. Programclkta, topluluk elemanlarnn tmyle yaplan ilemlerle ok sk karlalr. Bu yzden for dngsnn bir dizinin btn elemanlarna erimek iin nasl kullanldn grmtk: for (int i = 0; i != dizi.length; ++i) { writeln(dizi[i]); } Bu i iin gereken admlar yle zetleyebiliriz: ismi geleneksel olarak i olan bir saya tanmlamak (aslnda biz nceki rneklerde hep saya dedik) dngy topluluun .length niteliine kadar ilerletmek i 'yi arttrmak elemana erimek Bu admlar ayr ayr elle yapmak yerine; ayn ii foreach ile ok daha temiz bir ekilde yle ifade ederiz: foreach (eleman; dizi) { writeln(eleman); } Bunun nemini vurgulamak iin, for dngsnn her eit topluluk iin ayn ekilde yazlmadn hatrlatrm. rnein bir eleme tablosunun btn elemanlarna erimek iin for dngsn tablo'nun .values nitelii zerinde kurmak gerekir: auto elemanlar = tablo.values; for (int i = 0; i != elemanlar.length; ++i) { writeln(elemanlar[i]); } Oysa ayn ilem foreach ile eleme tablolar iin de ayn ekilde yazlr: foreach (eleman; tablo) { writeln(eleman); }

32.1

Sz dizimi
foreach blmden oluur: foreach (isimler; topluluk_veya_aralk) { ilem_blou } topluluk_veya_aralk: dngnn iletilecei elemanlar belirler ilem_blou: her elemanla yaplacak ilemleri belirler isimler: eriilen elemann ve varsa baka nesnelerin isimlerini belirler; seilen isimler programcya bal olsa da, bunlarn anlam ve adedi topluluk eidine gre deiir

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

125

foreach Dngs

32.2

continue ve break ayn anlamdadr


Bu anahtar szcklerin ikisi de burada da ayn anlama gelirler: continue dngnn ilerletilmesini, break dngnn sonlandrlmasn bildirir.

32.3

Dizilerle kullanm
isimler blmne yazlan tek isim, dizinin elemann ifade eder: foreach (eleman; dizi) { writeln(eleman); } Eer iki isim yazlrsa; birincisi sayatr, ikincisi yine eleman ifade eder: foreach (saya, eleman; dizi) { writeln(saya, ": ", eleman); } O rnekteki saya isminin seimi de programcya baldr; rnein allm olan i de yazlabilir.

32.4

Dizgilerle kullanm ve std.range.stride


Dizilerle ayn ekilde kullanlr. Tek isim yazlrsa dizginin karakterini ifade eder, ift isim yazlrsa saya ve karakterdir: foreach (karakter; "merhaba") { writeln(karakter); } foreach (saya, karakter; "merhaba") { writeln(saya, ": ", karakter); } char ve wchar trlerinin Unicode karakterlerini barndrmaya genel olarak uygun olmadklarn hatrlayn. foreach bu trlerle kullanldnda karakterlere deil, kod birimlerine eriilir: foreach (saya, kod; "abcd") { writeln(saya, ": ", kod); } rnein 'yi oluturan kodlara ayr ayr eriilir: 0: 1: 2: 3: 4: 5: a b c d

UTF kodlamasndan bamsz olarak her tr dizginin foreach ile karakter karakter eriilmesini salayan olanak, std.range modlndeki stride 'dr. stride "adm" anlamna gelir ve karakterlerin kaar kaar atlanaca bilgisini de alr:

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

126

foreach Dngs

import std.range; // ... foreach (harf; stride("abcd", 1)) { writeln(harf); } stride kullanldnda UTF kodlarna deil Unicode karakterlerine eriilir: a b c d

32.5

Eleme tablolaryla kullanm


Tek isim yazlrsa eleman deerini, iki isim yazlrsa indeks ve eleman deerini ifade eder: foreach (eleman; tablo) { writeln(eleman); } foreach (indeks, eleman; tablo) { writeln(indeks, ": ", eleman); } Not: Eleme tablolarnda indeksin de herhangi bir trden olabileceini hatrlayn. O yzden son dngde saya yazmadm.

32.6

Say aralklaryla kullanm


Say aralklarn dilimler dersinde grmtk. foreach 'in topluluk_veya_aralk blmne bir say aral da yazlabilir: foreach (say; 10..15) { writeln(say); } Hatrlarsanz; yukardaki kullanmda 10 arala dahildir, 15 deildir.

32.7

Yaplarla ve snflarla kullanm


foreach , bu destei veren yap ve snf nesneleriyle de kullanlabilir. Nasl kullanld hakknda burada genel bir ey sylemek olanakszdr, nk tamamen o tr tarafndan belirlenir. Yaplar ve snflar foreach kullanm desteini ya opApply() isimli ye ilevleri ya da aralk (range) ye ilevleri araclyla verirler. Bu ilevleri daha sonraki derslerde anlatacam. foreach 'in belirli bir yap veya snf tr iin nasl ilediini ancak sz konusu yap veya snfn belgesinden renebiliriz.

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

127

foreach Dngs

32.8

Elemann kopyas, kendisi deil


foreach dngs; normalde elemann kendisine deil, bir kopyasna eriim salar. Topluluk elemanlarnn yanllkla deitirilmelerini nlemek amacyla byle tasarlandn dnebilirsiniz. Bir dizinin elemanlarnn her birisini iki katna kartmaya alan u koda bakalm: import std.stdio; void main() { double[] saylar = [ 1.2, 3.4, 5.6 ]; writefln("nce : %s", saylar); foreach (say; saylar) { say *= 2; } } writefln("Sonra: %s", saylar);

Programn kts, foreach kapsamnda say 'ya yaplan atamann etkisi olmadn gsteriyor: nce : 1.2 3.4 5.6 Sonra: 1.2 3.4 5.6 Bunun nedeni, say 'nn dizi elemannn kendisi deil, onun bir kopyas olmasdr. Dizi elemannn kendisinin ifade edilmesi istendiinde, isim bir referans olarak tanmlanr: foreach (ref say; saylar) { say *= 2; } Yeni ktda grld gibi, ref anahtar szc, dizideki asl elemann etkilenmesini salamtr: nce : 1.2 3.4 5.6 Sonra: 2.4 6.8 11.2 Oradaki ref anahtar szc, say 'nn, asl elemann bir takma ismi gibi kullanlmasn salamaktadr. say 'da yaplan deiiklik, artk elemann kendisini etkilemektedir.

32.9

Topluluun kendisi deitirilemez


Topluluk elemanlarn ref kullanarak deitirmekte bir saknca yoktur. Ancak, foreach dngs kapsamnda topluluun kendi yapsn etkileyecek hibir ilem yaplmamaldr. rnein diziden eleman silinmemeli, ve diziye eleman eklenmemelidir. Bu tr ilemler dizinin yapsn deitirdikleri iin, ilerlemekte olan foreach dngsnn iini bozarlar. O noktadan sonra programn davrannn ne olaca bilinemez.

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

128

foreach Dngs

32.10

Problemler
1. Eleme tablolarnn indeks deerleri ile eleman deerlerini elediklerini grmtk. Bu tek ynldr: indeks verildiinde eleman deerini elde ederiz, ama eleman deeri verildiinde indeks deerini elde edemeyiz. Elinizde hazrda yle bir eleme tablosu olsun: string[int] isimle = [ 1:"bir", 7:"yedi", 20:"yirmi" ]; O tablodan ve tek bir foreach dngsnden yararlanarak, rakamla isminde baka bir eleme tablosu oluturun. Bu yeni tablo, isimle tablosunun tersi olarak alsn: isime karlk rakam elde edebilelim. rnein writeln(rakamla["yirmi"]); yazdmzda kt yle olsun: 20 ... zmler

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

129

switch ve case

33

switch ve case
switch , oklu koul gibi alan bir deyimdir ve bu adan bir "if else if" zincirine benzer. Buradaki kullanmnda "durum" anlamna gelen case , switch 'in denetledii deerin karlatrld durumlar belirlemek iin kullanlr; kendisi bir deyim deildir. switch , parantez iinde bir ifade alr; o ifadenin deerini kendi kapsam iindeki case 'lerle karlatrr ve deere eit olan ilk case 'in ilemlerini iletir. Sz dizimini yle gsterebiliriz: switch (ifade) { case deer_1: // ifade'nin deer_1'e eit olduu durumdaki ilemler // ... break; case deer_2: // ifade'nin deer_2'ye eit olduu durumdaki ilemler // ... break; // ... baka case'ler ... default: // hibir deere uymayan durumdaki ilemler // ... break; } Her ne kadar bir koul gibi alsa da, switch 'in ald ifade, bir mantksal ifade olarak almaz. Yani bir if 'te olduu gibi "eer byleyse" anlamnda, veya bir while 'da olduu gibi "byle olduu srece" anlamnda deildir. switch 'teki ifadenin deerinin, case 'lerdeki deerlere eit olup olmadna baklr. Yani buradaki koul, bir eitlik karlatrmasdr. fadenin deerine eit olan bir case deeri varsa, o case 'in altndaki ilemler iletilir. Eer yoksa, "varsaylan" anlamna gelen default 'un altndaki ilemler iletilir. Bu ilemler break deyimine kadar devam eder; break , switch 'in iinin bitmesini salar. Bu adan baknca, bir "if else if" zinciri gibi dnlebilir: auto deer = ifade; if (deer == deer_1) { // deer_1 durumundaki ilemler // ... } else if (deer == deer_2) { // deer_2 durumundaki ilemler // ... } // ... baka 'else if'ler ... } else { // hibir deere uymayan durumdaki ilemler // ... } Bu "if else if", switch 'in tam edeeri deildir. Nedenlerini aadaki balklarda aklyorum.

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

130

switch ve case

33.1

case 'in kapsam yoktur


if koulunun kapsam olduu iin, kapsamdaki ilemler sonlannca btn if deyiminin ii bitmi olur. switch 'te ise; ifadenin deerine eit bir case bulunduu zaman, programn ileyii o case 'e atlar ve bir break ile karlalana kadar devam eder. Bu, break 'i olmayan case 'lerden, hemen altlarndaki case 'lere devam edilmesine neden olur: switch (deer) { case 5: writeln("be"); // burada break yok... case 4: writeln("drt"); break; default: writeln("bilmiyorum"); break; } deer 5 olduunda case 5 satrnn altna gidilir ve orada "be" yazdrlr. Ama o satrdan sonraki ilk break 'e gelene kadar "drt" de yazdrld iin ktda ikisi de yer alr: be drt Bir sonraki case 'e geilmesini zellikle istemiyorsanz break 'leri unutmayn. Dorusu, byle bir program davran iin normalde bir neden yoktur. Her zaman iin break 'leri hatrlayn.

33.2

fadenin deeri ancak tamsay, bool, veya dizgi olabilir


if 'te eitlik karlatrmasnda herhangi bir tr kullanlabilir. switch 'te ise ifade deeri olarak ancak tamsaylar, bool, veya dizgiler kullanlabilir. switch (ilem) { case "toplama": sonu = birinci + ikinci; break; case "karma": sonu = birinci - ikinci; break; case "arpma": sonu = birinci * ikinci; break; case "blme": sonu = birinci / ikinci; break; default: throw new Exception("Geersiz ilem"); } Not: Yukardaki kod, hibir case 'e uymayan durumda bir hata atmaktadr. Hatalar ilerideki bir derste greceiz.

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

131

switch ve case

Her ne kadar ifade tr olarak bool da kullanlabiliyor olsa da, false ve true diye iki deeri olan bu tr iin ou durumda if 'in veya ?: l ilecinin daha uygun olduunu dnebilirsiniz.

33.3

case deerleri derleme zamannda bilinmelidir


if deyimlerindeki eitlik koullarnda, eitliin her iki tarafndaki deer de programn almas srasnda belirlenmi olabilir. rnein bu kodda, birisi kullanc tarafndan girilmi ve dieri rasgele seilmi iki deerin eitlii karlatrlyor: if (tahminEdilenSay == tutulanSay) { writeln("Bildiniz!"); } Oysa case deerlerinin derleme zamannda bilinmeleri gerekir. switch 'in ifadesi alma zamannda hesaplanr, ama case deerleri derleme zamannda bilinmelidir.

33.4

Aralk deerleri
Belirli bir deer aralndaki durumlar case 'ler arasna .. karakterleri yerletirilerek belirtilir: switch (zarDeeri) { case 1: writeln("Sen kazandn"); break; case 2: .. case 5: writeln("Berabere"); break; case 6: writeln("Ben kazandm"); break; default: // Aslnda bu durumun hi gereklememesi gerekir! // nk yukardaki durumlar zarn btn deerlerini // kapsamaktadrlar. break; } Yukarda zarla oynanan bir oyunda zarn [2,5] aralndaki deerlerinde berabere kalnmaktadr.

33.5

Ayrk deerler virglle bildirilir


Yukardaki oyunda [2,5] aralnda deil de, 2 ve 4 deerleri geldiinde berabere kalndn varsayalm. yle durumlarda case 'in deerlerinin aralarna virgl yazlr: case 2, 4: writeln("Berabere"); break;

33.6

final switch deyimi


Bu deyim de switch gibidir, ama baz kstlamalar vardr:

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

132

switch ve case

default blm bulunamaz; zaten bu durum baz koullarda anlamszdr: rnein zarn deerlerinin altsnn da ilemlerinin belirli olduu bir durumda default blme gerek yoktur case 'lerde aralkl deerler kullanlamaz (virglle gsterilen ayrk deerler olur) eer ifade bir enum tryse, trn btn deerlerinin case 'ler tarafndan kapsanm olmalar gerekir (enum 'lar ok yaknda reneceksiniz) final switch (zarDeeri) { case 1: writeln("Sen kazandn"); break; case 2, 4: writeln("Berabere"); break; case 3, 5, 6: writeln("Ben kazandm"); break; }

33.7

Ne zaman kullanmal
Yukarda anlatlanlardan anlald gibi; switch , bir ifadenin derleme zamannda bilinen deerlerle karlatrld durumlarda kullanldr. Eer karlatrlacak deer yalnzca iki taneyse, switch yerine bir "if else" daha uygun olabilir. rnein yaz/tura gibi bir sonuta if deyimi yeterlidir: if (yazTuraSonucu == yaz) { // ... } else { // ... } Genel bir kural olarak, switch 'i veya daha ok durum iin dnebilirsiniz. Bu konuda da karar sizin! :o)

33.8

Problemler
1. Yukardaki rneklerden birisindeki gibi bir hesap makinesi yapn. Kullancdan nce ilemi string olarak, sonra da saylar double olarak alsn ve ileme gre hesap yapsn. rnein ilem "topla" olarak ve saylar "5 7" olarak girildiinde ekrana 12 yazsn. Girii u ekilde okuyabilirsiniz: string ilem; double birinci; double ikinci; // ... ilem = chomp(readln()); readf(" %s %s", &birinci, &ikinci); 2. Hesap makinesini gelitirin ve "topla" gibi szl ilemler yannda "+" gibi simgeleri de desteklemesini salayn: ilem dizgisi olarak "+" girildiinde de ayn ekilde alsn. 3. Program, bilinmeyen bir ilem girildiinde hata atsn. ... zmler

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

133

enum

34

enum
enum , "numaralandrmak" anlamna gelen "enumerate"in ksaltlmdr. simli sabit deerler retmek iin kullanlr.

34.1

Sihirli sabitler (kullanmayn)


Tamsaylar ve Aritmetik lemler blmnn problem zmlerinden birisinde yle bir koul kullanmtk: if (ilem == 1) { sonu = birinci + ikinci; } else if (ilem == 2) { sonu = birinci - ikinci; } else if (ilem == 3) { sonu = birinci * ikinci; } else if (ilem == 4) { sonu = birinci / ikinci; } O kod parasndaki 1, 2, 3, ve 4 deerlerine sihirli sabit denir. Kodu okuyan birisinin onlarn ne anlama geldiklerini bir bakta anlamas olanakszdr. rnein yukardaki kodda 1'in toplama ilemi, 2'nin karma ilemi, vs. anlamlarna geldiklerini ancak kapsamlarndaki kodlar okuduktan sonra anlayabiliyoruz. Bu durumda anslyz, nk her kapsamda yalnzca tek satr var; daha karmak kodlarda kodu anlamak ok g olabilir. Sihirli sabitler programclkta kanlan bir durumdur; nk iyi yazlm kodun en nemli niteliklerinden olan okunurluunu azaltrlar. enum olana, ite bu tr sabitlere isimler vermeyi ve bu sayede kodun okunurluunu arttrmay salar. Ayn kod, enum deerleriyle yazldnda her bir if koulunun hangi ilemle ilgili olduu aka anlalr: if (ilem == lem.Toplama) { sonu = birinci + ikinci; } else if (ilem == lem.karma) { sonu = birinci - ikinci; } else if (ilem == lem.arpma) { sonu = birinci * ikinci; } else if (ilem == lem.Blme) { sonu = birinci / ikinci; } Artk 1 gibi anlam ak olmayan bir deer yerine, lem.Toplama gibi isimli bir deer kullanlmaktadr. Bundan sonraki derslerdeki kodlarda sihirli sabitler yerine hep isimli sabitler kullanacam. Yukardaki 1, 2, 3, ve 4 deerlerine karlk gelen enum tanm yle yazlr: enum lem { Toplama = 1, karma, arpma, Blme }

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

134

enum

34.2

Sz dizimi
enum , en basit olarak u sz dizimine sahiptir: enum Trsmi { Deersmi_1, Deersmi_2, /* vs. */ } enum anahtar szcnden sonra, btn deerlerin toplu olarak ne anlama geldiini belirten bir tr ismi verilir. Btn olas deerler, isimler halinde enum kapsam iinde sralanrlar. Bir ka rnek: enum ParaAtSonucu { Yaz, Tura } enum OyunKadRengi { Maa, Kupa, Karo, Sinek } enum BiletTr { Normal, ocuk, renci, Emekli } Bu deerler, ayn zamanda yeni bir trn paralar haline de gelirler. rnein Yaz ve Tura , artk ParaAtSonucu diye tanmlanm olan yeni bir trn deerleridir. Bu yeni tr de baka trler gibi deiken tanmlamak iin kullanlabilir: ParaAtSonucu sonu; enum trlerinin deerleri; kod iinde sabit olarak belirtilecekleri zaman, ait olduklar trn ismiyle birlikte ve ondan bir nokta ile ayrlarak yazlrlar: if (sonu == ParaAtSonucu.Yaz) { // ... }

34.3

Asl deerleri
enum trlerin deerleri, normalde arka planda yine de int olarak kullanlrlar. Yani her ne kadar Yaz ve Tura gibi isimleri olsa da, yine de arka planda birer int deeridirler. (Not: Aslnda int yerine baka bir tr kullanmann da yolu vardr.) Bu deerler, programc zellikle belirtmedii srece 0'dan balar ve her isimli deer iin bir tane arttrlr. rnein yukarda tanmlanan ParaAtSonucu 'nun iki deerini yazdrdmzda 0 ve 1 deerlerine sahip olduklarn grrz: writeln("Yaz: ", ParaAtSonucu.Yaz); writeln("Tura: ", ParaAtSonucu.Tura);

Yaz: 0 Tura: 1 Normalde 0'dan balayan bu deerleri istediimiz noktadan itibaren = iareti ile kendimiz belirleyebiliriz. (Yukarda bunu lem 'in Toplama deerinde de grmtk.) Belirlediimiz deerden sonrakilerin deerleri de yine derleyici tarafndan birer birer arttrlarak verilir: enum Deneme { a, b, c, = 100, d, e, f = 222, g, } writeln(Deneme.b, ' ', Deneme., ' ', Deneme.);

1 100 224

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

135

enum

34.4

Trsz enum 'lar


Sihirli sabitlerden kurtulmann nemli olduunu ve bu amala enum 'lardan yararlanabileceimizi grdk. Ancak, enum kullanld diye sabit deerlere ayrca tr ismi vermek doal olmayabilir. rnein tek amacmzn 24 saatteki toplam saniye saysn tutan bir sabit tanmlamak olduunu dnelim. Byle tek bir sabitin tanmlanmasnda ayrca enum tr belirlemeye gerek yoktur. Byle durumlarda tr ismi ve enum kapsam parantezleri yazlmayabilir: enum gnBanaSaniye = 60 * 60 * 24; Artk o sabiti hesaplarda ismiyle kullanabiliriz: toplamSaniye = gnAdedi * gnBanaSaniye; enum , her trden hazr deeri tanmlamak iin de kullanlabilir. rnein bir string hazr deeri: enum dosyasmi = "liste.txt";

34.5

Dizgi karlklar
enum 'lar arka planda tamsay olduklar iin, ka normalde tamsay olarak yazdrlrlar. Bunu yukarda Yaz ve Tura iin 0 ve 1 olarak grmtk. Ek olarak, Phobos'un std.conv modlnn olanaklarndan yararlanarak enum deerleri dizgi olarak yazdrmak da mmkndr. O modldeki to ablonu, enum deerleri koddaki isimleriyle de yazdrabilir: import std.conv; // ... writeln(to!string(atSonucu)); Oradaki atSonucu 'nun deerinin ParaAtSonucu.Yaz olduunu varsayarsak, kts: Yaz Not: ablonlar daha sonra greceiz. imdilik to!string(atSonucu) kullanmn, "atSonucu'nu string'e dntr" olarak dnebilirsiniz.

34.6

Nitelikleri
.min ve .max nitelikleri, enum trnn srasyla en kk ve en byk deerleridir. Bunlar bir dngde kullanarak btn deerleri srayla gezebiliriz: enum OyunKadRengi { Maa, Kupa, Karo, Sinek } for (auto renk = OyunKadRengi.min; renk <= OyunKadRengi.max; ++renk) { } writeln(to!string(renk), ": ", renk);

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

136

enum

Maa: 0 Kupa: 1 Karo: 2 Sinek: 3

34.7

int 'ten dntrmek


Yukardaki yazdrma rneklerinde grld gibi, bir enum deer otomatik olarak int 'e dnr. Bunun tersi doru deildir: OyunKadRengi renk = 1; // derleme HATASI

Bunun nedeni, enum deikenlerine yanllkla geersiz deerlerin atanmasn nlemektir: renk = 100; // geerli bir deer olmad iin // anlamsz olurdu

Geerli olduunu bildiimiz bir deeri bir enum deerine dntrmek istiyorsak, bunu aka bir tr dnm olarak yazmamz gerekir: renk = cast(OyunKadRengi)1; Not: Tr dnmlerini de ileride greceiz. // imdi Kupa

34.8

Problem
Tamsaylar ve Aritmetik lemler dersinin problemlerindeki hesap makinesini deitirin: Drt ilemi destekleyen basit bir hesap makinesi, ilemi bir menden setirsin ve girilen iki deere o ilemi uygulasn. Program bu sefer u farklarla yazn: int yerine double kullansn ilemi sihirli sabitlerden deil, enum deerlerden anlasn "if else if" zinciri yerine switch kullansn ... zm

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

137

levler

35

levler
Elence yeni balyor! :o) levler gerek programlarn temel talardr. Nasl temel trler, btn trlerin yap talar iseler, ilevler de program davranlarnn yap talardr. levlerin ustalkla da ilgisi vardr. Usta programclarn yazdklar ilevler ksa ve z olur. Bunun tersi de dorudur: ksa ve z ilevler yazmaya almak, ustalk yolunda ilerlemenin nemli admlarndandr. lemleri oluturan alt admlar grmeye almak ve o admlar kk ilevler halinde yazmak, programclk konusunda gelimenize yardm edecektir. Bundan nceki derslerde temel deyimler ve ifadeler rendik. Daha hepsini bitirmedik ama D'nin programlarda ok kullanlan, ok yararl, ve ok nemli olanaklarn grdk. Yine de, hibirisi byk programlar yazmak iin yeterli deildir. imdiye kadar yazdmz biimdeki programlar, deneme programlar gibi hibir karmakl olmayan ok basit programlar olabilirler. En ufak bir karmakl bulunan bir ii ilev kullanmadan yazmaya almak ok zordur, ve ortaya kan program da hataya ak olur. levler, ifade ve deyimleri bir araya getiren olanaklardr. Bir araya getirilen ifade ve deyimlere toplu olarak yeni bir isim verilir ve o ilemlerin hepsi birden bu isimle iletilir. Bir araya getirerek yeni isim verme kavramn gnlk hayattan tanyoruz. rnein yada yumurta yapma iini u admlarla tarif edebiliriz: tavay kart ya kart yumurtay kart atei a tavay atee koy tava snnca ya iine at ya eriyince yumurtay iine kr yumurtann beyaz piince tavay ateten al atei sndr

O kadar ayrntya girmek zamanla gereksiz ve iinden klmaz bir hl alaca iin, birbiriyle ilikili admlarn bazlarna tek bir isim verilebilir: malzemeleri hazrla (tavay, ya, yumurtay kart) atei a yumurtay piir (tavay atee koy, vs.) atei sndr

Daha sonra daha da ileri gidilebilir ve btn o admlar ieren tek bir ifade de kullanlabilir: yada yumurta yap (btn admlar) levlerin bundan fark yoktur: Yaptklar ilerin hepsine birden genel bir isim verilebilen admlar tek bir ilev olarak tanmlanrlar. rnek olarak kullancya bir men gsteren u satrlara bakalm: writeln(" writeln(" writeln(" writeln(" writeln(" 0 1 2 3 4 k"); Toplama"); karma"); arpma"); Blme");

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

138

levler

Onlarn hepsine birden menyGster gibi bir isim verilebilecei iin onlar bir ilev olarak u ekilde bir araya getirebiliriz: void menyGster() { writeln(" 0 k"); writeln(" 1 Toplama"); writeln(" 2 karma"); writeln(" 3 arpma"); writeln(" 4 Blme"); } Artk o ilevi main iinden ksaca ismiyle iletebiliriz: void main() { menyGster(); } // ... dier ilemler ...

menyGster ile main 'in tanmlarndaki benzerlie bakarak main 'in de bir ilev olduunu grebilirsiniz. smi ngilizce'de "ana ilev" kullanmndaki "ana" anlamna gelen main , D programlarnn ana ilevidir. D programlarnn ileyii bu ilevle balar ve programcnn istedii ekilde baka ilevlere dallanr.

35.1

Parametre
levlerin gl yanlarndan birisi, yaptklar ilerin belirli lde ayarlanabiliyor olmasndan gelir. Yine yumurta rneine dnelim, ve bu sefer be yumurta yapmak isteyelim. zlenmesi gereken admlar aslnda bu durumda da ayndr; tek farklar, yumurta saysndadr. Daha nce drde indirgediimiz admlar be yumurtaya uygun olarak yle deitirebiliriz: malzemeleri be yumurta iin hazrla atei a yumurtalar piir atei sndr

Teke indirgediimiz adm da yle deiir: yada be yumurta yap Yani, temelde ayn olan yumurta piirme iiyle ilgili bir bilgiyi ek olarak belirtmemiz gerekir: "be yumurta kart" veya "be yumurta yap" gibi. levlerin davranlar da benzer ekilde ayarlanabilir. levlerin ilerini bu ekilde etkileyen bilgilere parametre denir. Parametreler, parametre listesinde virgllerle ayrlarak bildirilirler. Parametre listesi, ilevin isminden hemen sonra yazlan parantezin iidir. Daha nceki menyGster ilevinde parametre parantezini bo olarak tanmlamtk; nk o ilev her zaman iin ayn meny gstermekteydi. Mendeki ilk seeneinin hep "k" olmas yerine, duruma gre deien bir seenek olmasn istesek, bunu bir parametreyle salayabiliriz. rnein ilk seenei baz durumlarda "Geri Dn" olarak yazdrmak iin bu bilgiyi bir parametre olarak tanmlayabiliriz: void menyGster(string ilkSeenek) {

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

139

levler

writeln(" writeln(" writeln(" writeln(" writeln("

0 1 2 3 4

", ilkSeenek); Toplama"); karma"); arpma"); Blme");

ilkSeenek parametresi bu rnekte bir dizgi olduu iin trn de string olarak belirledik. Bu ilevi artk deiik dizgilerle ileterek mennn ilk satrnn farkl olmasn salayabiliriz. Tek yapmamz gereken, parametre deerini parantez iinde belirtmektir: menyGster("k"); menyGster("Geri Dn"); Not: Burada parametrenin tryle ilgili bir sorunla karlaabilirsiniz: yukarda yazld haliyle, bu ilev char[] trnde dizgilerle kullanlamaz. rnein u kod, char[] ile string uyumlu olmadklar iin derleme hatasna neden olur: char[] birSeenek = "Kare Kk Al"; menyGster(birSeenek); // derleme HATASI te yandan, menyGster 'in tanmnda parametrenin trn char[] olarak belirlediinizde de ilevi "k" gibi bir string 'le aramazsnz. immutable ile ilgili olan bu nemli konuyu bir sonraki derste aklayacam. Biraz daha ileri gidelim ve seenek numaralarnn hep 0 ile deil, duruma gre deiik bir deerle balamasn istiyor olalm. Bu durumda balang numarasn da parametre olarak verebiliriz. ki parametreyi ayrmak iin aralarna virgl yazarak: void menyGster(string ilkSeenek, int ilkNumara) { writeln(' ', ilkNumara, ' ', ilkSeenek); writeln(' ', ilkNumara + 1, " Toplama"); writeln(' ', ilkNumara + 2, " karma"); writeln(' ', ilkNumara + 3, " arpma"); writeln(' ', ilkNumara + 4, " Blme"); } O ileve hangi numarayla balayacan artk biz bildirebiliriz: menyGster("Geri Dn", 100);

35.2

armak
levin iini yapmas iin balatlmasna ilevin arlmas denir. rnein imdiye kadar hep kullandmz writeln 'i, yazdrmasn istediimiz deerlerle yle aryorduk: writeln("Merhaba ", rencismi); lev arsnn sz dizimi yledir: ilevin_ismi(parametre_deerleri) ini yaparken kullanmas iin ileve verilen bilgilere parametre deeri denir. Yukardaki kullanmda writeln 'e verilen parametre deerleri, "Merhaba" ve rencismi dizgileridir.

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

140

levler

35.3

yapmak: yan etki oluturmak veya deer retmek


Hem daha nceki derslerde, hem de bu derste i yapmaktan sz ettim. Program admlarnn, ifadelerin, ilevlerin, i yaptklarn syledim. Programn i yapmas, iki anlama gelebilir: Yan etki oluturmak: Baz ilemlerin yalnzca yan etkileri vardr. rnein ka men yazdran menyGster ilevi k etkilemektedir; oluturduu bir deer yoktur. Baka bir rnek olarak, kendisine verilen bir renci nesnesini bir renciler listesine ekleyen bir ilevin etkisi, listenin bymesidir. Onun da rettii bir deer yoktur. Genel olarak, programn durumunda bir deiiklie neden olan bir ilemin yan etkisinin olduu sylenir. Deer retmek: Baz ilemler yalnzca deer retirler. rnein toplama ileminin sonucunu veren bir ilev, toplanan deerlerin toplamn retir. Baka bir rnek olarak; isim, adres, vs. gibi kendisine verilen bilgiyi bir araya getirerek bir renci nesnesi oluturan bir ilevin de bir nesne rettii sylenir. Bu tr ilemlerin ayrca yan etkileri yoktur; programn durumunda hibir deiiklie neden olmazlar; yalnzca deer retirler. Hem deer retmek, hem yan etki oluturmak: Baz ilemler hem deer retirler, hem de yan etkileri vardr. rnein giriten okuduu saylarn toplamn hesaplayan bir ilev, hem toplamn sonucunu retmektedir; hem de iinden karakterler kartt iin girii etkilemektedir. Her ilev bu gruptan birisine girer: ya yalnzca deer retir, ya yan etki veya etkileri vardr, ya da ikisi birden... Not: Ne deer reten ne de yan etkisi olan bir ilevin programda bir "i yaptn" syleyemeyiz.

35.4

Dn deeri
Deer reten bir ilevin rettii deere, ilevin dn deeri denir. Bu terim, ilevin iini bitirdikten sonra bize geri dnmesi gibi bir dnceden tremitir. levi "arrz" ve "dndrd" deeri kullanrz. Her deerin olduu gibi, dn deerinin de tr vardr. Bu tr, ilevin isminden nce yazlr. rnein iki tane int deeri toplayan bir ilev, eer sonuta yine int trnde bir deer retiyorsa, dn tr olarak int yazlr: int topla(int birinci, int ikinci) { // ... yaplan ilemler ... } levin dndrd deer, sanki o deer ilev arsnn yerine yazlm gibi, onun yerine geer. rnein topla(5, 7) arsnn deer olarak 12 rettiini dnrsek, u iki satr birbirinin edeeridir: writeln("Toplam: ", topla(5, 7)); writeln("Toplam: ", 12); writeln arlmadan nce, topla(5, 7) ilevi arlr ve onun dndrd deer olan 12, yazdrmas iin writeln 'e parametre olarak verilir. Bu sayede ilevlerin deerlerini baka ilevlere parametre olarak verebilir ve daha karmak ifadeler oluturabiliriz:

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

141

levler

writeln("Sonu: ", topla(5, bl(100, kiiSays()))); O rnekte kiiSays 'nn dn deeri bl 'e, bl 'n dn deeri topla 'ya, ve en sonunda da topla 'nn dn deeri writeln 'e parametre olarak verilmektedir.

35.5

return
levin rettii deer, "dndr" anlamna gelen return anahtar szc ile bildirilir: int topla(int birinci, int ikinci) { int toplam = birinci + ikinci; return toplam; } lev, gereken ilemleri ve hesaplar yaparak dn deerini retir ve en son olarak return ile dndrr. levin ileyii de o noktada sona erer ve ilevden dnlm olur. levlerde birden fazla return anahtar szc kullanlabilir. fade ve deyimlere bal olarak nce hangi return iletilirse, ilevin dn deeri olarak o return 'n dndrd deer kullanlr: int karmakHesap(int birParametre, int bakaParametre) { if (birParametre == bakaParametre) { return 0; } } return birParametre * bakaParametre;

O ilev; iki parametresi birbirlerine eitse 0 deerini, deilse iki parametrenin arpmn dndrr.

35.6

void ilevler
Eer ilev deer retmeyen bir ilevse, dn tr olarak "boluk, yokluk" anlamna gelen void yazlr. O yzden main 'in ve menyGster 'in dn trlerini void olarak yazdk; imdiye kadar grdmz kadaryla ikisi de deer retmeyen ilevlerdir. Not: main aslnda int de dndrebilir. Bunu daha sonraki bir derste anlatacam.

35.7

levin ismi
levin ismi, programc tarafndan ilevin yapt ii aklayacak ekilde seilmelidir. rnein iki sayy toplayan ilevin ismini topla olarak setik; veya meny gsteren ileve menyGster dedik. levlere isim verirken izlenen bir kural, isimleri topla 'da ve menyGster 'de olduu gibi ikinci tekil ahs emir kipinde semektir. Yani toplam 'da ve men 'de olduu gibi isim halinde deil... Bylece ilevin bir eylemde bulunduu isminden anlalr. te yandan hibir yan etkileri olmayan, yani yalnzca deer reten ilevlere iinde eylem bulunmayan isimler de seilebilir. rnein u andaki hava scakln veren bir ilev iin havaScaklnVer yerine havaScakl gibi bir ismin daha uygun olduunu dnebilirsiniz.

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

142

levler

lev, nesne, deiken, vs. isim seimlerinin programcln sanat tarafnda kaldn dnebilirsiniz. e yarar, yeterince ksa, ve programdaki dier isimlerle tutarl olan isimler bulmak bazen yaratclk gerektirir.

35.8

Kod tekrar (yapmayn)


Programclkta kanlmas gereken bir eylem, kod tekrardr. Kod tekrar, ayn ii yapan ilemlerin programda birden fazla yerde tekrarlanmas anlamna gelir. Bu tekrar bazen bilinli olarak satrlarn bir yerden baka bir yere kopyalanmas ile yaplabilir. Bazen de farknda olmadan, ayn ilemlerin ayn ekilde kodlanmalar eklinde ortaya kabilir. Kod tekrarnn sakncalarndan birisi; tekrarlanan ilemlerdeki olas hatalarn btn kopyalarda da bulunmas, ve ayn hatann her kopyada giderilmesinin gerekmesidir. Oysa; tekrarlanan kod tek bir ilev iinde bulunuyor olsa, hatay yalnzca bir kere gidermek yeter. Yukarda ilevlerin ustalkla ilgili olduklarna deinmitim. Usta programclar koddaki ilemler arasndaki benzerlikleri yakalamaya ve kod tekrarn ortadan kaldrmaya alrlar. Bir rnek olarak, giriten ald saylar nce giriten geldikleri srada, sonra da sralanm olarak yazdran u programa bakalm: import std.stdio; void main() { int[] saylar; int adet; write("Ka say gireceksiniz? "); readf(" %s", &adet); // Saylar oku for (int i = 0; i != adet; ++i) { int say; write("Say ", i, "? "); readf(" %s", &say); } saylar ~= say;

// Diziyi ka yazdr writeln("Sralamadan nce:"); foreach (i, say; saylar) { writefln("%3d:%5d", i, say); } saylar.sort; // Diziyi ka yazdr writeln("Sraladktan sonra:"); foreach (i, say; saylar) { writefln("%3d:%5d", i, say); }

Kod tekrarn gryor musunuz? Diziyi yazdrmak iin kullanlan son iki foreach dngs birbirinin ayns... diziYazdr ismiyle bir ilev tanmlasak ve yazdrmasn istediimiz diziyi de parametre olarak versek, bu kod tekrarn ortadan kaldrm oluruz: void diziYazdr(int[] dizi) {

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

143

levler

foreach (i, eleman; dizi) { writefln("%3s:%5s", i, eleman); }

Dikkat ederseniz, parametrenin ismi olarak saylar yerine ondan daha genel olan dizi ismini setik. Bunun nedeni, bu ilev balamnda dizi yazdrmak dnda bir ey bilmiyor olduumuzdur. Dizinin elemanlarnn ne olduklarndan bu ilev iinde haberimiz yoktur. Dizideki int elemanlarn ne anlama geldiklerini ancak bu ilevi aran kapsam bilir: belki renci kayt numaralardr, belki bir ifrenin paralardr, belki bir grup insann yalardr... Ne olduklarn diziYazdr ilevi iinde bilemediimiz iin, ancak dizi ve eleman gibi genel isimler kullanabiliyoruz. imdi kod biraz daha dzenli bir hale gelir: import std.stdio; void diziYazdr(int[] dizi) { foreach (i, eleman; dizi) { writefln("%3s:%5s", i, eleman); } } void main() { int[] saylar; int adet; write("Ka say gireceksiniz? "); readf(" %s", &adet); // Saylar oku for (int i = 0; i != adet; ++i) { int say; write("Say ", i, "? "); readf(" %s", &say); } saylar ~= say;

// Diziyi ka yazdr writeln("Sralamadan nce:"); diziYazdr(saylar); saylar.sort; // Diziyi ka yazdr writeln("Sraladktan sonra:"); diziYazdr(saylar);

imiz bitmedi: iki diziYazdr arsndan nce birer de balk yazdrlyor. Yazdrlan dizgi farkl olsa da ilem ayndr. Eer bal da diziYazdr 'a parametre olarak verirsek, bal yazdrma tekrarndan da kurtulmu oluruz. Programn yalnzca deien blmlerini gsteriyorum: void diziYazdr(string balk, int[] dizi) { writeln(balk, ":"); foreach (i, eleman; dizi) { writefln("%3s:%5s", i, eleman); }

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

144

levler

// ... // Diziyi ka yazdr diziYazdr("Sralamadan nce", saylar); // ... // Diziyi ka yazdr diziYazdr("Sraladktan sonra", saylar);

Bu ilemin diziYazdr 'dan nceki aklama satrlarn da gereksiz hale getirdiini grebiliriz. lemlere diziYazdr diye aklayc bir isim verdiimiz iin, ayrca "Diziyi ka yazdr" gibi bir aklamaya da gerek kalmam oluyor. imdi programn son satrlar yle ksaltlabilir: diziYazdr("Sralamadan nce", saylar); saylar.sort; diziYazdr("Sraladktan sonra", saylar); Bu programda bir kod tekrar daha var: giriten adet ve say iin ayn ekilde tamsay okunuyor. Tek farklar, kullancya verilen mesaj: int adet; write("Ka say gireceksiniz? "); readf(" %s", &adet); // ... int say; write("Say ", i, "? "); readf(" %s", &say); sayOku diye bir ilev yazarsak ve kullancya gsterilecek mesaj bir parametre olarak alrsak, kod ok daha temiz bir hale gelir. Bu sefer bu ilevin giriten okuduu deeri dndrmesi gerektiine dikkat edin: int sayOku(string mesaj) { int say; write(mesaj, "? "); readf(" %s", &say); return say; } Bu ilevi ararak adet 'in deerini okumak kolay. adet 'i ilevin dn deeriyle ilkleyebiliriz: int adet = sayOku("Ka say gireceksiniz"); Fakat say 'y okumak o kadar kolay deil, nk dng sayac olan i de bu mesajn bir paras... std.conv modlndeki to!string dnm ilevini ve dizgi birletirmek iin kullanlan ~ ilecini hatrlayarak: int say = sayOku("Say " ~ to!string(i)); say 'nn for iinde tek bir yerde kullanldn grerek say 'nn tanmn da tamamen kaldrabilir ve onun kullanld tek yerde dorudan sayOku ilevini arabiliriz. Bylece dng iindeki satrlar da azalm olur:

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

145

levler

for (int i = 0; i != adet; ++i) { saylar ~= sayOku("Say " ~ to!string(i)); } Ben bu programda son bir deiiklik daha yapacam ve saylarn okunmasyla ilgili btn ilemleri tek bir ileve tayacam. Bylece "Saylar oku" aklamas da ortadan kalkacak; nk yeni ilevin ismi zaten ne ilem yaplmakta olduunu aklayacak. saylarOku ismini verebileceimiz bu ilevin hibir parametre almas gerekmez, ama deer olarak btn diziyi retirse ismi ile de uyumlu bir kullanm olur. Bylece btn program son olarak yle yazlabilir: import std.stdio; import std.conv; void diziYazdr(string balk, int[] dizi) { writeln(balk, ":"); foreach (i, eleman; dizi) { writefln("%3s:%5s", i, eleman); }

int sayOku(string mesaj) { int say; write(mesaj, "? "); readf(" %s", &say); return say; } int[] saylarOku() { int[] saylar; int adet = sayOku("Ka say gireceksiniz"); for (int i = 0; i != adet; ++i) { saylar ~= sayOku("Say " ~ to!string(i)); } } return saylar;

void main() { int[] saylar = saylarOku(); diziYazdr("Sralamadan nce", saylar); saylar.sort; diziYazdr("Sraladktan sonra", saylar); } Programn bu halini ilk haliyle karlatrn. Yeni programda main ilevi iinde programn ana admlar ak bir ekilde anlalmaktadr. Oysa ilk halinde programn ne yaptn ancak kodlar ve aklamalarn okuyarak anlamak zorunda kalyorduk. Programn son halinde daha fazla satr bulunuyor olmas sizi yanltmasn. levler aslnda kodu kltrler, ama bunu ok ksa olan bu programda gremiyoruz. rnein sayOku ilevini yazmadan nce, giriten tamsay okumak iin her seferinde 3 satr kod yazyorduk. imdi ise sayOku 'yu ararak her noktadaki kod satr saysn 1'e indirmi olduk. Hatta, for dngs iindeki say 'nn tanmn da tamamen kaldrabildik.

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

146

levler

35.9

Aklamalar (gerekmesinler)
Eer programdaki ilemlerin bazlarnn ne yaptklarn aklama satrlar yazarak aklama gerei duyuyorsanz, belki de o ilemlerin bir ileve tanmalar zaman gelmitir. levin ismini aklayc olarak semek, aklama satrnn gereini de ortadan kaldrr. Yukardaki programdaki aklama satrndan bu sayede kurtulmu olduk. Aklama satrlarndan kurtulmann nemli baka bir nedeni daha vardr: aklama satrlar zaman iinde kodun ne yapt hakknda yanl bilgi vermeye balarlar. Batan iyi niyetle ve doru olarak yazlan aklama satr, kod deitiinde unutulur ve zamanla koddan ilgisiz hale gelebilir. Artk aklama satr ya yanl bilgi veriyordur, ya da tamamen ie yaramaz durumdadr. Bu yzden programlar aklama satrlarna gerek brakmadan yazmaya almak nemlidir.

35.10

Paralara ayrn
Program bir btn halinde main iinde yazmak yerine kk paralara ayrmak btn program kolaylatrr. Kk ilevlerin birim olarak ilemleri de basit olacandan, teker teker yazlmalar ok daha kolay olur. Programn dier ilemleri bu yap talar zerine kurulunca btn program daha kolay yazlr. Daha da nemlisi, programda daha sonradan gereken deiiklikler de ok daha kolay olur.

35.11

Problemler
Bundan sonraki btn derslerde ve yazacanz btn programlarda hep ilevler olacak. O yzden hi ilev deneyimi sknts ekmeyeceksiniz. Burada ilgin problemler bulabildiime inanmyorum. Hi olmazsa zmlerini ilgin bulabilirsiniz. 1. menyGster ilevini, btn seeneklerini bir dizi olarak alacak ekilde deitirin. rnein u ekilde arabilelim: string[] seenekler = [ "Siyah", "Krmz", "Yeil", "Mavi", "Beyaz" ]; menyGster(seenekler, 1); kts yle olsun: 1 Siyah 2 Krmz 3 Yeil 4 Mavi 5 Beyaz 2. Komut satrndan girilen dizgiyi mors koduna eviren bir program yazn. Bu program yazarken amacnz olabildiince ok ilev kullanmak olsun. Fikirler: Trke harfleri boverin. Hem mors kodunun gnmzde geerlilii kalmad, hem de zaten Trk harfi eki olduunu sanmyorum Dizgideki harflerin mors kodu karlklarn bir eleme tablosu ile bulabilirsiniz: string[char] morsAlfabesi = [ 'a' : ".-", 'b' : "-...", 'c' : "-.-.",

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

147

levler

];

/* ... dierleri ... */

Hi gerekmese de, bu programn ktsn daha doru yapmak isterseniz mors kodundaki boluklar da gze aln. Mors kodunun hep ksa ve uzun iaretlerden olutuunu duyarz. Oysa bu iaretlerin aralarndaki boluklarn uzunluklar da nemlidir: uzun iaret ksann 3 kat uzunluundadr boluk, ksa iaret uzunluundadr ksa ve uzun iaretler arasnda tek boluk vardr harf aralarnda 3 boluk vardr szck aralarnda 7 boluk vardr

Boluk iin '.', ksa iaret iin '=', ve uzun iaret iin '===' kullanrsak; "ab 12" dizgisi iin programn kts yle olabilir:
=.===...===.=.=.=.......=.===.===.===.===...=.=.===.===.=== a b 1 2

4. ki boyutlu bir diziyi bir resim kad gibi kullanan bir program yazdm. Siz bu program istediiniz ekilde deitirin: import std.stdio; /* "Deimez" anlamna gelen immutable' bir sonraki derste * gstereceim */ immutable int satrAdedi = 20; immutable int stunAdedi = 60; /* * alias, "takma isim" anlamna gelir. Programn geri * kalannda hep dchar[stunAdedi] yazmak yerine, daha * aklayc olarak 'Satr' yazabilmemizi salyor. * * Dikkat ederseniz Satr "sabit uzunluklu dizi" trdr. */ alias dchar[stunAdedi] Satr; /* * Belirsiz sayda satra da ksaca 'Kat' takma ismini * veriyoruz. * * Kat ise "dinamik dizi"dir. */ alias Satr[] Kat; /* * Verilen kad satr satr ve kare kare ka gnderir */ void kadGster(Kat kat) { foreach (satr; kat) { foreach (kare; satr) { write(kare); } } writeln();

/* * Verilen kadn belirtilen yerine bir benek koyar; bir * anlamda o kareyi "boyar"

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

148

levler

*/ void benekKoy(Kat kat, int satr, int stun) { kat[satr][stun] = '#'; } /* * Kadn belirtilen yerinden aaya doru, belirtilen * uzunlukta izgi izer */ void deyizgiiz(Kat kat, int satr, int stun, int uzunluk) { foreach (izilecekSatr; satr .. satr + uzunluk) { benekKoy(kat, izilecekSatr, stun); } } void main() { /* Biraz aada yararlanmak zere bo bir satr * oluturuyoruz */ Satr boSatr; foreach (ref kare; boSatr) { kare = '.'; } /* Hi satr bulunmayan bir kat */ Kat kat; /* Ona bo satrlar ekliyoruz */ foreach (i; 0 .. satrAdedi) { kat ~= boSatr; } /* Ve kullanmaya balyoruz */ benekKoy(kat, 7, 30); deyizgiiz(kat, 5, 10, 4); } ... zmler kadGster(kat);

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

149

const ve immutable

36

const ve immutable
Ddili'nin makaleler blmnde bu konuyu anlatan "const ve immutable Kavramlar" isimli bir eviri var. O makale, bu kavramlar baka programlama dillerinden, ve zellikle C++'dan tanyan kiilere ynelik olarak yazlm. Eer byle bir deneyiminiz varsa, o makale sizin iin yeterli olabilir. Ben bu derste bu kavramlar yalnzca imdiye kadar rendiklerimize bal olarak gstereceim. Deikenler, programda geen kavramlar temsil ederler. Somut veya soyut, her trl kavram bir deiken olarak tanmlarz. Temsil ettikleri kavramlar arasndaki etkileimleri de bu deikenlerin deerlerini deitirerek salarz: // Paray de toplamFiyat = fiyatHesapla(fiyatListesi); czdandakiMiktar -= toplamFiyat; bakkaldakiMiktar += toplamFiyat; Bu adan baknca, deikenler olmadan olmaz: deiebilme kavram, programlarn herhangi bir i yapabilmeleri iin kesinlikle gereklidir. Buna ramen, bu esnekliin uygun olmad durumlar da vardr: baz kavramlar zaten deimezdirler; rnein haftadaki gn says 7'dir, matematikteki pi saysnn deeri bellidir, bir programn destekledii dil says programn alt srece deimeyecektir (rnein Trke ve ngilizce'dir), vs. koddaki ilemlerin her birisinin her deikeni deitirebilecek kadar esnek olmas; hangi ilemlerin hangi deikenleri deitirdiklerini fazla serbest brakt iin, kodun okunmas ve gelitirilmesi g bir duruma gelir. rnein emekliEt(banka, alan) gibi bir ilev ars sonucunda alan 'n banka 'dan emekli edildiini anlayabiliriz. Ancak, bu ilevden dnldnde bu iki deikenden hangilerinin deimi olabileceklerini bilmek de nemlidir; yoksa her ilev arsna pheyle bakmaya balarz. Herhalde banka 'nn eleman says azalacaktr; peki alan deikeni de deiecek midir; rnein bu ilev alan deikeninin iindeki bir enum deikeni de almaDurumu.emekli olarak deitirecek midir? Kodun iinden klmaz derecede g bir duruma gelmemesi iin baz durumlarda deimezlik olgusundan yararlanmak isteriz. Baz kavramlarn baz ilemler srasnda deimeyecekleri gvencesine gerek duyarz. D'deki deimezlik kavramlarn gsteren iki anahtar szck ngilizce'de de ok yakn anlamdadrlar: const , "sabit, deimez" anlamna gelen "constant"n ksasdr. immutable ise, "deiebilen" anlamna gelen "mutable"n kart anlamlsdr. Sonuta ikisi de "deimez" anlamna geliyor olsalar da, programda grevleri farkldr ve baz durumlarda birbirleriyle uyumsuzdurlar. Bu uyumsuzlua nceki derslerde rastladk: kendisi aslnda immutable(char)[] 'n takma ismi olan string , deiebilen char[] dizgileriyle uyumlu deildir.

36.1

const
Bu anahtar szck bir deikene sabitmi gibi davranlacan, yani o deikenin deitirilmeyeceini belirler. Tanmland srada tr isminden nce const yazlan deikenler, ilk deerleri verildikten sonra artk deitirilemezler.

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

150

const ve immutable

imdiye kadar hep deitirilebilen kavramlar grdk: int birDeer = birHesap(); // ... daha sonra: birDeer = bakaHesap(); // alr; deiebilir Ayn kavram const olarak tanmlamak, derleyicinin onun deitirilmesine izin vermemesini salar: const int birDeer = birHesap(); // ... daha sonra: birDeer = bakaHesap(); // derleme HATASI Bu, programc iin yararl bir kstlamadr. Deikenin belirli bir kapsam iinde deimeyecek olduunu bilmek, kodun anlalmasn ok kolaylatrr. Olas hatalar da nler: deimeyecek olduu dnld iin const olarak belirlenmi olan bir deikeni sonradan deitirmeye almaya derleyici izin vermez. O noktada programc ya yanll farkeder, ya da tasarm gzden geirerek const 'n kaldrlmas gerektiine karar verir. Bir rnek olarak, bir fiyatHesapla ilevine bakalm. Bu ilev, fiyat listesi olarak bir double dizisi alyor ve dizideki btn fiyatlarn toplamlarn dndryor olsun: import std.stdio; double fiyatHesapla(double[] fiyatListesi) { double toplam = 0; foreach (birimFiyat; fiyatListesi) { toplam += birimFiyat; } } return toplam;

void main() { double[] fiyatListesi = [ 10.25, 5.50, 3.00 ]; // ... sonra baka fiyat da eklenebilir double toplamFiyat = fiyatHesapla(fiyatListesi); writeln("Toplam fiyat: ", toplamFiyat);

fiyatHesapla ilevinin, kendisine verilen fiyat listesinde bir deiiklik yapmas mantkl olmaz. Onun ii, listedeki birimlerin fiyatlarn ekleyerek toplam fiyat dndrmek olmaldr. Oysa, parametresini const olarak almad iin, fiyatListesi 'nin deimesi iin bir engel yoktur. Belki de bir yanllk sonucu olarak eklenebilecek bir satr, fiyatListesi 'nin deimesine neden olur: double fiyatHesapla(double[] fiyatListesi) { fiyatListesi[0] = 1234.56; // sorunsuz derlenir fiyatHesapla ilevini aran kapsamlar, verilen parametrenin deimediinden emin olamazlar. Bu, programcnn aklnda tutmas gereken bir bilgidir: bugn deitirmiyorsa bile, ilerideki bir kod gelitirme sonucunda parametre deiebilir. Bu sakncay ortadan kaldrmak iin, parametrenin deimeyeceini const anahtar szc ile belirtmek gerekir. Bylece parametrenin deimesini hem derleyici nleyecektir, hem de

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

151

const ve immutable

programclar ileve verilen parametrenin deimediinden emin olabileceklerdir. Bu kod artk derlenemez: double fiyatHesapla(const double[] fiyatListesi) { fiyatListesi[0] = 1234.56; // derleme HATASI Bu yararl bir denetimdir. Fiyat hesaplayan bir ilevin listede deiiklik yapmas olsa olsa bir hata olaca iin; derleyicinin salad bu gvenceden yararlanmak nemlidir. Devam edersek; eer ayn rnekteki toplamFiyat 'n bir kere ilklendikten sonra bir daha deimeyecei dnlyorsa, o da const olarak tanmlanabilir: const double toplamFiyat = fiyatHesapla(fiyatListesi); toplamFiyat da artk deitirilemez.
36.1.1

const deiken, const olmayan parametre yerine kullanlamaz


Deimeyecek olduu halde const olarak tanmlanmayan bir parametre, ilevin kullanlln drr. Bunu grmek iin tekrar fiyatHesapla ilevine dnelim ve parametresinin en bataki gibi yanl olarak const olmadan tanmlandn varsayalm: double fiyatHesapla(double[] fiyatListesi) { O ilev, programn ilk gsterdiim halinde de zaten yleydi. O haliyle derlenir ve program doru olarak alr. imdi o ilevin arlmasndan hemen nceye dnelim. main iindeki fiyatListesi 'nin deimeyecek olduunu, ve bu durumda programcnn onu doru olarak const ile tanmladn varsayalm: const double[] fiyatListesi = [ 10.25, 5.50, 3.00 ]; // ... artk baka fiyat eklenemez double toplamFiyat = fiyatHesapla(fiyatListesi); // derleme HATASI Program artk derlenemez. main iinde const olarak tanmlanan, yani deimeyecek olan bir dizinin, onu deitirmeyecei gvencesini vermeyen bir ileve parametre olarak gnderilmesine derleyici izin vermez. Sonuta, ilev parametresinin const olarak tanmlanmam olmas, ilevin kullanlln drmektedir, ve ilevin ancak const olmayan dizilerle kullanlabilmesine neden olmaktadr. const 'n, mantkl olan her yerde kullanlmas gerekir. const 'n yalnzca programcya yardmc bir olanak olarak deil, programn tutarll asndan dikkat edilmesi gereken bir olgu olarak kabul edilmesi gerekir. Not: const 'n doru olarak kullanlmasyla ilgili olan bu konunun ngilizce'si "const correctness"tr.

36.1.2

const deiken, kesinlikle deimez deildir


const , deikenin "bu kapsam iinde" veya "bu parametre yoluyla" deitirilemeyeceini belirler. Oysa deikenin kendisi deimez olmayabilir. Bunu yukardaki fiyatHesapla ilevinin parametresinde grmtk: deiken, parametre const olduu iin, ilev iinde deitirilemiyordu; oysa main iindeki fiyatListesi yine de deitirilebiliyordu.

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

152

const ve immutable

Bunu deiik bir rnekle tekrar grelim. Herhangi bir deikeni ka yazdrmak iin kullanlacak olan bir ilevin, yazdraca parametrede deiiklik yapmasn beklemeyiz. Yoksa hem srprizlerle karlarz, hem de son grdmz derleme hatasnda olduu gibi, const deikenleri o ilevle yazdramayz. Bu yzden, bir rencinin notlarn yazdran bir ilevin parametrelerini const olarak tanmlamak doru olur: void notlarYazdr(const char[] isim, const int[] notlar) { writeln("renci: ", isim); write("Notlar :"); foreach (not; notlar) { write(' ', not); } } writeln();

Oysa rnein notlar , o ilevi aran tarafta deiebiliyor olabilir: string isim = "Mehmet"; int[] notlar = [ 80, 95, 90 ]; notlarYazdr(isim, notlar); notlar[0] = 81; notlarYazdr(isim, notlar);

renci: Notlar : renci: Notlar :

Mehmet 80 95 90 Mehmet 81 95 90

zetle, const , bir deikenin "burada deimeyecei" veya "bu isimle deimeyecei" gibi bir anlam tar. notlarYazdr ilevinin parametresi const olduu iin notlar orada deitirilemez; ama ilevin arld kapsamda int[] olarak tanmland iin deiebilir.

36.2

immutable
Bu anahtar szck, deikenin deimezlii konusunda const 'tan daha kuvvetli bir anlama sahiptir. const 'tan farkl olarak, bir deikenin programn almas srasnda kesinlikle deimeyeceini bildirir. rnein haftann gn isimlerini tayan bir dizi hibir zaman deimez; kullancya deneme ans veren bir oyunda deneme says snr deimez; programn gsterdii men seenekleri deimezdir, vs. Bu tr deikenler immutable ile tanmlanrlar: immutable string [ "Pazartesi", "Cumartesi", ]; gnsimleri[0] = gnsimleri[] = "Sal", "aramba", "Perembe", "Cuma", "Pazar" "lk gn"; // derleme HATASI

immutable int denemeSaysSnr = 3; denemeSaysSnr = 4; // derleme HATASI

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

153

const ve immutable

36.2.1

const deiken, immutable parametre yerine kullanlamaz


Her ikisi de deimezlii belirliyor olsalar da, const deikenler ilevlere immutable parametre olarak gnderilemezler: void immutableAlanlev(immutable int[] birDizi) { // ... } void main() { const int[] constDizi; immutable int[] immutableDizi; immutableAlanlev(constDizi); immutableAlanlev(immutableDizi); // derleme HATASI // alr

Bu konunun benzeriyle daha nceki derslerde de karlatk. Hatrlarsanz, dizgi trlerinin takma isimlerinin asl trlerinin immutable olduklarndan bahsetmitik: immutable(char)[] 'n takma ismi string immutable(wchar)[] 'n takma ismi wstring immutable(dchar)[] 'n takma ismi dstring Ek olarak, dizgi hazr deerleri de deimezdirler: "merhaba"c hazr dizgisinin tr string 'dir "merhaba"w hazr dizgisinin tr wstring 'dir "merhaba"d hazr dizgisinin tr dstring 'dir

36.3

.dup ve .idup
Bu yzden, dizgileri ilevlere parametre olarak geirirken uyumsuz durumlarla karlaabiliriz. Bu durumlarda dizgilerin .dup ve .idup niteliklerinden yararlanmamz gerekebilir: .dup dizginin deiebilen bir kopyasn oluturur; ismi, "kopyasn al" anlamndaki "duplicate"ten gelir .idup dizginin deimez bir kopyasn oluturur; ismi "immutable duplicate"ten gelir rnein, deiebilen bir dizgi alan bir ilevi bir string ile dorudan aramayz. Bu durumda ilevi .dup ile aldmz kopya ile armamz gerekir: void foo(char[] dizgi) { // ... } void main() { foo("merhaba"); foo("merhaba".dup); }

// derleme HATASI // alr

Parametresinin programn almas sresince kesinlikle deimeyecek olmasn isteyen ve bu yzden de onu immutable olarak belirlemi olan bir ilevi de .idup ile armamz gerekebilir:

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

154

const ve immutable

void foo(string dizgi) { // ... } void main() { char[] selam; foo(selam); foo(selam.idup); }

// derleme HATASI // alr

36.4

Nasl kullanmal
Deiken tanmlarken, programn almas srasnda kesinlikle deimeyecek olan deerleri immutable olarak tanmlayn. rnein bir dakikadaki toplam saniye says deimeyecektir: immutable int dakikaBanaSaniye = 60; Parametre tanmlarken; eer parametrede bir deiiklik yaplmayacaksa, parametreyi const olarak tanmlayn. yle yaptnzda parametreyi deitirmeme sz verdiiniz iin; deiebilen, const , ve immutable deikenleri o ileve gnderebilirsiniz: void foo(const char[] dizgi) { // ... } void main() { char[] deiebilenDizgi; const char[] constDizgi; string immutableDizgi; foo(deiebilenDizgi); foo(constDizgi); foo(immutableDizgi); // alr // alr // alr

Eer parametrede bir deiiklik yapacaksanz, o parametreyi deiebilen ekilde tanmlayn; zaten const veya immutable tanmladnzda deitiremezsiniz: import std.stdio; void tersevir(char[] dizgi) { const int uzunluk = dizgi.length; for (int i = 0; i != uzunluk / 2; ++i) { const char geici = dizgi[i]; dizgi[i] = dizgi[uzunluk - 1 - i]; dizgi[uzunluk - 1 - i] = geici; }

void main() { char[] selam = "merhaba".dup; tersevir(selam); writeln(selam); }

abahrem

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

155

const ve immutable

Genel bir kural olarak, olabildii kadaryla hep const tarafta kalmaya aln. rnein ilevin iinde deitirilmeyen parametreleri const olarak tanmlayn. Geerli bir nedeni yoksa, ilev parametrelerinde immutable kullanmayn. Yoksa ilevinizin kullanll der. Bundan sonraki blmlerdeki rneklerde ben de bol bol const ve immutable kullanacam.

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

156

lev Parametreleri

37

lev Parametreleri
Bu derste parametrelerin ilevlere gnderilmeleri konusundaki nemli ayrntlar gstereceim ve D'deki parametre eitlerini tantacam. Aslnda bu konunun bir ksmna foreach Dngs dersinde ref anahtar szcn anlatrken ok ksaca deinmitik: o szck kullanlmadnda, foreach iinde dizi elemanlarnn kendilerine deil, kopyalarna eriiyorduk. Ek olarak, const ve immutable 'n parametrelerle kullanmn da bir nceki derste grmtk. nceki programlarda ilevlerin parametrelerini kullanarak nasl sonu rettiklerini grdk. rnein hibir yan etkisi olmayan ve ii yalnzca deer retmek olan bir ilev yle yazlabiliyordu: double seneSonuNotu(double vizeNotu, double finalNotu) { const double sonu = vizeNotu * 0.4 + finalNotu * 0.6; return sonu; } O ilevde vize notu arlnn %40, final notununkinin de %60 olarak hesaplandn grebiliyoruz. O ilevi rnein u ekilde arabiliriz: int vizeOrtalamas = 76; int finalNotu = 80; writefln("Sene sonu notu: %2.0f", seneSonuNotu(vizeOrtalamas, finalNotu));

37.1

ou parametre kopyalanr
Yukardaki kodun vizeOrtalamas ve finalNotu deikenlerini kullandn sylediimizde aslnda temelde bir hataya dm oluruz; nk aslnda ilev tarafndan kullanlanlar deikenlerin kendileri deil, kopyalardr. Bu ayrm nemlidir; nk parametrede yaplan deiiklik ancak kopyay etkiler. Bunu yan etki retmeye alan aadaki ilevde grebiliriz. Bir oyun karakterinin enerjisini drmek iin yazlm olsun: void enerjisiniDr(double enerji) { enerji /= 4; } O ilevi denemek iin yazlm olan u programa bakalm: import std.stdio; void enerjisiniDr(double enerji) { enerji /= 4; } void main() { double enerji = 100; enerjisiniDr(enerji);

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

157

lev Parametreleri

writeln("Yeni enerji: ", enerji);

Yeni enerji: 100

Deimedi

enerjisiniDr ilevi, parametresinin deerini drtte birine drd halde main iindeki enerji isimli deikenin deeri ayn kalmaktadr. Bunun nedeni, main iindeki enerji ile enerjisiniDr ilevinin parametresi olan enerji 'nin farkl deikenler olmalardr. Parametre olan, main iindeki deikenin kopyasdr. Bu olay biraz daha yakndan incelemek iin programa baz kt ifadeleri yerletirebiliriz: import std.stdio; void enerjisiniDr(double enerji) { writeln("leve girildiinde enerji /= 4; writeln("levden klrken } void main() { double enerji = 100; writeln("levi armadan nce : ", enerji); enerjisiniDr(enerji); writeln("levden dnldkten sonra: ", enerji);

: ", enerji); : ", enerji);

levi armadan nce : leve girildiinde : levden klrken : levden dnldkten sonra:

100 100 25 100

parametre deiir, asl enerji deimez

ktdan anlald gibi, isimleri ayn olsa da, main iindeki enerji ile enerjisiniDr iindeki enerji farkl deikenlerdir. leve main iindeki deikenin deeri kopyalanr ve deiiklik bu kopyay etkiler. Bu, ilerideki derslerde greceimiz yap nesnelerinde de byledir: yap nesneleri de ilevlere kopyalanarak gnderilirler.

37.2

Dizi ve snf nesnesi olan parametreler kopyalanmazlar


Diziler ve daha sonraki derslerde greceimiz snf nesneleri, parametrelerin kopyalanmalar kuralna uymazlar. Bu tr deikenler ilevlere referans olarak geirilirler. Referans olarak geirilen parametre, asl nesnenin bir takma ismi gibi kullanlr ve parametrede yaplan deiiklik asl nesneyi deitirir. Dizgiler de dizi olduklarndan, bu durum onlar iin de geerlidir. Parametresinde deiiklik yapan u ileve bakalm: import std.stdio; void baHarfiniNoktaYap(char[] dizgi) { dizgi[0] = '.'; }

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

158

lev Parametreleri

void main() { char[] dizgi = "abc".dup; baHarfiniNoktaYap(dizgi); writeln(dizgi); } Parametrede yaplan deiiklik, main iindeki asl nesneyi deitirmitir: .bc nk; ilevin parametresi olan dizgi , main iindeki asl dizgi 'nin takma ismidir.

37.3

Parametre eitleri
Parametrelerin ilevlere geirilmeleri normalde yukardaki iki temel kurala uyar: diziler, snf nesneleri ve daha sonra greceimiz referans trleri referans olarak geirilirler dier trler kopyalanrlar Bunlar, bir belirte kullanlmad zaman varsaylan kurallardr. Aadaki anahtar szckleri kullanarak bu kurallar etkileyebiliriz.

37.3.1

in
levlerin deer veya yan etki reten dzenekler olduklarn kabul etmi ve o dzenein ileyiini parametrelerin belirlediini grmtk. in anahtar szc, parametrenin bu dzenekte yalnzca bir giri bilgisi olarak kullanlacan belirler. Bu tr parametreler ilevde yalnzca bilgi olarak kullanlrlar, kendileri deitirilemezler. in bu adan const 'a benzese de, ngilizce'de "ieriye" anlamna geldii iin, parametrenin amacn daha ak bir ekilde ifade eder: import std.stdio; double arlklToplam(in double imdikiToplam, in double arlk, in double eklenecekDeer) { return imdikiToplam + (arlk * eklenecekDeer); } void main() { writeln(arlklToplam(1.23, 4.56, 7.89)); } in parametreler deitirilemezler: void deneme(in int deer) { deer = 1; // derleme HATASI } Giri amacyla kullanlan parametreleri in anahtar szc ile belirtmenizi neririm. levlerinizin daha okunakl ve daha gvenli olmalarna yardm eder.

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

159

lev Parametreleri

37.3.2

out
levin rettii bilginin ilevden dn deeri olarak dndrldn grmtk. levlerden yalnzca tek bir bilgi dndrlebiliyor olmas, bazen kstlayc olabilir; bazen ilevin birden fazla sonu retmesini isteyebiliriz. "Darya" anlamna gelen out belirteci, ilevlerin parametreleri yoluyla da sonu retmelerini salar. lev, bu tr parametrelerin deerlerini atama yoluyla deitirdiinde, o deerler ilevi aran ortamda da sonu olarak grlebilirler. Bir anlamda bilgi ilevden darya gnderilmektedir. Not: Aslnda dn tr olarak birden fazla deer de dndrlebilir. Bunu, dn trn bir Tuple veya struct tr olarak belirleyerek gerekletirebiliriz. Bunlar da ilerideki derslerde greceiz. rnek olarak iki sayy blen, ve hem blm hem de kalan reten bir ileve bakalm. levin dn deerini blm dndrmek iin kullanrsak, kalan da bir out parametre olarak dndrebiliriz: import std.stdio; int kalanlBl(in int blnen, in int blen, out int kalan) { kalan = blnen % blen; return blnen / blen; } void main() { int blm; int kalan; blm = kalanlBl(7, 3, kalan); } writeln("blm: ", blm, ", kalan: ", kalan);

kalan parametresini ilev iinde deitirmek, main iindeki kalan 'n deimesine neden olmaktadr: blm: 2, kalan: 1 out parametreler yalnzca darya bilgi verirler; o parametrelerin deeri aran tarafta ne olursa olsun, ileve girildiinde her zaman iin trnn ilk deerine dnr: import std.stdio; void deneme(out int parametre) { writeln("leve girildiinde } void main() { int deer = 100; writeln("lev arlmadan nce: ", deer); deneme(deer); writeln("levden dnldnde : ", deer);

: ", parametre);

O ilevde parametreye hibir deer atanmyor bile olsa, ileve girildiinde parametrenin deeri int 'in ilk deeri olmakta ve bu main iindeki deeri de etkilemektedir:

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

160

lev Parametreleri

lev arlmadan nce: 100 leve girildiinde : 0 levden dnldnde : 0

int'in ilk deerinde

Yani out parametreler dardan bilgi alamazlar, yalnzca darya bilgi gnderebilirler. out parametreler yerine, dn tr olarak Tuple veya struct trleri kullanmay da dnebilirsiniz. O olanaklar, birden fazla bilgiyi bir arada dndrmeyi salarlar.
37.3.3

const
Bir nceki derste de grdmz gibi, parametrenin ilev ierisinde deitirilmeyecei garantisini verir. Bu sayede, ilevi aranlar hem parametrede deiiklik yaplmadn bilmi olurlar, hem de ilev const veya immutable olan deikenlerle de arlabilir: import std.stdio; char sonHarfi(const char[] dizgi) { return dizgi[$ - 1]; } void main() { writeln(sonHarfi("sabit")); } Burada bir seim yapmak durumundasnz: in ve const 'n ikisi de parametre listesinde ayn anlama geldikleri iin birisinde karar klmak isteyebilirsiniz. in 'in yarar, anlam "ieriye" olduu iin amacn daha ak bir ekilde ifade etmesidir. const 'n yarar ise, parametre listeleri dnda da kullanlabilen bir anahtar szck olduu iin deimezlik kavramn belirtme konusunda tutarllk salyor olmasdr. Seim sizin...

37.3.4

immutable
Bir nceki derste de grdmz gibi, parametrenin programn almas sresince deimemesini art koar. Bu konuda srarl olduu iin, aadaki ilevi ancak immutable dizgilerle arabiliriz (rnein dizgi hazr deerleriyle): import std.stdio; dchar[] kartr(immutable dchar[] birinci, immutable dchar[] ikinci) { dchar[] sonu; int i; for (i = 0; (i < birinci.length) && (i < ikinci.length); ++i) { sonu ~= birinci[i]; sonu ~= ikinci[i]; } sonu ~= birinci[i..$]; sonu ~= ikinci[i..$]; } return sonu;

void main() {

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

161

lev Parametreleri

writeln(kartr("MERHABA", "dnya"));

Kstlayc bir belirte olduu iin, immutable ' ancak gerekten gereken durumlarda ve kendi ilevlerinizde belki de hibir zaman kullanmayacaksnz.
37.3.5

ref
leve normalde kopyalanarak geirilecek olan bir parametrenin referans olarak geirilmesini salar. Yukarda parametresi normalde kopyaland iin istediimiz gibi almayan enerjisiniDr ilevinin; main iindeki asl deikeni deitirebilmesi iin, parametresini referans olarak almas gerekir: import std.stdio; void enerjisiniDr(ref double enerji) { enerji /= 4; } void main() { double enerji = 100; enerjisiniDr(enerji); writeln("Yeni enerji: ", enerji);

Bu sayede; ilev parametresinde yaplan deiiklik, main iindeki enerji 'nin deerini deitirir: Yeni enerji: 25 Grld gibi, ref parametreler ilev iinde hem kullanlmak zere giri bilgisidirler, hem de sonu retmek zere k bilgisidirler... ref parametreler ilevlerin yan etki reten trden ilevler olmalarna neden olurlar: Dikkat ederseniz, enerjisiniDr ilevi deer retmiyor, parametresinde bir deiiklik yapyor. levsel programlama [functional programming] denen programlama mantnda yan etkilerin zellikle azaltlmasna allr; hatta baz programlama dillerinde yan etkilere izin verilmez bile. Deer reten ilevlerin yan etkisi olan ilevlerden programclk asndan daha stn olduklar kabul edilir. Ben de, ilevlerinizi eer olabiliyorsa deer retecek ekilde tasarlanamnz neririm. levlerin yan etkilerini azaltmak, programlarnzn gvenilirlii asndan etkili olacaktr. Ayn ii fonksiyonel programlama manta uygun olacak ekilde gerekletirmek iin, yani deer reten bir ilev kullanacak ekilde yazmak iin, program yle deitirmek nerilir: import std.stdio; double dkEnerji(double enerji) { return enerji / 4; } void main() { double enerji = 100;

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

162

lev Parametreleri

enerji = dkEnerji(enerji); writeln("Yeni enerji: ", enerji);

37.3.6

lazy
imdiye kadar, parametre deerlerinin ilevlere gnderilmeden nce iletildiklerini syleme gerei duymadm. Bunun size de doal geldiini dnrm. rnein topla gibi bir ilevi baka iki ilevin sonucu ile arsak: sonu = topla(birMiktar(), bakaBirMiktar()); topla 'nn arlabilmesi iin ncelikle birMiktar ve bakaBirMiktar ilevlerinin arlmalar gerekir. Yoksa topla hangi iki deeri toplayacan bilemez. lemlerin bu ekilde doal olarak iletilmeleri hevesli [eager] olarak tanmlanr. Program, ilemleri ncelik sralarna gre hevesle iletir. Oysa baz parametreler, ilevin ileyiine bal olarak belki de hibir zaman kullanlmayacaklardr. Parametre deerlerinin hevesli olarak nceden iletilmeleri, byle parametrelerin gereksiz yere hesaplanm olmalarna neden olacaktr. rnek olarak, parametrelerinden birisinin deerini ancak belirli bir koul salandnda kullanan bir ileve bakalm. Aadaki ilev, istenen saydaki yumurtay ncelikle dolapta bulunanlarla yapmaya alyor; komularda ka yumurta bulunduuna ise ancak dolapta yeterince yumurta bulunmadnda bakyor: void yumurtaYap(in int istenen, in int dolaptaOlan, in int komulardaOlan) { if (istenen <= dolaptaOlan) { writeln("Hepsini dolaptaki yumurtalarla yaparm"); } else if (istenen <= (dolaptaOlan + komulardaOlan)) { writeln("Dolaptaki ", dolaptaOlan, " yumurtaya", " komulardan da ", istenen - dolaptaOlan, " yumurta eklerim"); } else { writeln("O kadar yumurta yapamam"); }

Buna ek olarak, komularda bulunan yumurta adedinin bir hesap sonucunda bilinebildiini varsayalm. Komulardaki yumurta says bir deikenin deeri olarak hemen bilinmek yerine, belirli bir hesap sonrasnda bulunuyor olsun: int toplamYumurta(in int[string] adetler) { int toplam; foreach (sahip, adet; adetler) { writeln(sahip, ": ", adet, " tane"); toplam += adet; } } return toplam;

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

163

lev Parametreleri

O ilev; kendisine verilen bir eleme tablosunda ilerliyor, yumurta sahibini ve o kiide ka yumurta bulunduunu ktya yazdryor, ve btn yumurtalarn toplamn dndryor. imdi yumurtaYap ilevini toplamYumurta 'y kullanarak yle arabiliriz: void main() { int[string] komulardakiYumurtalar = [ "Zmrt Teyze":5, "Yakup Abi":3, "Veli Efendi":7 ]; } yumurtaYap(2, 5, toplamYumurta(komulardakiYumurtalar));

Programn ktsnda grld gibi, nce toplamYumurta ilevi iletilmektedir ve ondan sonra yumurtaYap arlmaktadr: Zmrt Teyze: 5 tane Veli Efendi: 7 tane komulardaki yumurta hesab Yakup Abi: 3 tane Hepsini dolaptaki yumurtalarla yaparm Ancak bu durumda gereksiz bir ilem var: sonuta btn yumurtalar dolaptakilerle yaplabilecek olduu halde, gereksizce komulardaki yumurtalar da saym olduk. Kelime anlam "tembel" olan lazy , bir parametrenin ilev iinde belki de hi iletilmeyeceini bildirmek iin kullanlr. yumurtaYap ilevinin komulardaOlan parametresinin her durumda hesaplanmasna gerek olmadn ve ancak gerektiinde hesaplanabileceini bildirmek iin, onu lazy olarak belirleyebiliriz: void yumurtaYap(in int istenen, in int dolaptaOlan, lazy int komulardaOlan) { // ... gerisi ayn ... imdiki ktda grld gibi, istenen yumurta adedinin dolaptakilerden karlanabildii yukardaki durumda, komulardaki yumurta toplam artk hesaplanmamaktadr: Hepsini dolaptaki yumurtalarla yaparm O hesap yine de gerektii zaman yaplacaktr. rnein dolapta bulunanlardan daha fazla yumurta istendiinde: yumurtaYap(9, 5, toplamYumurta(komulardakiYumurtalar)); Bu sefer ilev iinde gerekten gerektii iin, komulardaki yumurta says yine hesaplanmaktadr: Zmrt Teyze: 5 tane Veli Efendi: 7 tane Yakup Abi: 3 tane Dolaptaki 5 yumurtaya komulardan da 4 yumurta eklerim

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

164

lev Parametreleri

37.3.7

lazy parametre, her eriildiinde hesaplanr


lazy parametrelerin deerleri, o deer her kullanldnda tekrar hesaplanr. rnein bu programda lazy olarak belirtilmi olan parametre kere kullanld iin, onu hesaplayan ilev de kere arlmaktadr: import std.stdio; int parametreyiHesaplayanlev() { writeln("Hesap yaplyor"); return 1; } void tembelParametrelilev(lazy int deer) { int sonu = deer + deer + deer; writeln(sonu); } void main() { tembelParametrelilev(parametreyiHesaplayanlev()); }

Hesap yaplyor Hesap yaplyor Hesap yaplyor 3 lazy belirtecini, deeri ancak baz durumlarda kullanlan parametreleri belirlemek iin kullanabilirsiniz. Ancak, deerin birden fazla sayda hesaplanabileceini de unutmamak gerekir. Bu belirtecin salad tembel deerlendirmeler olanana bir sonraki derste de devam edeceim.

37.4

Problem
Kendisine verilen iki parametrenin deerlerini dei toku etmek iin bir ilev yazlm: import std.stdio; void deiToku(int birinci, int ikinci) { int geici = birinci; birinci = ikinci; ikinci = geici; } void main() { int birSay = 1; int bakaSay = 2; deiToku(birSay, bakaSay); } writeln(birSay, ' ', bakaSay);

O program istendii gibi almyor:

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

165

lev Parametreleri

1 2

dei toku olmam

levi dzeltin ve programn ktsnn 2 1 olmasn salayn. ... zm

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

166

Tembel Deerlendirmeler

38

Tembel Deerlendirmeler
Tembel deerlendirmeler, ilemlerin gerekten gerekli olduu zamana kadar geciktirilmesi anlamna gelir. ngilizcesi "lazy evaluation" olan tembel deerlendirmeler, Haskell gibi baz programlama dillerinin de temel olanaklar arasndadr. lemlerin gerekene kadar geciktirilmeleri, doal olarak hz kazanc salayabilir; nk belki de gerekmeyecek olan bir ilem iin batan zaman harcanmam olur. te yandan; bir nceki derste de grdmz gibi, lazy parametrelerin her eriildiklerinde tekrar hesaplanyor olmalar zaman kaybna da neden olabilir. Bu olanak, dikkatli kullanldnda ilgin programlama yntemlerine olanak verir. D'de tembel deerlendirme olana yalnzca lazy belirtecine bal deildir; onu aslnda daha nce grdmz ileten tanyorsunuz: || ileci (veya ileci): kinci ifadeyi ancak birincisi false olduunda iletir if (birfade() || belkiDeletilmeyecekfade()) { // ... } Eer birfade() 'nin sonucu true ise, sonucun da true olaca daha ikinci ifade iletilmeden bellidir. O durumda ikinci ifade iletilmez. && ileci (ve ileci): kinci ifadeyi ancak birincisi true olduunda iletir if (birfade() && belkiDeletilmeyecekfade()) { // ... } Eer birfade() 'nin sonucu false ise, sonucun da false olaca daha ikinci ifade iletilmeden bellidir. O durumda ikinci ifade iletilmez. ?: ileci (l ile): koul true olduunda nceki ifade, false olduunda sonraki ifade iletilir int i = birKoul() ? yaBufade() : yaDaBufade(); birKoul() 'un sonucuna gre ifadelerden birisi gerekmedii iin hi iletilmez. lazy anahtar szc, bu ilecin dnda da tembel deerlendirmeler kullanmay salar. Yararlarndan birisi, yukarda da grdmz gibi ilemlerin gereksizce iletilmelerinin nne gemesidir. Bu, program hz asndan nemli olabilir. Dier ve belki de daha nemli yarar, ilemlerin baz admlarnn sabit olmak yerine, ileve dardan verilebilmesidir. Bu sayede, ilevin davran deitirilebilmektedir. Bunun bir rneini grmek iin nce lazy kullanmayan bir programa bakalm: import std.stdio; import std.random; void selamVer(in int adet, const char[] selam) { foreach (i; 0 .. adet) { writeln(selam, " dnya!"); } } string rasgeleSelamSe() {

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

167

Tembel Deerlendirmeler

string[] selamlar = [ "Merhaba", "Selam", "Gnaydn", "yi akamlar" ]; return selamlar[uniform(0,$)];

void main() { selamVer(5, rasgeleSelamSe()); } O programda selamVer ilevinin kulland selam parametresinin deeri hevesli olarak hesapland iin, selam ifadesi daha selamVer ilevi arlmadan nce main iinde belirlenir ve bu yzden de rasgele seilen selam be kere tekrarlanm olur. rnek: yi yi yi yi yi akamlar akamlar akamlar akamlar akamlar dnya! dnya! dnya! dnya! dnya!

Not: Mesaj programn her altrlnda rasgele seildii iin, be kere tekrarlanan mesajn programn her altrlnda farkl olmas normaldir. Oysa, parametrenin tanmnda yaplan kk bir deiiklik, selamn her yazdrlnda rasgele seilmesini salar: void selamVer(in int adet, lazy const char[] selam) {

Merhaba dnya! Selam dnya! Merhaba dnya! yi akamlar dnya! Selam dnya! Bunun nedeni, foreach dngs iinde kullanlan selam deerinin tembel bir ekilde her seferinde gerektike hesaplanmasdr. Bu, programlama asndan son derece gl bir olanaktr. lazy kullanlmadnda, ileve "al bu selam kullan" demi gibi oluruz. lazy kullandmzda ise, "selam gerektii zaman u ilevi ar ve onun dndrd dizgiyi kullan" demi gibi oluruz. lazy bildirimi bir anlamda ilevin davrann deitirmektedir. Bu rnekte sonunda " dnya!" olan be mesaj yazdrlaca kesindir; ama mesajn ba tarafna ne yazdrlacann seimi, ileve parametre olarak verilmektedir. Buradaki fark nemli: ileve bir deer deil, o deerin nasl hesaplanaca verilmektedir. Yani parametresi bir deer deildir, bir hesaplama ilemidir.
38.0.1

rnek
levin davrann deitirmenin bir rnei olarak bilgisayar ve kullancnn zar attklar bir oyun dndm. Att iki zarn toplam daha byk olan taraf puan kazanyor. Programn banda, bilgisayarn hile yapp yapmayaca soruluyor. O koula bal olarak; bilgisayar ya hileli zarlar ya da rasgele zarlar atyor:

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

168

Tembel Deerlendirmeler

hileYapsn ? hileliZarlar() : rasgeleZarlar(), Yukardaki iki ilevden duruma gre birisi zarOyunu ilevine lazy parametre olarak gnderiliyor ve btn oyun, parametre olarak verilen bu davrana gre oynatlyor. Not: Umarm ?: ilecinin kullanlmas karkla neden olmaz. O ile en bata hangi ilevin kullanlacana karar vermek iin kullanlyor. Seilen ilevi kendisi iletmiyor; nk setii ilev zarOyunu ilevine bir lazy parametre olarak gnderiliyor. Dier aklamalar programn iine yazdm: import std.stdio; import std.string; import std.random; /* * 1 ve 6 arasnda eit dalml bir deer dndrr */ int rasgeleZar() { return uniform(1, 7); } /* * 5 ve 6 arasnda eit dalml bir deer dndrr */ int hileliZar() { return uniform(5, 7); } /* * ki tane rasgele zarn toplamn dndrr */ int rasgeleZarlar() { return rasgeleZar() + rasgeleZar(); } /* * Birisi hileli olan iki zarn toplamn dndrr */ int hileliZarlar() { return rasgeleZar() + hileliZar(); } void bilgiVer(in int oyunSays, in int bilgisayarnAt, in int oyuncununAt, in int bilgisayarnPuan, in int oyuncununPuan) { writefln( "%3d: bilgisayar %2d %2d oyuncu skor:%3d - %d", oyunSays, bilgisayarnAt, oyuncununAt, bilgisayarnPuan, oyuncununPuan); } void zarOyunu(in int adet, lazy int bilgisayarnZarAt, lazy int oyuncununZarAt) { int bilgisayarnPuan; int oyuncununPuan; foreach (i; 0 .. adet) {

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

169

Tembel Deerlendirmeler

/* * lazy parametrelerin deerlerinin yalnzca bir kere * hesaplanmalar iin onlara bu dng iinde bir kere * erimemiz gerekir. Eritiimiz deerleri iki yerel * deikende saklyoruz. */ const int bilgisayarnBuAt = bilgisayarnZarAt; const int oyuncununBuAt = oyuncununZarAt; if (bilgisayarnBuAt > oyuncununBuAt) { ++bilgisayarnPuan; } else if (bilgisayarnBuAt < oyuncununBuAt) { ++oyuncununPuan; } bilgiVer(i + 1, bilgisayarnBuAt, oyuncununBuAt, bilgisayarnPuan, oyuncununPuan);

bool bilgisayarHileYapsn_m() { write("Bilgisayar hile yapsn m? (E/H) "); string yant = chomp(readln()); } return (yant == "e") || (yant == "E");

void main() { const bool hileYapsn = bilgisayarHileYapsn_m(); zarOyunu(10, hileYapsn ? hileliZarlar() : rasgeleZarlar(), } rasgeleZarlar());

levlere deer gndermenin yannda davran da gnderilebildii kavramn imdiden anlarsanz, D'nin daha sonra greceimiz class , function , ve delegate gibi fonksiyonel programlama ve nesneye ynelik programlama olanaklarn da daha kolay anlayacaksnz.

38.1

Problemler
1. Bir int dizisi ve bir deer alan bir ilev yazn. Dizinin tek sayl indekslerindeki (1, 3, 5, vs.) elemanlarnn deerleri o deerle deitirilsin. rnein [0,11,22,33,44,55] gibi bir dizi ve 7 gibi bir deer verildiinde dizi u hale gelsin: [0,7,22,7,44,7] . Bu ilevi lazy parametre kullanmadan yazabilirsiniz. 2. Ayn ilevin deer belirtmek iin kullanlan parametresini lazy olarak belirleyerek, hem bir nceki zmde olduu gibi 7 gibi bir deerle, hem de rnein 100 ve 200 arasnda rasgele bir deerle arlabilmesini salayn. Bu sefer deer alan parametreyi lazy yaparak ona hem sabit bir deer, hem de bir ilev verebilirsiniz. ... zmler

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

170

main'in Parametreleri ve Dn Deeri

39

main 'in Parametreleri ve Dn Deeri


levleri anlatrken main 'in de bir ilev olduunu sylemitim. Programn ileyii main 'le balar ve oradan baka ilevlere dallanr. main 'in imdiye kadar grdmz tanm yleydi: void main() O tanma bakarak main 'in bir deer dndrmediini ve hibir parametre almadn dnrz. Aslnda bu mmkn deildir, nk program balatan ortam bir dn deeri bekler; main , void dndryor gibi yazlm olsa da aslnda bir deer dndrr.

39.1

main 'in dn deeri


Programlar her zaman iin baka bir ortam tarafndan balatlrlar. Bu ortam, program ismini yazp Enter'a basarak balattmz u birim olabilir, menlerindeki "altr" gibi bir komutla balattmz bir gelitirme ortam olabilir, program kendisi balatan baka bir program olabilir, vs. Programmz, kendisini balatan bu ortama iini baaryla tamamlayp tamamlamad bilgisini, main 'in dn deeri olarak bildirir. Programn dn deeri olarak 0 deeri, programn baaryla sonulandn; 0'dan baka bir deer ise programn almas srasnda bir hata olutuunu bildirmek iin kullanlr. Programlarmzdan istediimiz deeri dndrmek bize kalm olsa da, 0'n baar anlamna gelmesi standartlamtr. Not: Dn deeri olarak ancak [0,127] aralndaki tamsaylara gvenebilirsiniz. Bunun dndaki deerler her ortam tarafndan desteklenmiyor olabilir. Sfrdan baka deerler her programa gre deiik anlamlar tayabilir. rnein Unix trevi ortamlarda dosyalar listelemek iin kullanlan ls program; nemsiz hatalarda 1 deerini, ciddi hatalarda ise 2 deerini dndrr. Komut satrndan balatlan programlarn dn deerleri $? ortam deikeninden okunabilir. rnein klasrde bulunmayan bir dosyay grmek istediimizde, programn dn deerini komut satrnda $? deikeninden yle okuyabiliriz: Not: Aadaki komut satr rneklerinde # karakteriyle balayan satrlar, kullancn yazd satrlar gsteriyor. Ayn admlar denemek istediinizde o satrlar sizin yazarak Enter'a basmanz gerekir. O satrlar daha koyu olarak gsterdim. Ek olarak, aadaki rnekler bir Linux ortamnda denenmi olsalar da, benzerlerini rnein Windows DOS pencerelerinde de kullanabilirsiniz. # ls klasorde_bulunmayan_bir_dosya ls: klasorde_bulunmayan_bir_dosya: No such file or directory # echo $? 2 programn dn deeri

39.1.1

Dn deeri void olan main 'ler de deer retirler


imdiye kadar karlatmz ilevlerin bazlarnn ilerini yapamayacaklar durumlara dtklerinde hata attklarn grmtk. imdiye kadar grdmz kadaryla, hata atld zaman program bir object.Exception mesajyla sonlanyordu.

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

171

main'in Parametreleri ve Dn Deeri

Bu gibi durumlarda, main 'in dn tr olarak void kullanlm olsa bile, yani main bir deer dndrmeyecekmi gibi yazlm olsa bile, program altran ortama 1 deeri dndrlr. Bunun bir rneini grmek iin hata atarak sonlanan u son derece basit program altralm: void main() { throw new Exception("Bir hata oldu"); } Dn tr void olarak tanmland halde program altran ortama 1 deeri dndrlmtr: # ~/deneme/d/deneme object.Exception: Bir hata oldu # echo $? 1 Benzer ekilde, dn tr void olarak tanmlanm olan main ilevleri baaryla sonlandklarnda, dn deeri olarak 0 retirler. Yine son derece basit, ama bu sefer baaryla sonlanan bir program: import std.stdio; void main() { writeln("Baardm!"); }

# ~/deneme/d/deneme Baardm! # echo $? 0

39.1.2

Kendi dn deerlerimizi belirlemek


Kendi programlarmzdan deer dndrmek; baka ilevlerde de olduu gibi, main 'i dn tr int olacak ekilde tanmlamak ve bir return satr kullanmak kadar basittir: import std.stdio; int main() { int say; write("Ltfen 3-6 arasnda bir say giriniz: "); readf(" %s", &say); if ((say < 3) || (say > 6)) { stderr.writeln("HATA: ", say, " uygun deil!"); return 3; } writeln("Teekkr: ", say); } return 0;

Programn isminin deneme olduunu kabul edersek ve istenen aralkta bir sayyla balatrsak, programn dn deeri 0 olur:

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

172

main'in Parametreleri ve Dn Deeri

# ./deneme Ltfen 3-6 arasnda bir say giriniz: 5 Teekkr: 5 # echo $? 0 Araln dnda bir deerle balatrsak, programdan dndrdmz 3 deerini grrz: # ~/deneme/d/deneme Ltfen 3-6 arasnda bir say giriniz: 10 HATA: 10 uygun deil! # echo $? 3 Normalde hata iin 1 deerini kullanmak yeterlidir. Ben yalnzca rnek olmas iin, ve zel bir nedeni olmadan 3 deerini setim.

39.2

Standart hata akm stderr


Yukardaki programda stderr akmn kullandm. Bu akm, standart akmlarn ncsdr ve programn hata mesajlarn yazmak iin kullanlr: stdin : standart giri akm stdout : standart k akm stderr : standart hata akm Programlar komut satrndan balatldklarnda stdout ve stderr akmlarna yazlanlar normalde ekranda belirirler. Bu akmlara yazlan mesajlar istendiinde ayr ayr elde etmek de mmkndr.

39.3

main 'in parametreleri


Baz programlar kendilerini balatan ortamlardan parametre alabilirler. rnein yukarda grdmz ls program bazen parametresiz olarak yalnzca ls yazarak balatlabilir: # ls deneme deneme.d stee bal olarak da bir veya daha ok parametreyle balatlabilir. Bu parametrelerin anlamlar btnyle programa baldr ve o programn belgelerinde belirtilmitir: # ls -l deneme -rwxr-xr-x 1 acehreli users 460668 Nov

6 20:38 deneme

D programlarn balatrken kullanlan parametreler, main 'e bir string dizisi olarak gnderilirler. main 'i string[] parametre alacak ekilde tanmlamak, bu parametrelere erimek iin yeterlidir. rnein balatlrken kendisine verilen parametrelerini ktsna yazdran bir program yle yazlabilir: import std.stdio; void main(string[] parametreler)

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

173

main'in Parametreleri ve Dn Deeri

foreach (i, parametre; parametreler) { writefln("%3s numaral parametre: %s", i, parametre); }

# ./deneme komut satirina yazilan parametreler 42 -bir_secenek 0 numaral parametre: ./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, her zaman iin program balatlrken kullanlan program ismidir. Dier parametreler, bu dizinin geri kalan elemanlardr. Bu parametrelerle ne yapaca, artk tamamen programa kalmtr. rnein kendisine verilen iki szc ters srada yazdran bir program: import std.stdio; int main(string[] parametreler) { if (parametreler.length != 3) { stderr.writeln("HATA! Doru kullanm:\n ", parametreler[0], " bir_szck baka_szck"); return 1; } writeln(parametreler[2], ' ', parametreler[1]); } return 0;

Program, yanl balatldnda uyar da veriyor: # ./deneme HATA! Doru kullanm: ./deneme bir_szck baka_szck # ./deneme dnya merhaba merhaba dnya

39.4

Program seenekleri ve std.getopt modl


main 'in parametreleriyle ve dn deeriyle ilgili olarak bilinmesi gerekenler aslnda bu kadardr. Ancak parametreleri teker teker listeden ayklamak zahmetli olabilir. Onun yerine, bu konuda yardm alabileceimiz std.getopt modlnn bir kullanmn gstereceim. Baz parametreler, program tarafndan bilgi olarak kullanlrlar. rnein yukardaki programa verilen "dnya" ve "merhaba" parametreleri, o programn ekrana yazdraca bilgiyi belirliyordu. Baz parametreler ise programn iini nasl yapacan belirlerler; bunlara program seenei denir. rnein yukarda kullandmz ls programna, komut satrnda seenek olarak -l vermitik.

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

174

main'in Parametreleri ve Dn Deeri

Program seenekleri, programlarn kullanllklarn arttrr. Programn istedii parametreler, teker teker bir kii tarafndan verilmek yerine komut satrndan okunurlar. Bylece bu programlar rnein betik programlar iinden arlabilirler. Komut satr parametrelerinin ne olduklar ve ne anlama geldikleri her ne kadar tamamen programa bal olsalar da, onlarn da bir standard gelimitir. POSIX standardna uygun bir kullanmda, her seenek -- ile balar, seenek ismi bunlara bitiik olarak yazlr, ve seenek deeri de bir = karakterinden sonra gelir: # ./deneme --bir_secenek=17 Phobos'un std.getopt modl, programa parametre olarak verilen bu tr seeneklerin ayklanmasnda yardmc olur. Ben burada fazla ayrntya girmeden yalnzca getopt ilevinin bir kullanmn gstereceim. kna rasgele setii saylar yazdran bir program tasarlayalm. Ka tane say yazdraca ve saylarn deerlerinin hangi aralkta olaca programa komut satrndan seenekler olarak verilsin. Program rnein u ekilde balatlabilsin: # ./deneme --adet=7 --en-kucuk=10 --en-buyuk=15 getopt ilevi, bu deerleri program parametrelerinden seip kartmakta yararldr. getopt 'a okuduu deerleri yazaca deikenleri, readf kullanmndan tandmz & karakteriyle bir gsterge olarak bildiririz: import std.stdio; import std.getopt; import std.random; void main(string[] parametreler) { int adet; int enKkDeer; int enBykDeer; getopt(parametreler, "adet", &adet, "en-kucuk", &enKkDeer, "en-buyuk", &enBykDeer); foreach (i; 0 .. adet) { write(uniform(enKkDeer, enBykDeer + 1), ' '); } } writeln();

# ./deneme --adet=7 --en-kucuk=10 --en-buyuk=15 11 11 13 11 14 15 10 ou zaman parametrelerin kestirmeleri de olur. rnein --adet yazmak yerine ksaca -a yazlr. Seeneklerin kestirmeleri, getopt 'a | ayracndan sonra bildirilir: getopt(parametreler, "adet|a", &adet, "en-kucuk|k", &enKkDeer, "en-buyuk|b", &enBykDeer); Artk program bu kestirme seeneklerle de balatabiliriz:

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

175

main'in Parametreleri ve Dn Deeri

# ./deneme -a=7 -k=10 -b=15 11 13 10 15 14 15 14 Parametrelerin programa string trnde geldiklerini grmtk. getopt , bu dizgileri deikenlerin trlerine otomatik olarak dntrr. rnein yukardaki programdaki adet 'in int olduunu bildii iin, onu string 'den int 'e evirir. getopt 'u kullanmadmz zamanlarda bu dnm daha nce de bir ka kere kullandmz to ileviyle kendimiz de gerekletirebiliriz. std.conv modlnde tanmlanm olan to 'yu daha nce hep tamsaylar string 'e dntrmek iin to!string eklinde kullanmtk. string yerine baka bir tr yazarsak, baka trlere de dntrebiliriz. rnein 0'dan balayarak kendisine komut satrndan bildirilen sayda ikier ikier sayan bir programda to 'yu yle kullanabiliriz: import std.stdio; import std.conv; void main(string[] parametreler) { // Parametre verilmediinde 10 varsayyoruz int adet = 10; if (parametreler.length > 1) { // Bir parametre verilmi adet = to!int(parametreler[1]); } foreach (i; 0 .. adet) { write(i * 2, ' '); } } writeln();

Program; parametre verilmediinde 10 adet, verildiinde ise belirtildii kadar say retir: # 0 # 0 ./deneme 2 4 6 8 10 12 14 16 18 ./deneme 3 2 4

39.5

Ortam deikenleri
Bu konuyla dorudan ilgili olmasa da, d dnyadan programa verilen bilgiler olduklar iin ortam deikenlerine de ksaca deinmek istiyorum. Programlar balatan ortamlar, programlarn yararlanmalar iin ortam deikenleri de sunarlar. Bu deikenlere std.process modlndeki getenv ileviyle eriilir. rnein altrlacak olan programlarn hangi klasrlerde arandklarn belirten PATH deikenini yazdrmak iin: import std.stdio; import std.process; void main() { writeln(getenv("PATH")); }

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

176

main'in Parametreleri ve Dn Deeri

# ./deneme /usr/local/bin:/usr/bin:/home/acehreli/dmd/linux/bin

39.6

Baka programlar balatmak


Yine bu konuyla dorudan ilgili olmasa da; programlarn baka programlar balatmalarn, ve o programlarn dn trlerini almalarn salayan system ilevine de deinmek istiyorum. Bu ilev de std.process modlnde bulunur. system , kendisine parametre olarak verilen dizgiyi sanki komut satrnda yazlm gibi balatr ve programn dn deerini dndrr: import std.stdio; import std.process; void main() { const int dnDeeri = system("ls -l deneme"); } writeln("ls program ", dnDeeri," deerini dndrd");

# ./deneme -rwxr-xr-x 1 acehreli users 461107 Nov ls program 0 deerini dndrd

7 17:33 deneme

39.7

Problemler
1. Komut satrndan parametre olarak iki deer ve bir ilem karakteri alan bir hesap makinesi yazn. yle alsn: # ./deneme 3.4 x 7.8 26.52 Not: * karakterinin komut satrnda zel bir anlam olduu iin ben arpma iin x karakterini setim. Siz yine de * kullanmak isterseniz, komut satrnda \* eklinde yazmanz gerekir. 2. Hangi program balatmak istediinizi soran, bu program balatan ve dn trn bildiren bir program yazn. ... zmler

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

177

Hata Atma ve Yakalama

40

Hata Atma ve Yakalama


Beklenmedik durumlar, programlarn yaamlarnn doal paralardr. Kullanc hatalar, programc hatalar, ortamdaki beklenmedik durumlar, vs. programlarn almalar srasnda her zaman karlalan durumlardr. Bu durumlar bazen normal ileyie devam edilemeyecek kadar vahim olabilir. rnein gereken bir bilgi elde edilemiyordur, eldeki bilgi geersizdir, bir evre aygt almyordur, vs. Byle aresiz kalnan durumlarda D'nin hata atma dzenei kullanlarak ileme son verilir. Devam edilemeyecek kadar kt bir durum rnei olarak, yalnzca drt aritmetik ilemi destekleyen bir ilevin bunlarn dndaki bir ilemle arlmas durumunu dnebilirsiniz. nceki dersin problem zmlerinde de olduu gibi: switch (ilem) { case "+": writeln(birinci + ikinci); break; case "-": writeln(birinci - ikinci); break; case "x": writeln(birinci * ikinci); break; case "/": writeln(birinci / ikinci); break; default: throw new Exception("Geersiz ilem: " ~ ilem); } Yukardaki switch deyiminde case 'lerle belirtilmi olan drt ilem dnda ne yaplaca bilinmemektedir. O yzden deyimin default kapsamnda bir hata atlmaktadr. aresiz durumlarda atlan hata rnekleriyle Phobos'ta da karlarz. rnein bir dizgiyi int trne dntrmek iin kullanlan to!int , int olamayacak bir dizgiyle arldnda hata atar: import std.conv; void main() { const int say = to!int("merhaba"); } "merhaba" dizgisi bir tamsay deer ifade etmedii iin; o program, to!int 'in att bir hatayla sonlanr. # ./deneme std.conv.ConvException@std/conv.d(37): std.conv(1161): Can't convert value `merhaba' of type const(char)[] to type int to!int 'in att yukardaki hatay u ekilde evirebiliriz: "const(char)[] trndeki `merhaba' deeri int trne dntrlemez".

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

178

Hata Atma ve Yakalama

Hata mesajnn ba tarafndaki std.conv.ConvException da hatann trn belirtir. Bu hatann ismine bakarak onun std.conv modl iinde tanmlanm olduunu anlayabiliyoruz. smi de "dnm hatas" anlamna gelen "conversion exception"dan tremi olan ConvException 'dr.

40.1

Hata atmak iin throw


Bunun rneklerini hem yukardaki switch deyiminde, hem de daha nceki derslerde grdk. Anlam "at, frlat" olan throw deyimi, kendisinden sonra yazlan ifadenin deerini bir hata nesnesi olarak atar ve ileme hemen son verilmesine neden olur. throw deyiminden sonraki admlar iletilmez. Bu, hata kavramna uygun bir davrantr: hatalar ilemlere devam edilemeyecek durumlarda atldklar iin, zaten devam etmek sz konusu olmamaldr. Baka bir bak asyla; eer ileme devam edilebilecek gibi bir durumla karlamsak, hata atlacak kadar aresiz bir durum yok demektir. O durumda hata atlmaz ve ilev bir aresini bulur iine devam edebilir.

40.1.1

Exception ve Error hata trleri


throw deyimi ile yalnzca Throwable trnden tremi olan nesneler atlabilir. Buna ramen, programlarda ondan da tremi olan Exception ve Error trleri kullanlr. rnein Phobos'taki hatalar ya Exception snfndan, ya da Error snfndan tremilerdir. Error , giderilemez derecede hatal durumlar ifade eder. O hatann yakalanmas nerilmez. Bu yzden, atacanz hatalar ya dorudan Exception 'dan, ya da ondan treteceiniz daha belirgin trlerden atmanz gerekir. (Not: Snflarla ilgili bir konu olan tremeyi daha sonra greceiz.) Exception nesneleri, kurulurlarken hata mesajn string olarak alrlar. Bu mesaj, dizi birletirme ileci olan ~ ile hata nesnesini attnz noktada oluturmak kolaylk salar: import std.stdio; import std.conv; import std.random; int[] rasgeleZarlarAt(int adet) { if (adet < 0) { throw new Exception( "Geersiz 'adet' deeri: " ~ to!string(adet)); } int[] saylar; foreach (i; 0 .. adet) { saylar ~= uniform(1, 7); } } return saylar;

void main() { writeln(rasgeleZarlarAt(-5)); }

# ./deneme object.Exception: Geersiz 'adet' deeri: -5

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

179

Hata Atma ve Yakalama

40.1.2

Hata atldnda btn kapsamlardan klr


Programn, main ilevinden balayarak baka ilevlere, onlardan da daha baka ilevlere dallandn grmtk. levlerin birbirlerini katmanlar halinde armalar, arlan ilevlerin kendilerini aran ilevlere dnmeleri, ardndan baka ilevlerin arlmalar, vs. bir aacn dallar halinde gsterilebilir. rnein main 'den arlan yumurtaYap adl bir ilev, kendisi malzemeleriHazrla adl baka bir ilevi arabilir, ve o ilev de yumurtaHazrla adl baka bir ilevi arabilir. Oklarn ilev arlar anlamna geldiklerini kabul edersek, byle bir programn dallanmasn u ekilde gsterebiliriz: main | +-- yumurtaYap | | | +-- malzemeleriHazrla | | | | | +- yumurtaHazrla | | +- yaHazrla | | +- tavaHazrla | | | +-- yumurtalarPiir | +-- malzemeleriKaldr | +-- yumurtaYe Toplam 3 alt dzeye dallanan bu program, dallanma dzeylerini deiik miktarlarda girintiyle gsterecek ekilde aadaki gibi yazabiliriz. Tabii bu programda ilevler yararl iler yapmyorlar; burada ama, yalnzca programn dallanmasn gstermek: import std.stdio; import std.conv; void girinti(in int miktar) { foreach (i; 0 .. miktar * 2) { write(' '); } } void balyor(in char[] ilev, in int girintiMiktar) { girinti(girintiMiktar); writeln(" ", ilev, " ilk satr"); } void bitiyor(in char[] ilev, in int girintiMiktar) { girinti(girintiMiktar); writeln(" ", ilev, " son satr"); } void main() { balyor("main", 0); yumurtaYap(); yumurtaYe(); bitiyor("main", 0); } void yumurtaYap() { balyor("yumurtaYap", 1); malzemeleriHazrla(); yumurtalarPiir();

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

180

Hata Atma ve Yakalama

malzemeleriKaldr(); bitiyor("yumurtaYap", 1);

void yumurtaYe() { balyor("yumurtaYe", 1); bitiyor("yumurtaYe", 1); } void malzemeleriHazrla() { balyor("malzemeleriHazrla", 2); yumurtaHazrla(); yaHazrla(); tavaHazrla(); bitiyor("malzemeleriHazrla", 2); } void yumurtalarPiir() { balyor("yumurtalarPiir", 2); bitiyor("yumurtalarPiir", 2); } void malzemeleriKaldr() { balyor("malzemeleriKaldr", 2); bitiyor("malzemeleriKaldr", 2); } void yumurtaHazrla() { balyor("yumurtaHazrla", 3); bitiyor("yumurtaHazrla", 3); } void yaHazrla() { balyor("yaHazrla", 3); bitiyor("yaHazrla", 3); } void tavaHazrla() { balyor("tavaHazrla", 3); bitiyor("tavaHazrla", 3); } Normal ileyii srasnda program u kty retir:

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

181

Hata Atma ve Yakalama

main ilk satr yumurtaYap ilk satr malzemeleriHazrla ilk satr yumurtaHazrla ilk satr yumurtaHazrla son satr yaHazrla ilk satr yaHazrla son satr tavaHazrla ilk satr tavaHazrla son satr malzemeleriHazrla son satr yumurtalarPiir ilk satr yumurtalarPiir son satr malzemeleriKaldr ilk satr malzemeleriKaldr son satr yumurtaYap son satr yumurtaYe ilk satr yumurtaYe son satr main son satr balyor ve bitiyor ilevleri sayesinde iareti ile ilevin ilk satrn, iareti ile de son satrn gsterdik. Program main 'in ilk satryla balyor, baka ilevlere dallanyor, ve en son main 'in son satryla sonlanyor. imdi, program yumurtaHazrla ilevine dolaptan ka yumurta kartacan parametre olarak belirtecek ekilde deitirelim; ve bu ilev eksi bir deer geldiinde hata atsn: void yumurtaHazrla(int adet) { balyor("yumurtaHazrla", 3); if (adet < 0) { throw new Exception("Dolaptan " ~ to!string(adet) ~ " yumurta kartlamaz"); } } bitiyor("yumurtaHazrla", 3);

Programn doru olarak derlenebilmesi iin tabii baka ilevleri de deitirmemiz gerekir. Dolaptan ka yumurta kartlacan ilevler arasnda main 'den balayarak elden elden iletebiliriz. Bu durumda programn dier taraflar da aadaki gibi deitirilebilir. Bu rnekte, main 'den bilerek geersiz olan -8 deerini gnderiyoruz; ama, programn dallanmasn bir kere de hata atldnda grmek: // ... void main() { balyor("main", 0); yumurtaYap(-8); yumurtaYe(); bitiyor("main", 0); } void yumurtaYap(int adet) { balyor("yumurtaYap", 1); malzemeleriHazrla(adet);

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

182

Hata Atma ve Yakalama

yumurtalarPiir(); malzemeleriKaldr(); bitiyor("yumurtaYap", 1);

// ... void malzemeleriHazrla(int adet) { balyor("malzemeleriHazrla", 2); yumurtaHazrla(adet); yaHazrla(); tavaHazrla(); bitiyor("malzemeleriHazrla", 2); } // ... Programn bu halini altrdmzda, throw ile hata atld yerden sonraki hibir satrn iletilmediini grrz: main ilk satr yumurtaYap ilk satr malzemeleriHazrla ilk satr yumurtaHazrla ilk satr object.Exception: Dolaptan -8 yumurta kartlamaz Hata olutuu an; en alt dzeyden en st dzeye doru, nce yumurtaHazrla ilevinden, sonra malzemeleriHazrla ilevinden, daha sonra yumurtaYap ilevinden, ve en sonunda da main ilevinden klr. Bu k srasnda, ilevlerin henz iletilmemi olan admlar iletilmez. lemlere devam etmeden btn ilevlerden klmasnn mant; en alt dzeydeki yumurtaHazrla ilevinin baarszlkla sonulanm olmasnn, onu aran daha st dzeydeki ilevlerin de baarsz olacaklar anlamna gelmesidir. Alt dzey bir ilevden atlan hata, teker teker o ilevi aran st dzey ilevlere geer ve en sonunda main 'den de karak programn sonlanmasna neden olur. Hatann izledii yolu krmz ile u ekilde gsterebiliriz: | main ---+ | | +-- yumurtaYap -+ | | | | +-- malzemeleriHazrla -------+ | | | |hata | | +- yumurtaHazrla X-+ | | +- yaHazrla | | +- tavaHazrla | | | +-- yumurtalarPiir | +-- malzemeleriKaldr | +-- yumurtaYe Hata atma dzeneinin yarar, hatal bir durumla karlaldnda dallanlm olan btn ilevlerin derhal terkedilmelerini salamasdr. Baz durumlarda, atlan hatann yakalanmas ve programn devam edebilecek ekilde dzeltilmesi de mmkndr. Bunu salayan catch anahtar szcn biraz aada gsteriyorum.

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

183

Hata Atma ve Yakalama

40.1.3

throw 'u ne zaman kullanmal


throw 'u gerekten ie devam edilemeyecek durumlarda kullann. rnein kaytl renci adedini bir dosyadan okuyan bir ilev, bu deer sfrdan kk ktnda hata atabilir. nk rnein eksi adet renci ile iine devam etmesi olanakszdr. te yandan; eer devam edilememesinin nedeni kullancnn girdii bir bilgiyse, kullancnn girdii bu bilgiyi denetlemek daha uygun olabilir. Kullancya bir hata mesaj gsterilebilir ve bilgiyi geerli olacak ekilde tekrar girmesi istenebilir. Kullancyla etkileilen byle bir durum, programn atlan bir hata ile sonlanmasndan daha uygun olabilir.

40.2

Hata yakalamak iin try-catch deyimi


Yukarda, atlan hatann btn ilevlerden ve en sonunda da programdan hemen klmasna neden olduunu anlattm. Aslnda atlan bu hata yakalanabilir ve hatann trne veya duruma gre davranlarak programn sonlanmas nlenebilir. Hata, atld ilevden st dzey ilevlere doru adm adm ilerlerken, onunla ilgilenen bir noktada try-catch deyimi ile yakalanabilir. "try"n anlam "dene", "catch"in anlam da "yakala"dr. try-catch deyimini, bu anlamlar gze alarak "altrmay dene, eer hata atlrsa yakala" olarak anlatabiliriz. Sz dizimi yledir: try { // altrlmas istenen ve belki de // hata atacak olan kod blou } catch (ilgilenilen_bir_hata_tr_nesnesi) { // bu trden hata atldnda // iletilecek olan ilemler } catch (ilgilenilen_dier_bir_hata_tr_nesnesi) { // bu dier trden hata atldnda // iletilecek olan ilemler // ... seime bal olarak baka catch bloklar ... } finally { // hata atlsa da atlmasa da; // mutlaka iletilmesi gereken ilemler } Bu blou anlamak iin nce aadaki try kullanmayan programa bakalm. Bu program, zar deerini bir dosyadan okuyor ve ka yazdryor: import std.stdio; import std.conv; int dosyadanZarOku() { auto dosya = File("zarin_yazili_oldugu_dosya", "r"); int zar; dosya.readf(" %s", &zar); } return zar;

void main() { const int zar = dosyadanZarOku(); } writeln("Zar: ", zar);

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

184

Hata Atma ve Yakalama

Dikkat ederseniz, dosyadanZarOku ilevi hi hatalarla ilgilenmeden ve sanki dosya baaryla alacakm ve iinden bir zar deeri okunacakm gibi yazlm. O, yalnzca kendi iini yapyor. Bu, hata atma dzeneinin baka bir yarardr: ilevler her ey yolunda gidecekmi gibi yazlabilirler. imdi o program klasrde zarin_yazili_oldugu_dosya isminde bir dosya bulunmad zaman balatalm: # ./deneme std.exception.ErrnoException@std/stdio.d(286): Cannot open file `zarin_yazili_oldugu_dosya' in mode `r' (No such file or directory) Klasrde dosya bulunmad zaman, mesaj "'zarin_yazili_oldugu_dosya' 'r' modunda alamyor" olan bir ErrnoException atlmtr. Yukarda grdmz dier rneklere uygun olarak, program kna "Zar: " yazdramam ve hemen sonlanmtr. imdi programa dosyadanZarOku ilevini bir try blou iinde aran bir ilev ekleyelim, ve main 'den bu ilevi aralm: import std.stdio; import std.conv; int dosyadanZarOku() { auto dosya = File("zarin_yazili_oldugu_dosya", "r"); int zar; dosya.readf(" %s", &zar); } return zar;

int dosyadanZarOkumayDene() { int zar; try { zar = dosyadanZarOku(); } catch (std.exception.ErrnoException hata) { writeln("(Dosyadan okuyamadm; 1 varsayyorum)"); zar = 1; } } return zar;

void main() { const int zar = dosyadanZarOkumayDene(); } writeln("Zar: ", zar);

Eer program yine ayn ekilde klasrde zarin_yazili_oldugu_dosya dosyas olmadan balatrsak, bu sefer programn hata ile sonlanmadn grrz: $ ./deneme (Dosyadan okuyamadm; 1 varsayyorum) Zar: 1

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

185

Hata Atma ve Yakalama

Bu kodda, dosyadanZarOku ilevinin ileyii bir try blou iinde denenmektedir. Eer hatasz alrsa, ilev ondan sonra return zar; satr ile normal olarak sonlanr. Ama eer zellikle belirtilmi olan std.exception.ErrnoException hatas atlrsa, ilevin ileyii o catch blouna geer ve o bloun iindeki kodlar altrr. Bunu programn yukardaki ktsnda gryoruz. zetle, klasrde zar dosyas bulunmad iin nceki programdaki gibi bir std.exception.ErrnoException hatas atlmakta, (bunu bizim kodumuz deil, File atyor) bu hata catch ile yakalanmakta, zar iin 1 deeri varsaylmakta, ve programn ileyiine devam edilmektedir. te catch , atlabilecek olan hatalar yakalayarak o durumlara uygun olarak davranlmasn, ve programn ileyiine devam etmesini salar. Baka bir rnek olarak, yumurtal programa dnelim ve onun main ilevine bir try-catch deyimi ekleyelim: void main() { balyor("main", 0); try { yumurtaYap(-8); yumurtaYe(); } catch (Exception hata) { write("Yumurta yiyemedim: "); writeln('"', hata.msg, '"'); writeln("Komuda yiyeceim..."); } } bitiyor("main", 0);

main ilk satr yumurtaYap ilk satr malzemeleriHazrla ilk satr yumurtaHazrla ilk satr Yumurta yiyemedim: "Dolaptan -8 yumurta kartlamaz" Komuda yiyeceim... main son satr Grld gibi, bu program bir hata atld diye artk hemen sonlanmamaktadr. Program; hataya kar nlem almakta, ileyiine devam etmekte, ve main ilevi normal olarak sonuna kadar iletilmektedir.
40.2.1

catch bloklar srayla taranr


rneklerde kendimiz hata atarken kullandmz Exception , genel bir hata trdr. Bu hatann atlm olmas, programda bir hata olduunu belirtir; ve hatann iinde saklanmakta olan mesaj, o mesaj okuyan insanlara da hatayla ilgili bilgi verir. Ancak, Exception snf hatann tr konusunda bir bilgi tamaz. Bu derste daha nce grdmz ConvException ve ErrnoException ise daha zel hata trleridir: birincisi, atlan hatann bir dnm ile ilgili olduunu; ikincisi ise dosya ama ile ilgili olduunu anlatr.

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

186

Hata Atma ve Yakalama

Phobos'taki ou baka hata gibi ConvException ve ErrnoException , Exception snfndan tremilerdir. Atlan hata trleri, Error ve Exception genel hata trlerinin daha zel halleridir. Error ve Exception da kendilerinden daha genel olan Throwable snfndan tremilerdir. ("Throwable"n anlam "atlabilen"dir.) Her ne kadar catch ile yakalanabiliyor olsa da, Error trnden veya ondan tremi olan hatalarn yakalanmalar nerilmez. Ayn nedenden, Error 'dan daha genel olduu iin Throwable 'n yakalanmas da nerilmez. Yakalanmasnn doru olduu sradzen, Exception sradzenidir. Throwable (yakalamayn) Exception Error (yakalamayn) ... ... ... ... Not: Sradzen gsterimini daha sonraki Treme dersinde gstereceim. Yukardaki ekil, Throwable 'n en genel, Exception ve Error 'n daha zel trler olduklarn ifade eder. Atlan hatalar zellikle belirli bir trden olacak ekilde yakalayabiliriz. rnein ErrnoException trn yakalayarak dosya ama sorunu ile karlaldn anlayabilir ve programda buna gre hareket edebiliriz. Atlan hata, ancak eer catch blounda belirtilen tre uyuyorsa yakalanr. rnein zelBirHata trn yakalamaya alan bir catch blou, ErrnoException hatasn yakalamaz. Bir try deyimi ierisindeki kodlarn (veya onlarn ard baka kodlarn) att hata, o try deyiminin catch bloklarnda belirtilen hata trlerine srayla uydurulmaya allr. Eer atlan hatann tr srayla baklan catch blounun hata trne uyuyorsa, o hata yakalanm olur ve o catch blounun ierisindeki kodlar iletilir. Uyan bir catch blou bulunursa, artk dier catch bloklarna baklmaz. catch bloklarnn byle srayla taranmalarnn doru olarak almas iin catch bloklarnn daha zel hata trlerinden daha genel hata trlerine doru sralanm olmalar gerekir. Buna gre; genel bir kural olarak eer yakalanmas uygun bulunuyorsa, yakalanmas nerilen en genel hata tr olduu iin Exception her zaman en sondaki catch blounda belirtilmelidir. rnein renci kaytlaryla ilgili hatalar yakalamaya alan bir try deyimi, catch bloklarndaki hata trlerini zelden genele doru u ekilde yazabilir: try { // ... hata atabilecek kayt ilemleri ... } catch (KaytNumarasHanesiHatas hata) { // zellikle kayt numarasnn bir hanesiyle ilgili // olan bir hata } catch (KaytNumarasHatas hata) { // kayt numarasyla ilgili olan, ama hanesi ile // ilgili olmayan daha genel bir hata } catch (KaytHatas hata) { // kaytla ilgili daha genel bir hata } catch (Exception hata) { // kaytla ilgisi olmayan genel bir hata

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

187

Hata Atma ve Yakalama

40.2.2

finally blou
try-catch deyiminin son blou olan finally , hata atlsa da atlmasa da mutlaka iletilecek olan ilemleri ierir. catch gibi, finally blou da istee baldr; gerekmiyorsa yazlmayabilir. finally 'nin etkisini grmek iin %50 olaslkla hata atan u programa bakalm: import std.stdio; import std.random; void yzdeElliHataAtanlev() { if (uniform(0, 2) == 1) { throw new Exception("hata mesaj"); } } void deneme() { writeln("ilk satr"); try { writeln("try'n ilk satr"); yzdeElliHataAtanlev(); writeln("try'n son satr"); // ... istee bal olarak catch bloklar da olabilir ... } finally { writeln("finally ilemleri"); } } writeln("son satr");

void main() { deneme(); } O ilev hata atmadnda programn kts yledir: ilk satr try'n ilk satr try'n son satr finally ilemleri son satr Hata attnda ise yle: ilk satr try'n ilk satr finally ilemleri object.Exception@deneme.d(7): hata mesaj Grld gibi, hata atldnda "try'n son satr" ve "son satr" yazdrlmam, ama finally blounun ii iki durumda da iletilmitir.

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

188

Hata Atma ve Yakalama

40.2.3

try-catch 'i ne zaman kullanmal


try-catch deyimi, atlm olan hatalar yakalamak ve bu durumlarda zel ilemler yapmak iin kullanlr. Dolaysyla, try-catch deyimini ancak ve ancak atlan bir hata ile ilgili zel ilemler yapmanz gereken veya yapabildiiniz durumlarda kullann. Baka durumlarda hatalara karmayn. Hatalar, onlar yakalamaya alan ilevlere brakn.

40.3

scope(exit) , scope(success) , ve scope(failure)


Kesinlikle iletilmeleri gereken ifadelerin finally bloklarna, hatal durumlarda iletilmeleri gereken ifadelerin de catch bloklarna yazldklarn grdk. Bu bloklarn kullanmlaryla ilgili bir ka gzlemde bulunabiliriz: catch ve finally bloklar, try blou olmadan kullanlamaz bu bloklarda kullanlmak istenen baz deikenler o noktalarda geerli olmayabilir: void birlev(ref int k) { try { int birDeer = 42; k += birDeer; hataAtabilecekBirlev(); } catch (Exception hata) { k -= birDeer; } // derleme HATASI

Yukardaki ilev, referans trndeki parametresinde deiiklik yapmakta, ve hata atlmas durumunda onu eski haline getirmeye almaktadr. Ne yazk ki, birDeer yalnzca try blou iinde tanml olduu iin bir derleme hatas alnr. (Not: Yaam sreleriyle ilgili olan bu konuyu ilerideki bir derste anlatacam.) bir kapsamdan klrken kesinlikle iletilmesi gereken ifadelerin hepsinin bir arada en aadaki finally blouna yazlmalar, ilgili olduklar kodlardan uzakta kalacaklar iin istenmeyebilir catch ve finally bloklarna benzer ekilde ileyen ve baz durumlarda daha uygun olan bir olanak, scope deyimleridir. farkl scope deyimi, yine ifadelerin kapsamlardan klrken kesinlikle iletilmeleri ile ilgilidir: scope(exit) : ifade, kapsamdan ne ekilde klrsa klsn iletilir scope(success) : ifade, kapsamdan baaryla klrken iletilir scope(failure) : ifade, kapsamdan hata ile klrken iletilir Bu deyimler yine atlan hatalarla ilgili olsalar da, try-catch blou olmadan kullanlabilirler. rnein, hata atldnda k 'n deerini dzeltmeye alan yukardaki ilevi bir scope(failure) deyimiyle ve daha ksa olarak yle yazabiliriz: void birlev(ref int k) { int birDeer = 42; k += birDeer; scope(failure) k -= birDeer;

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

189

Hata Atma ve Yakalama

hataAtabilecekBirlev();

Yukardaki scope deyimi, kendisinden sonra yazlan ifadenin ilevden hata ile kld durumda iletileceini bildirir. Bunun bir yarar, yaplan bir deiikliin hatal durumda geri evrilecek olduunun tam da deiikliin yapld yerde grlebilmesidir. scope deyimleri bloklar halinde de bildirilebilirler: scope(exit) { // ... ifadeler ... } Bu kavramlar deneyen bir ilevi yle yazabiliriz: void deneme() { scope(exit) writeln("karken 1"); scope(success) { writeln("baarlysa 1"); writeln("baarlysa 2"); } scope(failure) writeln("hata atlrsa 1"); scope(exit) writeln("karken 2"); scope(failure) writeln("hata atlrsa 2"); } yzdeElliHataAtanlev();

levin kts, hata atlmayan durumda scope(exit) ve scope(success) ifadelerini ierir: karken 2 baarlysa 1 baarlysa 2 karken 1 Hata atlan durumda ise scope(exit) ve scope(failure) ifadelerini ierir: hata atlrsa 2 karken 2 hata atlrsa 1 karken 1 object.Exception: hata mesaj ktlardan anlald gibi, scope deyimlerinin ifadeleri ters srada iletilmektedir. Bunun nedeni, daha sonra gelen kodlarn daha nceki deikenlerin durumlarna bal olmalardr. scope deyimlerindeki ifadelerinin ters srada iletilmeleri, programn durumunda yaplan deiikliklerin geri admlar atlarak ters srada yaplmalarn salar.

40.4

Hata eitleri
Hata atma dzeneinin ne kadar yararl olduunu grdk. Hem alt dzeydeki ilemlerin, hem de o ileme baml olan daha st dzey ilemlerin hemen sonlanmalarna neden olur. Bylece program yanl bilgiyle veya eksik ilemle devam etmemi olur.

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

190

Hata Atma ve Yakalama

Buna bakarak her hatal durumda hata atlmasnn uygun olduunu dnmeyin. Hatann eidine bal olarak farkl davranmak gerekebilir.
40.4.1

Kullanc hatalar
Hatalarn bazlar kullancdan gelir. Yukarda da grdmz gibi, rnein bir say beklenen durumda "merhaba" gibi bir dizgi girilmi olabilir. Programn kullancyla etkiletii bir durumda programn hata ile sonlanmas uygun olmayaca iin, byle durumlarda kullancya bir hata mesaj gstermek ve doru bilgi girmesini istemek daha uygun olabilir. Yine de, kullancnn girdii bilginin dorudan ilenmesinde ve o ilemler srasnda bir hata atlmasnda da bir saknca olmayabilir. nemli olan, bu tr bir hatann programn sonlanmasna neden olmak yerine, kullancya geerli bilgi girmesini sylemesidir. Bir rnek olarak, kullancdan dosya ismi alan bir programa bakalm. Aldmz dosya isminin geerli olup olmad konusunda iki yol izleyebiliriz: Bilgiyi denetlemek: std.file modlndeki exists ilevini kullanarak verilen isimde bir dosya olup olmadna bakabiliriz: if (exists(dosya_ismi)) { // dosya mevcut } else { // dosya mevcut deil } Dosyay ancak dosya mevcut olduunda aarz. Ancak; dosya, program bu denetimi yapt anda mevcut olduu halde, az sonra File ile almaya alldnda mevcut olmayabilir. nk rnein sistemde almakta olan baka bir program tarafndan silinmi veya ismi deitirilmi olabilir. Bu yzden, belki de aadaki dier yntem daha uygundur. Bilgiyi dorudan kullanmak: Kullancdan alnan bilgiye gvenebilir ve dorudan ilemlere geebiliriz. Eer verilen bilgi geersizse, zaten File bir hata atacaktr: import std.stdio; import std.string; void dosyayKullan(string dosyasmi) { auto dosya = File(dosyasmi, "r"); // ... } string dizgiOku(in char[] soru) { write(soru, ": "); string dizgi = chomp(readln()); } return dizgi;

void main() { bool dosyaKullanlabildi = false; while (!dosyaKullanlabildi) { try { dosyayKullan( dizgiOku("Dosyann ismini giriniz")); /* * Eer bu noktaya gelebildiysek, dosyayKullan

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

191

Hata Atma ve Yakalama

* ilevi baaryla sonlanm demektir. Yani, * verilen dosya ismi geerlidir. * * Bu yzden bu noktada bu deikenin deerini * 'true' yaparak while'n sonlanmasn * salyoruz. */ dosyaKullanlabildi = true; writeln("Dosya baaryla kullanld"); } catch (std.exception.ErrnoException amaHatas) { stderr.writeln("Bu dosya alamad"); }

40.4.2

Programc hatalar
Baz hatalar programcnn kendisinden kaynaklanr. rnein yazlan bir ilevin programda kesinlikle sfrdan kk bir deerle arlmayacandan eminizdir. Programn tasarmna gre bu ilev kesinlikle eksi bir deerle arlmyordur. levin buna ramen eksi bir deer almas; ya programn mantndaki bir hatadan kaynaklanyordur, ya da o mantn gerekletirilmesindeki bir hatadan. Bunlarn ikisi de programc hatas olarak kabul edilir. Byle, programn yazmyla ilgili olan, yani programcnn kendisinden kaynaklanan hatal durumlarda hata atmak yerine bir assert kullanmak daha uygun olabilir (Not: assert ' bir sonraki derste anlatacam.): void menSeenei(int sraNumaras) { assert(sraNumaras >= 0); } void main() { menSeenei(-1); }

core.exception.AssertError@deneme.d(3): Assertion failure assert , programcya hangi kaynak dosyann hangi satrndaki beklentinin gereklemedii bilgisini verir. (Bu mesajda deneme.d dosyasnn nc satr olduu anlalyor.)
40.4.3

Beklenmeyen durumlar
Yukardaki iki durumun dnda kalan her trl hatal durumda hata atmak uygundur. Zaten baka are kalmamtr: ne bir kullanc hatasyla ne de bir programc hatasyla kar karyayzdr. Eer iimize devam edemiyorsak, hata atmaktan baka are yoktur. Bizim attmz hatalar karsnda ne yapacaklar bizi aran st dzey ilevlerin grevidir. Eer uygunsa, attmz hatay yakalayarak bir are bulabilirler.

40.5

zet
Eer bir kullanc hatasyla karlamsanz ya kullancy uyarn, ya da yine de ilemlere devam ederek nasl olsa bir hata atlacana gvenin Programn mantnda veya gerekletirilmesinde hata olmadn garantilemek iin assert ' kullann Bunlarn dndaki durumlarda throw ile hata atn

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

192

Hata Atma ve Yakalama

Hatalar ancak ve ancak, yakaladnzda yararl bir ilem yapabilecekseniz yakalayn. Yoksa hi try-catch deyimi iine almayn; belki de ilevinizi aran daha st dzeydeki bir ilev yakalayacaktr catch bloklarn zelden genele doru sralayn letilmeleri mutlaka gereken ilemleri finally blouna yazn try blou kullanmann gerekmedii durumlarda, iletilmeleri kesinlikle gereken ilemleri scope(exit) , scope(success) , ve scope(failure) deyimleri ile belirtin

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

193

assert fadesi

41

assert fadesi
Programlar yazarken ok sayda varsaymda bulunuruz ve baz beklentilerin doru kmalarn umarz. Programlar ancak bu varsaymlar ve beklentiler doru ktklarnda doru alrlar. assert , programn dayand bu varsaymlar ve beklentileri denetlemek iin kullanlr. Programcnn en etkili yardmclarndandr. ou zaman bu varsaymlarn farkna bile varmayz. rnein iki kiinin yalarnn ortalamasn alan aadaki ilevde kullanlan hesap, ya parametrelerinin ikisinin de sfr veya daha byk olacaklar varsaylarak yazlmtr: double ortalamaYa(double birinciYa, double ikinciYa) { return (birinciYa + ikinciYa) / 2; } Yalardan en az birisinin eksi bir deer olarak gelmesi hatal bir durumdur. Buna ramen, ilev mantkl bir ortalama retebilir ve program bu hata hi farkedilmeden iine yanl da olsa devam edebilir. Baka bir rnek olarak, aadaki ilev yalnzca iki komuttan birisi ile arlacan varsaymaktadr: "ark syle" ve "dans et": void komutlet(in char[] komut) { if (komut == "ark syle") { robotaarkSylet(); } else { robotuDansEttir(); } } Byle bir varsaymda bulunduu iin; geerli olsun olmasn, "ark syle" dndaki her komuta karlk robotuDansEttir ilevini aracaktr. Eer bu varsaymlar kendimize saklarsak, sonuta ortaya kan program hatal davranabilir. assert , bu varsaymlarmz dile getirmemizi salayan ve varsaymlar hatal ktnda ilemlerin durdurulmalarna neden olan bir olanaktr. assert , bir anlamda programa "byle olduunu varsayyorum, eer yanlsa ilemi durdur" dememizi salar.

41.1

Sz dizimi
assert iki ekilde yazlabilir: assert(mantksal_ifade); assert(mantksal_ifade, hata_mesaj); assert , kendisine verilen mantksal ifadeyi iletir. Eer ifadenin deeri true ise varsaym doru km kabul edilir; bu durumda assert ifadesinin hibir etkisi yoktur. Eer ifadenin deeri false ise varsaym yanl km kabul edilir ve AssertError hatas atlr. sminden de anlalabilecei gibi, bu hata Error 'dan tremitir ve Hatalar blmnde grdmz gibi, yakalanmamas gerekir. Byle bir hata atldnda programn hemen sonlanmas nemlidir; bylece programn yanl varsaymlara dayanarak yanl olabilecek sonular retmesi nlenmi olur. Yukardaki ortalamaYa ilevindeki varsaymlarmz iki assert ile yle ifade edebiliriz:

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

194

assert fadesi

double ortalamaYa(double birinciYa, double ikinciYa) { assert(birinciYa >= 0); assert(ikinciYa >= 0); } return (birinciYa + ikinciYa) / 2;

void main() { auto sonu = ortalamaYa(-1, 10); } O assert 'ler Trke olarak "birinciYa'n 0 veya daha byk olduunu varsayyorum" ve "ikinciYa'n 0 veya daha byk olduunu varsayyorum" anlamna gelir. Baka bir bak asyla, "assert" szcnn "emin olarak ne srmek" karln kullanarak, "birinciYa'n 0 veya daha byk olduundan eminim" gibi de dnlebilir. assert bu varsaymlar denetler ve yukardaki programda olduu gibi, varsaymn yanl kt durumda bir hata atar: core.exception.AssertError@deneme(5): Assertion failure Hatann @ karakterinden sonra gelen blm hangi dosyann hangi satrndaki varsaymn doru kmadn gsterir. Bu rnekte, deneme(5) 'e bakarak hatann deneme.d dosyasnn beinci satrnda olduunu anlarz. assert ifadesinin yanl kt durumda kendi yazdmz zel bir mesaj grmek istediimizde assert ifadesinin ikinci eklini kullanrz: assert(birinciYa >= 0, "Ya sfrdan kk olamaz");

core.exception.AssertError@deneme.d(5): Ya sfrdan kk olamaz Programda kesinlikle gelinmeyecei dnlen veya gelinmemesi gereken noktalarda, zellikle baarsz olsun diye mantksal ifade olarak bilerek false sabit deeri kullanlr. rnein yukardaki "ark syle" ve "dans et" rneinde baka komutlarn geersiz olduklarn belirtmek ve bu durumlarda hata atlmasn salamak iin: void komutlet(in char[] komut) { if (komut == "ark syle") { robotaarkSylet(); } else if (komut == "dans et") { robotuDansEttir(); } else { assert(false); }

Artk ilev yalnzca o iki komutu kabul eder ve baka komut geldiinde assert(false) nedeniyle ilem durdurulur.

41.2

Kesinlikle doru olan (!) varsaymlar iin bile assert


"Kesinlikle doru olan"n zellikle zerine basyorum. Hibir varsaym bilerek yanl olmayaca iin, zaten ou hata kesinlikle doru olan varsaymlara dayanr.

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

195

assert fadesi

Bu yzden bazen kesinlikle gereksizmi gibi duran assert ifadeleri de kullanlr. rnein belirli bir senenin aylarnn ka gn ektikleri bilgisini bir dizi olarak dndren bir ilev ele alalim: int[] ayGnleri(in int yl) { int[] gnler = [ 31, ubatGnleri(yl), 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 ]; assert((diziToplam(gnler) == 365) || (diziToplam(gnler) == 366)); } return gnler;

Doal olarak bu ilevin dndrd dizideki gn toplamlar ya 365 olacaktr, ya da 366. Bu yzden yukardaki assert ifadesinin gereksiz oduunu dnebilirsiniz. Oysa, her ne kadar gereksiz gibi grnse de, o ifade ubatGnleri ilevinde ilerideki bir zamanda yaplabilecek bir hataya kar bir gvence salar. ubatGnleri ilevinde ileride yaplabilecek bir hata, dizinin ubat elemannn rnein 30 olmasna neden olsa, o assert sayesinde bu hata hemen farkedilecektir. Hatta biraz daha ileri giderek dizinin uzunluunun her zaman iin 12 olacan da denetleyebiliriz: assert(gnler.length == 12); Bylece kendimizi diziden yanllkla silinebilecek veya diziye yanllkla eklenebilecek bir elemana kar da gven altna alm oluruz. Byle denetimler her ne kadar gereksizmi gibi grnseler de son derece yararldrlar. Kodun salamln arttran ve kodu ilerideki deiiklikler karsnda gvencede tutan ok etkili yaplardr. Kodun salamln arttran ve programn yanl sonular douracak ilemlerle devam etmesini nleyen bir olanak olduu iin, assert bundan sonraki derslerde greceimiz birim testleri ve szlemeli programlama olanaklarnn da temelini oluturur.

41.3

Deer retmez ve yan etkisi yoktur


fadelerin deer retebildiklerini ve yan etkilerinin olabildiini sylemitim. assert deer retmeyen bir ifadedir; yani dn tr void 'dir. Ek olarak, assert ifadesinin yan etkisi de yoktur. stelik, ona verilen mantksal ifadenin de yan etkisinin olmamas, D standard tarafndan art koulmutur. assert , programn durumunu deitirmeyen ve yalnzca varsaymlar denetleyen bir yap olarak kalmak zorundadr.

41.4

Ne zaman kullanmal
assert ifadesinin hata atan bir olanak olmas, onun yerine bizim dorudan hata atabileceimiz dncesini dourabilir. Hatal durumlarda bu iki olanaktan hangisini kullanacamzn karar bazen g olabilir. Baka dillerde assert hata atmaz ve program hemen sonlandrr. Bu yzden, geleneksel olarak programc hatalarn yakalamak iin kullanlr. Siz de programlarnzda bunu

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

196

assert fadesi

uygulayabilir, ve hatann program ile ilgili olduundan emin olduunuz durumlarda assert ifadesini kullanabilirsiniz. rnein yukardaki ayGnleri ilevinde kullanlan assert , tamamen programclkla ilgili hatalara kar bir gvence olarak kullanlmtr. Programcnn ubatGnleri ierisinde ileride yapabilecei hatalar nler. Bundan sonra greceimiz birim testlerinde ve szlemeli programlamada assert temel olarak kullanld iin, orada zaten seiminiz olmayacak. Buna bakarak assert ifadesini belki de yalnzca birim testlere ve szlemeli programlamaya ayrabilir, ve dier hatal durumlarda hata atma olanan kullanabilirsiniz. Daha nce de duyduunuz gibi: karar sizin... :o)

41.5

Problemler
1. Bu problemde size nceden yazlm bir program gstermek istiyorum. Bu programn hata olasln azaltmak iin baz noktalarna assert ifadeleri yerletirilmi. Amacm, bu assert ifadelerinin programdaki hatalar ortaya kartma konusunda ne kadar etkili olduklarn gstermek. Program kullancdan bir balang zaman ve bir ilem sresi alyor ve o ilemin ne zaman sonulanacan hesaplyor. Program, saylardan sonra gelen 'da' eklerini de doru olarak yazdryor: 9:6'da balayan ve 1 saat 2 dakika sren ilem, 10:8'de sonlanr import std.stdio; import std.conv; /* * Verilen mesaj kullancya gsterir ve girilen zaman * bilgisini saat ve dakika olarak okur */ void zamanOku(in dchar[] mesaj, out int saat, out int dakika) { write(mesaj, "? (SS:DD) "); readf(" %s:%s", &saat, &dakika); if ((saat < 0) || (saat > 23) || (dakika < 0) || (dakika > 59)) { throw new Exception("Geersiz zaman"); }

/* * Zaman dizgi dzeninde dndrr */ dstring zamanDizgisi(in int saat, in int dakika) { assert((saat >= 0) && (saat <= 23)); assert((dakika >= 0) && (dakika <= 59)); } return to!dstring(saat) ~ ":" ~ to!dstring(dakika);

/* * ki zaman bilgisini birbirine ekler ve nc parametre * ifti olarak dndrr */ void zamanEkle( in int balangSaati, in int balangDakikas, in int eklenecekSaat, in int eklenecekDakika, out int sonuSaati, out int sonuDakikas)

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

197

assert fadesi

sonuSaati = balangSaati + eklenecekSaat; sonuDakikas = balangDakikas + eklenecekDakika; if (sonuDakikas > 59) { ++sonuSaati; }

/* * Saylardan sonra kesme iaretiyle ayrlarak kullanlacak * olan "de, da" ekini dndrr */ dstring daEki(in int say) { dstring ek; const int sonHane = say % 10; switch (sonHane) { case 1: case 2: case 7: case 8: ek = "de"; break; case 3: case 4: case 5: ek = "te"; case 6: case 9: ek = "da"; break; default: break; } assert(ek.length != 0); } return ek;

void main() { int balangSaati; int balangDakikas; zamanOku("Balang zaman", balangDakikas, balangSaati); int ilemSaati; int ilemDakikas; zamanOku("lem sresi", ilemSaati, ilemDakikas); int bitiSaati; int bitiDakikas; zamanEkle(balangSaati, balangDakikas, ilemSaati, ilemDakikas, bitiSaati, bitiDakikas); sonucuYazdr(balangSaati, balangDakikas, ilemSaati, ilemDakikas, bitiSaati, bitiDakikas);

void sonucuYazdr( in int balangSaati, in int balangDakikas,

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

198

assert fadesi

in int ilemSaati, in int ilemDakikas, in int bitiSaati, in int bitiDakikas) writef("%s'%s balayan", zamanDizgisi(balangSaati, balangDakikas), daEki(balangDakikas)); writef(" ve %s saat %s dakika sren ilem,", ilemSaati, ilemDakikas); writef(" %s'%s sonlanr", zamanDizgisi(bitiSaati, bitiDakikas), daEki(bitiDakikas));

writeln();

Bu program altrn ve giriine balang olarak 6:9 ve sre olarak 1:2 verin. Programn normal olarak sonlandn greceksiniz. Not: Aslnda ktda bir hata farkedebilirsiniz. Bunu imdilik grmezden gelin; nk az sonra assert 'lerin yardmyla bulacaksnz. 2. Bu sefer programa 6:9 ve 15:2 zamanlarn girin. Bir AssertError atldn greceksiniz. Hatada belirtilen satra gidin ve bu hatay giderin. 3. Bu sefer programa 6:9 ve 1:1 zamanlarn girin. Yeni bir hata ile karlaacaksnz. O satra da gidin ve o hatay da giderin. 4. Bu sefer programa 6:9 ve 20:0 bilgilerini girin. Yine hata... 5. Bu sefer programa 6:9 ve 1:41 bilgilerini girin. Programn da ekinin doru almadn greceksiniz: Balang zaman? (SS:DD) 6:9 lem sresi? (SS:DD) 1:41 6:9'da balayan ve 1 saat 41 dakika sren ilem, 7:50'da sonlanr Bunu dzeltin ve duruma gre doru ek yazmasn salayn: 7:10'da, 7:50'de, 7:40'ta, vs. ... zmler

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

199

Birim Testleri

42

Birim Testleri
Programcln kanlmaz uralarndan birisi hata ayklamaktr. Her kullancnn yakndan tand gibi, iinde bilgisayar program alan her cihaz yazlm hatalar ierir. Yazlm hatalar, kol saati gibi basit elektronik aletlerden uzay arac gibi byk sistemlere kadar her yerde bulunur.

42.1

Hata nedenleri
Yazlm hatalarnn ok eitli nedenleri vardr. Programn fikir aamasndan balayarak kodlanmasna doru kabaca sralarsak: Programdan istenenler ak bir ekilde ortaya konmam olabilir; hatta belki de programn tam olarak ne yapaca bandan bilinmiyordur Programc, programdan istenenleri yanl anlam olabilir Programlama dili, programdan istenenleri ifade etmekte yetersizdir; bir insana Trke anlatrken bile anlamazlklar yaandn gz nne alrsak, bilgisayar dilinin karmak sz dizimleri ve kurallar, istenenlerin tam olarak ifade edilmesi iin yeterli olmayabilir Programcnn varsaymlar yanl kabilir; rnein pi says olarak 3.14 deerinin yeterli olduu varsaylm olabilir Programcnn bilgisi herhangi bir konuda yetersiz veya yanl olabilir; rnein kesirli saylarn eitlik karlatrmalarnda kullanlmalarnn gvensiz olduunu bilmiyordur Program, batan dnlmemi olan bir durumla karlaabilir; rnein bir klasrdeki dosyalardan birisi, program o listeyi bir dngde kullanrken silinmi veya o dosyann ismi deitirilmi olabilir Programc kodu yazarken dikkatsizlik yapabilir; rnein bir ilem srasnda toplamFiyat yerine toptanFiyat yazabilir vs. Ne yazk ki, gnmzde henz tam olarak salam kod reten yazlm gelitirme yntemleri bulunamamtr. Bu konu, srekli olarak zm bulunmaya allan ve her be on ylda bir mit verici yntemlerin ortaya kt bir konudur.

42.2

Hatann farkedildii zaman


Yazlm hatasnn ne zaman farkna varld da eitlilik gsterir. En erkenden en gee doru sralayarak: Kod yazlrken Program yazan kii tarafndan Baka bir programc tarafndan; rnein iftli programlama (pair programming) yntemi uygulandnda, yaplan bir yazm hatasn program yazan kiinin yanndaki programc farkedebilir Derleyici tarafndan; derleyicinin verdii hata mesajlar veya uyarlar ounlukla programc hatalarn gsterirler Programn programc tarafndan oluturulmas srasnda birim testleri tarafndan Kod incelenirken Kaynak kodu inceleyen ara programlar tarafndan; (baka diller iin rnein Coverity)

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

200

Birim Testleri

Kodu inceleyen baka programclar tarafndan kod incelemesi (code review) srasnda Program kullanmdayken Programn ileyiini inceleyen ara programlar tarafndan (rnein Linux ortamlarndaki ak kodlu 'valgrind' program ile) Srmden nce test edilirken; ya assert ifadelerinin baarszlndan, ya da programn gzlemlenen davranndan Srmden nce beta kullanclar tarafndan test edilirken Srmdeyken son kullanclar tarafndan Hata ne kadar erken farkedilirse hem zarar o kadar az olur, hem de o kadar az sayda insann zamann alm olur. Bu yzden en iyisi, hatann kodun yazld srada yakalanmasdr. Ge farkedilen hata ise baka programclarn, program test edenlerin, ve ok saydaki kullancnn da zamann alr. Son kullancya gidene kadar farkedilmemi olan bir hatann kodun hangi noktasndan kaynaklandn bulmak da ou durumda olduka zordur. Bu noktaya kadar farkedilmemi olan bir hata, bazen aylarca srebilen uralar sonucunda temizlenebilir.

42.3

Hata yakalamada birim testleri


Kodu yazan programc olmazsa zaten kod olmaz. Ayrca, derlemeli bir dil olduu iin D programlar zaten derleyici kullanmadan oluturulamazlar. Bunlar bir kenara braktmzda, program hatalarn yakalamada en erken ve bu yzden de en etkin yntem olarak birim testleri kalr. Birim testleri, modern programcln ayrlmaz aralarndandr. Kod hatalarn azaltma konusunda en etkili yntemlerdendir. Birim testleri olmayan kod, hatal kod olarak kabul edilir. Ne yazk ki bunun tersi doru deildir: birim testlerinin olmas, kodun hatasz olduunu kantlamaz; ama hata orann ok byk lde azaltr. Birim testleri ayrca kodun rahata ve gvenle gelitirilebilmesini de salarlar. Kod zerinde deiiklik yapmak, rnein yeni olanaklar eklemek, doal olarak o kodun eski olanaklarnn artk hatal hale gelmelerine neden olabilir. Kodun gelitirilmesi srasnda ortaya kan byle hatalar, ya ok sonraki srm testleri srasnda farkedilirler, ya da daha kts, program son kullanclar tarafndan kullanlrken. Bu tr hatalar kodun yeniden dzenlenmesinden ekinilmesine ve kodun gittike rmesine (code rot) neden olurlar. rnein baz satrlarn aslnda yeni bir ilev olarak yazlmasnn gerektii bir durumda, yeni hatalardan korkulduu iin koda dokunulmaz ve kod tekrar gibi zararl durumlara dlebilir. Programc kltrnde duyulan "bozuk deilse dzeltme" ("if it isn't broken, don't fix it") gibi szler, hep bu korkunun rndr. Bu gibi szler, yazlm olan koda dokunmamay erdem olarak gsterdikleri iin zaman getike kodun rmesine ve zerinde deiiklik yaplamaz hale gelmesine neden olurlar. Modern programclkta bu dncelerin yeri yoktur. Tam tersine, kod rmesinin nne gemek iin kodun gerektike serbeste gelitirilmesi nerilir: "acmaszca gelitir" ("refactor mercilessly"). te bu yararl yaklamn en gl silah birim testleridir. Birim testi, program oluturan en alt birimlerin birbirlerinden olabildiince bamsz olarak test edilmeleri anlamna gelir. Alt birimlerin bamsz olarak testlerden gemeleri, o

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

201

Birim Testleri

birimlerin birlikte almalar srasnda oluacak hatalarn olasln byk lde azaltr. Eer paralar doru alyorsa, btnn de doru alma olasl artar. Birim testleri baka baz dillerde JUnit, CppUnit, Unittest++, vs. gibi ktphane olanaklar olarak gerekletirilmilerdir. D'de ise birim testleri dilin i olanaklar arasndadr. Her iki yaklamn da stn olduu yanlar gsterilebilir. Bunun bir rnei olarak, D'de birim testleri konusunu ktphane olarak halleden Dunit projesine de bakmak isteyebilirsiniz. D'de birim testleri, nceki derste grdmz assert ifadelerinin unittest bloklar iinde kullanlmalarndan oluurlar. Ben burada yalnzca D'nin bu i olanan gstereceim. Bu bloklar anlatmadan nce, birim testlerinin nasl balatldklarn gstermem gerekiyor.

42.4

Birim testlerini balatmak


Programn asl ileyii ile ilgili olmadklar iin, birim testlerinin yalnzca programn gelitirilmesi aamasnda altrlmalar gerekir. Birim testleri derleyici veya gelitirme ortam tarafndan, ve ancak zellikle istendiinde balatlr. Birim testlerinin nasl balatldklar kullanlan derleyiciye ve gelitirme ortamna gre deiir. Ben burada rnek olarak Digital Mars'n derleyicisi olan dmd 'nin -unittest seeneini gstereceim. Programn deneme.d isimli bir kaynak dosyaya yazldn varsayarsak, komut satrnda normalde u ekilde oluturabiliriz: dmd deneme.d -ofdeneme -w Ksaca: dmd , derleyici; deneme.d , derlenmesi istenen kaynak dosya; -of , oluturulacak olan programn ismini belirleyen seenek (bu durumda deneme ); ve -w da derleyici uyarlarn etkinletiren seenektir. Program kodun iindeki birim testlerini de ekleyerek oluturmak iin bu komut satrna unittest seenei eklenir: dmd deneme.d -ofdeneme -w -unittest Bu ekilde oluturulan program altrldnda nce birim testleri iletilir ve ancak onlar baaryla tamamlanrsa programn ileyii main ile devam eder.

42.5

unittest bloklar
Birim testlerini oluturan kodlar bu bloklarn iine yazlr. Bu kodlarn programn normal ileyii ile ilgileri yoktur; yalnzca program ve zellikle ilevleri denemek iin kullanlrlar: unittest { ilevi_deneyen_kodlar_ve_assert_ifadeleri } unittest bloklarn sanki ilev tanmlyor gibi kendi balarna yazabilirsiniz. Ama daha iyisi, bu bloklar denetledikleri ilevlerin hemen altna yazmaktr. rnek olarak, bir nceki derste grdmz ve kendisine verilen sayya Trke ses uyumuna uygun olarak da eki dndren ileve bakalm. Bu ilevin doru almasn denetlemek iin, unittest blouna bu ilevin dndrmesini beklediimiz koullar yazarz:

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

202

Birim Testleri

dstring daEki(in int say) { // ... } unittest { assert(daEki(1) == "de"); assert(daEki(5) == "te"); assert(daEki(9) == "da"); } Oradaki koul; 1, 5, ve 9 saylar iin srasyla "de", "te", ve "da" dndrldn denetler. Her ne kadar testlerin temeli assert denetimleri olsa da, unittest bloklarnn iinde her trl D olanan kullanabilirsiniz. rnein, bir dizgi iindeki belirli bir harfi o dizginin en banda olacak ekilde dndren bir ilevin testleri yle yazlabilir: dchar[] harfBaa(in dchar[] dizgi, in dchar harf) { // ... } unittest { dchar[] dizgi = "merhaba"d.dup; assert(harfBaa(dizgi, 'm') == "merhaba"); assert(harfBaa(dizgi, 'e') == "emrhaba"); assert(harfBaa(dizgi, 'a') == "aamerhb");

Oradaki assert ifadesi, harfBaa ilevinin nasl almasnn beklendiini denetliyorlar. Bu rneklerde grld gibi, birim testleri ayn zamanda ilevlerin belgeleri ve rnek kodlar olarak da kullanldrlar. Yalnzca birim testine bakarak ilevin kullanl hakknda hzlca fikir edinebiliriz.

42.6

Test ynelimli programlama: nce test, sonra kod


Modern programclk yntemlerinden olan test ynelimli programlama ("test driven development" - TDD), birim testlerinin kod yazlmadan nce yazlmasn ngrr. Bu yntemde asl olan birim testleridir. Kodun yazlmas, birim testlerinin baarya ulamalarn salayan ikincil bir uratr. Yukardaki daEki ilevine bu bak asyla yaklaarak onu nce birim testleriyle yle yazmamz gerekir: dstring daEki(in int say) { return "bilerek hatal"; } unittest { assert(daEki(1) == "de"); assert(daEki(5) == "te"); assert(daEki(9) == "da"); } void main() {} Her ne kadar o ilevin hatal olduu ak olsa da, nce programn birim testlerinin doru olarak altklarn, yani beklendii gibi hata attklarn grmek isteriz:

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

203

Birim Testleri

$ dmd deneme.d -ofdeneme -w -O -unittest $ ./deneme core.exception.AssertError@deneme.d(7): Assertion failure lev ancak ondan sonra ve bu testleri geecek ekilde yazlr: dstring daEki(in int say) { dstring ek; const int sonHane = say % 10; switch (sonHane) { case 1: case 2: case 7: case 8: ek = "de"; break; case 3: case 4: case 5: ek = "te"; break; case 6: case 9: case 0: ek = "da"; break; default: // Buraya hibir durumda gelmemeliyiz assert(false); } return ek; } unittest { assert(daEki(1) == "de"); assert(daEki(5) == "te"); assert(daEki(9) == "da"); } void main() {} Artk program bu testleri geer, ve bizim de daEki ilevi konusunda gvenimiz geliir. Bu ilevde daha sonradan yaplacak olas gelitirmeler, unittest blouna yazdmz koullar korumak zorundadrlar. Bylelikle kodu gelitirmeye gvenle devam edebiliriz.

42.7

Bazen de nce hata, sonra test, ve en sonunda kod


Birim testleri btn durumlar kapsayamazlar. rnein yukardaki testlerde farkl eki reten say deeri seilmi, ve daEki ilevi bu testten getii iin baarl kabul edilmitir. Bu yzden, her ne kadar ok etkili yntemler olsalar da, birim testleri btn hatalar yakalayamazlar ve baz hatalar bazen son kullanclara kadar sakl kalabilir. daEki ilevi iin bunun rneini assert dersinin problemlerinde de grmtk. O problemde olduu gibi, bu ilev 50 gibi bir deer geldiinde hataldr:

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

204

Birim Testleri

import std.stdio; void main() { writefln("%s'%s", 50, daEki(50)); }

$ ./deneme 50'da "50'de" olmas gerektii halde, ilev yalnzca son haneye bakt iin bu durumda 50 iin hatal olarak "da" dndrmektedir. Test ynelimli programlama; hemen ilevi dzeltmek yerine, ncelikle bu hatal durumu yakalayan bir birim testinin eklenmesini art koar. nk hatann birim testlerinin gznden kaarak programn kullanm srasnda ortaya km olmas, birim testlerinin bir yetersizlii olarak grlr. Buna uygun olarak, bu durumu yakalayan bir test rnein yle yazlabilir: unittest { assert(daEki(1) == "de"); assert(daEki(5) == "te"); assert(daEki(9) == "da"); assert(daEki(50) == "de"); } Program bu sefer bu birim testi denetimi nedeniyle sonlanr: $ ./deneme core.exception.AssertError@deneme(39): Assertion failure Artk bu hatal durumu denetleyen bir test bulunduu iin, ilevde ileride yaplabilecek gelitirmelerin tekrardan byle bir hataya neden olmasnn nne geilmi olur. Kod ancak bu birim testi yazldktan sonra, ve o testi geirmek iin yazlr. Not: Bu ilev, sonu "bin" ve "milyon" gibi okunarak biten baka saylarla da sorunlu olduu iin burada kapsaml bir zm bulmaya almayacam.

42.8

Problem
Yukarda sz geen harfBaa ilevini, birim testlerini geecek ekilde gerekletirin: dchar[] harfBaa(in dchar[] dizgi, in dchar harf) { dchar[] sonu; return sonu; } unittest { dchar[] dizgi = "merhaba"d.dup; assert(harfBaa(dizgi, 'm') == "merhaba"); assert(harfBaa(dizgi, 'e') == "emrhaba"); assert(harfBaa(dizgi, 'a') == "aamerhb");

void main() {}

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

205

Birim Testleri

O tanmdan balayn; ilk test yznden hata atldn grn; ve ilevi hatay giderecek ekilde yazn. ... zm

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

206

Szlemeli Programlama

43

Szlemeli Programlama
Szlemeli programlama, ilevlerin hizmet sunan birimler olarak kabul edilmeleri dncesi zerine kurulu bir programlama yntemidir. Bu dnceye gre, ilevler ve onlar aran kodlar arasnda yazsz baz anlamalar vardr. Szlemeli programlama, bu anlamalar dil dzeyinde belirlemeye yarayan olanaktr. Szlemeli programlama, ticari bir dil olan Eiffel tarafndan "design by contract (DBC)" adyla yaylmtr. Bu yntem D dilinde "contract programming" olarak geer. Birim testlerinde olduu gibi, assert ifadesine dayanr ve D'nin kod salaml salayan bir baka olanadr. D'de szlemeli programlama temelden oluur: levlerin in bloklar levlerin out bloklar Snflarn invariant bloklar (Bunu daha sonra snflar anlatrken gstereceim)

43.1

Giri koullar iin in bloklar


levlerin doru alabilmeleri, aldklar parametrelere bal olabilir. rnein karekk alan bir ilev kendisine verilen parametrenin sfrdan kk olmamasn art koar; veya parametre olarak tarih bilgisi alan bir ilev ayn 1 ile 12 arasnda olmasn art koar. Bu tr koullar daha nce assert dersinde grmtk. levlerin parametreleriyle ilgili denetimleri, assert ifadeleri kullanarak ilevin tanmland blok iinde yapyorduk: dstring zamanDizgisi(in int saat, in int dakika) { assert((saat >= 0) && (saat <= 23)); assert((dakika >= 0) && (dakika <= 59)); } return to!dstring(saat) ~ ":" ~ to!dstring(dakika);

D'nin szlemeli programlama anlaynda ilevlerin giri koullar "giri" anlamna gelen in bloklarnda denetlenir. Szlemeli programlama bloklar kullanld zaman, ilevin asl blou da "gvde" anlamna gelen body ile belirlenir: import std.stdio; import std.conv; dstring zamanDizgisi(in int saat, in int dakika) in { assert((saat >= 0) && (saat <= 23)); assert((dakika >= 0) && (dakika <= 59)); } body { return to!dstring(saat) ~ ":" ~ to!dstring(dakika); } void main() { writeln(zamanDizgisi(12, 34)); } levin in blounun yarar, ilevin balatlmasyla ilgili olan denetimlerin bir arada ve ayr bir blok iinde yaplmasdr. Bylece assert ifadeleri ilevin asl ilemlerinin arasna karmam

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

207

Szlemeli Programlama

olurlar. levin iinde yine de gerektike assert ifadeleri kullanlabilir, ama giri koullar szlemeli programlama anlayna uygun olarak in blouna yazlrlar. in bloklarndaki kodlar programn almas srasnda ilevin her arlnda otomatik olarak iletilirler. levin asl ileyii, ancak bu koullar salandnda devam eder. Bylece ilevin geersiz balang koullar ile almas ve programn yanl sonularla devam etmesi nlenmi olur. in bloundaki bir assert ifadesinin baarsz olmas, szlemeyi ilevi aran tarafn bozduunu gsterir; ilev, szlemenin gerektirdii ekilde arlmam demektir.

43.2

k garantileri iin out bloklar


levin yapt kabul edilen szlemenin kar taraf da ilevin salad garantilerdir. rnein belirli bir senedeki ubat aynn ka gn ektii bilgisini dndren bir ilevin k garantisi, dndrd deerin 28 veya 29 olmasdr. k garantileri, ilevlerin "k" anlamna gelen out bloklarnda denetlenirler. levin dn deerinin zel bir ismi yoktur; bu deer return ile isimsiz olarak dndrlr. Bu durum, dn deeriyle ilgili garantileri yazarken bir sorun dourur: ismi olmaynca, dn deeriyle ilgili assert ifadeleri de yazlamaz. Bu sorun out anahtar szcnden sonra verilen isimle halledilmitir. Bu isim dn deerini temsil eder ve denetlenecek olan garantilerde bu isim kullanlr: int ubattaKaGn(in int yl) out (sonu) { assert((sonu == 28) || (sonu == 29)); } body { return artkYl_m(yl) ? 29 : 28; } Ben out blounun parametresinin ismi olarak sonu yazmay uygun buldum; siz dnDeeri gibi baka bir isim de verebilirsiniz. Hangi ismi kullanrsanz kullann, o isim ilevin dn deerini temsil eder. Bazen ilevin dn deeri yoktur, veya dn deerinin denetlenmesi gerekmiyordur. O zaman out blou parametresiz olarak yazlr: out { }

// ...

leve girerken in bloklarnn otomatik olarak iletilmeleri gibi, out bloklar da ilevden karken otomatik olarak iletilirler. out bloundaki bir assert ifadesinin baarsz olmas, szlemenin ilev tarafndan bozulduunu gsterir; ilev, szlemenin gerektirdii deeri veya yan etkiyi retememi demektir. Daha nceki derslerde hi kullanmam olduumuzdan da anlalabilecei gibi, in ve out bloklarnn kullanm seime baldr. Bunlara yine seime bal olan unittest bloklarn da eklersek, D'de ilevler drt blok halinde yazlabilirler: Giri koullar iin in blou: seime baldr ve giri koullarn denetler

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

208

Szlemeli Programlama

k garantileri iin out blou: seime baldr ve k garantilerini denetler levin asl ilemlerini ieren body blou: bu bloun yazlmas arttr, ama eer in ve out bloklar kullanlmamsa body anahtar szc yazlmayabilir levin birim testlerini ieren unittest blou: bu aslnda ilevin paras deildir ve kendi bana ilev gibi yazlr; ama denetledii ilevin hemen altna yazlmas, aralarndaki ba gsterme bakmndan uygun olur Btn bu bloklar ieren bir ilev tanm yle yazlabilir: import std.stdio; /* * Toplam iki para olarak bltrr. * * Toplamdan ncelikle birinciye verir, ama birinciye hibir * zaman 7'den fazla vermez. Gerisini ikinciye verir. */ void bltr(in int toplam, out int birinci, out int ikinci) in { assert(toplam >= 0); } out { assert(toplam == (birinci + ikinci)); } body { birinci = (toplam >= 7) ? 7 : toplam; ikinci = toplam - birinci; } unittest { int birinci; int ikinci; // Toplam 0 ise ikisi de 0 olmal bltr(0, birinci, ikinci); assert(birinci == 0); assert(ikinci == 0); // Toplam 7'den az ise birincisi toplam'a, ikincisi 0'a // eit olmal bltr(3, birinci, ikinci); assert(birinci == 3); assert(ikinci == 0); // Snr koulunu deneyelim bltr(7, birinci, ikinci); assert(birinci == 7); assert(ikinci == 0); // 7'den fazla olduunda birinci 7 olmal, gerisi ikinciye // gitmeli bltr(8, birinci, ikinci); assert(birinci == 7); assert(ikinci == 1); // Bir tane de byk bir deerle deneyelim bltr(1_000_007, birinci, ikinci); assert(birinci == 7); assert(ikinci == 1_000_000);

void main() { int birinci; int ikinci;

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

209

Szlemeli Programlama

bltr(123, birinci, ikinci); writeln("birinci: ", birinci, " ikinci: ", ikinci);

$ dmd deneme.d -ofdeneme -w -unittest $ ./deneme birinci: 7 ikinci: 116 Bu ilevin asl ii yalnzca 2 satrdan oluuyor; denetleyen kodlar ise tam 19 satr! Bu kadar kk bir ilev iin bu kadar emein gereksiz olduu dnlebilir. Ama dikkat ederseniz, programc hibir zaman bilerek hatal kod yazmaz. Programcnn yazd kod her zaman iin doru alacak ekilde yazlmtr. Buna ramen, hatalar da hep byle doru alaca dnlen kodlar arasndan kar. levlerden beklenenlerin; birim testleri ve szlemeli programlama ile aka ortaya koyulmalar, doru olarak yazdmz ilevlerin her zaman iin doru kalmalarna yardm eder. Program hatalarn azaltan hibir olana kmsememenizi neririm. Birim testleri ve szlemeli programlama olanaklar bizi zorlu hatalardan koruyan ok etkili aralardr. Bylece zamanmz hata ayklamak yerine, ondan ok daha zevkli ve verimli olan kod yazmaya ayrabiliriz.

43.3

Szlemeli programlamay etkisizletirmek


Birim testlerinin tersine, szlemeli programlama normalde etkilidir; etkisizletirmek iin zel bir derleyici veya gelitirme ortam ayar gerekir. Bunun iin dmd derleyicisinde release seenei kullanlr: dmd deneme.d -ofdeneme -w -release Program o seenekle derlendiinde in , out , ve invariant bloklar programa dahil edilmezler.

43.4

Problem
ki futbol takmnn puanlarn bir man sonucuna gre arttran bir ilev yazn. Bu ilevin ilk iki parametresi, birinci ve ikinci takmn attklar goller olsun. Son iki parametresi de bu takmlarn matan nceki puanlar olsun. Bu ilev, golleri dikkate alarak birinci ve ikinci takmn puanlarn dzenlesin: fazla gol atan taraf puan kazansz, goller eitse iki takm da birer puan kazansnlar. Ek olarak, ilevin dn deeri de kazanan taraf belirtsin: birinci kazanmsa 1, ikinci kazanmsa 2, berabere kalmlarsa 0. Aadaki programla balayn ve ilevin drt blounu uygun ekilde doldurun. Benim main iine yazdm assert ifadelerini silmeyin; onlar, benim bu ilevin almas konusundaki beklentilerimi belgeliyorlar. int puanEkle(in int goller1, in int goller2, ref int puan1, ref int puan2) in { // ... }

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

210

Szlemeli Programlama

out (sonu) { // ... } body { // ... return -1; } unittest { // ... } void main() { int birincininPuan = 10; int ikincininPuan = 7; int kazananTaraf; kazananTaraf = puanEkle(3, 1, birincininPuan, ikincininPuan); assert(birincininPuan == 13); assert(ikincininPuan == 7); assert(kazananTaraf == 1); kazananTaraf = puanEkle(2, 2, birincininPuan, ikincininPuan); assert(birincininPuan == 14); assert(ikincininPuan == 8); assert(kazananTaraf == 0);

Not: Aslnda burada deeri olan bir enum tr dndrmek ok daha doru olur: enum MaSonucu { birinciKazand, ikinciKazand, berabere } MaSonucu puanEkle(in int goller1, in int goller2, ref int puan1, ref int puan2) // ... Ben out blounda int trnden bir dn deerini denetleyebilelim diye ilevde int setim. ... zm

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

211

Yaam Sreleri ve Temel lemler

44

Yaam Sreleri ve Temel lemler


ok yaknda yap ve snflar anlatmaya balayacam. Yaplarn kullanc trlerinin temeli olduklarn greceiz. Onlar sayesinde temel trleri ve baka yaplar bir araya getirerek yeni trler oluturabileceiz. Daha sonra, D'nin nesneye dayal programlama olanaklarnn temelini oluturan snflar tanyacaz. Snflar, baka trleri bir araya getirmenin yannda bu trlerle ilgili zel ilemleri de belirlememizi salayacaklar. O konulara gemeden nce, imdiye kadar hi zerinde durmadan kullandmz baz temel kavramlar ve temel ilemleri aklamam gerekiyor. Bu kavramlar ileride yap ve snf tasarmlar srasnda yararl olacak. imdiye kadar kavramlar temsil eden veri yaplarna deiken adn verdik. Bir ka noktada da yap ve snf trnden olan deikenlere zel olarak nesne dedik. Ben bu derste bunlarn hepsine birden genel olarak deiken diyeceim. Herhangi bir trden olan herhangi bir veri yaps, en azndan bu ders kapsamnda deiken adn alacak. Bu derste yalnzca imdiye kadar grdmz temel trleri, dizileri, ve eleme tablolarn kullanacam; siz bu kavramlarn btn trler iin geerli olduklarn aklnzda tutun.

44.1

Deikenlerin yaam sreleri


Bir deikenin tanmlanmas ile balayan ve geerliliinin bitmesine kadar geen sreye o deikenin yaam sreci denir. Geerliliin bitmesi kavramn isim alan dersinde deikenin tanmland kapsamdan klmas olarak tanmlamtm. O konuyu hatrlamak iin u rnee bakalm: void hzDenemesi() { int hz;

// tek bir deiken ...

for (int i = 0; i != 10; ++i) { hz = 100 + i; // ... 10 farkl deer alr // ... } // yaam burada sonlanr

O koddaki hz deikeninin yaam sreci hzDenemesi ilevinden kldnda sona erer. Orada 100 ile 109 arasnda 10 deiik deer alan tek bir deiken vardr. Aadaki kodda ise durum yaam sreleri asndan ok farkldr: void hzDenemesi() { for (int i = 0; i != 10; ++i) { int hz = 100 + i; // 10 farkl deiken vardr // ... } // yaamlar burada sonlanr } O kodda her birisi tek bir deer alan 10 farkl deiken vardr: dngnn her tekrarnda hz isminde yeni bir deiken yaamaya balar; yaam, dngnn kapama parantezinde sona erer.

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

212

Yaam Sreleri ve Temel lemler

44.2

Parametrelerin yaam sreleri


lev parametreleri dersinde tandmz parametre trlerine bir de yaam sreleri asndan bakmakta yarar var: in : Parametrenin yaam ileve girildii an balar ve ilevden kld an sona erer. Parametrenin deeri, ilev arlrken kullanlan deerin bir kopyasdr. ref : Parametre aslnda ilev arldnda kullanlan deikenin takma ismidir. Parametrenin asl deikenin yaam sreci zerinde etkisi yoktur. out : Parametre aslnda ilev arldnda kullanlan deikenin takma ismidir. ref 'ten farkl olarak, ileve girildiinde asl deikene nce otomatik olarak trnn .init deeri atanr, ve bu deer daha sonra ilev iinde deitirilebilir. lazy : Parametre tembel olarak iletildiinden, yaam kullanld an balar ve yine o an biter. Bu drt parametre trn kullanan ve yaam srelerini aklayan bir rnek: void main() { int main_in; int main_ref; int main_out;

// deeri ileve kopyalanr // ileve kendisi olarak ve kendi // deeriyle gnderilir // ileve kendisi olarak gnderilir; // ileve girildii an deeri sfrlanr

ilev(main_in, main_ref, main_out, birHesap());

void ilev( in int p_in,

// yaam main_in'in kopyas olarak // ileve girilirken balar ve ilevden // klrken sonlanr // main_ref'in takma ismidir // main_out'un takma ismidir; ref'ten // farkl olarak, ileve girildiinde // deeri nce int.init olarak atanr // // // // // yaam ilev iinde kullanld an balar ve eer kullanm bitmise hemen o an sonlanr; deeri iin, her kullanld an 'birHesap' ilevi arlr

ref int p_ref, out int p_out,

lazy int p_lazy)

{ }

// ...

int birHesap() { // ... }

44.3

Temel ilemler
Hangi trden olursa olsun, bir deikenin yaam boyunca etkili olan temel ilem vardr: Kurma: Yaamn balangc. Sonlandrma: Yaamn sonu. Atama: Deerin deimesi.

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

213

Yaam Sreleri ve Temel lemler

Deikenlerin yaam sreleri, kurma ilemiyle balar ve sonlandrma ilemiyle sona erer. Bu sre boyunca deikene yeni deerler atanabilir.

44.4

Kurma
Her deiken, kullanlmadan nce kurulmak zorundadr. Burada "kurma" szcn "hazrlamak, ina etmek" anlamlarnda kullanyorum. Kurma iki alt admdan oluur: 1. Yer ayrlmas: Deikenin yaayaca yer belirlenir. 2. lk deerinin verilmesi: O adrese ilk deeri yerletirilir. Her deiken bilgisayarn belleinde kendisine ayrlan bir yerde yaar. Derleyicinin istediimiz ileri yaptrmak iin mikro ilemcinin anlad dilde kodlar rettiini biliyorsunuz. Derleyicinin rettii kodlarn bir blmnn grevi, tanmlanan deikenler iin bellekten yer ayrmaktr. rnein tamsay bir kavram temsil eden yle bir deiken int hz = 123; bellekte bir int 'in bykl kadar yer tutar. Bellei soldan saa doru bir erit halinde devam ediyormu gibi gsterirsek, o deikenin bellekte u ekilde yaadn dnebiliriz: --+-----+-----+-----+-| | 123 | | --+-----+-----+-----+-Her deikenin bellekte bulunduu yere o deikenin adresi denir. Bir anlamda o deiken o adreste yaamaktadr. Programda bir deikenin deerini deitirdiimizde, o deiken iin bellekte ayrlan yere, deikenin yeni deeri yerletirilir: ++hz; Ayn adresteki deer bir artar: --+-----+-----+-----+-| | 124 | | --+-----+-----+-----+-Kurma, deikenin yaam balad anda gerekletirilir; nk deikeni kullanma hazrlayan ilemleri ierir. Onun herhangi bir ekilde kullanlabilmesi iin kurulmu olmas nemlidir. Deikenler farkl ekilde kurulabilirler: varsaylan ekilde: biz deer vermezsek kopyalanarak: baka bir deikenin deeriyle belirli bir deerle: bizim tarafmzdan belirlenen deerle Hibir deer kullanlmadan kurulduunda deikenin deeri o trn varsaylan deeridir. Varsaylan deer, her trn .init niteliidir: int hz; O durumda hz 'n deeri int.init 'tir; yani 0. Varsaylan deerle kurulan bir deikenin daha sonradan baka deerler alacan dnebiliriz.

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

214

Yaam Sreleri ve Temel lemler

File dosya; Dosyalar dersinde grdmz std.stdio.File trnden olan yukardaki dosya nesnesi ise iletim sisteminin hibir dosyasna bal olmayan bir File yaps nesnesidir. Onun da iletim sisteminin hangi dosyasna erimek iin kullanlacann daha sonradan belirleneceini dnebiliriz; varsaylan ekilde kurulmu olduu iin henz kullanlamaz. Deiken bazen baka bir deikenin deeri kopyalanarak kurulur: int hz = bakaHz; O durumda hz 'n deeri bakaHz 'n deerinden kopyalanr ve hz yaamna o deerle balar. Snf deikenlerinde ise durum farkldr: auto snfDeikeni = bakaSnfDeikeni; snfDeikeni de yaamna bakaSnfDeikeni 'nin kopyas olarak balar. Aralarndaki nemli ayrm; hz ile bakaHz 'n birbirlerinden farkl iki deer olmalar, te yandan snfDeikeni ile bakaSnfDeikeni 'nin ayn nesneye eriim salamalardr. Bu nemli nokta, deer trleri ile referans trleri arasndaki farktan ileri gelir. Bu konuyu bir sonraki derste anlatacam. Son olarak, deikenler belirli deerlerle veya zel ekillerde kurulabilirler: int hz = birHesabnSonucu(); hz 'n ilk deeri, programn almas srasndaki bir hesabn deeri olarak belirlenir. auto snfDeikeni = new BirSnf; snfDeikeni , yaamna new ile kurulan nesneye eriim salayacak ekilde balar.

44.5

Sonlandrma
Deikenin yaamnn sona ermesi srasnda yaplan ilemlere sonlandrma denir. Kurma gibi, sonlandrma da iki admdan oluur: 1. son ilemler: deikenin yapmas gereken son ilemler iletilir 2. bellein geri verilmesi: deikenin yaad yer geri verilir Temel trlerin ounda, sonlandrma srasnda zel ilemler gerekmez. rnein int trnden bir deikenin bellekte yaamakta olduu yere sfr gibi zel bir deer atanmaz. Program o adresin artk bo olduunun hesabn tutar ve oray daha sonra baka deikenler iin kullanr. te yandan, baz trlerden olan deikenlerin yaamlarnn sonlanmas srasnda zel ilemler gerekebilir. rnein bir File nesnesi, eer varsa, ara belleinde tutmakta olduu karakterleri diske yazmak zorundadr. Ek olarak, dosyayla iinin bittiini iletim sistemine bildirmek iin de dosyay kapatmak zorundadr. Bu ilemler, File 'n sonlandrma ilemleridir. Dizilerde durum biraz daha st dzeydedir: btn dizi sonlanrken, o dizinin sahip olduu btn elemanlar da sonlanrlar. Eer dizinin elemanlar temel trlerdense, zel bir sonlanma ilemi gerekmez. Ama eer dizinin elemanlar sonlanma gerektiren bir yap veya snf trndense, o trn sonlandrma ilemleri her eleman iin uygulanr.

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

215

Yaam Sreleri ve Temel lemler

Sonlandrma, eleme tablolarnda da dizilerdeki gibidir. Ek olarak, eleme tablosunun sahip olduu indeks deikenleri de sonlandrlrlar. Eer indeks tr olarak bir yap veya snf tr kullanlmsa, her indeks nesnesi iin o trn gerektirdii sonlandrma ilemleri uygulanr. p toplayc: D, p toplaycl bir dildir. Bu tr dillerde sonlandrma ilemleri programc tarafndan aka yaplmak zorunda deildir. Yaam sona eren bir deikenin sonlandrlmas otomatik olarak p toplayc denen dzenek tarafndan halledilir. Deikenler iki ekilde sonlandrlabilirler: hemen: sonlandrma ilemleri hemen iletilir sonra: p toplayc tarafndan ilerideki bir zamanda Bir deikenin bunlardan hangi ekilde sonlandrlaca ncelikle kendi trne baldr. Temel trlerin hemen sonlandrldklarn dnebilirsiniz nk zaten sonlandrma iin zel ilemleri yoktur. Baz trlerin deikenlerinin son ilemleri ise p toplayc tarafndan daha sonraki bir zamanda iletilebilir.

44.6

Atama
Bir deikenin yaam boyunca karlat dier nemli ilem atamadr. Temel trlerde atama ilemi yalnzca deikenin deerinin deitirilmesi olarak grlebilir. Yukardaki bellek gsteriminde olduu gibi, deiken rnein 123 olan bir deer yerine artk 124 deerine sahip olabilir. Daha genel olarak aslnda atama ilemi de iki admdan oluur: 1. eski deerin sonlandrlmas: eer varsa, sonlandrma ilemleri ya hemen, ya da p toplayc tarafndan daha sonra yaplr 2. yeni deerin verilmesi: eski deerin yerine yeni deer atanr Bu iki adm, sonlandrma ilemleri olmayan temel trlerde nemli deildir. Ama sonlandrma ilemleri bulunan trlerde atamann byle iki admdan olutuunu aklda tutmakta yarar vardr: atama aslnda bir sonlandrma ve bir yeni deer verme ilemidir. Bundan sonraki derste trlerin nasl deer trleri ve referans trleri diye ikiye ayrldklarn anlatacam.

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

216

Deerler ve Referanslar

45

Deerler ve Referanslar
Yaknda balayacamz yaplara ve snflara gemeden nce deer trleri ve referans trleri kavramlarn anlamak gerekiyor. Bu bilgileri bir nceki dersteki nesne yaam sreleri konusuyla badatrnca yap ve snf nesnelerini anlamak daha kolay olacak. Bu derste ayrca deikenlerin adreslerini bildiren & ilecini de tantacam. En sonda da u iki nemli kavram gsteren bir tablo vereceim: deer karlatrmas adres karlatrmas

45.1

Deer tr
Bunun tanm son derece basittir: Deikenleri deer tayan trlere deer tr denir. rnein btn temel trler ve sabit uzunluklu diziler deer trleridir, nk bu trlerden olan her deikenin kendi deeri vardr. rnein int trnden bir deiken, bir tamsay deer tar: int hz = 123; Bellei nceki derste grdmz gibi bir erit olarak hayal edersek, bu deikenin bellekte u ekilde yaadn dnebiliriz: hz ---+-----+--| 123 | ---+-----+--Deer trlerinin deikenleri kopyalandklarnda kendi zel deerlerini edinirler: int yeniHz = hz; Yeni deikene bellekte kendisine ait bir yer ayrlr ve yeniHz 'n da kendi deeri olur: hz ---+-----+--| 123 | ---+-----+--yeniHz ---+-----+--| 123 | ---+-----+---

Doal olarak, artk bu deikenlerde yaplan deiiklikler birbirlerinden bamszdr: hz = 200; Yeni deikenin deeri deimez: hz ---+-----+--| 200 | ---+-----+--yeniHz ---+-----+--| 123 | ---+-----+---

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

217

Deerler ve Referanslar

45.1.1

assert hatrlatmas
Bu derste kavramlarn doruluklarn gstermek iin assert dersinde grdmz assert ifadelerini kullanacam. Aadaki rneklerde kullandm assert ifadelerini "bu dorudur" demiim gibi kabul edin. rnein aadaki assert(hz == yeniHz) ifadesi, "hz, yeniHz'a eittir" anlamna geliyor.

45.1.2

Deer kimlii
Yukardaki gsterimlerden de anlalabilecei gibi, deikenlerin eitlikleri iki anlamda ele alnabilir: Deer eitlii: imdiye kadar bir ok rnekte kullandmz == ileci, deikenlerin deerlerini karlatrr. Birbirlerinden farkl olan iki deikenin bu adan eit olmalar, onlarn deerlerinin eit olmalar anlamna gelir. Deer kimlii: Kendi deerlerine sahip olmalar asndan bakldnda, hz ve yeniHz 'n ayr kimlikleri vardr. Deerleri eit olsalar bile, birisinde yaplan deiiklik dierini etkilemez. int hz = 123; int yeniHz = hz; assert(hz == yeniHz); hz = 200; assert(hz != yeniHz);

45.1.3

Adres ileci &


Daha nce readf kullanmnda grdmz gibi, bu ile deikenin adresini dndrr. Okuduu bilgiyi hangi deikene yazacan readf 'e o deikenin adresini vererek bildiriyorduk. Deikenlerin adreslerini baka amalar iin de kullanabiliriz. Bir rnek olarak iki farkl deikenin adresini yazdran bir kod yle yazlabilir: int hz = 123; int yeniHz = hz; writeln("hz : ", hz, " adresi: ", &hz); writeln("yeniHz: ", yeniHz, " adresi: ", &yeniHz); hz ve yeniHz deikenlerinin deerleri ayndr, ama yukarda da gsterildii gibi bu deerler bellein farkl adreslerinde bulunmaktadrlar: hz : 123 adresi: BF9A78F0 yeniHz: 123 adresi: BF9A78F4 Not: Program her altrdnzda farkl adresler grmeniz normaldir. Bu deikenler iletim sisteminden alnan bellein bo yerlerine yerletirilirler. Deiken adresleri normalde onaltl say sisteminde yazdrlr. Ayrca, adreslerin int 'in uzunluu olan 4 kadar farkl olmalarna bakarak o deikenlerin bellekte yan yana durduklarn da anlayabiliriz.

45.2

Referans deikenleri
Referans trlerini anlatmaya gemeden nce referans deikenlerini tantmam gerekiyor.

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

218

Deerler ve Referanslar

Referans deikenleri, baka deikenlerin takma isimleri gibi kullanlan deikenlerdir. Her ne kadar kendileri deiken gibi olsalar da, kendi zel deerleri yoktur. Byle bir deikende yaplan bir deiiklik asl deikeni etkiler. Referans deikenlerini aslnda imdiye kadar iki konuda grm, ama zerinde fazla durmamtk: foreach dngsnde ref ile: Bir grup elemana foreach dngsnde srayla eriilirken ref anahtar szc kullanldnda; eleman, dizi elemannn kendisi anlamna geliyordu. Kullanlmadnda ise dizi elemannn kopyas oluyordu. Bunu, & ileci ile de gsterebiliriz. Adresleri ayn ise, iki farkl deiken aslnda bellein ayn yerindeki bir deere eriim salyorlar demektir: int[] dizi = [ 0, 1, 2, 3, 4 ]; foreach (i, ref eleman; dizi) { assert(&eleman == &dizi[i]); } Her ne kadar farkl iki deiken olsalar da, & ileci ile alnan adreslerinin eit olmalar, dngnn her tekrarnda tanmlanan eleman ve dizi[i] 'nin deer kimlii asndan aslnda ayn olduklarn gsterir. Bir baka deyile, eleman , dizi[i] 'nin takma ismidir. Daha baka bir deyile, eleman ile dizi[i] ayn deere eriim salarlar. Birisinde yaplan deiiklik, asl deeri etkiler. eleman takma ismi, srayla asl dizi elemanlarnn takma ismi haline gelir. Bu durumu dngnn i 'nin rnein 3 olduu tekrar iin yle gsterebiliriz: dizi[0] dizi[1] dizi[2] dizi[3] dizi[4] (eleman) --+--------+--------+--------+--------+---------+-| 0 | 1 | 2 | 3 | 4 | --+--------+--------+--------+--------+---------+- ref ve out ilev parametrelerinde: lev parametreleri ref veya out olarak tanmlandklarnda, ileve gnderilen asl deikenin takma ismi gibi ilem grrler. Bunu grmek iin, byle iki parametre alan bir ilevin iki parametresine de ayn deikeni gnderelim. Ayn deer kimliine sahip olduklarn & ileci ile u ekilde kantlayabiliriz: import std.stdio; void main() { int asl; writeln("asl'n adresi ilev(asl, asl); }

: ", &asl);

void ilev(ref int referans, out int k) { writeln("referans'n adresi: ", &referans); writeln("k'n adresi : ", &k); assert(&referans == &k); } Her ne kadar farkl parametre olarak tanmlansalar da, ilev iindeki referans ve k aslnda ayn deere eriim salarlar; nk zaten ikisi de main iindeki asl 'n takma ismidir:

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

219

Deerler ve Referanslar

asl'n adresi : BFCA9F34 referans'n adresi: BFCA9F34 k'n adresi : BFCA9F34 O koddaki referans deikenlerinin bellekte kendilerine ait yerleri olmadn ve asl 'n takma isimleri olduklarn aadaki ekilde ifade edebiliriz.: asl (referans) (k) ---+-----+--| 0 | ---+-----+---

45.3

Referans tr
Baz trlerden olan deikenler kendi kimlikleri olduu halde kendileri deer tamazlar; deer tayan baka deikenlere eriim salarlar. Byle trlere referans tr denir. Bu kavram daha nce dizi dilimlerinde grmtk. Dilimler, varolan baka bir dizinin elemanlarna eriim salayan trlerdir; kendi elemanlar yoktur: int[] dizi = [ 0, 1, 2, 3, 4 ]; // bataki ve sondaki eleman dlayarak ortadaki ne // eriim salayan bir dilim: int[] dilim = dizi[1 .. $ - 1]; // imdi dilim[0] ile dizi[1] ayn deere eriirler: assert(&dilim[0] == &dizi[1]); // gerekten de dilim[0]'da yaplan deiiklik dizi[1]'i // etkiler: dilim[0] = 42; assert(dizi[1] == 42); Referans deikenlerinin tersine, referans trleri yalnzca takma isim deildirler. Bunu grmek iin ayn dilimin kopyas olan bir dilim daha oluturalm: int[] dilim2 = dilim; Bu iki dilim kendi adresleri olan, bir baka deyile kendi kimlikleri olan deikenlerdir; dilim2 ile dilim farkl adreslerde yaarlar. Hatta dizi 'yi de katacak olursak, nn de adresi farkldr: assert(&dizi != &dilim); assert(&dizi != &dilim2); assert(&dilim != &dilim2); Not: Aslnda dinamik diziler (uzunluu sabit olmayan diziler) de teknik olarak dilimdirler. te referans deikenleri ile referans trlerinin ayrm buna dayanr: Referans deikenlerinin kendi kimlikleri yoktur, onlar baka bir deikenin takma ismidirler Referans trnden olan deikenler ise, kendi kimlikleri olan deikenlerdir; ama yine baka bir deere eriim salarlar rnein yukardaki dilim ve dilim2 'yi bellek zerinde yle gsterebiliriz:

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

220

Deerler ve Referanslar

dilim dilim2 ---+---+---+---+---+---+--- ---+---+--- ---+---+--| 0 | 1 | 2 | 3 | 4 | | o | | o | ---+---+---+---+---+---+--- ---+-|-+--- ---+-|-+-- | | | | | +--------------------+------------+ O dilimlerin eriim saladklar asl dizi elemanlarn mavi ile gsterdim. D'nin gl bir olana olan snflar daha ilerideki derslerde greceiz. D'yi C++'dan ayran nemli farklardan birisi, D'nin snflarnn referans trleri olmalardr. Yalnzca bunu gstermi olmak iin ok basit bir snf tanmlayacam: class BirSnf { int ye; } Snf nesneleri, daha nce hata atarken de kullandmz new ile oluturulurlar: auto deiken = new BirSnf; Bu durumda deiken , new ileciyle oluturulmu olan isimsiz bir BirSnf nesnesine eriim salayan bir referanstr: (isimsiz BirSnf nesnesi) deiken ---+-------------------+--- ---+---+--| ... | | o | ---+-------------------+--- ---+-|-+-- | | | +--------------------+ Dilimlere benzer ekilde, deiken kopyalandnda kopyas da ayn nesneye eriim salar. Bunu == ileci ile grebiliriz. Snflarla kullanldnda == ileci, ayn nesneye eriim asndan eit olup olmama bilgisini verir: auto deiken = new BirSnf; auto deiken2 = deiken; assert(deiken == deiken2); assert(&deiken != &deiken2); Yukarda grld gibi, eriim salama asndan eit olsalar da, adresleri farkl olduu iin farkl deikenlerdir. Dilimlerde olduu gibi: (isimsiz BirSnf nesnesi) deiken deiken2 ---+-------------------+--- ---+---+--- ---+---+--| ... | | o | | o | ---+-------------------+--- ---+-|-+--- ---+-|-+-- | | | | | +--------------------+------------+ Byle iki farkl BirSnf nesnesinin gerekten de ayn nesneye eriim saladklarn bir de yle gsterebiliriz: auto deiken = new BirSnf; deiken.ye = 1; auto deiken2 = deiken; // ayn nesneyi paylarlar

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

221

Deerler ve Referanslar

deiken2.ye = 2; assert(deiken.ye == 2); // deiken'in de eriim // salad nesne // deimitir

deiken2 yoluyla 2 deerini alan ye, deiken 'in de eriim salad nesnenin yesidir. Baka bir referans tr, eleme tablolardr. Eleme tablolar da atandklarnda ayn asl tabloya eriim salarlar: string[int] isimleSaylar = [ 1 : "bir", 10: "on", 100:"yz", ]; // ayn asl tabloyu paylamaya balarlar: string[int] isimleSaylar2 = isimleSaylar; // rnein ikincisine eklenen eleman ... isimleSaylar2[4] = "drt"; // ... birincisinde de grnr assert(isimleSaylar[4] == "drt");

45.3.1

Atama ileminin fark


Deer trlerinde ve referans deikenlerinde atama ileminin sonucunda asl deer deiir: void main() { int say = 8; yaryaBl(say); assert(say == 4); // asl deer deiir

void yaryaBl(ref int blnen) { blnen /= 2; } Referans trlerinde ise atama ilemi, hangi asl nesneye eriim salandn deitirir. rnein aadaki kodda dilim 'e yaplan atama ilemi onun eritirdii elemanlar deitirmez; dilim 'in baka elemanlar gstermesini salar: void main() { int[] dizi1 = [ 10, 11, 12, 13, 14 ]; int[] dizi2 = [ 20, 21, 22 ]; int[] dilim = dizi1[1 .. 3]; // 1 ve 2 indeksli elemanlara // eritirir dilim[0] = 777; assert(dizi1 == [ 10, 777, 12, 13, 14 ]); // Bu atama ilemi dilim'in eritirdii elemanlar // deitirmez, dilim'in artk baka elemanlara // eriim salamasna neden olur dilim = dizi2[$ - 1 .. $]; // sonuncu elemana eritirir dilim[0] = 888;

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

222

Deerler ve Referanslar

assert(dizi2 == [ 20, 21, 888 ]);

Atama ilecinin referans trlerindeki bu etkisini bir de BirSnf trnde grelim: auto deiken1 = new BirSnf; deiken1.ye = 1; auto deiken2 = new BirSnf; deiken2.ye = 2; auto kopya = deiken1; kopya.ye = 3; kopya = deiken2; kopya.ye = 4; assert(deiken1.ye == 3); assert(deiken2.ye == 4); Oradaki atama ilemleri sonucunda kopya nce deiken1 'in nesnesine, sonra da deiken2 'nin nesnesine eriim salar. kopya yoluyla deeri deitirilen ye ilk seferde deiken1 'inkidir, sonra ise deiken2 'ninkidir.
45.3.2

Referans trleri hibir deere eriim salamyor olabilirler


Referans deikenlerinde mutlaka bir asl deer vardr; onlarn yaam sreleri eriim saladklar bir asl deer olmadan balamaz. Referans trlerinin deikenleri ise, henz hibir deere eriim salamayacak ekilde oluturulabilirler. rnein bir BirSnf deikeni, eriim salad nesne henz belli olmadan yle tanmlanabilir: BirSnf deiken; Byle deikenler null zel deerine eittirler. Bu zel deeri ve is anahtar szcn bir sonraki derste anlatacam.

45.4

Sabit uzunluklu diziler deer, dinamik diziler referans


D'nin iki dizi tr bu konuda farkllk gsterir. Dinamik diziler (dilimler), yukardaki rneklerde de grld gibi, referans trleridir. Dinamik diziler, kendilerine ait olmayan elemanlara eriim salarlar. Temel ilemler asndan referans olarak davranrlar. Sabit uzunluklu diziler ise deer trleridir. Kendi elemanlarna sahiptirler ve deer tr olarak davranrlar: int[3] dizi1 = [ 10, 20, 30 ]; // dizi2'nin elemanlar dizi1'inkilerden farkl olur auto dizi2 = dizi1; dizi2[0] = 11; // lk dizi deimez assert(dizi1[0] == 10);

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

223

Deerler ve Referanslar

Tanmland zaman uzunluu da belirlendii iin dizi1 sabit uzunluklu bir dizidir. auto anahtar szc nedeniyle dizi2 de ayn tr edinir. Her ikisi de kendi elemanlarna sahiptirler. Birisinde yaplan deiiklik dierini etkilemez.

45.5

zet
Deer trlerinden olan her deikenin kendi deeri ve kendi adresi vardr Referans deikenlerinin ne deerleri vardr ne de adresleri; takma isim gibidirler Referans trlerinden olan deikenlerin kendi adresleri vardr; ama eriim saladklar deer kendilerinin deildir Referans trlerinde atama ilemi deer deitirmez, hangi asl nesneye eriildiini deitirir Referans trlerinden olan deikenler null olabilirler Yukarda anlatlan deiik trlerin deerlerine ve adreslerine == ilecini uygulaynca ortaya yle bir tablo kyor:
Deiken Tr a == b &a == &b ===================================================================== ayn deerli deikenler (deer tr) true false farkl deerli deikenler (deer tr) false false ref deikenli foreach true true ref olmayan deikenli foreach true false out parametreli ilev true true ref parametreli ilev true true in parametreli ilev true false ayn elemanlara erien dilimler true false farkl elemanlara erien dilimler false false ayn nesneye erien BirSnf'lar (referans tr) true false farkl nesneye erien BirSnf'lar (referans tr) false false

O tabloyu u programla rettim: import std.stdio; import std.conv; int evrenselDeiken = 9; class BirSnf { int ye; } void balkiz() { const dchar[] balk = " " writeln(); writeln(balk); foreach (i; 0 .. balk.length) { write('='); } } writeln();

Deiken Tr" a == b &a == &b";

void bilgiSatr(const dchar[] balk, bool deerEitlii, bool adresEitlii) { writefln("%50s%9s%9s", balk, to!string(deerEitlii), to!string(adresEitlii)); }

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

224

Deerler ve Referanslar

void main() { balkiz(); int say1 = 12; int say2 = 12; bilgiSatr("ayn deerli deikenler (deer tr)", say1 == say2, &say1 == &say2); int say3 = 3; bilgiSatr("farkl deerli deikenler (deer tr)", say1 == say3, &say1 == &say3); int[] dizi = [ 4 ]; foreach (i, ref eleman; dizi) { bilgiSatr("ref deikenli foreach", eleman == dizi[i], &eleman == &dizi[i]); } foreach (i, eleman; dizi) { bilgiSatr("ref olmayan deikenli foreach", eleman == dizi[i], &eleman == &dizi[i]); } outParametre(evrenselDeiken); refParametre(evrenselDeiken); inParametre(evrenselDeiken); int[] uzunDizi = [ 5, 6, 7 ]; int[] dilim1 = uzunDizi; int[] dilim2 = dilim1; bilgiSatr("ayn elemanlara erien dilimler", dilim1 == dilim2, &dilim1 == &dilim2); int[] dilim3 = dilim1[0 .. $ - 1]; bilgiSatr("farkl elemanlara erien dilimler", dilim1 == dilim3, &dilim1 == &dilim3); auto deiken1 = new BirSnf; auto deiken2 = deiken1; bilgiSatr( "ayn nesneye erien BirSnf'lar (referans tr)", deiken1 == deiken1, &deiken1 == &deiken2); auto deiken3 = new BirSnf; bilgiSatr( "farkl nesneye erien BirSnf'lar (referans tr)", deiken1 == deiken3, &deiken1 == &deiken3);

void outParametre(out int parametre) { bilgiSatr("out parametreli ilev", parametre == evrenselDeiken, &parametre == &evrenselDeiken); } void refParametre(ref int parametre) { bilgiSatr("ref parametreli ilev", parametre == evrenselDeiken, &parametre == &evrenselDeiken);

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

225

Deerler ve Referanslar

} void inParametre(in int parametre) { bilgiSatr("in parametreli ilev", parametre == evrenselDeiken, &parametre == &evrenselDeiken); } Programda ilev parametrelerini karlatrmak iin bir de evrensel deiken kullandm. Evrensel deikenler ilevlerin dnda tanmlanrlar ve iinde tanmlandklar kaynak dosyadaki (modldeki) btn kodlar tarafndan eriilebilirler.

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

226

null deeri ve is ileci

46

null deeri ve is ileci


Bir nceki derste referans trnden olan deikenlerin hibir nesneye eriim salamadan da oluturulabileceklerini sylemitim: BirSnf eriimSalayan = new BirSnf; BirSnf deiken; // eriim salamayan

Bir referans tr olduu iin yukardaki deiken 'in bir kimlii vardr; ama eriim salad bir nesne henz yoktur. Byle bir deikenin bellekte u ekilde durduunu dnebiliriz: deiken ---+------+--| null | ---+------+--Hibir nesneye eriim salamayan referanslarn deerleri null 'dr. Bunu aada anlatyorum. Byle bir deiken kendisine bir nesne atanana kadar kullanlamaz bir durumdadr. Doal olarak, eriim salad bir BirSnf nesnesi olmad iin o deiken ile ilemler yapmamz beklenemez: void kullan(BirSnf deiken) { writeln(deiken.ye); } void main() { BirSnf deiken; kullan(deiken); } kullan ilevine verilen deiken hibir nesneye eriim salamad iin, olmayan nesnenin ye 'sine eriilmeye allmas programn kmesine neden olur: $ ./deneme Segmentation fault "Segmentation fault", programn geerli olmayan bir bellek blgesine erimeye alt iin iletim sistemi tarafndan acil olarak sonlandrldn gsterir.

46.1

null deeri
Eriim salad nesne henz belli olmayan referans tr deikenleri null zel deerine sahiptir. Bu deeri de herhangi baka bir deer gibi yazdrabiliriz: writeln(null); Bu deer, dier btn dillerde olduu gibi, D'de de 0 zel deerini tar. Yukardaki satrn kts: 0 Deeri null olan bir deiken ok kstl sayda ilemde kullanlabilir:

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

227

null deeri ve is ileci

1. Eriim salamas iin geerli bir nesne atamak deiken = new BirSnf; // artk nesnesi var

O atamadan sonra artk deiken 'in eriim salad bir nesne vardr. deiken artk BirSnf ilemleri iin kullanlabilir. 2. null olup olmadn denetlemek if (deiken == null) // derleme HATASI

Ne yazk ki, == ileci asl nesneleri karlatrd iin; ve null bir deikenin eritirdii geerli bir nesne bulunmad iin, o ifade derlenemez. Bu yzden, bir deikenin null olup olmadn denetlemek iin is ileci kullanlr.

46.2

is ileci
is , ngilizce'de "olmak" fiilinin "yledir" kullanmndaki anlamna sahiptir. Bu dersi ilgilendiren kullanmnda ikili bir iletir, yani sol ve sa tarafna iki deer alr. Bu iki deer aynysa true , deilse false retir. Not: is 'in rnein ablon olananda tekli ile olarak kullanld durumlar da vardr. ki deerden birisinin null olabildii durumlarda == ilecinin kullanlamadn yukarda grdk. Onun yerine is 'i kullanmak gerekir. "Bu deiken null ise" koulunu denetlemeye yarar: if (deiken is null) { // hibir nesneye eriim salamyor } is , baka trlerle de kullanlabilir. rnein iki tamsay deikenin deerleri yle karlatrlabilir: if (hz is yeniHz) { // ikisi ayn deerde } else { // ikisi farkl deerde } Dilimlerde de iki dilimin ayn elemanlara eriim salayp salamadklarn denetler: if (dilim is dilim2) { // ayn elemanlar paylayorlar }

46.3

!is ileci
== ve != ilelerine benzer ekilde, is 'in tersi !is ilecidir. Deerler eit olmadnda true retir: if (hz !is yeniHz) { // farkl deerlere sahipler }

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

228

null deeri ve is ileci

46.4

null deer atamak


Referans trleri eriim saladklar nesnelere sahip deildirler; yalnzca o nesnelere eriim salarlar. Byle bir deikene null deerini atamak, o deikenin artk hibir nesneye eriim salamamasna neden olur. Eer bu atama sonucunda asl nesneye erien baka referans deikeni kalmamsa, asl nesne p toplayc tarafndan sonlandrlacaktr. Hibir referans tarafndan eriilmiyor olmas, o nesnenin artk kullanlmadn gsterir. rnek olarak, nceki dersteki iki deikenin tek bir nesneye eritikleri duruma bakalm: auto deiken = new BirSnf; auto deiken2 = deiken; (isimsiz BirSnf nesnesi) deiken deiken2 ---+-------------------+--- ---+---+--- ---+---+--| ... | | o | | o | ---+-------------------+--- ---+-|-+--- ---+-|-+- | | | | | +--------------------+------------+ Bu deikenlerden birisine null atamak, onun bu deerle ilikisini keser: deiken = null; BirSnf nesnesine artk yalnzca deiken2 tarafndan eriilmektedir: (isimsiz BirSnf nesnesi) deiken deiken2 ---+-------------------+--- ---+----+--- ---+---+--| ... | |null| | o | ---+-------------------+--- ---+----+--- ---+-|-+- | | | +----------------------------------+ simsiz BirSnf nesnesine erien son referans olan deiken2 'ye de null atanmas, asl nesnenin sonlanmasna neden olur: deiken2 = null; p toplayc asl nesneyi trne gre ya hemen, ya da ilerideki bir zamanda sonlandracaktr. Program asndan artk o nesne yoktur nk o nesneye erien referans kalmamtr: deiken ---+----+--|null| ---+----+--deiken2 ---+----+--|null| ---+----+--

---+-------------------+--| | ---+-------------------+---

Eleme tablolar dersinin birinci problemi, bir eleme tablosunu boaltan yntem gsteriyordu. imdi o yntemlere bir drdncsn ekleyebiliriz; eleme tablosu deikenine null deer atamak, deikenin eriim salad asl tablo ile ilikisini keser: string[int] isimleSaylar; // ... isimleSaylar = null; // artk hibir elemana eriim // salamaz

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

229

null deeri ve is ileci

Yukardaki BirSnf rneine benzer ekilde, eer isimleSaylar asl tabloya eriim salayan son referans idiyse, asl tablonun elemanlar p toplayc tarafndan sonlandrlacaklardr. Bir dilimin de artk hibir elemana eriim salamas istenmiyorsa null atanabilir: int[] dilim = dizi[ 10 .. 20 ]; // ... dilim = null; // artk hibir elemana eriim salamaz

46.5

zet
null , hibir deere eriim salamayan referans deeridir null referanslar yalnzca iki ilemde kullanlabilirler: deer atamak, null olup olmadn denetlemek == ileci asl nesneye erimeyi gerektirebilecei iin, null olma olasl bulunan referanslar is ile denetlenmelidir is 'in tersi !is 'dir null atanan referans artk hibir elemana eriim salamaz Hibir referansn eriim salamad nesneler p toplayc tarafndan sonlandrlrlar

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

230

Tr Dnmleri

47
47.1

Tr Dnmleri
zet
nce tr dnmleri konusunda bilinmesi gerektiini dndm noktalar vermek istiyorum. Yaznn geri kalann daha ok bavuru kayna olarak dnebilirsiniz: Otomatik aritmetik tr dnmleri gvenli ynde yaplr: kk trden byk tre doru ve deiebilen trden deimez tre doru enum trler tamsay trlere otomatik olarak dnrler, ama tamsaylar enum trlere otomatik olarak dnmezler bool trnde false 0'a, true da 1'e otomatik olarak dnr; tersi de dorudur: 0 false 'a, 1 de true 'ya dnr Aka yaplan tr dnmnn sz dizimi yledir: cast (Trsmi)deer Tr dnmlerinden kanmak daha iyidir lemlerde kullanlan deiken ve nesne trlerinin hem o ilemlerle hem de birbirleriyle uyumlu olmalar gerekir. Yoksa anlamsz veya yanl sonular doabilir. D'nin de aralarnda bulunduu baz diller trlerin uyumluluklarn derleme zamannda denetlerler. Byle dillere "trleri derleme zamannda belli olan" anlamnda "statically typed" dil denir. Anlamsz ilem rnei olarak, bir toplama ileminde sanki bir sayym gibi dizgi kullanmaya alan u koda bakalm: char[] dizgi; writeln(dizgi + 5); Derleyici o kodu tr uyumazl nedeniyle reddeder. Bu yazy yazdm srada kullandm derleyici, trlerin uyumsuz olduunu bildiren u hatay veriyor: Error: incompatible types for ((dizgi) + (5)): 'char[]' and 'int' O hata mesaj, ((dizgi) + (5)) ifadesinde uyumsuz trler olduunu belirtir: char[] ve int . Tr uyumsuzluu, farkl tr demek deildir. nk farkl trlerin gvenle kullanlabildii ilemler de vardr. rnein double trndeki bir deikene int trnde bir deer eklenmesinde bir saknca yoktur: double toplam = 1.25; int art = 3; toplam += art; toplam ve art farkl trlerden olduklar halde o ilemde bir yanllk yoktur; nk bir kesirli saynn bir tamsay kadar arttrlmasnda bir uyumsuzluk olduu dnlemez.

47.2

Otomatik tr dnmleri
Her ne kadar bir double deerin bir int deer kadar arttrlmasnda bir saknca olmasa da, o ilemin mikro ilemcide yine de belirli bir trde yaplmas gerekir. Kesirli saylar dersinden hatrlayacanz gibi; 64 bitlik olan double , 32 bitlik olan int 'ten daha byk (veya geni) bir trdr. Bir int 'e sabilen her deer bir double 'a da sabilir. Birbirinden farkl trler kullanlan ilemlerle karlatnda, derleyici nce deerlerden birisini dier tre dntrr, ve ilemi ondan sonra gerekletirir. Bu dnmde kullanlan tr, deer kaybna neden olmayacak ekilde seilir. rnein double tr int trnn btn

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

231

Tr Dnmleri

deerlerini tutabilir, ama bunun tersi doru deildir. O yzden yukardaki toplam += art ilemi double trnde gvenle gerekletirilebilir. Dntrlen deer, her zaman iin isimsiz ve geici bir deiken veya nesnedir. Asl deerin kendisi deimez. rnein yukardaki += ilemi srasnda art 'n kendi tr deitirilmez, ama art 'n deerine eit olan geici bir deer kullanlr. Yukardaki ilemde perde arkasnda neler olduunu yle gsterebiliriz: double aslnda_isimsiz_olan_double_bir_deger = art; toplam += aslnda_isimsiz_olan_double_bir_deger; Derleyici, int deeri nce double trndeki geici bir ara deere dntrr ve ilemde o dntrd deeri kullanr. Bu rnekteki geici deer yalnzca += ilemi sresince yaar. Byle otomatik dnmler aritmetik ilemlerle snrl deildir. Birbirinin ayns olmayan trlerin kullanld baka durumlarda da otomatik tr dnmleri uygulanr. Eer kullanlan trler bir dnm sonucunda birlikte kullanlabiliyorlarsa, derleyici gerektike deerleri otomatik olarak dntrr. rnein int trnde parametre alan bir ileve byte trnde bir deer gnderilebilir: void birlem(int say) { // ... } void main() { byte kkDeer = 7; birlem(kkDeer); }

// otomatik tr dnm

Orada da nce kkDeer 'e eit geici bir int oluturulur, ve birlem o geici int deeri ile arlr.
47.2.1

Aritmetik dnmler
Aritmetik ilemlerde kullanlan deerler gvenli ynde, yani kk trden byk tre doru gerekletirilirler. Bu kadarn aklda tutmak ou durumda yeterlidir. Dnm kurallar yledir: 1. Deerlerden birisi real ise dieri real 'e dntrlr 2. Deilse ama birisi double ise dieri double 'a dntrlr 3. Deilse ama birisi float ise dieri float 'a dntrlr 4. Deilse tamsay terfisi uygulanr ve sonra u ilemlere geilir: 1. Eer iki tr de ayn ise durulur 2. Eer her ikisi de iaretli ise, veya her ikisi de iaretsiz ise; kk tr byk tre dntrlr 3. Eer iaretli tr iaretsiz trden bykse, iaretsiz olan iaretliye dntrlr 4. Hibirisi deilse iaretli tr iaretsiz tre dntrlr Daha kk tamsay trnden daha byk tre doru yaplan tamsay terfileri aadaki tablodakine benzer:

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

232

Tr Dnmleri

Hangi Trden bool byte ubyte short ushort char wchar dchar

Hangi Tre int int int int int int int uint

Benzer dnmler dier tamsaylar arasnda da vardr. rnein short , int , uint , vs. gibi kk trler; long ve ulong gibi daha byk trlere otomatik olarak dnrler.

47.3

Aka yaplan tr dnmleri


Baz durumlarda baz tr dnmlerini elle kendimiz yapmak zorunda kalabiliriz. Programcnn isteiyle yaplan tr dnmlerinin sz dizimi yledir: cast(Trsmi)deer cast parantezinin iine hedef tr yazlr ve deer o tre dntrlr. Yukarda anlatld gibi; int trnn kendisinden daha kk olan short 'a otomatik olarak dntrlmesi gvenli deildir. Bu yzden derleyici aadaki kodu kabul etmez: void kkTrlelem(short say) { // ... } void main() { int intSay = 42; kkTrlelem(intSay); }

// derleme HATASI

Eer o ilev arsnn yine de her zaman iin gvenli olduundan, rnein intSay 'nn deerinin programn almas srasnda her zaman iin short 'a da sabildiinden eminsek, tr dnmn elle kendimiz gerekletirebiliriz: kkTrlelem(cast(short)intSay); // imdi derlenir

O kodda intSay 'nn deerine eit olan short trnde bir deer oluturulur ve kkTrlelem o geici deerle arlr. Burada intSay 'nn trnn deimediine zellikle dikkat ekmek istiyorum. Onun trnde bir deiiklik olmaz, onun deeri kullanlarak isimsiz bir short deer oluturulur ve ilev o geici deerle arlr.

47.4

const dnm
Her referans tr, kendisinin const olanna otomatik olarak dnr. Bu gvenli bir dnmdr, nk hem zaten trn byklnde bir deiiklik olmaz, hem de const deerler deitirilemezler:

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

233

Tr Dnmleri

dchar[] parantezineAl(const dchar[] metin) { return "{" ~ metin ~ "}"; } // ... dchar[] birSz; birSz ~= "merhaba dnya"; parantezineAl(birSz); O kodda sabit olmayan birSz , sabit parametre alan ileve gvenle gnderilebilir, nk deerler sabit referanslar araclyla deitirilemezler. Bunun tersi doru deildir: const bir referans tr, const olmayan bir tre dnmez: const double[string] kesirler = [ "eyrek" : 0.25, "yarm" : 0.50 ]; double[string] bakaKesirler = kesirler; // derleme HATASI kesirler const olduu iin, elemanlarn deitirilmelerine izin vermez. O elemanlarn deitirilmelerinin engellenebilmesi iin onlara const olmayan bakaKesirler referansyla eriilmesi engellenmelidir. Derleyici bu yzden yukardaki dnm reddeder. Bu konu yalnzca referans deikenleri ve referans trleri ile ilgilidir. nk deer trlerinde zaten deer kopyaland iin, kopyann const olan asl nesneyi deitirmesi sz konusu olamaz: const int keAdedi = 4; int kopyas = keAdedi;

// derlenir (deer tr)

Yukardaki durumda const trden const olmayan tre dnm yasaldr; nk dntrlen deer asl deerin bir kopyas haline gelir; asl deeri deitirmesi sz konusu deildir. Bu yzden, otomatik const dnmleri yalnzca referanslarla ilgilidir.

47.5

enum dnmleri
enum dersinden hatrlayacanz gibi, enum trleri isimli deerler kullanma olana sunarlar: enum OyunKadRengi { Maa, Kupa, Karo, Sinek } Deerleri zellikle belirtilmedii iin o tanmda deerler sfrdan balayarak ve birer birer arttrlarak atanr. Buna gre rnein OyunKadRengi.Sinek 'in deeri 3 olur. Byle isimli enum deerleri, otomatik olarak tamsay trlere dnrler. rnein aadaki koddaki toplama ilemi srasnda OyunKadRengi.Kupa 1 deerini alr ve sonu 11 olur: int sonu = 10 + OyunKadRengi.Kupa; assert(sonu == 11); Bunun tersi doru deildir: tamsay deerler enum trlerine otomatik olarak dnmezler. rnein aadaki kodda renk deikeninin 2 deerinin karl olan OyunKadRengi.Karo deerini almasn bekleyebiliriz; ama derlenemez: OyunKadRengi renk = 2; // derleme HATASI

D, tamsaydan enum 'a dnmn aka yaplmasn art koar:

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

234

Tr Dnmleri

OyunKadRengi renk = cast(OyunKadRengi)2; veya daha ksa olarak: auto renk = cast(OyunKadRengi)2;

// derlenir

47.6

bool dnmleri
bool trnn deerlerinden false 0'a, true da 1'e otomatik olarak dnr: int birKoul = false; assert(birKoul == 0); int bakaKoul = true; assert(bakaKoul == 1); Bunun tersi de dorudur: 0 deeri false 'a, 1 deeri de true 'ya otomatik olarak dnr: bool birDurum = 0; assert(!birDurum); bool bakaDurum = 1; assert(bakaDurum);

// false // true

Sfr ve bir dndaki deerler otomatik olarak dnmezler. Bir ifadeyi aka bool trne dntrmek, o ifade 0 ise false , 0'dan farkl ise true deerini retir: bool birDurum = cast(bool)(5 - 5); assert(!birDurum);

// false

bool bakaDurum = cast(bool)(8 * 7); assert(bakaDurum); // true

47.7

Tr dnmlerinden kann
Hem otomatik hem de elle aka yaplan tr dnmleri programlarda artc durumlara neden olabilirler. rnek olarak yukarda grdmz cast(short) dnmne bakalm. Kodda ileride yaplabilecek bir deiiklik artk intSay 'nn deerini short 'a smayacak kadar byk hale getirebilir ve cast(short)intSay tr dnm yanl sonulara neden olabilir: import std.stdio; void kkTrlelem(short say) { writeln("ilevdeki deer: ", say); } void main() { int intSay = 40000; writeln("asl deer : ", intSay); kkTrlelem(cast(short)intSay); } Tamsaylar ve Aritmetik lemler dersinde grdmz gibi, 40000 deeri short.max 'tan daha byk olduu iin taar ve eksi bir deere dnr:

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

235

Tr Dnmleri

asl deer : 40000 ilevdeki deer: -25536

yanl deer

Bu yzden tr dnmlerinden genel olarak kanmak gerekir. En iyisi, hangi ilemlerin hangi trlerle yaplacana program tasarlanrken batan karar vermek ve ilemleri olabildiince tr dnmlerine bavurmadan o trlerle gerekletirmektir. Ne yazk ki ne kadar iyi olurlarsa olsunlar tasarmlar eninde sonunda ya kendileri deimek, ya da etkiletikleri baka ktphanelere uymak zorunda kalrlar. Bu yzden tr dnmleri az da olsa hemen hemen her programda bulunur.

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

236

Yaplar

48

Yaplar
Derslerin banda temel trlerin st dzey kavramlar ifade etmede yetersiz kalacaklarn sylemitim. int trnden bir tamsay rnein iki olay arasnda geen sreyi dakika trnden ifade etmek iin kullanlabilir; ama byle bir deer her zaman tek bana kullanl olamaz. Deikenler bazen baka deikenlerle bir araya geldiklerinde anlam kazanrlar. Yaplar, temel trleri veya baka yaplar bir araya getirerek yeni trler oluturmaya yarayan olanaklardr. Yeni tr, struct anahtar szc ile oluturulur. struct , "yap" anlamna gelen "structure"n ksaltmasdr. Bu derste yaplarla ilgili olarak anlatacam ou bilgi, daha sonra greceimiz snflar anlamada da yardmc olacak. zellikle bir araya getirerek yeni tr tanmlama kavram, yaplarda ve snflarda ayndr. Yap kavramn anlamak iin daha nce assert dersinde grdmz zamanEkle ilevine bakalm: void zamanEkle( in int balangSaati, in int balangDakikas, in int eklenecekSaat, in int eklenecekDakika, out int sonuSaati, out int sonuDakikas) { sonuDakikas = balangDakikas + eklenecekDakika; sonuSaati = balangSaati + eklenecekSaat; sonuSaati += sonuDakikas / 60; sonuDakikas %= 60; sonuSaati %= 24;

Not: levin in , out , ve unittest bloklarn fazla yer tutmamak iin bu derste gstermiyorum. Her ne kadar o ilev alt tane parametre alyor gibi grnse de, birbirleriyle ilgili olan parametreleri ifter ifter dnrsek, aslnda ift bilgi aldn grrz. O iftlerden ilk ikisi giri olarak, sonuncusu da k olarak kullanlmaktadr.

48.1

Tanmlanmas
te struct , birbirleriyle ilikili deikenleri bir araya getirerek yeni bir tr olarak kullanma olana verir: struct GnnSaati { int saat; int dakika; } Yukardaki tanm, saat ve dakika isimli iki int deikeni bir araya getiren ve ismi GnnSaati olan yeni bir tr tanmlar. Yukardaki tanmdan sonra artk baka trler gibi kullanabileceimiz GnnSaati isminde yeni bir trmz olur. rnek olarak int trne benzer kullanmn yle gsterebiliriz: int say; say = bakaSay; GnnSaati zaman; zaman = bakaZaman; // bir deiken // bakaSay'nn deerini almas // bir nesne // bakaZaman'n deerini almas

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

237

Yaplar

Yap trleri yle tanmlanr: struct Trsmi { // ... tr oluturan yeler ve varsa zel ilevleri ... } Yaplar iin zel ilevler de tanmlanabilir. Bunlar daha sonraki bir derste anlatacam. Bu derste yalnzca yap yelerini gsteriyorum. Yapnn bir araya getirdii paralara ye ad verilir. Bu tanma gre, yukardaki GnnSaati yapsnn iki yesi vardr: saat ve dakika .
48.1.1

Yap tanm, tr tanmdr; nesne tanm deildir


Burada bir uyarda bulunmam gerekiyor: sim Alan dersinde ve Yaam Sreleri dersinde anlatlanlar dorultusunda; yap tanmnda kullanlan kme parantezlerine bakarak, o kapsam iindeki yelerin yapnn tanmland an yaamaya baladklarn dnebilirsiniz. Bu doru deildir. Yap tanm, deiken tanmlamaz: struct GnnSaati { int saat; int dakika; }

// deiken tanm deil! // deiken tanm deil!

Yap tanm, daha sonradan yap nesneleri oluturulduunda ne tr ye deikenlerinin olacan belirler: struct GnnSaati { int saat; int dakika; }

// sonra, nesnenin iinde oluturulacak // sonra, nesnenin iinde oluturulacak

O ye deikenler; bu yap trnden bir nesne oluturulduu zaman, o nesnenin paras olarak oluturulurlar: GnnSaati yatmaZaman; GnnSaati kalkmaZaman; // iinde kendi saat ve dakika // deikenlerini barndrr // // // // // bu da kendi saat ve dakika deikenlerini barndrr; bunun saat ve dakika deikenleri ncekinden bamszdr

48.1.2

Kodlama kolayl
Saat ve dakika gibi iki bilgiyi byle bir araya getirerek tek bir tr gibi kullanabilmek byk kolaylk salar. rnein yukardaki ilev alt tane int yerine, asl amacna ok daha uygun olacak ekilde tane GnnSaati trnde parametre alabilir: void zamanEkle(in GnnSaati balang, in GnnSaati eklenecek, out GnnSaati sonu) { // ... }

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

238

Yaplar

Not: Gnn saatini belirten byle iki deerin eklenmesi aslnda normal bir ilem olarak kabul edilmemelidir. rnein kahvalt zaman olan 7:30'a le yemei zaman olan 12:00'yi eklemek doal deildir. Burada aslnda Sre diye yeni bir tr tanmlamak ve GnnSaati nesnelerine Sre nesnelerini eklemek ok daha doru olurdu. Ben bu derste yine de yalnzca GnnSaati trn kullanacam. Hatrlayacanz gibi, ilevler return deyimiyle tek bir deer dndrebilirler. zamanEkle rettii saat ve dakika deerlerini zaten bu yzden iki tane out parametre olarak tanmlamak zorunda kalyordu. rettii iki tane sonucu tek bir deer olarak dndremiyordu. Yaplar bu kstlamay da ortadan kaldrrlar: Birden fazla bilgiyi bir araya getirerek tek bir tr oluturduklar iin, ilevlerin dn tr olarak kullanlabilirler. Artk ilevden tek bir GnnSaati nesnesi dndrebiliriz: GnnSaati zamanEkle(in GnnSaati balang, in GnnSaati eklenecek) { // ... } Bylece zamanEkle artk yan etki oluturan deil, deer reten bir ilev haline de gelmi olur. levler dersinden hatrlayacanz gibi; ilevlerin yan etki oluturmak yerine deer retmeleri tercih edilir. Yaplar da yap yesi olabilirler. rnein GnnSaati yapsndan iki yesi bulunan baka bir yap yle tasarlanabilir: struct Toplant { string konu; int katlmcSays; GnnSaati balang; GnnSaati biti; } Toplant yaps da baka bir yapnn yesi olabilir. Yemek diye bir yap olduunu da varsayarsak: struct GnlkPlan { Toplant projeToplants; Yemek leYemei; Toplant bteToplants; }

48.2

ye eriimi
Yap yelerini de herhangi bir deiken gibi kullanabiliriz. Tek fark, yelere erimek iin nesnenin isminden sonra nce eriim ileci olan nokta, ondan sonra da yenin isminin yazlmasdr: balang.saat = 10; O satr, balang nesnesinin saat yesine 10 deerini atar. Yaplarla ilgili bu kadarlk bilgiyi kullanarak zamanEkle ilevini artk u ekilde deitirebiliriz:

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

239

Yaplar

GnnSaati zamanEkle(in GnnSaati balang, in GnnSaati eklenecek) { GnnSaati sonu; sonu.dakika = balang.dakika + eklenecek.dakika; sonu.saat = balang.saat + eklenecek.saat; sonu.saat += sonu.dakika / 60; sonu.dakika %= 60; sonu.saat %= 24; } return sonu;

Bu ilevde kullanlan deiken isimlerinin artk ok daha ksa seilebildiine dikkat edin: nesnelere balang , eklenecek , ve sonu gibi ksa isimler verebiliyoruz. balangSaati gibi bileik isimler kullanmak yerine de nesnelerin yelerine nokta ile balang.saat eklinde eriiyoruz. O ilevi kullanan bir kod aadaki ekilde yazlabilir. Bu program, 1 saat 15 dakika sren ve 8:30'da balayan dersin biti zamann 9:45 olarak hesaplar: void main() { GnnSaati dersBa; dersBa.saat = 8; dersBa.dakika = 30; GnnSaati dersSresi; dersSresi.saat = 1; dersSresi.dakika = 15; const GnnSaati dersSonu = zamanEkle(dersBa, dersSresi); writefln("Ders sonu: %s:%s", dersSonu.saat, dersSonu.dakika);

Ders sonu: 9:45 Yukardaki main 'i imdiye kadar bildiklerimizi kullanarak yazdm. Biraz aada bu ilemlerin ok daha kolay ve ksa olanlarn gstereceim.

48.3

Kurma
Yukardaki main 'in ilk satr, dersBa nesnesinin kurulmas ile ilgilidir; ondan sonraki satr da dersSresi nesnesinin kurulmasdr. O satrlarda nce nesne tanmlanmakta, sonra saat ve dakika yelerinin deerleri atanmaktadr. Herhangi bir deikenin veya nesnenin tutarl bir ekilde kullanlabilmesi iin mutlaka kurulmas gerekir. Bu ok nemli ve yaygn bir ilem olduu iin, yap nesneleri iin ksa bir kurma sz dizimi vardr: GnnSaati dersBa = GnnSaati(8, 30); GnnSaati dersSresi = GnnSaati(1, 15); Bu yazm, tr otomatik olarak sa taraftan karsayan auto anahtar szc ile daha da ksa hale getirilebilir:

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

240

Yaplar

auto dersBa = GnnSaati(8, 30); auto dersSresi = GnnSaati(1, 15); Nesneler bu ekilde kurulurken belirtilen deerler, yapnn yelerine yap iinde tanmlandklar sra ile atanrlar: yap iinde saat nce tanmland iin 8 deeri dersBa.saat 'e, 30 deeri de dersBa.dakika 'ya atanr.
48.3.1

const olarak kurabilme olana


Nesneleri ayn anda hem tanmlamak hem de deerlerini verebilmek, onlar const olarak iaretleme olana da salar: const auto dersBa = GnnSaati(8, 30); const auto dersSresi = GnnSaati(1, 15); Kurulduktan sonra artk hi deimeyecek olduklar durumlarda, bu nesnelerin sonraki satrlarda yanllkla deitirilmeleri bylece nlenmi olur. Yukardaki programda ise nesneleri const olarak iaretleyemezdik, nk ondan sonra yelerinin deerlerini atamamz mmkn olmazd: const GnnSaati dersBa; dersBa.saat = 8; // derleme HATASI dersBa.dakika = 30; // derleme HATASI const olarak iaretlenmi olan sabit dersBa nesnesinin yelerini deitirmek yasal deildir.

48.3.2

Sondaki yelerin deerleri bo braklabilir


Yap nesneleri kurulurken sondaki yelerin deerleri belirtilmeyebilir. Bu durumda sondaki yeler yine de otomatik olarak kendi trlerinin .init deeri ile ilklenirler. Bunu gsteren aadaki programda Deneme tr gittike azalan sayda parametre ile kuruluyor ve geri kalan parametrelerin de otomatik olarak ilklendikleri assert ifadeleri ile gsteriliyor (programda kullanmak zorunda kaldm isnan ilevini programdan sonra aklyorum): import std.math; struct Deneme { char karakter; int tamsay; double kesirli; } void main() { // Btn deerlerle auto d1 = Deneme('a', 1, 2.3); assert(d1.karakter == 'a'); assert(d1.tamsay == 1); assert(d1.kesirli == 2.3); // Sonuncusu eksik auto d2 = Deneme('a', 1); assert(d2.karakter == 'a'); assert(d2.tamsay == 1); assert(isnan(d2.kesirli)); // Son ikisi eksik

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

241

Yaplar

auto d3 = Deneme('a'); assert(d3.karakter == 'a'); assert(d3.tamsay == int.init); assert(isnan(d3.kesirli)); // Hibirisi yok auto d4 = Deneme(); assert(d4.karakter == char.init); assert(d4.tamsay == int.init); assert(isnan(d4.kesirli)); // Yukardakiyle ayn ey Deneme d5; assert(d5.karakter == char.init); assert(d5.tamsay == int.init); assert(isnan(d5.kesirli));

Kesirli saylar dersinden hatrlayacanz gibi, double 'n ilk deerini bildiren double.init niteliinin deeri double.nan 'dr. nan da kesirli saylarda "geersiz deer" anlamna geldii iin eitlik karlatrmalarnda bile kullanlamaz. O yzden programda d2.kesirli == double.init yerine isnan(d2.kesirli) yazmak zorunda kaldm. std.math modlnde tanmlanan isnan ilevi, kendisine verilen deerin nan 'a eit olup olmadn bildirir.
48.3.3

Varsaylan ye deerlerinin belirlenmesi


yelerin otomatik olarak ilkleniyor olmalar ok yararl bir olanaktr. yelerin rasgele deerlerle kullanlmalar nlenmi olur. Ancak, her yenin kendi trnn .init deerini almas her duruma uygun deildir. rnein char.init deeri geerli bir karakter bile deildir. Bu yzden yelerin otomatik olarak alacaklar deerler programc tarafndan belirlenebilir. Bu sayede rnein yukarda grdmz ve hibir kullanll olmayan double.nan deeri yerine, ou zaman ok daha uygun olan 0.0 deerini kullanabiliriz. yelerin aldklar bu zel ilk deerlere varsaylan deer denir ve ye tanmndan sonraki atama sz dizimiyle belirlenir: struct Deneme { char karakter = 'A'; int tamsay = 11; double kesirli = 0.25; } Tr tanm srasnda kullanlan bu yazm eklinin bir atama ilemi olmadna dikkat edin. Yukardaki kodun tek amac, yeler iin hangi deerlerin varsaylacan belirlemektir. Bu deerler, daha sonra nesne oluturulurken gerekirse kullanlacaktr. Nesne kurulurken deerleri zellikle belirtilmeyen yeler o varsaylan deerleri alrlar. rnein aadaki kullanmda nesnenin hibir yesinin deeri verilmemektedir: Deneme d; // hibir ye deeri belirtilmiyor writefln("%s,%s,%s", d.karakter, d.tamsay, d.kesirli); Buna ramen btn yeler trn tanmnda belirtilmi olan ilk deerlere sahip olurlar: A,11,0.25

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

242

Yaplar

48.3.4

{ } karakterleriyle kurma
Yukardaki kurma sz dizimi varken kullanmaya gerek olmasa da, bunu da bilmeniz gerekir. Yap nesnelerini baka bir sz dizimiyle de kurabilirsiniz: GnnSaati dersBa = { 8, 30 }; Belirlenen deerler bu kullanmda da yelere sra ile atanrlar; ve bu kullanmda da ye saysndan daha az deer verilebilir. Not: dmd'nin bir hatas nedeniyle, bu dersi yazdm sralarda deerleri belirtilmeyen yelerin ilk deerleri atanmyor. lk deerleri atanmad iin de yelerin deerleri belirsizdir ve dikkat edilmezse programda hataya neden olabilir. D'ye C++ yoluyla C'den geen bu sz dizimi yerine, nesneleri yukarda gsterdiim ekilde kurmanz neririm: auto dersBa = GnnSaati(8, 30); GnnSaati dersSonu = { 9, 30 }; // nerilen // C'deki gibi

48.4

Kopyalama ve Atama
Yaplar deer trleridir. Bundan; Deerler ve Referanslar dersinde akland gibi, her yap nesnesinin kendisine ait deeri olduunu anlarz. Kurulduklarnda kendi deerlerini edinirler; atandklarnda da yalnzca kendi deerleri deiir. (Not: Bu konuda referans trnden olan yelere zellikle dikkat etmek gerekir. Bunu biraz aada aklyorum.) auto seninYemekSaatin = GnnSaati(12, 0); auto benimYemekSaatim = seninYemekSaatin; // Yalnzca benim yemek saatim 12:05 olur: benimYemekSaatim.dakika += 5; // ... senin yemek saatin deimez: assert(seninYemekSaatin.dakika == 0); Kopyalama srasnda bir nesnenin btn yeleri srayla dier yeye kopyalanr. Benzer ekilde, atama ilemi de btn yelerin srayla atanmalar anlamna gelir.

48.4.1

Referans trnden olan yelere dikkat!


Burada ok nemli bir konuyu hatrlatmak gerekiyor: referans trnden olan deikenler kopyalandklarnda veya atandklarnda asl nesne deimez, ona eriim salayan referans deiir, ve sonuta asl nesneye birden fazla referans tarafndan eriim salanm olur. Bunun yap yeleri asndan nemi, iki farkl yap nesnesinin yelerinin ayn asl nesneye eriim salyor olacaklardr. Bunu grmek iin referans trnden bir yesi olan bir yapya bakalm. Bir rencinin numarasn ve notlarn ieren yle bir yap tanmlanm olsun: struct renci { int numara; int[] notlar; } O trden bir nesnenin baka bir nesnenin deeriyle kurulduu u koda bakalm:

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

243

Yaplar

// Birinci renciyi kuralm: auto renci1 = renci(1, [ 70, 90, 85 ]); // kinci renciyi birincinin kopyas olarak kuralm ... auto renci2 = renci1; // ... ve sonra numarasn deitirelim: renci2.numara = 2; // lk rencinin notunda bir deiiklik yaptmzda ... renci1.notlar[0] += 5; // ... ikinci rencinin notunun da deitiini grrz: writeln(renci2.notlar[0]); renci2 nesnesi kurulduu zaman, yeleri srayla renci1 'in yelerinin deerlerini alr. int bir deer tr olduu iin, her iki renci nesnesinin ayr numara deeri olur. Her iki renci nesnesinin birbirlerinden bamsz olan notlar yeleri de vardr. Ancak, dizi dilimleri referans trleri olduklarndan, her ne kadar notlar yeleri bamsz olsalar da, aslnda ayn asl dizinin elemanlarna eriim salarlar. Sonuta, bir nesnenin notlar yesinde yaplan deiiklik dierini de etkiler. Yukardaki kodun kts, iki renci nesnesinin ayn asl notlar paylatklarn gsterir: 75 Belki de burada hi kopyalama ilemini kullanmadan, ikinci nesneyi kendi numarasyla ve birincinin notlarnn kopyasyla kurmak daha doru olur: // kinci renciyi birincinin notlarnn kopyas ile // kuruyoruz auto renci2 = renci(2, renci1.notlar.dup); // lk rencinin notunda bir deiiklik yaptmzda ... renci1.notlar[0] += 5; // ... ikinci rencinin notu bu sefer deimez: writeln(renci2.notlar[0]); Dizilerin .dup nitelii ile kopyaland iin bu sefer her nesnenin ayr notlar olur. imdiki kt, ikinci rencinin notunun etkilenmediini gsterir: 70 Not: stenen durumlarda referans trnden yelerin otomatik olarak kopyalanmalar da salanabilir. Bunu daha sonra yap ilevlerini anlatrken gstereceim.

48.5

Yap hazr deerleri


Nasl 10 gibi hazr deerleri hi deiken tanmlamak zorunda kalmadan ilemlerde kullanabiliyorsak, yap nesnelerini de isimleri olmayan hazr deerler olarak kullanabiliriz. Yap hazr deerlerini oluturmak iin yine kurma sz dizimi kullanlr ve yap nesnesi gereken her yerde kullanlabilir. GnnSaati(8, 30) // hazr deer Yukardaki main ilevini imdiye kadar rendiklerimizi kullanarak yle yazabiliriz:

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

244

Yaplar

void main() { const auto dersBa = GnnSaati(8, 30); const auto dersSresi = GnnSaati(1, 15); const GnnSaati dersSonu = zamanEkle(dersBa, dersSresi); writefln("Ders sonu: %s:%s", dersSonu.saat, dersSonu.dakika);

Dikkat ederseniz, o programda dersBa ve dersSresi nesnelerinin aka belirtilmelerine gerek yoktur. Onlar yalnzca dersSonu nesnesini hesaplamak iin kullanlan aslnda geici nesnelerdir. O nesneleri aka tanmlamak yerine, zamanEkle ilevine u ekilde hazr deer olarak da gnderebiliriz: void main() { const GnnSaati dersSonu = zamanEkle(GnnSaati(8, 30), GnnSaati(1, 15)); writefln("Ders sonu: %s:%s", dersSonu.saat, dersSonu.dakika);

48.6

static yeler
ou durumda her yap nesnesinin kendi yelerine sahip olmasn isteriz. Baz durumlarda ise belirli bir yap trnden olan btn nesnelerin tek bir deikeni paylamalar uygun olabilir. Bu, o yap tr iin aklda tutulmas gereken genel bir bilgi bulunduunda gerekebilir. Btn nesnelerin tek bir yeyi paylamalarnn bir rnei olarak, her bir nesne iin farkl bir tantc numara olduu bir durum dnelim: struct Nokta { // Her nesnenin kendi tantc numaras int numara; int satr; int stun;

Her nesneye farkl bir numara verebilmek iin sonrakiNumara gibi bir deiken barndrmak, ve her nesne iin o sayy bir arttrmak gerekir: Nokta NoktaOlutur(int satr, int stun) { int numara = sonrakiNumara; ++sonrakiNumara; } return Nokta(numara, satr, stun);

Burada karar verilmesi gereken ey, her nesnenin oluturulmas srasnda ortak olarak kullanlan sonrakiNumara bilgisinin nerede tanmlanacadr. static yeler ite bu gibi durumlarda kullanldrlar.

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

245

Yaplar

O bilgi bir yap yesi olarak tanmlanr ve static olarak iaretlenir. Dier yelerin aksine, byle yelerden yalnzca bir adet oluturulur: import std.stdio; struct Nokta { // Her nesnenin kendi tantc numaras int numara; int satr; int stun; // Bundan sonra oluturulacak olan nesnenin numaras static int sonrakiNumara;

Nokta NoktaOlutur(int satr, int stun) { int numara = Nokta.sonrakiNumara; ++Nokta.sonrakiNumara; } return Nokta(numara, satr, stun);

void main() { auto stteki = NoktaOlutur(7, 0); auto ortadaki = NoktaOlutur(8, 0); auto alttaki = NoktaOlutur(9, 0); writeln(stteki.numara); writeln(ortadaki.numara); writeln(alttaki.numara);

sonrakiNumara her seferinde bir arttrld iin her nesnenin farkl numaras olur: 0 1 2 static yeler btn tre ait olduklarndan, onlara erimek iin bir nesne bulunmas gerekmez. Yukarda olduu gibi, o yeye trn ismi kullanlarak da eriilebilir: ++alttaki.sonrakiNumara; ++Nokta.sonrakiNumara;

// st satrn edeeri

48.7

Problemler
1. Tek bir oyun kadn temsil eden ve ismi OyunKad olan bir yap tasarlayn. Bu yapnn kat rengi ve kat deeri iin iki yesi olduu dnlebilir. Renk iin bir enum deer kullanabileceiniz gibi; dorudan , , , ve karakterlerini de kullanabilirsiniz. Kat deeri olarak da bir int veya bir dchar ye kullanabilirsiniz. int seerseniz 1'den 13'e kadar deerlerden belki de 1, 11, 12, ve 13 deerlerini srasyla as, vale, kz ve papaz iin dnebilirsiniz. Daha baka zmler de bulunabilir. rnein kat deerini de bir enum olarak tanmlayabilirsiniz.

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

246

Yaplar

Bu yapnn nesnelerinin nasl kurulacaklar, yeler iin setiiniz trlere bal olacak. rnein eer her iki yeyi de dchar trnde tasarladysanz, yle kurulabilirler: auto kat = OyunKad('', '2'); 2. Bir OyunKad nesnesi alan ve o nesneyi ka yazdran oyunKadYazdr isminde bir ilev tanmlayn: void oyunKadYazdr(in OyunKad kat) { // ... burasn siz yazn ... } void main() { auto kat = OyunKad(/* ... */); oyunKadYazdr(kat); } rnein sinek ikiliyi ktya u ekilde yazdrsn: 2 Kupa asn da u ekilde: A O ilevin ierii, doal olarak yapy nasl tasarladnza bal olacaktr. 3. smi yeniDeste olan bir ilev yazn. Elli iki farkl oyun kadn temsil eden OyunKad [] trnde bir dizi dndrsn: OyunKad[] yeniDeste() out (sonu) { assert(sonu.length == 52); } body { // ... burasn siz yazn ... } Bu ilev rnein yle kullanlabilsin: void main() { OyunKad[] deste = yeniDeste(); foreach (kat; deste) { oyunKadYazdr(kat); write(' '); } } writeln();

Eer destedeki her kat gerekten farkl olmusa, una benzer bir kt olmaldr:

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

247

Yaplar

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. Desteyi kartran bir ilev yazn. std.random modlnde tanml olan uniform ilevini kullanarak rasgele setii iki kadn yerini deitirsin. Bu ilemi ka kere tekrarlayacan da parametre olarak alsn: void kartr(OyunKad[] deste, in int deiTokuAdedi) { // ... burasn siz yazn } u ekilde arlabilsin: void main() { OyunKad[] deste = yeniDeste(); kartr(deste, 1); foreach (kat; deste) { oyunKadYazdr(kat); write(' '); } } writeln();

deiTokuAdedi ile belirtilen deer kadar dei toku ilemi gerekletirsin. rnein 1 ile arldnda 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

deiTokuAdedi olarak daha yksek bir deer verdiinizde deste iyice karm olmaldr: kartr(deste, 100);

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 kartrmak iin daha etkin bir yntemi zm programnda aklyorum. ... zmler

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

248

Parametre Serbestlii

49

Parametre Serbestlii
Bu blmde lev Parametreleri dersinde anlatlanlarla dorudan ilgili olan ve ilev arma konusunda baz serbestlikler salayan iki olana gstereceim: Varsaylan parametre deerleri Belirsiz sayda parametreler

49.1

Varsaylan parametre deerleri


levlerle ilgili bir kolaylk, parametrelere varsaylan deerler atanabilmesidir. Bu, yap yelerinin varsaylan deerlerinin belirlenebilmesine benzer. Baz ilevlerin baz parametreleri ou durumda hep ayn deerle arlyor olabilirler. rnek olarak, bir eleme tablosunu ka yazdran bir ilev dnelim. Yazdrd eleme tablosunun hem indeks tr hem de deer tr string olsun. Bu ilev, ktda kullanaca ayra karakterlerini de parametre olarak alacak ekilde esnek tasarlanm olsun: void tabloYazdr(in char[] balk, in string[string] tablo, in char[] indeksAyrac, in char[] elemanAyrac) { writeln("-- ", balk, " --"); const char[][] indeksler = tablo.keys; indeksler.sort; foreach (saya, indeks; indeksler) { // lk elemandan nce ayra olmamal if (saya != 0) { write(elemanAyrac); } } } write(indeks, indeksAyrac, tablo[indeks]);

writeln();

O ilev, indekslerle deerler arasna ":" , elemanlar arasna da ", " gelecek ekilde yle arlabilir: string[string] szlk = [ "mavi":"blue", "krmz":"red", "gri":"gray" ]; tabloYazdr("Trke'den ngilizce'ye Renkler", szlk, ":", ", ");

-- Trke'den ngilizce'ye Renkler -gri:gray, krmz:red, mavi:blue Ayn programda baka tablolarn da yazdrldklarn, ve ou durumda hep ayn ayralarn kullanldklarn varsayalm. Yalnzca baz zel durumlarda farkl ayralar kullanlyor olsun. Parametre deerlerinin ounlukla ayn deeri aldklar durumlarda, o deerler varsaylan deer olarak belirtilebilirler:

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

249

Parametre Serbestlii

void tabloYazdr(in in in in { // ... }

char[] balk, string[string] tablo, char[] indeksAyrac= ":", char[] elemanAyrac= ", ")

Varsaylan deerleri olan parametreler, ilev ars srasnda belirtilmeyebilirler: tabloYazdr("Trke'den ngilizce'ye Renkler", szlk); /* ayralar belirtilmemi; * varsaylan deerlerini alrlar */ O durumda, belirtilmeyen parametrelerin varsaylan deerleri kullanlr. Normalin dnda deer kullanlaca durumlarda ilev arlrken o parametreler iin yine de zel deerler verilebilir. Gerekiyorsa yalnzca ilki: tabloYazdr("Trke'den ngilizce'ye Renkler", szlk, "=");

-- Trke'den ngilizce'ye Renkler -gri=gray, krmz=red, mavi=blue Veya gerekiyorsa her ikisi birden: tabloYazdr("Trke'den ngilizce'ye Renkler", szlk, "=", "\n");

-- Trke'den ngilizce'ye Renkler -gri=gray krmz=red mavi=blue Varsaylan deerler yalnzca parametre listesinin son tarafndaki parametreler iin belirtilebilir. Bataki veya aradaki parametrelerin varsaylan deerleri belirtilemez.

49.2

Belirsiz sayda parametreler


Varsaylan parametre deerleri, ilevin aslnda ka tane parametre aldn deitirmez. rnein yukardaki tabloYazdr ilevi her zaman iin drt adet parametre alr; ve iini yaparken o drt parametreyi kullanr. D'nin baka bir olana, ilevleri belirsiz sayda parametre ile arabilmemizi salar. Bu olana aslnda daha nce de ok kere kullandk. rnein writefln 'i hibir kstlamayla karlamadan snrsz sayda parametre ile arabiliyorduk: writeln( "merhaba", 7, "dnya", 9.8 /*, ve istediimiz kadar * daha parametre */); D'de belirsiz sayda parametre kullanmann yolu vardr:

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

250

Parametre Serbestlii

C dilindeki gibi ileyen ve _argptr anahtar szcn kullanan dzenek: gvensiz olan bu olana burada anlatmayacam _arguments ve _typeinfo anahtar szcklerinden yararlanan ve writefln 'in de kulland olanak; hem henz renmediimiz gstergeleri kulland iin, hem de ayn nedenden tr gvensiz olabilen bu olana da burada anlatmayacam Belirsiz parametrelerin hep ayn trden olmalarn gerektiren, ama bunun yannda gvenli olan D olana; aada bunu anlatyorum D, belirsiz saydaki parametreleri o tr ilevlere bir dizi halinde sunar. Belirsiz sayda parametre alacak olan ilevin parametresi olarak bir dizi belirtilir ve hemen arkasndan ... karakterleri yazlr: double topla(in double[] saylar ...) { double sonu = 0.0; foreach (say; saylar) { sonu += say; } } return sonu;

O ekilde tanmlanan topla , belirsiz sayda parametre alan bir ilev haline gelmi olur. Onu istediimiz sayda double ile arabiliriz: writeln(topla(1.1, 2.2, 3.3)); Parametre listesindeki dizi ve ondan sonra gelen ... karakterleri, ilevin arld srada kullanlan parametreleri temsil ederler. topla ilevi rnein 5 double parametre ile arldnda, topla 'nn saylar parametresi o be sayy ierir. Byle ilevlerin art kotuklar parametreleri de bulunabilir. rnein belirsiz saydaki szc parantezler arasnda yazdran bir ilev dnelim. Bu ilev, her ne kadar szck saysn serbest braksa da, ne tr parantezler kullanlacann belirtilmesini art kosun. Kesinlikle belirtilmesi gereken parametreler parametre listesinde ba tarafa yazlrlar. Belirsiz saydaki parametreyi temsil eden dizi ise en sona yazlr: char[] parantezle( in char[] ama, // ilev arlrken belirtilmelidir in char[] kapama, // ilev arlrken belirtilmelidir in char[][] szckler ...) // hi belirtilmeyebilir { char[] sonu; foreach (szck; szckler) { sonu ~= ama; sonu ~= szck; sonu ~= kapama; } } return sonu;

O ilevi arrken ilk iki parametre mutlaka belirtilmelidir: parantezle("{"); // derleme HATASI

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

251

Parametre Serbestlii

Kesinlikle belirtilmeleri gereken bataki parametreler verildii srece, geri kalan parametreler konusunda serbestlik vardr. Buna uygun olarak ama ve kapama parantezlerini kullanan bir rnek: writeln(parantezle("{", "}", "elma", "armut", "muz"));

{elma}{armut}{muz}

49.3

Problem
Daha nce grdmz u enum trnn tanml olduunu varsayn: enum lem { Toplama, karma, arpma, Blme } O ilem eidini ve ilemde kullanlacak iki kesirli sayy ieren bir de yap olsun: struct Hesap { lem ilem; double birinci; double ikinci; } rnein Hesap(lem.Blme, 7.7, 8.8) nesnesi, 7.7'nin 8.8'e blnecei anlamna gelsin. Bu yap nesnelerinden belirsiz sayda parametre alan, her birisini teker teker hesaplayan, ve btn sonular bir double dizisi olarak dndren hesapla isminde bir ilev yazn. Bu ilev rnein yle arlabilsin: void main() { writeln(hesapla(Hesap(lem.Toplama, 1.1, 2.2), Hesap(lem.karma, 3.3, 4.4), Hesap(lem.arpma, 5.5, 6.6), Hesap(lem.Blme, 7.7, 8.8))); } Yukardaki gibi kullanldnda, hesapla 'nn ilem sonularn yerletirdii dizi writefln tarafndan ktya yle yazdrlacaktr: [3.3,-1.1,36.3,0.875] ... zm

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

252

lev Ykleme

50

lev Ykleme
Ayn isimde birden fazla ilev tanmlamaya ilev ykleme denir. simleri ayn olan bu ilevlerin ayrt edilebilmeleri iin parametrelerinin birbirlerinden farkl olmalar gerekir. Bu kullanmda "ykleme" szcnn "ayn isme yeni grev ykleme" anlamna geldiini dnebilirsiniz. Aada ayn isimde ama parametreleri farkl ilevler gryorsunuz: import std.stdio; void bilgiVer(in double say) { writeln("Kesirli say: ", say); } void bilgiVer(in int say) { writeln("Tamsay : ", say); } void bilgiVer(in char[] dizgi) { writeln("Dizgi : ", dizgi); } void main() { bilgiVer(1.2); bilgiVer(3); bilgiVer("merhaba"); } levlerin hepsinin de ismi bilgiVer olduu halde, derleyici parametrenin trne uygun olan ilevi seer ve onun arlmasn salar. rnein 1.2 hazr deerinin tr double olduu iin, onun kullanld durumda o ilevler arasndan double parametre alan arlr. Hangi ilevin arlaca derleme zamannda seilir. Bu seim her zaman kolay veya ak olmayabilir. rnein u kodda kullanlan int deer hem real hem de double trne uyduu iin derleyici hangisini seeceine karar veremez: real yediKat(in real deer) { return 7 * deer; } double yediKat(in double deer) { return 7 * deer; } void main() { int say = 5; auto sonu = yediKat(say); }

// derleme HATASI

Not: Normalde ayn ii yapan byle iki ilevin yazlmas gereksizdir. rnein bu ilevlerden real parametreli olan yeterlidir; nk double deerler de zaten kaypsz olarak real 'e dnrler. te yandan, bu ilevlerin long parametre alan bir ncs tanmlansa,

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

253

lev Ykleme

long yediKat(in long deer) { return 7 * deer; } Derleme hatas ortadan kalkar; nk yklenen ilev seimi konusunda int deerler long trne kesirli trlerden daha uyumludur. Program bu yeni ilev arlacak ekilde derlenir.

50.1

Parametre uyum kurallar


Ayn isimde birden fazla ilev bulunmas, derleyicinin bir seim yapmasn gerektirir. Yklenen ilevler arasndan, kullanlan parametrelere daha ok uyan ilev seilir. Bu seim ou durumda kolay ve beklendii gibi olur; ama hangi ilevin daha ok uyduu konusu bazen ok karktr. Bu yzden uyum kurallar gelitirilmitir. Parametreler iin uyum konusunda drt durum vardr: uyumsuzluk otomatik tr dnm yoluyla uyum const 'a dntrerek uyum tam uyum

Derleyici, yklenmi olan ilevlerden hangisini aracana karar vermek iin ilevleri gzden geirir. Her ilevin parametrelerine teker teker bakar ve her parametrenin yukardaki drt uyum durumundan hangisinde olduunu belirler. Btn parametreler iindeki en az uyum, bu ilevin de uyumu olarak kabul edilir. Bu ekilde btn ilevlerin uyum durumlar belirlendikten sonra; eer varsa, en ok uyan ilev seilir. Eer birden fazla ilev ayn derecede uymusa, onlardan hangisinin seileceine daha da kark baka kurallar yoluyla karar verilir. Ben burada bu kurallarn daha derinine inmeyeceim; nk eer bu kadar kark kurallarla yz yze kalmsanz, aslnda programnzn tasarmnda deiiklik yapma zaman gelmi demektir. Belki de ilev ablonlarn kullanmak daha doru olacaktr. Hatta belki de ayn isimde ilev tanmlamak yerine, daha aklayc isimler kullanarak hangisini armak istediinizi aka belirtmek btn karkl ortadan kaldracaktr: yediKat_real ve yediKat_double gibi...

50.2

Yaplar iin ilev ykleme


lev ykleme, yaplarda ve snflarda ok yararldr; stelik o trlerde ilev seimi konusunda uyum sorunlar da ok daha azdr. Yukardaki bilgiVer ilevini Yaplar dersinde kullandmz baz trler iin ykleyelim: struct GnnSaati { int saat; int dakika; } void bilgiVer(in GnnSaati zaman) { writef("%02s:%02s", zaman.saat, zaman.dakika); }

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

254

lev Ykleme

O tanm sayesinde artk GnnSaati nesnelerini de bilgiVer ilevine gnderebiliriz. Bylece programmzda her tr nesneyi ayn isimle yazdrabileceimiz allm bir yntemimiz olur: auto kahvaltZaman = GnnSaati(7, 0); bilgiVer(kahvaltZaman); Temel trlerde olduu gibi, artk GnnSaati nesneleri de kendilerine zg kt dzenleri ile yazdrlm olurlar: 07:00 bilgiVer ilevini yaplar dersinde deinilen Toplant yaps iin de ykleyelim: struct Toplant { string konu; int katlmcSays; GnnSaati balang; GnnSaati biti; } void bilgiVer(in Toplant toplant) { bilgiVer(toplant.balang); write('-'); bilgiVer(toplant.biti); writef(" \"%s\" toplants (%s katlmc)", toplant.konu, toplant.katlmcSays);

Grdnz gibi; bilgiVer 'in GnnSaati iin yklenmi olan, Toplant 'y yazdran ilev tarafndan kullanlmaktadr. Artk Toplant nesnelerini de altmz isimdeki ilevle yazdrabiliriz: auto geziToplants = Toplant("Bisikletle gezilecek yerler", 3, GnnSaati(9, 0), GnnSaati(9, 10)); bilgiVer(geziToplants);

09:00-09:10 "Bisikletle gezilecek yerler" toplants (3 katlmc)

50.3

Eksiklikler
Yukardaki bilgiVer ilevi her ne kadar kullanm kolayl getirse de, bu yntemin baz eksiklikleri vardr: bilgiVer ilevi yalnzca stdout 'a yazd iin fazla kullanl deildir. Oysa rnein File trnden bir dosyaya da yazabiliyor olsa, kullanll artard. Bunu salamann yolu, ktnn yazdrlaca akm da ileve bir parametre olarak vermektir: void bilgiVer(File akm, in GnnSaati zaman) { akm.writef("%02s:%02s", zaman.saat, zaman.dakika); } O sayede GnnSaati nesnelerini istersek stdout 'a, istersek de bir dosyaya yazdrabiliriz:

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

255

lev Ykleme

bilgiVer(stdout, kahvaltZaman); auto dosya = File("bir_dosya", "w"); bilgiVer(dosya, kahvaltZaman); Not: stdin , stdout , ve stderr nesnelerinin trleri de aslnda File 'dr. Daha nemlisi, bilgiVer gibi bir ilev, yap nesnelerini dier trler kadar rahata kullanabilmemiz iin yeterli deildir. Dier trlerden alk olduumuz rahatlk yoktur: writeln(kahvaltZaman); // kullansz: trn ismini yazar

O kod altrldnda GnnSaati trnn ismi yazdrlr: GnnSaati Bunun yerine, yap nesnesinin deerini rnein "12:34" biiminde bir string 'e dntrebilen bir ilev olmas ok daha yararl olur. Yap nesnelerinin de otomatik olarak string 'e dntrlebileceklerini bundan sonraki derste gstereceim.

50.4

Problem
bilgiVer ilevini u iki yap iin de ykleyin: struct Yemek { GnnSaati zaman; string adres; } struct GnlkPlan { Toplant sabahToplants; Yemek leYemei; Toplant akamToplants; } Yemek yaps yalnzca balang zamann barndrd iin; onun biti zamann balang zamanndan bir buuk saat sonras olarak belirleyin. Bu ilem iin yaplar dersinde tanmladmz zamanEkle ilevi yararl olabilir: GnnSaati zamanEkle(in GnnSaati balang, in GnnSaati eklenecek) { GnnSaati sonu; sonu.dakika = balang.dakika + eklenecek.dakika; sonu.saat = balang.saat + eklenecek.saat; sonu.saat += sonu.dakika / 60; sonu.dakika %= 60; sonu.saat %= 24; } return sonu;

Yemek biti zamanlar o ilev yardmyla hesaplannca GnlkPlan nesneleri ka una benzer ekilde yazdrlabilirler:

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

256

lev Ykleme

10:30-11:45 "Bisiklet gezisi" toplants (4 katlmc) 12:30-14:00 Yemek, Yer: Taksim 15:30-17:30 "Bte" toplants (8 katlmc) ... zm

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

257

ye levler

51

ye levler
Bu derste her ne kadar yaplar kullanyor olsak da; buradaki bilgilerin ou daha sonra greceimiz snflar iin de geerlidir. Bu derste yaplarn ve snflarn ye ilevlerini tanyacaz, ve bunlarn ierisinden zel olarak, nesneleri string trne dntrmede kullanlan toString ye ilevini greceiz. Bir yapnn tanmland ou durumda, o yapy kullanan bir grup ilev de onunla birlikte tanmlanr. Bunun rneklerini nceki derslerde zamanEkle ve bilgiVer ilevlerinde grdk. O ilevler bir anlamda GnnSaati yaps ile birlikte sunulan ve o yapnn arayzn oluturan ilevlerdir. Hatrlarsanz; zamanEkle ve bilgiVer ilevlerinin ilk parametresi, zerinde ilem yaptklar nesneyi belirliyordu. imdiye kadar tanmladmz btn dier ilevler gibi, onlar da bamsz olarak, tek balarna, ve evrensel isim alannda tanmlanmlard. Bir yapnn arayzn oluturan ilevler ok karlalan bir kavram olduu iin; o ilevler yapnn iinde, yapnn ye ilevleri olarak da tanmlanabilirler.

51.1

ye ilev
Bir yapnn veya snfn kme parantezlerinin iinde tanmlanan ilevlere ye ilev denir: struct BirYap { void ye_ilev(/* parametreleri */) { // ... ilevin tanm ... } } // ... yapnn yeleri ve dier ilevleri ...

ye ilevlere yapnn dier yelerinde olduu gibi, nesne isminden sonraki nokta karakteri ve ardndan yazlan ilev ismi ile eriilir: nesne.ye_ilev(parametreleri); ye ilevleri aslnda daha nce de kullandk; rnein standart giri ve k ilemlerinde stdin ve stdout nesnelerini aka yazabiliyorduk: stdin.readf(" %s", &numara); stdout.writeln(numara); O satrlardaki readf ve writeln ye ilevlerdir. lk rneimiz olarak, GnnSaati yapsn yazdran bilgiVer ilevini bir ye ilev olarak tanmlayalm. O ilevi daha nce serbest olarak yle tanmlamtk: void bilgiVer(in GnnSaati zaman) { writef("%02s:%02s", zaman.saat, zaman.dakika); } ye ilev olarak yapnn iinde tanmlanrken baz deiiklikler gerekir:

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

258

ye levler

struct GnnSaati { int saat; int dakika; void bilgiVer() { writef("%02s:%02s", saat, dakika); }

Bu ye ilevin, daha nce yap dnda serbest olarak tanmladmz bilgiVer ilevinden iki fark vardr: yazdrd nesneyi parametre olarak almaz o yzden; yelere zaman.saat ve zaman.dakika diye deil, saat ve dakika diye eriir Bunun nedeni, ye ilevlerin zaten her zaman iin bir nesne zerinde arlyor olmalardr: auto zaman = GnnSaati(10, 30); zaman.bilgiVer(); Orada, bilgiVer ilevi zaman nesnesini yazdracak ekilde arlmaktadr. ye ilevin tanm iinde noktasz olarak yazlan saat ve dakika , zaman nesnesinin yeleridir; ve srasyla zaman.saat ve zaman.dakika yelerini temsil ederler. O ye ilev ars, daha nceden serbest olarak yazlm olan bilgiVer 'in u ekilde arld durumla edeerdir: zaman.bilgiVer(); bilgiVer(zaman); // ye ilev // serbest ilev (nceki tanm)

ye ilev her arldnda, zerinde arld nesnenin yelerine eriir: auto sabah = GnnSaati(10, 0); auto akam = GnnSaati(22, 0); sabah.bilgiVer(); write('-'); akam.bilgiVer(); writeln(); bilgiVer , sabah zerinde arldnda sabah 'n deerini, akam zerinde arldnda da akam 'n deerini yazdrr: 10:00-22:00

51.1.1

Nesneyi string olarak ifade eden toString


Bir nceki derste bilgiVer ilevinin eksikliklerinden sz etmitim. Rahatsz edici bir dier eksikliini burada gstermek istiyorum: Her ne kadar zaman okunakl bir dzende ktya gnderiyor olsa da, genel kt dzeni asndan '-' karakterini yazdrmay ve satrn sonlandrlmasn kendimiz ayrca halletmek zorunda kalyoruz. Oysa, nesnelerin dier trler gibi kullanl olabilmeleri iin rnein u ekilde yazabilmemiz ok yararl olurdu: writefln("%s-%s", sabah, akam);

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

259

ye levler

yle yazabilseydik; daha nceki 4 satr byle tek satra indirgemi olmann yannda, nesneleri stdout 'tan baka akmlara da, rnein bir dosyaya da ayn ekilde yazdrabilirdik: auto dosya = File("zaman_bilgisi", "w"); dosya.writefln("%s-%s", sabah, akam); Phobos ktphanesi, yaplarn toString ismindeki ye ilevlerini, nesneleri string trne dntrmek iin kullanr. Bunun doru olarak alabilmesi iin, ismi "string'e dntr"den gelen bu ilev, o nesneyi ifade eden bir string dndrmelidir. Bu ilevin ieriini sonraya brakalm, ve nce yap iinde nasl tanmlandna bakalm: import std.stdio; struct GnnSaati { int saat; int dakika; string toString() { return "deneme"; }

void main() { auto sabah = GnnSaati(10, 0); auto akam = GnnSaati(22, 0); } writefln("%s-%s", sabah, akam);

Nesneleri dizgi olarak kullanabilen ktphane ilevleri, nesnelerin toString ilevini arrlar ve dndrlen dizgiyi kendi amalarna uygun bir ekilde kullanrlar. Bu rnekte henz anlaml bir dizgi retmediimiz iin kt da imdilik anlamsz oluyor: deneme-deneme Ayrca bilgiVer 'i artk emekliye ayrmakta olduumuza da dikkat edin; toString 'in tanmn tamamlaynca ona ihtiyacmz kalmayacak. toString ilevini yazmann en kolay yolu, std.string modlnde tanmlanm olan format ilevini kullanmaktr. Bu ilev, kt dzeni iin kullandmz btn olanaklara sahiptir ve rnein writef ile ayn ekilde alr. Tek fark, rettii sonucu bir akma gndermek yerine, bir string olarak dndrmesidir. toString 'in de zaten bir string dndrmesi gerektii iin, format 'n dndrd deeri olduu gibi dndrebilir: import std.stdio; import std.string; struct GnnSaati { int saat; int dakika; string toString() { return format("%02s:%02s", saat, dakika); }

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

260

ye levler

} void main() { auto sabah = GnnSaati(10, 0); auto akam = GnnSaati(22, 0); } writefln("%s-%s", sabah, akam);

toString 'in yalnzca bu nesneyi string 'e dntrdne dikkat edin. ktnn geri kalan, writefln ars tarafndan halledilmektedir. writefln , "%s" dzen bilgilerine karlk olarak toString 'i otomatik olarak iki nesne iin ayr ayr arr, aralarna '-' karakterini yerletirir, ve en sonunda da satr sonlandrr: 10:00-22:00

51.1.2

Baka bir rnek: ekle


Bu sefer de GnnSaati nesnelerine zaman ekleyen bir ye ilev tanmlayalm. Ama ona gemeden nce, nceki derslerde yaptmz bir yanl gidermek istiyorum. Yaplar dersinde tanmladmz zamanEkle ilevinin, GnnSaati nesnelerini toplamasnn normal bir ilem olmadn grm, ama yine de o ekilde kullanmtk: GnnSaati zamanEkle(in GnnSaati balang, in GnnSaati eklenecek) { // ... } Gn iindeki iki zaman birbirine eklemek doal bir ilem deildir. rnein yola kma zamanna sinemaya varma zamann ekleyemeyiz. Gn iindeki bir zamana eklenmesi normal olan, bir sredir. rnein yola kma zamanna yol sresini ekleyerek sinemaya var zamann buluruz. te yandan, gn iindeki iki zamann birbirlerinden kartlmalar normal bir ilem olarak grlebilir. O ilemin sonucu da rnein Sre trnden olmaldr. Bu bak as ile, dakika duyarlyla alan bir Sre yapsn ve onu kullanan zamanEkle ilevini yle yazabiliriz: struct Sre { int dakika; } GnnSaati zamanEkle(in GnnSaati balang, in Sre sre) { // balang'n kopyasyla balyoruz GnnSaati sonu = balang; // Sreyi ekliyoruz sonu.dakika += sre.dakika; // Tamalar ayarlyoruz sonu.saat += sonu.dakika / 60; sonu.dakika %= 60; sonu.saat %= 24; } return sonu;

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

261

ye levler

unittest { // Basit bir test assert(zamanEkle(GnnSaati(10, 30), Sre(10)) == GnnSaati(10, 40)); // Gece yars testi assert(zamanEkle(GnnSaati(23, 9), Sre(51)) == GnnSaati(0, 0)); // Sonraki gne tama testi assert(zamanEkle(GnnSaati(17, 45), Sre(8 * 60)) == GnnSaati(1, 45));

imdi ayn ilevi bir ye ilev olarak tanmlayalm. ye ilev zaten bir nesne zerinde alaca iin GnnSaati parametresine gerek kalmaz ve parametre olarak yalnzca sreyi geirmek yeter: struct Sre { int dakika; } struct GnnSaati { int saat; int dakika; string toString() { return format("%02s:%02s", saat, dakika); } void ekle(in Sre sre) { dakika += sre.dakika; saat += dakika / 60; dakika %= 60; saat %= 24;

} unittest { auto zaman = GnnSaati(10, 30); // Basit bir test zaman.ekle(Sre(10)); assert(zaman == GnnSaati(10, 40)); // 15 saat sonra bir sonraki gne tamal zaman.ekle(Sre(15 * 60)); assert(zaman == GnnSaati(1, 40)); // 22 saat ve 20 dakika sonra gece yars olmal zaman.ekle(Sre(22 * 60 + 20)); assert(zaman == GnnSaati(0, 0));

ekle , nesnenin zamann belirtilen sre kadar ilerletir. Daha sonraki derslerde greceimiz ile ykleme olana sayesinde bu konuda biraz daha kolaylk kazanacaz. rnein += ilecini ykleyerek yap nesnelerini de temel trler gibi kullanabileceiz: zaman += Sre(10); // bunu daha sonra reneceiz

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

262

ye levler

Ayrca grdnz gibi, ye ilevler iin de unittest bloklar yazlabilir. O bloklarn yap tanmn kalabalklatrdn dnyorsanz, blou btnyle yapnn dnda da tanmlayabilirsiniz: struct GnnSaati { // ... yap tanm ... } unittest { // ... yap testleri ... } Bunun nedeni, unittest bloklarnn aslnda belirli bir noktada tanmlanmalarnn gerekmemesidir. Denetledikleri kodlarla bir arada bulunmalar daha doal olsa da, onlar uygun bulduunuz baka yerlerde de tanmlayabilirsiniz.

51.2

Problemler
1. GnnSaati yapsna, nesnelerin deerini Sre kadar azaltan bir ye ilev ekleyin. ekle ilevinde olduu gibi, sre azaltldnda bir nceki gne tasn. rnein 00:05'ten 10 dakika azaltnca 23:55 olsun. Baka bir deyile, azalt ilevini u birim testlerini geecek ekilde yazn: struct GnnSaati { // ... void azalt(in Sre sre) { // ... burasn siz yazn ... } unittest { auto zaman = GnnSaati(10, 30); // Basit bir test zaman.azalt(Sre(12)); assert(zaman == GnnSaati(10, 18)); // 3 gn ve 11 saat nce zaman.azalt(Sre(3 * 24 * 60 + 11 * 60)); assert(zaman == GnnSaati(23, 18)); // 23 saat ve 18 dakika nce gece yars olmal zaman.azalt(Sre(23 * 60 + 18)); assert(zaman == GnnSaati(0, 0)); // 1 dakika ncesi zaman.azalt(Sre(1)); assert(zaman == GnnSaati(23, 59));

2. Daha nce lev Ykleme dersinin zmnde kullanlan dier btn bilgiVer ilevlerinin yerine; Toplant , Yemek , ve GnlkPlan yaplar iin toString ye ilevlerini tanmlayn. ok daha kullanl olmalarnn yannda, her birisinin tek satrda yazlabildiini greceksiniz. ... zmler

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

263

const ref Parametreler ve const ye levler

52

const ref Parametreler ve const ye levler


Bu derste ye ilevlerin const nesnelerle de kullanlabilmeleri iin nasl const olarak iaretlenmeleri gerektiini greceiz. Burada her ne kadar yaplar kullanyor olsak da; const ye ilevler snflar iin de geerlidir.

52.1

const nesneler
imdiye kadarki derslerde baz rneklerde const deikenler ve nesneler tanmlam ve const anahtar szcnn nesnelerin deitirilemez olmalarn saladn grmtk: const auto okumaSaati = GnnSaati(15, 0); okumaSaati deitirilemez: okumaSaati = GnnSaati(16, 0); okumaSaati.dakika += 10; // derleme HATASI // derleme HATASI

Derleyici; const nesneye yeni bir deer atanmasna, bir yesinin deitirilmesine, veya herhangi baka bir deiiklik yaplmasna izin vermez. Zaten const olarak iaretlemenin amac da budur: baz nesnelerin deerlerinin deimeyecei garantisi gerekebilir.

52.2

const ref parametreler


const ve immutable dersinde grdmz gibi, immutable nesneler programda kesinlikle deimeyen nesnelerdir. const nesneler ise belirli bir referans yoluyla, veya belirli bir balamda deimeyen nesnelerdir. Kendisi aslnda const olmayan bir nesne, belirli bir balamda deitirilmeden kullanlabilir. Bunun bir rnei, parametrelerdir. const ref olarak iaretlenen bir parametre, o ilev iinde deitirilmeyecek demektir: int toplamSaniye(const ref Sre sre) { return 60 * sre.dakika; } O ilev, parametresini const olarak iaretleyerek o parametrede deiiklik yapmayaca garantisini vermi olur. Derleyici de o nesnenin deitirilmesine izin vermez: int toplamSaniye(const ref Sre sre) { sre.dakika = 7; // derleme HATASI // ... }

52.3

const olmayan ref parametreler


ref parametrelerin ilev iinde deitirilmemeleri ynnde bir kstlama yoktur. O parametreyi deitirmiyor bile olsalar, bunun garantisini vermedikleri iin o ilevlere const nesne gnderilemez: // sre'yi deitirmiyor olsa bile const olarak iaretlememi int toplamSaniye(ref Sre sre) {

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

264

const ref Parametreler ve const ye levler

return 60 * sre.dakika; } // ... const Sre snmaSresi = Sre(3); toplamSaniye(snmaSresi);

// derleme HATASI

Not: Yukardaki kod, bu dersi yazdm srada kullandm derleyicinin (dmd 2.046) bir hatas yznden derlenebiliyor. O hata giderildiinde yukardaki kod derleme hatasna neden olacaktr. Derleyici, const olan snmaSresi nesnesinin toplamSaniye ilevine gnderilmesine izin vermez; nk toplamSaniye ilevi, parametresinde deiiklik yapmayaca garantisini vermemektedir.

52.4

const olmayan ye ilevler


Nesneleri deitirmenin baka bir yolu, ye ilevlerdir. rnein GnnSaati.ekle ilevi, zerinde arld nesneyi ona bir Sre ekleyerek deitiriyordu: struct GnnSaati { // ... void ekle(in Sre sre) { dakika += sre.dakika; saat += dakika / 60; dakika %= 60; saat %= 24;

} // ... } // ... auto balang = GnnSaati(5, 30); balang.ekle(Sre(30)); // balang deiir

52.5

const ye ilevler
Baz ye ilevler, zerinde arldklar nesnede deiiklik yapmazlar: struct GnnSaati { // ... string toString() { return format("%02s:%02s", saat, dakika); } // ... } toString 'in ii, nesneyi string olarak ifade etmektir; nesnenin kendisinde bir deiiklik yapmaz. ye ilevlerin nesnede bir deiiklik yapmayacaklar garantisi, parametre listesinden sonra yazlan const szc ile belirtilir: struct GnnSaati { // ... string toString() const {

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

265

const ref Parametreler ve const ye levler

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

O const , nesnenin o ilev iinde deitirilmeyecei garantisini salar. Bylece derleyici toString 'i const nesnelerle de arabilir. nk toString const olarak iaretlenmemi olsa, const nesneleri yazdramamak gibi yapay bir kstlamayla kar karya kalnr: struct GnnSaati { // ... // const olarak iaretlenmemi (yanl tasarm) string toString() { return format("%02s:%02s", saat, dakika); } } // ... const auto balang = GnnSaati(5, 30); string dizgiOlarak = balang.toString(); // derleme HATASI imdiye kadarki derslerde grdmz toString ye ilevlerinin bu kstlama yznden yanl tasarlandklarn syleyebiliriz.

52.6

Ne zaman kullanmal
toString gibi, nesnede deiiklik yapmayan ye ilevleri her zaman iin const olarak iaretleyin: struct GnnSaati { // ... string toString() const { return format("%02s:%02s", saat, dakika); } } Bylece yapnn ve snfn kullanll gereksizce kstlanmam olur. Bundan sonraki derslerdeki kodlar da buna uygun olarak tasarlanacaklar. Not: levin nesnede deiiklik yapmayacan garanti eden bu const anahtar szc aslnda ilevin tanmndan nce de yazlabilir: // sttekiyle ayn ey const string toString() { return format("%02s:%02s", saat, dakika); } Dn tryle karma riski olduu iin ben const anahtar szcn bu ekilde deil, yukarda gsterdiim gibi parametre listesinden sonra yazmay yeliyorum.

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

266

Kurucu ve Dier zel levler

53

Kurucu ve Dier zel levler


Bu derste her ne kadar yaplar kullanyor olsak da; bu temel ilemler daha sonra greceimiz snflar iin de geerlidir. Snflardaki farkllklarn daha sonraki derslerde gstereceim. Yaplarn ye ilevleri arasndan drt tanesi, nesnelerin temel ilemlerini belirledikleri iin ayrca nemlidir: kurucu ilev this sonlandrc ilev ~this kopya sonrasn belirleyen this(this) atama ileci opAssign

Nesnelerin bu temel ilemlerini de kendimiz belirleyebiliriz. Bu drt temel ilemin normalde yaplar iin zel olarak tanmlanmalar gerekmez. nk o ilemler zaten derleyici tarafndan otomatik olarak halledilirler. Yine de, baz durumlarda bu ilevleri kendimiz tanmlamak isteyebiliriz.

53.1

Kurucu ilev
Kurucu ilevin asl grevi, bir nesnenin yelerine gerekli deerleri atayarak onu kullanlabilir hale getirmektir. Kurucu ilevleri imdiye kadar hem btn yap rneklerinde hem de rnein File gibi baka trlerde grdk. Trn ismini bir ilev ars gibi kullandmz noktalarda o trn kurucu ilevi arlr. rnein u satrn sa tarafnda: auto dersBa = GnnSaati(8, 30); Benzer ekilde, bir snf nesnesi kurulurken yine sa tarafta: auto deiken = new BirSnf; Tr ismi ilev ars gibi kullanlrken parantez iinde yazlanlar da kurucu ilevin parametreleri olurlar. rnein yukardaki 8 ve 30 deerleri GnnSaati kurucu ilevinin parametreleridir.

53.1.1

Sz dizimi
Dier ilevlerden farkl olarak, kurucu ilevlerin dn deerleri yoktur; ve dn tr olarak void bile yazlmaz. Kurucu ilevin ismi this olmak zorundadr. "Bu" anlamna gelen "this"in "bu trden nesne kuran ilev" sznden geldiini dnebilirsiniz: struct BirYap { // ... this(/* kurucu parametreleri */) { // ... nesneyi kuran ilemler ... }

Kurucu parametreleri, yap nesnesini kullanma hazrlamak iin gereken bilgilerden oluur. Parametre listesi yaplarda bo olamaz:

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

267

Kurucu ve Dier zel levler

struct BirYap { this() // derleme HATASI { // ... } } Derleyici, varsaylan kurucunun yaplar iin tanmlanamayacan bildiren bir hata mesaj vererek sonlanr: Error: constructor deneme.BirYap.this default constructor not allowed for structs Bunun snflar iin mmkn olduunu ileride greceiz.
53.1.2

Derleyici tarafndan salanan otomatik kurucu ilev


imdiye kadar grdmz btn yap rneklerinde derleyici tarafndan salanan otomatik kurucu ilevi kullandk. O kurucunun ii, parametre deerlerini srayla yelere atamaktr. Ayrca Yaplar dersinden hatrlayacanz gibi, parametre listesinde sonda bulunan parametreler iin deer belirtilmesi gerekmez. Deerleri belirtilmeyen yeler, kendi trlerinin .init deerlerini alrlar. Parametre Serbestlii dersinde gsterilen varsaylan parametre deerleri olanan da hatrlarsak, derleyicinin salad otomatik kurucu ilevin u ekilde yazlm olduunu dnebiliriz: struct Deneme { char karakter; int tamsay; double kesirli; /* Derleyicinin salad kurucu ilevin edeeri */ this(in char karakter_parametre = char.init, in int tamsay_parametre = int.init, in double kesirli_parametre = double.init) { karakter = karakter_parametre; tamsay = tamsay_parametre; kesirli = kesirli_parametre; }

Eer ou yapda olduu gibi o kadar yeterliyse, bizim ayrca kurucu ilev tanmlamamz gerekmez. Btn yeler iin geerli deerlerin parametre olarak verilmesi, nesnelerin kurulmu olmalar iin yeterlidir.
53.1.3

yelere this. ile eriim


Yukardaki kodda parametrelerle yeler karmasnlar diye parametrelerin sonlarna _parametre diye bir belirte ekledim. nk parametrelerin isimlerini de yelerle ayn yapsaydm kod hatal olurdu: struct Deneme { char karakter; int tamsay; double kesirli;

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

268

Kurucu ve Dier zel levler

this(in char karakter = char.init, in int tamsay = int.init, in double kesirli = double.init) { // parametreyi kendisine atyor! karakter = karakter; // derleme HATASI tamsay = tamsay; kesirli = kesirli; }

lev iinde karakter yazldnda ye deil, parametre anlalr. Parametreler de giri parametresi olduklar iin in olarak iaretlendiklerinden sabit deerin deitirilemeyeceini bildiren derleme hatas alrz: Error: variable deneme.Deneme.this.karakter cannot modify const Bu konuda bir zm olarak this. 'dan yararlanlr: ye ilevler iinde this. , bu nesne anlamna gelir. Bu olana kullannca, parametrelerin isimlerinin sonlarna artk _parametre gibi ekler yazmak gerekmez: this(in char karakter = char.init, in int tamsay = int.init, in double kesirli = double.init) { this.karakter = karakter; this.tamsay = tamsay; this.kesirli = kesirli; } karakter yazldnda parametre, this.karakter yazldnda da "bu nesnenin yesi" anlalr; ve kod artk istediimizi yapacak ekilde derlenir ve alr. Bunlar, derleyicinin otomatik olarak yazd kurucu ilevin perde arkasnda nasl altn gstermek iin anlattm. Yukarda da belirttiim gibi, eer yapnn kurulmas iin bu kadar yeterliyse, bu kurucuyu yazmanz gerekmez. Bu kurucu, perde arkasnda zaten derleyici tarafndan otomatik olarak yazlr ve arlr.
53.1.4

Programc tarafndan tanmlanan kurucu ilev


Bazen nesnenin kurulabilmesi iin yelere srayla deer atamaktan daha karmak ilemler gerekebilir. rnek olarak nceki derste kullandmz Sre yapsna bakalm: struct Sre { int dakika; } Tek bir tamsay yesi bulunan bu yap iin derleyicinin salad kurucu ou durumda yeterlidir: zaman.azalt(Sre(12)); Ancak; o kurucu yalnzca dakika miktarn ald iin, baz durumlarda programclarn hesaplar yapmalar gerekebilir: // 23 saat ve 18 dakika ncesi zaman.azalt(Sre(23 * 60 + 18));

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

269

Kurucu ve Dier zel levler

// 22 saat ve 20 dakika sonras zaman.ekle(Sre(22 * 60 + 20)); Programclar bu fazladan hesaplardan kurtarmak iin, saat ve dakika miktarlarn iki ayr parametre olarak alan bir Sre kurucusu tanmlayabiliriz. Bylece toplam dakika hesab kurucu iinde yaplabilir: struct Sre { int dakika; this(int saat, int dakika) { this.dakika = saat * 60 + dakika; }

Saat ve dakika farkl iki parametre olunca, programclar da aritmetik hesab kendileri yapmak zorunda kalmam olurlar: // 23 saat ve 18 dakika ncesi zaman.azalt(Sre(23, 18)); // 22 saat ve 20 dakika sonras zaman.ekle(Sre(22, 20));

53.1.5

Programcnn kurucusu otomatik kurucunun baz kullanmlarn geersizletirir


Programc tarafndan tek bir kurucu ilevin bile tanmlanm olmas, derleyicinin oluturduu kurucu ilevin varsaylan parametre deerleri ile kullanmn geersiz hale getirir. rnein Sre 'nin tek parametre ile kurulmas derleme hatasna neden olur: zaman.azalt(Sre(12)); // derleme HATASI

O tek parametreli kullanm, programcnn tanmlam olduu iki parametreli kurucuya uymamaktadr. Ek olarak, Sre 'nin otomatik kurucusu o kullanmda artk geersizdir. zm olarak kurucuyu ykleyebilir ve bir tane de tek parametreli kurucu tanmlayabiliriz: struct Sre { int dakika; this(int saat, int dakika) { this.dakika = saat * 60 + dakika; } this(int dakika) { this.dakika = dakika; }

Programc tarafndan tanmlanan kurucu, nesnelerin { } karakterleriyle kurulmalar olanan da ortadan kaldrr: Sre sre = { 5, 0 }; // derleme HATASI

Bunlara ramen, hi parametre yazlmadan kurulum her zaman iin geerlidir:

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

270

Kurucu ve Dier zel levler

auto s = Sre();

// derlenir

Grld gibi; tek bir kurucu ilevin bile tanmlanm olmas otomatik kurucunun baz kullanmlarn geersiz hale getirir, ama hi parametre yazlmadan kurulum yine de geerlidir.
53.1.6

Baka kurucu ilevleri armak


Kurucu ilevler baka kurucu ilevleri arabilirler. Bylece kod tekrar azaltlm olur. Sre gibi basit bir yap bunun yararn grmek iin uygun deil. Yine de kullanmn yle gsterebiliriz: this(int saat, int dakika) { this.dakika = saat * 60 + dakika; } this(int dakika) { this(0, dakika); }

// dier kurucuyu aryor

Yalnzca dakika alan kurucu, dier kurucuyu saat deeri olarak 0 gndererek aryor.
53.1.7

Uyar
Yukardaki Sre kurucularnda bir tasarm hatas bulunduunu syleyebiliriz. Bunun nedeni, nesneler tek parametre ile kurulduklarnda ne yaplmak istendiinin ak olmayabileceidir: auto yolSresi = Sre(10); // 10 saat mi, 10 dakika m?

Sre 'nin belgelerine veya tanmna bakarak "10 dakika" dendiini anlayabiliriz. te yandan, iki parametre alan kurucuda ilk parametrenin saat olmas bir tutarszlktr. Byle tasarmlar programlarda karklklara neden olur; kanlmalar gerekir.

53.2

Sonlandrc ilev
Nesnenin yaam sreci sona ererken gereken ilemler sonlandrc ilev tarafndan iletilir. Derleyicinin sunduu otomatik sonlandrc, sra ile btn yelerin kendi sonlandrclarn arr. Kurucu ilevde de olduu gibi, ou yap iin bu kadar zaten yeterlidir. Baz durumlarda ise nesnenin sonlanmasyla ilgili baz zel ilemler gerekebilir. rnein nesnenin sahiplenmi olduu bir iletim sistemi kaynann geri verilmesi gerekiyordur; baka bir nesnenin bir ye ilevi arlacaktr; baka bir bilgisayar zerinde almakta olan bir programa onunla olan balantmz kesmekte olduumuz bildirilecektir; vs. Kurucuda olduu gibi, sonlandrc ilevin de dn tr yoktur ve ismi ~this 'tir.

53.2.1

Sonlandrc ilev mutlaka iletilir


Sonlandrc ilev, yap nesnesinin geerlilii bittii an iletilir. Yaam Sreleri dersinden hatrlayacanz gibi; nesnelerin yaam srelerinin, tanmlandklar kapsamdan klrken sona erdiini grmtk. Bir yap nesnesinin yaamnn sona erdii durumlar unlardr:

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

271

Kurucu ve Dier zel levler

iinde tanmland kapsamdan normal olarak veya atlan bir hata ile kld zaman if (birKoul) { auto sre = Sre(7); // ... } // sre'nin sonlandrcs burada iletilir

hazr deerler iin; hazr deer nesnesinin tanmland ifadenin en sonu zaman.ekle(Sre(5)); // Sre(5) hazr deeri bu // satrn sonunda sonlanr

yesi olduu yap nesnesinin sonlanmas: bir nesnenin btn yeleri de asl nesne ile birlikte sonlanrlar
53.2.2

Sonlandrc rnei
Sonlandrc rnei olarak XML dzeni oluturmaya yarayan bir yapya bakalm. XML elemanlar al parantezlerlerle belirtilirler, ve verilerden ve baka XML elemanlarndan oluurlar. XML elemanlarnn nitelikleri de olabilir; onlar bu rnekte dikkate almayacaz. Burada amacmz, <isim> eklinde alan bir XML elemannn doru olarak ve mutlaka </isim> eklinde kapatlmasn salamak olsun: <ders1> <not> 57 </not> </ders1> dtaki XML elemannn almas iteki XML elemannn almas veri itekinin kapatlmas dtakinin kapatlmas

Bunu salayacak bir yapy iki yesi olacak ekilde tasarlayabiliriz. Bu yeler, XML elemannn ismini ve kta ne kadar girintiyle yazdrlacan temsil edebilirler: struct XmlEleman { string isim; string girinti; } Eer XML elemann ama iini kurucu ileve, ve kapama iini de sonlandrc ileve yaptrrsak; nesnelerin yaam srelerini belirleyerek istediimiz kty elde edebiliriz. rnein ktya nesne kurulduunda <eleman> , sonlandrldnda da </eleman> yazdrabiliriz. Kurucu ilevi bu amaca gre yle yazabiliriz: this(in string isim, in int dzey) { this.isim = isim; this.girinti = girintiDizgisi(dzey); } writeln(girinti, '<', isim, '>');

Kurucunun son satr, XML elemannn almasn salar. girintiDizgisi , o dzeyin girintisini belirleyen ve boluklardan oluan bir string retir: string girintiDizgisi(in int girintiAdm) {

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

272

Kurucu ve Dier zel levler

return repeat(" ", girintiAdm * 2);

Yararland repeat ilevi, kendisine verilen dizgiyi belirtilen sayda u uca ekleyerek yeni bir dizgi reten bir ilevdir; std.string modlnde tanmldr. Bu durumda yalnzca boluk karakterlerinden oluuyor ve satr balarndaki girintileri oluturmak iin kullanlyor. Sonlandrc ilevi de benzer ekilde ve XML elemann kapatmak iin yle yazabiliriz: ~this() { writeln(girinti, "</", isim, '>'); } O yapy kullanan bir deneme program aadaki gibi yazlabilir: void main() { auto dersler = XmlEleman("dersler", 0); foreach (dersNumaras; 0 .. 2) { auto ders = XmlEleman("ders" ~ to!string(dersNumaras), 1); foreach (i; 0 .. 3) { auto not = XmlEleman("not", 2); const int rasgeleNot = uniform(50, 101); writeln(girintiDizgisi(3), rasgeleNot);

XmlEleman nesnelerinin kapsamda oluturulduklarna dikkat edin. Bu programdaki XML elemanlarnn alp kapanmalar, btnyle o nesnelerin kurucu ve sonlandrc ilevleri tarafndan oluturulmaktadr. Programn ktsnda birbirlerine karlk gelen kurucu ve sonlandrc ktlarn ayn renkle gsterdim. D kapsam iin krmz, ortadaki iin mavi, ve ierdeki iin yeil:

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

273

Kurucu ve Dier 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> ktda rnek olarak <dersler> elemanna bakalm: main iinde ilk olarak dersler nesnesi kurulduu iin ilk olarak onun kurucusunun ktsn gryoruz; ve main 'den klrken sonlandrld iin de en son onun sonlandrcsnn ktsn gryoruz.

53.3

Kopya sonras ilevi


Kopyalama, var olan bir nesnenin kopyas olarak yeni bir nesne oluturmaktr. Yaplarda kopyalama iinin ilk aamasn derleyici gerekletirir. Yeni nesnenin btn yelerini srayla, var olan nesnenin yelerinden kopyalar: auto dnSresi = gidiSresi; // kopyalama

Bu ilemi atama ilemi ile kartrmayn. Sol taraftaki auto , dnSresi isminde yeni bir nesne kurulduunu gsterir. auto , oradaki tr ismi yerine gemektedir. Atama olmas iin, dnSresi 'nin daha nceden tanmlanm bir nesne olmas gerekir: dnSresi = gidiSresi; // atama (aada anlatlyor)

Eer herhangi bir nedenle kopyalama ile ilgili olarak zel bir ilem gerekletirmek istersek, bunu ancak otomatik kopyalama ileminden sonrasn belirleyecek ekilde yapabiliriz. Biz otomatik kopyalamaya karamayz, ancak sonrasn belirleyebiliriz. Kurma ile ilgili olduu iin kopya sonras ilevinin ismi de this 'tir. Dier kuruculardan ayrt edilebilmesi iin de parametre listesine zel olarak this yazlr: this(this) {

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

274

Kurucu ve Dier zel levler

// ...

Yaplar dersinde basit bir renci yaps kullanm ve onun nesnelerinin kopyalanmalar ile ilgili bir sorundan sz etmitik: struct renci { int numara; int[] notlar; } O yapnn notlar yesi dinamik dizi olduu iin bir referans trdr. Bu, bir renci nesnesinin bir bakasna kopyalanmas durumunda ikisinin notlar yelerinin ayn asl diziye eriim salamalarna neden olur. Birinin notlarnda yaplan deiiklik, dierinde de grlr: auto renci1 = renci(1, [ 70, 90, 85 ]); auto renci2 = renci1; renci2.numara = 2; // kopyalama

renci1.notlar[0] += 5; // ikincinin notu da deiir: assert(renci2.notlar[0] == 75); Bunun nne gemek iin, ikinci rencinin notlar yesinin o nesneye ait bir dizi olmas salanmaldr. Bunu, kopya sonras ilevinde gerekletirebiliriz: struct renci { int numara; int[] notlar; this(this) { notlar = notlar.dup; }

this(this) ilevine girildiinde btn yelerin oktan asl nesnenin kopyalar olarak kurulduklarn hatrlayn. leve girildiindeki notlar , asl nesnenin notlar ' ile ayn diziye eriim salamaktadr. Yukardaki tek satrda yaplan ise, notlar 'n eriim salad asl dizinin bir kopyasn almak, ve onu yine bu nesnenin notlar 'na atamaktr. Bylece bu nesnenin notlar ' yeni bir diziye eriim salamaya balar. Birinci rencinin notlarnda yaplan deiiklik, artk ikinci rencinin notlarn etkilemez: renci1.notlar[0] += 5; assert(renci2.notlar[0] == 70);

53.4

Atama ileci
Atama, zaten var olan bir nesneye yeni bir deer vermek anlamna gelir: dnSresi = gidiSresi; // atama

Atama, nesne temel ilemleri arasnda dierlerinden biraz daha karmaktr ve bu nedenle hatalara aktr. Bunun nedeni, atama ileminin aslnda iki admdan oluuyor olmasdr: nesnenin sonlandrlmas

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

275

Kurucu ve Dier zel levler

yeni deerle tekrar kurulmas Ancak, bu iki admn yazdm srada iletilmelerinde nemli bir sorun vardr: Daha nesnenin ikinci admda baaryla kurulabileceinden emin olmadan, onu birinci admda gvenle sonlandramayz. Yoksa, nesnenin yeni deerle tekrar kurulmas aamasnda bir hata atlsa, elimizde sonlandrlm ama tekrar kurulamam bir nesne kalr. Derleyicinin sunduu otomatik atama ileci bu yzden gvenli hareket etmek zorundadr ve perde arkasnda u ilemleri gerekletirir: 1. bu nesneyi geici bir nesneye kopyalar 2. yeni deeri bu nesneye kopyalar 3. geici nesneyi sonlandrr Derleyicinin sunduu otomatik atama ileci hemen hemen her durumda yeterlidir. Eer herhangi bir nedenle kendiniz tanmlamak isterseniz, atlabilecek olan hatalara kar dikkatli olmak gerektiini unutmayn. Sz dizimi u ekildedir: opAssign zel ismiyle tanmlanr Parametre olarak yapnn trn const ref olarak alr Dn tr, yapnn trnn ref olandr levden return this ile klr

Ben burada basit Sre yaps zerinde ve ktya bir mesaj yazdracak ekilde tanmlayacam: struct Sre { int dakika; ref Sre opAssign(const ref Sre sadaki) { writefln( "dakika, %s deerinden %s deerine deiiyor", this.dakika, sadaki.dakika); this.dakika = sadaki.dakika; } return this;

} // ... auto sre = Sre(100); sre = Sre(200);

// atama

dakika, 100 deerinden 200 deerine deiiyor Sre gibi kk yaplarda parametre tr const ref yerine in olarak da belirtilebilir: ref Sre opAssign(in Sre sadaki) { // ... } Fark, in parametrenin ileve kopyalanarak gnderilmesidir. Anlamsal olarak farklar olmad gibi, kk yaplarda hz kaygs da dourmaz.

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

276

Kurucu ve Dier zel levler

53.4.1

Baka trlerden atamak


Baz durumlarda nesnelere kendi trlerinden farkl trlerin deerlerini de atamak isteyebiliriz. rnein atama ilecinin sa tarafnda her zaman iin Sre tr kullanmak yerine, dorudan bir tamsay deer kullanmak isteyebiliriz: sre = 300; Bunu, parametre olarak int alan bir atama ileci daha tanmlayarak salayabiliriz: struct Sre { int dakika; ref Sre opAssign(const ref Sre sadaki) { writefln( "dakika, %s deerinden %s deerine deiiyor", this.dakika, sadaki.dakika); this.dakika = sadaki.dakika; } return this;

ref Sre opAssign(int dakika) { writeln( "dakika, bir tamsay deer ile deitiriliyor"); this.dakika = dakika; } return this;

} // ... sre = Sre(200); sre = 300;

dakika, 100 deerinden 200 deerine deiiyor dakika, bir tamsay deer ile deitiriliyor

53.4.2

Uyar
Farkl trleri bu ekilde birbirlerine eitleyebilmek, veya daha genel olarak birbirlerinin yerine kullanabilmek; kolaylk getirdii kadar karklklara ve hatalara da neden olabilir. Atama ilecini farkl trlerden parametre alacak ekilde tanmlamann yararl olduunu dndnz zamanlarda, bunun gerekten gerekli olup olmadn iyice tartmanz neririm. Kimi zaman yararldr, kimi zaman gereksiz ve sorunludur.

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

277

le Ykleme

54

le Ykleme
Bu derste yaplar zerinde anlatlanlar daha sonra greceimiz snflar iin de hemen hemen aynen geerlidir. En belirgin fark, zel ilevler dersinde grdmz atama ileci opAssign 'n snflar iin tanmlanamyor olmasdr. le ykleme, ilelerin kendi trlerimizle nasl alacaklarn belirleme olanadr. Yaplarn ve onlar iin zel olarak tanmlayabildiimiz ye ilevlerin yararlarn nceki derslerde grdk. Bunun bir rnei, GnnSaati nesnelerine Sre nesnelerini ekleyebilmekti. Kodu ksa tutmak iin yalnzca bu dersi ilgilendiren yelerini gsteriyorum: struct Sre { int dakika; } struct GnnSaati { int saat; int dakika; void ekle(in Sre sre) { dakika += sre.dakika; saat += dakika / 60; dakika %= 60; saat %= 24;

void main() { auto yemekZaman = GnnSaati(12, 0); yemekZaman.ekle(Sre(10)); } ye ilevlerin yarar, yap ile ilgili ilemlerin yap tanm ile ayn yerde yaplabilmesidir. yeler ve ilevler bir arada tanmlannca, yapnn yelerinin btn ilevler tarafndan doru olarak kullanldklar da hep birden denetlenebilir. Btn bu yararlarna karn, temel trlerle karlatrldklarnda yaplarn bir yetersizlikleri ortaya kar: Temel trler, zel hibir ey yapmaya gerek olmadan ilelerle rahata kullanlabilirler: int arlk = 50; arlk += 10;

// ilele

imdiye kadar rendiimiz bilgiler dorultusunda benzer ilemleri yaplar iin ancak ye ilevlerle gerekletirebiliyoruz: auto yemekZaman = GnnSaati(12, 0); yemekZaman.ekle(Sre(10)); // ye ilevle le ykleme olana bu konuda yarar salar: Yaplar da temel trlerde olduu gibi ilelerle kullanabilme olana sunar. rnein GnnSaati yaps iin tanmlayabileceimiz += ileci, yukardaki ilemi temel trlerde olduu gibi doal olarak yazmamz salar: yemekZaman += Sre(10); // yap iin de ilele

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

278

le Ykleme

Yklenebilecek btn ilelere gemeden nce bu kullanmn nasl salandn gstermek istiyorum. Daha nce ismini ekle olarak yazdmz ilevi, D'nin zel olarak belirledii opOpAssign(string ile) ismiyle tanmlamak ve bu tanmn "+" ilemi iin yaplmakta olduunu belirtmek gerekir. imdiye kadar grdmz ilev tanmlarndan farkl olan bu tanm, opOpAssign aslnda bir ilev ablonu olduundandr. ablonlar daha ilerideki derslerde gstereceim; imdilik ile ykleme konusunda bu sz dizimini bir kalp olarak uygulamak gerektiini kabul etmenizi rica ediyorum: struct GnnSaati { // ... void opOpAssign(string ile)(in Sre sre) if (ile == "+") { dakika += sre.dakika; saat += dakika / 60; dakika %= 60; saat %= 24;

// (1) // (2)

Yukardaki ablon tanm iki paradan oluur: 1. yelevsmi(string ile) : Yukardaki opOpAssign 'da olduu gibi yelevsmi'nin ve (string ile) 'in aynen yazlmalar gerekir; kullanlabilecek btn ye ilev isimlerini aada greceiz. 2. if (ile == "ile_karakteri") : Ayn ye ilev ismi birden fazla ilev iin kullanldndan hangi ile karakterinin tanmlanmakta olduu bu sz dizimiyle belirtilir. Aslnda bir ablon kstlamas olan bu sz diziminin ayrntlarn da daha sonraki bir derste greceiz. Derleyici, GnnSaati nesnelerinin += ileciyle kullanld yerlerde perde arkasnda opOpAssign!"+" ilevini arr: yemekZaman += Sre(10); // sttekinin edeeri: yemekZaman.opOpAssign!"+"(Sre(10)); opOpAssign 'dan sonra yazlan !"+" , opOpAssign 'n + ileci iin tanm olan ilevin arlmakta olduu anlamna gelir. ablonlarla ilgili olan bu sz dizimini de daha ilerideki bir derste gstereceim. Kod iindeki += ilecine karlk gelen ye ilevde "+=" deil, "+" kullanldna dikkat edin. opOpAssign 'n isminde geen ve "deer ata" anlamna gelen "assign", zaten atama kavramn ierir; yani opOpAssign!"+" , atamal toplama ilemi olan += ilecinin tanmdr.

54.1

Allm olan davranlar deitirmeyin


lelerin davranlarn bizim belirleyebiliyor olmamz, bize ou ile iin istediimiz eyi yapma serbestisi verir. rnein yukardaki ilevin ieriini sre ekleyecek ekilde deil, tam tersine sre azaltacak ekilde de yazabilirdik. Oysa kodu okuyanlar += ilecini grdklerinde doal olarak deerin artmas gibi bir davran bekleyeceklerdir. leleri doal davranlar dnda yazdnzda bunun herkesi yanltacan ve programda hatalara neden olacan aklnzda bulundurun.

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

279

le Ykleme

54.2

Tekli ile
Tek nesneyle ileyen ilelere tekli ile denir: ++arlk; ++ ileci tekli iletir nk iini yaparken yalnzca arlk ' kullanr ve onun deerini bir arttrr.

54.3

kili ile
ki nesne kullanan ilelere ikili ile denir: toplamArlk = kutuArl + ikolataArl; Yukardaki ifadede iki farkl ikili ile gryoruz: + ileci, kendisinin solundaki ve sandaki deerleri toplar; = ileci de sandakinin deerini solundakine atar. Bu derste yklenebilen btn ilelerin rneklerini vermeyeceim; yalnzca ok kullanldn dndm ileleri anlatacam. Zaten bu ilelerin bazlarn yklemeye belki de hi ihtiyacnz olmayacak.

54.4

Yklenebilen tekli ileler


Bu ileler opUnary ye ilev ismiyle tanmlanrlar; parametre almazlar nk yalnzca ilecin kullanld nesneyi etkilerler. lev tanmnda kullanlmas gereken ile dizgileri unlardr: le -nesne +nesne ~nesne *nesne ++nesne --nesne Anlam ters iaretlisini ret ayn iaretlisini ret bit dzeyinde tersini al gsterdiine eri bir arttr bir azalt le Dizgisi "-" "+" "~" "*" "++" "--"

rnein ++ ilecini Sre iin yle tanmlayabiliriz: struct Sre { int dakika; void opUnary(string ile)() if (ile == "++") { ++dakika; }

Sre nesneleri bu sayede artk ++ ileci ile arttrlabilirler: auto sre = Sre(20); ++sre;

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

280

le Ykleme

nceki deerli arttrma ve nceki deerli azaltma ileleri olan nesne++ ve nesne-kullanmlar yklenemez. O kullanmlardaki nceki deerleri derleyici otomatik olarak halleder. rnein nesne++ kullanmn edeeri udur: /* nceki deer derleyici tarafndan otomatik olarak * kopyalanr: */ Sre __ncekiDeer__ = sre; /* Tanmlanm olan normal ++ ileci arlr: */ ++sre; /* Daha sonra btn ifadede __ncekiDeer__ kullanlr. */

54.5

Yklenebilen ikili ileler


Aadaki tabloda gsterilen deimeli kavram, ilecin sol tarafndaki ile sa tarafndakinin yer deitirmesi durumunda sonucun ayn olup olmadn gsterir. rnein + ileci deimeli bir iletir, nk x+y ile y+x ilemlerinin sonular ayndr. te yandan, - deimeli bir ile deildir, nk x-y ve y-x ilemlerinin sonular farkldr. leleri gruplandrmak iin aadaki tabloda ilelerin trlerini de belirttim: a: aritmetik ile b: bit dzeyinde ilem ileci m: mantksal ile; bool dndrr s: sralama ileci; eksi deer, sfr veya art deer dndrr =: atamal ile; sol taraf deitirir

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

281

le Ykleme

le + * / % ^^ & | ^ << >> >>> ~ == != < <= > >= = += -= *= /= %= ^^= &= |= ^= <<= >>= >>>= ~= in

Anlam topla kar arp bl kalann hesapla ssn al bit ilemi ve bit ilemi veya bit ilemi ya da sola kaydr saa kaydr iaretsiz saa kaydr birletir eittir eit deildir ncedir sonra deildir sonradr nce deildir ata arttr azalt katn ata blmn ata kalann ata ssn ata & sonucunu ata | sonucunu ata ^ sonucunu ata << sonucunu ata >> sonucunu ata >>> sonucunu ata sonuna ekle iinde mi?

Deimeli (Komtatif) evet hayr evet hayr hayr hayr evet evet evet hayr hayr hayr hayr evet evet hayr hayr hayr hayr hayr hayr hayr hayr hayr hayr hayr hayr hayr hayr hayr hayr hayr hayr hayr

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 iin lev smi opBinaryRight opBinaryRight opBinaryRight opBinaryRight opBinaryRight opBinaryRight opBinaryRight opBinaryRight opBinaryRight opBinaryRight opBinaryRight opBinaryRight opBinaryRight opBinaryRight

Tr a a a a a a b b b b b b m m s s s s = = = = = = = = = = = = = = m

Tabloda sa taraf iin olarak belirtilen ilev isimleri, nesne ilecin sa tarafnda da kullanlabildiinde iletilir. Kodda yle bir ikili ile kullanldn dnelim: x ile y Derleyici hangi ye ilevi ileteceine karar vermek iin yukardaki ifadeyi u iki ye ilev arsna dntrr: x.opBinary!"ile"(y); y.opBinaryRight!"ile"(x);

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

282

le Ykleme

Eer o ilevlerden birisi tanmlanmsa, o iletilir. Deilse, ve ile deimeli bir ilese, ayrca u ilevleri de dener: x.opBinaryRight!"ile"(y); y.opBinary!"ile"(x); Normalde bu kadar karmak kurallar bilmek zorunda kalmayz. ou durumda bu ilevlerden ismi Right ile sonlananlarn hi tanmlanmasna gerek yoktur. (Bu durum in ilecinde tam tersidir: Onun iin ounlukla opBinaryRight tanmlanr.) Aadaki rneklerde ye ilevleri tanmlarken parametre ismini sadaki olarak setim. Bununla parametrenin ilecin sandaki nesne olduunu vurguluyorum: x ile y O ifade kullanldnda, ye ilevin sadaki ismindeki parametresi y olacaktr.

54.6

Aritmetik ileler
Kendi trlerimizin aritmetik ilemlerde nasl kullanldklarn belirler. rnein Sre nesnelerini birbirleriyle toplamak iin opBinary ilecini "+" ileci iin yle ykleyebiliriz: struct Sre { int dakika; Sre opBinary(string ile)(in Sre sadaki) const if (ile == "+") { return Sre(dakika + sadaki.dakika); }

O tanmdan sonra programlarmzda artk Sre nesnelerini + ileciyle toplayabiliriz: auto gitmeSresi = Sre(10); auto dnmeSresi = Sre(11); Sre toplamSre; // ... toplamSre = gitmeSresi + dnmeSresi; Derleyici o ifadeyi dntrr ve perde arkasnda gitmeSresi nesnesi zerinde bir ye ilev olarak arr: toplamSre = gitmeSresi.opBinary!"+"(dnmeSresi);

54.7

Eitlik karlatrmalar iin opEquals


== ve != ilelerinin davrann belirler. Mantksal ifadelerde kullanld iin dn tr bool 'dur. opEquals ye ilevi bu ilelerin ikisini de karlar. O ilevi nesnelerin eitlii iin tanmlaynca, derleyici != ileci iin onun tersini kullanr: x == y; x.opEquals(y);

// sttekinin edeeri

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

283

le Ykleme

x != y; !(x.opEquals(y));

// sttekinin edeeri

Normalde opEquals ilevini yaplar iin tanmlamaya gerek yoktur; derleyici btn yelerin eitliklerini srayla otomatik olarak karlatrr ve nesnelerin eit olup olmadklarna yle karar verir. Bazen nesnelerin eitliklerini kendimiz belirlemek isteyebiliriz. rnein baz yeler nesnenin eit kabul edilmesi iin nemsiz olabilirler, bir trn nesnelerinin eit kabul edilmeleri zel bir kurala bal olabilir, vs. Bunu gstermek iin GnnSaati snf iin dakika bilgisini gzard eden bir opEquals tanmlayalm: struct GnnSaati { int saat; int dakika; bool opEquals(const ref GnnSaati sadaki) const { return saat == sadaki.saat; }

} // ... assert(GnnSaati(20, 10) == GnnSaati(20, 59));

Eitlik karlatrmasnda yalnzca saat bilgisine bakld iin 20:10 ve 20:59 zamanlar eit kmaktadr. (Not: Bunun karklk douraca aktr; rnek olsun diye ylesine tanmladm.)

54.8

Sra karlatrmalar iin opCmp


Sralama ileleri nesnelerin ncelik/sonralk ilikilerini belirler. Sralama ile ilgili olan < , <= , > , ve >= ilelerinin hepsi opCmp ye ilevi tarafndan karlanr. Bu drt ileten birisinin u ekilde kullanldn kabul edelim: if (x ile y) { Derleyici o ifadeyi aadakine dntrr ve o mantksal ifadenin sonucunu kullanr: if (x.opCmp(y) ile 0) { rnek olarak, if (x <= y) { ifadesi una dntrlr: if (x.opCmp(y) <= 0) { Kendi yazdmz bu ilevin bu kurala gre doru alabilmesi iin ilevin u deerleri dndrmesi gerekir: soldaki nesne nce olduunda eksi bir deer sadaki nesne nce olduunda art bir deer ikisi eit olduklarnda sfr deeri

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

284

le Ykleme

Bundan anlalaca gibi, opCmp 'n dn tr bool deil, int 'tir. GnnSaati nesnelerinin sralama ilikilerini ncelikle saat deerine, saatleri eit olduunda da dakika deerlerine bakacak ekilde yle belirleyebiliriz: int opCmp(const ref GnnSaati sadaki) const { return (saat == sadaki.saat ? dakika - sadaki.dakika : saat - sadaki.saat); } Saat deerleri ayn olduunda dakika deerlerinin fark, saatler farkl olduunda da saatlerin fark dndrlyor. Bu tanm, zaman sralamasnda soldaki nesne nce olduunda eksi bir deer, sadaki nesne nce olduunda art bir deer dndrr. opCmp 'un tanmlanm olmas, bu trn sralama algoritmalaryla da kullanlabilmesini salar. Sralama algoritmalarnn bir rneini dizilerin .sort niteliinde grmtk. Aadaki program 10 adet rasgele zaman deeri oluturur ve onlar dizinin .sort nitelii ile sralar. Sralamay belirlerken nesneler karlatrlrken hep opCmp iletilir: import std.random; import std.stdio; import std.string; struct GnnSaati { int saat; int dakika; int opCmp(const ref GnnSaati sadaki) const { return (saat == sadaki.saat ? dakika - sadaki.dakika : saat - sadaki.saat); } string toString() { return format("%02s:%02s", saat, dakika); }

void main() { GnnSaati[] zamanlar; foreach (i; 0 .. 10) { zamanlar ~= GnnSaati(uniform(0, 24), uniform(0, 60)); } zamanlar.sort; } writeln(zamanlar);

Dizinin .sort nitelii bizim tanmladmz sralama ilikisini kullanr. Beklendii gibi, ktdaki saat deerleri doru sradadr: [03:40,04:10,09:06,10:03,10:09,11:04,13:42,16:40,18:03,21:08]

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

285

le Ykleme

54.9

lev gibi armak iin opCall


lev arrken kullanlan parantezler de iletir. Bu ilecin davran da opCall ye ilevi tarafndan belirlenir. Bu ile sayesinde trn nesnelerini de ilev gibi kullanabiliriz: BirTr nesne; nesne(); O kodda nesne bir ilev gibi arlmaktadr. Bunun bir rnei olarak bir dorusal denklemde, verilen x deerlerine karlk y deerlerini hesaplayan bir yap dnelim: y = ax + b O hesaptaki a ve b'yi arpan ve eklenen isimli yeler olarak dnrsek, y deerlerini opCall ilevi iinde yle hesaplayabiliriz: struct DorusalDenklem { double arpan; double eklenen; double opCall(double x) { return arpan * x + eklenen; }

Bir kere bunu tanmladktan sonra, o snfn nesnelerini bir ilev gibi kullanarak verilen x deerlerine karlk olan y deerlerini hesaplatabiliriz: DorusalDenklem denklem = { 1.2, 3.4 }; double y = denklem(5.6); // nesne ilev gibi kullanlyor Not: opCall ilevi tanmlanm olan yaplar Tr(parametreler) yazmyla kuramayz; nk o yazm da bir opCall ars olarak kabul edilir. opCall 'u tanmlanm olan yaplarn nesnelerini { } yazmyla kurmamz gerekir. lk satrda nesne kurulurken denklemin arpan olarak 1.2, ekleneni olarak da 3.4 deerinin kullanlaca belirleniyor. Bunun sonucunda denklem nesnesi, y = 1.2x + 3.4 denklemini ifade etmeye balar. Ondan sonra nesneyi artk bir ilev gibi kullanarak, x deerlerini parametre olarak gnderiyor ve dn deeri olarak y deerlerini elde ediyoruz. Bunun nemi, arpan ve eklenen deerlerin batan bir kere belirlenebilmesidir. Nesne o bilgiyi kendi iinde barndrr ve ilev gibi kullanld zaman kullanr. Baka arpan ve eklenen deerleri ile kurulan bir nesneyi bu sefer de bir dng iinde kullanan bir rnek: DorusalDenklem denklem = { 0.01, 0.4 }; for (double x = 0.0; x <= 1.0; x += 0.125) { writefln("%f: %f", x, denklem(x)); } O da y = 0.01x + 0.4 denklemini x'in 0.0 ile 1.0 aralndaki 0.125'in kat olan deerleri iin hesaplar.

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

286

le Ykleme

54.10

Dizi eriim ileleri opIndex , opIndexAssign , opIndexUnary ve opIndexOpAssign


Bu ileler, nesneyi nesne[indeks] eklinde dizi gibi kullanma olana verirler. opIndex eriim amacyla kullanlr. Keli parantezler iinde kullanlan deerler ilevin parametreleri haline gelirler: birNot = btnNotlar[3, 1]; // eriim birNot = btnNotlar.opIndex(3, 1); // sttekinin edeeri opIndexUnary , opUnary 'nin edeeridir; fark, ilemin belirtilen indeksteki eleman zerinde ileyecek olmasdr: ++btnNotlar[4, 0]; // arttrma btnNotlar.opIndexUnary!"++"(4, 0);// sttekinin edeeri opIndexAssign atama amacyla kullanlabilir. lk parametresi atanan deer, sonraki parametreleri de keli parantezler iinde kullanlan deerlerdir: btnNotlar[1, 1] = 95; // atama btnNotlar.opIndexAssign(95, 1, 1);// sttekinin edeeri opIndexOpAssign , opOpAssign 'n edeeridir; fark, atamal ilemin belirtilen indeksteki eleman zerinde ileyecek olmasdr: btnNotlar[2, 1] += 42; // atamal arttrma btnNotlar.opIndexOpAssign!"+"(42, 2, 1); // sttekinin edeeri Btn bu ilelerle kullanlan bir rnek olarak, btn rencilerin btn notlarn ieren bir yapy yle tanmlayabiliriz: import std.stdio; struct BtnrencilerinNotlar { // 5 renci iin 2 not int[2][5] notlar; // Belirtilen notu dndrr int opIndex(int rencindeksi, int notndeksi) { return notlar[rencindeksi][notndeksi]; } // Belirtilen notu bir arttrr void opIndexUnary(string ile)(int rencindeksi, int notndeksi) if (ile == "++") { ++notlar[rencindeksi][notndeksi]; } // Belirtilen notu belirtilen rencinin notuna atar int opIndexAssign(int not, int rencindeksi, int notndeksi) { return notlar[rencindeksi][notndeksi] = not; }

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

287

le Ykleme

// Belirtilen rencinin notunu arttrr void opIndexOpAssign(string ile)(int art, int rencindeksi, int notndeksi) if (ile == "+") { notlar[rencindeksi][notndeksi] += art; }

void main() { BtnrencilerinNotlar btnNotlar; int birNot = btnNotlar[3, 1]; ++btnNotlar[4, 0]; btnNotlar[1, 1] = 95; btnNotlar[2, 1] += 42; } writeln(btnNotlar.notlar); // // // // eriim arttrma atama atamal arttrma

Eriim satrn yalnzca gstermek amacyla kullandm; programda bir amac bulunmuyor. Dier satrlar ise belirtilen rencilerin belirtilen notlarn etkilemi oluyor: [[0, 0], [0, 95], [0, 42], [0, 0], [1, 0]]

54.11

Dilim ileleri opSlice , opSliceUnary , opSliceAssign , ve opSliceOpAssign


Yukardaki opIndex ilelerine ok benzerler; farklar, nesneleri dilim ileciyle kullanma olana salamalardr. Btn bu tanmlarn iki farkl kullanm vardr: keli parantezlerin iinin bo olduu kullanm ve keli parantezler iinde bir aralk belirtilen kullanm. Bu sekiz kullanmn hangi ilevler tarafndan yklendiklerini bu ilevleri tanmlamadan gsteren bir rnek: class BirTr { int opSlice(); int opSlice(int i, int j);

// nesne[] // nesne[i .. j]

kullanm kullanm

void opSliceUnary(string ile)() if (ile == "++"); // ++nesne[] kullanm void opSliceUnary(string ile)(int i, int j) if (ile == "++"); // ++nesne[i .. j] kullanm int opSliceAssign(int deer); // nesne[] = deer kullanm int opSliceAssign(int deer, int i, int j); // nesne[i .. j] = deer kullanm void opSliceOpAssign(string ile)(int deer) if (ile == "+"); // nesne[] += deer kullanm void opSliceOpAssign(string ile)(int deer, int i, int j) if (ile == "+"); // nesne[i .. j] += deer kullanm

void main() { BirTr nesne;

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

288

le Ykleme

int i; int deer; i = nesne[]; i = nesne.opSlice(); i = nesne[3 .. 4]; i = nesne.opSlice(3, 4); ++nesne[]; nesne.opSliceUnary!"++"(); ++nesne[3 .. 4]; nesne.opSliceUnary!"++"(3, 4); nesne[] = deer; nesne.opSliceAssign(deer); nesne[3 .. 4] = deer; nesne.opSliceAssign(deer, 3, 4); nesne[] += deer; nesne.opSliceOpAssign!"+"(deer); // sttekinin edeeri // sttekinin edeeri // sttekinin edeeri // sttekinin edeeri // sttekinin edeeri // sttekinin edeeri // sttekinin edeeri

nesne[3 .. 4] += deer; nesne.opSliceOpAssign!"+"(deer, 3, 4); // sttekinin edeeri

54.12

Tr dnm ileci opCast


Elle aka yaplan tr dnmn belirleyen opCast , dntrlecek her tr iin ayr ayr yklenebilir ve o tr opCast 'in dn tr olarak yazlr. Bu ile de ablon olarak tanmlanr ama kalb farkldr: hangi dnmn tanmlanmakta olduu (Tr : dntrlecek_tr) sz dizimiyle belirtilir: dntrlecek_tr opCast(Tr : dntrlecek_tr)() { // ... } Yine imdilik bir kalp olarak kabul etmenizi isteyeceim bu sz dizimini de daha sonra ablonlar dersinde anlatacam. Sre 'nin saat ve dakikadan oluan bir tr olduunu kabul edelim. Bu trn nesnelerini double trne dntren ilev yle tanmlanabilir: import std.stdio; struct Sre { int saat; int dakika; double opCast(Tr : double)() { return saat + (cast(double)dakika / 60); }

void main() { auto sre = Sre(2, 30); double kesirli = cast(double)sre;

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

289

le Ykleme

writeln(kesirli);

Aka tr dnm istenen satrda derleyici ye ilevi yle arr: double kesirli = sre.opCast!double(); double trne dntren ile, iki saat otuz dakikaya karlk 2.5 deerini retmektedir: 2.5

54.13

Sevk ileci opDispatch


Nesnenin var olmayan bir yesine eriildiinde arlacak olan ye ilevdir. Var olmayan yelere eriim, bu ilece sevk edilir. Var olmayan yenin ismi, opDispatch 'e bir ablon parametresi olarak gelir. (Not: ablonlar daha sonraki bir derste anlatacam.) Bu ileci ok basit olarak gsteren bir rnek: import std.stdio; struct BirTr { void opDispatch(string isim, T)(T parametre) { writefln("BirTr.opDispatch - isim: %s, deer: %s", isim, parametre); } } void main() { BirTr nesne; nesne.varOlmayanlev(42); nesne.varOlmayanBakalev(100); } Var olmayan yelerine eritiimiz halde derleme hatas almayz. O arlar, opDispatch ilevinin arlmasn salarlar. Birinci ablon parametresi ilevin ismidir. arlan noktada kullanlan parametreler de opDispatch 'in parametreleri haline gelirler: BirTr.opDispatch - isim: varOlmayanlev, deer: 42 BirTr.opDispatch - isim: varOlmayanBakalev, deer: 100

54.14

erme sorgusu iin opBinary!"in"


Eleme tablolarndan tandmz in ilecini nesneler iin de tanmlama olana salar. Dier ilelerden farkl olarak, bu ilete nesnenin sada yazld durum daha doaldr: if (zaman in leTatili) { O yzden bu ile iin daha ok opBinaryRight!"in" yklenir ve derleyici de perde arkasnda o ye ilevi arr:

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

290

le Ykleme

// sttekinin edeeri if (leTatili.opBinaryRight!"in"(zaman)) { Bu ileci aadaki rnekte kullanyorum.

54.15

rnek
Bu rnek, daha nce grdmz Sre ve GnnSaati yaplarna ek olarak bir ZamanAral yaps tanmlyor. Bu yap iin tanmlanan in ileci, belirli bir zamann belirli bir aralkta olup olmadn belirtiyor. Bu rnekte de yalnzca gerektii kadar ye ilev kullandm. GnnSaati nesnesinin for dngsnde nasl temel trler kadar rahat kullanldna zellikle dikkat edin. O dng, ile yklemenin yararn gzelce gsteriyor. import std.stdio; import std.string; struct Sre { int dakika; } struct GnnSaati { int saat; int dakika; void opOpAssign(string ile)(in Sre sre) if (ile == "+") { dakika += sre.dakika; saat += dakika / 60; dakika %= 60; saat %= 24;

int opCmp(const ref GnnSaati sadaki) const { return (saat == sadaki.saat ? dakika - sadaki.dakika : saat - sadaki.saat); } string toString() { return format("%02s:%02s", saat, dakika); }

struct ZamanAral { GnnSaati ba; GnnSaati son;

// araln dnda kabul edilir

bool opBinaryRight(string ile) (const ref GnnSaati zaman) const if (ile == "in") { return (zaman >= ba) && (zaman < son); }

void main()

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

291

le Ykleme

auto leTatili = ZamanAral(GnnSaati(12, 00), GnnSaati(13, 00)); for (auto zaman = GnnSaati(11, 30); zaman < GnnSaati(13, 30); zaman += Sre(15)) { if (zaman in leTatili) { writeln(zaman, " le tatilinde"); } else { writeln(zaman, " le tatili dnda"); }

Programn kts: 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 dnda tatili dnda tatilinde tatilinde tatilinde tatilinde tatili dnda tatili dnda

54.16

Problemler
1. GnnSaati yapsnn opCmp ilevini zamanlarn ters srada sralanmalarn salayacak ekilde deitirin. 2. Yukarda kullanlan ZamanAral yaps iin >>= ve <<= ilelerini tanmlayn. Normalde bit ilemleri iin kullanlan bu ileler, zaman araln belirli bir Sre kadar saa ve sola kaydrsnlar: aralk >>= Sre(10); // btn aralk 10 dakika // sonrasna kaysn

<<= ileci iin ye levler dersinin zmlerinde grdmz GnnSaati.azalt ilevinden yararlanmak isteyebilirsiniz. Onun ismini deitirerek GnnSaati nesneleri iin -= ileci olarak kullanabilirsiniz. 3. Bu kullanm pheli olsa da, ~ ilecini ZamanAral ve GnnSaati nesnelerinin aadaki iki kullanmlar iin ykleyin: ZamanAral aralk; GnnSaati zaman; // ... yeniAralk = aralk ~ zaman; eklindeki kullanmda yeni araln sonu zaman 'a eit olsun, ve yeniAralk = zaman ~ aralk; eklindeki kullanmda da yeni araln ba zaman 'a eit olsun. ... zmler

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

292

Snflar

55

Snflar
Snflar, kullanc trleri tanmlamaya yarayan bir baka olanaktr. D'nin nesneye ynelik programlama olanaklar snflar yoluyla gerekletirilir. Nesneye ynelik programlamay temel kavram zerinde dnebiliriz: sarma: yelere eriimin kstlanmas (aslnda yaplarda da bulunan bu olana genelde yaplarn kullanm amalarnn dnda kald iin gstermedim) kaltm: baka bir trn yelerini ve ye ilevlerini kendisininmi gibi edinmek ok ekillilik: birbirlerine yakn trlerin daha genel ortak bir tr gibi kullanlabilmeleri Sarma, daha sonra greceimiz eriim haklar ile salanr. Kaltm, gerekletirme tremesidir. ok ekillilik ise arayz tremesi yoluyla gerekletirilir. Bu derste snflar genel olarak tantacam ve zellikle referans tr olduklarna dikkat ekeceim. Snflarn dier olanaklarn daha sonraki derslere brakacam.

55.1

Yaplarla karlatrlmas
Snflar yaplara temelde ok benzerler. Bu yzden daha nce u derslerde yaplar zerinde grdmz hemen hemen her konu snflar iin de geerlidir: Yaplar ye levler const ref Parametreler ve const ye levler Kurucu ve Dier zel levler le Ykleme

Snflar yaplardan ayran nemli farklar da vardr. Bu farklar aadaki blmlerde anlatyorum.
55.1.1

Referans trleridir
Snflarn yaplardan farkl olmalarnn en byk nedeni, yaplarn deer tr olmalarna karn snflarn referans tr olmalardr. Aadaki farkllklarn byk bir ounluu, snflarn bu zelliinden kaynaklanr.

55.1.2

new ile kurulurlar


Snf deikenlerinin kendileri deer tamadklar iin, asl nesne new anahtar szc ile oluturulur. Ayn nedenden, null ve is dersinde de gsterildii gibi, snf deikenleri null da olabilirler. Yani, "hibir nesneye eriim salamyor" olabilirler. Hatrlayacanz gibi; bir deikenin null olup olmadn == ileciyle deil, is ileciyle denetlememiz gerekir: BirSnf eriimSalayan = new BirSnf; BirSnf deiken; // eriim salamayan assert(deiken is null); Bunun nedeni == ilecinin nesnenin yelerini de kullanmasnn gerekebileceidir. O ye eriimi, deikenin null olduu durumda programn bir bellek hatas ile sonlanmasna neden olur. O yzden snf deikenlerinin is ile karlatrlmalar gerekir.

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

293

Snflar

55.1.3

Snf nesneleri ve deikenleri


Snf nesnesi ile snf deikeni farkl kavramlardr. Snf nesnesi, new anahtar szc ile oluturulan ve kendi ismi olmayan bir program yapsdr. Temsil ettii kavram gerekletiren, onun ilemlerini yapan, ve o trn davrann belirleyen hep bu snf nesnesidir. Snf nesnelerine dorudan eriemeyiz. Snf deikeni ise snf nesnesine eriim salayan bir program yapsdr. Kendisi i yapmasa da eritirdii nesnenin aracs gibi ilem grr. Daha nce Deerler ve Referanslar dersinde grdmz u koda bakalm: auto deiken1 = new BirSnf; auto deiken2 = deiken1; lk satrda sa taraftaki new , isimsiz bir BirSnf nesnesi oluturur. deiken1 ve deiken2 ise yalnzca bu isimsiz nesneye eriim salayan deikenlerdir: (isimsiz BirSnf nesnesi) deiken1 deiken2 ---+-------------------+--- ---+---+--- ---+---+--| ... | | o | | o | ---+-------------------+--- ---+-|-+--- ---+-|-+-- | | | | | +--------------------+------------+

55.1.4

Kopyalama
Deikenleri etkiler. Referans tr olduklar iin; snf deikenlerinin kopyalanarak oluturulmalar, onlarn hangi nesneye eriim salayacaklarn belirler. Bu ilem srasnda asl nesne kopyalanmaz. Bir nesne kopyalanmad iin de, yaplarda kopya sonras ilevi olarak rendiimiz this(this) ye ilevi snflarda bulunmaz. auto deiken2 = deiken1; Yukardaki kodda deiken2 , deiken1 'in kopyas olarak oluturulmaktadr. O ilem her ikisinin de ayn nesneye eriim salamalarna neden olur. Snf nesnelerinin kopyalanmalar gerektiinde bunu salayan bir ye ilev tanmlanmaldr. Bu ileve kopyala() gibi bir isim verebileceiniz gibi, dizilere benzemesi asndan dup() isminin daha uygun olduunu dnebilirsiniz. Bu ilev yeni bir nesne oluturmal ve ona eriim salayan bir deiken dndrmelidir: class Snf { Yap yapNesnesi; dchar[] dizgi; int tamsay; // ... this(Yap yapNesnesi, dchar[] dizgi, int tamsay) { this.yapNesnesi = yapNesnesi; this.dizgi = dizgi; this.tamsay = tamsay; }

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

294

Snflar

Snf dup() { return new Snf(yapNesnesi, dizgi.dup, tamsay); }

dup() iinde oluturulan yeni nesne iin yalnzca dizgi yesinin dup() ile aka kopyalandna dikkat edin. yapNesnesi ve tamsay yeleri ise deer trleri olduklarndan onlar zaten otomatik olarak kopyalanrlar. O ilevden rnein yle yararlanlabilir: auto nesne1 = new Snf(Yap(1.5), "merhaba"d.dup, 42); auto nesne2 = nesne1.dup(); Sonuta, nesne2 nesne1 'in hibir yesini paylamayan ayr bir nesnedir.
55.1.5

Atama
Deikenleri etkiler. Referans tr olduklar iin; snf deikenlerinin atanmalar, daha nce eriim saladklar nesneyi brakmalarna ve yeni bir nesneye eriim salamalarna neden olur. Eer braklan nesneye eriim salayan baka deiken yoksa, asl nesne ilerideki belirsiz bir zamanda p toplayc tarafndan sonlandrlacak demektir. auto deiken1 = new BirSnf; auto deiken2 = new BirSnf; deiken1 = deiken2; Yukardaki son atama ilemi, deiken1 'in kendi nesnesini brakmasna ve deiken2 'nin nesnesine eriim salamaya balamasna neden olur. Braklan nesne daha sonra p toplayc tarafndan sonlandrlacaktr. Atama ileminin davran snflar iin deitirilemez; yani opAssign snflarda yklenemez.

55.1.6

Tanmlama
struct yerine class anahtar szc kullanlr: class SatranTa { // ... }

55.1.7

Kurma
Kurucu ilevin ismi, yaplarda olduu gibi this 'tir. Yaplardan farkl olarak snf nesneleri {} karakterleri ile kurulamaz. class SatranTa { dchar ekil; this(dchar ekil) { this.ekil = ekil; }

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

295

Snflar

Eer snf baka bir snftan tretilmise, tretildii st snfn kurucusunu armak bu snfn grevidir. Bunu bir sonraki derste super anahtar szcn tantrken gstereceim.
55.1.8

Sonlandrma
Sonlandrc ilevin ismi yaplarda olduu gibi ~this 'tir: ~this() { // ... nesne sonlanrken gereken ilemler ... }

55.1.9

ye eriimi
Yaplarda olduu gibi, yelere nokta karakteri ile eriilir. auto ah = new SatranTa(''); writeln(ah.ekil); Her ne kadar deikenin bir yesine eriiliyor gibi yazlsa da; eriilen, asl nesnenin yesidir. Not: ye deikenlere byle dorudan eriilmesi ou durumda doru kabul edilmez. Onun yerine daha sonra anlatacam snf niteliklerinden yararlanmak daha uygundur.

55.1.10

le ykleme
Yaplardaki gibidir. Bir fark, opAssign 'n snflar iin zel olarak tanmlanamamasdr. Yukarda atama balnda anlatld gibi, snflarda atama ileminin anlam yeni nesneye eriim salamaktr; bu anlam deitirilemez.

55.1.11

ye ilevler
Yaplardaki gibidir. Bir fark, baz ilevlerin Object snfndan kaltm yoluyla hazr olarak edinilmi olmalardr. rnein toString ilevini kendimiz tanmlamam olsak bile kullanabiliriz: writefln("%s", ah); Hatrlarsanz, bir nesnenin "%s" dzeniyle yazdrlmas, onun toString ye ilevini aryordu. Yukardaki SatranTa snfnda byle bir ilev tanml olmad halde o satr derlenir ve bir kt retir: deneme.SatranTa Hemen hemen btn durumlarda kullansz olan bu ktnn override szc ile nasl deitirildiini bir sonraki derste gstereceim.

55.1.12

is ve !is ileleri
Snf deikenleri zerinde iler. is ileci, snf deikenlerinin ayn nesneye eriim salayp salamadklarn bildirir. ki deiken de ayn nesneye eriim salyorlarsa true , deilse false deerini retir. !is ileci

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

296

Snflar

de bunun tersi olarak iler: Ayn nesneye eriim salyorlarsa false , deilse true deerini retir. auto benimah = new SatranTa(''); auto seninah = new SatranTa(''); assert(benimah !is seninah); Yukardaki koddaki benimah ve seninah deikenleri new ile oluturulmu olan iki farkl nesneye eriim saladklar iin !is 'in sonucu true 'dur. Bu iki nesnenin ayn ekilde kurulmu olmalar, yani ikisinin ekil yelerinin de '' olmas bunu deitirmez; nesneler birbirlerinden ayr iki nesnedir. ki deikenin ayn nesneye eriim saladklar durumda ise is ileci true retir: auto benimah2 = benimah; assert(benimah2 is benimah); Yukardaki atama ilemi sonucunda iki deiken de ayn nesneye eriim salamaya balarlar. is ileci bu durumda true retir.

55.2

zet
Snflarn yaplarla ok sayda ortak yanlar olduu kadar byk farklar da vardr. Snflar referans trleridir; new ile isimsiz bir snf nesnesi kurulur; dndrlen, o nesneye eriim salayan bir snf deikenidir. Hibir nesneye eriim salamayan snf deikenlerinin deeri null 'dr; bu durum is veya !is ile denetlenir (== veya != ile deil). Kopyalama normalde deikeni kopyalar; nesnenin kopyalanabilmesi iin dup() gibi bir ye ilev yazlmas gerekir. Atama, deikenin baka bir nesneyi gstermesini salar; bu davran deitirilemez.

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

297

Treme

56

Treme
Daha genel bir trn daha zel bir alt trn tanmlamaya tretme denir. Tretilen alt tr; genel trn yelerini edinir, onun gibi davranr, ve onun yerine geebilir. D'de treme yalnzca snflar arasnda geerlidir. Yeni bir snf, mevcut baka bir snftan tretilerek tanmlanabilir. Bir snfn tretildii tre st snf, ondan tretilen yeni snfa da alt snf ad verilir. st snfn zelliklerinin alt snf tarafndan edinilmesine kaltm denir. D'de iki tr treme vardr. Bu derste gerekletirme tremesi olan class 'tan tremeyi gstereceim; arayz tremesi olan interface 'ten tremeyi ise daha sonraki bir derse brakacam. Snfn hangi snftan tretildii, tanmlanrken isminden sonra yazlan : karakterinden sonra belirtilir: class AltSnf : stSnf { // ... } Masa saati kavramn temsil eden bir snf olduunu varsayalm: class Saat { int saat; int dakika; int saniye; void ayarla(int { this.saat = this.dakika this.saniye } saat, int dakika, int saniye = 0) saat; = dakika; = saniye;

Bu snfn yelerinin, nesne oluturulduu an zel deerler almalarnn art olmadn varsayalm. O yzden bu snfn kurucu ilevine gerek yok. Saat, daha sonraki bir zamanda ayarla ye ilevi ile ayarlanabiliyor; ve varsaylan deeri belirtilmi olduu iin de saniye deerini vermek istee bal: auto masaSaati = new Saat; masaSaati.ayarla(20, 30); writefln( "%02s:%02s:%02s", masaSaati.saat, masaSaati.dakika, masaSaati.saniye); Not: Zaman bilgisini toString ye ilevi ile yazdrmak ok daha uygun olurdu. O ilevi biraz aada override anahtar szcn tanrken ekleyeceiz. Yukardaki kodun kts: 20:30:00 Bu kadarna bakarak Saat snfnn bir yap olarak da tanmlanabileceini dnebiliriz. Bu yeyi bir yap olarak da bir araya getirebilirdik, ve o yap iin de ye ilevler tanmlayabilirdik. Programa bal olarak, bu kadar yeterli de olabilirdi. Oysa Saat 'in snf olmas, bize ondan yararlanarak yeni trler tanmlama olana sunar.

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

298

Treme

rnein, temelde bu Saat snfnn olanaklarn olduu gibi ieren, ve ek olarak alarm bilgisi de tayan bir alarSaat snf dnebiliriz. Bu snf tek bana tanmlamak istesek; Saat 'in mevcut yesinin aynlarna ek olarak iki tane de alarm yesi, ve saati ayarlamak iin kullanlan ayarla ilevinin yannda da bir alarmKur ilevi gerekirdi. Bu snf, bu anlatma uygun olarak yle gerekletirilebilir: class alarSaat { int saat; int dakika; int saniye; int alarmSaati; int alarmDakikas; void ayarla(int { this.saat = this.dakika this.saniye } saat, int dakika, int saniye = 0) saat; = dakika; = saniye;

void alarmKur(int saat, int dakika) { alarmSaati = saat; alarmDakikas = dakika; }

Saat snfnda da bulunan yelerini sar ile gsterdim. Grld gibi; Saat ve alarSaat snflarn ayn program iinde bu ekilde ayr ayr tanmlamak olduka fazla kod tekrarna neden olur. te class 'tan tretmek, bir snfn yelerinin baka bir snf tarafndan olduklar gibi edinilmelerini salar. alarSaat 'i Saat 'ten treterek tanmlamak, yeni snf byk lde kolaylatrr ve kod tekrarn ortadan kaldrr: class alarSaat : Saat { int alarmSaati; int alarmDakikas; void alarmKur(int saat, int dakika) { alarmSaati = saat; alarmDakikas = dakika; }

alarSaat 'in Saat 'ten tretildii bu tanm ncekinin edeeridir. Bu tanmdaki sar ile iaretli blm, bir nceki tanmdaki sar ile iaretli blm yerine geer. alarSaat , Saat 'in btn ye deikenlerini ve ilevlerini kaltm yoluyla edindii iin bir Saat gibi de kullanlabilir: auto baucuSaati = new alarSaat; baucuSaati.ayarla(20, 30); baucuSaati.alarmKur(7, 0); Yeni trn Saat 'ten kaltm yoluyla edindii yeleri de kendi yeleri haline gelir, ve istendiinde dardan eriilebilir:

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

299

Treme

writefln("%02s:%02s:%02s %02s:%02s", baucuSaati.saat, baucuSaati.dakika, baucuSaati.saniye, baucuSaati.alarmSaati, baucuSaati.alarmDakikas); Bunun nedeni, Saat 'ten kaltm yoluyla edinilen yelerin alarSaat 'in paralar haline gelmi olmalardr. Her alarSaat nesnesinin artk hem kendi tanmlad alarmla ilgili yeleri, hem de kaltmla edindii saatle ilgili yeleri vardr. Yukardaki kodun kts: 20:30:00 07:00 Not: Onun yerine de, biraz aada gsterilecek olan alarSaat.toString ilevini kullanmak ok daha doru olur. Bu rnekte grld gibi, ye veya ye ilev edinmek amacyla yaplan tremeye gerekletirme tremesi ad verilir.

56.1

Uyar: "o trden" ise tretin


Gerekletirme tremesinin ye edinme ile ilgili olduunu grdk. Bu amala tretmeyi, ancak trler arasnda "bu zel tr, o genel trdendir" gibi bir iliki kurabiliyorsanz dnn. Yukardaki rnek iin byle bir ilikinin var olduunu syleyebiliriz, nk "alar saat bir saattir." Baz trler arasnda ise byle bir iliki yoktur. ou durumda trler arasnda bir ierme ilikisi vardr. rnein Saat snfna Pil de eklemek istediimizi dnelim. Pil yesini treme yoluyla edinmek uygun olmaz, nk "saat bir pildir" ifadesi doru deildir: class Saat : Pil { // ... } Yanl tasarm

Bunun nedeni; saatin bir pil olmamas, ama bir pil iermesidir. Trler arasnda byle bir ierme ilikisi bulunduunda; ierilen trn, ieren trn bir yesi olarak tanmlanmas doru olur: class Saat { Pil pil; // ... }

Doru tasarm

56.2

En fazla bir class 'tan tretilebilir


Snflar birden ok class 'tan tretilemezler. rnein "alar saat sesli bir alettir" ilikisini gerekletirmek iin alarSaat 'i bir de SesliAlet snfndan tretmek istesek, derleme hatas ile karlarz: class SesliAlet { // ... }

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

300

Treme

class alarSaat : Saat, SesliAlet { // ... }

// derleme HATASI

interface 'lerden ise istenildii kadar sayda tretilebilir. Bunu da daha sonra greceiz. te yandan, snflarn ne kadar derinlemesine tretildiklerinin bir snr yoktur: class alg { // ... } class Tellialg : alg { // ... } class Kemene : Tellialg { // ... } Yukardaki kodda Kemene Tellialg 'dan, Tellialg da alg 'dan tretilmitir. Bu tanmda Kemene , Tellialg ve alg zelden genele doru bir sradzen olutururlar.

56.3

Sradzenin gsterimi
Aralarnda treme ilikisi bulunan trlerin hepsine birden sradzen ismi verilir. Nesneye ynelik programlamada sradzenin geleneksel bir gsterimi vardr: st snflar yukarda ve alt snflar aada olacak ekilde gsterilirler. Snflar arasndaki treme ilikisi de alt snftan st snfa doru bir okla belirtilir. rnein yukardaki snf ilikisini de ieren bir sradzen yle gsterilir: alg Tellialg Nefeslialg Kemene Saz Kaval Ney Bundan sonraki gsterimlerde ok yerine, yeterince anlalr olduklarn dndm iin /, \, ve | ASCII karakterlerini kullanacam.

56.4

st snf yelerine erimek iin super anahtar szc


Alt snf iinden st snfn yelerine eriilmek istendiinde, st snf temsil etmek iin super anahtar szc kullanlr. rnein alarSaat snfnn ye ilevlerinin iindeyken, Saat 'ten edindii bir yeye super.dakika diye eriilebilir. Ama bu art deildir; nk yalnzca dakika yazldnda da st snftaki dakika anlalr: class alarSaat : Saat { // ... void biryelev()

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

301

Treme

{ }

super.dakika = 10; // Saat'ten edindii dakika deiir dakika = 10; // ... ayn ey

super 'in bu kullanm, hem st snfta hem de alt snfta ayn isimde yeler bulunduu durumlardaki karklklar gidermek iin yararldr. Bunu biraz aadaki super.sfrla() ve super.toString() kullanmlarnda greceiz.

56.5

st snf yelerini kurmak iin super anahtar szc


super anahtar szc, st snfn kurucusu anlamna da gelir. Alt snfn kurucusundan st snfn kurucusunu armak iin kullanlr. Bu kullanmda; this nasl bu snfn kurucusu ise, super de st snfn kurucusudur. Yukardaki Saat ve alarSaat snflarnn kurucularn tanmlamamtk. Bu yzden, her ikisinin yeleri de kendi .init deerleri ile ilklenirler; ve hatrlarsanz o deer int iin sfrdr. Saat 'in kurucusunu basite yle tanmlam olalm: class Saat { this(int saat, int dakika, int saniye) { this.saat = saat; this.dakika = dakika; this.saniye = saniye; } } // ...

Artk kullanclarn Saat nesnelerini bu kurucu ile kurmalar gerektiini biliyoruz: auto saat = new Saat(17, 15, 0); Bir Saat nesnesinin yle tek bana kurulmas doaldr. Ancak, kullanclarn bir alarSaat kurduklar durumda, onun tremeyle edindii Saat parasn aka kurmalar olanakszdr. Hatta kullanclar baz durumlarda alarSaat 'in bir Saat 'ten trediini bile bilmek zorunda deillerdir. Kullancnn amac yalnzca alt snf kurmak ve o tr olarak kullanmaktr: auto baucuSaati = new alarSaat(/* ... */); // ... bir alarSaat olarak kullan ... Bu yzden; kaltmla edindii st snf parasn kurmak, alt snfn grevidir. st snfn kurucusu, bu amala super yazmyla arlr: class alarSaat : Saat { this(int saat, int dakika, int saniye, // Saat iin int alarmSaati, int alarmDakikas) // alarSaat iin { super(saat, dakika, saniye); this.alarmSaati = alarmSaati; this.alarmDakikas = alarmDakikas; }

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

302

Treme

// ...

alarSaat 'in kurucusu; hem kendisi iin gereken alarmla ilgili bilgileri, hem de st snf iin gereken saat bilgilerini parametre olarak almakta, ve Saat 'in yelerini super 'i ararak kurmaktadr. Eer st snfn bir otomatik kurucusu varsa, alt snfn super 'i armas art deildir. nk o durumda st snfn yeleri varsaylan deerleriyle zaten otomatik olarak ilklenirler.

56.6

ye ilevleri override ile zel olarak tanmlamak


Tremenin nemli bir yarar, st snfta bulunan ilevlerin alt snf tarafndan zel olarak yeniden tanmlanabilmesidir. override , bu kullanmda "hkmsz klmak, bastrmak" anlamna gelir. Alt snf, st snfn ilevini kendisine uygun olacak ekilde yeniden tanmlayabilir. Saat 'in sfrla isminde bir ye ilevi olduunu dnelim. Bu ilev btn yelerin deerlerini sfrlyor olsun: class Saat { void sfrla() { saat = 0; dakika = 0; saniye = 0; } } // ...

Hatrlayacanz gibi, ayn ilev kaltm yoluyla alarSaat tarafndan da edinilir ve onun nesneleri ile de kullanlabilir: auto baucuSaati = new alarSaat(20, 30, 0, 7, 0); // ... baucuSaati.sfrla(); Ancak, Saat 'in bu sfrla ilevinin alarmla ilgili yelerden haberi yoktur; o, yalnzca kendi snfnn yeleri ile ilgili olabilir. Bu yzden, alt snfn yelerinin de sfrlanabilmeleri iin; st snftaki sfrla ilevinin bastrlmas, ve alt snfta yeniden tanmlanmas gerekir: class alarSaat : Saat { override void sfrla() { super.sfrla(); alarmSaati = 0; alarmDakikas = 0; } } // ...

Alt snfn yalnzca kendi yelerini sfrladna, ve st snfn iini super.sfrla() ars yoluyla st snfa havale ettiine dikkat edin.

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

303

Treme

Yukardaki kodda super.sfrla() yerine yalnzca sfrla() yazamadmza da ayrca dikkat edin. Eer yazsaydk, alarSaat snf iinde bulunduumuz iin ncelikle onun ilevi anlalrd, ve iinde bulunduumuz bu ilev tekrar tekrar kendisini arrd. Sonuta da program sonsuz dngye girer, bir bellek sorunu yaar, ve kerek sonlanrd. toString 'in tanmn bu noktaya kadar geciktirmemin nedeni, her snfn bir sonraki derste anlatacam Object isminde bir snftan otomatik olarak tremi olmas ve Object 'in zaten bir toString ilevi tanmlam olmasdr. Bu yzden, bir snfn toString ilevinin tanmlanabilmesi iin override anahtar szcnn de kullanlmas 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 ilevinin Saat 'in ilevini super.toString() diye ardna dikkat edin. Artk alarSaat nesnelerini de dizgi olarak ifade edebiliriz: void main() { auto masaSaatim = new alarSaat(10, 15, 0, 6, 45); writeln(masaSaatim); } kts: 10:15:00 06:45

56.7

Alt snf nesnesi, st snf nesnesi yerine geebilir


st snf daha genel, ve alt snf daha zel olduu iin; alt snf nesneleri st snf nesneleri yerine geebilirler. Buna ok ekillilik denir. Bu genellik ve zellik ilikisini "bu tr o trdendir" gibi ifadelerde grebiliriz: "alar saat bir saattir", "renci bir insandr", "kedi bir omurgal hayvandr", vs. Bu ifadelere uygun olarak; saat gereken yerde alar saat, insan gereken yerde renci, omurgal hayvan gereken yerde de kedi kullanlabilir. st snfn yerine kullanlan alt snf nesneleri kendi trlerini kaybetmezler. Nasl normal hayatta bir alar saatin bir saat olarak kullanlmas onun aslnda bir alar saat olduu gereini deitirmiyorsa, tremede de deitirmez. Alt snf kendisi gibi davranmaya devam eder.

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

304

Treme

Elimizde Saat nesneleri ile alabilen bir ilev olsun. Bu ilev, kendi ilemleri srasnda bu verilen saati de sfrlyor olsun: void kullan(Saat saat) { // ... saat.sfrla(); // ... } ok ekilliliin yarar byle durumlarda ortaya kar. Yukardaki ilev Saat trnden bir parametre bekledii halde, onu bir alarSaat nesnesi ile de arabiliriz: auto masaSaatim = new alarSaat(10, 15, 0, 6, 45); writeln("nce : ", masaSaatim); kullan(masaSaatim); writeln("sonra: ", masaSaatim); kullan ilevi, masaSaatim nesnesini bir alarSaat olmasna ramen kabul eder, ve bir Saat gibi kullanr. Bu, aralarndaki tremenin "alar saat bir saattir" ilikisini oluturmu olmasndandr. Sonuta, masaSaatim nesnesi sfrlanmtr: nce : 10:15:00 06:45 sonra: 00:00:00 00:00 Burada dikkatinizi ekmek istediim nemli nokta, yalnzca saat bilgilerinin deil, alarm bilgilerinin de sfrlanm olmasdr. kullan ilevinde bir Saat 'in sfrla ilevinin arlyor olmasna karn; asl nesne bir alarSaat olduu iin, o trn zel sfrla ilevi arlr ve onun tanm gerei hem saatle ilgili yeleri, hem de alarmla ilgili yeleri sfrlanr. masaSaatim 'in kullan ilevine bir Saat 'mi gibi gnderilebilmesi, onun asl trnde bir deiiklik yapmaz. Grld gibi, kullan ilevi bir Saat nesnesi kullandn dnd halde, elindeki nesnenin asl trne uygun olan sfrla ilevi arlmtr. Saat sradzenine bir snf daha ekleyelim. Sfrlanmaya alldnda yelerinin rasgele deerler ald BozukSaat snf: import std.random; class BozukSaat : Saat { this() { super(0, 0, 0); } override void sfrla() { saat = uniform(0, 24); dakika = uniform(0, 60); saniye = uniform(0, 60); }

O trn parametre kullanmadan kurulabilmesi iin parametresiz bir kurucu ilev tanmladna da dikkat edin. O kurucunun tek yapt, kendi sorumluunda bulunan st snfn kurmaktr.

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

305

Treme

kullan ilevine bu trden bir nesne gnderdiimiz durumda da bu trn zel sfrla ilevi arlr. nk bu durumda da kullan iindeki saat parametresinin asl tr BozukSaat 'tir: auto raftakiSaat = new BozukSaat; kullan(raftakiSaat); writeln(raftakiSaat); BozukSaat 'in kullan iinde sfrlanmas sonucunda oluan rasgele saat deerleri: 22:46:37

56.8

Treme geilidir
Snflarn birbirlerinin yerine gemeleri yalnzca treyen iki snfla snrl deildir. Alt snflar, st snflarnn tredikleri snflarn da yerine geerler. Yukardaki alg sradzenini hatrlayalm: class alg { // ... } class Tellialg : alg { // ... } class Kemene : Tellialg { // ... } Oradaki tremeler u iki ifadeyi gerekletirirler: "telli alg bir algdr" ve "kemene bir telli algdr". Dolaysyla, "kemene bir algdr" da doru bir ifadedir. Bu yzden, alg beklenen yerde Kemene de kullanlabilir. Gerekli trlerin ve ye ilevlerin tanmlanm olduklarn varsayarsak: void gzelal(alg alg, Para para) { alg.akordEt(); alg.al(para); } // ... auto kemenem = new Kemene; gzelal(kemenem, doalama); gzelal ilevi bir alg bekledii halde, onu bir Kemene ile arabiliyoruz; nk geili olarak "kemene bir algdr".

56.9

Sradzen daha kapsaml olabilir


Treme yalnzca iki snfla snrl deildir. Eldeki probleme bal olarak, ve her snfn tek bir class 'tan treyebilecei kuralna uyulduu srece, sradzen gerektii kadar kapsaml olabilir.

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

306

Treme

Bir rnek olarak, std.stream.File snfnn bal olduu sradzene bakalm (bu eski arayz ve snflar artk Phobos'a dahil deiller): InputStream OutputStream (interface) (interface) \ / CFile --> Stream <-- SocketStream / | | \ MemoryStream | File \ | TArrayStream FilterStream / | \ / BufferedStream \ EndianStream | SliceStream BufferedFile Doal olarak, hibir programcnn bu sradzeni ezbere bilmesi gerekmez. Yalnzca bir fikir vermesi asndan gsteriyorum. Yukardaki sradzende her snfn tek bir class 'tan trediine dikkat edin. stnde iki tr grnen tek snf Stream 'dir, nk tretildii iki tr de interface 'tir.

56.10

Soyut ye ilevler ve soyut snflar


Bazen; bir snfta bulunmasnn doal olduu, ama o snfn kendisinin tanmlayamad ilevlerle karlalabilir. Somut bir gerekletirmesi bulunmayan bu ileve bu snfn bir soyut ilevi denir. En az bir soyut ilevi bulunan snflara da soyut snf ismi verilir. rnein satran talarn ifade eden bir sradzende SatranTa snfnn tan hamlesinin yasal olup olmadn sorgulamaya yarayan yasal_m isminde bir ilevi olduunu varsayalm. Byle bir sradzende bu st snf, tan hangi karelere ilerletilebileceini bilemiyor olabilir; her tan hareketi, onunla ilgili olan alt snf tarafndan biliniyordur: piyonun hareketini Piyon snf biliyordur, ahn hareketini ah snf, vs. abstract anahtar szc, o ye ilevin bu snfta gerekletirilmediini, ve alt snflardan birisinde gerekletirilmesinin art olduunu bildirir: class SatranTa { abstract bool yasal_m(in Kare nereden, in Kare nereye); } Grld gibi; o ilev o snfta tanmlanmam, yalnzca abstract olarak bildirilmitir. Soyut snf trlerinin nesneleri oluturulamaz: auto ta = new SatranTa; // derleme HATASI

Bunun nedeni, eksik ilevi yznden bu snfn kullanlamaz durumda bulunmasdr. nk; eer oluturulabilse, ta.yasal_m(buKare, uKare) gibi bir arnn sonucunda ne yaplaca bu snf tarafndan bilinemez. te yandan, bu ilevin tanmn veren alt snflarn nesneleri oluturulabilir; nk alt snf bu ilevi kendisine gre tanmlamtr ve ilev ars sonucunda ne yaplaca bylece bilinir: class Piyon : SatranTa { override bool yasal_m(in Kare nereden, in Kare nereye) { // ... ilevin piyon tarafndan gerekletirilmesi ...

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

307

Treme

return karar;

Bu ilevin tanmn da sunduu iin bu alt snftan nesneler oluturulabilir: auto ta = new Piyon; // derlenir

56.11

rnek
Bir rnek olarak demir yolunda ilerleyen aralarla ilgili bir sradzene bakalm. Bu rnekte sonuta u sradzeni gerekletirmeye alacaz: DemirYoluArac { ilerle() } / | \ \ Drezin | Vagon Lokomotif | Tren { lokomotif, vagonlar, indir()?, bindir()? } / \ YkTreni YolcuTreni { kondktrler } Her snfn kendisinin tanmlad yeleri gri renkle gsterdim. Soyut olan iki ilevi de krmz soru iaretiyle belirttim. Aada bu snflar ve yelerini en stten en alta doru teker teker gstereceim. Burada amacm yalnzca snf ve sradzen tasarmlarn gstermek olduu iin, hi ayrntya girmeyeceim ve yalnzca gerektii kadar kod yazacam. O yzden aadaki ilevlerde gerek iler yapmak yerine yalnzca ka mesaj yazdrmakla yetineceim. Yukardaki tasarmdaki en genel ara olan DemirYoluArac , yalnzca ilerleme iiyle ilgilenecek ekilde tasarlanm. Genel olarak bir demir yolu arac olarak kabul edilebilmek iin bu tasarmda bundan daha fazlas da gerekmiyor. O snf yle tanmlayabiliriz: class DemirYoluArac { void ilerle(in int kilometre) { writeln(kilometre, " kilometre ilerliyorum"); } } DemirYoluArac 'ndan tretilebilecek en basit ara, insan gcyle giden drezin olabilir. Bu snfa zel bir ye eklemeye imdilik gerek duymuyorum: class Drezin : DemirYoluArac {} Srada, en yaygn demir yolu arac olan tren var. Bir trenin lokomotifinin ve baz vagonlarnn olmas doaldr. nce bu yardmc snflar tanmlayalm: class Lokomotif : DemirYoluArac {} class Vagon : DemirYoluArac {} Treni temsil eden snf bir lokomotif ve bir dizi vagon olarak yle tanmlayabiliriz:

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

308

Treme

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

Burada ok nemli bir konuya dikkatinizi ekmek istiyorum. Her ne kadar Lokomotif ve Vagon demir yolu aralar olsalar da, trenin onlardan tretilmesi doru olmaz. Yukarda deindiimiz kural hatrlayalm: snflarn tremeleri iin, "bu zel tr, o genel trdendir" gibi bir iliki bulunmas gerekir. Oysa tren ne bir lokomotiftir, ne de vagondur. Tren, onlar ierir. Bu yzden lokomotif ve vagon kavramlarn trenin yeleri olarak tanmladk. Trenle ilgili baka bir durumu daha dnmemiz gerekiyor: Her trenin indirme ve bindirme ilemlerini desteklemesi gerekir. Kullanm amac gerei, her tren mal veya yolcu tar. Bu genel tanma uygun olarak bu snfa iki tane de ilev ekleyelim: class Tren : DemirYoluArac { Lokomotif lokomotif; Vagon[] vagonlar; abstract void indir(); abstract void bindir();

Grdnz gibi, bu ilevleri soyut olarak tanmlamak zorunda kaldk. nk trenin indirme ve bindirme ilemleri srasnda tam olarak ne yaplaca, o trenin trne baldr. Yolcu trenlerinin indirme ilemi, vagon kaplarnn almas ve yolcularn trenden kmalarn beklemek kadar basittir. Yk trenlerinde ise yk tayan grevlilere ve belki de vin gibi baz aralara gerek duyulabilir. Bu yzden, indir ve bindir ilevlerinin sradzenin bu aamasnda soyut olmalar gerekir. Soyut Tren snfnn soyut ilevlerini gerekletirmek, ondan treyen somut iki snfn grevidir: class YkTreni : Tren { override void indir() { writeln("mal boalyor"); } override void bindir() { writeln("mal ykleniyor"); }

Bu iki somut tr arasnda baka farklar da olabileceini gstermek iin YolcuTreni 'ne kondktrlerini ifade eden bir de dizi ekleyelim: class Kondktr {} class YolcuTreni : Tren { Kondktr[] kondktrler; override void indir() {

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

309

Treme

writeln("yolcular iniyor");

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

Soyut bir snf olmas, Tren 'in kullanlamayaca anlamna gelmez. Tren snfnn nesneleri oluturulamaz, ama Tren snf bir arayz olarak kullanlabilir. rnein parametre olarak Tren alan u ilev, trenlerin duraktaki ileriyle ilgileniyor olsun: void duraktaArla(Tren tren) { tren.indir(); tren.bindir(); } Yukardaki tretmeler "yk treni bir trendir" ve "yolcu treni bir trendir" ilikilerini gerekletirdikleri iin, bu iki snf Tren yerine kullanabiliyoruz. Bu programda geriye kalan, std.stdio modlnn eklenmesi ve bu snflar kullanan bir main ilevinin yazlmasdr: import std.stdio; void main() { auto bizimEkspres = new YolcuTreni; bizimEkspres.ilerle(30); duraktaArla(bizimEkspres); auto sebzeTreni = new YkTreni; sebzeTreni.ilerle(400); duraktaArla(sebzeTreni);

O kod, farkl trden iki nesne oluturur: YolcuTreni ve YkTreni . Bu iki snf, o kodda iki deiik arayzle kullanlmaktadr: 1. ilerle ilevinin arld noktalarda, bu nesneler DemirYoluArac olarak kullanlmaktadrlar; nk o ilev o snf dzeyinde bildirilmitir 2. duraktaArla ilevi, nesneleri bir Tren olarak kullanmaktadr; nk nesneler ilevin parametresine Tren olarak gnderilirler ktlarn karlatralm: 30 kilometre ilerliyorum yolcular iniyor yolcular biniyor 400 kilometre ilerliyorum mal boalyor mal ykleniyor ilerle ilevinin her ikisi iin de ayn ekilde altna dikkat edin. te yandan, duraktaArla ilevinin ard indir ve bindir ye ilevleri, her ikisinin kendi asl trlerinin tanmlad ekilde almtr.

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

310

Treme

56.12

zet
Treme, "bu tr o trdendir" ilikisi iindir Tek bir class 'tan tretilebilir super 'in iki anlam vardr: st snfn kurucusu, ve st snfa eriim override , st snfn ilevini bu snf iin zel olarak tanmlar abstract , soyut ilevin alt snfta tanmlanmasn art koar

56.13

Problemler
1. Yukardaki sradzenin en st snf olan DemirYoluArac 'n deitirelim. Ka kilometre ilerlediini bildirmek yannda, her kilometre iin bir de ses kartsn: class DemirYoluArac { void ilerle(in int kilometre) { write(kilometre, " kilometre: "); foreach (i; 0 .. kilometre) { write(ses(), ' '); } } writeln();

Ancak, ses ilevi DemirYoluArac snfnda tanmlanamasn; nk her aracn kendi zel sesi olsun: insan gcyle alan drezin iin "of puf" vagon iin "taktak tukutak" lokomotif iin "uf uf" Not: Dier snflar imdilik bir sonraki soruya brakn. Her aracn ayr sesinin olabilmesi iin, ses ilevinin st snfta soyut olarak bildirilmesi gerekir: class DemirYoluArac { // ... } abstract string ses();

ses ilevini alt snflar iin gerekletirin 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 programn aadaki kty vermesini salayn:

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

311

Treme

2 kilometre: of puf of puf 3 kilometre: taktak tukutak taktak tukutak taktak tukutak 4 kilometre: uf uf uf uf uf uf uf uf 4. ses ilevini Tren , YkTreni , ve YolcuTreni snflar iin nasl tanmlayabileceinizi dnn. Bir fikir: ses ilevini, yalnzca bunlarn en stndeki snf olan Tren 'de tanmlamak isteyebilirsiniz. Hatta, tren trnn sesinin de lokomotiften kaynaklanacan varsayarsak; bu ilevi, dorudan lokomotifin sesini dndrecek ekilde yazabilirsiniz: class Tren : DemirYoluArac { Lokomotif lokomotif; Vagon[] vagonlar; abstract void indir(); abstract void bindir(); override string ses() { return lokomotif.ses(); }

Ne yazk ki, yukardaki snf u ekilde denediinizde programn ktn greceksiniz: auto tren = new YolcuTreni; tren.ilerle(1);

Segmentation fault

Program ker

Neden? Hatay giderin ve o kodun beklenen u kty vermesini salayn: 1 kilometre: uf uf ... zmler

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

312

Object

57

Object
Aka baka bir snftan tretilmeyen snflar, otomatik olarak Object adl snftan trerler. Sradzenin en stndeki snf Object 'ten otomatik olarak trer: class alg : Object { // ... } class Tellialg : alg { // ... } // ": Object" yazlmaz; otomatiktir

// dolayl olarak Object'ten trer

En stteki snf Object 'ten tredii iin, altndaki btn snflar da dolayl olarak Object 'ten trerler. Bu anlamda "her snf, Object trndendir". Bu treme sonucunda her snf Object 'in baz ye ilevlerini edinir: toString : nesnenin dizgi olarak ifadesi opEquals : eitlik karlatrmas opCmp : sra karlatrmas toHash : eleme tablosu indeks deeri

Bu ilevlerden son snf nesnelerinin deerlerini n plana kartmak ve en azndan onlar eleme tablolarnda indeks tr olarak kullanmak iin gereklidir. Treme yoluyla edinildikleri iin, bu ilevlerin treyen tr iin override anahtar szc ile tanmlanmalar gerekir. Not: Object 'ten edinilen baka yeler de vardr; onlar burada anlatmayacam.

57.1

toString
Yaplarda olduu gibi, ve bir nceki derste de grdmz gibi, nesnenin dizgi olarak kullanlabilmesini salar: auto saat = new Saat(20, 30, 0); writeln(saat); // Object.toString'i arr Snfn Object 'ten kaltm yoluyla edindii toString ilevi fazla kullanl deildir; dndrd string yalnzca trn ismini ierir: deneme.Saat Snfn isminden nceki blm, yani yukardaki deneme , o snf ieren modln ismini belirtir. Ona bakarak, Saat snfnn deneme.d isimli bir kaynak dosya iinde tanmlandn anlayabiliriz. Daha anlaml ve daha uygun bir string retmesi iin bu ilevi hemen hemen her zaman kendimiz tanmlamak isteriz: import std.string; class Saat { override string toString() const

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

313

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 baucuSaati = new alarSaat(20, 30, 0, 7, 0); writeln(baucuSaati); Yukardaki alarSaat.toString ; kendi yelerini dorudan kullanyor ve st snfn yeleri iin st snfn toString ilevinden yararlanyor. kts: 20:30:00 07:00

57.2

opEquals
le Ykleme dersinde grdmz gibi, bu ye ilev == ilecinin tanmn belirler. levin dn deeri nesneler eitlerse true , deillerse false olmaldr. Uyar: Bu ilevin opCmp ile tutarl olmas gerekir; true dndrd durumda opCmp da sfr dndrmelidir. == ilecine karlk ncelikle drt adml bir algoritma iletilir. Programcnn tanmlam olduu opEquals ancak baz durumlarda arlr: 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 deiken de ayn nesneye eriim salyorlarsa (veya ikisi de null iseler) eittirler. Yalnzca birisi null ise eit deildirler. Her iki nesne de ayn trden iseler a.opEquals(b) iletilir. Aksi taktirde eit olarak kabul edilebilmeleri iin eer tanmlanmlarsa hem a.opEquals(b) 'nin hem de b.opEquals(a) 'nn true retmesi gerekir.

opEquals programc tarafndan zellikle tanmlanmamsa snf nesnelerinin deerlerine baklmaz, iki snf deikeninin ayn nesneye eriim salayp salamadklarna baklr: auto deiken0 = new Saat(6, 7, 8); auto deiken1 = new Saat(6, 7, 8); assert(deiken0 != deiken1); // eit deiller // (nk farkl nesneler)

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

314

Object

Yukardaki koddaki iki nesne ayn parametrelerle kurulduklar halde, new ile ayr ayr kurulmu olduklar iin iki farkl nesnedir. Bu yzden; onlara eriim salayan deiken0 ve deiken1 deikenleri, Object 'in gznde eit deillerdir. te yandan, ayn nesneye eriim saladklar iin u iki deiken eittir: auto ortak0 = new Saat(9, 10, 11); auto ortak1 = ortak0; assert(ortak0 == ortak1); // eitler // (nk ayn nesne)

Bazen nesneleri byle kimliklerine gre deil, deerlerine gre karlatrmak isteriz. rnein deiken0 'n ve deiken1 'in eriim saladklar nesnelerin deerlerinin eit olmalarna bakarak, == ilecinin true retmesini bekleyebiliriz. Yaplardan farkl olarak, ve Object 'ten kaltmla edinildii iin, opEquals ilevinin parametresi Object 'tir. O yzden, bu ilevi kendi snfmz iin tanmlarken parametresini Object olarak yazmamz gerekir: class Saat { override bool opEquals(Object o) const { // ... } } // ...

Kendimiz Object olarak dorudan kullanmayacamz iin bu parametrenin ismini kullansz olarak o diye semekte bir saknca grmyorum. lk ve ou durumda da tek iimiz, onu bir tr dnmnde kullanmak olacak. opEquals 'a parametre olarak gelen nesne, kod iinde == ilecinin sa tarafnda yazlan nesnedir. rnein u iki satr birbirinin edeeridir: deiken0 == deiken1; deiken0.opEquals(deiken1);

// ayns

Bu ileteki ama bu snftan iki nesneyi karlatrmak olduu iin, ilevi tanmlarken yaplmas gereken ilk ey, parametre olarak gelen Object 'in trn kendi snfmzn trne dntrmektir. Sadaki nesneyi deitirmek gibi bir niyetimiz de olmad iin, tr dnmnde const belirtecini kullanmak da uygun olur: override bool opEquals(Object o) const { auto sadaki = cast(const Saat)o; } // ...

Yukardaki tr dnm ilemi ya sadaki 'nin trn bu ekilde const Saat olarak belirler ya da dnm uyumsuzsa null retir. Burada karar verilmesi gereken nemli bir konu, sadaki nesnenin trnn bu nesnenin tr ile ayn olmadnda ne olacadr. Sadaki nesnenin tr dnm sonucunda null retmesi, sadaki nesnenin aslnda bu tre dntrlemedii anlamna gelir.

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

315

Object

Ben nesnelerin eit kabul edilebilmeleri iin bu dnmn baarl olmas gerektiini varsayacam. Bu yzden eitlik karlatrmalarnda ncelikle sadaki 'nin null olmadna bakacam. Zaten null olduu durumda sadaki 'nin yelerine erimek hataldr: class Saat { int saat; int dakika; int saniye; override bool opEquals(Object o) const { auto sadaki = cast(const Saat)o; return (sadaki && (saat == sadaki.saat) && (dakika == sadaki.dakika) && (saniye == sadaki.saniye));

} }

// ...

levin bu tanm sayesinde, == ileci Saat nesnelerini artk deerlerine gre karlatrr: auto deiken0 = new Saat(6, 7, 8); auto deiken1 = new Saat(6, 7, 8); assert(deiken0 == deiken1); // artk eitler // (nk deerleri ayn) opEquals ' tanmlarken, eer varsa ve nesnelerin eit kabul edilmeleri iin gerekliyse, st snfn yelerini de unutmamak gerekir. rnein alt snf olan alarSaat 'in nesnelerini karlatrrken, Saat 'ten kaltmla edindii paralar da karlatrmak anlaml olur: class alarSaat : Saat { int alarmSaati; int alarmDakikas; override bool opEquals(Object o) const { auto sadaki = cast(const alarSaat)o; return (sadaki && (super == o) && (alarmSaati == sadaki.alarmSaati) && (alarmDakikas == sadaki.alarmDakikas));

} }

// ...

Oradaki ifade super 'in opEquals ilevini arr ve eitlik kararnda onun da sonucunu kullanm olur.

57.3

opCmp
Snf nesnelerini sralamak iin kullanlr. < , <= , > , ve >= ilelerinin tanm iin perde arkasnda bu ilev kullanlr.

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

316

Object

Bu ilevin dn deerini < ileci zerinde dnebilirsiniz: Soldaki nesne nce olduunda eksi bir deer, sadaki nesne nce olduunda art bir deer, ikisi eit olduklarnda sfr dndrmelidir. Uyar: Bu ilevin opEquals ile tutarl olmas gerekir; sfr dndrd durumda opEquals da true dndrmelidir. toString 'in ve opEquals 'un aksine, bu ilevin Object snfndan kaltmla edinilen bir davran yoktur. Tanmlanmadan kullanlrsa hata atlr: auto deiken0 = new Saat(6, 7, 8); auto deiken1 = new Saat(6, 7, 8); assert(deiken0 < deiken1); // Program ker

object.Exception: need opCmp for class deneme.Saat Yukarda opEquals iin sylenenler bu ilev iin de geerlidir: Sadaki nesnenin trnn bu nesnenin trne eit olmad durumda hangisinin daha nce sralanmas gerektii konusuna bir ekilde karar vermek gerekir. Bunun en kolay bu karar derleyiciye brakmaktr, nk derleyici trler arasnda zaten genel bir sralama belirler. Trler ayn olmadklarnda bu sralamadan yararlanmann yolu, typeid 'lerinin opCmp ilevinden yararlanmaktr: class Saat { int saat; int dakika; int saniye; override int opCmp(Object o) const { /* Trler ayn olmadklarnda trlerin genel * sralamasndan yararlanyoruz. */ if (typeid(this) != typeid(o)) { return typeid(this).opCmp(typeid(o)); } auto sadaki = cast(const Saat)o; if (saat != sadaki.saat) { return saat - sadaki.saat; } else if (dakika != sadaki.dakika) { return dakika - sadaki.dakika; } else { return saniye - sadaki.saniye; }

} }

// ...

Yukardaki tanm, nesneleri sralama amacyla karlatrrken ncelikle trlerinin uyumlu olup olmadklarna bakyor. Eer uyumlu iseler saat bilgisini dikkate alyor; saatler eitlerse dakikalara, onlar da eitlerse saniyelere bakyor. Ne yazk ki, bu ilevin bu gibi karlatrmalarda daha gzel veya daha etkin bir yazm yoktur. Eer daha uygun bulursanz, if-else-if zinciri yerine onun edeeri olan l ileci de kullanabilirsiniz:

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

317

Object

override int opCmp(Object o) const { if (typeid(this) != typeid(o)) { return typeid(this).opCmp(typeid(o)); } auto sadaki = cast(const Saat)o; return (saat != sadaki.saat ? saat - sadaki.saat : (dakika != sadaki.dakika ? dakika - sadaki.dakika : saniye - sadaki.saniye));

Bu ilevi bir alt snf iin tanmlarken ve karlatrmada nemi varsa, st snfn da unutmamak gerekir. rnein alarSaat iin: override int opCmp(Object o) const { auto sadaki = cast(const alarSaat)o; const int stSonu = super.opCmp(o); if (stSonu != 0) { return stSonu; } else if (alarmSaati != sadaki.alarmSaati) { return alarmSaati - sadaki.alarmSaati; } else { return alarmDakikas - sadaki.alarmDakikas; }

st snfn sfrdan farkl bir deer dndrmesi durumunda, iki nesnenin sralar ile ilgili yeterli bilgi edinilmitir; ve o deer dndrlr. Yukardaki kodda, alt snfn yelerine ancak st snf paralar eit ktnda baklmaktadr. Artk bu trn nesneleri sralama karlatrmalarnda kullanlabilir: auto s0 = new alarSaat(8, 0, 0, 6, 30); auto s1 = new alarSaat(8, 0, 0, 6, 31); assert(s0 < s1); O kodda dier btn yeleri eit olduu iin, s0 ve s1 'in nasl sralanacaklarn en son baklan alarm dakikas belirler. Bu ilev yalnzca kendi yazdmz kodlarda kullanlmak iin deildir. Programda kullandmz ktphaneler ve dil olanaklar da bu ilevi arabilir. rnein bir dizi iindeki nesnelerin .sort ile sralanmalarnda, veya snfn bir eleme tablosunda indeks tr olarak kullanlmasnda da perde arkasnda bu ilevden yararlanlr. Yukardaki tanmlarmz denemek iin, rasgele saat deerleriyle kurulan alarSaat nesnelerini ieren bir diziyi sralayalm. Snflarn yalnzca bu programda kullanlan ilevlerini gsteriyorum: import std.string; import std.random; import std.stdio; class Saat {

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

318

Object

int saat; int dakika; int saniye; this(int saat, int dakika, int saniye) { this.saat = saat; this.dakika = dakika; this.saniye = saniye; } override string toString() const { return format("%02s:%02s:%02s", saat, dakika, saniye); } override int opCmp(Object o) const { auto sadaki = cast(const Saat)o; if (saat != sadaki.saat) { return saat - sadaki.saat; } else if (dakika != sadaki.dakika) { return dakika - sadaki.dakika; } else { return saniye - sadaki.saniye; }

class alarSaat : Saat { int alarmSaati; int alarmDakikas; this(int saat, int dakika, int saniye, int alarmSaati, int alarmDakikas) { super(saat, dakika, saniye); this.alarmSaati = alarmSaati; this.alarmDakikas = alarmDakikas; } override string toString() const { return format("%s %02s:%02s", super.toString(), alarmSaati, alarmDakikas); } override int opCmp(Object o) const { auto sadaki = cast(const alarSaat)o; const int stSonu = super.opCmp(o); if (stSonu != 0) { return stSonu; } else if (alarmSaati != sadaki.alarmSaati) { return alarmSaati - sadaki.alarmSaati; } else { return alarmDakikas - sadaki.alarmDakikas; }

void main() {

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

319

Object

alarSaat[] saatler; foreach (i; 0 .. 10) { saatler ~= new alarSaat(uniform(0, uniform(0, uniform(0, uniform(0, uniform(0, } saatler.sort; foreach (saat; saatler) { writeln(saat); } 24), 60), 60), 24), 60));

Yalnzca on nesne kullanan bu rnekte sralamann ncelikle nesnelerin saat deerleri tarafndan belirlendii grlyor: 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

ktnn yalnzca son iki nesnesinin sralanmalarnda dakika 'larn deerlerine bakld anlalyor. Daha fazla nesne kullandnzda veya kurucuya verilen rasgele deer aralklaryla oynadnzda daha belirgin sonular greceksinizdir.
57.3.1

Dizgi trnden olan yeler iin opCmp


Dizgi yeler iin opCmp ilevini eksi, sfr, veya art dndrecek ekilde uzun uzun yle yazabilirsiniz: class renci { string isim; override int opCmp(Object o) const { auto sadaki = cast(renci)o; if (isim < sadaki.isim) { return -1; } else if (isim > sadaki.isim) { return 1; } else { return 0; }

} }

// ...

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

320

Object

Onun yerine, std.string modlnde tanmlanm olan ve ayn karlatrmay byk olaslkla daha hzl olarak gerekletiren cmp ilevini de kullanabilirsiniz: import std.string; class renci { string isim; override int opCmp(Object o) const { auto sadaki = cast(renci)o; } } return cmp(isim, sadaki.isim);

// ...

57.4

toHash
Bu ilev, snfn eleme tablolarnda indeks tr olarak kullanlabilmesini salar. Eleme tablosunun eleman tr olarak kullanld durumda bir etkisi yoktur. Uyar: Yalnzca bu ilevi tanmlamak yetmez. Bu ilevin eleme tablolarnda doru olarak kullanlabilmesi iin opEquals ve opCmp ilevlerinin de birbirleriyle tutarl olarak tanmlanm olmalar gerekir.

57.4.1

Eleme tablosu indeks deerleri


Eleme tablolar eleman eriimini ok hzl ekilde gerekletiren veri yaplardr. stelik bunu, tabloda ne kadar eleman bulunduundan bamsz olarak yapabilirler. (Not: Her eyin olduu gibi bu hzn da bir bedeli vardr: elemanlar srasz olarak tutmak zorundadrlar, ve kesinlikle gereken miktardan daha fazla bellek kullanyor olabilirler.) Eleme tablolarnn bu hz, indeks olarak kullanlan tr nce hash denen bir tamsay deere evirmelerinden kaynaklanr. Bu tamsayy kendilerine ait bir dizinin indeksi olarak kullanrlar. Bu yntemin hzdan baka bir yarar, tamsayya dntrlebilen her trn eleme tablosu indeks tr olarak kullanlabilmesidir. toHash , snf nesnelerinin bu ama iin indeks deerleri dndrmelerini salar. Bu sayede, pek mantkl olmasa da, Saat trn bile indeks olarak kullanabiliriz: string[Saat] zamansimleri; zamansimleri[new Saat(12, 0, 0)] = "leni gsteren saat"; Object 'ten kaltm yoluyla edinilen toHash ilevi, farkl nesneler iin farkl indeks deerleri retecek ekilde tanmlanmtr. Bu, opEquals 'un farkl nesnelerin eit olmadklarn kabul etmesine benzer. Yukardaki kod Saat snf iin zel bir toHash ilevi tanmlanmam olsa bile derlenir; ama istediimiz gibi almaz. Yukardaki tabloya eklenmi olan Saat nesnesi ile ayn deere sahip olan, ama ondan farkl bir Saat nesnesi ile erimek istesek; doal olarak tablodaki "leni gsteren saat" deerini bulmay bekleriz:

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

321

Object

if (new Saat(12, 0, 0) in zamansimleri) { writeln("var"); } else { writeln("yok"); } Ne yazk ki, oradaki in ileci false dndrr; yani bu nesnenin tabloda bulunmadn belirtir: yok Bunun nedeni, yerletirilirken kullanlan nesne ile eriirken kullanlan nesnenin new ile ayr ayr oluturulmu olmalardr; yani ikisi farkl nesnelerdir. Dolaysyla; Object 'ten kaltmla edinilen toHash , eleme tablolarnda indeks deeri olarak kullanlmaya ou durumda elverili deildir. toHash 'i, bir tamsay indeks dndrecek ekilde bizim yazmamz gerekir.
57.4.2

toHash iin seilecek yeler


ndeks deeri, nesnenin yeleri kullanlarak hesaplanr. Ancak, her ye bu indeks hesabna uygun deildir. Bunun iin seilecek yeler, nesneyi dier nesnelerden ayrt etmeye yarayan yeler olmaldr. rnein renci gibi bir snfn isim ve soyad yelerinin ikisi birden nesneleri ayrt etmek iin kullanlabilir; nk bu iki yenin her nesnede farkl olduunu dnebiliriz. (sim benzerliklerini gzard ediyorum.) te yandan, renci snfnn notlar dizisi uygun deildir; nk hem birden fazla nesnede ayn not deerleri bulunabilir; hem de ayn rencinin notlar zamanla deiebilir.

57.4.3

Tamsay yeler iin indeks deerleri


Saat nesnelerinin farkl kabul edilebilmeleri iin btn yelerinin deerlerinin nemli olduunu dnebiliriz. Bu yzden, indeks deeri olarak o yenin toplanmas ile elde edilen tamsay kullanlabilir. Bu sayede, herhangi bir yesi deiik olan iki nesnenin indeks deerlerinin farkl olaca garanti edilmi olur: class Saat { int saat; int dakika; int saniye; override hash_t toHash() const { return saat + dakika + saniye; } } // ...

Bu ilevin dn deeri olarak yazlan hash_t , iaretsiz bir tr temsil eder. Ortama bal olarak uint , ulong , veya daha baka bir tr olabilir. Bu ilevi tanmlarken dn trn hash_t olarak yazmanz neririm. Bizim iin asl nemli olan, yeleri kullanarak bir tamsay deer retmektir.

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

322

Object

Saat eleme tablolarnda indeks tr olarak kullanldnda, artk bizim tanmladmz bu toHash kullanlr. Yukardaki kodda new ile farkl olarak kurulmu olsalar bile; ikisinin saat, dakika, ve saniye deerleri ayn olduu iin eleme tablosunda ayn indeks deerini retirler. Programn kts artk beklenen sonucu verir: var nceki ilevlerde olduu gibi, st snf unutmamamz gerekebilir. rnein alarSaat 'in toHash ilevi, Saat 'inkini yle kullanabilir: class alarSaat : Saat { int alarmSaati; int alarmDakikas; override hash_t toHash() const { return super.toHash() + alarmSaati + alarmDakikas; } } // ...

57.4.4

Tamsay olmayan yeler iin indeks deerleri


Tamsay yeleri kullanarak indeks deeri retmek onlar toplamak kadar basittir. D dili; kesirli saylar, dizgiler, ve yap trleri iin ou duruma uygun olan indeks deeri algoritmalar kullanr. Bu algoritmalardan biz de yararlanabiliriz. Kulaa karmak geldii halde aslnda ok ksaca yapmamz gereken; nce typeid 'yi ye ile, sonra da typeid 'nin dndrd nesnenin getHash ye ilevini yenin adresi ile armaktr. Hepsinin dn deeri, o yeye uygun bir indeks deeridir. Bu; kesirli saylar, dizgiler ve yaplar iin hep ayn ekilde yazlr. rencinin ismini bir string yesinde tutan ve eleme tablolar iin indeks deeri olarak bundan yararlanmak isteyen bir snfn toHash ilevi yle yazlabilir: class renci { string isim; override hash_t toHash() const { return typeid(isim).getHash(&isim); } } // ...

Ayn yazm her tr iin kullanabilirsiniz. Bir tamsay deer rettii iin, birden fazla ye iin ararak deerlerini toplayabilirsiniz: return (typeid(isim).getHash(&isim) + typeid(soyAd).getHash(&soyAd));

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

323

Object

57.4.5

Yaplar iin toHash


Yaplar deer trleri olarak kabul edildikleri iin, onlarn indeks deerleri zaten otomatik olarak ve etkin bir algoritmayla hesaplanr. O algoritma, nesnenin btn yelerini dikkate alr. Eer herhangi bir nedenle, rnein bir renci yapsnn not bilgisini darda brakacak ekilde kendiniz yazmak isterseniz; toHash 'i yaplar iin de tanmlayabilirsiniz.

57.5

Problemler
1. Elimizde renkli noktalar ifade eden bir snf olsun: enum Renk { mavi, yeil, krmz }; class Nokta { int x; int y; Renk renk; this(int x, int y, Renk renk) { this.x = x; this.y = y; this.renk = renk; }

Bu snfn opEquals ilevini rengi gzard edecek ekilde yazn. u iki nokta, renkleri farkl olduu halde eit ksnlar. Yani assert denetimi doru ksn: // Renkleri farkl auto maviNokta = new Nokta(1, 2, Renk.mavi); auto yeilNokta = new Nokta(1, 2, Renk.yeil); // Yine de eitler assert(maviNokta == yeilNokta); 2. Ayn snf iin opCmp ilevini; ncelikle x'e, sonra y'ye bakacak ekilde yazn. Aadaki assert denetimleri doru ksn: auto krmzNokta1 = new Nokta(-1, 10, Renk.krmz); auto krmzNokta2 = new Nokta(-2, 10, Renk.krmz); auto krmzNokta3 = new Nokta(-2, 7, Renk.krmz); assert(krmzNokta1 < maviNokta); assert(krmzNokta3 < krmzNokta2); 3. noktay bir araya getiren bir snf daha olsun: class genBlge { Nokta[3] noktalar; this(Nokta bir, Nokta iki, Nokta ) { noktalar = [ bir, iki, ]; }

O snf iin toHash ilevini btn noktalarn kullanacak ekilde yazn. Yine aadaki assert 'ler doru ksn:

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

324

Object

/*

Farkl ama ayn deerli noktalarla kuruluyorlar. Hatrlatma: maviNokta ve yeilNokta deer olarak eit kabul ediliyorlard.

*/ auto blge1 = new genBlge(maviNokta, yeilNokta, krmzNokta1); auto blge2 = new genBlge(yeilNokta, maviNokta, krmzNokta1); // Yine de eitler assert(blge1 == blge2); // Bir eleme tablosu double[genBlge] blgeler; // blge1 ile indeksleniyor blgeler[blge1] = 1.25; // blge2 ile de ayn veriye eriiliyor assert(blge2 in blgeler); assert(blgeler[blge2] == 1.25); Bu snf iin opEquals ve opCmp ilevlerini de yazmanz gerektiini unutmayn. ... zmler

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

325

Arayzler

58

Arayzler
interface , yalnzca soyut ilevler ieren bir snf gibidir. Hibir gerekletirme sunmaz, yalnzca bir snf arayz bildirir. Not: Yukarda sylenen artk doru deil. Bu ders yazldktan sonra D'de yaplan deiiklikler, interface 'lerin de static ve final ilevler sunmalarna izin verir. Bu dersi deitireceim. nceki derslerde grdmz class 'tan treme, gerekletirme tremesi idi. Yeni tanmlanan snf, tretildii snfn btn yelerini ve ilevlerini ediniyordu. Treme dersinde grdmz gibi; abstract anahtar szc de ye ilevlerin ve dolaysyla snflarn soyut olmalarn salyordu. Hatrlarsanz, soyut snflarn kendi nesneleri oluturulamaz; yalnzca onlardan treyen ve o soyut ilevleri tanmlayan trlerin nesneleri oluturulabilir. class 'tan treme zetle unlar getirir: st snfn gerekletirmesini (yelerini ve ye ilevlerini) edinme soyut ilevleri tanmlama zorunluluu interface , btnyle soyut ilevlerden oluan bir snf olarak dnlebilir. Tek yapt, belirli bir arayz belirlemek, ve bu arayzn alt snflar tarafndan tanmlamalarn art komaktr. Kendi yeleri veya ye ilevleri bulunamaz, ve bir class 'tan treyemez. Dolaysyla ye edinmeye deil, belirli bir tr gibi kullanlabilmeyi garanti etmeye yarar. interface 'ten treme zetle unu salar: bildirdii btn ilevlerin, alt snflardan en az birisi tarafndan tanmlanma zorunluluu

58.1

Tanmlanmas
class yerine interface yazlarak tanmlanr: interface SesliAlet { // ... } interface , o arayzn gerektirdii ilevleri bildirir; ama tanmlarn vermez: interface SesliAlet { string ses(); }

// Yalnzca bildirilir (tanm verilmez)

O arayz ile kullanlabilmeleri iin, interface 'ten treyen snflarn interface 'in bildirdii ilevleri tanmlamalar gerekir.

58.2

interface 'ten tretme


Treme sz dizimi class 'tan farkl deildir: class Keman : SesliAlet { string ses() { return "";

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

326

Arayzler

class an : SesliAlet { string ses() { return "n"; } } st snflarda da olduu gibi; parametre olarak interface alan ilevler, onlar asl trlerini bilmeden kullanabilirler. rnein ilemleri srasnda bir SesliAlet kullanan bir ilev, hangi tr bir sesli alet olduunu bilmeden onun ses ilevinden yararlanabilir: void sesliAletKullan(SesliAlet alet) { // ... baz ilemler ... writeln(alet.ses()); // ... baka ilemler ... } Snflarda da olduu gibi, o ilev SesliAlet arayznden treyen her snf ile arlabilir: sesliAletKullan(new Keman); sesliAletKullan(new an); Her aletin kendi asl trnn tanmlad ses ilevi arlr ve sonuta srasyla Keman.ses ve an.ses ye ilevlerinin kts grlr: n

58.3

Birden fazla interface 'ten tretme


Bir snf, ancak tek bir class 'tan tretilebiliyordu. interface 'ten tremede ise byle bir kstlama yoktur. rnein haberleme aletlerini temsil eden yle bir arayz olduunu dnelim: interface HaberlemeAleti { void konu(string mesaj); string dinle(); } Telefon diye bir snf hem sesli bir alet, hem de bir haberleme aleti olarak kullanabilmek iin onu bu iki arayzden birden treterek tanmlayabiliriz: class Telefon : SesliAlet, HaberlemeAleti { // ... }

O tanm u iki ilikiyi birden salar: "telefon bir sesli alettir", ve "telefon bir haberleme aletidir".

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

327

Arayzler

Nesnelerinin oluturulabilmesi iin, Telefon snfnn bu iki arayzn gerektirdii btn ilevleri tanmlamas gerekir: class Telefon : SesliAlet, HaberlemeAleti { string ses() // SesliAlet iin { return "zrrr zrrr"; } void konu(string mesaj) { // ... mesaj hatta ilet ... } string dinle() { string hattaDuyulanSes; // ... sesi hattan oku ... return hattaDuyulanSes; } // HaberlemeAleti iin

// HaberlemeAleti iin

Bu rnekte grld gibi iki interface ile snrl deildir; programn gerekleri dorultusunda snrsz sayda interface 'ten tretilebilir.

58.4

interface 'ten ve class 'tan tretme


Bir snf; bir veya daha fazla interface 'ten tretilmenin yannda, bir adet olduu srece ayn zamanda bir snftan da tretilebilir: class Saat { // ... kendi gerekletirmesi ... } class alarSaat : Saat, SesliAlet { string ses() { return "bi bi biip"; } } alarSaat , Saat 'in btn yelerini ve ye ilevlerini edinmenin yannda, bir de SesliAlet arayznn gerektirdii ses ilevini tanmlamak zorundadr.

58.5

interface 'ten interface tretme


Baka bir arayzden tretilen bir arayz, kendisinden tretilecek olan snflarn kendi bildirdii ilevleri de tanmlamalarn gerektirir: interface MzikAleti : SesliAlet { void akortEt(); } Yukardaki tanma gre; bir MzikAleti olabilmek iin, hem SesliAlet 'in gerektirdii ses ilevini, hem de kendi gerektirdii akortEt ilevini tanmlamak gerekir.

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

328

Arayzler

rnein yukardaki Keman snfn dorudan SesliAlet arayznden tretmek yerine MzikAleti 'nden tretsek, onun bildirdii akortEt ilevini de tanmlamamz gerekir: class Keman : MzikAleti { string ses() { return ""; }

// dolayl olarak SesliAlet iin

void akortEt() // MzikAleti iin { // ... akort ilemleri ... }

58.6

Ne zaman kullanmal
Olduka sk kullanlr. Hemen hemen btn sradzenlerin en stnde bir veya daha fazla interface bulunur. En sk karlalan sradzenlerden birisi, tek bir interface 'ten treyen basit gerekletirme snflarndan oluan sradzendir: MzikAleti (interface) / | \ \ Kemene Saz Kaval ... Daha karmak sradzenlerle de karlalr, ama bu basit yap ou programn ihtiyac iin yeterlidir. Btn alt snflarda da bulunan ortak gerekletirmelerin bir ara snfta tanmland durumlarla da sk karlalr. Alt snflar bu ortak snftan trerler. Aadaki sradzende TelliMzikAleti ve NefesliMzikAleti snflar, kendi alt trlerinin ortak yelerini ve ye ilevlerini ieriyor olabilir: MzikAleti (interface) / \ TelliMzikAleti NefesliMzikAleti / | \ / | \ Kemene Saz ... Kaval Ney ... O ortak snflardan treyen alt snflar da kendi daha zel tanmlarn ierebilirler.

58.7

Soyutlama
Arayzler programlarn alt blmlerini birbirlerinden bamszlatrmaya yararlar. Buna soyutlama denir. rnein mzik aletleri kullanan bir programn byk bir blm yalnzca MzikAleti arayznden haberi olacak ekilde, ve yalnzca onu kullanarak yazlabilir. Mzisyen gibi bir snf, asl trn bilmeden bir MzikAleti ierebilir: class Mzisyen { MzikAleti alet; // ... } Birden fazla mzik aletini bir araya getiren trler, o aletlerin asl trlerini bilmek zorunda deillerdir:

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

329

Arayzler

MzikAleti[] orkestradakiAletler; Programn ou ilevi yalnzca bu arayz kullanarak yazlabilir: bool akortGerekiyor_mu(MzikAleti alet) { bool karar; // ... return karar; } void gzelal(MzikAleti alet) { if (akortGerekiyor_mu(alet)){ alet.akortEt(); } } writeln(alet.ses());

Bu ekilde bir soyutlama kullanarak programn blmlerinin birbirlerinden bamsz hale getirilmeleri, alt snflarda ileride gerekebilecek kod dzenlemelerinin serbeste yaplabilmelerini salar. Alt snflarn gerekletirmeleri bu arayzn arkasnda olduklar iin, bu arayz kullanan kodlar o deiikliklerden etkilenmemi olurlar.

58.8

rnek
Yukardaki btn arayzleri ve snflar ieren bir program yle yazlabilir: import std.stdio; interface SesliAlet { string ses(); } class an : SesliAlet { string ses() { return "n"; } } interface MzikAleti : SesliAlet { void akortEt(); } class Keman : MzikAleti { string ses() { return ""; } void akortEt() { // ... akort ilemleri ... }

interface HaberlemeAleti { void konu(string mesaj);

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

330

Arayzler

string dinle();

class Telefon : SesliAlet, HaberlemeAleti { string ses() { return "zrrr zrrr"; } void konu(string mesaj) { // ... mesaj hatta ilet ... } string dinle() { string hattaDuyulanSes; // ... sesi hattan oku ... return hattaDuyulanSes; }

class Saat { // ... Saat'in gerekletirilmesi } class alarSaat : Saat, SesliAlet { string ses() { return "bi bi biip"; } } // ... alarSaat'in gerekletirilmesi

void main() { SesliAlet[] aletler; aletler aletler aletler aletler ~= ~= ~= ~= new new new new an; Keman; Telefon; alarSaat;

foreach (alet; aletler) { writeln(alet.ses()); }

main 'in iindeki aletler bir SesliAlet dizisi olduu iin, o diziye SesliAlet 'ten treyen her tr eklenebiliyor. Sonuta programn kts btn aletlerin rettikleri sesleri ierir: n zrrr zrrr bi bi biip

58.9

zet
interface bir arayz tanmlar; btn ilevleri soyut olan bir snf gibidir Bir snfn "o trden" nesnelerinin olabilmesi iin o arayzn bildirdii btn ilevleri tanmlamas gerekir

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

331

Arayzler

Tek class 'tan tretebilme kstlamas interface 'lerde yoktur; snflar ve arayzler birden fazla interface 'ten tretilebilirler En sk karlalan sradzenlerden birisi, stte bir arayz (interface ), ve alttaki gerekletirmeleridir (class )

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

332

clear ve scoped

59

clear ve scoped
Yaam Sreleri ve Temel lemler dersinde deikenlerin kurma ilemiyle balayan ve sonlandrma ilemiyle biten yaam srelerini grmtk. Daha sonraki derslerde de nesnelerin kurulmas srasnda gereken ilemlerin this isimli kurucu ilevde, sonlandrlmas srasnda gereken ilemlerin de ~this isimli sonlandrc ilevde tanmlandklarn renmitik. Sonlandrc ilev, yaplarda ve baka deer trlerinde nesnenin yaam sona ererken hemen iletilir. Snflarda ve baka referans trlerinde ise p toplayc tarafndan sonraki bir zamanda iletilir. Burada nemli bir ayrm vardr: bir snf nesnesinin yaamnn sona ermesi ile sonlandrc ilevinin iletilmesi ayn zamanda gereklemez. Nesnenin yaam, rnein geerli olduu kapsamdan kld an sona erer. Sonlandrc ilevi ise p toplayc tarafndan belirsiz bir zaman sonra otomatik olarak iletilir. Sonlandrc ilevlerin grevlerinden bazlar, nesne iin kullanlm olan sistem kaynaklarn geri vermektir. rnein std.stdio.File yaps, iletim sisteminden kendi ii iin alm olduu dosya kaynan sonlandrc ilevinde geri verir. Artk sonlanmakta olduu iin zaten o kayna kullanmas sz konusu deildir. Snflarn sonlandrclarnn p toplayc tarafndan tam olarak ne zaman arlacaklar belli olmad iin, bazen kaynaklarn sisteme geri verilmeleri gecikebilir ve yeni nesneler iin kaynak kalmayabilir.

59.1

Snf sonlandrc ilevlerinin ge iletilmesini gsteren bir rnek


Snflarn sonlandrc ilevlerinin ilerideki belirsiz bir zamanda iletildiklerini gstermek iin bir snf tanmlayalm. Bu snfn kurucu ilevi snfn static bir sayacn arttrsn ve sonlandrc ilevi de o sayac azaltsn. Hatrlarsanz, static yelerden bir tane bulunur: Snfn btn nesneleri o tek yeyi ortaklaa kullanrlar. Bylece o sayacn deerine bakarak snfn nesnelerinden ka tanesinin henz sonlandrlmadklarn anlayabileceiz. class YaamGzlenen { int[] dizi;

// her nesnenin kendisine aittir

static int saya; // btn nesneler tarafndan // paylalr this() { /* * Her nesne bellekte ok yer tutsun diye bu diziyi * ok sayda int'lik hale getiriyoruz. Nesnelerin * bylece byk olmalar nedeniyle, p * toplaycnn bellek amak iin onlar daha sk * sonlandracan umuyoruz. */ dizi.length = 30_000; /* * Bir nesne daha kurulmu olduu iin nesne sayacn * bir arttryoruz. */ ++saya;

~this()

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

333

clear ve scoped

/* * Bir nesne daha sonlandrlm olduu iin nesne * sayacn bir azaltyoruz. */ --saya;

O snfn nesnelerini bir dng iinde oluturan bir program: import std.stdio; void main() { foreach (i; 0 .. 20) { auto deiken = new YaamGzlenen; write(YaamGzlenen.saya, ' '); } // son } writeln();

// ba

O programda oluan her YaamGzlenen nesnesinin yaam aslnda ok ksadr: new anahtar szcyle balar, ve foreach dngsnn kapama parantezinde son bulur. Yaamlar sona eren bu nesneler p toplaycnn sorumluluuna girerler. Programdaki ba ve son aklamalar her nesnenin yaamnn balad ve sona erdii noktay gsteriyor. Nesnelerin sonlandrc ilevlerinin, yaamlarnn sona erdii an iletilmediklerini sayacn deerine bakarak grebiliyoruz: 1 2 3 4 5 6 7 1 2 3 4 5 6 7 1 2 3 4 5 6 p toplaycnn bellek ayrma algoritmas, bu deneyde bu snfn nesnelerinden en fazla 7 adet bulunmasna izin veriyor. (Not: Bu kt p toplaycnn yrtt algoritmaya, bo bellek miktarna ve baka etkenlere bal olarak farkl da olabilir.)

59.2

Nesnenin sonlandrcsn iletmek iin clear()


"Temizle, boalt" anlamna gelen clear() , nesnenin sonlandrc ilevini arr: void main() { foreach (i; 0 .. 20) { auto deiken = new YaamGzlenen; write(YaamGzlenen.saya, ' '); clear(deiken); } } writeln();

YaamGzlenen.saya 'n deeri new satrnda kurucu ilevin iletilmesi sonucunda arttrlr ve 1 olur. Deerinin yazdrld satrdan hemen sonraki clear() satrnda da sonlandrc ilev tarafndan tekrar sfra indirilir. O yzden yazdrld satrda hep 1 olduunu gryoruz: 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, http://ddili.org

334

clear ve scoped

59.3

Ne zaman kullanmal
Yukardaki rnekte grdmz gibi, kaynaklarn p toplaycnn kararna kalmadan hemen geri verilmesi gerektiinde kullanlr. Baka bir nedeni, nesnelerin belirli bir srada sonlandrlyor olmalarnn programn ak asndan nemli olduu durumlardr. rnein RAII yntemi, sonlandrclardaki kodlarn nesnelerin yaamlarnn sona erdii anda iletilmeleri beklentisi zerine kuruludur. RAII, "resource acquisition is initialization"n ksaltmasdr. Tam evirisi "kaynak ayrmak ilklemektir" olsa da, daha ok bunun tmleyeni olan "kaynaklar sonlandrc ilevlerde geri verilmelidir" ilkesini temsil eder. Aadaki XmlEleman , sonlandrc ilevindeki etiket kapama ilemi nedeniyle RAII ynteminin bir rnei olarak grlebilir.

59.4

rnek
Kurucu ve Dier zel levler dersinde XmlEleman isminde bir yap tanmlamtk. O yap, XML elemanlarn <etiket>deer</etiket> eklinde yazdrmak iin kullanlyordu. XML elemanlarnn kapama etiketlerinin yazdrlmas sonlandrc ilevin greviydi: struct XmlEleman { // ... ~this() { writeln(girinti, "</", isim, '>'); }

O yapy kullanan bir programla aadaki kty elde etmitik. Ayn dzeyde bulunan ama ve kapama etiketlerini ayn renkte gsteriyorum:

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

335

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 doru satrlarda beliriyor

O ktnn doru almasnn nedeni, XmlEleman 'nn bir yap olmasdr. Yaplarn sonlandrclar hemen arld iin; nesneleri uygun kapsamlara yerletirmek, istenen kty elde etmek iin yeterlidir: void main() { auto dersler = XmlEleman("dersler", 0); foreach (dersNumaras; 0 .. 2) { auto ders = XmlEleman("ders" ~ to!string(dersNumaras), 1); foreach (i; 0 .. 3) { auto not = XmlEleman("not", 2); const int rasgeleNot = uniform(50, 101); writeln(girintiDizgisi(3), rasgeleNot); } // not sonlanr } // ders sonlanr } // dersler sonlanr Nesneler aklama satrlar ile belirtilen noktalarda sonlandka XML kapama etiketlerini de ka yazdrrlar. Snflarn farkn grmek iin ayn program bu sefer XmlEleman bir snf olacak ekilde yazalm: import std.stdio; import std.string; import std.random;

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

336

clear ve scoped

import std.conv; string girintiDizgisi(in int girintiAdm) { return repeat(" ", girintiAdm * 2); } class XmlEleman { string isim; string girinti; this(in string isim, in int dzey) { this.isim = isim; this.girinti = girintiDizgisi(dzey); } writeln(girinti, '<', isim, '>');

~this() { writeln(girinti, "</", isim, '>'); }

void main() { auto dersler = new XmlEleman("dersler", 0); foreach (dersNumaras; 0 .. 2) { auto ders = new XmlEleman( "ders" ~ to!string(dersNumaras), 1); foreach (i; 0 .. 3) { auto not = new XmlEleman("not", 2); const int rasgeleNot = uniform(50, 101); writeln(girintiDizgisi(3), rasgeleNot);

Referans trleri olan snflarn sonlandrc ilevleri p toplaycya braklm olduu iin programn kts artk istenen dzende deildir:

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

337

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

Btn sonlandrc ilevler iletilmilerdir ama kapama etiketleri beklenen yerlerde deildir. (Not: Aslnda p toplayc btn nesnelerin sonlandrlacaklar garantisini vermez. rnein programn ktsnda hibir kapama parantezi bulunmayabilir.) XmlEleman 'nn sonlandrc ilevinin doru noktalarda iletilmesini salamak iin clear() arlr: void main() { auto dersler = new XmlEleman("dersler", 0); foreach (dersNumaras; 0 .. 2) { auto ders = new XmlEleman( "ders" ~ to!string(dersNumaras), 1); foreach (i; 0 .. 3) { auto not = new XmlEleman("not", 2); const int rasgeleNot = uniform(50, 101); writeln(girintiDizgisi(3), rasgeleNot); } } } clear(not);

clear(ders);

clear(dersler);

Sonuta, nesneler kapsamlardan klrken sonlandrldklar iin programn kts yap tanmnda olduu gibi dzgndr:

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

338

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 doru satrlarda belirmi

59.5

Sonlandrc ilevi otomatik olarak armak iin scoped


Yukardaki programn bir yetersizlii vardr: Kapsamlardan daha clear() satrlarna gelinemeden atlm olan bir hata nedeniyle klm olabilir. Eer clear() satrlarnn kesinlikle iletilmeleri gerekiyorsa, bunun bir zm Hatalar dersinde grdmz scope ve dier olanaklardan yararlanmaktr. Baka bir yntem, snf nesnesini new yerine std.typecons.scoped ile kurmaktr. scoped() , snf deikenini perde arkasnda bir yap nesnesi ile sarmalar. O yap nesnesinin sonlandrcs kapsamdan klrken otomatik olarak arldnda snf nesnesinin sonlandrcsn da arr. scoped 'un etkisi, yaam sreleri asndan snf nesnelerini yap nesnelerine benzetmesidir. Programn yalnzca deien satrlarn gsteriyorum: import std.typecons; // ... auto dersler = scoped!XmlEleman("dersler", 0); // ... auto ders = scoped!XmlEleman( "ders" ~ to!string(dersNumaras), 1); // ... auto not = scoped!XmlEleman("not", 2); Bu deiikliklerden sonra programn kts yine istenen dzende olur.

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

339

clear ve scoped

59.6

zet
Bir snf nesnesinin sonlandrc ilevinin istenen bir anda arlmas iin clear() ilevi kullanlr. scoped ile kurulan snf nesnelerinin sonlandrclar kapsamdan klrken otomatik olarak arlr.

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

340

Modller ve Ktphaneler

60

Modller ve Ktphaneler
D programlarn ve ktphanelerini oluturan en alt yapsal birimler modllerdir. D'nin modl kavram ok basit bir temel zerine kuruludur: Her kaynak dosya bir modldr. Bu tanma gre, imdiye kadar deneme programlarmz yazdmz tek kaynak dosya bile bir modldr. Her modln ismi, dosya isminin .d uzantsndan nceki blm ile ayndr; ve kaynak dosyann en bana yazlan module anahtar szc ile belirtilir. rnein bir kaynak dosyann isminin "kedi.d" olduunu varsayarsak: module kedi; class Kedi { // ... } module satr istee baldr. Yazlmad zaman, otomatik olarak dosyann isminin .d 'den nceki blm kullanlr.

60.1

Dosya ve modl isimleri


D programlarn Unicode olarak oluturma konusunda anslyz; bu, hangi ortamda olursa olsun geerlidir. Ancak, dosya sistemleri konusunda ayn serbesti bulunmaz. rnein Windows iletim sistemlerinin standart dosya sistemleri dosya isimlerinde byk/kk harf ayrm gzetmezken, Linux sistemlerinde byk/kk harfler farkldr. Ayrca ou dosya sistemi, dosya isimlerinde kullanlabilecek karakterler konusunda kstlamalar getirir. O yzden, programlarnzn tanabilir olmalar iin dosya isimlerinde yalnzca ASCII kk harfler kullanmanz neririm. rnein yukardaki Kedi snf ile birlikte kullanlacak olan bir Kpek snfnn modlnn dosya ismini "kopek.d" olarak seebiliriz. Bu yzden modln ismi de ASCII harflerden oluur: module kopek; class Kpek { // ... } // ASCII harflerden oluan modl ismi // Unicode harflerden oluan program kodu

60.2

Paketler
Modllerin bir araya gelerek oluturduklar yapya paket denir. D'nin paket kavram da ok basittir: Dosya sisteminde ayn klasrde bulunan btn modller ayn pakedin paras olarak kabul edilirler. Pakedi ieren klasrn ismi de modl isimlerinin ba tarafn oluturur. rnein yukardaki "kedi.d" ve "kopek.d" dosyalarnn "hayvan" isminde bir klasrde bulunduklarn dnrsek, modl isimlerinin bana klasr ismini yazmak, onlar ayn pakedin modlleri yapmaya yeter: module hayvan.kedi; class Kedi {

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

341

Modller ve Ktphaneler

// ...

Ayn ekilde kopek modl iin de: module hayvan.kopek; class Kpek { // ... } Paket isimleri dosya sistemi klasrlerine karlk geldii iin, i ie klasrlerde bulunan modllerin paket isimleri de o klasr yapsnn edeeridir. rnein "hayvan" klasrnn altnda bir de "omurgalilar" klasr olsa, oradaki bir modln paket ismi bu klasr de ierir: module hayvan.omurgalilar.kedi; Kaynak dosyalarn ne derece dallanaca programn byklne ve tasarmna baldr. Kk bir programn btn dosyalarnn tek bir klasrde bulunmasnda bir saknca yoktur. te yandan, dosyalar belirli bir dzen altna almak iin klasrleri gerektii kadar dallandrmak da mmkndr.

60.3

Modllerin programda kullanlmalar


imdiye kadar ok kullandmz import anahtar szc, bir modln baka bir modle tantlmasn, ve o modl iinde kullanlabilmesini salar: import std.stdio; import 'tan sonra yazlan modl ismi, eer varsa, paket bilgisini de ierir. Yukardaki koddaki std. , standart ktphaneyi oluturan modllerin std isimli pakette bulunduklarn gsterir. Benzer ekilde, hayvan.kedi ve hayvan.kopek modlleri bir "deneme.d" dosyasnda u ekilde bildirilir: module deneme; import hayvan.kedi; import hayvan.kopek; // bu modln ismi // kulland bir modl // kulland baka bir modl

void main() { auto kedi = new Kedi; auto kpek = new Kpek; } Not: Aada anlatld gibi, yukardaki programn derlenip oluturulabilmesi iin o modl dosyalarnn da balaycya derleme satrnda bildirilmeleri gerekir.

60.4

Modllerin dosya sistemindeki yerleri


Modl isimleri dosya sistemindeki dosyalara bire bir karlk geldii iin; derleyici, bir modl dosyasnn nerede bulunduunu modl ismini klasr ve dosya isimlerine dntrerek bulur.

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

342

Modller ve Ktphaneler

rnein yukardaki programn kulland iki modl, "hayvan/kedi.d" ve "hayvan/kopek.d" dosyalardr. Dolaysyla, yukardaki dosyay da sayarsak bu program oluturmak iin modl kullanlmaktadr.

60.5

Ksa ve uzun isimler


Programda kullanlan isimler, paket ve modl bilgilerini de ieren uzun halde de yazlabilirler. Bunu, Kedi snfnn tr ismini ksa ve uzun yazarak yle gsterebiliriz: auto kedi0 = new Kedi; auto kedi1 = new hayvan.kedi.Kedi;

// sttekinin ayns

Normalde uzun isimleri kullanmak gerekmez. Onlar yalnzca olas karklklar gidermek iin kullanrz. rnein iki modlde birden tanmlanm olan bir ismi ksa olarak yazdmzda, derleyici hangi modldekinden bahsettiimizi anlayamaz. Hem hayvan modlnde hem de arabalar modlnde bulunabilecek Jaguar isimli iki snftan hangisinden bahsettiimizi uzun ismiyle yle belirtmek zorunda kalrz: import hayvan.jaguar; import arabalar.jaguar; // ... auto karklk = new Jaguar; auto hayvanm = new hayvan.jaguar.Jaguar; auto arabam = new arabalar.jaguar.Jaguar; // derleme HATASI // derlenir // derlenir

60.6

Modllerdeki tanmlarn programa dahil edilmesi


import anahtar szc, belirtilen modln programn paras haline gelmesi iin yeterli deildir. import , yalnzca o modldeki olanaklarn bu kaynak kod iinde kullanlabilmelerini salar. O kadar, ancak kaynak kodun derlenebilmesi iin gereklidir. Yukardaki program yalnzca "deneme.d" dosyasn kullanarak oluturmaya almak yetmez: $ dmd deneme.d -w deneme.o: In function `_Dmain': deneme.d:(.text._Dmain+0x4): undefined reference to `_D6hayvan4kedi4Kedi7__ClassZ' deneme.d:(.text._Dmain+0xf): undefined reference to `_D6hayvan5kopek6Kpek7__ClassZ' collect2: ld returned 1 exit status --- errorlevel 1 Not: Derleyici uyarlarn etkinletiren -w ayarn da her zaman iin kullanmanz neririm. O hata mesajlar balaycdan gelir. Her ne kadar anlalmaz isimler ieriyor olsalar da; yukardaki hata mesajlar, programda kullanlan baz tanmlarn bulunamadklarn bildirir. Programn oluturulmas, perde arkasnda arlan balaycnn grevidir. Derleyici tarafndan derlenen modller balaycya verilir; ve program, balaycnn bir araya getirdii paralardan oluturulur.

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

343

Modller ve Ktphaneler

Bu yzden; program oluturan btn paralarn derleme satrnda belirtilmeleri gerekir. Yukardaki programn oluturulabilmesi iin, kulland "hayvan/kedi.d" ve "hayvan/kopek.d" dosyalar da derleme satrnda bildirilmelidir: $ dmd deneme.d hayvan/kedi.d hayvan/kopek.d -w Modlleri derleme satrnda her program iin ayr ayr belirtmek yerine, onlar ktphaneler iinden de kullanabiliriz.

60.7

Ktphaneler
Modl tanmlarnn derlendikten sonra bir araya getirilmelerine ktphane ad verilir. Ktphaneler kendileri program olmadklar iin; programlarn balang ilevi olan main , ktphanelerde bulunmaz. Ktphaneler yalnzca ilev, yap, snf, vs. tanmlarn bir araya getirirler. Daha sonra program oluturulurken programn dier modlleriyle balanrlar. Ktphane oluturmak iin dmd'nin -lib seenei kullanlr. Oluturulan ktphanenin isminin hayvan olmasn salamak iin -of seeneini de kullanrsak, yukardaki "kedi.d" ve "kopek.d" modllerini ieren bir ktphane u ekilde oluturulabilir: $ dmd hayvan/kedi.d hayvan/kopek.d -lib -ofhayvan -w Konsoldan altrlan o komut, belirtilen .d dosyalarn derler ve bir ktphane dosyas olarak bir araya getirir. altnz ortama bal olarak ktphane dosyasnn ismi farkl olacaktr. rnein Linux ortamlarnda ktphane dosyalarnn uzantlar .a olur: hayvan.a Program oluturulurken artk "hayvan/kedi.d" ve "hayvan/kopek.d"nin ayr ayr bildirilmelerine gerek kalmaz. Onlar ieren ktphane dosyas tek bana yeterlidir: $ dmd deneme.d hayvan.a -w Yani o komut, daha nce kullandmz u komutun edeeridir: $ dmd deneme.d hayvan/kedi.d hayvan/kopek.d -w Bir istisna olarak, imdiye kadar ok yararlandmz Phobos modllerini ieren standart ktphanenin aka bildirilmesi gerekmez. O ktphane, programa otomatik olarak dahil edilir. Yoksa normalde onu da rnein u ekilde belirtmemiz gerekirdi: $ dmd deneme.d hayvan.a /usr/lib/libphobos2.a -w Not: Phobos ktphane dosyasnn yeri ve ismi sizin ortamnzda farkl olabilir.

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

344

Sarma ve Eriim Haklar

61

Sarma ve Eriim Haklar


imdiye kadar tasarladmz btn yap ve snf trlerinin btn yeleri dardan eriime akt. Hatrlamak iin yle bir renci yaps dnelim. enum Cinsiyet { kz, erkek } struct renci { string isim; Cinsiyet cinsiyet; } O yapnn nesnelerinin yelerine istediimiz gibi eriebiliyorduk: auto renci = renci("Tolga", Cinsiyet.erkek); writeln(renci.isim, ", bir ", to!string(renci.cinsiyet), " rencidir"); yelere byle serbeste eriebilmek, o yeleri programda istediimiz gibi kullanma olana salad iin yararldr. O kod, renci hakknda bilgiyi ka yle yazdrr: Tolga, bir erkek rencidir Ancak, ye eriiminin bu kadar serbest olmas sakncalar da dourabilir. rnein belki de yanllkla, rencinin yalnzca ismini deitirdiimizi dnelim: renci.isim = "Aye"; O atama sonucunda artk nesnenin geerlilii bozulmu olabilir: Aye, bir erkek rencidir Baka bir rnek olarak, bir grup renciyi barndran Okul isminde bir snfa bakalm. Bu snf, okuldaki kz ve erkek rencilerin saylarn ayr olarak tutuyor olsun: class Okul { renci[] renciler; int kzToplam; int erkekToplam; void ekle(in renci renci) { renciler ~= renci; final switch (renci.cinsiyet) { case Cinsiyet.kz: ++kzToplam; break; case Cinsiyet.erkek: ++erkekToplam; break; }

override string toString() {

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

345

Sarma ve Eriim Haklar

return format( "%s kz, %s erkek; toplam %s renci", kzToplam, erkekToplam, renciler.length);

ekle ilevini kullanarak o snfn nesnelerine yeni renciler ekleyebiliriz: auto okul = new Okul; okul.ekle(renci("Leyla", Cinsiyet.kz)); okul.ekle(renci("Metin", Cinsiyet.erkek)); writeln(okul); ve tutarl bir kt elde ederiz: 1 kz, 1 erkek; toplam 2 renci Oysa bu snfn yelerine serbeste eriebiliyor olmak, onun nesnelerini de tutarsz hale getirebilir. rnein renciler yesine dorudan yeni bir renci eklediimizi dnelim: okul.renciler ~= renci("Nimet", Cinsiyet.kz); Yeni renci, toplamlar sayan ekle ilevi arlmadan eklendii iin bu Okul nesnesi artk tutarszdr: 1 kz, 1 erkek; toplam 3 renci

61.1

Sarma
te sarma, bu tr durumlar nlemek iin yelere eriimi kstlayan bir olanaktr. Baka bir yarar, kullanclarn snfn i yapsn bilmek zorunda kalmamalardr. Snf, sarma yoluyla bir anlamda bir kara kutu haline gelir ve ancak arayzn belirleyen ilevler araclyla kullanlabilir. Kullanclarn snfn yelerine dorudan eriememeleri, ayrca snfn i tasarmnn ileride rahata deitirilebilmesini de salar. Snfn arayzndeki ilevlerin tanmna dokunulmad srece, iinin yaps istendii gibi deitirilebilir. Sarma, kredi kart numaras veya ifre gibi deerli veya gizli verilere eriimi kstlamak iin deildir ve bu amala kullanlamaz. Sarma, program gelitirme konusunda yararl bir olanaktr: tanmlarmzn kolay ve doru kullanlmalarn ve kolayca deitirilebilmelerini salar.

61.2

Eriim haklar
D'de eriim haklar iki balamda belirtilebilir: 1. yap veya snf dzeyinde: her yap veya snf yesinin eriim hakk ayr olarak belirtilebilir 2. modl dzeyinde: modl iinde tanmlanm olan her tr olanan eriim hakk ayr olarak belirtilebilir: snflar, yaplar, ilevler, enum'lar, vs. yelerin veya modl tanmlarnn eriim haklar, aadaki drt zellikten birisi olarak belirtilebilir. Varsaylan eriim public 'tir.

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

346

Sarma ve Eriim Haklar

public , genel: Programn her tarafndan eriilebilmeyi ifade eder; hibir eriim kstlamas yoktur. Bunun bir rnei olarak stdout standart akmn dnebilirsiniz. Onu bildiren std.stdio modlnn import ile eklenmesi, stdout 'un serbeste kullanlabilmesi iin yeterlidir. private , zel: zel eriimi ifade eder Bu ekilde tanmlanan yelere iinde tanml olduklar snfn kodlar tarafndan, veya o snf barndran modldeki kodlar tarafndan eriilebilir. package , pakede ak: paketteki modller tarafndan eriilebilmeyi ifade eder Bu ekilde iaretlenmi olan bir tanm, onu barndran paketteki btn kodlara aktr. Bu eriim hakk yalnzca modl ieren en iteki pakede verilir. rnein hayvan.omurgalilar.kedi isimli bir modl iinde package olarak iaretlenmi olan bir tanm; kedi modlnn kendisinden baka, omurgalilar pakedindeki btn modllere de aktr. protected , korumal: tretilen snf tarafndan da eriilebilmeyi ifade eder private eriimi genileten bir eriimdir: bu ekilde iaretlenmi olan yeye snf tarafndan eriilmek yannda, o snftan tretilen snflardan da eriilebilir.

61.3

Belirtilmesi
Eriim haklar ekilde belirtilebilir. Tek bir tanmn nne yazldnda yalnzca o tanmn eriim haklarn belirler. Bu, Java ve baz baka dillerdeki gibidir: private int birSay; private void birlev() { // ... } ki nokta st ste karakteriyle yazldnda, ayn ekilde yeni bir eriim hakk yazlana kadarki btn tanmlar etkiler. Bu, C++'daki gibidir: private: // ... // ... buradaki btn tanmlar zel ... // ... protected: // ... // ... buradakiler korumal ... // ... Blok sz dizimiyle yazldnda btn bloun iini etkiler: private { // ... // ... buradakilerin hepsi zel ... // ... } Bu yntemin etkisi ayndr. Hangisini kullanacanza tasarmnza uygun olduunu dndnz ekilde serbeste karar verebilirsiniz.

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

347

Sarma ve Eriim Haklar

61.4

import 'lar normalde modle zeldir


import ile eklenen modller, o modl dolayl olarak ekleyen baka modller tarafndan grlemezler. rnein okul modl std.stdio modln eklese, okul modln ekleyen baka modller std.stdio 'dan otomatik olarak yararlanamazlar. rnein okul modl yle balyor olsun: module okul.okul; import std.stdio; // ... Onu kullanan u program derlenemez: import okul.okul; void main() { writeln("merhaba"); } // kendi ii iin eklenmi...

// derleme HATASI

O yzden, std.stdio 'yu asl modln ayrca eklemesi gerekir: import okul.okul; import std.stdio; void main() { writeln("merhaba"); }

// imdi derlenir

Bazen, eklenen bir modln baka modlleri de otomatik ve dolayl olarak sunmas istenebilir. rnein okul isimli bir modln eklenmesinin, ogrenci modln de otomatik olarak eklemesi istenebilir. Bu, import ilemi public olarak iaretlenerek salanr: module okul.okul; public import okul.ogrenci; // ... Artk okul modln ekleyen modller ogrenci modln aka eklemek zorunda kalmadan, onun iindeki renci yapsn kullanabilirler: import std.stdio; import okul.okul; void main() { auto renci = renci("Tolga", Cinsiyet.erkek); // ... O program yalnzca okul modln ekledii halde renci yapsn da kullanabilmektedir.

61.5

Sarmay ne zaman kullanmal


Sarma, giri blmnde gsterdiim sorunlar nlemek ve snf tasarmlarn serbest brakmak iin ok etkili bir olanaktr.

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

348

Sarma ve Eriim Haklar

yelerin ve baka deikenlerin ilgisiz kodlar tarafndan serbeste deitirilmeleri nlenmi olur. Bylece, yukardaki ekle ilevinde olduu gibi, nesnelerin tutarllklar denetim altna alnm olur. Ayrca, baka kodlar yap ve snf gerekletirmelerine baml kalmam olurlar. rnein Okul.renciler yesine eriilebiliyor olmas, sizin o diziyi daha sonradan rnein bir eleme tablosu olarak deitirmenizi gletirir. nk bu deiiklik kullanc kodlarn da etkileyecektir. Sarma, nesne ynelimli programlamann en yararl olanaklar arasndadr.

61.6

rnek
Yukardaki renci yapsn ve Okul snfn sarmaya uygun olacak ekilde tanmlayalm ve kk bir deneme programnda kullanalm. Bu rnekte toplam dosya tanmlayacaz. nceki dersten de hatrlayacanz gibi; "okul" isminde bir klasr iinde tanmlandklar iin ilk ikisi okul pakedinin paralar olacaklar: "okul/ogrenci.d": renci yapsn ieren ogrenci modl "okul/okul.d": Okul snfn tanmlayan okul modl "deneme.d": kk bir deneme program lk nce "okul/ogrenci.d" dosyasn grelim: module okul.ogrenci; import std.string; import std.conv; enum Cinsiyet { kz, erkek } struct renci { package string isim; package Cinsiyet cinsiyet; string toString() { return format("%s bir %s rencidir", isim, to!string(cinsiyet)); }

Bu yapnn yelerini yalnzca kendi pakedindeki kodlara amak iin package olarak belirledim; nk biraz sonra greceimiz Okul snfnn bu yapnn yelerine dorudan erimesini istedim. Ayn pakedin paras olsa bile baka bir modl tarafndan yapnn yelerine eriilmesi, aslnda temelde sarmaya kardr. Yine de; renci ve Okul 'un birbirlerinin yelerine dorudan eriebilecek kadar yakn tanmlar olduklar dnlebilir. Bu sefer de, o modlden yararlanan "okul/okul.d" dosyasna bakalm: module okul.okul; public import okul.ogrenci; import std.string; class Okul { // 1

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

349

Sarma ve Eriim Haklar

private: renci[] renciler; int kzToplam; int erkekToplam; public: void ekle(in renci renci) { renciler ~= renci; final switch (renci.cinsiyet) { case Cinsiyet.kz: ++kzToplam; break; case Cinsiyet.erkek: ++erkekToplam; break; }

// 2

// 3

// 4a

override string toString() { string sonu = format( "%s kz, %s erkek; toplam %s renci", kzToplam, erkekToplam, renciler.length); for (int i = 0; i != renciler.length; ++i) { sonu ~= (i == 0) ? ": " : ", "; sonu ~= renciler[i].isim; // 4b } } return sonu;

1. Bu modl ekleyen programlar ogrenci modln de ayrca eklemek zorunda kalmasnlar diye; public olarak ekleniyor. Bir anlamda, bu "ekleme", genele alyor. 2. Okul snfnn btn yeleri zel olarak iaretleniyor. Bu sayede snf nesnelerinin tutarll gvence altna alnm oluyor. 3. Bu snfn herhangi bir ekilde kullanl olabilmesi iin ye ilevler sunmas gerekir; ekle ve toString ilevleri, kullanlabilmeleri iin public olarak iaretleniyorlar. 4. nceki dosyada package olarak iaretlendikleri iin, renci yapsnn her iki yesine de bu modldeki kodlar tarafndan eriilebiliyor. Son olarak bu iki modl kullanan program dosyasna da bakalm: import std.stdio; import okul.okul; void main() { auto renci = renci("Tolga", Cinsiyet.erkek); writeln(renci.toString()); auto okul = new Okul; okul.ekle(renci("Leyla", Cinsiyet.kz)); okul.ekle(renci("Metin", Cinsiyet.erkek)); okul.ekle(renci("Nimet", Cinsiyet.kz)); } writeln(okul);

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

350

Sarma ve Eriim Haklar

Bu program, renci ve Okul 'u ancak genel eriime ak olan arayzleri araclyla kullanabilir. Ne renci 'nin, ne de Okul 'un yelerine eriemez ve bu yzden nesneler her zaman iin tutarldr: Tolga bir erkek rencidir 2 kz, 1 erkek; toplam 3 renci: Leyla, Metin, Nimet Dikkat ederseniz, o program bu tanmlar yalnzca Okul.ekle ve renci.toString ilevleri araclyla kullanyor. O ilevlerin kullanmlar deitirilmedii srece, renci 'nin ve Okul 'un tanmlarnda yaplan hibir deiiklik bu program etkilemez.

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

351

Nitelikler

62

Nitelikler
Nitelikler, yap ve snf ilevlerinin sanki ye deikenlermi gibi arlmalarn salayan bir olanaktr. Bu olana dinamik dizilerden tanyorsunuz. Dizilerin length nitelii, dizideki eleman adedini bildirir: int[] dizi = [ 7, 8, 9 ]; assert(dizi.length == 3); Yalnzca bu kullanma, yani uzunluu bildirmesine bakarsak, length 'in bir ye deiken olarak tasarlandn dnebiliriz: struct BirDiziGerekletirmesi { int length; } // ...

Ancak, bu niteliin dier kullanm bunun doru olamayacan gsterir. nk dinamik dizilerde length niteliine yeni bir deer atamak, dizi uzunluunu belki de yeni elemanlar ekleyecek biimde deitirir: dizi.length = 5; assert(dizi.length == 5); // imdi 5 eleman var

Not: Sabit uzunluklu dizilerde length nitelii deitirilemez. Yukardaki atama yalnzca bir deer deiiklii deildir. length 'e yaplan o atamann arkasnda ye deeri deitirmekten ok daha karmak baka ilemler gizlidir: dizinin kapasitesinin yeni elemanlar iin yeterli olup olmadna baklmas gerekiyorsa daha byk yeni bir yer ayrlmas eski elemanlarn o yeni yerin ba tarafna kopyalanmalar Bu adan baknca, length 'e yaplan atamann aslnda bir ilev gibi almas gerektii grlebilir. Nitelikler, ye deiken gibi kullanlmalarna ramen, duruma gre belki de ok karmak ilemleri olan ilevlerdir. Bu ilevler @property belirteciyle iaretlenerek tanmlanrlar.

62.1

Deer reten nitelik ilevleri


ok basit bir rnek olarak yalnzca en ve boy yeleri bulunan bir dikdrtgen yapsna bakalm: struct Dikdrtgen { double en; double boy; } Daha sonradan, bu dikdrtgenin alann bildiren bir yesinin olmasn da isteyelim: auto bahe = Dikdrtgen(10, 20); writeln(bahe.alan);

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

352

Nitelikler

imdiye kadarki derslerde rendiimiz kadaryla, bunu o sz dizimiyle gerekletirmek iin bir nc ye eklememiz gerekir: struct Dikdrtgen { double en; double boy; double alan; } Bu tasarmn sakncas, bu yap nesnelerinin tutarsz durumlara debilecek olmalardr: aralarnda her zaman iin "en * boy == alan" gibi bir ilikinin bulunmas gerekirken, bu yapnn yeleri kullanclar tarafndan serbeste deitirilebildikleri iin bu iliki kullanm srasnda bozulabilir. Bir rnek olarak, nesne kurulurken tamamen ilgisiz deerler kullanlabilir: // Tutarsz nesne: alan 10 * 20 == 200 deil, 1111 auto bahe = Dikdrtgen(10, 20, 1111); te byle durumlar nlemenin bir yolu, alan bilgisini D'nin nitelik olanandan yararlanarak sunmaktr. Bu durumda yapya yeni ye eklenmez; onun deeri, @property olarak iaretlenmi olan bir ilevin sonucu olarak hesaplanr. levin ismi, ye deiken gibi kullanlacak olan isimdir; yani bu durumda alan . Bu ilevin dn deeri, niteliin deeri haline gelir: struct Dikdrtgen { double en; double boy; @property double alan() const { return en * boy; }

Not: lev bildiriminin sonundaki const , const ref Parametreler ve const ye levler dersinden hatrlayacanz gibi, bu nesnenin bu ilev iinde deitirilmediini bildirir. Artk o yapy sanki nc bir yesi varm gibi kullanabiliriz: auto bahe = Dikdrtgen(10, 20); writeln("Bahenin alan: ", bahe.alan); Bu olanak sayesinde, alan niteliinin deeri ilevde enin ve boyun arpm olarak hesapland iin her zaman tutarl olacaktr: Bahenin alan: 200

62.2

Atama ileci ile kullanlan nitelik ilevleri


Dizilerin length niteliinde olduu gibi, kendi tanmladmz nitelikleri de atama ilemlerinde kullanabiliriz. rnein Dikdrtgen yapsnn alannn byle bir atama ile deitirilebilmesini isteyelim: bahe.alan = 50;

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

353

Nitelikler

O atamann sonucunda alann gerekten deimesi iin dikdrtgenin yelerinin, yani eninin veya boyunun deimesi gerekir. Bunu salamak iin dikdrtgenimizin esnek olduunu kabul edebiliriz: "en * boy == alan" ilikisini koruyabilmek iin kenar uzunluklarnn uygun ekilde deimeleri gerekir. Niteliklerin atama ileminde kullanlmalarn salayan ilev de @property ile iaretlenir. levin ismi yine niteliin isminin aynsdr. Atama ileminin sa tarafnda kullanlan deer, bu ilevin tek parametresinin deeri olarak gelir. alan niteliine deer atamay salayan bir snf yle yazlabilir: import std.stdio; import std.math; struct Dikdrtgen { double en; double boy; @property double alan() const { return en * boy; } @property void alan(double yeniAlan) { const double byltme = sqrt(yeniAlan / alan); en *= byltme; boy *= byltme;

void main() { auto bahe = Dikdrtgen(10, 20); writeln("Bahenin alan: ", bahe.alan); bahe.alan = 50; writefln("Yeni durum: %s x %s = %s", bahe.en, bahe.boy, bahe.alan);

Atama ilemi ile kullanlan ilevde std.math modlnn karekk almaya yarayan ilevi olan sqrt 'u kullandm. Dikdrtgenin hem eni hem de boyu, orann karekk kadar deiince alan da yeni deere gelmi olur. rnein alan niteliine drtte biri kadar bir deer atandnda, yani 200 yerine 50 atandnda, kenarlarn uzunluklar yarya iner: Bahenin alan: 200 Yeni durum: 5 x 10 = 50

62.3

Nitelikler art deildir


Yukardaki rnekteki yapnn nasl sanki nc bir yesi varm gibi kullanlabildiini grdk. Ancak bu hibir zaman kesinlikle gerekmez; nk deiik ekilde yazlyor olsa da, ayn ii ye ilevler yoluyla da gerekletirebiliriz: import std.stdio; import std.math;

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

354

Nitelikler

struct Dikdrtgen { double en; double boy; double alan() const { return en * boy; } void alanDeitir(double yeniAlan) { const double byltme = sqrt(yeniAlan / alan); en *= byltme; boy *= byltme;

void main() { auto bahe = Dikdrtgen(10, 20); writeln("Bahenin alan: ", bahe.alan()); bahe.alanDeitir(50); writefln("Yeni durum: %s x %s = %s", bahe.en, bahe.boy, bahe.alan());

Hatta, lev Ykleme dersinde de anlatld gibi, bu iki ilevin ismini ayn da seebiliriz: double alan() const { // ... } void alan(double yeniAlan) { // ... }

62.4

Ne zaman kullanmal
Bu derste anlatlan nitelik ilevleri ile daha nceki derslerde grdmz eriim ilevleri arasnda seim yapmak, her zaman kolay olmayabilir. Bazen eriim ilevleri, bazen nitelikler, bazen de ikisi birden doal gelecektir. Niteliklerin kullanlmamalar da bir kayp deildir. rnein C++ gibi baka baz dillerde nitelik olana bulunmaz. Ancak ne olursa olsun, Sarma ve Eriim Haklar dersinde grdmz gibi, yelere dorudan eriimin engellenmesi nemlidir. Yap ve snf tasarmlar zamanla gelitike, yelerin kullanc kodlar tarafndan dorudan deitirilmeleri sorun haline gelebilir. O yzden, ye eriimlerini mutlaka nitelikler veya eriim ilevleri yoluyla salamanz neririm. rnein yukardaki Dikdrtgen yapsnn en ve boy yelerinin eriime ak braklmalar, yani public olmalar, ancak ok basit yaplarda kabul edilir bir davrantr. Normalde bunun yerine ya ye ilevler, ya da nitelikler kullanlmaldr: struct Dikdrtgen { private: double en_; double boy_;

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

355

Nitelikler

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

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

yelerin private olarak iaretlendiklerine ve o sayede deerlerine yalnzca nitelik ilevleri yoluyla eriilebildiklerine dikkat edin. Ayrca ayn isimdeki nitelik ilevleriyle karmasnlar diye yelerin isimlerinin sonlarna _ karakteri eklediime dikkat edin. ye isimlerinin bu ekilde farkllatrlmalar, nesneye ynelik programlamada olduka sk karlalan bir uygulamadr. Yukarda da grdmz gibi; yelere eriimin nitelik ilevleri yoluyla salanmas, kullanm asndan hibir fark getirmez. en ve boy yine sanki nesnenin yeleriymi gibi kullanlabilir: auto bahe = Dikdrtgen(10, 20); writeln("en: ", bahe.en, " boy: ", bahe.boy); Hatta; atama ileci ile kullanlan nitelik ilevini bu yeler iin bilerek tanmlamadmz iin, enin ve boyun dardan deitirilmeleri de artk olanakszdr: bahe.en = 100; // derleme HATASI

Bu da, yelere yaplan deiikliklerin kendi denetimimiz altnda olmas asndan ok nemlidir. Bu yeler ancak bu snfn kendi ilevleri tarafndan deitirilebilirler. Nesnelerin tutarllklar bu sayede bu yapnn veya snfn ilevleri tarafndan salanabilir. Dardan deitirilmelerinin yine de uygun olduu yeler varsa, atamay salayan nitelik ilevi onlar iin zel olarak tanmlanabilir.

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

356

Yap ve Snflarda Szlemeli Programlama

63

Yap ve Snflarda Szlemeli Programlama


Szlemeli programlama, kod hatalarn azaltmaya yarayan ok etkili bir olanaktr. D'nin szlemeli programlama olanaklarndan ikisini Szlemeli Programlama dersinde grmtk. in ve out bloklar, ilevlerin giri ve k koullarn denetlemek iin kullanlyordu. Hatrlamak amacyla, gen alann Heron formln kullanarak kenar uzunluklarndan hesaplayan bir ilev yazalm. genin alannn doru olarak hesaplanabilmesi iin kenar uzunluunun da sfr veya daha byk olmas gerekir. Ek olarak, bir genin hibir kenarnn dier ikisinin toplamndan uzun olmamas da gerekir. O giri koullar salandnda, genin alan da sfr veya daha byk olacaktr. Bu koullar ve bu garantiyi salayan bir ilev yle yazlabilir: private import std.math; double genAlan(in double a, in double b, in double c) in { // Kenarlar sfrdan kk olamaz assert(a >= 0); assert(b >= 0); assert(c >= 0); // Hibir kenar dier ikisinin toplamndan uzun olamaz assert(a <= (b + c)); assert(b <= (a + c)); assert(c <= (a + b));

} out (sonu) { assert(sonu >= 0); } body { const double yarevre = return sqrt(yarevre * (yarevre * (yarevre * (yarevre }

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

63.1

ye ilevlerin in ve out bloklar


in ve out bloklar ye ilevlerle de kullanlabilir ve ayn ekilde, ilevin giri koullarn ve k garantisini denetler. Yukardaki alan hesab ilevini bir ye ilev olarak yazalm: 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

357

Yap ve Snflarda Szlemeli Programlama

{ } body {

assert(sonu >= 0);

const double yarevre = return sqrt(yarevre * (yarevre * (yarevre * (yarevre

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

void main() { auto DrtBegeni = gen(3, 4, 5); writeln(DrtBegeni.alan); } genin kenarlar zaten yapnn ye deikenleri olduklar iin, bu ilevin parametreleri yok. O yzden bu ilevin in blounu yazmadm. ye deikenlerin tutarllklar iin aadaki bilgileri kullanmanz gerekir.

63.2

Nesnelerin geerlilii iin in ve out bloklar


Yukardaki ye ilev parametre almad iin in blounu yazmadk. levdeki hesab da nesnenin yelerini kullanarak yaptk. Yani bir anlamda yelerin geerli deerlere sahip olduklarn varsaydk. Bu varsaymn doru olmasn salamann bir yolu, snfn kurucu ilevine in blou eklemektir. Bylece kurucunun ald parametrelerin geerli olduklarn en bandan, daha nesne kurulmadan denetleyebiliriz. in blounu da yazabilmek iin, kurucu ilevi aka bizim tanmlamamz gerekir; derleyicinin otomatik olarak sunduu kurucuyu kullanamayz: class gen { // ... this(in double a, in double b, in double c) in { // Kenarlar sfrdan kk olamaz assert(a >= 0); assert(b >= 0); assert(c >= 0); // Hibir kenar dier ikisinin toplamndan 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 geersiz deerlerle oluturulmalar en bandan engellenmi olur. Artk programn geersiz deerlerle kurulmu olan bir gen nesnesi kullanmas olanakszdr:

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

358

Yap ve Snflarda Szlemeli Programlama

auto eksiKenarUzunluklu = gen(-1, 1, 1); auto birKenarFazlaUzun = gen(1, 1, 10); Kurucu ilevin in blou, yukardaki geersiz nesnelerin oluturulmalarna izin vermez: core.exception.AssertError@deneme.d: Assertion failure Bu sefer de out blounu yazmadma dikkat edin. Eer gerekirse, daha karmak trlerde kurucu ilevin out blou da yazlabilir. O da nesnenin yeleri kurulduktan sonra gerekebilecek denetimler iin kullanlabilir.

63.3

Nesnelerin tutarll iin invariant blou


Kurucuya eklenen in ve out bloklar, nesnenin yaamnn geerli deerlerle balayacan; yelere eklenen in ve out bloklar da ilevlerin doru ekilde alacaklarn garanti eder. Ancak bu denetimler, nesnenin yelerinin her zaman iin geerli veya tutarl olacaklarn garanti etmeye elverili deillerdir. Nesnenin yeleri, ye ilevler iinde programc hatalar sonucunda tutarsz deerler edinebilirler. Nesnenin tutarlln tarif eden koullara "mutlak deimez" denir. rnein bir mteri takip snfnda her siparie karlk bir fatura bulunacan varsayarsak; fatura adedinin sipari adedinden fazla olamayaca, bu snfn bir mutlak deimezidir. Eer bu koulun geerli olmad bir mteri takip nesnesi varsa, o nesnenin tutarl durumda olduunu syleyemeyiz. Bunun bir rnei olarak Sarma ve Eriim Haklar dersinde kullandmz Okul snfn ele alalm: class Okul { private: renci[] renciler; int kzToplam; int erkekToplam; // ... } Bu snftan olan nesnelerin tutarl olarak kabul edilmeleri iin, yesi arasndaki bir mutlak deimezin salanmas gerekir. renci dizisinin uzunluu, her zaman iin kz rencilerin toplam ile erkek rencilerin toplamna eit olmaldr: assert(renciler.length == kzToplam + erkekToplam); O koulun bozulmu olmas, bu snf kodlarnda yaplan bir hatann gstergesidir. Yap ve snf nesnelerinin tutarllklar, o trn invariant blounda denetlenir. Bu blok, yap veya snf tanm iine yazlr ve snf nesnelerinin tutarllk koullarn ierir. in ve out bloklarnda olduu gibi, burada da assert ifadeleri kullanlr: class Okul { private: renci[] renciler; int kzToplam; int erkekToplam;

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

359

Yap ve Snflarda Szlemeli Programlama

invariant() { assert( renciler.length == kzToplam + erkekToplam); } // ... } Yap ve snf tanmlarnda yalnzca bir tane invariant blou bulunabilir. invariant bloklarndaki kodlar aadaki zamanlarda otomatik olarak iletilir, ve bu sayede programn yanl verilerle devam etmesi nlenmi olur: kurucu ilev sonunda; bylece nesnenin yaamna tutarl olarak balad garanti edilir sonlandrc ilev arlmadan nce; bylece sonlandrma ilemlerinin tutarl yeler zerinde yaplacaklar garanti edilir public bir ilevden nce ve sonra; bylece ye ilevlerdeki kodlarn nesneyi bozmadklar garanti edilir Not: Yukardaki listede public ilevler iin sylenenler export ilevler iin de geerlidir. export ilevleri ksaca "dinamik ktphalerin sunduklar ilevler" olarak tanmlayacam, fakat ayrntlarna burada girmeyeceim. invariant bloundaki denetimlerin baarsz olmalar da in ve out bloklarnda olduu gibi AssertError atlmasna neden olur. Bu sayede programn tutarsz nesnelerle devam etmesi nlenmi olur. in ve out bloklarnda olduu gibi, invariant bloklar da -release seenei ile iptal edilebilir: dmd deneme.d -w -release

63.4

zet
in ve out bloklarn ye ilevlerle de kullanabilirsiniz; kurucu ileve ekleyerek nesnelerin geersiz parametrelerle kurulmalarn nleyebilirsiniz. Nesnelerin yaamlar boyunca her zaman iin tutarl olmalarn garantilemek iin invariant bloklarn kullanabilirsiniz.

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

360

ablonlar

64

ablonlar
ablonlar, derleyicinin belirli bir kalba uygun olarak kod retmesini salayan olanaktr. Herhangi bir kod parasnn baz blmleri sonraya braklabilir; ve derleyici o kod blmlerini gereken trler, deerler, vs. iin kendisi oluturur. ablonlar algoritmalarn ve veri yaplarnn trden bamsz olarak yazlabilmelerini salarlar; ve bu yzden zellikle ktphanelerde ok yararldrlar. D'nin ablon olana baz baka dillerdekilerle karlatrldnda ok gl ve ok kapsamldr. Bu yzden ablonlarn btn ayrntlarna bu derste giremeyeceim. Burada; gndelik kullanmda en ok karlalan ilev, yap, ve snf ablonlarnn trlerle nasl kullanldklarn gstereceim. ablonlarn yararlarn anlamak iin, kendisine verilen deeri parantez iinde yazdran basit bir ileve bakalm: void parantezliYazdr(int deer) { writefln("(%s)", deer); } Parametresi int olarak tanmland iin, o ilev yalnzca int tryle veya otomatik olarak int 'e dnebilen trlerle kullanlabilir. Derleyici, rnein kesirli say trleriyle arlmasna izin vermez. O ilevi kullanan programn gelitiini, ve artk baka trlerden olan deerleri de parantez iinde yazdrmaya ihtiya duyulduunu dnelim. Bunun iin bir zm, D'nin ilev ykleme olanadr; ayn ilevi rnein double iin de tanmlayabiliriz: void parantezliYazdr(double deer) { writefln("(%s)", deer); } Bu da ancak belirli bir noktaya kadar yeterlidir; nk bu ilevi bu sefer de rnein real tryle, veya kendi tanmlam olabileceimiz baka trlerle kullanamayz. Tabii ilevi o trler iin de yklemeyi dnebiliriz, ama her tr iin ayr ayr yazlmasnn ok klfetli olaca aktr. Burada dikkatinizi ekmek istediim nokta; tr ne olursa olsun, ilevin ieriinin hep ayn olduudur. Trler iin yklenen bu ilevdeki ilemler, trden bamsz olarak hepsinde ayndr. Benzer durumlar zellikle algoritmalarda ve veri yaplarnda karmza karlar. rnein ikili arama algoritmas trden bamszdr: o algoritma, yalnzca ilemlerle ilgilidir. Ayn ekilde, rnein bal liste veri yaps da trden bamszdr: yalnzca topluluktaki elemanlarn nasl bir arada tutulduklarn belirler. te ablonlar bu gibi durumlarda yararldr: kod, bir kalp halinde tarif edilir; ve derleyici, programda kullanlan trler iin kodu gerektike kendisi retir.

64.1

lev ablonlar
levi bir kalp olarak tarif etmek, iinde kullanlan bir veya daha fazla trn belirsiz olarak sonraya braklmas anlamna gelir.

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

361

ablonlar

levdeki hangi trlerin sonraya brakldklar, ilev parametrelerinden hemen nce yazlan ablon parametrelerinde belirtilir. Bu yzden ilev ablonlarnda iki adet parametre parantezi bulunur; birincisi ablon parametreleridir, ikincisi de ilev parametreleri: void parantezliYazdr(T)(T deer) { writefln("(%s)", deer); } Yukarda ablon parametresi olarak kullanlan T , "bu ilevde T yazdmz yerlerde asl hangi trn kullanlacana derleyici gerektike kendisi karar versin" anlamndadr. T yerine herhangi baka bir isim de yazlabilir; ancak, "type"n ba harfi olduu iin T harfi geleneklemitir. "Tr"n ba harfine de uyduu iin aksine bir neden olmad srece T kullanmak yerinde olacaktr. O ablonu yle bir kere yazm olmak; kendi trlerimiz de dahil olmak zere, onu eitli trlerle arma olana salar: import std.stdio; void parantezliYazdr(T)(T deer) { writefln("(%s)", deer); } void main() { parantezliYazdr(42); parantezliYazdr(1.2); auto birDeer = BirYap(); parantezliYazdr(birDeer);

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

struct BirYap { string toString() const { return "merhaba"; } } Derleyici; programdaki kullanmlarna bakarak, yukardaki ilev ablonunu gereken her tr iin ayr ayr retir. Program, sanki o ilev T 'nin kullanld farkl tr iin, yani int , double , ve BirYap iin ayr ayr yazlm gibi derlenir: /* * Not: Bu ilevlerin hibirisi programa dahil deildir. * Derleyicinin kendi rettii ilevlerin edeerleri * olarak gsteriyorum. */ void parantezliYazdr(int deer) { writefln("(%s)", deer); } void parantezliYazdr(double deer) { writefln("(%s)", deer); } void parantezliYazdr(BirYap deer) {

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

362

ablonlar

writefln("(%s)", deer);

Programn kts da o farkl ilevin etkisini gsterecek ekilde, her tr iin farkldr: (42) (1.2) (merhaba)

64.2

Birden fazla ablon parametresi kullanlabilir


Ayn ilevi, ama ve kapama parantezlerini de kullancdan alacak ekilde deitirdiimizi dnelim: void parantezliYazdr(T)(T deer, char ama, char kapama) { writeln(ama, deer, kapama); } Artk o ilevi, istediimiz parantez karakterleri ile arabiliriz: parantezliYazdr(42, '<', '>'); Parantezleri belirleyebiliyor olmak, ilevin kullanlln arttrm olsa da; parantezlerin trnn char olarak sabitlenmi olmalar, ilevin kullanlln tr asndan drmtr. levi rnein ancak wchar ile ifade edilebilen Unicode karakterleri arasnda yazdrmaya alsak, wchar 'n char 'a dntrlemeyecei ile ilgili bir derleme hatas alrz: parantezliYazdr(42, '', ''); // derleme HATASI

Error: template deneme.parantezliYazdr(T) cannot deduce template function from argument types !()(int,wchar,wchar) Bunun bir zm; parantez karakterlerini, her karakteri ifade edebilen dchar olarak tanmlamaktr. Bu da yetersiz olacaktr, nk bu sefer de rnein string ile veya kendi zel trlerimizle kullanlamaz. Baka bir zm, yine ablon olanandan yararlanmak ve parantezin trn de derleyiciye brakmaktr. Yapmamz gereken, ilev parametresi olarak char yerine yeni bir ablon parametresi kullanmak; ve onu da ablon parametre listesinde belirtmektir: void parantezliYazdr(T, ParantezTr)(T deer, ParantezTr ama, ParantezTr kapama) { writeln(ama, deer, kapama); } Yeni ablon parametresinin anlam da T 'ninki gibidir: "bu ilev tanmnda ParantezTr geen yerlerde, aslnda hangi tr gerekiyorsa o kullanlsn". Artk parantez olarak herhangi bir tr kullanlabilir. rnein wchar ve string trleriyle: parantezliYazdr(42, '', ''); parantezliYazdr(1.2, "-=", "=-");

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

363

ablonlar

42 -=1.2=Bu ablonun yarar; tek bir ilev tanmlam olduumuz halde T ve ParantezTr ablon parametrelerinin otomatik olarak belirlenebilmeleridir.

64.3

Tr karsama
Derleyici yukardaki iki kullanmda u trleri otomatik olarak seer: 42'nin yazdrld satrda int ve wchar 1.2'nin yazdrld satrda double ve string levin arld noktalarda hangi trlerin gerektii, ilevin parametrelerinden kolayca anlalabilmektedir. Derleyicinin tr ilev arlrken kullanlan parametrelerden anlamasna tr karsamas denir. Derleyici, ablon parametrelerini ancak ve ancak ilev arlrken kullanlan trlerden karsayabilir.

64.4

Trn aka belirtilmesi


Baz durumlarda ise ablon parametreleri karsanamazlar, nk rnein ilevin parametresi olarak gemiyorlardr. Onlarn derleyici tarafndan karsanmalar olanakszdr. rnek olarak kullancya bir soru soran ve o soru karlnda giriten bir deer okuyan bir ilev dnelim. Bu ilev, okuduu deeri dndryor olsun. Ayrca, btn trler iin kullanlabilmesi iin de dn trn sabitlemeyelim ve bir ablon parametresi olarak tanmlayalm: T giritenOku(T)(string soru) { writef("%s (%s): ", soru, T.stringof); T cevap; readf(" %s", &cevap); } return cevap;

O ilev, giriten okuma iini trden bamsz olarak gerekletirdii iin programda ok yararl olacaktr. rnein kullanc bilgilerini edinmek iin u ekilde armay dnebiliriz: giritenOku("Yanz?"); Ancak, o arma srasnda T 'nin hangi trden olacan belirten hibir ipucu yoktur. Soru ileve string olarak gitmektedir, ama derleyici dn tr iin hangi tr istediimizi bilemez ve T 'yi karsayamadn bildiren bir hata verir: Error: template deneme.giritenOku(T) cannot deduce template function from argument types !()(string) Bu gibi durumlarda ablon parametrelerinin ne olduklar programc tarafndan aka belirtilmek zorundadr. ablonun hangi trlerle retilecei, yani ablon parametreleri, ilev isminden sonraki nlem iareti ve hemen ardndan gelen ablon parametre listesi ile bildirilir:

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

364

ablonlar

giritenOku!(int)("Yanz?"); O kod artk derlenir ve yukardaki ablon, T yerine int yazlm gibi derlenir. Tek bir ablon parametresinin belirtildii durumlarda, bir kolaylk olarak ablon parantezleri yazlmayabilir: giritenOku!int("Yanz?"); // sttekiyle ayn ey

O yazl imdiye kadar ok kullandmz to!string 'den tanyorsunuz. to bir ilev ablonudur. Ona verdiimiz deerin hangi tre dntrleceini bir ablon parametresi olarak alr. Tek bir ablon parametresi gerektii iin de to!(string) yerine, onun ksas olan to!string yazlr.

64.5

ablon zellemeleri
giritenOku ilevini baka trlerle de kullanabiliriz. Ancak, derleyicinin rettii kod her tr iin geerli olmayabilir. rnein iki boyutlu dzlemdeki bir noktay ifade eden bir yapmz olsun: struct Nokta { int x; int y; } Her ne kadar yasal olarak derlenebilse de, giritenOku ablonunu bu yap ile kullanrsak, ablon iindeki readf ilevi doru almaz. ablon iinde Nokta trne karlk olarak retilen kod yle olacaktr: Nokta cevap; readf(" %s", &cevap);

// YANLI

Dorusu, Nokta 'y oluturacak olan x ve y deerlerinin giriten ayr ayr okunmalar ve nesnenin bu deerlerle kurulmasdr. Byle durumlarda, ablonun belirli bir tr iin zel olarak tanmlanmasna zelleme denir. zelleme, ablon parametre listesinde, hangi tr iin zellendii : karakterinden sonra yazlarak belirtilir: T giritenOku(T : Nokta)(string soru) { writefln("%s (Nokta)", soru); auto x = giritenOku!int(" auto y = giritenOku!int(" } return Nokta(x, y); x"); y");

giritenOku ilevi bir Nokta iin arldnda, derleyici o zel tanm kullanr: auto merkez = giritenOku!Nokta("Merkez?"); O ilev de kendi iinde giritenOku!int 'i iki kere ararak x ve y deerlerini ayr ayr okur:

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

365

ablonlar

Merkez? (Nokta) x (int): 11 y (int): 22 Baka bir rnek olarak, ablonu string ile kullanmay da dnebiliriz. Ne yazk ki ablonun genel tanm, bu durumda dizginin giriin sonuna kadar okunmasna neden olur: // btn girii okur: auto isim = giritenOku!string("sminiz?"); Eer string 'lerin tek satr olarak okunmalarnn uygun olduunu kabul edersek, bu durumda da zm ablonu string iin zel olarak tanmlamaktr: T giritenOku(T : string)(string soru) { writef("%s (string): ", soru); return chomp(readln()); }

64.6

Yap ve snf ablonlar


Yukardaki Nokta snfnn iki yesi int olarak tanmlanm olduklar iin, ilev ablonlarnda karlatmz yetersizlikler onda da vardr. Nokta yapsnn daha kapsaml olduunu dnelim. rnein kendisine verilen baka bir noktaya olan uzakln hesaplayabilsin: import std.math; // ... struct Nokta { int x; int y; int uzaklk(in Nokta dierNokta) const { const real xFark = x - dierNokta.x; const real yFark = y - dierNokta.y; const auto uzaklk = sqrt((xFark * xFark) + (yFark * yFark)); } return cast(int)uzaklk;

O yap, rnein kilometre duyarlndaki uygulamalarda yeterlidir: auto merkez = giritenOku!Nokta("Merkez?"); auto ube = giritenOku!Nokta("ube?"); writeln("Uzaklk: ", merkez.uzaklk(ube)); Ancak, kesirli deerler gerektiren daha hassas uygulamalarda kullanszdr. Yap ve snf ablonlar, onlar da belirli bir kalba uygun olarak tanmlama olana salarlar. Bu durumda yapnn tanmndaki int 'ler yerine T kullanmak, bu tanmn bir ablon haline gelmesi ve yelerin trlerinin derleyici tarafndan belirlenmesi iin yeterlidir:

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

366

ablonlar

struct Nokta(T) { T x; T y; T uzaklk(in Nokta dierNokta) const { const real xFark = x - dierNokta.x; const real yFark = y - dierNokta.y; const auto uzaklk = sqrt((xFark * xFark) + (yFark * yFark)); } return cast(T)uzaklk;

Yap ve snflar ilev olmadklarndan, arlmalar sz konusu deildir. Bu yzden, derleyicinin ablon parametrelerini karsamas olanakszdr; trleri aka belirtmemiz gerekir: auto merkez = Nokta!int(0, 0); auto ube = Nokta!int(100, 100); writeln("Uzaklk: ", merkez.uzaklk(ube)); Yukardaki kullanm, derleyicinin Nokta ablonunu T yerine int gelecek ekilde retmesini salar. Doal olarak, baka trler de kullanabiliriz. rnein virglden sonrasnn nemli olduu bir uygulamada: auto nokta1 = Nokta!double(1.2, 3.4); auto nokta2 = Nokta!double(5.6, 7.8); writeln(nokta1.uzaklk(nokta2)); Yap ve snf ablonlar, veri yaplarn byle trden bamsz olarak tanmlama olana sunar. Dikkat ederseniz, Nokta ablonundaki yeler ve ilemler tamamen T 'nin asl trnden bamsz olarak yazlmlardr. Nokta yapsn bir yap ablonu olarak yazmak, giritenOku ilev ablonunun daha nce yazm olduumuz zellemesinde bir sorun oluturur: T giritenOku(T : Nokta)(string soru) { writefln("%s (Nokta)", soru); auto x = giritenOku!int(" auto y = giritenOku!int(" } return Nokta(x, y); x"); y"); // derleme HATASI

Hatann nedeni, artk Nokta diye bir tr bulunmamasdr. Nokta ; artk bir tr deil, bir yap ablonudur. Bir tr olarak kabul edilebilmesi iin, mutlaka ablon parametresinin de belirtilmesi gerekir. giritenOku ilev ablonunu btn Nokta kullanmlar iin zellemek iin aadaki deiiklikleri yapabiliriz. Aklamalarn koddan sonra yapacam: Nokta!T giritenOku(T : Nokta!T)(string soru) { writefln("%s (Nokta!%s)", soru, T.stringof); auto x = giritenOku!T(" x"); // 2, 1 // 5 // 3a

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

367

ablonlar

auto y = giritenOku!T(" } return Nokta!T(x, y);

y");

// 3b // 4

1. bu ilev ablonu zellemesinin Nokta 'nn btn kullanmlarn desteklemesi iin, ablon parametre listesinde Nokta!T yazlmas gerekir; bir anlamda, T ne olursa olsun, bu zellemenin Nokta!T trleri iin olduu belirtilmektedir 2. okuduumuz tre uymas iin, dn trnn de Nokta!T olarak belirtilmesi gerekir 3. bu ilevin nceki tanmnda olduu gibi giritenOku!int 'i aramayz; nk Nokta 'nn yeleri herhangi bir tr olabilir; bu yzden, T ne ise, giritenOku ablonunu o trden deer okuyacak ekilde, yani giritenOku!T eklinde armamz gerekir 4. 1 ve 2 numaral maddelere benzer ekilde, dndrdmz deer de bir Nokta!T olmak zorundadr 5. okumakta olduumuz trn "(Nokta)" yerine rnein "(Nokta!double)" olarak bildirilmesi iin ablon trnn ismini T.stringof 'tan ediniyoruz

64.7

Varsaylan ablon parametreleri


ablonlarn getirdii bu esneklik ok kullanl olsa da, ablon parametrelerinin her sefer belirtilmeleri bazen gereksiz olabilir. rnein giritenOku ilev ablonu programda hemen hemen her yerde int ile kullanlyordur. Ek olarak, belki de yalnzca bir ka noktada rnein double ile de kullanlyordur. Byle durumlarda ablon parametrelerine varsaylan trler verilebilir ve aka belirtilmediinde o trler kullanlr. Varsaylan ablon parametre trleri = karakterinden sonra belirtilir: T giritenOku(T = int)(string soru) { // ... } // ... auto ya = giritenOku("Yanz?"); Yukardaki ilev arsnda ablon parametresi belirtilmedii iin int varsaylr ve o ar giritenOku!int ile ayndr. Varsaylan ablon parametreleri yalnzca ilev ablonlaryla kullanlabilirler. Yap ve snf ablonlarnda parametre deerlerinin aka belirtilmesi arttr. Parametre Serbestlii dersinde ilev parametreleri iin anlatlana benzer ekilde, varsaylan ablon parametreleri ya btn parametreler iin, ya da yalnzca sondaki parametreler iin belirtilebilir: void birablon(T0, T1 = int, T2 = char)() { // ... } O ablonun son iki parametresinin belirtilmesi gerekmez, ama birincisi arttr: birablon!(string); O kullanmda ikinci parametre int , nc parametre de char olur.

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

368

ablonlar

64.8

Her ablon gerekletirmesi farkl bir trdr


Bir ablonun belirli bir tr veya trler iin retilmesi yepyeni bir tr oluturur. rnein Nokta!int balbana bir trdr. Ayn ekilde, Nokta!double da balbana bir trdr. Bu trler birbirlerinden farkldrlar: Nokta!int nokta3 = Nokta!double(0.25, 0.75); // derleme HATASI Trlerin uyumsuz olduklarn gsteren bir derleme hatas alnr: Error: cannot implicitly convert expression (Nokta(0.25,0.75)) of type Nokta!(double) to Nokta!(int)

64.9

Derleme zaman olanadr


ablon olana btnyle derleme zamannda ileyen ve derleyici tarafndan iletilen bir olanaktr. Derleyicinin kod retmesiyle ilgili olduu iin, program almaya baladnda ablonlarn koda evrilmeleri ve derlenmeleri oktan tamamlanmtr.

64.10

Snf ablonu rnei: yn veri yaps


Yap ve snf ablonlar veri yaplarnda ok kullanlrlar. Bunun bir rneini grmek iin bir yn topluluu (stack container) tanmlayalm. Yn topluluu, veri yaplarnn en basit olanlarndandr: Elemanlarn st ste durduklar dnlr. Eklenen her eleman, en ste yerletirilir ve yalnzca bu stteki elemana eriilebilir. Topluluktan eleman kartlmak istendiinde, yalnzca en stteki eleman kartlabilir. Kullanl olsun diye topluluktaki eleman saysn veren bir nitelik de tasarlarsak, bu basit veri yapsnn ilemlerini yle sralayabiliriz: eleman eklemek eleman kartmak sttekine eritirmek eleman adedini bildirmek

Bu veri yapsn gerekletirmek iin D'nin i olanaklarndan olan dizilerden yararlanabiliriz. Dizinin sonuncu eleman, yn topluluunun stteki eleman olarak kullanlabilir. Dizi eleman trn de sabit bir tr olarak yazmak yerine ablon parametresi olarak belirlersek, bu veri yapsn her trle kullanabilecek ekilde yle tanmlayabiliriz: class Yn(T) { T[] elemanlar_; public: void ekle(T eleman) { elemanlar_ ~= eleman; } void kart() { --elemanlar_.length;

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

369

ablonlar

} @property T stteki() const { return elemanlar_[$ - 1]; } @property int uzunluk() const { return elemanlar_.length; }

Ekleme ve kartma ilemlerinin ye ilevler olmalar doaldr. stteki ve uzunluk ilevlerini ise nitelik olarak tanmlamay daha uygun buldum. nk ikisi de bu veri yapsyla ilgili basit bir bilgi sunuyorlar. Bu snf iin bir unittest blou tanmlayarak beklediimiz ekilde altndan emin olabiliriz: unittest { auto yn = new Yn!int; // Eklenen eleman stte grnmeli yn.ekle(42); assert(yn.stteki == 42); assert(yn.uzunluk == 1); /* * Ayn ilemler tekrarlanabilmeli ve veri yapsnda bir * deiiklie yol amamal: */ assert(yn.stteki == 42); assert(yn.uzunluk == 1); // Yeni eklenen eleman stte grnmeli yn.ekle(100); assert(yn.stteki == 100); assert(yn.uzunluk == 2); // Eleman kartlnca nceki grnmeli yn.kart(); assert(yn.stteki == 42); assert(yn.uzunluk == 1); // Tek eleman kartlnca bo kalmal yn.kart(); assert(yn.uzunluk == 0);

Bu veri yapsn bir ablon olarak tanmlam olmann yararn grmek iin, onu kendi trlerimizle de kullanmay deneyebiliriz. rnein yukardaki Nokta ablonunun bir benzeri olsa: struct Nokta(T) { T x; T y; string toString() const { return format("(%s,%s)", x, y); }

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

370

ablonlar

double trnde yeleri bulunan Nokta 'lar ieren bir Yn ablonu yle oluturulabilir: auto noktalar = new Yn!(Nokta!double); Bu veri yapsna on tane rasgele deerli nokta ekleyen, ve sonra onlar teker teker kartan bir deneme program yle yazlabilir: import std.string; import std.stdio; import std.random; struct Nokta(T) { T x; T y; string toString() const { return format("(%s,%s)", x, y); }

// -0.50 ile 0.50 arasnda rasgele bir deer dndrr double rasgele_double() out (sonu) { assert((sonu >= -0.50) && (sonu < 0.50)); } body { return (cast(double)uniform(0, 100) - 50) / 100; } // Belirtilen sayda rasgele Nokta!double ieren bir Yn // dndrr Yn!(Nokta!double) rasgeleNoktalar(int adet) in { assert(adet >= 0); } out (sonu) { assert(sonu.uzunluk == adet); } body { auto noktalar = new Yn!(Nokta!double); foreach (i; 0 .. adet) { const auto nokta = Nokta!double(rasgele_double(), rasgele_double()); writeln("ekliyorum : ", nokta); noktalar.ekle(nokta); } } return noktalar;

void main() { auto ststeNoktalar = rasgeleNoktalar(10); while (ststeNoktalar.uzunluk) { writeln("kartyorum: ", ststeNoktalar.stteki); ststeNoktalar.kart(); }

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

371

ablonlar

Programn ktsndan anlalaca gibi, eklenenlerle kartlanlar ters srada olmaktadr: ekliyorum : ekliyorum : ekliyorum : ekliyorum : ekliyorum : ekliyorum : ekliyorum : ekliyorum : ekliyorum : ekliyorum : kartyorum: kartyorum: kartyorum: kartyorum: kartyorum: kartyorum: kartyorum: kartyorum: kartyorum: kartyorum: (0.02,0.1) (0.23,-0.34) (0.47,0.39) (0.03,-0.05) (0.01,-0.47) (-0.25,0.02) (0.39,0.35) (0.32,0.31) (0.02,-0.27) (0.25,0.24) (0.25,0.24) (0.02,-0.27) (0.32,0.31) (0.39,0.35) (-0.25,0.02) (0.01,-0.47) (0.03,-0.05) (0.47,0.39) (0.23,-0.34) (0.02,0.1)

64.11

lev ablonu rnei: ikili arama algoritmas


kili arama algoritmas, bir dizi halinde yan yana ve sral olarak bulunan deerler arasnda arama yapan en hzl algoritmadr. Bu algoritmann bir dier ad "ikiye blerek arama", ngilizcesi de "binary search"tr. ok basit bir algoritmadr: sral olarak bulunan deerlerin en ortadakine baklr. Eer aranan deere eitse, deer bulunmu demektir. Eer deilse, o orta deerin aranan deerden daha kk veya byk olmasna gre ya sol yarda ya da sa yarda ayn algoritma tekrarlanr. Byle kendisini tekrarlayarak tarif edilen algoritmalar zyinelemeli olarak da programlanabilirler. Ben bu ilevi yukardaki tanmna da ok uyduu iin kendisini aran bir ilev olarak yazacam. levi ablon olarak yazmak yerine, nce int iin gerekletireceim; ondan sonra algoritmada kullanlan int 'leri T yaparak onu bir ablona dntreceim. // Aranan deeri bulursa, deerin dizide hangi indekste // bulunduunu, bulamazsa -1 dndrr int ikiliAra(const int[] deerler, in int deer) { // Dizi bosa, bulamadk demektir if (deerler.length == 0) { return -1; } const auto ortaNokta = deerler.length / 2; if (deer == deerler[ortaNokta]) { // Bulduk return ortaNokta; } else if (deer < deerler[ortaNokta]) { // lk yarda aramaya devam etmeliyiz return ikiliAra(deerler[0 .. ortaNokta], deer);

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

372

ablonlar

} else { // Son yarda aramaya devam etmeliyiz return (ortaNokta + 1 + ikiliAra(deerler[ortaNokta + 1 .. $], deer)); } } assert(false, "Buraya hi gelinmemeliydi");

Yukardaki ilev, bu basit algoritmay u drt adm halinde gerekletiriyor: dizi bosa, bulamadmz bildirmek iin -1 dndr ortadaki deer aranan deere eitse ortadaki deerin indeksini dndr aranan deer ortadaki deerden nceyse, ayn ilevi soldaki yarda devam ettir deilse, ayn ilevi sadaki yarda devam ettir

O ilevi deneyen bir kod da yle yazlabilir: import std.stdio; import std.random; // ... unittest { int[] karkSaylar(in int adet) { int[] saylar; foreach (i; 0 .. adet) { saylar ~= i; } randomShuffle(saylar); } return saylar;

void dene(in int adet) { int[] saylar = karkSaylar(adet * 4); int aranacak = saylar[0]; saylar = saylar[0 .. adet].sort; const auto indeks = ikiliAra(saylar, aranacak); if (indeks == -1) { writefln( "Hata! %s deeri %s dizisinde bulunamad", aranacak, saylar); assert(false); } if (saylar[indeks] != aranacak) { writefln( "Hata! %s indeksinde %s deil, %s var: %s", indeks, aranacak, saylar[indeks], saylar); assert(false); }

foreach (i; 0 .. 1000) { const auto adet = uniform(1, 100); dene(adet); }

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

373

ablonlar

O kodda std.random modlnde tanmlanm olan ve ismi "rasgele kartr"dan gelen randomShuffle ilevinden de yararlandm. O ilevi int iin yazp doru altndan emin olduktan sonra, artk bir ablon haline getirebiliriz. Dikkat ederseniz, ilevin tanmnda yalnzca yerde int geiyor: int ikiliAra(const int[] deerler, in int deer) { // ... burada hi int bulunmuyor ... } Dn deeri olan int , bir indeks bilgisi olduu iin onun yle kalmasn isteriz. Parametrelerde geen int 'ler ise bu ilevin kullanlabildii deerlerin trn belirliyor. Onlar ablon parametreleri olarak tanmlamak, bu ilevin bir ablon haline gelmesi ve dolaysyla baka trlerle de kullanlabilmesi iin yeterlidir: int ikiliAra(T)(const T[] deerler, in T deer) { // ... } O ilevi, iindeki kullanmlara uyan her trle kullanabiliriz. lev ierisinde, elemanlar yalnzca == ve < ileleriyle kullanlyorlar: if (deer == deerler[ortaNokta]) { // ... } else if (deer < deerler[ortaNokta]) { // ... O yzden, yukarda tanmladmz Nokta ablonu bu trle kullanlmak iin henz hazr deildir: struct Nokta(T) { T x; T y; string toString() const { return format("(%s,%s)", x, y); }

void main() { Nokta!int[] noktalar; foreach (i; 0 .. 15) { noktalar ~= Nokta!int(i, i); } } assert(ikiliAra(noktalar, Nokta!int(10, 10)) == 10);

Bir derleme hatas alrz: Error: need member function opCmp() for struct const(Nokta!(int)) to compare O hata, Nokta!int 'in bir karlatrma ileminde kullanlabilmesi iin opCmp ilevinin tanmlanm olmas gerektiini bildirir. le Ykleme dersinde gsterildii ekilde bir opCmp

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

374

ablonlar

tanmlamak, programn derlenmesini ve ikili arama ilevinin Nokta ablonu iin de kullanlabilmesini salar: struct Nokta(T) { // ... int opCmp(const ref Nokta sadaki) const { return (x == sadaki.x ? y - sadaki.y : x - sadaki.x); }

64.12

zet
ablonlar bu derste gsterdiklerimden ok daha kapsamldr. Devamn sonraya brakarak bu dersin zeti: ablonlar, kodun kalp halinde tarif edilmesini ve derleyici tarafndan her tr iin retilmesini salayan olanaktr ablonlar btnyle derleme zamannda ileyen bir olanaktr tanmlarken isimlerinden sonra ablon parametresi de belirtmek; ilevlerin, yaplarn, ve snflarn ablon haline gelmeleri iin yeterlidir void ilevablonu(T)(T ilevParametresi) { // ... } class Snfablonu(T) { // ... } ablon parametreleri nlem iaretinden sonra aka belirtilebilirler; tek parametre iin parantez kullanmaya gerek yoktur auto nesne1 = new Snfablonu!(double); auto nesne2 = new Snfablonu!double; ablonun farkl trlerle her kullanm farkl bir trdr assert(typeid(Snfablonu!int) != typeid(Snfablonu!uint)); ablon parametreleri yalnzca ilev ablonlarnda karsanabilirler ilevablonu(42); // ilevablonu!int arlr // ayn ey

ablonlar, : karakterinden sonra belirtilen tr iin zellenebilirler class Snfablonu(T : dchar) { // ... } ilev ablonlarnda varsaylan ablon parametre trleri = karakterinden sonra belirtilebilir

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

375

ablonlar

void ilevablonu(T = long)(T ilevParametresi) { // ... }

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

376

alias ve alias this

65

alias ve alias this


Not: alias ve alias this ayr blmlerde anlatlacak kadar bamsz olanaklardr. Yine de ileride daha uygun bir ders olmadn bildiimden, isim benzerlii nedeniyle ve ok ksa olduu iin alias this 'i de burada anlatmaya karar verdim.

65.1

alias
alias anahtar szc, programda geen bir isme yeni bir takma isim vermek iin kullanlr. Takma isimler vermenin eitli yararlar vardr.

65.1.1

Uzun bir ismi ksaltmak


nceki derste grdmz ablonlarda olduu gibi, programda geen baz isimler kullansz derecede uzun olabilirler. Daha nce tanmladmz u ilevi hatrlayalm: Yn!(Nokta!double) rasgeleNoktalar(int adet) { auto noktalar = new Yn!(Nokta!double); } // ...

Programda aka Yn!(Nokta!double) yazmann bir ka sakncas grlebilir: okumay gletirecek derecede karmaktr onun bir yn veri yaps olduunun ve elemanlarnn Nokta ablonunun double tr ile kullanlmalarndan olutuunun her noktada grlmesi, gereksiz bir bilgi olarak kabul edilebilir programn ihtiyalarnn gelimesi durumunda rnein double yerine artk real kullanlmas gerektiinde, veya yn veri yaps yerine bir ikili aa veri yaps gerektiinde; trn aka yazld her yerde deiiklik yaplmas gerekecektir Bu sakncalar, Yn!(Nokta!double) ismine tek bir yerde yeni bir isim vererek giderilebilir: alias Yn!(Nokta!double) Noktalar; // ... Noktalar rasgeleNoktalar(int adet) { auto noktalar = new Noktalar; } // ...

Bir adm daha ileri giderek yukardaki alias ' iki para halinde de tanmlayabiliriz: alias Nokta!double HassasNokta; alias Yn!HassasNokta Noktalar; alias 'n sz dizimi yledir: alias var_olan_isim takma_isim; Takma isim ve daha nceden var olan isim birbirinin edeeridir ve ayn ekilde kullanlr.

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

377

alias ve alias this

Trlerin isimlerini modlleriyle birlikte uzun uzun yazmak yerine de alias 'tan yararlanabiliriz. rnein okul ve firma isimli iki modlde Mdr isminde iki farkl tr tanml olduunu varsayalm. Bu iki modln de programa eklendikleri bir durumda yalnzca Mdr yazldnda program derlenemez: import okul; import firma; // ... Mdr kii; // derleme HATASI

Derleyici hangisini kasdettiimizi anlayamaz: Error: okul.Mdr at [...]/okul.d(1) conflicts with firma.Mdr at [...]/firma.d(1) Bunun nne gemek iin, programda kullanmak istediimiz Mdr 'e bir takma isim verebiliriz. Bylece her seferinde uzun uzun rnein okul.Mdr yazmak zorunda kalmadan birden fazla yerde kullanabiliriz: import std.stdio; alias okul.Mdr OkulMdr; void main() { OkulMdr kii; // ... } OkulMdr bakaKii;

65.1.2

Tasarm esneklii
Baz durumlarda, her ne kadar ileride deimeyecek olduundan emin bile olunsa, tasarmn esnek olmas iin int gibi temel trlere bile anlaml yeni isimler verilebilir: alias int MteriNumaras; alias string irketsmi; // ... struct Mteri { MteriNumaras numara; irketsmi irket; // ... } Her ne kadar srasyla int 'in ve string 'in ayns olsalar da, eer o yapnn kullanclar her zaman iin MteriNumaras ve irketsmi yazarlarsa, yap tanmnda int veya string yerine baka bir tr kullanldnda daha az satrda deiiklik gerekmi olur. Bu yntem, kodun anlalr olmasna da yardm eder. Bir deerin trnn int yerine MteriNumaras olmas, kod okunurken o deerin anlam konusunda hibir phe brakmaz. Baz durumlarda byle tr isimleri bir yap veya snfn iinde de tanmlanabilir. Bylece o yapnn veya snfn arayznde bu takma isimleriyle kullanlrlar. rnek olarak arlk niteliine sahip bir snfa bakalm:

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

378

alias ve alias this

class Kutu { private: double arlk_; public: @property double arlk() const { return arlk_; } } // ...

Bu snfn yesinin ve niteliinin aka double yazlarak tanmlanm olmas, onun kullanclarnn da arl double olarak kullanmalarn neden olacaktr: double toplamArlk = 0; foreach (kutu; kutular) { toplamArlk += kutu.arlk; } Bunun kart olarak, arln trnn snf iindeki bir alias ile tanmland dnrsek: class Kutu { private: Arlk arlk_; public: alias double Arlk; @property Arlk arlk() const { return arlk_; } } // ...

Kullanc kodu da dorudan double yazmak yerine, snfn arayzne bal kalarak Arlk yazacaktr: // ... Kutu.Arlk toplamArlk = 0; foreach (kutu; kutular) { toplamArlk += kutu.arlk; } Bu sayede, Kutu snfnn tasarmcs Arlk ' daha sonradan baka ekilde tanmlarsa, kodda deitirilmesi gereken yerlerin says da az olur.
65.1.3

st snfn gizlenen isimlerini alt snfta grnr yapmak


st snfta da ayn isimde bulunan isimler, alt snfta grnmezler. Alt snfta ayn isimde tek bir ilev bile bulunsa, st snfn ilevlerinin isimleri gizlenirler ve alt snf arayznde grnmezler:

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

379

alias ve alias this

class GenelHesap { void hesapla(int x) { // ... } } class zelHesap : GenelHesap { void hesapla() { // ... } } void main() { auto hesap = new zelHesap; hesap.hesapla(42); }

// derleme HATASI

O arda 42 deeri kullanld iin, zelHesap nesnesinin kaltm yoluyla edindii GenelHesap.hesapla ilevinin arlacan bekleyebiliriz. Oysa, her ne kadar parametre listeleri farkl olsa da zelHesap.hesapla ilevi, ayn isme sahip olduu iin GenelHesap.hesapla ilevini gizler ve program derlenmez. Not: Burada st snfn ilevini alt snfta deiik olarak yeniden tanmlamaktan bahsetmediimize dikkat edin. yle olsayd, Treme dersinde anlatld gibi, parametre listesini st snftakiyle ayn yapar ve override anahtar szcn kullanrdk. Burada; alt snfa eklenen yeni bir ilev isminin, st snftaki bir isimle ayn olduu durumla ilgileniyoruz. Derleyici, GenelHesap.hesapla 'y bu gizleme nedeniyle hi dikkate bile almad iin zelHesap.hesapla 'nn bir int ile arlamayacan belirten bir hata verir: Error: function deneme.zelHesap.hesapla () is not callable using argument types (int) Bunun geerli bir nedeni vardr: sim gizleme olmasa, ileride bu snflara eklenen veya onlardan kartlan hesapla ilevleri, hibir uyar verilmeden kodun istenenden farkl bir ilevi armasna neden olabilirler. sim gizleme, nesneye ynelik programlamay destekleyen baka dillerde de bulunan ve bu tr hatalar nleyen bir olanaktr. Gizlenen isimlerin alt snf arayznde grnmeleri de alias ile salanr: class GenelHesap { void hesapla(int x) { // ... } } class zelHesap : GenelHesap { void hesapla() { // ... } } alias GenelHesap.hesapla hesapla;

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

380

alias ve alias this

Yukardaki alias , st snftaki hesapla ismini alt snf arayzne getirir; ve bylece gizlenmesini nlemi olur. O eklemeden sonra kod artk derlenir, ve beklendii gibi st snfn hesapla ilevi arlr. Eer daha uygun olduu dnlrse, st snfn ilevi farkl bir isimle bile grnr hale getirilebilir: class GenelHesap { void hesapla(int x) { // ... } } class zelHesap : GenelHesap { void hesapla() { // ... } } alias GenelHesap.hesapla genelHesapla;

void main() { auto hesap = new zelHesap; hesap.genelHesapla(42); } sim gizleme ye deikenler iin de geerlidir. stendiinde onlarn alt snf arayznde grnmeleri de alias ile salanr. class stSnf { int ehir; } class AltSnf : stSnf { string ehir() const { return "Kayseri"; } } Her ne kadar birisi ye deiken, dieri ye ilev olsa da; alt snftaki ehir , st snfn ayn isimdeki yesini gizler ve bu yzden aadaki kod derlenemez: void main() { auto nesne = new AltSnf; nesne.ehir = 42; }

// derleme HATASI

st snfn ye deikeni alias ile alt snf arayzne getirildiinde kod artk derlenir. Yeni bir isimle getiren bir rnek: class stSnf { int ehir; }

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

381

alias ve alias this

class AltSnf : stSnf { string ehir() const { return "Kayseri"; } } alias stSnf.ehir ehirKodu;

void main() { auto nesne = new AltSnf; nesne.ehirKodu = 42; }

65.2

alias this
Baka balamlarda kendi zel anlamlar bulunan alias ve this anahtar szckleri, bir arada kullanldklarnda tamamen farkl bir anlamdadrlar. Bu yzden, ikisi bir arada kullanldnda tek bir anahtar szck olarak kabul edilmelidir. alias this , bir yapnn veya snfn otomatik tr dnm yoluyla baka bir tr yerine kullanlabilmesini salar. Bu iki szcn arasna yapnn veya snfn bir yesi yazlr: struct Para { long lira; long kuru; } alias lira this;

O tanm, Para nesnelerinin otomatik olarak lira 'nn tr olan long yerine kullanlabilmelerini salar: void main() { auto miktar = Para(10, 25); long kurusuz = miktar; // derlenir, ve otomatik olarak // long'a dnr assert(kurusuz == 10); } Yukardaki koddaki Para nesnesi; lira yesinin tr olan long yerine, ve onun deeri ile kullanlabilmitir. Bir yap veya snf tanmnda birden fazla alias this bulunamaz. Tr dnm iin baka bir seenek de opCast ilecidir.

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

382

Gstergeler

66

Gstergeler
Gstergeler baka deikenlere eriim salamak iin kullanlrlar. Deerleri, eriim saladklar deikenlerin adresleridir. Gstergeler her trden deikeni, nesneyi, ve hatta baka gstergeleri de gsterebilirler. Ben bu derste ksa olsun diye, bunlarn hepsinin yerine deiken szn kullanacam. Gstergeler mikro ilemcilerin en temel olanaklarndandr ve sistem programclnn nemli bir parasdr. D'nin gsterge kavram ve kullanm C'den gemitir. C renenlerin anlamakta en ok zorlandklar olanak gstergeler olduu halde, D'de gstergelerin ok daha kolay renileceini dnyorum. Bunun nedeni, gstergelerin amalarndan bazlarnn D'nin baka olanaklar tarafndan zaten karlanyor olmasdr. Bu yzden, hem bir ok durumda gsterge kullanlmas gerekmez, hem de baka D olanaklarnn zaten anlalm olmas, gstergelerin anlalmalarn da kolaylatrr. Bu blmde zellikle basit olarak setiim rnekler, gstergelerin kullanm amalarn anlatma konusunda yetersiz kalabilirler. Yazmlarn ve kullanmlarn renirken bunu gzard edebilirsiniz. En sonda vereceim rneklerin daha anlaml olacaklarn dnyorum. Ek olarak, rneklerde basite gsterge diye setiim isimlerin kullansz olduklarn aklnzda bulundurun. Kendi programlarnzda her ismi anlaml ve aklayc olarak semeye zen gsterin.

66.1

Referans kavram
Gstergelere gemeden nce, gstergelerin temel amac olan referans kavramn imdiye kadarki derslerden tandmz D olanaklar ile ksaca hatrlayalm.

66.1.1

foreach 'in ref deikenleri


foreach Dngs dersinde grdmz gibi, dng deikenleri normalde elemanlarn kopyalardr: import std.stdio; void main() { int[] dizi = [ 1, 11, 111 ]; foreach (say; dizi) { say = 0; // kopya deiir; asl eleman deimez } } writeln("Dngden sonra elemanlar: ", dizi);

Yukardaki dng iinde sfrlanmakta olan say deikeni, her seferinde dizi elemanlarndan birisinin kopyasdr. Onun deitirilmesi, dizideki asl eleman etkilemez: Dngden sonra elemanlar: 1 11 111 Dizideki elemanlarn kendilerinin deimeleri istendiinde foreach deikeni ref olarak tanmlanr:

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

383

Gstergeler

foreach (ref say; dizi) { say = 0; // asl eleman deiir } say , bu sefer dizideki asl elemann takma ismi gibi ilem grr ve dizideki asl elemanlar deiir: Dngden sonra elemanlar: 0 0 0

66.1.2

ref ilev parametreleri


lev Parametreleri dersinde grdmz gibi, deer trnden olan ilev parametreleri normalde baka deikenlerin kopyalardr: import std.stdio; void yarmEkle(double deer) { deer += 0.5; // main'deki deer deimez } void main() { double deer = 1.5; yarmEkle(deer); } writeln("levden sonraki deer: ", deer);

lev parametresi ref olarak tanmlanmad iin, ilev iindeki atama yalnzca ilevin yerel deikeni olan deer 'i etkiler; main 'deki deer deimez: levden sonraki deer: 1.5 lev parametresinin, ilevin arld yerdeki deikenin takma ismi olmas iin ref anahtar szc kullanlr: void yarmEkle(ref double deer) { deer += 0.5; } imdi main iindeki deer etkilenmi olur: levden sonraki deer: 2

66.1.3

Referans trleri
D'de baz trler referans trleridir. Bu trlerden olan deikenler, kendileri sahip olmadklar baka deerlere eriim salarlar: snflar dinamik diziler eriim tablolar Referans kavramn Deerler ve Referanslar dersinde grmtk. Burada, o derse dahil etmediim snflar zerinde bir rnek gstermek istiyorum:

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

384

Gstergeler

import std.stdio; class TkenmezKalem { double mrekkep; this() { mrekkep = 15; } void kullan(double miktar) { mrekkep -= miktar; }

void main() { auto kalem = new TkenmezKalem; auto bakaKalem = kalem; // imdi ikisi de ayn nesneye // eriim salarlar writefln("nce : %s %s", kalem.mrekkep, bakaKalem.mrekkep); kalem.kullan(1); bakaKalem.kullan(2); // ayn nesne kullanlr // ayn nesne kullanlr

writefln("Sonra: %s %s", kalem.mrekkep, bakaKalem.mrekkep);

Snflar referans trleri olduklar iin; farkl snf deikenleri olan kalem ve bakaKalem , new ile oluturulmu olan tek TkenmezKalem nesnesine eriim salamaktadr. Sonuta, iki deikenin kullanlmas da ayn nesneyi etkiler: nce : 15 15 Sonra: 12 12 Bu snf nesnesinin ve ona eriim salayan iki snf deikeninin bellekte u ekilde durduklarn dnebiliriz: (TkenmezKalem nesnesi) kalem bakaKalem ---+-------------------+--- ---+---+--- ---+---+--| mrekkep | | o | | o | ---+-------------------+--- ---+-|-+--- ---+-|-+-- | | | | | +--------------------+------------+ Referans kavram her zaman iin bu ekilde dnlebilir. Referanslar, asl deikenleri gsterirler. Programlama dillerindeki referans ve gsterge kavramlar perde arkasnda mikro ilemcilerin gsterme amacyla kullanlan yazmalar ile gerekletirilir. D'nin yukarda hatrlattm st dzey olanaklar da perde arkasnda gstergelerle gerekletirilmitir. Bu yzden hem zaten ok etkin alrlar, hem de aka gsterge kullanmaya gerek brakmazlar. Buna ramen, baka sistem programlama dillerinde de olduu gibi, gstergelerin D programclnda da mutlaka bilinmeleri gerekir.

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

385

Gstergeler

66.2

Tanmlanmas
D'nin gsterge sz dizimi ayn C'de olduu gibidir. Bu, C bilen programclar iin bir kolaylk olarak grlse de, zellikle * ilecinin farkl anlamlara sahip olmas, C'de olduu gibi D'de de renmeyi gletirebilir. Biraz aada anlatacam her tr gsterebilen gsterge dndaki gstergeler ancak belirli trden bir deikeni gsterebilirler. rnein bir int gstergesi yalnzca int trnden olan deikenleri gsterebilir. Bir gsterge tanmlanrken, nce hangi trden deer gsterecei, sonra da bir * karakteri yazlr: gsterecei_tr * gstergenin_ismi; Buna gre, bir int 'i gsteren bir gsterge yle tanmlanabilir: int * benim_gstergem; Byle bir tanmda * karakterini "gstergesi" diye okuyabilirsiniz. benim_gstergem 'in tr bir int* 'dr; yani bir "int gstergesidir". * karakterinden nceki ve sonraki boluklarn yazlmalar istee baldr ve aadaki gibi kullanmlar da ok yaygndr: int* benim_gstergem; int *benim_gstergem; Tek bana tr ismi olarak "int gstergesi" anlamnda kullanldnda, boluksuz olarak int* olarak yazlmas da ok yaygndr.

66.3

Gstergenin deeri ve adres alma ileci &


Gstergeler de deikendir ve her deikenin olduu gibi onlarn da deerleri vardr. Deer atanmayan gstergelerin varsaylan deeri, hibir deikene eriim salamama deeri olan null 'dr. Bir gstergenin hangi deikeni gsterdii (eriim salad), gstergenin deer olarak o deikenin adresini tamas ile salanr. Baka bir deyile, deer olarak bir adres tamas, gstergenin o adresteki deikeni gstermesi anlamna gelir. imdiye kadar readf ilevi ile ok kullandmz & ilecini Deerler ve Referanslar dersinden de hatrlayacaksnz. Bu ile, nne yazld deikenin adresini alr. Bu adres deeri, gsterge deeri olarak kullanlabilir: int beygirGc = 180; int * benim_gstergem = &beygirGc; Yukardaki ifadede gstergenin beygirGc 'nn adresi ile ilklenmesi, benim_gstergem 'in beygirGc 'n gstermesini salar. Gstergenin deeri ka yazdrldnda beygirGc 'nn adresi ile ayn olur: writeln("beygirGc'nn adresi : ", &beygirGc); writeln("benim_gstergem'in deeri: ", benim_gstergem);

beygirGc'nn adresi : BFDB9830 benim_gstergem'in deeri: BFDB9830

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

386

Gstergeler

Not: Adres deeri siz denediinizde farkl olacaktr. beygirGc , programn iletim sisteminden ald daha byk bir bellein kk bir yerinde bulunur. Bu yer, programn her altrlnda byk olaslkla farkl bir adreste bulunacaktr. Bir gstergenin deerinin, eriim salad deikenin adresi olmasn, ve bylece o deikeni gstermesini, referanslara benzer ekilde yle dnebiliriz: BFDB9830 adresindeki baka bir adresteki beygirGc benim_gstergem ---+-------------------+-----+---------------+--| 180 | | BFDB9830 | ---+-------------------+-----+-------|-------+-- | | | +-----------------------------+ beygirGc 'nn deeri 180, benim_gstergem 'in deeri de beygirGc 'nn adresidir. Gstergeler de deiken olduklarndan, onlarn adreslerini de & ileci ile renebiliriz: writeln("benim_gstergem'in adresi: ", &benim_gstergem);

benim_gstergem'in adresi: BFDB9834 beygirGc ile benim_gstergem 'in adreslerinin arasndaki farkn bu rnekte 4 olduuna bakarak ve beygirGc 'nn tr olan int 'in byklnn 4 bayt olduunu hatrlayarak, bu iki deikenin bellekte yan yana bulunduklar sonucunu kartabiliriz. Gsterme kavramn belirtmek iin kullandm oku da kaldrrsak, bir erit gibi soldan saa doru uzadn hayal ettiimiz bellei imdi yle dnebiliriz: BFDB9830 BFDB9834 BFDB9838 : : : ---+----------------+----------------+--| 180 | BFDB9830 | ---+----------------+----------------+---

Kaynak kodda geen deiken ismi, ilev ismi, anahtar szck, vs. gibi isimler; D gibi derlemeli diller ile oluturulan programlarn iinde bulunmazlar. rnein programcnn isim vererek tanmlad ve kulland deikenler, program iinde mikro ilemcinin anlad adreslere ve deerlere dnrler. Not: Programda kullanlan isimler hata ayklaycda yararlanlmak zere programn debug halinde de bulunurlar; ama o isimlerin programn ileyiiyle ilgileri yoktur.

66.4

Eriim ileci *
arpma ileminden tandmz * karakterinin gsterge tanmlarken tr isminden sonra yazldn yukarda grdk. Gstergeleri renirken karlalan bir glk, bu karakterin gstergenin gsterdii deikene erimek iin de kullanlmasdr. Bir gstergenin isminden nce yazldnda, gstergenin eriim salad deer anlamna gelir: writeln("Gsterdii deer: ", *benim_gstergem);

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

387

Gstergeler

Gsterdii deer: 180

66.5

Gsterdiinin yesine eriim iin . (nokta) ileci


Not: Eer gstergeleri C'den biliyorsanz; bu ile C'deki -> ileci ile ayndr. * ilecinin gsterilen deikene eriim iin kullanldn grdk. Bu, temel trleri gsteren gstergeler iin yeterli derecede kullanldr: *benim_gstergem yazlarak gsterilen deere kolayca eriilir. Gsterilen deiken yap veya snf nesnesi olduunda ise, bu yazm skntl hale gelir. rnek olarak x ve y yeleri ile iki boyutlu dzlemdeki bir noktay ifade eden bir yapya bakalm: struct Konum { int x; int y; string toString() const { return format("(%s,%s)", x, y); }

O trden bir deikeni gsteren bir gstergeyi aadaki gibi tanmlayabiliriz ve gsterdiine eriebiliriz: auto merkez = Konum(0, 0); Konum * gsterge = &merkez; writeln(*gsterge);

// tanm // eriim

O kullanm, toString ilevi tanmlanm olduu iin Konum nesnesini yazdrmak iin yeterlidir: (0,0) Ancak, gsterilen nesnenin bir yesine erimek iin * ileci kullanldnda yazm karmaklar: // 10 birim saa tele (*gsterge).x += 10; O ifade, merkez nesnesinin x yesinin deerini deitirmektedir. Bunu u admlarla aklayabiliriz: gsterge : merkez 'i gsteren gsterge *gsterge : nesneye eriim; yani merkez 'in kendisi (*gsterge) : nokta karakteri gsterge'ye deil, onun gsterdiine uygulansn diye gereken parantezler (*gsterge).x : gsterdii nesnenin x yesi Gsterilen nesnenin yesine eriim byle kark bir ekilde yazlmak zorunda kalnmasn diye, . (nokta) ileci gstergenin kendisine uygulanr ama gsterdiinin yesine eriim salar. Yukardaki ifadeyi ok daha ksa olarak yle yazabiliriz: gsterge.x += 10; writeln(merkez);

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

388

Gstergeler

Daha basit olan gsterge.x ifadesi, merkez 'in x yesini deitirmitir: (10,0) Bunun snflarla ayn olduuna dikkat edin. Snflarda da snf deikenlerine dorudan uygulanan . (nokta) ileci, aslnda snf nesnesinin yesine eriim salar: class SnfTr { int ye; } // ... // Solda deiken; sada nesne SnfTr deiken = new SnfTr; // Deikene uygulanr; nesnenin yesine eriir deiken.ye = 42; Snflar dersinden hatrlayacanz gibi, yukardaki koddaki nesne, new ile sada isimsiz olarak oluturulur. deiken , o nesneye eriim salayan bir snf deikenidir. Deikene uygulanan . (nokta) ileci, aslnda asl nesnenin yesine eriim salar. Ayn durumun gstergelerde de bulunmas, yap deikenleri ile gstergelerin temelde benzer ekilde gerekletirildiklerini ortaya koyar.

66.6

Gsterge deerinin deitirilmesi


Gstergelerin deerleri arttrlabilir, azaltlabilir, ve toplama ve karma ilemlerinde kullanlabilir: ++birGsterge; --birGsterge; birGsterge += 2; birGsterge -= 2; writeln(birGsterge + 3); writeln(birGsterge - 3); Aritmetik ilemlerden altmzdan farkl olarak; bu ilemler gstergenin deerini belirtilen miktar kadar arttrmazlar. Gstergenin deeri, belirtilen miktar kadar sonraki (veya nceki) deikeni gsterecek ekilde deiir. rnein gstergenin deerinin ++ ileciyle arttrlmas, o gstergenin bellekte bir sonra bulunan deikeni gstermesini salar: ++birGsterge; // daha nce gsterdiinden bir sonraki // deikeni gstermeye balar

Derleyici, bunu salayabilmek iin gstergenin deerini trn bykl kadar arttrr. rnein int* trndeki bir gstergenin deeri; int 'in bykl 4 olduu iin, ++ ilemi sonucunda 4 artar. Uyar: Gstergelerin programa ait olmayan adresleri gstermeleri tanmsz davrantr. Erimek iin kullanlmasa bile, bir gstergenin var olmayan bir deikeni gstermesi hataldr. (Not: Bunun tek istisnas, bir dizinin sonuncu elemanndan sonraki hayali elemann gsterilebilmesidir. Bunu aada aklyorum.)

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

389

Gstergeler

rnein yukarda tek bir int olarak tanmlanm olan beygirGc deikenini gsteren gstergeyi arttrmak yasal deildir: ++benim_gstergem; // tanmsz davran

yle bir ilemde programn nasl davranacann tanmlanmam olmas, o ilemin sonucunda ne olacann belirsiz olmas anlamna gelir. O ilem sonucunda programn kecei sistemler bulunabilir. Gnlk kullanmdaki bilgisayarlardaki mikro ilemcilerde ise, gstergenin deeri 4 sonraki bellek adresine sahip olacaktr. (Not: lgin bir gzlem olarak, yukardaki programdaki adreslerden anlald gibi, beygirGc 'nden bir sonraki adreste benim_gstergem 'in kendisi bulunduu iin, gsterge kendisini sanki bir int 'mi gibi gsterecektir.) O yzden, gstergelerin deerlerinin arttrlmas veya azaltlmas ancak yan yana bulunduklar bilinen deikenler gsterildiinde kullanlmaldr. Deikenlerin yan yana bulunmalar; dizilerin, ve zaten karakter dizileri olan dizgilerin tanmlar gereidir. Dizi iindeki bir eleman gsteren gstergenin deerinin ++ ileci ile artrlmas, onun bir sonraki eleman gstermesini salar: import std.stdio; import std.string; import std.conv; enum Renk { krmz, sar, mavi } struct KurunKalem { Renk renk; double uzunluk; string toString() const { return format("%s santimlik %s bir kalem", uzunluk, to!string(renk)); }

void main() { writeln("KurunKalem nesnelerinin bykl: ", KurunKalem.sizeof, " bayt"); KurunKalem[] kalemler = [ KurunKalem(Renk.krmz, 11), KurunKalem(Renk.sar, 12), KurunKalem(Renk.mavi, 13) ]; KurunKalem * gsterge = &kalemler[0]; for (int i = 0; i != kalemler.length; ++i) { writeln("gsterge deeri: ", gsterge); writeln("kalem: ", *gsterge); ++gsterge; // (1) // (2) // (3) // (4)

} 1. 2. 3. 4.

tanmlanmas; dizinin ilk elemannn adresi ile ilklenmektedir deerinin kullanlmas; deeri, gsterdii elemann adresidir gsterdii nesneye eriim bir sonraki nesneyi gstermesi

Bu programn kts:

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

390

Gstergeler

KurunKalem nesnelerinin bykl: 12 bayt gsterge deeri: 114FC0 kalem: 11 santimlik krmz bir kalem gsterge deeri: 114FCC kalem: 12 santimlik sar bir kalem gsterge deeri: 114FD8 kalem: 13 santimlik mavi bir kalem Dikkat ederseniz, yukardaki dng kalemler.length kere tekrarland iin, gsterge hibir zaman var olmayan bir nesneye erimemektedir.

66.7

Gstergeler risklidir
Gstergelerin doru olarak kullanlp kullanlmadklar konusunda denetim salanamaz. Ne derleyici, ne de alma zamanndaki denetimler bunu garantileyebilirler. Bir gstergenin deerinin her zaman iin geerli olmas programcnn sorumluluundadr. O yzden, gstergeleri kullanmay dnmeden nce D'nin st dzey ve gvenli olanaklarnn yeterli olup olmadklarna bakmanz neririm.

66.8

Dizinin son elemanndan bir sonras


Gstergelerin, bir dizinin sonuncu elemanndan hemen sonraki hayali eleman gstermeleri yasaldr. Bu, dilimlerden alk olduumuz aralk kavramna benzeyen yntemlerde kullanldr. Hatrlarsanz, dilim aralklarnn ikinci indeksi, ilem yaplacak olan elemanlardan bir sonrasn gsterir: int[] saylar = [ 0, 1, 2, 3 ]; writeln(saylar[1 .. 3]); // 1 ve 2 dahil, 3 hari Bu yntem gstergelerle de kullanlabilir. Balang gstergesinin ilk eleman gstermesi, ve biti gstergesinin son elemandan sonraki eleman gstermesi olduka yaygn bir ilev tasarmdr. Bunu bir ilevin parametrelerinde grelim: import std.stdio; // Kendisine verilen aralktaki deerleri 10 katna kartr void onKat(int * ba, int * son) { while (ba != son) { *ba *= 10; ++ba; } } void main() { int[] saylar = [ 0, 1, 2, 3 ]; int * ba = &saylar[1]; // ikinci elemann adresi onKat(ba, ba + 2); // ondan iki sonrakinin adresi writeln(saylar); }

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

391

Gstergeler

ba + 2 deeri, ba 'n gsterdiinden 2 sonraki elemann, yani indeksi 3 olan elemann adresi anlamna gelir. Yukardaki onKat ilevi, iki gsterge almakta ve bunlardan ilkinin gsterdii int 'i kullanmakta, ama ikincisinin gsterdii int 'e hibir zaman erimemektedir. kinci gstergeyi, ilem yapaca int 'lerin dn belirten bir deer olarak kullanmaktadr. son 'un gsterdii eleman kullanmad iin de dizinin yalnzca 1 ve 2 numaral indeksli elemanlar deimitir: 0 10 20 3 Yukardaki gibi ilevlerin for dngleriyle gerekletirilenlerine de rastlayabilirsiniz: void onKat(int * ba, int * son) { for ( ; ba != son; ++ba) { *ba *= 10; } } Dikkat ederseniz, for dngsnn hazrlk blm bo braklmtr. Bu ilev yeni bir gsterge kullanmak yerine, dorudan ba parametresini arttrmaktadr. Aralk bildiren ift gstergeler foreach deyimi ile de uyumlu olarak kullanlabilir: foreach (gsterge; ba .. son) { *gsterge *= 10; } Bu gibi bir yntemde bir dizinin elemanlarnn hepsinin birden kullanlabilmesi iin, ikinci gstergenin dizinin sonuncu elemanndan bir sonray gstermesi gerekir: // ikinci gsterge, dizinin sonuncu elemanndan sonraki // hayali bir eleman gsteriyor: onKat(ba, ba + saylar.length); te bunun iin, dizilerin son elemanlarndan sonraki aslnda var olmayan bir elemann gsterilmesi yasaldr.

66.9

Dizi eriim ileci [] ile kullanm


D'de hi gerekmese de, gstergeler bir dizinin elemanlarna eriir gibi de kullanlabilirler: double[] kesirliler = [ 0.0, 1.1, 2.2, 3.3, 4.4 ]; double * gsterge = &kesirliler[2]; *gsterge = -100; gsterge[1] = -200; writeln(kesirliler); kts: 0 1.1 -100 -200 4.4 Byle bir kullanmda, gstergenin gstermekte olduu deiken sanki bir dizinin ilk elemanym gibi dnlr ve [] ileci o hayali dizinin belirtilen elemanna eriim salar. Yukardaki programdaki gsterge , kesirliler dizisinin 2 indeksli elemann gstermektedir. // gsterdiine eriim // dizi gibi eriim

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

392

Gstergeler

gsterge[1] kullanm, sanki orada hayali bir dizi varm gibi, o dizinin 1 indeksli elemanna, yani asl dizinin 3 indeksli elemanna eriim salar. Kark gibi grlse de, bu kullanmn temelinde ok basit bir dnm yatar. Derleyici, gsterge[indeks] gibi bir yazm perde arkasnda *(gsterge + indeks) olarak dntrr: gsterge[1] = -200; *(gsterge + 1) = -200; // dizi gibi eriim // sttekiyle ayn elemana eriim

Yukarda da belirttiim gibi, bu kullanmn geerli bir deikeni gsterip gstermedii denetlenemez. Gvenli olabilmesi iin bunun yerine dilim kullanlmaldr: double[] dilim = kesirliler[2 .. 4]; dilim[0] = -100; dilim[1] = -200; O dilimin yalnzca iki eleman bulunduuna dikkat edin. Asl dizinin 2 ve 3 indeksli elemanlarna eriim salamaktadr. ndeksi 4 olan eleman dilimin dndadr. Dilimler gvenlidir; eleman eriimi hatalar alma zamannda yakalanr: dilim[2] = -300; // HATA: dilimin dna eriim

Dilimin 2 indeksli eleman bulunmad iin bir hata atlr ve bylece programn yanl sonularla devam etmesi nlenmi olur: core.exception.RangeError@deneme(8391): Range violation

66.10

Her tr gsterebilen void*


D'de hi gerekmese de, yine C'den gelen bir olanak, herhangi trden deikenleri gsterebilen gstergelerdir. Bunlar void gstergesi olarak tanmlanrlar: int tamsay = 42; double kesirli = 1.25; void * herTrGsterebilen; herTrGsterebilen = &tamsay; herTrGsterebilen = &kesirli; Yukardaki koddaki void* trnden olan gsterge hem bir int 'i hem de bir double ' gsterebilmektedir; o satrlarn ikisi de yasaldr ve hatasz olarak derlenir. void* trnden olan gstergeler ok kstldrlar: ne onlarn deerlerini sonraki (veya nceki) deikeni gsterecek ekilde deitirebiliriz, ne de gsterdikleri deikenlere eriebiliriz. Bu, getirdii esnekliin bir sonucudur: gsterilen trn ne olduu bilinmedii iin, gsterdii elemann ka baytlk olduu da bilinemez: *herTrGsterebilen = 43; // derleme HATASI

Byle ilemlerde kullanlabilmesi iin, void* 'nin deerinin nce doru tr gsteren bir gstergeye aktarlmas gerekir: int tamsay = 42; void * herTrGsterebilen = &tamsay; // (1) // (2)

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

393

Gstergeler

// ... int * tamsayGstergesi = cast(int*)herTrGsterebilen; // (3) *tamsayGstergesi = 43; // (4) Yukardaki rnek kodu u admlarla aklayabiliriz: 1. 2. 3. 4. asl deiken deikenin deerinin bir void* iinde saklanmas daha sonra o deerin doru tr gsteren bir gstergeye aktarlmas deikenin deerinin doru tr gsteren gsterge ile eriilerek deitirilmesi

interface , snf, ablon, vs. gibi st dzey olanaklar bulunmayan C ktphanelerinde void* trleri ok kullanlr. Byle ktphaneler kullandmzda bizim de void* trnde gstergeler kullanmamz gerekebilir.

66.11

Mantksal ifadelerde kullanlmalar


Gstergeler otomatik olarak bool trne dnebilirler. Bu, onlarn deerlerinin mantksal ifadelerde kullanlabilmesini salar. null deere sahip olan gstergeler mantksal ifadelerde false deerini alrlar, dierleri de true deerini. Yani hibir deikeni gstermeyen gstergeler false 'tur. ka nesne yazdran bir ilev dnelim. Bu ilev, ka bayt yazdn da bir k parametresi ile bildiriyor olsun. Ama o ii ancak zellikle istendiinde yapyor olsun. Bunun istee bal olmas, ileve gnderilen gsterge deerinin null olup olmamas ile gerekletirilebilir: void bilgiVer(const ref KurunKalem kalem, int * baytAdedi) { const string bilgi = format("Kalem: %s", kalem); writeln(bilgi); if (baytAdedi) { *baytAdedi = bilgi.length; }

Ka bayt yazld bilgisinin gerekmedii durumlarda gsterge olarak null deeri gnderilebilir: bilgiVer(KurunKalem(Renk.sar, 7), null); Bayt adedinin nemli olduu durumlarda ise null olmayan bir deer: int baytAdedi; bilgiVer(KurunKalem(Renk.mavi, 8), &baytAdedi); writeln("ka ", baytAdedi, " bayt yazlm");

66.12

new baz trler iin adres dndrr


imdiye kadar snf nesneleri olutururken karlatmz new ' yap nesneleri, diziler, ve temel tr deikenleri iin de kullanabiliriz. new ile oluturulan deikenlere dinamik deiken denir.

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

394

Gstergeler

new , bellekten deiken iin gereken sayda baytlk bir yer ayrr. Ondan sonra bu yerde bir deiken kurar. Bu deikenlerin kendi isimleri bulunmad iin; onlara ancak new 'n dndrm olduu referans ile eriilir. Bu referans, snf nesnelerinde, imdiye kadar ok grdmz gibi bir snf deikenidir: Snf snfDeikeni = new Snf; yap nesnelerinde ve temel trlerde bir gstergedir: Yap * yapGstergesi = new Yap; int * intGstergesi = new int; dizilerde ise bir dinamik dizidir: int[] dinamikDizi = new int[100]; auto ve typeof dersinden hatrlayacanz gibi, sol taraftaki tr isimleri yerine normalde auto anahtar szc kullanld iin ounlukla bu ayrma dikkat etmemiz gerekmez: auto auto auto auto snfDeikeni = new Snf; yapGstergesi = new Yap; intGstergesi = new int; dinamikDizi = new int[100];

Herhangi bir ifadenin tr isminin typeof(Tr).stringof yntemiyle yazdrlabildiini hatrlarsanz, new 'n deiik trler iin ne dndrd, kk bir programla yle grlebilir: import std.stdio; struct Yap {} class Snf {} void main() { writeln(typeof(new writeln(typeof(new writeln(typeof(new writeln(typeof(new }

int ).stringof); int[5]).stringof); Yap ).stringof); Snf ).stringof);

ktdan anlald gibi; new , temel tr ve yaplar iin gsterge trnde bir deer dndrmektedir: int* int[] Yap* Snf

66.13

Dizilerin .ptr nitelii


Dizilerin .ptr nitelii, dizideki ilk elemann adresini dndrr. Bu deerin tr, dizinin eleman trn gsteren bir gstergedir:

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

395

Gstergeler

int[] saylar = [ 7, 12 ]; int * ilkElemannAdresi = saylar.ptr; writeln("lk eleman: ", *ilkElemannAdresi); Bu deer C ktphanelerini kullanrken yararl olabilir. Baz C ilevleri, bellekte art arda bulunan elemanlarn ilkinin adresini alrlar. Dizgilerin de dizi olduklarn hatrlarsanz, onlarn .ptr nitelii de ilk karakterlerinin adresini verir. Burada dikkat edilmesi gereken bir konu, dizgi elemanlarnn harf deil, o harflerin Unicode kodlamasndaki karlklar olduklardr. rnein harfi bir char[] veya string iinde iki tane char olarak bulunur. .ptr niteliinin dndrd adres ile eriildiinde, Unicode kodlamasnda kullanlan karakterler ayr ayr gzlemlenebilirler. Bunu rnekler blmnde gstereceim.

66.14

Eleme tablolarnn in ileci


Gstergeleri aslnda Eleme Tablolar dersinde grdmz in ileci ile kullandk. Orada henz gstergeleri anlatmam olduum iin, in ilecinin dn trn geitirmi ve o deeri st kapal olarak bir mantksal ifadede kullanmtm: if ("mor" in renkKodlar) { // evet, renkKodlar'nda "mor" indeksli eleman varm } else { // hayr, yokmu... } in ileci, aslnda tabloda bulunuyorsa elemann adresini, bulunmuyorsa null deerini dndrr. Yukardaki koul da bu deerin false 'a veya true 'ya dnmesine gre iler. in 'in dn deerini bir gstergeye atarsak, elemann tabloda bulunduu durumlarda ona etkin bir ekilde eriebiliriz: import std.stdio; void main() { // Tamsaydan string'e dnm tablosu string[int] saylar = [ 0 : "sfr", 1 : "bir", 2 : "iki", 3 : "" ]; int say = 2; auto eleman = say in saylar; if (eleman) { writeln("Biliyorum: ", *eleman); // (1) // (2) // (3)

} else { writeln(say, " saysnn yazln bilmiyorum"); }

Yukardaki koddaki eleman gstergesi in ileci ile ilklenmekte (1) ve deeri bir mantksal ifadede kullanlmaktadr (2). Deeri null olmadnda da gsterdii deikene eriilmektedir (3). Hatrlarsanz, null deerinin gsterdii geerli bir nesne olmad iin, deeri null olan bir gstergenin gsterdiine eriilemez.

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

396

Gstergeler

Orada eleman 'n tr, eleme tablosunun deer trnde bir gstergedir. Bu tablodaki deerler string olduklar iin, in 'in dn tr string* 'dir. Dolaysyla, auto yerine tr ak olarak yazmak istesek: string * eleman = say in saylar;

66.15

Ne zaman kullanmal
Ktphaneler gerektirdiinde
readf ilevinde de grdmz gibi, kullandmz bir ktphane bizden bir gsterge bekliyor olabilir. Her ne kadar D ktphanelerinde az sayda olacaklarn dnsek de, bu tr ilevlerle karlatmzda onlara istedikleri trde gsterge gndermemiz gerekir. rnein bir C ktphanesi olan gtk'den uyarlanm olan gtkD'nin baz ilevlerinin baz parametreleri gstergedir: GdkGeometry boyutlar; // ... boyutlar nesnesinin yelerinin kurulmas ... pencere.setGeometryHints(/* ... */, &boyutlar, /* ... */);

66.15.1

66.15.2

Deer trnden deikenleri gstermek iin


Yine kesinlikle gerekmese de, deer trnden olan bir deikenin hangisiyle ilem yaplacan bir gsterge ile belirleyebiliriz. rnek olarak yaz-tura deneyi yapan bir programa bakalm: import std.stdio; import std.random; void main() { int yazAdedi; int turaAdedi; foreach (i; 0 .. 100) { int * hangisi = (uniform(0, 2) == 1 ? &yazAdedi : &turaAdedi); *hangisi += 1; } } writefln("yaz: %s tura: %s", yazAdedi, turaAdedi);

Tabii ayn ilemi gsterge kullanmadan da gerekletirebiliriz: uniform(0, 2) ? ++yazAdedi : ++turaAdedi; Veya bir if kouluyla: if (uniform(0, 2)) { ++yazAdedi; } else { ++turaAdedi; }

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

397

Gstergeler

66.15.3

Veri yaplarnda
Baz veri yaplarnn temeli gstergelere dayanr. Dizilerin elemanlarnn yan yana bulunmalarnn aksine, baz veri yaplarnn elemanlar bellekte birbirlerinden ayr olarak dururlar. Bunun bir nedeni, elemanlarn veri yapsna farkl zamanlarda eklenmeleri olabilir. Byle veri yaplar, elemanlarn birbirlerini gstermeleri temeli zerine kuruludur. rnein bal liste veri yapsnn her dm, kendisinden bir sonraki dm gsterir. kili aa veri yapsnn dmleri de sol ve sa dallardaki dmleri gsterirler. Baka veri yaplarnda da gsterge kullanmna ok rastlanr. D'de veri yaplar referans trleri kullanarak da gerekletirilebilseler de, gstergeler baz durumlarda daha hzl veya daha doal olabilirler.

66.15.4

Bellee dorudan erimek gerektiinde


Gstergeler bellee dorudan ve bayt dzeyinde eriim salarlar. Hataya ak olduklarn aklda tutmak gerekir. Ek olarak, programa ait olmayan bellee erimek tanmsz davrantr.

66.16

rnekler
Basit bir bal liste
Bal liste veri yapsnn elemanlar dmler halinde tutulurlar. Liste, her dmn kendisinden bir sonraki dm gstermesi dncesi zerine kuruludur. Sonuncu dm hibir dm gstermez (deeri null 'dr):
ilk dm +--------+---+ | eleman | o---- +--------+---+ dm +--------+---+ | eleman | o---- +--------+---+ dm +--------+---+ | eleman | o---- ... +--------+---+ son dm +--------+------+ | eleman | null | +--------+------+

66.16.1

Yukardaki ekil yanltc olabilir: burada dmlerin bellekte art arda bulunduklar sanlmasn. Dmler normalde bellein herhangi bir yerinde bulunabilirler. nemli olan, her dmn kendisinden bir sonraki dm gsteriyor olmasdr. Bu ekle uygun olarak, bir int listesinin dmn yle tanmlayabiliriz: struct Dm { int eleman; Dm * sonraki; } // ...

Not: Kendi trnden nesneleri gsterdii iin bunun zyinelemeli bir yap olduunu syleyebiliriz. Btn dmlerin bir liste olarak dnlmesi de yalnzca balang dmn gsteren bir gsterge ile salanabilir: struct Liste { Dm * ba; } // ...

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

398

Gstergeler

Dersin amacndan fazla uzaklamamak iin, burada yalnzca listenin bana eleman ekleyen ilevi gstermek istiyorum: struct Liste { Dm * ba; void banaEkle(int eleman) { ba = new Dm(eleman, ba); } } // ...

Bu kodun en nemli noktas banaEkle ilevini oluturan satrdr. O satr, yeni eleman listenin bana ekler ve bylece bu yapnn bir bal liste olmasn salar. (Not: Aslnda sonuna ekleme ilemi daha doal ve kullanldr. Bunu problemler blmnde soruyorum.) Yukardaki satrda sa tarafta dinamik bir Dm nesnesi oluturuluyor. Bu yeni nesne kurulurken, sonraki yesi olarak listenin u andaki ba kullanlyor. Listenin yeni ba olarak da bu yeni dmn adresi kullanlnca, listenin bana eleman eklenmi oluyor. Bu kk veri yapsn deneyen kk bir program: import std.stdio; import std.conv; import std.string; struct Dm { int eleman; Dm * sonraki; this(int eleman, Dm * sonraki) { this.eleman = eleman; this.sonraki = sonraki; } string toString() const { string sonu = to!string(eleman); if (sonraki) { sonu ~= " -> " ~ to!string(*sonraki); } } return sonu;

struct Liste { Dm * ba; void banaEkle(int eleman) { ba = new Dm(eleman, ba); } string toString() const { return format("(%s)", ba ? to!string(*ba) : ""); }

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

399

Gstergeler

void main() { Liste saylar; writeln("nce : ", saylar); foreach (say; 0 .. 10) { saylar.banaEkle(say); } } writeln("sonra: ", saylar);

kts: nce : () sonra: (9 -> 8 -> 7 -> 6 -> 5 -> 4 -> 3 -> 2 -> 1 -> 0)

66.16.2

ubyte gstergesi ile bellein incelenmesi


Bellein adresleme birimi bayttr. Her adreste tek baytlk bilgi bulunur. Her deiken, kendi tr iin gereken sayda bayt zerinde kurulur. Gstergeler, bize bellee bayt bayt erime olana sunarlar. Bellee bayt olarak erimek iin en uygun tr ubyte* 'dir. rnein bir deikenin adresi bir ubyte gstergesine atanr ve bu gsterge ilerletilerek o deikeni oluturan baytlarn tm gzlemlenebilir. rnek olarak, deerini aklayc olsun diye onaltl dzende yazdm bir tamsay olsun: int birSay = 0x01020304; Bu deikeni gsteren bir gstergenin u ekilde tanmlandn grdk: int * adresi = &birSay; O gstergenin deeri, birSay 'nn bellekte bulunduu yerin adresidir. Gstergenin deerini tr dnm ile bir ubyte gstergesine atayabiliriz: ubyte * baytGstergesi = cast(ubyte*)adresi; Bu adresteki int 'i oluturan 4 bayt yle yazdrabiliriz: writeln(baytGstergesi[0]); writeln(baytGstergesi[1]); writeln(baytGstergesi[2]); writeln(baytGstergesi[3]); Eer sizin mikro ilemciniz de benimki gibi kk soncul ise, int 'i oluturan baytlarn bellekte ters srada durduklarn grebilirsiniz: 4 3 2 1 Deikenleri oluturan baytlar gzlemleme iini kolaylatrmak iin bir ilev ablonu yazabiliriz:

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

400

Gstergeler

void baytlarnGster(T)(ref T deiken) { const ubyte * ba = cast(ubyte*)&deiken; writefln("tr : writefln("deer : writefln("adres : writef( "baytlar: %s", T.stringof); %s", deiken); %s", ba); ");

// (1)

// (2) // (3) // (4)

foreach (i; 0 .. T.sizeof) { writef("%02x ", ba[i]); } writeln(); writeln();

} 1. 2. 3. 4.

Deikenin adresinin bir ubyte gstergesine atanmas Gstergenin deerinin, yani deikenin balang adresinin yazdrlmas Trn byklnn .sizeof nitelii ile edinilmesi Gstergenin gsterdii bayta [] ileci ile eriilmesi

Yukardaki dngy * ileci ile eriecek ekilde yle de yazabilirdik: foreach (bayt; ba .. ba + T.sizeof) { writef("%02x ", *bayt); } O dngde bayt gstergesininin deeri, ba .. ba + T.sizeof aralnda deiir. ba + T.sizeof deerinin aralk dnda kaldna ve ona hibir zaman eriilmediine dikkat edin. O ilev ablonunu deiik trlerle arabiliriz: struct Yap { int birinci; int ikinci; } class Snf { int i; int j; this(int i, int j) { this.i = i; this.j = j; }

void main() { int tamsay = 0x11223344; baytlarnGster(tamsay); double kesirli = double.nan; baytlarnGster(kesirli); string dizgi = "merhaba dnya"; baytlarnGster(dizgi); int[3] dizi = [ 1, 2, 3 ]; baytlarnGster(dizi);

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

401

Gstergeler

auto yapNesnesi = Yap(0xaa, 0xbb); baytlarnGster(yapNesnesi); auto snfNesnesi = new Snf(1, 2); baytlarnGster(snfNesnesi);

kts aydnlatc olabilir: tr : deer : adres : baytlar: tr : deer : adres : baytlar: tr : deer : adres : baytlar: tr : deer : adres : baytlar: tr : deer : adres : baytlar: tr : deer : adres : baytlar: int 287454020 BFFD6D0C 44 33 22 11 double nan BFFD6D14 00 00 00 00 00 00 f8 7f string merhaba dnya 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, 187) BFFD6D34 aa 00 00 00 bb 00 00 00 Snf deneme.Snf BFFD6D3C c0 ec be 00

(1)

(2)

(3)

(1)

(1)

(4)

Gzlemler: 1. Baz trlerin baytlar beklediimiz gibidir: int 'in, sabit uzunluklu dizinin (int[3u] ), ve yap nesnesinin deerlerinin baytlar bellekte ters srada bulunmaktadr 2. double.nan zel deerini oluturan baytlar ters srada dnnce, bu deerin 0x7ff8000000000000 zel bit dizisi ile ifade edildiini reniyoruz 3. string 8 bayttan olumaktadr; onun deeri olan "merhaba dnya"nn o kadar kk bir alana smas olanakszdr. Bu, string trnn perde arkasnda bir yap gibi tanmlanm olmasndan gelir. Derleyicinin bir i tr olduunu vurgulamak iin ismini __ ile balatarak, rnein yle bir yap olduunu dnebiliriz: struct __string { int uzunluk; char * ptr; }

// asl karakterler

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

402

Gstergeler

Bu tahmini destekleyen bulguyu string 'i oluturan baytlarda gryoruz: dikkat ederseniz, "merhaba dnya" dizgisindeki toplam 13 harf, ilerindeki 'nn UTF-8 kodlamasnda iki baytla ifade edilmesi nedeniyle 14 bayttan oluur. string 'in yukarda grlen ilk 4 bayt olan 0x0000000e'nin deerinin onlu sistemde 14 olmas, bu tahmini glendiriyor. 4. Benzer ekilde, snf nesnesini oluturan i ve j yelerinin 4 bayta smalar olanakszdr; iki int iin 8 bayt gerektiini biliyoruz. O kt, snf deikenlerinin snf nesnesini gsterecek ekilde tek bir gstergeden olutuu phesini uyandrr: struct __Snf_DeikenTr { __Snf_AslNesneTr * nesne; }

imdi biraz daha esnek bir ilev dnelim. Belirli bir deikenin baytlar yerine, belirli bir adresteki belirli saydaki bayt gsteren bir ilev yazalm: import std.ctype; // ... void belleiGster(T)(T * bellek, int uzunluk) { const ubyte * ba = cast(ubyte*)bellek; foreach (i; 0 .. uzunluk) { const ubyte * adres = ba + i; const char karakter = isprint(*adres) ? *adres : '.'; } writefln("%s: %02x %s", adres, *adres, karakter);

std.ctype modlnde tanml olan isprint ilevi, kendisine verilen bayt deerinin ASCII tablosunun grntlenebilen bir karakteri olup olmadn bildirir. Baz bayt deerlerinin tesadfen u birimin kontrol karakterlerine karlk gelerek u birimin almasn bozmalarn nlemek iin, "isprint olmayan" karakterler yerine '.' karakterini yazdryoruz. Bu ilevi, string 'in .ptr niteliinin gsterdii karakterlere erimek iin kullanabiliriz: string dizgi = "merhaba dnya"; belleiGster(dizgi.ptr, dizgi.length); ktdan anlaldna gre harfi iin gerekten de iki bayt kullanlmaktadr:

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

403

Gstergeler

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 . . n y a

66.17

Problemler
1. Kendisine verilen iki int 'in deerlerini dei toku etmeye alan u ilevi parametrelerinde ref kullanmadan dzeltin: void deiToku(int birinci, int ikinci) { int geici = birinci; birinci = ikinci; ikinci = geici; } void main() { int i = 1; int j = 2; deiToku(i, j); // Deerleri deisin assert(i == 2); assert(j == 1);

O program altrdnzda assert denetimlerinin baarsz olduklarn greceksiniz. Bu zmde ref parametre kullanmak yasak! :o) 2. Bu derste gsterilen liste yapsn ablona dntrn ve bylece int 'ten baka trlerle de kullanlabilmesini salayn. 3. Bal listede yeni elemanlarn sona eklenmeleri daha doal bir ilemdir. Ben daha ksa olduu iin bu derste bana eklemeyi setim. Yeni elemanlarn listenin bana deil, sonuna eklenmelerini salayn. Bunun iin listenin sonuncu elemann gsteren bir gsterge yararl olabilir. ... zmler

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

404

Bit lemleri

67

Bit lemleri
Bu blmde mikro ilemcinin en kk bilgi birimi olan bitlerle yaplan ilemleri tanyacaz. Bit ilemleri mikro ilemcinin en temel olanaklarndandr. Bu ilemler hem alt dzey programclk asndan bilinmelidir, hem de parametre olarak bayrak alan ilevler iin gereklidir. Byle ilevlere zellikle C ktphanelerinden uyarlanm olan D ktphanelerinde rastlayabilirsiniz.

67.1

Verinin en alt dzeyde gerekletirilmesi


D gibi bir programlama dili aslnda bir soyutlamadr. Program iinde tanmladmz renci gibi bir kullanc trnn bilgisayarn i yaps ile dorudan bir ilgisi yoktur. Bir programlama dilinin amalarndan birisi, donanmn anlad dil ile insann anlad dil arasnda araclk yapmaktr. Yine de, D dili ile ifade ettiimiz kavramlarn en alt dzeyde elektronik devre elemanlarna nasl bal olduklarn anlamak nemlidir. Bu konularda baka kaynaklarda ok miktarda bilgi bulabileceinizi bildiim iin bu bal olabildiince ksa tutacam.

67.1.1

Transistr
Modern elektronik aletlerin ilem yapma yetenekleri byk lde transistr denen elektronik devre eleman ile salanr. Transistrn bir zellii, devrenin baka tarafndaki sinyallerle kontrol edilebilmesidir. Bir anlamda, elektronik devrenin kendi durumundan haberinin olmasn ve kendi durumunu deitirebilmesini salar. Transistrler hem mikro ilemcinin iinde hem de bilgisayarn ana belleinde ok byk saylarda bulunurlar. Programlama dili araclyla ifade ettiimiz ilemleri ve verileri en alt dzeyde gerekletiren elemanlardr.

67.1.2

Bit
Bilgisayarlarda en kk bilgi birimi bittir. Bit, en alt dzeyde bir ka tane transistrn belirli bir dzende bir araya getirilmesi ile gerekletirilir. Bit, veri olarak iki farkl deerden birisini depolayabilir: 0 veya 1. Depolad veriyi, tekrar deitirilene kadar veya enerji kayna tkenene kadar korur. Bilgisayarlar, veriye bit dzeyinde dorudan eriim salamazlar. Bunun nedeni; her bitin adreslenebilmesinin bilgisayarn karmakln ve maliyetini ok arttracak olmas, ve zaten tek bitlik kavramlarn desteklenmeye demeyecek kadar nadir olmalardr.

67.1.3

Bayt
Bayt, birbirleriyle ilikilendirilen 8 bitin bir araya gelmesinden oluur. Bilgisayarlarda adreslenebilen, yani ayr ayr eriilebilen en kk veri bayttr. Bellekten tek seferde en az bir bayt veri okunabilir ve bellee en az bir bayt veri yazlabilir. Bu yzden, yalnzca false ve true diye iki farkl deer alan ve bu yzden de tek bitlik bilgi tayan bool tr bile 1 bayt olarak gerekletirilir. Bunu, bool.sizeof deerine bakarak kolayca grebiliriz: writeln(bool.stringof, ' ', bool.sizeof, " bayttr");

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

405

Bit lemleri

bool 1 bayttr

67.1.4

Yazma
Mikro ilemcinin kendi iinde bulunan depolama ve ilem birimleri yazmalardr. Yazmalar olduka kstl ama ok hzl ilemler sunarlar. Yazmalar her ilemcinin bit geniliine bal olan sayda bayttan oluurlar. rnein 32 bitlik ilemcilerde yazmalar 4 bayttan, 64 bitlik ilemcilerde de 8 bayttan oluur. Yazma genilii, mikro ilemcinin en etkin olarak ileyebildii bilgi miktarn belirler. rnein 32 bitlik bir ilemci en etkin olarak int ve uint trlerini, 64 bitlik bir ilemci de long ve ulong trlerini ileyebilir. Programlama dili araclyla gerekletirilen her i eninde sonunda bir veya daha fazla yazma tarafndan halledilir.

67.2

kili say sistemi


Gnlk hayatta kullandmz onlu say sisteminde 10 rakam vardr: 0123456789. kili say sistemlerinde de iki rakam vardr: 0 ve 1. Bu, bitin iki deer alabilmesinden gelir. (Not: Bitler rnein farkl deer alabilseler, bilgisayarlar l say sistemini kullanrlard.) Gnlk hayatta kullandmz saylarn birler, onlar, yzler, binler, vs. basamaklar vardr. rnein 1023 gibi bir say yle ifade edilebilir: 1023 == 1 adet 1000, 0 adet 100, 2 adet 10, ve 3 adet 1 Dikkat ederseniz, sola doru ilerlendiinde her basaman deeri 10 kat artmaktadr: 1, 10, 100, 1000, vs. Ayn tanm ikili say sistemine taynca, ikili sistemde yazlm olan saylarn basamaklarnn da birler, ikiler, drtler, sekizler, vs. eklinde gitmesi gerektiini grrz. Yani sola doru ilerlendiinde her basaman deeri 2 kat artmaldr: 1, 2, 4, 8, vs. rnein 1011 gibi bir ikili say yle ifade edilebilir: 1011 == 1 adet 8, 0 adet 4, 1 adet 2, 1 adet 1 Basamaklar numaralanrken; en sadaki, yani en dk deerli olan basamaa 0 numaral basamak denir. Buna gre, ikili say sisteminde yazlm olan 32 bitlik iaretsiz bir deerin btn basamaklarn ve basamak deerlerini yle gsterebiliriz:

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

406

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

Deeri 2,147,483,648 1,073,741,824 536,870,912 268,435,456 134,217,728 67,108,864 33,554,432 16,777,216 8,388,608 4,194,304 2,097,152 1,048,576 524,288 262,144 131,072 65,536 32,768 16,384 8,192 4,096 2,048 1,024 512 256 128 64 32 16 8 4 2 1

Yksek deerli bitlere st bit, dk deerli bitlere alt bit denir. kili sistemde yazlan deerlerin 0b ile baladklarn Hazr Deerler dersinde grmtk. kili sistemde deerler yazarak bu tabloya nasl uyduklarna bakabiliriz. Okumay kolaylatrmak iin alt izgi karakterlerinden de yararlanarak: import std.stdio; void main() { // 1073741824 4 1 // int say = 0b_01000000_00000000_00000000_00000101; writeln(say); } kts:

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

407

Bit lemleri

1073741829 Dikkat ederseniz, o hazr deerin iinde rakam 1 olan yalnzca 3 adet basamak vardr. Bu basamaklarn yukardaki tablodaki basamak deerlerini toplaynca bu deere eit olduunu grrz: 1073741824 + 4 + 1 == 1073741829.
67.2.1

aretli trlerin iaret biti


En st bit, iaretli trlerde saynn art veya eksi olduunu bildirmek iin kullanlr: int say = 0b_10000000_00000000_00000000_00000000; writeln(say);

-2147483648 En st bitin dierlerinden bamsz olduunu dnmeyin. rnein yukardaki say, dier bitlerinin 0 olmalarna bakarak -0 deeri olarak dnlmemelidir (zaten tamsaylarda -0 diye bir deer yoktur). Bunun ayrntsna burada girmeyeceim ve bunun D'nin de kulland ikiye tmleyen say gsterimi ile ilgili olduunu sylemekle yetineceim. Burada nemli olan, yukardaki tabloda gsterilen en yksek 2,147,483,648 deerinin yalnzca iaretsiz trlerde geerli olduunu bilmenizdir. Ayn deneyi uint ile yaptmzda tablodaki deeri grrz: uint say = 0b_10000000_00000000_00000000_00000000; writeln(say);

2147483648 Bu yzden, aksine bir neden olmad srece aada gsterilenler gibi bit ilemlerinde her zaman iin iaretsiz trler kullanlr: ubyte , uint , ve ulong .

67.3

On altl say sistemi


Yukardaki hazr deerlerden de grlebilecei gibi, ikili say sistemi grntlenmeye uygun deildir. Hem ok yer kaplar, hem de yalnzca 0 ve 1'lerden olutuu iin okunmas ve anlalmas zordur. Daha kullanl olduu iin on altl say sistemi yaygnlamtr. On altl say sisteminde toplam 16 rakam vardr. Alfabelerde 10'dan fazla rakam bulunmad iin, Latin alfabesinden 6 harf dn alnarak bu sistemin rakamlar olarak 0123456789abcdef kabul edilmitir. O sralamadan beklenecei gibi; a 10, b 11, c 12, d 13, e 14, ve f 15 deerindedir. abcdef harfleri yerine istee bal olarak ABCDEF harfleri de kullanlabilir. Yukardaki say sistemlerine benzer ekilde, bu sistemde sola doru ilerlendiinde her basaman deeri 16 kat artar: 1, 16, 256, 4096, vs. rnein, on altl sistemdeki 8 basamakl bir saynn basamak deerleri yledir:

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

408

Bit lemleri

Basamak 7 6 5 4 3 2 1 0

Deeri 268,435,456 16,777,216 1,048,576 65,536 4,096 256 16 1

On altl hazr deerlerin 0x ile yazldklarn hatrlayarak bir deneme: // 1048576 4096 1 // uint say = 0x_0030_a00f; writeln(say);

3186703 Bunun nedenini say iindeki sfr olmayan basamaklarn katklarna bakarak anlayabiliriz: 3 adet 1048576, a adet 4096, ve f adet 1. a'nn 10 ve f'nin 15 olduklarn hatrlayarak hesaplarsak: 3145728 + 40960 + 15 == 3186703. On altl ve ikili sistemde yazlan saylar kolayca birbirlerine dntrlebilirler. On altl sistemdeki bir sayy ikili sisteme dntrmek iin, saynn her basama ikili sistemde drt basamak olarak yazlr. Birbirlerine karlk gelen deerler yledir: On altl 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

rnein yukarda kullandmz 0x0030a00f on altl deerini ikili olarak yle yazabiliriz: // on altl: 0 0 3 0 a 0 0 f uint ikili = 0b_0000_0000_0011_0000_1010_0000_0000_1111; kili sistemden on altl sisteme dntrmek iin de ikili saynn her drt basama on altl sistemde tek basamak olarak yazlr. Yukarda ilk kullandmz ikili deer iin:

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

409

Bit lemleri

// ikili: 0100 0000 0000 0000 0000 0000 0000 0101 uint onAltl = 0x___4____0____0____0____0____0____0____5;

67.4

Bit ilemleri
Deerlerin bitlerle nasl ifade edildiklerini ve ikili veya onaltl olarak nasl yazldklarn grdk. imdi, deerleri bit dzeyinde deitiren ilemlere geebiliriz. Her ne kadar bit dzeyindeki ilemlerden bahsediyor olsak da, bitlere dorudan eriilemedii iin, bu ilemler en az 8 biti birden etkilemek zorundadr. rnein ubyte trndeki bir ifadenin 8 bitinin hepsi de, ama ayr ayr olarak bit ilemine dahil edilir. Ben st bitin zel anlam nedeniyle iaretli trleri gzard edeceim ve bu rneklerde uint trn kullanacam. Siz buradaki ilemleri ubyte ve ulong trleri ile, ve iaret bitinin nemini hatrlamak artyla byte , int , ve long trleri ile de deneyebilirsiniz. nce, aadaki ilemleri aklamada yardmc olacak bir ilev yazalm. Kendisine verilen sayy ikili, on altl, ve onlu sistemde gstersin: import std.stdio; void gster(uint say) { writefln("%032b %08x %10s", say, say, say); } void main() { gster(123456789); } Srasyla; ikili, on altl, ve onlu: 00000111010110111100110100010101 075bcd15 123456789

67.4.1

Tersini alma ileci ~


Bu ile, nne yazld ifadenin bitleri ters olann retir. 1 olan bitler 0, 0 olanlar 1 olur: uint deer = 123456789; write(" "); gster(deer); write("~ "); gster(~deer); Bu ilecin etkisi ikili gsteriminde ok kolay anlalyor. Her bit tersine dnmtr: 00000111010110111100110100010101 075bcd15 123456789 ~ 11111000101001000011001011101010 f8a432ea 4171510506 Bu ilecin bit dzeyindeki etkisini yle zetleyebiliriz: ~0 1 ~1 0

67.4.2

Ve ileci &
ki ifadenin arasna yazlr. ki ifadenin ayn numaral bitlerine srayla baklr. Sonu olarak, her iki ifadede de 1 olan bitler iin 1 deeri, dierleri iin 0 deeri retilir.

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

410

Bit lemleri

uint soldaki = 123456789; uint sadaki = 987654321; write(" "); gster(soldaki); write(" "); gster(sadaki); writeln("& --------------------------------"); write(" "); gster(soldaki & sadaki); Mikro ilemci bu ilemde her iki ifadenin 31, 30, 29, vs. numaral bitlerini ayr ayr kullanr. ktda nce soldaki ifadeyi, sonra da sadaki ifadeyi gryoruz. Kesikli izginin altnda da bit ileminin sonucu yazdrlyor: 00000111010110111100110100010101 075bcd15 00111010110111100110100010110001 3ade68b1 & -------------------------------00000010010110100100100000010001 025a4811 123456789 987654321 39471121

Dikkat ederseniz, kesikli izginin altna yazdrdm sonu deerde 1 olan bitler, izginin stndeki her iki ifadede de 1 deerine sahip olan bitlerdir. Bu ile bu yzden ve ileci olarak isimlendirilmitir: soldaki ve sadaki bit 1 olduunda 1 deerini retir. Bunu bir tablo ile gsterebiliriz. ki bitin 0 ve 1 olduklar drt farkl durumda ancak iki bitin de 1 olduklar durum 1 sonucunu verir: 0 0 1 1 & & & & 0 1 0 1 0 0 0 1

Gzlemler: Bir taraf 0 ise, dier taraftan bamsz olarak sonu 0'dr; 0 ile "ve"lemek, sfrlamak anlamna gelir Bir taraf 1 ise, sonu dierinin deeridir; 1 ile "ve"lemek etkisizdir
67.4.3

Veya ileci |
ki ifadenin arasna yazlr. ki ifadenin ayn numaral bitlerine srayla baklr. Her iki ifadede de 0 olan bitlere karlk 0 deeri retilir; dierlerinin sonucu 1 olur: uint soldaki = 123456789; uint sadaki = 987654321; write(" "); gster(soldaki); write(" "); gster(sadaki); writeln("| --------------------------------"); write(" "); gster(soldaki | sadaki);

00000111010110111100110100010101 075bcd15 123456789 00111010110111100110100010110001 3ade68b1 987654321 | -------------------------------00111111110111111110110110110101 3fdfedb5 1071639989 Dikkat ederseniz; sonuta 0 olan bitler, her iki ifadede de 0 olan bitlerdir. Bitin soldaki veya sadaki ifadede 1 olmas, sonucun da 1 olmas iin yeterlidir:

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

411

Bit lemleri

0 0 1 1

| | | |

0 1 0 1

0 1 1 1

Gzlemler: Bir taraf 0 ise, sonu dierinin deeridir; 0 ile "veya"lamak etkisizdir Bir taraf 1 ise, dier taraftan bamsz olarak sonu 1'dir; 1 ile "veya"lamak 1 yapmak anlamna gelir
67.4.4

Ya da ileci ^
ki ifadenin arasna yazlr. ki ifadenin ayn numaral bitlerine srayla baklr. ki ifadede farkl olan bitlere karlk 1 deeri retilir; dierlerinin sonucu 0 olur: uint soldaki = 123456789; uint sadaki = 987654321; write(" "); gster(soldaki); write(" "); gster(sadaki); writeln("^ --------------------------------"); write(" "); gster(soldaki ^ sadaki);

00000111010110111100110100010101 075bcd15 123456789 00111010110111100110100010110001 3ade68b1 987654321 ^ -------------------------------00111101100001011010010110100100 3d85a5a4 1032168868 Dikkat ederseniz; sonuta 1 olan bitler, soldaki ve sadaki ifadelerde farkl olan bitlerdir. kisinde de 0 veya ikisinde de 1 olan bitlere karlk 0 retilir. 0 0 1 1 ^ ^ ^ ^ 0 1 0 1 0 1 1 0

Gzlem: Kendisiyle "ya da"lamak sfrlamak anlamna gelir Deeri ne olursa olsun, ayn deikenin kendisiyle "ya da"lanmas 0 sonucunu retir: uint deer = 123456789; gster(deer ^ deer);

00000000000000000000000000000000 00000000

67.4.5

Saa kaydrma ileci >>


fadenin deerini oluturan bitleri belirtilen sayda basamak kadar saa kaydrr. Kaydrlacak yerleri olmayan en sadaki bitler derler ve deerleri kaybedilir. Sol taraftan yeni gelen bitler iaretsiz trlerde 0 olur. Bu rnek, bitleri 2 basamak kaydryor:

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

412

Bit lemleri

uint deer = 123456789; gster(deer); gster(deer >> 2); Sadan kaybedilecek olan bitleri griyle, soldan yeni gelen bitleri de maviyle iaretliyorum: 00000111010110111100110100010101 075bcd15 00000001110101101111001101000101 01d6f345 123456789 30864197

Dikkat ederseniz, alt satrdaki bitler st satrdaki bitlerin iki bit saa kaydrlmas ile elde edilmitir. Bitler saa kaydrlrken sol tarafa yeni gelenlerin 0 olduklarn grdnz. Bu, iaretsiz trlerde byledir. aretli trlerde ise iaret geniletilmesi (sign extension) denen bir yntem uygulanr ve saynn en soldaki biti ne ise, soldan hep o bitin deerinde bitler gelir. Bu etkiyi gstermek iin int trnde ve zellikle st biti 1 olan bir deer seelim: int deer = 0x80010300; gster(deer); gster(deer >> 3); Asl sayda st bit 1 olduu iin yeni gelen bitler de 1 olur: 10000000000000010000001100000000 80010300 2147549952 11110000000000000010000001100000 f0002060 4026540128 st bitin 0 olduu bir deerde yeni gelen bitler de 0 olur: int deer = 0x40010300; gster(deer); gster(deer >> 3);

01000000000000010000001100000000 40010300 1073808128 00001000000000000010000001100000 08002060 134226016

67.4.6

aretsiz saa kaydrma ileci >>>


Bu ile saa kaydrma ilecine benzer ekilde alr. Tek fark, iaret geniletilmesinin kullanlmamasdr. Trden ve en soldaki bitten bamsz olarak soldan her zaman iin 0 gelir: int deer = 0x80010300; gster(deer); gster(deer >>> 3);

10000000000000010000001100000000 80010300 2147549952 00010000000000000010000001100000 10002060 268443744

67.4.7

Sola kaydrma ileci <<


Saa kaydrma ilecinin tersi olarak, bitleri belirtilen basamak kadar sola kaydrr: uint deer = 123456789 ; gster(deer); gster(deer << 4);

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

413

Bit lemleri

En soldaki bit deerleri kaybedilir ve sa taraftan 0 deerli bitler gelir: 00000111010110111100110100010101 075bcd15 123456789 01110101101111001101000101010000 75bcd150 1975308624

67.4.8

Atamal bit ileleri


Btn bu ilelerin atamal olanlar da vardr: ~= , &= , |= , ^= , >>= , >>>= , ve <<= . Tamsaylar ve Aritmetik lemler dersinde grdmz atamal aritmetik ilelerine benzer ekilde, bunlar ilemi gerekletirdikten sonra sonucu soldaki ifadeye atarlar. rnek olarak &= ilecini kullanrsak: deer = deer & 123; deer &= 123;

// sttekiyle ayn ey

67.5

Anlamlar
Bu ilelerin bit dzeyinde nasl iledikleri, ilemlerin hangi anlamlarda grlmeleri gerektii konusunda yeterli olmayabilir. Burada bu anlamlara dikkat ekmek istiyorum.

67.5.1

| ileci, birleim kmesidir


ki ifadenin 1 olan bitlerinin birleimini verir. U bir rnek olarak, bitleri birer basamak atlayarak 1 olan ve birbirlerini tutmayan iki ifadenin birleimi, sonucun btn bitlerinin 1 olmasn salar: uint soldaki = 0xaaaaaaaa; uint sadaki = 0x55555555; write(" "); gster(soldaki); write(" "); gster(sadaki); writeln("| --------------------------------"); write(" "); gster(soldaki | sadaki);

10101010101010101010101010101010 aaaaaaaa 2863311530 01010101010101010101010101010101 55555555 1431655765 | -------------------------------11111111111111111111111111111111 ffffffff 4294967295

67.5.2

& ileci, kesiim kmesidir


Her iki ifadede de 1 olan bitlerin kesiimini verir. U bir rnek olarak, yukardaki iki ifadenin 1 olan hibir biti dierini tutmad iin, kesiimlerinin btn bitleri 0'dr: uint soldaki = 0xaaaaaaaa; uint sadaki = 0x55555555; write(" "); gster(soldaki); write(" "); gster(sadaki); writeln("& --------------------------------"); write(" "); gster(soldaki & sadaki);

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

414

Bit lemleri

10101010101010101010101010101010 aaaaaaaa 2863311530 01010101010101010101010101010101 55555555 1431655765 & -------------------------------00000000000000000000000000000000 00000000 0

67.5.3

|= ileci, belirli bitleri 1 yapar


fadelerden bir taraftakini asl deiken olarak dnrsek, dier ifadeyi de 1 yaplacak olan bitleri seen ifade olarak grebiliriz: uint ifade = 0x00ff00ff; uint birYaplacakBitler = 0x10001000; write("nce : write("1 olacaklar: "); gster(ifade); "); gster(birYaplacakBitler);

ifade |= birYaplacakBitler; write("sonra : "); gster(ifade); Etkilenen bitlerin nceki ve sonraki durumlarn maviyle iaretledim: nce : 1 olacaklar: sonra : 00000000111111110000000011111111 00ff00ff 00010000000000000001000000000000 10001000 00010000111111110001000011111111 10ff10ff 16711935 268439552 285151487

birYaplacakBitler deeri, bir anlamda hangi bitlerin 1 yaplacaklar bilgisini tam ve asl ifadenin o bitlerini 1 yapm ve dierlerine dokunmamtr.
67.5.4

&= ileci, belirli bitleri siler


fadelerden bir taraftakini asl deiken olarak dnrsek, dier ifadeyi de silinecek olan bitleri seen ifade olarak grebiliriz: uint ifade = 0x00ff00ff; uint sfrYaplacakBitler = 0xffefffef; write("nce : write("silinecekler: "); gster(ifade); "); gster(sfrYaplacakBitler);

ifade &= sfrYaplacakBitler; write("sonra : "); gster(ifade); Etkilenen bitlerin nceki ve sonraki durumlarn yine maviyle iaretleyerek: nce : silinecekler: sonra : 00000000111111110000000011111111 00ff00ff 16711935 11111111111011111111111111101111 ffefffef 4293918703 00000000111011110000000011101111 00ef00ef 15663343

sfrYaplacakBitler deeri, hangi bitlerin sfrlanacaklar bilgisini tam ve asl ifadenin o bitlerini sfrlamtr.
67.5.5

& ileci, belirli bir bitin 1 olup olmadn sorgular


Eer ifadelerden birisinin tek bir biti 1 ise, dier ifadede o bitin 1 olup olmad sorgulanabilir:

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

415

Bit lemleri

uint ifade = 123456789; uint sorgulananBit = 0x00010000; gster(ifade); gster(sorgulananBit); writeln(ifade & sorgulananBit ? "evet, 1" : "1 deil"); Asl ifadenin hangi bitinin sorgulandn mavi ile iaretliyorum: 00000111010110111100110100010101 075bcd15 00000000000000010000000000000000 00010000 evet, 1 Baka bir bitini sorgulayalm: uint sorgulananBit = 0x00001000; 123456789 65536

00000111010110111100110100010101 075bcd15 00000000000000000001000000000000 00001000 1 deil

123456789 4096

Sorgulama ifadesinde birden fazla 1 kullanarak o bitlerin hepsinin birden asl ifadede 1 olup olmadklar da sorgulanabilir.
67.5.6

Saa kaydrmak ikiye blmektir


Saa bir bit kaydrmak, deerin yarya inmesine neden olur. Bunu yukardaki basamak deerleri tablosunda grebilirsiniz: bir sadaki bit her zaman iin soldakinin yars deerdedir. Saa birden fazla sayda kaydrmak, o kadar sayda yarya blmek anlamna gelir. rnein 3 bit kaydrmak, 3 kere 2'ye blmek, yani sonuta 8'e blmek anlamna gelir: uint deer = 8000; writeln(deer >> 3);

1000 Ayrntsna girmediim ikiye tmleyen sistemi nedeniyle, saa kaydrmak iaretli trlerde de ikiye blmektir: int deer = -8000; writeln(deer >> 3);

-1000

67.5.7

Sola kaydrmak iki katn almaktr


Basamaklar tablosundaki her bitin, bir sandakinin iki kat olmas nedeniyle, bir bit sola kaydrmak 2 ile arpmak anlamna gelir:

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

416

Bit lemleri

uint deer = 10; writeln(deer << 5); Be kere 2 ile arpmak, 32 ile arpmann edeeridir: 320

67.6

Baz kullanmlar
Bayraklar
Bayraklar, birbirlerinden bamsz olarak bir arada tutulan tek bitlik verilerdir. Tek bitlik olduklar iin var/yok, olsun/olmasn, geerli/geersiz gibi iki deerli kavramlar ifade ederler. Her ne kadar byle tek bitlik bilgilerin yaygn olmadklarn sylemi olsam da, bazen bir arada kullanlrlar. Bayraklar zellikle C ktphanelerinde yaygndr. C'den uyarlanan D ktphanelerinde bulunmalar da beklenebilir. Bayraklar bir enum trnn birbirleriyle rtmeyen tek bitlik deerleri olarak tanmlanrlar. Bir rnek olarak, araba yaryla ilgili bir oyun program dnelim. Bu programn gerekilii kullanc seimlerine gre belirlensin: benzin, kullanma gre azalabilsin arpmalar hasar brakabilsin lastikler kullanma gre eskiyebilsin lastikler yolda iz brakabilsin

67.6.1

Oyun srasnda bunlardan hangilerinin etkin olacaklar bayrak deerleriyle belirtilebilir: enum Gerekilik { benzinBiter hasarOluur lastiklerEskir lastikzleriOluur }

= = = =

1 1 1 1

<< << << <<

0, 1, 2, 3

Dikkat ederseniz, o enum deerlerinin hepsi de tek bir bitten olumakta ve birbirleriyle akmamaktadr. Her deer, 1'in farkl sayda sola telenmesi ile elde edilmitir. Bit deerlerinin yle olduklarn grebiliriz: benzinBiter hasarOluur lastiklerEskir lastikzleriOluur : : : : ...0001 ...0010 ...0100 ...1000

Hibir bit dierlerininkiyle akmad iin, bu deerler | ile birletirilebilir ve hep birden tek bir deikende bulundurulabilir. rnein yalnzca lastiklerle ilgili ayarlarn etkin olmalar istendiinde, deer yle kurulabilir: Gerekilik ayarlar = Gerekilik.lastiklerEskir | Gerekilik.lastikzleriOluur; writefln("%b", ayarlar); Bu iki bayran bitleri, ayn deer iinde yan yana bulunurlar:

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

417

Bit lemleri

1100 Daha sonradan, programn asl ileyii srasnda, bu bayraklarn etkin olup olmadklar & ileci ile denetlenir: if (ayarlar & Gerekilik.benzinBiter) { // ... benzinin azalmasyla ilgili kodlar ... } if (ayarlar & Gerekilik.lastiklerEskir) { // ... lastiklerin eskimesiyle ilgili kodlar ... } & ilecinin sonucu, ancak belirtilen bayrak ayarlar iinde de 1 ise 1 sonucunu verir. if kouluyla kullanlabilmesinin bir nedeninin de Tr Dnmleri dersinden hatrlayacanz gibi, 1 deerinin otomatik olarak true 'ya dnmesidir. & ilecinin sonucu 0 olduunda false , farkl bir deer olduunda da true deerine dnr; ve bayran etkin olup olmad bylece anlalm olur.
67.6.2

Bayrak rnei
rnek olarak, Phobos'un std.cpuid modlnn kaynak kodlarna bakalm. Bu modl, mikro ilemci ile ilgili ok sayda bilgiyi yledir/deildir, destekler/desteklemez, vs. gibi tek bitlik olarak ierir: import std.cpuid; import std.stdio; void main() { writeln(std.cpuid.toString()); } Bu dersi yazdm ortamda aldm kt yle: Vendor string: Processor string: Signature: Features: Multithreading: GenuineIntel Intel(R) Core(TM)2 CPU T7400 @ 2.16GHz Family=6 Model=15 Stepping=6 MMX FXSR SSE SSE2 SSE3 SSSE3 AMD64 HTT 2 threads / 2 cores

O ktdaki Features satr, bu ilemcinin yetenekleri ile ilgili bilgi verir. O satrdaki her szck, o yetenein bu ilemcide var olduunu belirtir. Listelenmedii iin bilemediimiz yetenekler bu ilemcide bulunmaz. rnein, dmd/src/phobos/std/cpuid.d dosyasnn iine bakarak haberdar olduum IA64 yetenei, bu ortamda listelenmemektedir. Mikro ilemcinin yeteneklerini belirleyen bayraklardan bazlar, cpuid.d dosyasnda yle tanmlanmtr: MMX_BIT = 1<<23, FXSR_BIT = 1<<24, SSE_BIT = 1<<25, SSE2_BIT = 1<<26, HTT_BIT = 1<<28, IA64_BIT = 1<<30

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

418

Bit lemleri

Bu modldeki baz ilevler, belirli bayraklarn etkin olup olmadklarn & ilecinin sonucuna bakarak bildirir: bool mmx() {return (flags&MMX_BIT)!=0;}

lgin bir gzlem olarak, & ilecinin sonucu bool 'a otomatik olarak deil, aka !=0 karlatrmasyla dntrlyor. ki yntem de dorudur ve ayn etkiye sahiptir.
67.6.3

Maskeleme
Baz ktphanelerde ve sistemlerde, belirli bir tamsay deer iine birden fazla bilgi yerletirilmi olabilir. rnein 32 bitlik bir deerin st 3 bitinin belirli bir anlam ve alt 29 bitinin baka bir anlam bulunabilir. Bu veriler, maskeleme yntemiyle birbirlerinden ayrlabilirler. Bunun bir rneini IP adreslerinde grebiliriz. IP adresleri a paketleri iinde 32 bitlik tek bir deer olarak bulunurlar. Bu 32 bitin 8'er bitlik 4 paras, gnlk kullanmdan alk olduumuz noktal adres gsterimi deerleridir. rnein 192.168.1.2 gibi bir adres, 32 bit olarak 0xc0a80102 deeridir: c0 a8 01 02 == 12 * 16 + == 10 * 16 + == 0 * 16 + == 0 * 16 + 0 8 1 2 = 192 = 168 = 1 = 2

Maske, ilgilenilen veri ile rten sayda 1'lerden oluur. Asl deiken bu maske ile "ve"lendiinde, yani & ileci ile kullanldnda verinin deerleri elde edilir. rnein 0x000000ff gibi bir maske deeri; dier ifadenin alt 8 bitini olduu gibi korur, dier bitlerini sfrlar: uint deer = 123456789; uint maske = 0x000000ff; write("deer: "); gster(deer); write("maske: "); gster(maske); write("sonu: "); gster(deer & maske); Maskenin seerek koruduu bitleri mavi ile iaretliyorum. Dier btn bitler sfrlanmtr: deer: 00000111010110111100110100010101 075bcd15 maske: 00000000000000000000000011111111 000000ff sonu: 00000000000000000000000000010101 00000015 123456789 255 21

Bu yntemi 0xc0a80102 IP adresine ve en st 8 biti seecek bir maskeyle uyguladmzda noktal gsterimin ilk adres deerini elde ederiz: uint deer = 0xc0a80102; uint maske = 0xff000000; write("deer: "); gster(deer); write("maske: "); gster(maske); write("sonu: "); gster(deer & maske); Maskenin st bitleri 1 olduundan, deerin de st bitleri seilmi olur:

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

419

Bit lemleri

deer: 11000000101010000000000100000010 c0a80102 3232235778 maske: 11111111000000000000000000000000 ff000000 4278190080 sonu: 11000000000000000000000000000000 c0000000 3221225472 Ancak, sonucun onlu gsterimi beklediimiz gibi 192 deil, 3221225472 olmutur. Bunun nedeni, maskelenen 8 bitin deerin en sa tarafna kaydrlmalarnn da gerekmesidir. O 8 biti 24 bit saa kaydrrsak deerlerini doru olarak elde ederiz: uint deer = 0xc0a80102; uint maske = 0xff000000; write("deer: "); gster(deer); write("maske: "); gster(maske); write("sonu: "); gster((deer & maske) >> 24);

deer: 11000000101010000000000100000010 c0a80102 3232235778 maske: 11111111000000000000000000000000 ff000000 4278190080 sonu: 00000000000000000000000011000000 000000c0 192

67.7

Problemler
1. Verilen IP adresini noktal gsteriminde yazdran bir ilev yazn: string noktalOlarak(uint ipAdresi) { // ... } unittest { assert(noktalOlarak(0xc0a80102) == "192.168.1.2"); } 2. Verilen 4 deeri 32 bitlik IP adresine dntren bir ilev yazn: uint ipAdresi(ubyte ubyte ubyte ubyte { // ... } bayt3, bayt2, bayt1, bayt0) // en yksek deerli bayt // en dk deerli bayt

unittest { assert(ipAdresi(192, 168, 1, 2) == 0xc0a80102); } 3. Maske oluturan bir ilev yazn. Belirtilen bit ile balayan ve belirtilen uzunlukta olan maske olutursun: uint maskeYap(int dkBit, int uzunluk) { // ... } unittest { assert(maskeYap(1, 5) == 0b_0000_0000_0000_0000_0000_0000_0011_1110); //

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

420

Bit lemleri

} ... zmler

// //

balang biti 1, ve 5 bitten oluuyor

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

421

Koullu Derleme

68

Koullu Derleme
Programn baz blmlerinin belirli koullara bal olarak farkl derlenmesi, veya hi derlenmemesi istenebilir. D'nin koullu derleme olanaklar bu konuda kullanlr. Derleme ile ilgili olduklar iin, bu koullar yalnzca derleme zamannda deerlendirilir; programn almas srasnda etkileri yoktur. alma zamannda etkili olan if , for , while gibi D olanaklar koullu derleme olanaklar deildir. Aslnda nceki derslerde koullu derleme olarak kabul edilebilecek baka olanaklar grmtk: Birim testi bloklarndaki kodlar yalnzca -unittest derleyici seenei kullanldnda derlenir ve altrlr Szlemeli programlama olanaklar olan in , out , ve invariant bloklar -release seenei kullanlmad zaman etkindir Yukardakiler, programn doruluunu arttrma amacna ynelik yardmc olanaklar olarak grlebilir. Derleyici seeneklerine bal olarak kullanlp kullanlmamalar, programn asl davrann zaten deitirmemelidir. ablon zellemeleri, yalnzca belirtilen trler iin etkilidir. O trler programda kullanlmadnda, zelleme kodu derlenmez ve programa dahil edilmez: void deiToku(T)(ref T birinci, ref T ikinci) { T geici = birinci; birinci = ikinci; ikinci = geici; } unittest { auto bir = ''; auto iki = ''; deiToku(bir, iki); assert(bir == ''); assert(iki == '');

void deiToku(T : uint)(ref T birinci, ref T ikinci) { birinci ^= ikinci; ikinci ^= birinci; birinci ^= ikinc; // DKKAT: sondaki i harfi unutulmu! } void main() {} Yukardaki ilevin uint zellemesi, daha hzl olaca dnld iin bit ileci ^ (ya da ilemi) kullanlarak yazlm. Sonundaki yazm hatasna ramen, yukardaki program derlenir ve alr. Bunun nedeni, deiToku ilevinin uint tr ile arlmam olmas, ve bu yzden uint zellemesinin derlenmemi olmasdr. O programdaki hata, ilev uint tr ile arlana kadar ortaya kmaz. Bunu, birim testlerinin nemini gsteren bir baka rnek olarak da grebilirsiniz. Birim testi de yazlm olsa, o zellemedeki hata, ilev yazld srada ortaya kar:

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

422

Koullu Derleme

unittest { uint i = 42; uint j = 7; deiToku(i, j); assert(i == 7); assert(j == 42);

Grld gibi, ablon zellemelerinin de koullu olarak derlendiklerini kabul edebiliriz. D'nin koullu derlemeyi destekleyen ve btnyle bu ama iin tasarlanm olana daha vardr: debug version static if

68.1

debug
Program gelitirme aamasnda yararl olan bir olanak debug belirtecidir. Bu belirtele iaretlenmi olan ifadeler ve bloklar yalnzca derleyicinin -debug seenei kullanldnda etkilidir: debug koullu_derlenen_bir_ifade; debug { // ... koullu derlenen ifadeler ... } Yukardaki tek ifade de, blok iindeki ifadeler de ancak -debug derleyici seenei etkin olduunda derlenir. imdiye kadarki programlarn hemen hemen hepsinde programn nasl ilediini gsteren "ekliyorum", "kartyorum", gibi satrlar kullandk. Bylece algoritmalarn ileyilerini grsel bir hale getirerek olas hatalarn bulabiliyorduk. "debug", hata gidermek anlamna gelir ve bu konuda yararldr. Bunun bir rnei olarak ablonlar dersinde grdmz ikiliAra ilevine bakalm. O algoritmann aklama satrlarn kartyorum ve bilerek hatal olarak yazyorum: import std.stdio; // DKKAT! Bu algoritma hataldr int ikiliAra(const int[] deerler, in int deer) { if (deerler.length == 0) { return -1; } const auto ortaNokta = deerler.length / 2; if (deer == deerler[ortaNokta]) { return ortaNokta; } else if (deer < deerler[ortaNokta]) { return ikiliAra(deerler[0 .. ortaNokta], deer); } else {

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

423

Koullu Derleme

return ikiliAra(deerler[ortaNokta + 1 .. $], deer);

void main() { int[] saylar = [ -100, 0, 1, 2, 7, 10, 42, 365, 1000 ]; int indeks = ikiliAra(saylar, 42); writeln("Konum: ", indeks);

Yukardaki program altrdmzda, 42'nin 6 olarak bildirilmesi gereken konumunun yanl bildirildiini grrz: Konum: 1 Bu hatay bulmann bir yolu, ilevin nemli noktalarna ilemler hakknda bilgiler veren satrlar eklemektir: int ikiliAra(const int[] deerler, in int deer) { writeln(deerler, " iinde ", deer, " aryorum"); if (deerler.length == 0) { writeln(deer, " bulunamad"); return -1; } const auto ortaNokta = deerler.length / 2; writeln("baklan konum: ", ortaNokta); if (deer == deerler[ortaNokta]) { writeln(deer, ", ", ortaNokta, " konumunda bulundu"); return ortaNokta; } else if (deer < deerler[ortaNokta]) { writeln("ilk yarda olmas gerek"); return ikiliAra(deerler[0 .. ortaNokta], deer); } else { writeln("son yarda olmas gerek"); return ikiliAra(deerler[ortaNokta + 1 .. $], deer); }

Programn imdiki kts, algoritmann ileyi admlarn gsterir: [-100,0,1,2,7,10,42,365,1000] iinde 42 aryorum baklan konum: 4 son yarda olmas gerek [10,42,365,1000] iinde 42 aryorum baklan konum: 2 ilk yarda olmas gerek [10,42] iinde 42 aryorum baklan konum: 1 42, 1 konumunda bulundu Konum: 1 Hatann bu ktdan yararlanlarak bulunduunu ve giderildiini varsayalm. Hata giderildikten sonra artk writefln satrlarna gerek kalmaz ve hatta silinmeleri gerekir. Buna

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

424

Koullu Derleme

ramen, o satrlar silmek de bir israf olarak grlebilir; nk bugn silinirlerse, belki de ileride tekrar gerekebilirler. Onun yerine, bu satrlarn bana debug anahtar szc yazlabilir: debug writeln(deer, " bulunamad"); O satrlar artk yalnzca -debug derleyici seenei kullanldnda etkin olacaktr: dmd deneme.d -ofdeneme -w -debug Bylece, programn normal ileyii srasnda ktya hibir bilgi yazdrlmayacak; bir hata grldnde ise -debug kullanlarak algoritmann ileyii hakknda bilgi alnabilecektir.
68.1.1

debug(isim)
debug belirtecinin ok yerde kullanlmas durumunda programn kts ok kalabalklaabilir. Byle durumlarda debug belirtelerine isimler verebilir ve onlarn yalnzca komut satrnda belirtilenlerinin etkinlemelerini salayabiliriz: debug(ikili_arama) writeln(deer, " bulunamad"); simli debug belirtelerini etkinletirmek iin komut satrnda -debug=isim yazlr: dmd deneme.d -ofdeneme -w -debug=ikili_arama simli debug belirteleri de birden fazla ifade iin kullanlabilir: debug(ikili_arama) { // ... koullu derlenen ifadeler ... } Ayn anda birden ok isimli debug belirteci de kullanlabilir: $ dmd deneme.d -ofdeneme -w -debug=ikili_arama -debug=yigin_yapisi O durumda hem ikili_arama , hem de yigin_yapisi isimli debug bloklar etkin olur.

68.1.2

debug(dzey)
Bazen debug belirtelerine isimler vermek yerine, hata ayklama dzeylerini belirleyen saysal deerler verilebilir. Artan her dzey, daha derinlemesine bilgi verir: debug import std.stdio; void birlev(string dosyasmi, int[] saylar) { debug(1) writeln("birlev ilevine girildi"); debug(2) { writeln("ilev parametreleri: "); writeln(" isim: ", dosyasmi); foreach (i, say; saylar) { writefln(" %4s: %s", i, say); }

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

425

Koullu Derleme

// ... asl ilemler ...

Derleyiciye belirtilen debug dzeyi, o dzey ve daha dk olanlarnn etkinlemesini salar: $ dmd deneme.d -ofdeneme -w -debug=1 $ ./deneme birlev ilevine girildi Daha derinlemesine bilgi almak istendiinde: $ dmd deneme.d -ofdeneme -w -debug=2 $ ./deneme birlev ilevine girildi ilev parametreleri: isim: deneme.txt 0: 10 1: 4 2: 100

68.2

version(isim) , ve version(dzey)
version , debug olanana ok benzer ve kod iinde ayn ekilde kullanlr: version (denemeSrm) /* ... bir ifade ... */; version (okulSrm) { // ... okullara satlan srmle ilgili ifadeler ... } version(1) birDeiken = 5; version(2) { // ... srm 2 ile ilgili bir olanak ... } Btnyle ayn ekilde alyor olsa da, debug 'dan fark, program iin farkl srmler oluturma amacyla kullanlmasdr. Yine debug 'da olduu gibi, ayn anda birden fazla version blou etkinletirilebilir: $ dmd deneme.d -ofdeneme -w -version=kayit -version=hassas_hesap Baz version isimleri, programlarmzda yararlanabilmemiz iin hazr olarak tanmldr:

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

426

Koullu Derleme

ntanml version belirteleri 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, Digital Mars'nki Intel veya AMD 32 bit ilemcisi AMD veya Intel 64 bit ilemcisi Microsoft Windows sistemi Microsoft 32 bit Windows sistemi Microsoft 64 bit Windows sistemi Btn Linux sistemleri Btn Linux sistemleri Bayt sras; dk deerlinin nce olduu durum Bayt sras; yksek deerlinin nce olduu durum letilen btn kod satrlarn belirleyecek ekilde derleniyor (Code coverage analysis, -cov derleyici seenei Ddoc belgeleri retiliyor (-D derleyici seenei) X86 inline assembler' mevcut Gstergeler 64 bitlik (-m64 derleyici seenei) Position Independent Code retiliyor (-fPIC derleyici seenei) Birim testleri etkin (-unittest derleyici seenei) Bir D2 derleyicisi ile derlenmekte Hibir zaman tanml deildir; kod blounu etkisizletirir Her zaman iin tanmldr; none'n tersidir

D_InlineAsm_X86_64 X86-64 inline assembler' mevcut

O tanmlardan yararlanarak programnzn farkl olanaklarla derlenmesini salayabilirsiniz. Kullanm rnei olarak std.string modlnde tanml olan newline 'a bakalm: version (Windows) immutable char[2] newline = "\r\n"; else version (Posix) immutable char[1] newline = "\n"; Satr sonu anlamna gelen kodlar belirleyen newline dizisi, zerinde derlenmekte olduu iletim sistemine gre farkl olarak tanmlanmaktadr.

68.3

debug 'a ve version 'a isim atamak


debug ve version 'a sanki bir deikenmi gibi isim atanabilir. Deikenlerden farkl olarak, atama ilemi deer deitirmez, deer olarak belirtilen debug veya version isminin de etkinlemesini salar. import std.stdio; debug(hepsi) { debug = 1; debug = 2; version = denemeSrm; version = okulSrm; } void main() { debug(1) writeln("debug dzeyi 1");

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

427

Koullu Derleme

debug(2) writeln("debug dzeyi 2"); version(denemeSrm) writeln("deneme srm"); version(okulSrm) writeln("okul srm");

Yukardaki koddaki debug(hepsi) blou iindeki atamalar, her satrda belirtilen ismin de etkinlemesini salar. Bylece bu program iin derleme satrnda drt debug ve version seenei farkl olarak seilebilecei gibi, -debug=hepsi kullanldnda; 1, 2, denemeSrm, ve okulSrm sanki komut satrnda bildirilmiler gibi etkinleir: $ dmd deneme.d -ofdeneme -w -debug=hepsi $ ./deneme debug dzeyi 1 debug dzeyi 2 deneme srm okul srm

68.4

static if
Programn almas srasndaki kararlarda ok kullandmz if koulunun derleme zamanndaki edeeri static if 'tir. if koulunda olduu gibi, static if koulu da bir mantksal ifade ile kullanlr. static if bloundaki kodlar bu mantksal ifade true olduunda derlenir ve programa dahil edilir, false olduunda ise dahil edilmez. Yine if 'e benzer ekilde, else static if ve else bloklar da bulunabilir. if 'ten farkl olarak, mantksal ifadenin sonucunun derleme zamannda bilinebiliyor olmas arttr. static if her kapsamda kullanlabilir: modl dosyasnda en st dzeyde, yap, snf, ablon, ilev, vs. kapsamlarnda... Koul salandnda, blok iindeki kodlar yazldklar satrda programa dahil edilirler. Bir rnek olarak main iinde kullanalm: import std.stdio; void main() { static if (char.sizeof == 1) { writeln("Merhaba dnya"); } else static if (char.sizeof == 2) { writeln("Selam dnya"); } else { writeln("Zaten beraberiz"); }

char tek baytlk bir tr olduu iin, o program yalnzca "Merhaba dnya"l satr yazlm gibi derlenir: import std.stdio; void main() {

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

428

Koullu Derleme

writeln("Merhaba dnya");

alma zamannda hibir karlatrma yaplmaz. static if zincirleri olutururken else static if yazmak gerektiine dikkat edin. Yanllkla else if yazlmsa, static if 'in else blou olarak if kullanlacak demektir ve if de doal olarak alma zamannda iletilecektir. static if , ounlukla biraz aada greceimiz is ifadesi ile kullanlr.

68.5

static assert
Aslnda bir koullu derleme olana olarak kabul edilmese de, static if 'e benzerlii nedeniyle burada anlatmaya karar verdim. alma zamannda kullanmaya altmz assert 'le ayn ekilde, ama derleme zamannda iletilir. Mantksal ifadesi false olduunda derlemenin bir hata ile sonlandrlmasn salar. static if gibi, static assert de programda herhangi bir kapsamda bulunabilir. rnek olarak, belirli bir algoritmann yalnzca belirli byklkteki trlerle doru olarak alabildiini varsayalm. Bunu bir static assert ile yle denetleyebiliriz: void birAlgoritma(T)(T deer) { // Bu algoritma, bykl drdn kat olan trlerle // alabilir static assert((T.sizeof % 4) == 0); } // ...

O ilev ablonu rnein char ile arldnda, programn derlenmesi bir hata ile sonlanr: Error: static assert (1u == 0u) is false

Bylece algoritmann uygunsuz bir trle kullanlmasnn ve olaslkla hatal almasnn nne geilmektedir. static assert de aadaki is ifadesinden yararlanabilir.

68.6

is ifadesi
Bu ifade, daha nce null deeri ve is ileci dersinde grdmz is ilecinden anlam ve yazm asndan farkldr: a is b is (/* ... */) // daha nce grdmz is ileci // is ifadesi

Bu blmn konusu olan is ifadesi derleme zamannda iletilir, ve parantez iindeki koula bal olarak bir deer retir. rettii deerin tr int 'tir; koul geerli olduunda 1, geersiz olduunda 0 deerini alr. is 'in ald koul bir mantksal ifade deildir; ama is 'in kendisinin deeri bir mantksal ifadede kullanlmaya elverilidir. rnein if deyimiyle, ve derleme zamannda iletildii iin daha da uygun olarak static if deyimiyle kullanlabilir.

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

429

Koullu Derleme

Ald koul trlerle ilgilidir ve bir ka zel biimden birisi olarak yazlmak zorundadr. En ok ablon parametrelerini denetlemede ve ablon parametre trleriyle ilgili bilgi toplamada yararldr.
68.6.1

is (Tr)
Tr'n anlamsal olarak geerli bir tr olup olmadn denetler. is 'in bu kullanm iin tek bana rnekler bulmak olduka zor. Bunun ablon parametrelerinde yararl olacan dnebilirsiniz: static if (is (int)) { writeln("geerli"); } else { writeln("geersiz"); } Yukardaki koulda kullanlan int , geerli bir trdr: geerli Baka bir rnek olarak, eleme tablosu indeks tr olarak void kullanmak geersiz olduu iin, bu rnekte else blou iletilir: static if (is (string[void])) { writeln("geerli"); } else { writeln("geersiz"); }

geersiz

68.6.2

is (Tr Takmasim)
Yukardaki ile ayn ekilde alr. Ek olarak, koul geerli olduunda Takmasim'i trn yeni takma ismi olarak tanmlar: static if (is (int Yenisim)) { writeln("geerli"); Yenisim deiken = 42; // int ve Yenisim ayn ey } else { writeln("geersiz"); } Takma ismin bu ekilde is ifadesinin iinde tanmlanabilmesi, daha sonra greceimiz karmak is ifadelerinde yararldr.

68.6.3

is (Tr : zelTr)
Tr'n belirtilen zel tre otomatik olarak dnp dnemediini denetler. Tr Dnmleri dersinde grdmz temel tr dnmlerini, veya Treme dersinde grdmz "bu alt snf, o st snfn trndendir" ilikilerini denetlemede kullanlr. import std.stdio; interface Saat { void zamanOku();

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

430

Koullu Derleme

} class alarSaat : Saat { override void zamanOku() { writeln("10:00"); } } void birlev(T)(T nesne) { static if (is (T : Saat)) { // Eer buraya geldiysek, ablon parametresi olan T, // Saat'ten tremi bir trdr writeln("bu bir Saat; zaman syleyebiliriz"); nesne.zamanOku(); } else { writeln("bu bir Saat deil"); }

void main() { auto deiken = new alarSaat; birlev(deiken); birlev(42); } O kod, birlev ablonu Saat 'e dnebilen bir tr ile arldnda, nesne 'nin zamanOku ilevini de armaktadr. Tr int olduunda ise else blou iletilmektedir: bu bir Saat; zaman syleyebiliriz 10:00 bu bir Saat deil alarSaat iin alarSaat iin int iin

68.6.4

is (Tr Takmasim : zelTr)


Yukardakiyle ayn ekilde alr. Ek olarak, koul geerli olduunda Takmasim'i koulu salayan trn yeni takma ismi olarak tanmlar.

68.6.5

is (Tr == zelTr)
Tr'n belirtilen zel trn ayns olup olmadn, veya ayn belirtece sahip olup olmadn denetler. Ayn tr anlamnda: Yukardaki rnek kodu deitirsek ve : yerine == kullansak, bu sefer alarSaat iin de geersiz olacaktr: static if (is (T == Saat)) { writeln("bu bir Saat; zaman syleyebiliriz"); nesne.zamanOku(); } else { writeln("bu bir Saat deil"); } alarSaat Saat 'ten tredii iin bir Saat 'tir, ama Saat 'in ayns deildir. O yzden koul hem alarSaat iin, hem de int iin geersizdir:

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

431

Koullu Derleme

bu bir Saat deil bu bir Saat deil Ayn belirtece sahip anlamnda: zelTr yerine bir belirte kullanldnda, trn o belirtece uyup uymadn denetler. Bu kullanmda, belirte olarak u anahtar szckler kullanlabilir (bu anahtar szcklerden bazlarn daha sonraki derslerde anlatacam): struct union class interface enum function delegate const immutable shared

void birlev(T)(T nesne) { static if (is (T == class)) { writeln("bu bir snf tr"); } else static if (is (T == enum)) { writeln("bu bir enum"); } else static if (is (T == const)) { writeln("bu 'const' bir tr"); } else { writeln("bu baka bir tr"); }

Bylece; o ilev ablonu, arld tre gre deiik davranacak ekilde kodlanabilir. Koulun deiik bloklarnn etkinletiini gstermek iin yle deneyebiliriz: auto deiken = new alarSaat; birlev(deiken); birlev(HaftaGnleri.Pazartesi); const double say = 1.2; birlev(say); birlev(42);

bu bu bu bu

bir snf tr bir enum 'const' bir tr baka bir tr

68.6.6

is (Tr isim == Belirte)


Yukardaki ile ayn ekilde alr. Ek olarak, koul geerli olduunda isim'i duruma gre farkl anlamlarda tanmlar. isim, yukardaki takma isimli kullanmlardaki gibi dorudan trn takma ismi olabilecei gibi, belirtece bal olarak baka bir bilgi de olabilir:

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

432

Koullu Derleme

Belirte struct union class interface super enum function delegate return const immutable shared koulu salayan tr koulu salayan tr koulu salayan tr koulu salayan tr

isim'in tanm

st tr ve arayzlerden oluan okuzlu enum 'un gerekletirildii temel tr ilev parametrelerinden oluan okuzlu delegate 'in tr ilevin, delegate 'in, veya ilev gstergesinin dn tr koulu salayan tr koulu salayan tr koulu salayan tr

Bu olanan nasl altn gstermek iin nce baz trler tanmlayalm: struct Nokta { // ... } interface Saat { // ... } class alarSaat : Saat { // ... } enum HaftaGnleri { Pazartesi, Sal, aramba, Perembe, Cuma, Cumartesi, Pazar } char foo(double kesirli, int tamsay, Saat saat) { return 'a'; } is ifadesinin bu deiik trlerle kullanmlarn gstermek iin aadaki gibi bir ilev ablonu yazlabilir. levin arld trlerin, nesnelerin, ve isim'in ne anlamlara geldiklerini aklama satrlar olarak yazdm: void birlev(T)(T nesne) { static if (is (T YerelTr == struct)) { writefln("\n--- struct ---"); // T ve YerelTr ayn anlamdadr; 'nesne', bu ileve // gelen yap nesnesidir writeln("Yeni bir ", YerelTr.stringof, " nesnesini kopyalayarak oluturuyorum"); YerelTr yeniNesne = nesne;

static if (is (T stTrler == super)) { writeln("\n--- super ---"); // 'stTrler' okuzlusu btn st trleri ierir; // 'nesne', bu ileve gelen snf nesnesidir

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

433

Koullu Derleme

writeln(T.stringof, " snfnn ", stTrler.length, " adet st tr var"); writeln("hepsi birden: ", stTrler.stringof); writeln("en stteki: ", stTrler[0].stringof);

static if (is (T AslTr == enum)) { writeln("\n--- enum ---"); // 'AslTr', enum deerlerini gerekletirmek iin // kullanlan asl trdr; 'nesne', bu ileve gelen // enum deeridir writeln(T.stringof, " enum tr, perde arkasnda ", AslTr.stringof, " olarak gerekletirilmitir");

static if (is (T DnTr == return)) { writeln("\n--- return ---"); // 'DnTr', ilevin dn trdr; bu ileve // parametre olarak gelen 'nesne', bir ilev // gstergesidir writeln("Bu, dn tr ", DnTr.stringof, " olan bir ilev:"); writeln(" ", T.stringof); write("aryoruz... "); // Not: lev gstergeleri ilev gibi arlabilirler DnTr sonu = nesne(1.5, 42, new alarSaat); writeln("ve sonu: ", sonu);

O ilevi yukardaki farkl trlerle yle arabiliriz: // Yap nesnesiyle birlev(Nokta()); // Snf nesnesiyle birlev(new alarSaat); // enum deerle birlev(HaftaGnleri.Pazartesi); // lev gstergesiyle birlev(&foo); kts:

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

434

Koullu Derleme

--- struct --Yeni bir Nokta nesnesini kopyalayarak oluturuyorum --- super --alarSaat snfnn 2 adet st tr var hepsi birden: (in Object, in Saat) en stteki: Object --- enum --HaftaGnleri enum tr, perde arkasnda int olarak gerekletirilmitir --- return --Bu, dn tr char olan bir ilev: char function(double kesirli, int tamsay, Saat saat) aryoruz... ve sonu: a

68.6.7

is (Tr isim : Belirte, ablonParametreListesi) is (Tr isim == Belirte, ablonParametreListesi)


Bu iki kullanm, ok daha karmak ifadeler yazmaya olanak verir. isim'den sonra kullanlan : ve == ileleri, yukarda gsterilen kullanmlaryla ayn anlamdadr. Srasyla, o tre dnebilme ve ayn trden olma koulunu denetlerler. ablonParametreListesi, hem koulun paras olarak alr, hem de btn koul salandnda otomatik olarak uygun tr isimleri tanmlar. Bu, ablonlardaki tr karsamas ile ayn ekilde alr. rnek olarak, indeks deeri string olan eleme tablolar kullanldnda baz zel ilemler yapmak isteyelim. Yalnzca byle trlere uymaya alan bir is ifadesi yle yazlabilir: static if (is (T Deer : Deer[ndeks], ndeks : string)) { Belirte olarak Deer[ndeks] kullanlm olmas, ablon parametresi olan T 'nin bir eleme tablosu tr olmasn gerektirir. Ek olarak, eleme tablosunun indeks trnn de zellikle string olmas gerekmektedir. Dolaysyla, yukardaki is ifadesi, "T , indeks tr string olan bir eleme tablosu ise" anlamna gelmektedir. O koulu kullanan ve drt farkl trle arlan bir program: import std.stdio; void birlev(T)(T nesne) { writeln("\n--- ", T.stringof, " ile arldk ---"); static if (is (T Deer : Deer[ndeks], ndeks : string)) { writeln("Evet, koul saland"); writeln("deer tr : ", Deer.stringof); writeln("indeks tr: ", ndeks.stringof); } else { writeln("Hayr, koul salanmad"); }

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

435

Koullu Derleme

} void main() { int say; birlev(say); int[string] intTablosu; birlev(intTablosu); double[string] doubleTablosu; birlev(doubleTablosu); dchar[long] dcharTablosu; birlev(dcharTablosu);

Koul, yalnzca indeks tr string olan eleme tablolar iin salanmaktadr: --- int ile arldk --Hayr, koul salanmad --- AssociativeArray!(string,int) ile arldk --Evet, koul saland deer tr : int indeks tr: string --- AssociativeArray!(string,double) ile arldk --Evet, koul saland deer tr : double indeks tr: string --- AssociativeArray!(long,dchar) ile arldk --Hayr, koul salanmad

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

436

lev Gstergeleri ve Kapamalar

69

lev Gstergeleri ve Kapamalar


lev gstergeleri; ilevlerin adreslerinin saklanabilmelerini ve daha sonraki bir zamanda bu gstergeler yoluyla arlabilmelerini salar. lev gstergeleri D'ye C'den gemitir. Kapamalar; hem ilev gstergelerini, hem de o ilevlerin kullandklar kapsamlar bir arada saklayan olanaklardr.

69.1

lev gstergeleri
Bundan nceki derste is ifadesini denerken & ileci ile ilevlerin adreslerinin de alnabildiini grmtk. O adresi, bir ilev ablonuna parametre olarak gndermitik. ablonlarn eitli trlerle arlabilmelerinden ve trlerin .stringof niteliinden yararlanarak, ilev gstergelerinin trleri hakknda bilgi edinebiliriz: import std.stdio; int ilev(char c, double d) { return 42; } void main() { ablon(&ilev); }

// adresinin alnmas ve // parametre olarak gnderilmesi

void ablon(T)(T parametre) { writeln("tr : ", T.stringof); writeln("deeri: ", parametre); } O program altrldnda, ilev isimli ilevin adresinin tr konusunda bir fikir sahibi olabiliyoruz: tr : int function(char c, double d) deeri: 80495B4

69.1.1

Tanmlanmas
lev gstergeleri function anahtar szc ile tanmlanr. Bu szckten nce ilevin dn tr, sonra da ilevin ald parametreler yazlr: dn_tr function(ald_parametreler) gsterge; Bu tanmda parametrelere isim verilmesi gerekmez; yukardaki ktda grdmz parametre isimleri olan c 'nin ve d 'nin yazlmalar istee baldr. Bir rnek olarak, yukardak ilev isimli ilevi gsteren bir deikeni yle tanmlayabiliriz: int function(char, double) gsterge = &ilev; lev gstergelerinin yazm olduka karmak olduundan, o tre alias ile yeni bir isim vermek kodun okunaklln arttrr: alias int function(char, double) Hesaplevi;

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

437

lev Gstergeleri ve Kapamalar

Artk function 'l uzun yazm yerine ksaca Hesaplevi yazmak yeterlidir. Hesaplevi gsterge = &ilev;

69.1.2

arlmas
lev gstergesi olarak tanmlanan deiken, sanki kendisi bir ilevmi gibi isminden sonraki parametre listesiyle arlr ve dn deeri kullanlabilir: int sonu = gsterge('a', 5.67); assert(sonu == 42);

69.1.3

Ne zaman kullanmal
lev gstergeleri; deerlerin saklanmalarna benzer ekilde, ilemlerin de saklanabilmelerini salar. Saklanan gstergeler programda daha sonradan ilev gibi kullanlabilirler. Aslnda davran farkllklarnn D'nin baka olanaklar ile de salanabildiini biliyorsunuz. rnein alan gibi bir yapnn cretinin hesaplanmas srasnda hangi ilevin arlaca, bu yapnn bir enum deeri ile belirlenebilir: final switch (alan.tr) { case alanTr.maal: maalcretHesab(); break; case alanTr.saatli: saatlicretHesab(); break; } O yntemin bir yetersizlii, o kod bir ktphane iinde bulunduu zaman ortaya kar: btn enum deerlerinin ve onlara karlk gelen btn ilevlerin ktphane kodu yazld srada biliniyor olmas gerekmektedir. Farkl bir cret hesab gerektiinde, ktphane iindeki ilgili switch deyimlerinin hepsinin yeni tr de ierecek ekilde deitirilmeleri gerekir. Davran fark konusunda baka bir yntem, nesne ynelimli programlama olanaklarndan yararlanmak olabilir. alan diye bir arayz tanmlanabilir ve cret hesab ondan treyen alt snflara yaptrlabilir: interface alan { double cretHesab(); } class Maalalan : alan { double cretHesab() { double sonu; // ... return sonu; } } class Saatlialan : alan { double cretHesab() { double sonu; // ... return sonu;

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

438

lev Gstergeleri ve Kapamalar

// ... Maalalan alan; double cret = alan.cretHesab(); Bu, nesne ynelimli programlama dillerine uygun olan bir yntemdir. lev gstergeleri, davran farkll konusunda kullanlan bakaca bir yntemdir. Bu, zellikle nesne ynelimli olanaklar bulunmayan C dilinde yazlm olan ktphaneler kullanrken gerekebilir.
69.1.4

Parametre rnei
Kendisine verilen bir dizi say ile ilem yapan bir ilev tasarlayalm. Bu ilev, saylarn yalnzca sfrdan byk olanlarnn on katlarn ieren bir dizi dndrsn: int[] sz_ve_dntr(const int[] saylar) { int[] sonu; foreach (say; saylar) { if (say > 0) { int yeniDeer = say * 10; sonu ~= yeniDeer; } } } return sonu; // szme, // ve dntrme

O ilevi yle bir programla deneyebiliriz: import std.stdio; import std.random; void main() { int[] saylar; // Rasgele 20 say foreach (i; 0 .. 20) { saylar ~= uniform(0, 10) - 5; } writeln("giri: ", saylar); writeln("sonu: ", sz_ve_dntr(saylar));

ktsndan grld gibi, sonu yalnzca sfrdan byk olanlarn on katlarn iermektedir: 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 sz_ve_dntr ilevinin bu haliyle fazla kullanl olduunu dnemeyiz; nk her zaman iin sfrdan byk deerlerin on katlarn retmektedir. Oysa szme ve dntrme ilemlerini dardan alabilse ok daha kullanl olur. Dikkat ederseniz, szme ilemi int 'ten bool 'a bir dnm; say dntrme ilemi de int 'ten yine int 'e bir dnmdr:

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

439

lev Gstergeleri ve Kapamalar

say > 0 : int olan sayya bakarak bool sonu elde ediliyor say * 10 : int olan say kullanlarak yine int retiliyor Bu ilemleri ilev gstergeleri yoluyla yapmaya gemeden nce, bu dnmleri salayacak olan ilev gsterge trlerini yle tanmlayabiliriz: alias bool function(int) Szmelemi; alias int function(int) Dnmlemi; Szmelemi , "int alan ve bool dndren" ilev gstergesi; Dnmlemi de "int alan ve int dndren" ilev gstergesi anlamndadr. Bu trlerden olan ilev gstergelerini sz_ve_dntr ilevine dardan parametre olarak verirsek, szme ve dntrme ilemlerini o ilev gstergelerine yaptrabiliriz. Bylece ilev daha kullanl hale gelmi olur: int[] sz_ve_dntr(const int[] saylar, Szmelemi szc, Dnmlemi dntrc) { int[] sonu; foreach (say; saylar) { if (szc(say)) { int yeniDeer = dntrc(say); sonu ~= yeniDeer; } } } return sonu;

Bu ilev artk asl szme ve dntrme ilemlerinden bamsz bir hale gelmi olur; o ilemleri, kendisine verilen ilev gstergelerine yaptrmaktadr. Yukardaki gibi "sfrdan byk olanlarnn on katlarn" retebilmesi iin yle iki kk ilev tanmlayabiliriz ve sz_ve_dntr ilevini onlarn adresleri ile arabiliriz: bool sfrdanByk_m(int say) { return say > 0; } int onKat(int say) { return say * 10; } // ... writeln("sonu: ", sz_ve_dntr(saylar, &sfrdanByk_m, &onKat)); Bunun yarar, sz_ve_dntr ilevinin artk bambaka szc ve dntrc ilevleriyle de serbeste arlacak hale gelmi olmasdr. rnein "ift olanlarnn ters iaretlilerini" elde etmek iin: bool ift_mi(int say) { return (say % 2) == 0; }

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

440

lev Gstergeleri ve Kapamalar

int tersaretlisi(int say) { return -say; } // ... writeln("sonu: ", sz_ve_dntr(saylar, &ift_mi, &tersaretlisi)); kts: 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.1.5

ye rnei
lev gstergeleri deiken olarak kullanlabildikleri iin yap ve snf yeleri de olabilirler. Yukardaki sz_ve_dntr ilevi yerine, szme ve dntrme ilemlerini kurucu parametreleri olarak alan bir snf da yazabiliriz: class SzcDntrc { Szmelemi szc; Dnmlemi dntrc; this (Szmelemi szc, Dnmlemi dntrc) { this.szc = szc; this.dntrc = dntrc; } int[] ilemYap(const int[] saylar) { int[] sonu; foreach (say; saylar) { if (szc(say)) { int yeniDeer = dntrc(say); sonu ~= yeniDeer; } } } return sonu;

Daha sonra o trden bir nesne oluturabiliriz ve yukardaki sonular elde etmek iin u ekilde kullanabiliriz: auto ilemci = new SzcDntrc(&ift_mi, &tersaretlisi); writeln("sonu: ", ilemci.ilemYap(saylar));

69.2

simsiz ilevler
Yukardaki rnek programlarda sz_ve_dntr ilevinin esnekliinden yararlanmak iin kk ilevler tanmlandn ve sz_ve_dntr arlrken bu kk ilevlerin adreslerinin gnderildiini grdk.

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

441

lev Gstergeleri ve Kapamalar

Yukardaki rneklerde de grld gibi, baz durumlarda bal bana ilevler yazmak gereksiz gelebilir. Bu gereksizlik zellikle say > 0 ve say * 10 gibi kk ilemler kullanrken hissedilir. lev hazr deeri olarak da adlandrabileceimiz isimsiz ilev olana, baka ifadelerin arasnda kk ilevler tanmlamaya yarar. simsiz ilevler, ilev gstergesi kullanlabilen her yerde u sz dizimiyle tanmlanabilirler: function dn_tr(parametreleri) { /* ilemleri */ }; rnein, yukardaki rnekte tanmladmz snftan olan bir nesneyi "ikiden byk olanlarnn yedi katlarn" retecek ekilde yle kullanabiliriz: new SzcDntrc( function bool(int say) { return say > 2; }, function int(int say) { return say * 7; }); Bylece, hem bu kadar kk ilemler iin ayrca ilevler tanmlamak zorunda kalmam oluruz; hem de istediimiz davran tam da gereken noktada belirtmi oluruz.
69.2.1

Ksa sz dizimi
simsiz ilevlerin yazmlarnda baz kolaylklar da vardr. levin dn trnn return satrndan anlalabildii durumlarda dn tr yazlmayabilir: new SzcDntrc( function (int say) { return say > 2; }, function (int say) { return say * 7; }); simsiz ilevin parametre almad durumlarda da parametre listesi yazlmayabilir. Bunu grmek iin ilev gstergesi alan bir ilev dnelim: void birlev(/* ... ilev gstergesi alsn ... */) { // ... } O ilevin ald parametre, double dndren ama parametre almayan bir ilev gstergesi olsun: void birlev(double function() gsterge) { // ... } O parametrenin tanmndaki function 'dan sonraki parantezin bo olmas, o gstergenin parametre almayan bir ilev gstergesi olduunu ifade eder. Byle bir durumda, isimsiz ilevin oluturulduu noktada bo parantez yazmaya da gerek yoktur. u isimsiz ilev tanm birbirlerinin edeeridir: birlev(function double() { return 42.42; }); // uzun olarak birlev(function () { return 42.42; }); // dn trsz birlev(function { return 42.42; }); // parametresiz Birincisi hibir ksaltmaya bavurmadan yazlmtr; ikincisi, dn trnn return satrndan karsanmasndan yararlanmtr; ncs de gereksiz olan bo parametre listesini yazmamtr.

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

442

lev Gstergeleri ve Kapamalar

Baz durumlarda bir adm daha atabilir ve function da yazmayabiliriz. Ama o zaman bir isimsiz kapama kullanld varsaylr. Bunu biraz aada gstereceim.

69.3

Kapamalar
Kapama, daha ok fonksiyonel programlama dillerinde grlen bir olanaktr. Kapama, ilev gstergesine ek olarak, onun iinde tanmland kapsamn da saklanmasndan oluur. C ailesine bal olan D, emirli bir dildir. Bu gibi dillerde programn ilemleri adm adm programc tarafndan emir verir gibi tarif edilir: btn renci nesnelerine eri, notlarn topla, renci saysna bl, ka yazdr, vs. Kapamalar genelde byle dillerde bulunmazlar; D, sistem dilleri arasnda bu konuda bir istisnadr. Yaam Sreleri ve Temel lemler dersinde grdmz gibi; deikenlerin yaamlar, tanml olduklar kapsamdan kldnda son bulur: {

int art = 10; // ... } // art'n yaam burada son bulur levlerden art gibi yerel deikenlerin adreslerinin dndrlmesi de zaten bu yzden tanmsz davrantr. art 'n ilev gstergesi dndren bir ilev iinde tanmlanm olan yerel bir deiken olduunu dnelim. Bu ilevin dndrd isimsiz ilev, bu yerel deikeni de kullanyor olsun: alias int function(int) Hesaplevi; Hesaplevi hesap() { int art = 10; // derleme HATASI return function int(int say) { return art + say; }; } Dndrlen isimsiz ilev, yerel deikeni kullanmaya alt iin o kod hataldr. Derlenmesine izin verilmi olsa, isimsiz ilev daha sonradan iletildii srada, yaam oktan sona ermi olan art deikenine erimeye alacaktr. Bu yzden derleyici hata verir: Error: function deneme.hesap.__funcliteral1 cannot access frame of function hesap O kodun derlenip doru olarak alabilmesi iin yerel deikenlerin yaam srelerinin isimsiz ilev yaad srece uzatlmas gerekir. Kapamalar ite bu gibi durumlarda kullanlrlar: ilev gstergesi yannda, onun kulland kapsamlar da saklarlar. Bylece o kapsamlardaki deikenlerin yaamlar uzam olur.
69.3.1

Kullanlmas
Kapamalarn kullanm ilev gstergelerine ok benzer: tek farklar function yerine delegate anahtar szcnn kullanlmasdr. Yukardaki kodun derlenip doru olarak almas iin o kadar yeterlidir: alias int delegate(int) Hesaplevi; Hesaplevi hesap() {

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

443

lev Gstergeleri ve Kapamalar

int art = 10; return delegate int(int say) { return art + say; };

O kapamann kulland yerel kapsamdaki art gibi deikenlerin yaamlar, kapama yaad srece devam edecektir. Bu yzden kapamalar her arldklarnda yerel deikenleri deitirebilirler. Bunun rneklerini daha sonraki bir derste reneceimiz yap ve snflarn opApply ye ilevlerinde greceiz. Yukardaki kapamay yle bir kodla deneyebiliriz: auto ilev = hesap(); writeln("hesap: ", ilev(3)); hesap , isimsiz bir kapama dndrmektedir. Yukardaki kod, o kapamay ilev isimli bir deikenin deeri olarak kullanmakta, ve ilev(3) yazmyla armaktadr. Kapamann ii de kendisine verilen say ile art 'n toplamn dndrmek olduu iin, ka 3 ve 10'un toplam yazdrlacaktr: hesap: 13

69.3.2

Ksa sz dizimi
simsiz ilevlerde olduu gibi; eer karsanabiliyorsa, isimsiz kapamalarn dn trleri yazlmayabilir, eer bosa parametre listeleri belirtilmeyebilir, ve ek olarak delegate szc de yazlmayabilir. function veya delegate yazlmadnda delegate varsaylr. Btn bu ksaltmalar grmek iin, parametresiz bir kapama kullanan bir koda bakalm: int[] zelSaylarOlutur(int adet, int delegate() sayretici) { int[] sonu = [ -1 ]; foreach (i; 0 .. adet) { sonu ~= sayretici(); } } return sonu ~ -1;

O ilev, ilk ve son saylar -1 olan bir dizi say oluturmaktadr. Bu iki zel saynn arasna ka adet baka say geleceini ve bu saylarn nasl retileceklerini ise parametre olarak almaktadr. O ilevi, her arldnda ayn sabit deeri dndren aadaki gibi bir kapamayla arabiliriz. Bu isimsiz kapamay belirleyen kapsam parantezlerini sar ile iaretliyorum: writeln(zelSaylarOlutur(3, { return 42; })); Grdnz gibi, isimsiz kapama iin delegate anahtar szc kullanlmam; return satrndan karsanabildii iin dn tr belirtilmemi; ve bo olduu iin parametre listesi de yazlmamtr. kts: -1 42 42 42 -1 Ayn ilevi, bir de yerel bir deiken kullanan bir kapama ile aralm:

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

444

lev Gstergeleri ve Kapamalar

int sonSay; writeln(zelSaylarOlutur(15, { sonSay += uniform(0, 3); return sonSay; })); writeln("son retilen say: ", sonSay); O kapama; rasgele bir deer retmekte, ama her zaman iin son sayya ekledii iin, rasgele saylarn deerleri hep artan ynde gitmektedir. Yerel deikenin kapamann iletilmesi srasnda nasl deimi olduunu da ktnn son satrnda gryoruz: -1 0 2 3 4 6 6 8 9 9 9 10 12 14 15 17 -1 son retilen say: 17

69.4

zet
function anahtar szc ile ilev gstergeleri tanmlanabilir ve bu gstergeler daha sonra ilev gibi kullanlabilir delegate anahtar szc, kapama tanmlar; kapama, ilev gstergesine ek olarak o ilev gstergesinin kulland kapsam da barndrr lev gstergesi veya kapama gereken yerlerde isimsiz ilevler veya isimsiz kapamalar tanmlanabilir simsiz ilev ve kapama tanmlarnda ksa sz dizimleri kullanlabilir

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

445

Yap ve Snflarda foreach

70

Yap ve Snflarda foreach


foreach Dngs dersinden hatrlayacanz gibi, bu dng uyguland tre gre deiik ekillerde iler. Nasl kullanldna bal olarak farkl elemanlara eriim salar: dizilerde, sayal veya sayasz olarak dizi elemanlarna; eleme tablolarnda, indeksli veya indekssiz olarak tablo elemanlarna; say aralklarnda, deerlere; ktphane trlerinde, o tre zel bir ekilde, rnein File iin dosya satrlarna... foreach 'in nasl ileyeceini kendi trlerimiz iin de belirleyebiliriz. Bunun iin iki yntem kullanlabilir: trmzn aralk algoritmalaryla da kullanlmasna olanak veren aralk ilevleri tanmlamak, veya opApply ye ilevlerini tanmlamak. Bu iki yntemden opApply ilevleri nceliklidir: eer tanmlanmlarsa, derleyici o ye ilevleri kullanr; tanmlanmamlarsa, aralk ilevlerine bavurur. te yandan, aralk ilevlerini kullanmak ou durumda daha basit ve yeterli olabilir. Bu yntemlere gemeden nce, foreach 'in her tre uygun olamayacan vurgulamak istiyorum. Bir nesne zerinde foreach ile ilerlemek, ancak o tr herhangi bir ekilde bir topluluk olarak kabul edilebiliyorsa anlaml olabilir. rnein renci gibi bir snfn foreach ile kullanlmasnda ne tr deikenlere eriilecei ak deildir. O yzden renci snfnn byle bir konuda destek vermesi beklenmeyebilir. te yandan, baka bir bak as ile, foreach dngsnn renci nesnesinin notlarna erimek iin kullanlaca da dnlebilir. Kendi trlerinizin foreach destei verip vermeyeceine ve verecekse ne tr deikenlere eriim salayacaklarna siz karar vermelisiniz.

70.1

foreach desteini aralk ilevleri ile salamak


foreach 'in for 'un daha kullanls olduunu biliyoruz. yle bir foreach dngs olsun: foreach (eleman; aralk) { // ... ifadeler ... } O dng, derleyici tarafndan arka planda bir for dngs olarak yle gerekletirilir: for ( ; /* bitmedii srece */; /* sonrakine ge */) { auto eleman = /* araln_bandaki */; } // ... ifadeler ...

foreach 'in kendi trlerimizle de alabilmesi iin yukardaki zel blmde kullanlacak olan zel ye ilev tanmlamak gerekir. Bu ilev; dngnn sonunu belirlemek, sonrakine gemek (aral ba tarafndan daraltmak), ve en batakine eriim salamak iin kullanlr. Bu ye ilevin isimleri srasyla empty , popFront , ve front 'tur. Dolaysyla, derleyicinin arka planda rettii kod unun edeeri olacaktr: for ( ; !aralk.empty(); aralk.popFront()) { auto eleman = aralk.front();

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

446

Yap ve Snflarda foreach

// ... ifadeler ...

Bu ilev aadaki gibi ilemelidir: .empty() : aralk tkenmise true , deilse false dndrr .popFront() : bir sonrakine geer (aral ba tarafndan daraltr) .front() : bataki eleman dndrr O ekilde ileyen byle ye ileve sahip olmas, trn foreach ile kullanlabilmesi iin yeterlidir.
70.1.1

rnek
Belirli aralkta deerler reten bir yap tasarlayalm. Araln ban ve sonunu belirleyen deerler, nesne kurulurken belirlensin. Geleneklere uygun olarak, son deer araln dnda kabul edilsin. Bir anlamda, D'nin ba..son eklinde yazlan aralklarnn edeeri olarak alan bir tr tanmlayalm: struct Aralk { int ba; int son; this(int ba, int son) { this.ba = ba; this.son = son; } invariant() { // ba'n hibir zaman son'dan byk olmamas gerekir assert(ba <= son); } bool empty() const { // ba, son'a eit olduunda aralk tkenmi demektir return ba == son; } void popFront() { // Bir sonrakine gemek, ba' bir arttrmaktr. Bu // ilem, bir anlamda aral ba tarafndan ksaltr. ++ba; } int front() const { // Araln bandaki deer, ba'n kendisidir return ba; }

Not: Ben gvenlik olarak yalnzca invariant bloundan yararlandm. Ona ek olarak, popFront ve front ilevleri iin in bloklar da dnlebilirdi; o ilevlerin doru olarak alabilmesi iin ayrca araln bo olmamas gerekir. O yapnn nesnelerini artk foreach ile yle kullanabiliriz:

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

447

Yap ve Snflarda foreach

foreach (eleman; Aralk(3, 7)) { write(eleman, ' '); } foreach , o ilevden yararlanarak aralktaki deerleri sonuna kadar, yani empty 'nin dn deeri true olana kadar kullanr: 3 4 5 6

70.1.2

Ters srada ilerlemek iin std.range.retro


std.range modl, aralklarla ilgili eitli olanaklar sunar. Bunlar arasndan retro , kendisine verilen aral ters srada kullanr: import std.range; // ... foreach (eleman; retro(Aralk(3, 7))) { write(eleman, ' '); } Trn retro ile kullanlabilmesi iin empty yannda iki ye ilev daha gerekir: .empty() : ayn anlamdadr; aralk tkenmise true , deilse false dndrr .popBack() : bir ncekine geer (aral son tarafndan daraltr) .back() : sondaki eleman dndrr Bu iki yeni ilevi Aralk iin yle tanmlayabiliriz: struct Aralk { // ... void popBack() { // Bir ncekine gemek, son'u bir azaltmaktr. Bu // ilem, bir anlamda aral son tarafndan ksaltr. --son; } int back() const { // Araln sonundaki deer, son'dan bir nceki // deerdir; nk gelenek olarak araln sonu, // arala dahil deildir. return son - 1; }

Kodun ktsndan anlald gibi, retro yukardaki ye ilevlerden yararlanarak bu aral ters srada kullanr: 6 5 4 3

70.2

foreach desteini opApply ilevleri ile salamak


Yukardaki ye ilevler, nesneyi sanki bir aralkm gibi kullanmay salarlar. O yntem, nesnelerin foreach ile tek bir ekilde kullanlmalar durumuna daha uygundur. rnein

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

448

Yap ve Snflarda foreach

renciler gibi bir trn nesnelerinin, rencilere foreach ile teker teker eriim salamas, o yntemle kolayca gerekletirilebilir. te yandan, bazen bir nesne zerinde farkl ekillerde ilerlemek istenebilir. Bunun rneklerini eleme tablolarndan biliyoruz: dng deikenlerinin tanmna bal olarak ya yalnzca elemanlara, ya da hem elemanlara hem de indekslere eriilebiliyordu: string[string] ingilizcedenTrkeye; // ... foreach (trkesi; ingilizcedenTrkeye) { // ... yalnzca elemanlar ... } foreach (ingilizcesi, trkesi; ingilizcedenTrkeye) { // ... indeksler ve elemanlar ... } opApply ilevleri, kendi trlerimizi de foreach ile birden fazla ekilde kullanma olana salarlar. opApply 'n nasl tanmlanmas gerektiini grmeden nce opApply 'n nasl arldn anlamamz gerekiyor. Programn ileyii, foreach 'in kapsamna yazlan ilemler ile opApply ilevinin ilemleri arasnda, belirli bir anlamaya uygun olarak gider gelir. nce opApply 'n ii iletilir; opApply kendi ii srasnda foreach 'in ilemlerini arr; ve bu karlkl gidi geli dng sonuna kadar devam eder. Bu anlamay aklamadan nce foreach dngsnn yapsn tekrar hatrlatmak istiyorum: // Programcnn yazd dng: foreach (/* dng deikenleri */; nesne) { // ... ilemler ... } Eer dng deikenlerine uyan bir opApply ilevi tanmlanmsa; derleyici, dng deikenlerini ve dng kapsamn kullanarak bir kapama oluturur ve nesnenin opApply ilevini o kapama ile arr. Buna gre, yukardaki dng derleyici tarafndan arka planda aadaki koda dntrlr. Kapamay oluturan kapsam parantezlerini sar ile iaretliyorum: // Derleyicinin arka planda kulland kod: nesne.opApply(delegate int(/* dng deikenleri */) { // ... ilemler ... return sonlanmaBilgisi; }); Yani, foreach dngs ortadan kalkar; onun yerine nesnenin opApply ilevi derleyicinin oluturduu bir kapama ile arlr. Derleyicinin oluturduu bir kapamann kullanlyor olmas, opApply ilevinin yazm konusunda baz zorunluluklar getirir. Bu dnm ve uyulmas gereken zorunluluklar u maddelerle aklayabiliriz: 1. foreach 'in ilemleri, kapamay oluturan ilemler haline gelirler; bu kapama, opApply tarafndan arlmaldr 2. dng deikenleri, kapamann parametreleri haline gelirler; bu parametrelerin opApply 'n tanmnda ref olarak iaretlenmeleri gerekir

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

449

Yap ve Snflarda foreach

3. kapamann dn tr int 'tir; buna uygun olarak, kapamann sonuna derleyici tarafndan bir return satr eklenir. return 'n dndrd bilgi, dngnn rnein break ile sonlanp sonlanmadn anlamak iin kullanlr; eer sfr ise dng devam etmelidir; sfrdan farkl ise dng sonlanmaldr 4. asl dng opApply 'n iinde programc tarafndan gerekletirilir 5. opApply , kapamann dndrm olduu sonlanmaBilgisi 'ni dndrmelidir Aralk snfn bu anlamaya uygun olarak aadaki gibi tanmlayabiliriz. Yukardaki maddeleri, ilgili olduklar yerlerde aklama satrlar olarak belirtiyorum: struct Aralk { int ba; int son; this(int ba, int son) { this.ba = ba; this.son = son; } // (3) (2) (1) int opApply(int delegate(ref int) ilemler) const { int sonu; for (int say = ba; say != son; ++say) { sonu = ilemler(say); // (1) if (sonu) { break; // (3) } } } return sonu; // (5) // (4)

Bu snf da foreach ile ayn ekilde kullanabiliriz: foreach (eleman; Aralk(3, 7)) { write(eleman, ' '); } kts, aralk ilevleri kullanld zamanki ktnn ayns olacaktr: 3 4 5 6

70.2.1

Farkl biimlerde ilerlemek iin opApply 'n yklenmesi


Nesne zerinde farkl ekillerde ilerleyebilmek, opApply 'n deiik trlerdeki kapamalarla yklenmesi ile salanr. Derleyici, foreach deikenlerinin uyduu bir opApply yklemesi bulur ve onu arr. rnein, Aralk nesnelerinin iki foreach deikeni ile de kullanlabilmelerini isteyelim: foreach (birinci, ikinci; Aralk(0, 15)) { write(birinci, ',', ikinci, ' '); } O kullanm, eleme tablolarnn hem indekslerine hem de elemanlarna foreach ile eriildii duruma benzer.

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

450

Yap ve Snflarda foreach

Bu rnekte, Aralk yukardaki gibi iki deikenle kullanldnda art arda iki deere eriiliyor olsun; ve dngnn her ilerletiliinde deerler beer beer artsn. Yani yukardaki dngnn kts yle olsun: 0,1 5,6 10,11 Bunu salamak iin iki deikenli bir kapama ile alan yeni bir opApply tanmlamak gerekir. O kapama, opApply tarafndan ve bu kullanma uygun olan iki deerle arlmaldr: int opApply(int delegate(ref int, ref int) ilemler) const { int sonu; for (int i = ba; i + 1 < son; i += 5) { int birinci = i; int ikinci = i + 1; sonu = ilemler(birinci, ikinci); if (sonu) { break; }

} }

return sonu;

ki deikenli dng kullanldnda retilen kapama bu opApply yklemesine uyduu iin, derleyici bu tanm kullanr. Tr iin anlaml olduu srece baka opApply ilevleri de tanmlanabilir. Hangi opApply ilevinin seilecei dng deikenlerinin adedi yannda, trleri ile de belirlenebilir. Deikenlerin trleri foreach dngsnde aka yazlabilir ve bylece ne tr elemanlar zerinde ilerlenmek istendii aka belirtilebilir. Buna gre, foreach dngsnn hem rencilere hem de retmenlere erimek iin kullanlabilecei bir Okul snf yle tanmlanabilir: class Okul { int opApply(int delegate(ref renci) ilemler) const { // ... } int opApply(int delegate(ref retmen) ilemler) const { // ... }

Bu Okul trn kullanan programlar, hangi elemanlar zerinde ilerleneceini dng deikenini ak olarak yazarak seebilirler: foreach (renci renci; okul) { // ... } foreach (retmen retmen; okul) { // ... }

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

451

Yap ve Snflarda foreach

Derleyici, deikenin trne uyan bir kapama retecek, ve o kapamaya uyan opApply ilevini aracaktr.

70.3

Uyar: foreach 'in ileyii srasnda topluluk deimemelidir


Hangi yntemle olursa olsun, foreach destei veren bir tr, dngnn ileyii srasnda sunduu topluluk kavramnda bir deiiklik yapmamaldr: dngnn ileyii srasnda yeni elemanlar eklememeli ve var olan elemanlar silmemelidir. Bu kurala uyulmamas tanmsz davrantr.

70.4

Problemler
1. Yukardaki Aralk gibi alan, ama aralktaki deerleri birer birer deil, belirtilen adm kadar ilerleten bir snf tanmlayn. Adm bilgisini kurucu ilevinin nc parametresi olarak alsn: foreach (say; Aralk(0, 10, 2)) { write(say, ' '); } Sfrdan 10'a kadar ikier ikier ilerlemesi beklenen o Aralk nesnesinin kts yle olsun: 0 2 4 6 8 2. Yaz iinde geen Okul snfn, foreach 'in dng deikenlerine gre rencilere veya retmenlere eriim salayacak ekilde yazn. ... zmler

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

452

Birlikler

71

Birlikler
Birlikler, birden fazla yenin ayn bellek alann paylamalarn salarlar. D'ye C dilinden gemi olan alt dzey bir olanaktr. ki fark dnda yaplarla ayn ekilde kullanlr: struct yerine union anahtar szc ile tanmlanr yeleri ayn bellek alann paylarlar; birbirlerinden bamsz deillerdir imdiye kadar ok karlatmz yap trlerinin kullandklar bellek alan, btn yelerini barndracak kadar bykt: struct Yap { int i; double d; } // ... writeln(Yap.sizeof); Drt baytlk int 'ten ve sekiz baytlk double 'dan oluan o yapnn bykl 12'dir: 12 Ayn ekilde tanmlanan bir birliin bykl ise, yeleri ayn bellek blgesini paylatklar iin, yelerden en by iin gereken yer kadardr: union Birlik { int i; double d; } // ... writeln(Birlik.sizeof); Drt baytlk int ve sekiz baytlk double ayn alan paylatklar iin bu birliin bykl en byk ye iin gereken yer kadardr: 8 Bunun bellek kazanc salayan bir olanak olduunu dnmeyin. Ayn bellek alanna birden fazla bilgi sdrmak olanakszdr. Birliklerin yarar, ayn blgenin farkl zamanlarda farkl trden bilgiler iin kullanlabilmesidir. Belirli bir anda tek bir ye saklanabilir. Birliklerin yararlarndan birisi, her ortamda ayn ekilde almasa da, bilginin paralarna dier yeler yoluyla eriilebilmesidir. Yukardaki birlii oluturan sekiz baytn bellekte nasl durduklarn, ve yeler iin nasl kullanldklarn yle gsterebiliriz: 0 1 2 3 4 5 6 7 ---+------+------+------+------+------+------+------+------+--| <-- int iin 4 bayt --> | | | <-------------- double iin 8 bayt ---------------> | ---+------+------+------+------+------+------+------+------+---

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

453

Birlikler

Ya sekiz baytn hepsi birden double ye iin kullanlr, ya da ilk drt bayt int ye iin kullanlr ve gerisine dokunulmaz. Ben rnek olarak iki ye kullandm; birlikleri istediiniz kadar ye ile tanmlayabilirsiniz. yelerin hepsi ayn alan paylarlar. Ayn bellek blgesinin kullanlyor olmas ilgin sonular dourabilir. rnein, birliin bir int ile ilklenmesi ama bir double olarak kullanlmas, batan kestirilemeyecek double deerleri verebilir: auto birlik = Birlik(42); writeln(birlik.d); // int yenin ilklenmesi // double yenin kullanlmas

int yeyi oluturan drt baytn 42 deerini tayacak ekilde kurulmalar, double yenin deerini de etkiler: 4.9547e-270 Mikro ilemcinin bayt sralarna bal olarak int yeyi oluturan drt bayt bellekte 0|0|0|42, 42|0|0|0, veya daha baka bir dzende bulunabilir. Bu yzden yukardaki double yenin deeri baka ortamlarda daha farkl da olabilir.

71.1

simsiz birlikler
simsiz birlikler, iinde bulunduklar bir yapnn hangi yelerinin paylaml olarak kullanldklarn belirlerler: struct BirYap { int birinci; union { int ikinci; int nc; }

// ... writeln(BirYap.sizeof); Yukardaki yapnn son iki yesi ayn alan paylarlar ve bu yzden yap, toplam iki int 'in bykl kadar yer tutar. Birlik yesi olmayan birinci iin gereken 4 bayt, ve ikinci ile nc 'nn paylatklar 4 bayt: 8

71.2

Baka bir trn baytlarn ayrtrmak


Birlikler, trleri oluturan baytlara teker teker erimek iin kullanlabilirler. rnein aslnda 32 bitten oluan IP adreslerinin 4 blmn elde etmek iin bu 32 biti paylaan 4 baytlk bir dizi kullanlabilir. Adres deerini oluturan ye ve drt bayt bir birlik olarak yle bir araya getirilebilir: union IpAdresi {

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

454

Birlikler

uint deer; ubyte[4] baytlar;

O birlii oluturan iki ye, ayn bellei u ekilde paylarlar: 0 1 2 3 ---+------------+------------+------------+------------+--| <------ IP adresini oluturan 32 bit ---------> | | baytlar[0] | baytlar[1] | baytlar[2] | baytlar[3] | ---+------------+------------+------------+------------+--Bu birlik, daha nceki derslerde 192.168.1.2 adresinin deeri olarak karlatmz 0xc0a80102 ile ilklendiinde, baytlar dizisinin elemanlar teker teker adresin drt blmne karlk gelirler: void main() { auto adres = IpAdresi(0xc0a80102); foreach (i; 0 .. 4) { write(adres.baytlar[i], ' '); } } writeln();

Adresin blmleri, bu program denediim ortamda alk olunduundan ters srada kmaktadr: 2 1 168 192 Bu, program altran mikro ilemcinin kk soncul olduunu gsterir. Baka ortamlarda baka srada da kabilir. Bu rnekte asl gstermek istediim, birlik yelerinin deerlerinin belirsiz olabilecekleridir. Birlikler, ancak ve ancak tek bir yeleri ile kullanldklarnda beklendii gibi alrlar. Hangi yesi ile kurulmusa, birlik nesnesinin yaam boyunca o yesi ile kullanlmas gerekir. O ye dndaki yelere eriildiinde ne tr deerlerle karlalaca ortamdan ortama farkllk gsterebilir. Bu dersle ilgisi olmasa da, std.intrinsic modlnn bswap ilevinin bu konuda yararl olabileceini belirtmek istiyorum. bswap , kendisine verilen uint 'in baytlar ters srada olann dndrr. std.system modlndeki endian deerinden de yararlanrsak, kk soncul bir ortamda olduumuzu yle belirleyebilir ve yukardaki IP adresini oluturan baytlar tersine evirebiliriz: import std.system; import std.intrinsic; // ... if (endian == Endian.LittleEndian) { adres.deer = bswap(adres.deer); } Endian.LittleEndian deeri sistemin kk soncul olduunu, Endian.BigEndian deeri de byk soncul olduunu belirtir. Yukardaki dnm sonucunda IP adresinin blmleri alk olunan srada kacaktr:

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

455

Birlikler

192 168 1 2 Bunu yalnzca birliklerle ilgili bir kullanm rnei olarak gsterdim. Normalde IP adresleriyle byle dorudan ilgilenmek yerine, o i iin kullanlan bir ktphanenin olanaklarndan yararlanmak daha doru olur.

71.3

Protokol rnei
Baz protokollerde, rnein a protokollerinde, baz baytlarn anlam baka bir ye tarafndan belirleniyor olabilir. A pakedinin daha sonraki bir blm, o yenin deerine gre farkl bir ekilde kullanlyor olabilir: struct Adres { // ... } struct BirProtokol { // ... } struct BakaProtokol { // ... } enum ProtokolTr { birTr, bakaTr } struct APakedi { Adres hedef; Adres kaynak; ProtokolTr tr; union { BirProtokol birProtokol; BakaProtokol bakaProtokol; } } ubyte[] geriKalan;

Yukardaki APakedi yapsnda hangi protokol yesinin geerli olduu tr 'n deerinden anlalabilir, programn geri kalan da yapy o deere gre kullanr.

71.4

Ne zaman kullanmal
Bayt paylamak gibi kavramlar olduka alt dzey kabul edilecekleri iin, birlikler gnlk tasarmlarmzda hemen hemen hi kullanlmazlar. Birliklerle C ktphanelerinde de karlalabilir.

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

456

Etiketler

72

Etiketler
Etiketler, kod satrlarna isimler vermeye ve program akn bu isimli satrlara yneltmeye yararlar. Etiketin isminden ve : karakterinden oluurlar: biti: // bir etiket

Yukardaki etiket, tanmland satra biti ismini verir.

72.1

goto
ngilizce'de "git" anlamna gelen goto , program akn ismi belirtilen satra ynlendirir: void birlev(bool koul) { writeln("birinci"); if (koul) { goto biti; } writeln("ikinci"); biti: } writeln("nc");

Yukardaki ilev, koul 'un true olduu durumlarda dorudan biti isimli satra gider, ve "ikinci" yazdrlmaz. Etiketler ve goto D'ye C'den gemitir. goto , yapsal programlamaya aykr olduu iin C'de bile kanlmas nerilen bir olanaktr. Dorudan belirli satrlara ynlendiren goto 'lar yerine; while , for , vs. gibi yapsal deyimlerin kullanlmas nerilir. rnein yukardaki kodun edeeri, imdiye kadar ou kodda grdmz gibi, goto kullanmadan yle yazlabilir: void birlev(bool koul) { writeln("birinci"); if (!koul) { writeln("ikinci"); } } writeln("nc");

Buna ramen goto 'nun C dilinde iki tane geerli kullanm vardr. Bu kullanmlarn ikisi de D'de gereksizdir.
72.1.1

D'de gerekmeyen, sonlandrc blge


goto 'nun C'deki geerli bir kullanm, ilevlerin sonlarna yazlan ve o ilevde ayrlm olan kaynaklarn geri verilmesi gibi ilemleri ieren sonlandrc blgedir:

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

457

Etiketler

// --- C kodu --int birIslev() { // ... if (hata) { goto bitis; } // ... bitis: // ... sonlandirma islemleri buraya yazilir ... } return hata;

D'de kaynak ynetimi iin baka olanaklar bulunduu iin, bu kullanm D'de gereksizdir. D'de sonlandrma ilemleri; p toplayc, sonlandrc ilevler, hata atma dzeneinin catch ve finally bloklar, vs. gibi olanaklarla salanr. Not: Bu kullanma C++'da da gerek yoktur.
72.1.2

D'de gerekmeyen, i ie dnglerde kullanm


goto 'nun C'deki dier geerli kullanm, i ie dnglerin daha dta olanlarn etkilemektir. Dngy krmak iin kullanlan break , ve dngy hemen ilerletmek iin kullanlan continue , yalnzca en iteki dngy etkiler. C'de ve C++'da dtaki dngy krmann bir yolu, dngden sonraki bir etikete gitmektir; dtaki dngy ilerletmenin bir yolu da, onun hemen iindeki bir etikete gitmektir: // --- C kodu --while (birKosul) { while (baskaKosul) { // yalnizca icteki donguyu etkiler continue; // yalnizca icteki donguyu etkiler break; // distaki icin 'continue' gibi calisir goto distakiniIlerlet; // distaki icin 'break' gibi calisir goto distakindenCik;

distakiniIlerlet: ; } distakindenCik: Not: Bu kullanma C++ programlarnda da rastlanabilir. Ayn durum, i ie bulunan switch deyimlerinde de vardr; break yalnzca iteki switch 'i etkiledii iin, dtakinden de kmak iin goto kullanlabilir. D'de goto 'nun bu kullanmna da gerek yoktur. Onun yerine, biraz aada gstereceim dng etiketleri kullanlr.

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

458

Etiketler

72.1.3

goto 'nun kurucu ilevleri atlama sorunu


Kurucu ilevler, nesnelerin kurulduklar satrlarda arlr. Bunun nedenlerinden birisi, nesnenin kurulmas iin gereken bilginin henz mevcut olmamas olabilir. Bir baka neden, belki de hi kullanlmayacak olan bir nesneyi kurmak iin gereksizce zaman ve kaynak harcamamaktr. Nesnelerin kurulduklar satrlar goto ile atlanabilse, kurulamam olduklar iin hatal sonular douran nesnelerle karlalabilir: if (koul) { goto birEtiket; }

// kurucu ilevi atlar

auto nesne = Yap(42); // nesnenin kurulduu satr birEtiket: nesne.birlem(); // HATA: belki de kurulmam olan // nesneyi kullanr

Yukardaki koddaki goto ile birEtiket satrna gidilebilse; birlem() ilevi, kurulamam bir nesne zerinde iletilirdi. Bu yzden, goto 'nun bu tr kullanm D'de hataldr ve yukardaki kod bir derleme hatasna neden olur: Error: cannot goto forward into different try block level Not: Ayn sorun C++'da da vardr; ama bu konu, C++'da programcnn dikkatine braklmtr.

72.2

Dng etiketleri
D'de dnglerden hemen nce etiketler tanmlanabilir. continue ve break anahtar szcklerinde de etiket belirtilebilir, ve o dnglerin etkilenmeleri salanabilir: dDng: while (birKoul) { while (bakaKoul) { // iteki dngy ilerletir continue; // iteki dngden kar break; // dtaki dngy ilerletir continue dDng; // dtaki dngden kar break dDng;

Ayns switch deyimleri iin de geerlidir. break deyimlerinin dtaki bir switch 'i etkilemesi iin o switch deyiminden nce de etiket tanmlanabilir.

72.3

zet
D'de goto 'yu kullanmak iin hibir neden yoktur.

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

459

Etiketler

ie dnglerden veya switch deyimlerinden hangisinin etkileneceini belirtmek iin break ve continue deyimlerinde etiket belirtilebilir.

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

460

Ayrntl ablonlar

73

Ayrntl ablonlar
ablonlarn ne kadar kullanl olduklarn ablonlar dersinde grmtk. Algoritmalarn veya veri yaplarnn tek tanmn yazarak birden ok trle almalarn salayabiliyorduk. O derste ablonlarn en ok karlalan kullanmlarn gstermitim. Bu derste ablon olanan daha ayrntl olarak greceiz. Devam etmeden nce en azndan o dersin sonundaki zeti bir kere daha gzden geirmenizi neririm; o derste anlatlanlar burada tekrarlamamaya alacam. Daha nce ilev, yap, ve snf ablonlarn tanmtk ve ablon parametrelerinin trler konusunda serbestlik getirdiklerini grmtk. Bu derste; hem birlik ve arayz ablonlarn da tanyacaz; hem de ablon parametrelerinin deer, this , alias , ve okuzlu eitleri olduunu da greceiz.

73.1

Kestirme ve uzun sz dizimi


C++ gibi baka dillerde de olduu gibi D'nin ablonlar ok gl olanaklardr. Buna ramen, en ok yararlanlan kullanmlarnn olabildiince rahat ve anlalr olmasna allmtr. lev, yap, veya snf ablonu tanmlamak; isminden sonra ablon parametre listesi eklemek kadar kolaydr: T ikiKat(T)(T deer) { return 2 * deer; } class Kesirli(T) { T pay; T payda; } // ...

Daha nce de grm olduunuz yukardaki tanmlar, D'nin kestirme ablon tanmlardr. Aslnda ablonlar daha uzun olarak template anahtar szc ile tanmlanrlar. Yukardaki sz dizimleri, aadaki tanmlarn ksa edeerleridir: template ikiKat(T) { T ikiKat(T deer) { return 2 * deer; } } template Kesirli(T) { class Kesirli { T pay; T payda; } // ...

Derleyicinin her zaman iin uzun tanm kullandn, ve kestirme sz dizimini arka planda u ekilde uzun tanma dntrdn dnebiliriz:

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

461

Ayrntl ablonlar

1. tanmladmz ablonu bir template kapsam iine alr 2. o kapsama da ayn ismi verir 3. ablon parametre listesini bizim tanmladmz ablondan alr ve o kapsama verir Kestirme tanm; biraz aada greceimiz tek tanm ieren ablon olana ile ilgilidir.
73.1.1

ablon isim alan


template blou, aslnda bir seferde birden ok ablon tanmlanmasna da olanak verir: template ablonBlou(T) { T birlev(T deer) { return deer / 3; } struct BirYap { T ye; }

Yukardaki blokta bir ilev, bir de yap ablonu tanmlamaktadr. O ablonlar rnein int ve double trleri iin, ve uzun isimleriyle yle kullanabiliriz: auto sonu = ablonBlou!int.birlev(42); writeln(sonu); auto nesne = ablonBlou!double.BirYap(5.6); writeln(nesne.ye); ablonun belirli bir trle kullanm, bir isim alan tanmlar. Bloun iindeki isimler, o isim alan aka belirtilerek kullanlabilirler. Bu isimler fazla uzun olabileceklerinden, onlara alias ve alias this dersinde grdmz alias anahtar szc ile ksa takma isimler verilebilir: alias ablonBlou!dchar.BirYap KarakterYaps; // ... auto nesne = KarakterYaps(''); writeln(nesne.ye);

73.1.2

Tek tanm ieren template bloklar


inde tek tanm bulunan bir ablon, eer ablon blounun ismi o tek tanmn ismi ile ayn ise, dorudan o tek tanm yerine kullanlabilir. Bu, imdiye kadarki ablonlarda kullandmz kestirme sz dizimini salayan olanaktr. rnek olarak, bykl 20 bayttan fazla olan trlerin byk olarak kabul edildii bir program olsun. Bir trn byk olup olmadnn karar yle bir ablonun iindeki bir bool deiken ile belirlenebilir: template byk_m(T) { const bool byk_m = T.sizeof > 20; }

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

462

Ayrntl ablonlar

Dikkat ederseniz, hem ablonun hem de iindeki tek tanmn isimleri ayndr. yle olduunda, bu uzun ablon tanmnn isim alan ve iindeki tek tanm aka byk_m!int.byk_m diye yazlmaz; ksaca, yalnzca ablonun isim alan yazlr: writeln(byk_m!int); Yukardaki iaretli blm, ablon iindeki ayn isimli bool yerine geer. O kod, ktya false yazar; nk byk_m!int , ablon iindeki bool deikendir. int 'in uzunluu 4 bayt olduu iin o bool deikenin deeri false 'tur.

73.2

ablon eitleri
lev, snf, ve yap ablonlar
Bu alt bal btnlk amacyla yazdm. Yukarda da grld gibi, bu tr ablonlarla hem ablonlar dersinde hem de daha sonraki rneklerde ok karlatk.

73.2.1

73.2.2

Birlik ablonlar
Birlik ablonlar, yap ablonlar ile ayn ekilde tanmlanrlar. Birlik ablonlar iin de kestirme ablon sz dizimi kullanlabilir. Bir rnek olarak, Birlikler dersinde tanmladmz IpAdresi birliinin daha genel ve daha kullanl olann tasarlamaya alalm. O dersteki birlik; deer olarak uint trn kullanyordu. O deerin paralarna erimek iin kullanlan dizinin elemanlarnn tr de ubyte idi: union IpAdresi { uint deer; ubyte[4] baytlar; } O birlik, hem IP adresi deeri tutuyordu, hem de o deerin paralarna ayr ayr erime olana veriyordu. Ayn kavram, daha genel isimler de kullanarak bir ablon olarak yle tanmlayabiliriz: union ParalDeer(AslTr, ParaTr) { AslTr deer; ParaTr[/* gereken eleman adedi */] paralar; } Bu birlik ablonu, asl deerin ve alt paralarnn trn serbeste tanmlama olana verir. Asl tr ve para tr, birbirlerinden bamsz olarak seilebilirler. Burada gereken bir ilem, para dizisinin uzunluunun kullanlan trlere bal olarak hesaplanmasdr. IpAdresi birliinde, uint 'in drt adet ubyte paras olduunu bildiimiz iin sabit olarak 4 yazabilmitik. Bu ablonda ise dizinin uzunluu, kullanlan trlere gre otomatik olarak hesaplanmaldr. Trlerin bayt olarak uzunluklar .sizeof niteliinden renilebildii iin; bu bilgiden yararlanrsak, uzunluk hesabn bir ilev ablonuna yaptrabiliriz:

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

463

Ayrntl ablonlar

int elemanAdedi(AslTr, ParaTr)() { return (AslTr.sizeof + (ParaTr.sizeof - 1)) / ParaTr.sizeof; } Not: O hesaptaki (ParaTr.sizeof - 1) ifadesi, trlerin uzunluklarnn birbirlerine tam olarak blnemedii durumlarda gerekir. Asl trn 5 bayt, para trnn 2 bayt olduunu dnn. O ifadeyi eklemezsek; gerekte 3 adet para gerektii halde, 5/2 hesabnn sonucu tamsay krplmas nedeniyle 2 kard. Artk para dizisinin eleman adedi olarak o ilevin dndrd deeri kullanabiliriz ve bylece birliin tanm tamamlanm olur: union ParalDeer(AslTr, ParaTr) { AslTr deer; ParaTr[elemanAdedi!(AslTr, ParaTr)()] paralar; } Daha nce tanmladmz IpAdresi birliinin edeeri olarak bu ablonu kullanmak istesek, trleri IpAdresi 'nde olduu gibi srasyla uint ve ubyte olarak belirtmemiz gerekir: auto adres = ParalDeer!(uint, ubyte)(0xc0a80102); foreach (eleman; adres.paralar) { write(eleman, ' '); } Birlikler dersinde grdmz ktnn aynsn elde ederiz: 2 1 168 192 Bu ablonun getirdii esneklii grmek iin IP adresinin paralarn iki adet ushort olarak edinmek istediimizi dnelim. Bu sefer, ParalDeer ablonunun ParaTr parametresi olarak ushort yazmak yeterlidir: auto adres = ParalDeer!(uint, ushort)(0xc0a80102); Alk olmadmz bir dzende olsa da, bu seferki kt iki ushort 'tan olumaktadr: 258 49320 Bir sonraki bala gemeden nce, burada baka bir tasarmn da kullanlabileceini gstermek istiyorum. Ka adet para gerektiini yukardaki elemanAdedi ilev ablonu ile hesapladmz iin, o ilevi armak iin sonuna () karakterleri yazmtk: ParaTr[elemanAdedi!(AslTr, ParaTr)()] paralar; Ayn hesabn sonucunu, tek tanml bir ablon iindeki deiken deeri olarak da saklayabilirdik: template elemanAdedi(AslTr, ParaTr) { const int elemanAdedi = (AslTr.sizeof + (ParaTr.sizeof - 1))

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

464

Ayrntl ablonlar

/ ParaTr.sizeof;

O tek tanml ablon, iindeki int deiken yerine getii iin, dorudan o deikenin deerinden yararlanabilirdik: ParaTr[elemanAdedi!(AslTr, ParaTr)] paralar;

73.2.3

Arayz ablonlar
Arayz ablonlar, arayzde kullanlan trler konusunda serbestlik getirirler. Arayz ablonlarnda da kestirme tanm kullanlabilir. rnek olarak, renkli nesnelerin arayzn tanmlayan, ama renk olarak hangi trn kullanlacan serbest brakan bir arayz tasarlayalm: interface RenkliNesne(RenkTr) { void renklendir(RenkTr renk); } O arayz, kendisinden treyen snflarn renklendir ilevini tanmlamalarn gerektirir; ama renk olarak ne tr kullanlaca konusunu serbest brakr. Bir sitedeki bir ereveyi temsil eden bir snf; renk olarak krmz, yeil, ve maviden oluan l bir yap kullanabilir: struct KrmzYeilMavi { ubyte krmz; ubyte yeil; ubyte mavi; } class Siteerevesi : RenkliNesne!KrmzYeilMavi { override void renklendir(KrmzYeilMavi renk) { // ... } } te yandan, renk olarak n frekansn kullanmak isteyen bir snf, renk iin frekans deerine uygun olan baka bir trden yararlanabilir: alias double Frekans; class Lamba : RenkliNesne!Frekans { override void renklendir(Frekans renk) { // ... } } Yine ablonlar dersinden hatrlayacanz gibi, "her ablon gerekletirmesi farkl bir trdr". Buna gre, RenkliNesne!KrmzYeilMavi ve RenkliNesne!Frekans arayzleri, farkl arayzlerdir. Bu yzden, onlardan treyen snflar da birbirlerinden bamsz sradzenlerin paralar olurlar; Siteerevesi ve Lamba , birbirlerinden bamszdr.

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

465

Ayrntl ablonlar

73.3

ablon parametre eitleri


imdiye kadar grdmz ablonlar, hep trler konusunda serbestlik getiriyorlard. Yukardaki rneklerde de kullandmz T ve RenkTr gibi ablon parametreleri, hep trleri temsil ediyorlard. rnein T 'nin anlam, ablonun kod iindeki kullanmna bal olarak int , double , renci , vs. gibi bir tr olabiliyordu. ablon parametreleri; deer, this , alias , ve okuzlu da olabilirler.

73.3.1

Tr parametreleri
Bu alt bal btnlk amacyla yazdm. imdiye kadar grdmz btn ablon parametreleri zaten hep tr parametreleriydi.

73.3.2

Deer parametreleri
ablon parametresi olarak deerler de kullanlabilir. Bu, ablonun tanm ile ilgili bir deerin serbest braklmasn salar. ablonlar derleme zaman olanaklar olduklar iin, deer olarak kullanlan ablon parametresinin derleme zamannda hesaplanabilmesi arttr. Bu yzden, programn almas srasnda hesaplanan, rnein giriten okunan bir deer kullanlamaz. Bir rnek olarak, belirli sayda keden oluan ekilleri temsil eden yaplar tanmlayalm: struct gen { Nokta[3] keler; // ... } struct Drtgen { Nokta[4] keler; // ... } struct Begen { Nokta[5] keler; // ... } rnek ksa olsun diye baka yelerini gstermedim. Normalde, o trlerin baka yelerinin ve ilevlerinin de bulunduunu ve hepsinde tamamen ayn ekilde tanmlandklarn varsayalm. Sonuta, dizi uzunluunu belirleyen deer dnda, o yaplarn tanmlar ayn olsun. Deer ablon parametreleri byle durumlarda yararldr. Yukardaki tanmlar yerine tek yap ablonu tanmlanabilir. Yeni tanm genel amal olduu iin, ismini de o ekillerin genel ismi olan poligon koyarak yle tanmlayabiliriz: struct Poligon(int keAdedi) { Nokta[keAdedi] keler; // ... } O yap ablonu, ablon parametresi olarak int trnde ve keAdedi isminde bir ablon parametresi almaktadr. O parametre deeri, yapnn tanmnda herhangi bir yerde kullanlabilir.

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

466

Ayrntl ablonlar

Artk o ablonu istediimiz sayda kesi olan poligonlar ifade etmek iin kullanabiliriz: Poligon!100 yzKeli; Yine alias 'tan yararlanarak kullanl isimler verebiliriz: alias Poligon!3 gen; alias Poligon!4 Drtgen; alias Poligon!5 Begen; // ... gen gen; Drtgen drtgen; Begen begen; Bylece; hem balangtaki farkl yap yerine tek ablon tanmlam oluruz, hem de herhangi sayda kesi olan ekiller iin kullanabiliriz. Yukardaki deer ablon parametresinin tr int 'ti. Derleme zamannda bilinebildii srece, deer olarak baka trler de kullanlabilir. Baka bir rnek olarak, basit XML elemanlar oluturmakta kullanlan bir snf ablonu tasarlayalm. Bu basit XML tanm, ok basite u kty retmek iin kullanlsn: nce < > karakterleri arasnda elemann ismi: <isim> sonra elemann deeri en sonunda da </ > karakterleri arasnda yine elemann ismi: </isim> rnein deeri 42 olan bir elemann <isim>42</isim> eklinde grnmesini isteyelim. Eleman isimlerini bir snf ablonunun string trndeki bir deer parametresi olarak belirleyebiliriz: class XmlEleman(string isim) { double deer; this(double deer) { this.deer = deer; } override string toString() const { return format("<%s>%s</%s>", isim, deer, isim); }

Bu rnekteki ablon parametresi, ablonda kullanlan bir trle deil, bir string deeriyle ilgilidir. O string 'in deeri de ablon iinde gereken her yerde kullanlabilir. alias 'tan yararlanarak kullanl tr isimleri de tanmlayarak: alias XmlEleman!"konum" Konum; alias XmlEleman!"scaklk" Scaklk; alias XmlEleman!"arlk" Arlk; void main() { Object[] elemanlar; elemanlar ~= new Konum(1); elemanlar ~= new Scaklk(23);

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

467

Ayrntl ablonlar

elemanlar ~= new Arlk(78); } writeln(elemanlar);

Not: Ben bu rnekte ksa olsun diye ve nasl olsa btn snf sradzenlerinin en stnde bulunduu iin bir Object dizisi kullandm. O snf ablonu aslnda daha uygun bir arayz snfndan da tretilebilirdi. Yukardaki kodun kts: <konum>1</konum> <scaklk>23</scaklk> <arlk>78</arlk> Deer parametrelerinin de varsaylan deerleri olabilir. rnein, herhangi boyutlu bir uzaydaki noktalar temsil eden bir yap tasarlayalm. Noktalarn koordinat deerleri iin kullanlan tr ve uzayn ka boyutlu olduu, ablon parametreleri ile belirlensin: struct Konum(T, int boyut = 3) { T[boyut] koordinatlar; } boyut parametresinin varsaylan bir deerinin bulunmas, bu ablonun o parametre belirtilmeden de kullanlabilmesini salar: Konum!double merkez; // boyutlu uzayda bir nokta

Gerektiinde farkl bir deer de belirtilebilir: Konum!(int, 2) nokta; // iki boyutlu dzlemde bir nokta

73.3.3

ye ilev ablonlar iin this parametreleri


ye ilevler de ablon olarak tanmlanabilirler. ye ilev ablonlarnn da tr ve deer parametreleri bulunabilir, ve normal ilev ablonlarndan beklendii gibi alrlar. Ek olarak; ye ilev ablonlarnn parametreleri, this anahtar szc ile de tanmlanabilir. Bu durumda, o anahtar szckten sonra yazlan isim, o nesnenin this referansnn tr haline gelir. (Not: Burada, ounlukla kurucu ilevler iinde grdmz this.ye = deer kullanmndaki this referansndan, baka bir deyile nesnenin kendisini ifade eden referanstan bahsediyorum.) struct BirYap { void birlev(this KendiTrm)() const { writeln("Bu nesnenin tr: ", typeid(KendiTrm)); } } O ye ilev ablonunun KendiTrm parametresi, nesnenin asl trdr: auto deiebilen = BirYap(); const sabit = BirYap(); immutable deimez = BirYap(); deiebilen.birlev(); sabit.birlev(); deimez.birlev();

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

468

Ayrntl ablonlar

KendiTrm ; nesnenin const , immutable , vs. tr belirtelerine de sahiptir: Bu nesnenin tr: deneme.BirYap Bu nesnenin tr: const(deneme.BirYap) Bu nesnenin tr: immutable(deneme.BirYap)

73.3.4

alias parametreleri
alias ablon parametrelerine karlk olarak D programlarnda geebilen btn yasal szckler kullanlabilir. Bu szckler evrensel isimler, yerel isimler, modl isimleri, baka ablon isimleri, vs. olabilirler. Tek koul, o parametrenin ablon iindeki kullanmnn o parametreye uygun olmasdr. rnek olarak, hangi yerel deikeni deitirecei kendisine bir alias parametre olarak bildirilen bir yapya bakalm: struct BirYap(alias deiken) { void birlev(int deer) { deiken = deer; } } O yapnn ye ilevi, deiken isminde bir deikene bir atama yapmaktadr. O deikenin programdaki hangi deiken olduu; bu ablon tanmland zaman deil, ablon kullanld zaman belirlenir: int x = 1; int y = 2; auto nesne = BirYap!x(); nesne.birlev(10); writeln("x: ", x, ", y: ", y); Yap ablonunun yukardaki kullanmnda yerel x deikeni belirtildii iin, birlev iindeki atama, onu etkiler: x: 10, y: 2 ablon parametresi olarak y belirtildiinde de o deiken deiir: auto nesne = BirYap!y(); nesne.birlev(10); writeln("x: ", x, ", y: ", y); Bu sefer y deimitir: x: 1, y: 10 Baka bir rnek olarak, alias parametresini ilev olarak kullanan bir ilev ablonuna bakalm: void aran(alias ilev)() { write("aryorum: "); ilev(); }

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

469

Ayrntl ablonlar

() parantezlerinden anlald gibi, aran ismindeki ilev ablonu, kendisine verilen parametreyi bir ilev olarak kullanmaktadr. Ayrca; parantezlerin iinin bo olmasndan anlald gibi, o ilev parametre gndermeden arlmaktadr. Parametre almadklar iin o kullanma uyan iki de ilev bulunduunu varsayalm: void birinci() { writeln("birinci"); } void ikinci() { writeln("ikinci"); } O ilevler, aran ablonu iindeki kullanma uyduklar iin o ablonun alias parametresinin deeri olabilirler: aran!birinci(); aran!ikinci(); Belirtilen ilevin arldn grrz: aryorum: birinci aryorum: ikinci alias ablon parametrelerini her eit ablonla kullanabilirsiniz. nemli olan, o parametrenin ablon iindeki kullanma uymasdr. rnein, yukardaki alias parametresi yerine bir deiken kullanlmas derleme hatasna neden olacaktr: int deiken; aran!deiken();

// derleme HATASI

Aldmz hata, () karakterlerinden nce bir ilev beklendiini, int trndeki deiken 'in uygun olmadn belirtir: Error: function expected before (), not deiken of type int Her ne kadar iaretlediim satr nedeniyle olsa da, derleme hatas aslnda aran ilevinin iindeki ilev() satr iin verilir. Derleyicinin gznde hatal olan; ablona gnderilen parametre deil, o parametrenin ablondaki kullanldr. Uygunsuz ablon parametrelerini nlemenin bir yolu, ablon kstlamalar tanmlamaktr. Bunu aada greceiz.
73.3.5

okuzlu parametreleri
levlerin belirsiz sayda parametre alacak ekilde tanmlanabildiklerini biliyoruz. rnein writeln ilevini istediimiz sayda parametre ile arabiliriz. Bu tr ilevlerin nasl tanmlandklarn Parametre Serbestlii dersinde grmtk. Ayn serbestlik ablon parametrelerinde de bulunur. ablon parametrelerinin saysn ve eitlerini serbest brakmak, ablon parametre listesinin en sonuna bir okuzlu ismi ve ... karakterleri yazmak kadar basittir. Bunun bir rneini, bir ilev ablonunda grelim:

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

470

Ayrntl ablonlar

void bilgiVer(alias ilev, okuzlu ...)(okuzlu parametreler) { // ... } Yukardaki ilev ablonunun ilk parametresini alias olarak setim. Onun yerine baka eit bir ablon parametresi de olabilirdi, ve yannda baka ablon parametreleri de bulunabilirdi. sminden anlald gibi, o alias parametresinin ilev olarak kullanlaca dnlmektedir. O parametreden sonra yazlan okuzlu ... , bilgiVer ilev ablonunun belirsiz sayda parametre ile arlabilmesini salar. O okuzlu, belirsiz ilev parametrelerinin hepsini birden ierir. rnein o ilev ablonunun u ekilde arldn dnelim: bilgiVer!writeln(1, "abc", 2.3); ablona aka verilen writeln parametresi, ablonun tanmnda kullanlan alias 'n deeri olur. leve gnderilen btn parametreler de parametreler isimli okuzluyu olutururlar. O okuzlunun paralarna da ablon iinde rnein bir foreach ile eriilebilir: void bilgiVer(alias ilev, okuzlu ...)(okuzlu parametreler) { foreach (i, parametre; parametreler) { ilev(i, ": ", typeid(parametre), " trnde ", parametre); } } kts: 0: int trnde 1 1: immutable(char)[] trnde abc 2: double trnde 2.3 alias olan ilk ablon parametresi, bilgiVer 'in kendi iini yaparken hangi ilevden yararlanacan belirtmektedir. rnei devam ettirmek iin, writeln yerine kendi yazdmz bir ilevden yararlanalm. ilev 'in ablon iinde be parametre ile arldn gryoruz. O kullanma uyduu srece, alias parametresi olarak herhangi bir ilevi belirtebiliriz. O be parametreden bazlarn gzard eden, ve yalnzca tr ve deeri kme parantezleri arasnda yazdran u ilev ablonuna bakalm: void yalnzcaTrVeDeer(TrBilgisi, Tr)(int saya, string dizgi1, TrBilgisi trsmi, string dizgi2, Tr deer) { write('{', trsmi, ' ', deer, '}'); } bilgiVer iindeki ilev arsna uyduu iin, bilgiVer 'i bu yeni ilevle de kullanabiliriz: bilgiVer!yalnzcaTrVeDeer(1, "abc", 2.3); Sonuta da o parametrenin yalnzca trlerini ve deerlerini kme parantezleri arasnda elde ederiz:

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

471

Ayrntl ablonlar

{int 1} {immutable(char)[] abc} {double 2.3} Ayn amaca ulamak iin baka yollar bulunduunu biliyorsunuz. rnein bilgiVer yerine trVeDeerBildir gibi daha ksa bir ablon da yazabilirdik. Ben, okuzlu parametrelerinden nceki parametrelerin nasl kullanldklarn gstermek iin bir alias parametresi setim.

73.4

ablon zellemeleri
zellemeleri de ablonlar dersinde anlatmtm. Aadaki meta programlama balnda da zelleme rnekleri greceksiniz. Tr parametrelerinde olduu gibi, baka eit ablon parametreleri de zellenebilir. rnein deer parametreleri: void birlev(int birDeer)() { // ... genel tanm ... } void birlev(int birDeer : 0)() { // ... sfra zel tanm ... }

73.5

Meta programlama
Kod retme ile ilgili olmalar nedeniyle, ablonlar dier D olanaklarndan daha st dzeyde programlama aralar olarak kabul edilirler. ablonlar bir anlamda, kod oluturan kodlardr. Kodlarn daha st dzey kodlarla oluturulmalar kavramna meta programlama denir. ablonlarn derleme zaman olanaklar olmalar, normalde alma zamannda yaplan ilemlerin derleme zamanna tanmalarna olanak verir. (Not: Ayn amala Derleme Zamannda lev letme (CTFE) olana da kullanlabilir. Bu konuyu daha sonraki bir derste gstereceim.) ablonlarn bu amala derleme zamannda iletilmeleri, ounlukla zyineleme zerine kuruludur. Bunun bir rneini grmek iin 0'dan balayarak belirli bir sayya kadar olan btn saylarn toplamn hesaplayan normal bir ilev dnelim. Bu ilev, parametre olarak rnein 4 aldnda 0+1+2+3+4'n toplamn dndrsn: int topla(int sonDeer) { int sonu; foreach (deer; 0 .. sonDeer + 1) { sonu += deer; } } return sonu;

Ayn ilevi zyinelemeli olarak da yazabiliriz:

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

472

Ayrntl ablonlar

int topla(int sonDeer) { return (sonDeer == 0 ? sonDeer : sonDeer + topla(sonDeer - 1)); } zyinelemeli ilev; kendi dzeyindeki deeri, bir eksik deerli hesaba eklemektedir. levde 0 deerinin zel olarak kullanldn gryorsunuz; zyineleme zaten onun sayesinde sonlanmaktadr. levlerin normalde alma zaman olanaklar olduklarn biliyoruz. topla 'y alma zamannda gerektike arabilir ve sonucunu kullanabiliriz: writeln(topla(4)); Ayn sonucun yalnzca derleme zamannda gerektii durumlarda ise, o hesap bir ilev ablonuyla da gerekletirilebilir. Yaplmas gereken; deerin ilev parametresi olarak deil, ablon parametresi olarak kullanlmasdr: // Uyar: Bu kod yanltr int topla(int sonDeer)() { return (sonDeer == 0 ? sonDeer : sonDeer + topla!(sonDeer - 1)()); } Bu ablon da hesap srasnda kendisinden yararlanmaktadr. Kendisini, sonDeer 'in bir eksii ile kullanmakta ve hesab yine zyinelemeli olarak elde etmeye almaktadr. Ne yazk ki o kod yazld ekilde alamaz. Derleyici, ?: ilecini alma zamannda iletecei iin, yukardaki zyineleme derleme zamannda sonlanamaz: writeln(topla!4()); // derleme HATASI

Derleyici, ayn ablonun sonsuz kere dallandn anlar ve bir hata ile sonlanr: Error: template instance deneme.topla!(-296) recursive expansion ablon parametresi olarak verdiimiz 4'ten geriye doru -296'ya kadar saydna baklrsa, derleyici ablonlarn zyineleme saysn 300 ile snrlamaktadr. Meta programlamada zyinelemeyi krmann yolu, ablon zellemeleri kullanmaktr. Bu durumda, ayn ablonu 0 deeri iin zelleyebilir ve zyinelemenin krlmasn bu sayede salayabiliriz: // Sfrdan farkl deerler iin kullanlan tanm int topla(int sonDeer)() { return sonDeer + topla!(sonDeer - 1)(); } // Sfr deeri iin zellemesi int topla(int sonDeer : 0)() { return 0; }

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

473

Ayrntl ablonlar

Derleyici, sonDeer 'in sfrdan farkl deerleri iin hep genel tanm kullanr ve en sonunda 0 deeri iin zel tanma geer. O tanm da basite 0 deerini dndrd iin zyineleme sonlanm olur. O ilev ablonunu yle bir programla deneyebiliriz: import std.stdio; int topla(int sonDeer)() { return sonDeer + topla!(sonDeer - 1)(); } int topla(int sonDeer : 0)() { return 0; } void main() { writeln(topla!4()); } imdi hatasz olarak derlenecek ve 4+3+2+1+0'n toplamn retecektir: 10 Burada dikkatinizi ekmek istediim nemli nokta, topla!4() ilevinin btnyle derleme zamannda iletiliyor olmasdr. Sonuta derleyicinin rettii kod, writeln 'e dorudan 10 hazr deerini gndermenin edeeridir: writeln(10); // topla!4()'l kodun edeeri

Derleyicinin rettii kod, 10 hazr deerini dorudan programa yazmak kadar hzl ve basittir. O 10 hazr deeri, yine de 4+3+2+1+0 hesabnn sonucu olarak bulunmaktadr; ancak o hesap, ablonlarn zyinelemeli olarak kullanlmalarnn sonucunda derleme zamannda iletilmektedir. Burada grld gibi, meta programlamann yararlarndan birisi, ablonlarn derleme zamannda iletilmelerinden yararlanarak normalde alma zamannda yaplmasna altmz hesaplarn derleme zamanna tanabilmesidir. Yukarda da sylediim gibi, daha sonraki bir derste gstereceim CTFE olana, baz meta programlama yntemlerini D'de gereksiz hale getirir.

73.6

Derleme zaman ok ekillilii


Bu kavram, ngilizce'de "compile time polymorphism" olarak geer. Nesne ynelimli programlamada ok ekilliliin snf tretme ile salandn biliyorsunuz. rnein bir ilev parametresinin bir arayz olmas, o parametre yerine o arayzden tremi her snfn kullanlabilecei anlamna gelir. Daha nce grdmz bir rnei hatrlayalm: import std.stdio; interface SesliAlet { string ses();

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

474

Ayrntl ablonlar

} class Keman : SesliAlet { string ses() { return ""; } } class an : SesliAlet { string ses() { return "n"; } } void sesliAletKullan(SesliAlet alet) { // ... baz ilemler ... writeln(alet.ses()); // ... baka ilemler ... } void main() { sesliAletKullan(new Keman); sesliAletKullan(new an); } Yukardaki sesliAletKullan ilevi ok ekillilikten yararlanmaktadr. Parametresi SesliAlet olduu iin, ondan tremi olan btn trlerle kullanlabilir. Yukardaki son cmlede geen btn trlerle kullanlabilme kavramn ablonlardan da tanyoruz. Byle dnnce, ablonlarn da bir eit ok ekillilik sunduklarn grrz. ablonlar btnyle derleyicinin derleme zamanndaki kod retmesiyle ilgili olduklarndan, ablonlarn sunduklar ok ekillilie derleme zaman ok ekillilii denir. Dorusu, her iki ok ekillilik de btn trlerle kullanlamaz. Her ikisinde de trlerin uymalar gereken baz koullar vardr. alma zaman ok ekillilii, belirli bir arayzden treme ile kstldr. Derleme zaman ok ekillilii ise ablon iindeki kullanma uyma ile kstldr. ablon parametresi, ablon iindeki kullanmda derleme hatasna neden olmuyorsa, o ablonla kullanlabilir. (Not: Eer tanmlanmsa, ablon kstlamalarna da uymas gerekir. ablon kstlamalarn biraz aada anlatacam.) rnein, yukardaki sesliAletKullan ilevi bir ablon olarak yazldnda, ses ye ilevi bulunan btn trlerle kullanlabilir: void sesliAletKullan(T)(T alet) { // ... baz ilemler ... writeln(alet.ses()); // ... baka ilemler ... } class Araba { string ses() { return "dt dt"; } }

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

475

Ayrntl ablonlar

// ... sesliAletKullan(new Keman); sesliAletKullan(new an); sesliAletKullan(new Araba); Yukardaki ablon, dierleriyle kaltm ilikisi bulunmayan Araba tr ile de kullanlabilmitir.

73.7

Kod imesi
ablonlar kod retme ile ilgilidirler. Derleyici, ablonun farkl parametrelerle her kullanm iin farkl kod retir. rnein yukarda en son yazdmz sesliAletKullan ilev ablonu, programda kullanld her tr iin ayr ayr retilir ve derlenir. Programda yz farkl tr ile arldn dnrsek; derleyici o ilev ablonunun tanmn, her tr iin ayr ayr, toplam yz kere oluturacaktr. Programn boyutunun bymesine neden olduu iin bu etkiye kod imesi (code bloat) denir. ou programda sorun oluturmasa da, ablonlarn bu zelliinin aklda tutulmas gerekir. te yandan, sesliAletKullan ilevinin ilk yazdmz SesliAlet alan tanmnda, yani ablon olmayan tanmnda, byle bir kod tekrar yoktur. Derleyici, o ilevi bir kere derler ve her SesliAlet tr iin ayn ilevi arr. lev tek olduu halde her hayvann kendisine zel olarak davranabilmesi, derleyici tarafndan ilev gstergeleriyle salanr. Derleyici her tr iin farkl bir ilev gstergesi kullanr ve bylece her tr iin farkl ye ilev arlr. alma zamannda ok kk bir hz kaybna yol asa da, ilev gstergeleri kullanmann ou programda nemi yoktur ve zaten bu zm sunan en hzl gerekletirmedir. Burada szn ettiim hz etkilerini tasarmlarnzda fazla n planda tutmayn. Program boyutunun artmas da, alma zamannda fazladan ilemler yaplmas da hz drecektir. Belirli bir programda hangisinin etkisinin daha fazla olduuna ancak o program denenerek karar verilebilir.

73.8

ablon kstlamalar
ablonlarn her tr ve deerdeki ablon parametresi ile arlabiliyor olmalarnn getirdii bir sorun vardr. Uyumsuz bir parametre kullanldnda, bu uyumsuzluk ancak ablonun kendi kodlar derlenirken farkedilebilir. Bu yzden, derleme hatasnda belirtilen satr numaras, ablon blouna iaret eder. Yukardaki sesliAletKullan ablonunu ses isminde ye ilevi bulunmayan bir trle aralm: class Fincan { // ... ses() ilevi yok ... } // ... sesliAletKullan(new Fincan); // uyumsuz bir tr

Oradaki hata, ablonun uyumsuz bir trle arlyor olmasdr. Oysa derleme hatas, ablon iindeki kullanma iaret eder: void sesliAletKullan(T)(T alet) {

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

476

Ayrntl ablonlar

// ... baz ilemler ... writeln(alet.ses()); // ... baka ilemler ...

// derleme HATASI

Bunun bir sakncas, belki de bir ktphane modlnde tanml olan bir ablona iaret edilmesinin, hatann o ktphanede olduu yanlgsn uyandrabileceidir. Daha nemlisi, asl hatann hangi satrda olduunun hi bildirilmiyor olmasdr. Byle bir sorunun arayzlerde bulunmadna dikkat edin. Parametre olarak arayz alacak ekilde yazlm olan bir ilev, ancak o arayzden tremi olan trlerle arlabilir. Treyen her tr arayzn ilevlerini gerekletirmek zorunda olduu iin, ilevin uyumsuz bir trle arlmas olanakszdr. O durumda derleme hatas, ilevi uygunsuz trle aran satra iaret eder. ablonlarn yalnzca belirli koullar salayan trlerle kullanlmalar ablon kstlamalar ile salanr. ablon kstlamalar, ablon blounun hemen ncesine yazlan if deyiminin iindeki mantksal ifadelerdir: void birablon(T)() if (/* ... kstlama koulu ... */) { // ... ablonun tanm ... } Derleyici bu ablon tanmn ancak kstlama koulu true olduunda gze alr. Koulun false olduu durumda ise bu ablon tanmn gzard eder. Bunun bir rnei olarak meta programlama yoluyla faktriyel hesaplayan bir ablona ve onun 0 deeri iin zellemesine bakalm: import std.stdio; ulong faktriyel(ulong deer)() { return deer * faktriyel!(deer - 1)(); } ulong faktriyel(ulong deer : 0)() { return 1; } void main() { writefln("19! == %25s", faktriyel!19()); writefln("20! == %25s", faktriyel!20()); writefln("21! == %25s", faktriyel!21()); writeln(); writefln("(ulong.max: %s)", ulong.max); writefln("(doru 21!: 51090942171709440000)"); } Yukardaki program; 19, 20, ve 21 deerlerinin faktriyelini hesaplamaktadr. (Not: Ayn iin ablon kullanmadan, normal ilevlerle de gerekletirilebileceini hatrlatmak istiyorum. Yukardaki ablonlar, sonularn derleme zamannda hesaplanmalarn ve sanki kaynak koda rnein dorudan 121645100408832000 yazlm gibi derlenmelerini salarlar.) O programdaki deerleri zel olarak setim; nk 21'in faktriyelinin ulong 'a smadn biliyorum. Yukardaki kod, 21 ve daha byk deerlerin faktriyelini yanl hesaplar:

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

477

Ayrntl ablonlar

19! == 20! == 21! ==

121645100408832000 2432902008176640000 14197454024290336768

(ulong.max: 18446744073709551615) (doru 21!: 51090942171709440000) 21'in faktriyeli, 51,090,942,171,709,440,000 deeridir. O deer ulong 'a smad iin tamtr. faktriyel ablonunun geersiz deerlerle kullanlmas ve bu yzden yanl sonular retmesi bir ablon kstlamas ile engellenebilir: ulong faktriyel(ulong deer)() if (deer <= 20) { return deer * faktriyel!(deer - 1)(); } Derleyici, ablonu ancak o koula uyan deerler iin gze alacaktr: writefln("21! == %25s", faktriyel!21()); // derleme HATASI Derleme hatas, o kullanma karlk bir ablon tanm bulunmadn bildirir: Error: template instance faktriyel!(21) does not match any template declaration Bylece amacmza ulam oluruz. Artk derleme hatas ablonun yanl parametre ile kullanld satra iaret etmektedir. ablonlar derleme zaman olanaklar olduklar iin, ablon kstlamalar da derleme zamannda iletilirler. Bu yzden, Koullu Derleme dersinde grdmz ve derleme zamannda iletildiini rendiimiz is ifadesi ile de ok kullanlrlar. Bunun rneklerini aada gstereceim.
73.8.1

simli kstlama yntemi


ablon kstlamalar baz durumlarda yukardakinden ok daha karmak olabilirler. Bunun stesinden gelmenin bir yolu, benim isimli kstlama olarak adlandrdm bir yntemdir. Bu yntem D'nin drt olanandan yararlanarak kstlamaya anlalr bir isim verir. Bu drt olanak; isimsiz kapama, typeof , is ifadesi, ve tek tanm ieren ablonlardr. Bu yntemi burada daha ok bir kalp olarak gstereceim ve her ayrntsna girmemeye alacam. Parametresini belirli ekilde kullanan bir ilev ablonu olsun: void kullan(T)(T nesne) { // ... nesne.hazrlan(); // ... nesne.u(42); // ... nesne.kon(); // ... }

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

478

Ayrntl ablonlar

ablon iindeki kullanmndan anlald gibi, bu ablonun kullanld trlerin hazrlan , u , ve kon isminde ye ilevinin bulunmas gerekir. O ilevlerden u 'un ayrca int trnde bir de parametresi olmaldr. Bu kstlamay is ve typeof ifadelerinden yararlanarak yle yazabiliriz: void kullan(T)(T nesne) if (is (typeof(T.hazrlan)) && is (typeof(T.u(1))) && is (typeof(T.kon))) { // ... } O tanm aada biraz daha amaya alacam. imdilik, is (typeof(T.hazrlan)) kullanmn bir kalp olarak "eer o trn hazrlan isminde bir ye ilevi varsa" diye kabul edebilirsiniz. leve is (typeof(T.u(1))) eklinde bir de parametre verildiinde ise, "o ilev ek olarak o trden bir parametre de alyorsa" diye kabul edebilirsiniz. Yukardaki gibi bir kstlama istendii gibi alyor olsa da, her zaman iin tam ak olmayabilir. Onun yerine, o ablon kstlamasnn ne anlama geldiini daha iyi aklayan bir isim verilebilir: void kullan(T)(T nesne) if (uabilir_mi!T) { // ... }

// isimli kstlama

Bu kstlama bir ncekinden daha aktr. Bu ablonun uabilen trlerle altn okunakl bir ekilde belgeler. Yukardaki gibi isimli kstlamalar u kalba uygun olarak tanmlanrlar: template uabilir_mi(T) { const bool uabilir_mi { T uan; uan.hazrlan(); uan.u(1); uan.kon(); }())); }

= is (typeof( // umaya hazrlanabilmeli // belirli mesafe uabilmeli // istendiinde konabilmeli

O yntemde kullanlan D olanaklarn ve birbirleriyle nasl etkiletiklerini ok ksaca gstermek istiyorum: template uabilir_mi(T) { // (6) (5) (4) const bool uabilir_mi = is (typeof( { // (1) T uan; // (2) uan.hazrlan(); uan.u(1); uan.kon(); // (3) }())); } 1. simsiz kapama: simsiz kapamalar lev Gstergeleri ve Kapamalar dersinde grmtk. Yukardaki kme parantezleri, isimsiz bir kapama tanmlar.

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

479

Ayrntl ablonlar

2. Kapama blou: Kapama blou, kstlamas tanmlanmakta olan tr, asl ablonda kullanld gibi kullanr. Yukardaki blokta nce bu trden bir nesne oluturulmakta, ve o trn sahip olmas gereken ye ilevi arlmaktadr. (Not: Bu kodlar typeof tarafndan kullanlrlar ama hibir zaman iletilmezler.) 3. Kapamann iletilmesi: Bir kapamann sonuna yazlan () parantezleri, normalde o kapamay iletir. Ancak, yukardaki iletme bir typeof iinde olduu iin, bu kapama hibir zaman iletilmez. 4. typeof ifadesi: typeof , imdiye kadarki rneklerde ok kullandmz gibi, kendisine verilen ifadenin trn retir. typeof 'un nemli bir zellii, trn rettii ifadeyi hi bir ekilde iletmemesidir. typeof , bir ifadenin eer iletilse ne trden bir deer reteceini bildirir: int i = 42; typeof(++i) j; assert(i == 42);

// 'int j;' yazmakla ayn ey // ++i iletilmemitir

Yukardaki assert 'ten de anlald gibi, ++i ifadesi iletilmemitir. typeof , yalnzca o ifadenin trn retmi ve bylece j de int olarak tanmlanmtr. Eer typeof 'a verilen ifadenin geerli bir tr yoksa, typeof void bile olmayan geersiz bir tr dndrr. Eer uabilir_mi ablonuna gnderilen tr, o isimsiz kapama iindeki kodlarda gsterildii gibi derlenebiliyorsa, typeof geerli bir tr retir. Eer o tr kapama iindeki kodlardaki gibi derlenemiyorsa, typeof geersiz bir tr dndrr. 5. is ifadesi: Koullu Derleme dersinde is ifadesinin birden fazla kullanmn grmtk. Buradaki is (Tr) eklindeki kullanm, kendisine verilen trn anlaml olduu durumda true deerini retiyordu: int i; writeln(is (typeof(i))); writeln(is (typeof(varOlmayanBirsim))); // true // false

Yukardaki ikinci ifadede bilinmeyen bir isim kullanld halde, derleyici hata vermez. Programn kts ikinci satr iin false deerini ierir: true false Bunun nedeni, typeof 'un ikinci kullanm iin geersiz bir tr retmi olmasdr. 6. Tek tanm ieren ablon: Bu derste anlatld gibi, uabilir_mi ablonunun iinde tek tanm bulunduu iin, ve o tanmn ismi ablonun ismiyle ayn olduu iin; bu ablon, iindeki tek tanm yerine geebilir. te, yukardaki kullan ilev ablonunun kstlamas, btn bu olanaklar sayesinde kullanl bir isim edinmi olur: void kullan(T)(T nesne) if (uabilir_mi!T) { // ... } O ablonu birisi uyumlu, dieri uyumsuz iki trle armay deneyelim:

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

480

Ayrntl ablonlar

// ablondaki kullanma uyan bir tr class ModelUak { void hazrlan() {} void u(int mesafe) {} void kon() {}

// ablondaki kullanma uymayan bir tr class Gvercin { void u(int mesafe) {} } // ... kullan(new ModelUak); kullan(new Gvercin); // derlenir // derleme HATASI

simli veya isimsiz, bir ablon kstlamas tanmlanm olduu iin; bu derleme hatas artk ablonun iine deil, ablonun uyumsuz trle kullanld satra iaret eder.

73.9

zet
nceki ablonlar dersinin sonunda unlar hatrlatmtm: ablonlar, kodun kalp halinde tarif edilmesini salarlar btnyle derleme zamannda ileyen bir olanaktr ablon parametreleri nlem iaretinden sonra aka belirtilebilir; tek parametre iin parantez kullanmaya gerek yoktur ablon parametreleri yalnzca ilev ablonlarnda karsanabilirler ablon zellemeleri : karakteri ile belirtilir varsaylan ablon parametre deerleri = karakteri ile belirtilir Bu derste de u kavramlar grdk: ablonlar kestirme veya uzun sz dizimleriyle tanmlanabilirler ablon kapsam bir isim alan belirler iinde tek tanm bulunan ablon, o tanm yerine geer ilev, snf, yap, birlik, ve arayz ablonlar tanmlanabildii gibi; ablon kapsam iinde her tr tanm da bulunabilir ablon parametrelerinin tr, deer, this , alias , ve okuzlu eitleri vardr ablonlar, parametrelerinin herhangi bir kullanm iin zellenebilirler meta programlama, ilemlerin derleme zamannda yaplmalarn salar ablonlar derleme zaman ok ekillilii olanaklardr derleyici, ablonun her farkl parametreli kullanm iin ayr kod retir; ar olduu durumda buna kod imesi denir olas derleme hatalarnn ablonun yanl kullanld satra iaret edebilmesi iin ablon kstlamalar tanmlanabilir isimli kstlama yntemi, kstlamalara okunakl isimler vermeye yarar

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

481

Dier lev Olanaklar

74

Dier lev Olanaklar


levleri daha nce aadaki derslerde grmtk: levler lev Parametreleri lev Ykleme lev Gstergeleri ve Kapamalar

Bu derste, nceki derslerde yer almayan baka ilev olanaklarn anlatacam.

74.1

Dn tr olanaklar
levler auto , ref , inout , ve auto ref olarak bildirilebilirler. Bunlar, ilevlerin dn trleriyle ilgilidir.

74.1.1

auto ilevler
auto olarak bildirilen ilevlerin dn trlerinin aka yazlmas gerekmez: auto topla(int birinci, double ikinci) { double sonu = birinci + ikinci; return sonu; } Derleyici dn trn return satrndan otomatik olarak karsar. Yukardaki ilevin return ile dndrd sonu double olduu iin, o ilev sanki dn tr double yazlm gibi derlenir.

74.1.2

ref ilevler
levlerin dndrdkleri deerler, normalde ilevi aran tarafa kopyalanrlar. ref belirteci, dn deerinin kopyalanmak yerine referans olarak dndrlmesini salar. rnein aadaki ilev, kendisine verilen iki parametreden byk olann dndrmektedir: int by(int birinci, int ikinci) { return (birinci > ikinci) ? birinci : ikinci; } O ilevin hem parametreleri hem de dn deeri normalde kopyalanr: int a = 1; int b = 2; int sonu = by(a, b); sonu += 10; // ne a ne de b etkilenir writefln("a: %s, b: %s, sonu: %s", a, b, sonu); by ilevinin dn deeri sonu deikenine kopyaland iin; o deikenin arttrlmas, yalnzca sonu isimli kopyay etkiler. leve kendileri de zaten kopyalanarak geirilmi olan a ve b deimezler: a: 1, b: 2, sonu: 12

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

482

Dier lev Olanaklar

Parametrelerin kopyalanmak yerine referans olarak gnderilmeleri iin ref anahtar szcnn kullanldn biliyorsunuz. Ayn szck dn tr iin de kullanlabilir ve ilevin dn deerinin de referans olarak dndrlmesini salar: ref int by(ref int birinci, ref int ikinci) { return (birinci > ikinci) ? birinci : ikinci; } Dndrlen referans, parametrelerden birisinin takma ismi yerine geecek ve onda yaplan deiiklik artk ya a 'y ya da b 'yi deitirecektir: int a = 1; int b = 2; by(a, b) += 10; // ya a ya b etkilenir writefln("a: %s, b: %s", a, b); Dikkat ederseniz, ilevin dndrd referans sonu diye bir deiken kullanmadan dorudan arttryoruz. O ilem, a ve b 'den byk olann etkiler: a: 1, b: 12 Yerel referans iin gsterge gerekir: Burada bir noktaya dikkatinizi ekmek istiyorum. ref kullanlm olmasna ramen, o dn deerinin yerel bir deikene atanmas; a veya b 'yi yine de deitirmez: int sonu = by(a, b); sonu += 10;

// yine yalnzca 'sonu' deiir

by ilevi a 'ya veya b 'ye referans dndryor olsa da, o referans sonu ismindeki yerel deikene kopyaland iin a veya b deimez: a: 1, b: 2, sonu: 12 sonu 'un a 'nn veya b 'nin referans olmas istenseydi, bir gsterge olarak tanmlanmas gerekirdi: int * sonu = &by(a, b); *sonu += 10; writefln("a: %s, b: %s, sonu: %s", a, b, *sonu); sonu artk ya a 'ya ya da b 'ye eriim salad iin; onun araclyla yaplan deiiklik, o ikisinin byk olann etkilerdi: a: 1, b: 12, sonu: 12 Yerel deikene referans dndrlemez: Yukardaki ref dn deeri, daha ilev arlmadan nce yaamaya balayan iki deikenden birisinin takma ismi gibi almaktadr. Bir baka deyile, birinci de dndrlse, ikinci de dndrlse; o dn deeri hep ilevin arld noktada zaten yaamakta olan a 'nn veya b 'nin referansdr. Yaam sresi ilevden klrken sona erecek olan bir deikene referans dndrlemez: ref string parantezinde(string sz) { string sonu = '(' ~ sz ~ ')';

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

483

Dier lev Olanaklar

return sonu; } // sonu'un yaam burada sona erer lev kapsamnda tanmlanm olan sonu 'un yaam, o ilevden kldnda sona erer. O yzden, o deikenin takma ismi gibi kullanlacak bir dn deeri olamaz. Derleme, yerel deikene referans dndrldn bildiren bir hata ile sonlanr: Error: escaping reference to local variable sonu

74.1.3

auto ref ilevler


Yukardaki parantezinde ilevinin yaam sresi sona eren yerel deiken nedeniyle derlenemediini grdk. auto ref , yle durumlarda yararldr. auto ref olarak bildirilmi olan bir ilevin dn tr, auto ilevlerde olduu gibi otomatik olarak karsanr. Ek olarak, referans olamayacak bir deer dndrldnde; o deer referans olarak deil, kopyalanarak dndrlr. Ayn ilevi auto ref olarak yazdmzda program derlenir: auto ref parantezinde(string sz) { string sonu = '(' ~ sz ~ ')'; return sonu; // derlenir } Dndrlen deerin referans olabildii durumlarda ise o deikene referans dndrlr.

74.1.4

inout ilevler
Bu belirte ilev parametrelerinde ve dn trnde kullanlr, ve o ilevin parametrelerine bal olarak deiebilen, const , veya immutable anlamna gelir. Yukardaki ilevi string alacak ve string dndrecek ekilde tekrar yazalm: string parantezinde(string sz) { return '(' ~ sz ~ ')'; } O ileve string trnde parametre verilmesi gerektiini, ve sonucunun da string olduunu biliyoruz: writeln(parantezinde("merhaba")); "merhaba" hazr deerinin tr string , yani immutable(char)[] olduu iin, o kod derlenir ve alr: (merhaba) Burada kullansz bir durum vardr. O ilev string trne bal olarak yazld iin, immutable olmayan bir dizgi ile arlamaz: char[] dizgi; // elemanlar deiebilir dizgi ~= "selam"; writeln(parantezinde(dizgi));

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

484

Dier lev Olanaklar

Derleme hatas, deiebilen karakterlerden oluan char[] trnn string 'e dntrlemeyeceini bildirir: Error: cannot implicitly convert expression (dizgi) of type char[] to string Ayn sorun, const(char)[] dizgilerinde de vardr. Bu sorunu zmek iin bir ka yntem dnlebilir. Bir yntem, ilevi deiebilen ve const karakter dizileri iin de yklemektir: char[] parantezinde(char[] sz) { return '(' ~ sz ~ ')'; } const(char)[] parantezinde(const(char)[] sz) { return '(' ~ sz ~ ')'; } Bunun programclkta kanlmas gereken kod tekrar anlamna geldiini gryoruz. Bu ilevlerin ileride geliebileceklerini veya olas hatalarnn giderilebileceklerini dnrsek, o deiikliklerin nde de yaplmasnn unutulmamas gerekecektir. O yzden bu riskli bir tasarmdr. Baka bir yntem, ilevi ablon olarak tanmlamaktr: T parantezinde(T)(T sz) { return '(' ~ sz ~ ')'; } O zm ablon iindeki kullanma uyan her tr ile kullanlabilir. Bunun bazen fazla esnek olabileceini ve ablon kstlamalar kullanlmasnn gerekebileceini de nceki derste grmtk. inout yntemi ablon zmne ok benzer; ama btn tr deil, yalnzca trn deiebilen, const , veya immutable zelliini esnek brakr. O zellii, ilevin parametrelerinden otomatik olarak karsar: inout(char[]) parantezinde(inout(char[]) sz) { return '(' ~ sz ~ ')'; } Not: dmd'nin bu dersi yazdm srada kullandm 2.047 srmnde inout ile ilgili hatalar var. rnein parametrelerin daha doru olarak inout(char)[] olarak yazlmalar derleme hatasna neden oluyor. inout , parametreden otomatik olarak karsanan zellii dn trne de aktarr. O ilev char[] tryle arldnda; hi inout yazlmam gibi derlenir. immutable(char)[] veya const(char)[] trleriyle arldnda ise; inout , srasyla immutable veya const yerine geer. Bunu, ilevin dn trn yazdrarak grebiliriz: char[] deiebilen; writeln(typeid(parantezinde(deiebilen))); const(char)[] sabit;

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

485

Dier lev Olanaklar

writeln(typeid(parantezinde(sabit))); immutable(char)[] deimez; writeln(typeid(parantezinde(deimez))); arnn farkl dn trleri: char[] const(const(char)[]) immutable(immutable(char)[]) zetle; inout , parametre trnn deiebilme, const , veya immutable zelliini dn trne aktarr.

74.2

Davran olanaklar
pure ve nothrow , ilevlerin genel davranlaryla ilgilidir.

74.2.1

pure ilevler
levlerin deer retebildiklerini veya yan etki oluturabildiklerini biliyoruz. Bunlar levler dersinde grmtk. Programclkta deer retmenin yan etki oluturmaktan bir ok adan daha iyi olduu kabul edilir. levlerin sonularnn programn belirli bir andaki genel durumundan bamsz olmas ve programn genel durumunu deitirmemesi yelenir. Bir ilevin sonucunun programn genel durumundan bamsz ve btnyle giri parametrelerine bal olmas, o ilevin tutarl ve gvenli olarak almasna yardm eder. yle bir ilev, belirli parametre deerlerine karlk hep ayn dn deerini retecektir. Yan etki retmeyen byle ilevler daha kolay yazlrlar ve test edilirler. Bu ok nemli bir konu olarak grld iin, baz fonksiyonel programlama dillerinde ilevlerin yan etki retmeleri olanakszdr. Byle programlama dillerinin "saf" anlamna gelen "pure" olduu sylenir. levlerin safl konusunda derleyiciden yararlanmann yolu, onlar pure olarak bildirmektir. Derleyici, pure ilevlerin programda evrensel deiiklikler yapmalarna izin vermez: pure int topla(int birinci, int ikinci) { writeln("topluyorum"); // derleme HATASI return birinci + ikinci; } writeln yan etkisi olan bir ilevdir nk programn standart knda deiiklie neden olur. arlabilse, pure olarak bildirilmi olan topla 'nn da yan etkili olmasna neden olacaktr. O yzden yukardaki ilev derlenemez. Derleme hatas, pure olmayan writeln 'in arlamayacan bildirir: Error: pure function 'topla' cannot call impure function 'writeln' te yandan, pure ilevlerin bu kadar kstl olmalar bazen istenmez. rnein yukardaki gibi bir writeln ifadesi programdaki bir hatann giderilmesi srasnda geici olarak eklenmi olabilir. Bu gibi durumlarda yararl olmak amacyla, debug olarak iaretlenmi olan ifadeler ve bloklar saf olmasalar bile pure ilevlerde kullanlabilirler:

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

486

Dier lev Olanaklar

pure int topla(int birinci, int ikinci) { debug writeln("topluyorum"); // imdi derlenir return birinci + ikinci; } Not: Bu sefer programn -debug seenei ile derlenmesi gerekir. pure olarak bildirilmi olan btn ilevlerin uymas gereken koul udur: Programn evrensel durumu ile ilgili hibir bilgi edinemez, veya o durumu deitiremez. levin programn genel durumuna bal olmas veya o durumu deitirmesi bu sayede nlenir. Ek olarak, aadaki koulun salanp salanmamasna gre ilev tam saf (strongly pure) veya yar saf (weakly pure) olarak anlr: Tam saf ilevlerin btn parametreleri ya immutable 'dr ya da otomatik olarak immutable 'a dnebilir. Tam saf ilevlerin, parametrelerinde deiiklik yaparak yan etki oluturmalar da bylece nlenmi olur. Yar saf ilevler ise parametrelerinde deiiklik yapabilirler: // yar saf (weakly pure) pure void eksilt(ref int deer) { --deer; } Yukardaki garantilerin salanabilmesi iin derleyici pure ilevlerden pure olmayan ilevlerin arlmalarna izin vermez.
74.2.2

nothrow ilevler
D'nin hata dzeneini Hata Atma ve Yakalama dersinde grmtk. Her ilevin hangi durumlarda hangi hatalar atabilecei o ilevin belgesinde belirtilmelidir. Genel bir kural olarak, btn hatalarn dolayl da olsa Exception 'dan tremi olduklar varsaylabilir ve tek catch blou ile btn hatalar yakalanabilir. (Not: Error sradzeninden tremi olan hatalarn ve en genel hata tr olan Throwable trnn yakalanmasnn nerilmediini yine o derste grmtk.) Baz durumlarda ise ardmz ilevlerin ne tr hatalar attklarn deil, kesinlikle hata atmadklarn bilmek isteriz. Belirli admlarnn kesintisiz olarak devam etmesi gereken baz algoritmalar, o admlar srasnda hata atlmadndan emin olmak zorundadrlar. nothrow , ilevin hata atmadn garanti eder: nothrow int topla(int birinci, int ikinci) { // ... } O ilev ne kendisi hata atabilir ne de hata atabilen bir ilevi arabilir: nothrow int topla(int birinci, int ikinci) { writeln("topluyorum"); // derleme HATASI return birinci + ikinci; } Derleme hatas, topla 'nn hata atabileceini bildirir:

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

487

Dier lev Olanaklar

Error: function deneme.topla 'topla' is nothrow yet may throw Bunun nedeni, writeln 'in nothrow olarak bildirilmi bir ilev olmamasdr. Derleyici, ilevlerin kesinlikle hata atmayacaklarn da anlayabilir. topla 'nn aadaki tanmnda her tr hata yakaland iin, nothrow 'un getirdii garantiler geerliliini srdrr, ve ilev nothrow olmayan ilevleri bile arabilir: nothrow int topla(int birinci, int ikinci) { int sonu; try { writeln("topluyorum"); // derlenir sonu = birinci + ikinci; } catch (Exception hata) { // ... } } return sonu; // btn hatalar yakalar

74.3

Gvenlik olanaklar
@safe , @trusted , ve @system belirteleri ilevlerin garanti ettikleri gvenliklerle ilgilidir.

74.3.1

@safe ilevler
Programc hatalarnn nemli bir blm, farknda olmadan bellein yanl yerlerine yazlmas ve o yerlerdeki bilgilerin bu yzden bozulmalar ile ilgilidir. Bu hatalar genellikle gstergelerin yanl kullanlmalar ve gvensiz tr dnmleri sonucunda oluur. "Gvenli" anlamna gelen @safe ilevler, bellei bozmayacan garanti eden ilevlerdir. Bazlarna bu derslerde hi deinmemi olsam da, derleyici @safe ilevlerde u ilemlere ve olanaklara izin vermez: gstergeler void* dndaki gsterge trlerine dntrlemezler gsterge olmayan bir tr, gsterge trne dntrlemez gstergelerin deerleri deitirilemez gsterge veya referans yeleri bulunan birlikler kullanlamaz @system olarak bildirilmi olan ilevler arlamaz Exception snfndan tremi olmayan bir hata yakalanamaz inline assembler kullanlamaz deiebilen deikenler immutable 'a dntrlemezler immutable deikenler deiebilen trlere dntrlemezler iletim dizilerinin yerel deikenleri shared 'e dntrlemezler shared deikenler i paracnn yerel deikeni olacak ekilde dntrlemezler ilevlerin yerel deikenlerinin veya parametrelerinin adresleri alnamaz __gshared deikenlere eriilemez

74.3.2

@trusted ilevler
"Gvenilir" anlamna gelen @trusted olarak bildirilmi olan ilevler, @safe olarak bildirilemeyecek olduklar halde tanmsz davrana neden olmayan ilevlerdir.

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

488

Dier lev Olanaklar

Byle ilevler, @safe ilevlerin yasaklad ilemleri yapyor olduklar halde hatal olmadklar programc tarafndan garantilenen ilevlerdir. Programcnn derleyiciye "bu ileve gvenebilirsin" demesi gibidir. Derleyici programcnn szne gvenir, ve @trusted ilevlerin @safe ilevlerden arlmalarna izin verir.
74.3.3

@system ilevler
@safe veya @trusted olarak bildirilmi olmayan btn ilevlerin @system olduklar varsaylr. Derleyici byle ilevlerin doru veya gvenilir olduklarn dnemez.

74.4

Derleme zamannda ilev iletme


Derleme zamannda yaplabilen hesaplar ou programlama dilinde olduka kstldr. Bu hesaplar genellikle sabit uzunluklu dizilerin uzunluklarn hesaplamak, veya hazr deerler kullanan aritmetik ilemler yapmak kadar basittir: writeln(1 + 2); Yukardaki 1 + 2 ilemi derleme zamannda iletilir ve program dorudan 3 yazlm gibi derlenir; o hesap iin alma zamannda hi zaman harcanmaz. D'nin "compile time function execution (CTFE)" denen zellii ise, normal olarak alma zamannda iletildiklerini dneceimiz ilevlerin bile derleme zamannda iletilmelerine olanak salar. rnek olarak, ktya bir men yazdran bir ileve bakalm: import std.stdio; string men(string balk, string[] seenekler, int genilik) { string sonu = ortalanmSatr("-= " ~ balk ~ " =-", genilik); foreach (i, seenek; seenekler) { sonu ~= ortalanmSatr(". " ~ seenek ~ " .", genilik); } } return sonu;

O ilevin olduka karmak olduunu dnebiliriz. Hem balk satrn hem de seenekleri belirli lde iledikten sonra onlar ortalanmSatr isimli baka bir ileve gndermektedir. Benzer karmakl ortalanmSatr ilevinde de gryoruz: string ortalanmSatr(string yaz, int genilik) { string girinti; if (yaz.length < genilik) { foreach (i; 0 .. (genilik - yaz.length) / 2) { girinti ~= ' '; } } } return girinti ~ yaz ~ '\n';

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

489

Dier lev Olanaklar

Btn o karmakla ramen, kullanmna bal olarak, men ilevi btnyle derleme zamannda iletilebilir. rnein static bir deikenin ilklenmesi srasnda: void main() { static string tatlMens = men("Tatllar", [ "Baklava", "Kadayf", "Muhallebi" ], 30); writeln(tatlMens); } Yukardaki programdaki men ilevi, ortalanmSatr ilevinden ve kendi ilemlerinden yararlanarak u meny oluturur: -= Tatllar =. Baklava . . Kadayf . . Muhallebi . Burada nemli olan, yukardaki sonucun btnyle derleme zamannda oluturulmu olmasdr. Bu, tatlMens deikeni static olarak bildirildii iindir. static deikenler programda yalnzca bir kere ilklenirler. static deikenlerin ilk deerlerinin derleme zamannda bilinmeleri gerekir. Yukardaki programdaki ilevlerin derleme zamannda iletilmelerinin sonucunda, tatlMens deikeni sanki o hesaplarn sonunda oluan dizgi programa aka yazlm gibi derlenir: // Yukardaki kodun edeeri: static string tatlMens = " " " "

-= Tatllar =-\n" . Baklava .\n" . Kadayf .\n" . Muhallebi .\n";

men gibi bir ilev arld halde ve onun sonucunda ilemler yapld halde, o dizginin oluturulmas iin alma zamannda hi zaman harcanmaz. Bu olanak, derleyicinin davran ile ilgili olduundan, ilevlerin gerekten derleme zamannda iletilip iletilmediklerini grmek o kadar kolay olmayabilir. Dizginin gerekten derleme zamannda oluturulduunu grmenin bir yolu, program bir hex editr'de amaktr. Derleme zamannda oluturulmusa, btn men dizgisi, derlenmi olan programn iinde gml olarak grlr. O dizginin tamamn grmenin baka bir yolu, Unix ortamlarnda bulunan strings ve grep programlarndan yararlanmaktr. strings , bir dosya iindeki btn okunakl dizgileri bulur ve standart kna gnderir. grep ise, giriine gelen satrlar arasndan belirli bir dzene uyanlarn seer ve standart kna gnderir. Programn isminin deneme olduunu varsayarsak, aadaki komut, iinde "Tatl" geen satr, ve sonrasndaki 4 satr gsterir:

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

490

Dier lev Olanaklar

$ strings deneme | grep -A 4 Tatl -= Tatl lar =. Baklava . . Kaday . Muhallebi .

Yukardaki ktdaki harflerinin benim ortammda sorun kartyor olmalar nemli deildir. Burada gstermek istediim, sonu dizginin programn iinde gml olarak bulunduudur. men ilevi, derleme zamannda iletilmitir. Her ilev derleme zamannda iletilemez. Bir ilevin derleme zamannda iletilebilmesi iin ncelikle bunun mmkn olduu bir ifadede kullanlm olmas gerekir: static bir deikenin ilklenmesi enum bir deikenin ilklenmesi; rnein, yukardaki programdaki static yerine, buradaki kullanmnda kesinlikle deimeyen deer anlamna gelen enum da yazlabilir enum string tatlMens = /* ... */ sabit uzunluklu bir dizinin uzunluunun hesaplanmas bir deer ablon parametresinin deerinin hesaplanmas Ek olarak, aadaki koullarn salanmalar da gerekir: 1. ilevin parametrelerinin uymas gereken koullar: tamsay hazr deerleri kesirli say hazr deerleri karakter hazr deerleri dizgi hazr deerleri bu listedeki elemanlardan oluan dizi hazr deerleri bu listedeki elemanlardan oluan eleme tablosu hazr deerleri bu listedeki elemanlardan oluan yap hazr deerleri bu listedeki elemanlarla ilklenmi olan sabit deikenler kapamalar ilev gstergeleri isimsiz kapamalar isimsiz ilevler

2. belirsiz sayda parametre alan ilevlerin C dilindeki gibi tanmlanm olmalar (Parametre Serbestlii dersini anlatrken bu olana bilerek atlamtm) 3. ilev, e zamanl alacak ekilde tanmlanm olmamaldr 4. ilevdeki ifadeler hata atamazlar gsterge veya snf kullanamazlar evrensel deikenlere eriemezler yerel static deikenlere eriemezler delete 'i aramazlar derleme zamannda iletilemeyen ilevler aramazlar

5. u deyimler kullanlamaz: e zamanl alma deyimleri

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

491

Dier lev Olanaklar

throw with scope try-catch-finally etiketli break veya continue

6. bir istisna olarak, u nitelikler derleme zamannda iletilebilirler: .dup .length .keys .values

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

492

Katmalar

75

Katmalar
Katmalar, derleme zamannda ablonlar veya dizgiler tarafndan retilen kodlarn programn istenen noktalarna eklenmelerine yararlar.

75.1

ablon katmalar
ablonlarn belirli kalplara gre kod reten olanaklar olduklarn ablonlar ve Ayrntl ablonlar derslerinde grmtk. ablonlardan yararlanarak farkl parametre deerleri iin ilev, yap, birlik, snf, arayz, veya yasal olduu srece her tr D kodunu oluturabiliyorduk. ablon katmalar, bir ablon iinde tanmlanm olan btn kodlarn programn belirli bir noktasna, sanki oraya aka elle yazlm gibi eklenmelerini salarlar. Bu adan, C ve C++ dillerindeki makrolar gibi iledikleri dnlebilir. mixin anahtar szc, ablonun belirli bir kullanmn programn herhangi bir noktasna yerletirir. "Katmak", "iine kartrmak" anlamna gelen "mix in"den tremitir. mixin anahtar szcnden sonra, ablonun belirli parametre deerleri iin bir kullanm yazlr: mixin bir_ablon!(ablon_parametreleri) O ablonun o parametrelerle kullanm iin retilen kodlar, olduklar gibi mixin satrnn bulunduu noktaya yerletirilirler. rnek olarak bir ke dizisini, ve o keler zerindeki ilemleri tanmlayan bir ablon dnelim: template KeDizisiOlana(T, int adet) { T[adet] keler; void keDeitir(int indeks, T ke) { keler[indeks] = ke; } void keleriGster() { writeln("Btn kelerim:"); foreach (i, ke; keler) { write(i, ":", ke, ' '); } } writeln();

O ablon, dizi elemanlarnn tr ve eleman adedi konusunda esneklik getirmektedir. Tr ve eleman adedi, kullanma gre artk serbeste seilebilir. O ablonun int tryle ve 2 deeriyle kullanlmasn istediimizi bir mixin 'e yle belirtebiliriz: mixin KeDizisiOlana!(int, 2); Yukardaki mixin , ablonun iindeki kodlar kullanarak iki elemanl int dizisini ve o diziyi kullanan iki ilevi oluturur. Bylece, onlar rnein bir yapnn yeleri haline getirebiliriz:

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

493

Katmalar

struct izgi { mixin KeDizisiOlana!(int, 2); } ablon iindeki kodlar, T 'ye karlk int ve adet 'e karlk 2 olacak ekilde retilirler ve mixin anahtar szcnn bulunduu yere yerletirilirler. Bylece izgi yapsnn 2 elemanl bir dizisi ve o dizi ile ileyen iki ilevi olmu olur: auto izgi = izgi(); izgi.keDeitir(0, 100); izgi.keDeitir(1, 200); izgi.keleriGster(); Program u kty retir: Btn kelerim: 0:100 1:200 Ayn ablonu rnein bir ilev iinde ve baka parametre deerleri ile de kullanabiliriz: void main() { mixin KeDizisiOlana!(Nokta, 5); keDeitir(3, Nokta(1.1, 2.2)); keleriGster();

O mixin , main 'in iine yerel bir dizi ve yerel iki ilev yerletirir. mixin 'e verilen Nokta 'nn da yle basit bir yap olduunu dnebiliriz: import std.string; struct Nokta { double x = 0; double y = 0; string toString() const { return format("(%s,%s)", x, y); }

kts: Btn kelerim: 0:(0,0) 1:(0,0) 2:(0,0) 3:(1.1,2.2) 4:(0,0)

75.2

Dizgi katmalar
D'nin ok gl bir olana, deerleri derleme srasnda bilinebilen dizgilerin kod olarak programn iine yerletirilebilmeleridir. inde yasal D kodlar bulunan her dizgi, mixin anahtar szc ile programa eklenebilir. Bu kullanmda dizginin parantez iinde belirtilmesi gerekir:

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

494

Katmalar

mixin(derleme_zamannda_oluturulan_dizgi) rnein "merhaba dnya" programn bir dizgi katmas ile yle yazabiliriz: import std.stdio; void main() { mixin(`writeln("merhaba dnya");`); } Dizgi iindeki kod mixin satrna eklenir, program derlenir, ve beklediimiz kty verir: merhaba dnya Bunun etkisini gstermek iin biraz daha ileri gidebilir ve btn program bile bir dizgi katmas olarak yazabiliriz: mixin( `import std.stdio; void main() { writeln("merhaba dnya"); }` ); Ayn kt elde edilir: merhaba dnya Bu rneklerdeki mixin 'lere gerek olmad aktr. O kodlarn imdiye kadar hep yaptmz gibi programa aka yazlmalar daha mantkldr. Dizgi katmalarnn gc, kodun derleme zamannda otomatik olarak oluturulabilmesinden gelir. Derleme zamannda oluturulabildii srece, mixin ifadesi ilevlerin dndrd dizgilerden bile yararlanabilir. Aadaki rnek, mixin 'e verilecek olan kod dizgilerini bir ileve oluturtmaktadr: import std.stdio; string yazdrmaDeyimi(string mesaj) { return `writeln("` ~ mesaj ~ `");`; } void main() { mixin(yazdrmaDeyimi("merhaba dnya")); mixin(yazdrmaDeyimi("selam dnya")); } Yukardaki program, yazdrmaDeyimi 'nin oluturduu iki dizgiyi mixin satrlarnn yerlerine yerletirir ve program o kodlarla derlenir. Burada dikkatinizi ekmek istediim nokta, writeln ilevlerinin yazdrmaDeyimi 'nin iinde arlmadklardr. yazdrmaDeyimi 'nin yapt, yalnzca iinde "writeln" geen dizgiler dndrmektir. O dizgiler, mixin 'lerin bulunduklar satrlara kod olarak yerletirilirler. Sonuta derlenen program, unun edeeridir: import std.stdio; void main() { writeln("merhaba dnya");

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

495

Katmalar

writeln("selam dnya");

mixin 'li program, sanki o iki writeln satr varm gibi derlenir ve alr: merhaba dnya selam dnya

75.3

le yklemedeki kullanm
le Ykleme dersinde ilelerin ablon sz dizimi ile tanmlandklarn grmtk. O sz dizimlerini bir kalp olarak kabul etmenizi rica etmi ve onlarn ablonlarla ilgili derslerden sonra akla kavuacaklarn sylemitim. le yklemeyle ilgili olan ye ilevlerin ablonlar olarak tanmlanmalarnn nedeni, ileleri belirleyen ablon parametrelerinin string trnde olmalar ve bu yzden dizgi katmalarndan yararlanabilmeleridir. Bunu grmek iin tekrar Sre yapsna bakalm. Bu trn nesnelerinin + ve - ileleriyle kullanlrken int kabul edebilmeleri iin aadaki ye ilevleri tanmlanm olsun: struct Sre { int dakika; // sre += 2 gibi kullanmlar iin void opOpAssign(string ile)(int miktar) if (ile == "+") { dakika += miktar; } // sre -= 2 gibi kullanmlar iin void opOpAssign(string ile)(int miktar) if (ile == "-") { dakika -= miktar; } // sre + 2 gibi kullanmlar iin Sre opBinary(string ile)(int miktar) if (ile == "+") { return Sre(dakika + miktar); } // sre - 2 gibi kullanmlar iin Sre opBinary(string ile)(int miktar) if (ile == "-") { return Sre(dakika - miktar); }

Grld gibi, opOpAssign 'n ve opBinary 'nin + ve - ileleri iin tanmlar "+" ve "-" karakterleri dnda ayndr. Buradaki kod tekrarn ortadan kaldrmak iin mixin 'lerden yararlanlabilir: struct Sre { int dakika;

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

496

Katmalar

void opOpAssign(string ile)(int miktar) { mixin("dakika " ~ ile ~ "= miktar;"); } Sre opBinary(string ile)(int miktar) { mixin("return Sre(dakika ") ~ ile ~ " miktar;"); }

stelik bu sefer ablon kstlamalar da kullanlmad iin baka aritmetik ilemler de desteklenmektedir: import std.stdio; import std.string; struct Sre { int dakika; void opOpAssign(string ile)(int miktar) { mixin("dakika " ~ ile ~ "= miktar;"); } Sre opBinary(string ile)(int miktar) { mixin("return Sre(dakika ") ~ ile ~ " miktar;"); } string toString() { return format("%s dakika", dakika); }

void main() { auto sre = Sre(10); sre += 1; sre -= 2; sre /= 3; sre %= 4; writeln("sre auto auto auto auto sonu1 sonu2 sonu3 sonu4 = = = = : ", sre); sre sre sre sre + / % 3; 4; 4; 4; sonu1); sonu2); sonu3); sonu4);

writeln("sonu1: writeln("sonu2: writeln("sonu3: writeln("sonu4:

", ", ", ",

kts: sre : sonu1: sonu2: sonu3: sonu4: 3 dakika 6 dakika -1 dakika 0 dakika 3 dakika

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

497

Katmalar

Gerektiinde yine de ablon kstlamalar eklenebilir ve uygun olmayan ilemler o sayede elenebilir.

75.4

rnek
Kendisine verilen saylardan belirli bir koula uyanlarn seen ve bir dizi olarak dndren bir ileve bakalm: int[] se(string koul)(in int[] saylar) { int[] sonu; foreach (eleman; saylar) { if (mixin(koul)) { sonu ~= eleman; } } } return sonu;

O ilev, seme koulunu bir ablon parametresi olarak almakta, ve if deyiminin parantezinin iine o koulu olduu gibi kod olarak yerletirmektedir. O ifadenin rnein elemanlarn 7'den kk olanlarn semesi iin if deyimi iine yle bir ifadenin yazlmas gerekir: if (eleman < 7) { te, yukardaki se ilevi, bize o koulu programda bir dizgi olarak bildirme olana verir: int[] saylar = [ 1, 8, 6, -2, 10 ]; int[] seilenler = se!"eleman < 7"(saylar); Burada nemli bir noktaya dikkat ekmek istiyorum. se ablonuna parametre olarak verilen dizginin iinde kullanlan deiken isminin, se ilevi iinde tanmlananla ayn olmas arttr. O deiken isminin ne olduu, se ilevinin belgelerinde belirtilmek zorundadr. O ilevi kullanan programclar da o isme uymak zorundadrlar. Bu amala kullanlan deiken isimleri konusunda Phobos'ta bir standart gelimeye balamtr. Benim setiim "eleman" gibi uzun bir isim deil; a, b, c diye tek harflik isimler kullanlr. Bunun bir rnei olarak std.algorithm modlndeki sort ilevine bakabiliriz. "Srala" anlamna gelen sort , kendisine verilen aral sralamaya yarar. sort ilevi, sralama koulunda kullanlan deikenlerin a ve b olmalarn art koar. Konumuzla ilgisi olmayan blmlerini gstermeden, sort 'un tanm yledir. void sort(alias less = "a < b", /* ... */)(/* ... */) less isimli ablon parametresinin varsaylan deeri, a ve b deikenlerini kullanr. Oradaki a ve b , sort 'un her sralama kararnda kulland soldaki ve sadaki deikenlerdir. O koul soldakinin sadakinden kk olmasna bakt iin; less 'in varsaylan sralamas kkten bye dorudur. sort ablonu, yle bir varsaylan deere sahip olduu iin, o parametre bildirilmeden kullanlabilir:

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

498

Katmalar

import std.stdio; import std.algorithm; void main() { int[] saylar = [ 5, 1, 4, 2, 3 ]; sort(saylar); writeln(saylar); } Not: Dizilerin .sort niteliinin de ayn amala kullanlabileceini hatrlayn. Beklendii gibi, dizi elemanlar sralanmlardr: 1 2 3 4 5 Elemanlarn ters srada sralanmalar iin less parametresini aka yazmak ve < yerine > belirtmek yeter: int[] saylar = [ 5, 1, 4, 2, 3 ]; sort!"a > b"(saylar); writeln(saylar); Bu sefer, her karlatrmada soldaki elemann sadaki elemandan byk olmas kstas kullanldndan dizi bykten ke doru sralanr: 5 4 3 2 1 Aslnda ayn amaca ulamak iin baka yollar da bulunduunu hatrlayn. rnein lev Gstergeleri ve Kapamalar dersinde grdmz gibi bir delegate parametre de kullanlabilir. Her zaman olduu gibi, hangi durumda hangi yntemin daha uygun olacana kendiniz karar vermelisiniz.

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

499

Aralklar

76

Aralklar
Aralklar, topluluk elemanlarna eriim ilemini soyutlarlar. Bu soyutlama, ok saydaki veri yapsnn ok saydaki algoritma ile uyumlu olarak kullanlmasn salar. Veri yaplarnn nasl gerekletirilmi olduklar nemsizleir, elemanlarna nasl eriildii n plana kar. Aralklar, trlerin belirli isimdeki ilevleri sunmalar ilkesi zerine kurulu olan aslnda ok basit bir kavramdr. Bu kavramla daha nce Yap ve Snflarda foreach dersinde de karlamtk: empty , front ve popFront() ye ilevlerini tanmlayan her tr, foreach dngs ile kullanlabiliyordu. O ilev, InputRange aralk eidinin gerektirdii ilevlerdir. Aralklarla ilgili kavramlar en basit aralk eidi olan InputRange ile gstereceim. Dier aralklarn farklar, baka ilevler de gerektirmeleridir. Daha ileri gitmeden nce aralklarla dorudan ilgili olan topluluk ve algoritma tanmlarn hatrlatmak istiyorum. Topluluk (veri yaps): Topluluk, neredeyse btn programlarda karlalan ok yararl bir kavramdr. Deikenler belirli amalarla bir araya getirilirler ve sonradan bir topluluun elemanlar olarak kullanlrlar. D'deki topluluklar; diziler, eleme tablolar, ve std.container modlnde tanmlanm olan topluluk trleridir. Her topluluk belirli bir veri yaps olarak gerekletirilir. rnein eleme tablolar bir hash table veri yaps gerekletirmesidir. Her veri yaps tr, elemanlar o veri yapsna zel biimde barndrr ve elemanlara o veri yapsna zel biimde eritirir. rnein dizi veri yapsnda elemanlar yan yana dururlar ve sra numaras ile eriilirler; bal liste yapsnda elemanlar dmlerde saklanrlar ve bu dmler araclyla eriilirler; ikili aa veri yapsnda dmler kendilerinden sralamada nceki ve sonraki elemanlara farkl dallar yoluyla eriim salarlar; vs. Ben bu derste topluluk ve veri yaps deyimlerini ayn anlamda kullanacam. Algoritma (ilev): Veri yaplarnn belirli amalarla ve belirli admlar halinde ilenmelerine algoritma denir. rnein sral arama algoritmas, aranan deeri topluluktaki btn elemanlar bandan sonuna kadar ilerleyerek arayan bir algoritmadr; ikili arama algoritmas, her admda elemanlarn yarsn eleyerek arayan bir algoritmadr; vs. Ben bu derste algoritma ve ilev deyimlerini ayn anlamda kullanacam. Aadaki ou rnekte eleman tr olarak int , topluluk tr olarak da int[] kullanacam. Aslnda aralklarn gc ablonlarla birlikte kullanldklarnda ortaya kar. Aralklarn birbirlerine uydurduu ou topluluk ve ou algoritma ablondur. Bunlarn rneklerini bir sonraki derse brakacam.

76.1

Tarihe
Algoritmalarla veri yaplarn birbirlerinden baaryla soyutlayan bir ktphane, C++ dilinin standart ktphanesinin de bir paras olan STL'dir (Standard Template Library). STL bu soyutlamay C++'n ablon olanandan yararlanarak gerekletirdii eriici (iterator) kavram ile salar. ok gl bir soyutlama olmasna ramen eriici kavramnn baz zayflklar da vardr. Aralklar, eriicilerin bu zayflklarn gidermeye ynelik olarak Andrei Alexandrescu tarafndan tasarlanmtr. Phobos, aralklar kullanan ilk ve bilinen tek ktphanedir. Andrei Alexandrescu, Eleman Eriimi zerine isimli makalesinde aralklar tantr ve aralklarn eriicilerden neden daha stn olduklarn gsterir.

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

500

Aralklar

76.2

Aralklar D'de kanlmazdr


Aralklar D'ye zg bir kavramdr. Diziler en ilevsel aralk eidi olan RandomAccessRange 'e uyarlar ve Phobos, aralklarla ilgili ok sayda olanak ierir. ou programda kendi aralk trlerimizi veya aralk ilevlerimizi yazmamz gerekmez. Buna ramen aralklarn Phobos'ta nasl kullanldn bilmek nemlidir. Phobos'taki ok sayda algoritma, kullanmlar srasnda farkedilmese bile aslnda geici aralk nesneleri dndrrler. rnein elemanlarn 10'dan byk olanlarn semek iin kullanlan aadaki filter() dizi deil, aralk nesnesi dndrr: import std.stdio; import std.algorithm; void main() { int[] saylar = [ 1, 20, 7, 11 ]; writeln(filter!"a > 10"(saylar)); } Not: Deikenlerin a , b gibi harflerle ifade edildikleri ablon parametreleriyle daha nce Katmalar dersinde kullandmz sort() ilevinde karlamtk. writeln , filter() 'n dndrm olduu aralk nesnesini gerektike tembel olarak kullanr. Sonuta, belirtilen kstasa uyan elemanlar yazdrlrlar: [20, 11] O sonuca bakarak filter() 'n int dizisi dndrd dnlebilir; ancak bu doru deildir. Dndrlen nesne bir dizi olmad iin rnein aadaki satr derlenemez: int[] seilenler = filter!"a > 10"(saylar); // derleme HATASI Dndrlen nesnenin trn hata mesajnda gryoruz: Error: cannot implicitly convert expression (filter(saylar)) of type Filter!(result,int[]) to int[] O geici aralk nesnesinin istendiinde bir diziye de dntrlebileceini aada gstereceim.

76.3

Geleneksel algoritmalar
Geleneksel algoritmalar, ilemekte olduklar veri yaplarnn nasl gerekletirildiklerini bilmek zorundadrlar. rnein bir bal listenin elemanlarn srayla ka yazdran aadaki ilev, kulland bal listenin dmlerinin eleman ve sonraki isminde iki yesi bulunduunu bilmek zorundadr: struct Dm { int eleman; Dm * sonraki; } void yazdr(const(Dm) * liste) { for ( ; liste; liste = liste.sonraki) { write(' ', liste.eleman);

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

501

Aralklar

Benzer ekilde, bir diziyi yazdran ilev de dizilerin length isminde niteliklerinin bulunduunu ve elemanlarna [] ileci ile eriildiini bilmek zorundadr: void yazdr(const int[] dizi) { for (int i = 0; i != dizi.length; ++i) { write(' ', dizi[i]); } } Not: Dizilerde ilerlerken foreach 'in daha uygun olduunu biliyoruz. Amacm algoritmalarn geleneksel olarak veri yaplarna dorudan bal olduklarn gstermek olduu iin, for 'un gerekten gerektii bir durum olduunu kabul edelim. Algoritmalarn veri yaplarna bu biimde bal olmalar, onlarn her veri yaps iin zel olarak yazlmalarn gerektirir. rnein; dizi, bal liste, eleme tablosu, ikili aa, yn, vs. gibi veri yaplarnn her birisi iin ara(), srala(), ortakOlanlarnBul(), deitir(), vs. gibi algoritmalarn ayr ayr yazlmalar gerekir. Bunun sonucunda da A adet algoritmann V adet veri yaps ile kullanlabilmesi iin gereken ilev says A arp V'dir. (Not: Her algoritma her veri yaps ile kullanlamad iin gerekte bu say daha dktr. rnein eleme tablolar sralanamazlar.) te yandan, aralklar veri yaplaryla algoritmalar birbirlerinden soyutladklar iin yalnzca A adet algoritma ve V adet veri yaps yazmak yeterli olur. Yeni yazlan bir veri yaps, onun sunduu aralk eidini destekleyen btn algoritmalarla kullanlmaya hazrdr; yeni yazlan bir algoritma da onun gerektirdii aralk eidine uyan btn veri yaplar ile ilemeye hazrdr.

76.4

Phobos aralklar
Bu dersin konusu olan aralklar, ba..son biiminde yazlan say aralklarndan farkldr. Say aralklarn foreach dngsndeki ve dilimlerdeki kullanmlarndan tanyoruz: int[] dilim = dizi[5..10]; foreach (say; 3..7) { // say aral, // Phobos aral DEL // say aral, // Phobos aral DEL

Ben bu derste aralk yazdm yerlerde Phobos aralklarn kastedeceim. Aralklar bir aralk sradzeni olutururlar. Bu sradzen en basit aralk olan InputRange ile balar. Dier aralklar, temel aldklar araln gerektirdii ilevlere ek olarak baka ilevler de gerektirirler. Aralk eitleri, en temelden en ilevsele doru ve gerektirdikleri ilevlerle birlikte unlardr: InputRange , giri aral: empty , front ve popFront() ilevleri ForwardRange , ilerleme aral: ek olarak save() ilevi BidirectionalRange , ift ulu aralk: ek olarak back ve popBack() ilevleri RandomAccessRange , rastgele eriimli aralk: ek olarak [] ileci (sonlu veya sonsuz olmasna gre baka koullar da gerektirir)

Bu sradzeni aadaki gibi gsterebiliriz. RandomAccessRange , sonlu ve sonsuz olarak iki eittir:

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

502

Aralklar

InputRange (giri aral) ForwardRange (ilerleme aral) BidirectionalRange RandomAccessRange (sonsuz) (ift ulu aralk) (rastgele eriimli aralk) RandomAccessRange (sonlu) (rastgele eriimli aralk) Yukardaki aralklar eleman eriimine ynelik aralklardr. Onlara ek olarak eleman k ile ilgili olan bir aralk daha vardr: OutputRange , k aral: put(aralk, eleman) ilemini desteklemek Bu be aralk, algoritmalarn veri yaplarndan soyutlanmalar iin yeterlidir.
76.4.1

Aral daraltarak ilerlemek


imdiye kadar ou rnekte kullandmz ilerleme ynteminde araln kendi durumunda deiiklik olmaz. rnein bir dizide foreach veya for ile ilerlendiinde dizinin kendisi deimez: int[] dizi = [ 10, 11, 12 ]; for (int i = 0; i != dizi.length; ++i) { write(' ', dizi[i]); } assert(dizi.length == 3); // uzunluu deimez

Burada, salt ilerleme ileminin dizide bir deiiklik oluturmadn belirtmek istiyorum. Farkl bir bak as getiren bir yntem, aral bandan daraltarak ilerlemektir. Bu yntemde araln hep ilk elemanna eriilir. lerleme, her seferinde bataki eleman kartlarak salanr: for ( ; dizi.length; dizi = dizi[1..$]) { write(' ', dizi[0]); // hep ilk elemana eriilir } Yukardaki dngnn ilerlemesi, dizi = dizi[1..$] ifadesinin bataki eleman diziden kartmas ile salanmaktadr. Dizi, o ifadenin etkisiyle aadaki aamalardan geerek daralr ve sonunda tkenir: [ 10, 11, 12 [ 11, 12 [ 12 [ ] ] ] ]

te Phobos aralklarndaki ilerleme kavram, aral bu ekilde bandan daraltma dncesi zerine kuruludur. (BidirectionalRange ve sonlu RandomAccessRange aralklar son taraftan da daralabilirler.) O rnei yalnzca bu tr ilerleme kavramn gstermek iin verdim; for dnglerinin o ekilde yazlmas normal kabul edilmemelidir.

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

503

Aralklar

Salt ilerlemi olmak iin elemanlarn diziden bu ekilde kartlmalar ou durumda istenmeyeceinden; asl topluluun kendisi deil, yalnzca ilerlemek iin oluturulan baka bir aralk tketilir. Bu rnekteki asl diziyi korumak iin rnein bir dilimden yararlanlabilir: int[] dizi = [ 10, 11, 12 ]; int[] dilim = dizi; for ( ; dilim.length; dilim = dilim[1..$]) { write(' ', dilim[0]); } assert(dilim.length == 0); assert(dizi.length == 3); // dilim boalr // dizi deimez

Phobos ilevleri de asl topluluun deimemesi iin zel aralk nesneleri dndrrler.

76.5

InputRange , giri aral


Bu eit aralk, yukardaki geleneksel yazdr() ilevlerinde de olduu gibi elemanlarn art arda eriildikleri aralk eidini ifade eder. Bu eriim hep ileri yndedir; tekrar baa dnlemez. Buna ramen, ok sayda algoritma yalnzca InputRange kullanarak yazlabilir; nk ou algoritma yalnzca ileri ynde ilerleme zerine kuruludur. Programlarn standart girilerinde olduu gibi, okunduka elemanlarn tketildikleri akmlar da bu tr aralk tanmna girerler. InputRange aralklarnn gerektirdii ilevi btnlk amacyla bir kere daha hatrlatyorum: empty : "bo mu" anlamna gelir ve araln sonuna gelinip gelinmediini bildirir; aralk bo kabul edildiinde true , deilse false dndrmelidir front : "ndeki" anlamna gelir ve araln bandaki elemana eriim salar popFront() : "ndekini kart" anlamna gelir ve araln bandaki eleman kartarak aral ba tarafndan daraltr Not: empty ve front ilevlerini nitelik olarak kullanlmaya uygun olduklar iin parantezsiz, popFront() ilevini ise yan etkisi olan bir ilev olduu iin parametre listesi ile yazmaya karar verdim. yazdr() ilevini bir kere de bu ilevden yararlanacak ekilde gerekletirelim: void yazdr(T)(T aralk) { for ( ; !aralk.empty; aralk.popFront()) { write(' ', aralk.front); } } writeln();

Araln elemanlarnn tr konusunda bir kstlama getirmi olmamak iin ilevi ayrca ablon olarak tanmladma dikkat edin. yazdr() bylece topluluun asl trnden de bamsz hale gelir ve InputRange 'in gerektirdii ilevi sunan her toplulukla kullanlabilir.
76.5.1

Bir InputRange rnei


Daha nce de karlam olduumuz Okul trn InputRange tanmna uygun olarak tekrar tasarlayalm. Okul 'u bir renci topluluu olarak dnelim ve onu elemanlarnn tr renci olan bir aralk olarak tanmlamaya alalm.

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

504

Aralklar

rnei ksa tutmu olmak iin baz nemli konularla ilgilenmeyeceim: yalnzca bu blm ilgilendiren yeleri yazacam btn trleri yap olarak tasarlayacam private , public , const gibi aslnda yararl olan belirteler kullanmayacam szlemeli programlama veya birim testi olanaklarndan yararlanmayacam

import std.string; struct renci { string isim; int numara; string toString() { return format("%s(%s)", isim, numara); }

struct Okul { renci[] renciler; } void main() { auto okul = Okul( [ renci("Ebru", 1), renci("Derya", 2) , renci("Damla", 3) ] ); } Okul trn bir InputRange olarak kullanabilmek iin, InputRange 'in gerektirdii ye ilevi tanmlamamz gerekiyor. empty ilevinin aralk bo olduunda true dndrmesini salamak iin dorudan renciler dizisinin uzunluunu kullanabiliriz. Dizinin uzunluu 0 olduunda aralk da bo kabul edilmelidir: struct Okul { // ... @property bool empty() { return renciler.length == 0; }

Programda kullanrken okul.empty biiminde parantezsiz olarak yazabilmek iin ilevi @property belirteci ile tanmladm. front ilevinin aralktaki ilk eleman dndrmesi, dizinin ilk eleman dndrlerek salanabilir: struct Okul { // ... @property ref renci front() { return renciler[0]; }

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

505

Aralklar

Not: Dizideki asl elemana eriim salam olmak iin ref dn tr kullandmza dikkat edin. yle yazmasaydk, renci bir yap tr olduu iin ilk elemann kopyas dndrlrd. popFront() ilevinin aral bandan daraltmas, renciler dizisini banda daraltarak salanabilir: struct Okul { // ... void popFront() { renciler = renciler[1 .. $]; }

Not: Yukarda da deindiim gibi, salt ilerlemi olmak iin aralktan renci kartlyor olmas ou duruma uygun deildir. Bu sorunu daha sonra zel bir aralk tr yardmyla gidereceiz. Bu ilev Okul trnn InputRange olarak kullanlmas iin yeterlidir. Okul nesnelerini artk baka hibir ey gerekmeden rnein yazdr() ablonuna gnderebiliriz: yazdr(okul); yazdr() , InputRange tanmna uyan Okul 'u aralk ilevleri araclyla kullanr. Sonuta araln elemanlar teker teker ka yazdrlrlar: Ebru(1) Derya(2) Damla(3) Bylece kendi yazdmz bir tr InputRange tanmna uydurmu ve InputRange 'lerle ileyen bir ileve gnderebilmi olduk. Okul , Phobos veya baka ktphanelerin InputRange alan algoritmalaryla da kullanlmaya hazrdr. Bunu biraz aada greceiz.
76.5.2

Dizileri aralk olarak kullanabilmek iin std.array modl


En sk kullanlan topluluk eidi olan diziler, en ilevsel aralk eidi olan RandomAccessRange olarak bile kullanlabilirler. Bunun iin std.array modlnn eklenmesi yeterlidir. std.array modl; empty , front , popFront() ve dier aralk ilevlerini diziler iin zel olarak tanmlar. Bylece diziler rnein yazdr() ilevine gnderilmeye hazrdrlar: import std.array; // ... yazdr([ 1, 2, 3, 4 ]); Not: Biraz aada greceimiz std.range modl eklendiinde std.array 'in ayrca eklenmesine gerek yoktur. Her dizinin aralk olarak kullanlabilmesinin aksine, her aralk dizi olarak kullanlamaz. Aralk elemanlarndan dizi oluturmak gerektiinde elemanlar teker teker aka kopyalanmaldr. Bunun iin std.array.array ilevi kullanlabilir. array() , InputRange araln bandan sonuna kadar ilerler, her eleman kopyalar, ve yeni bir dizi dndrr: import std.array;

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

506

Aralklar

// ... auto rencilerinKopyalar = array(okul); writeln(rencilerinKopyalar); kts: [Ebru(1), Derya(2), Damla(3)]

76.5.3

std.array modlnn dizgilere zel yarar


Tanm gerei olarak zaten karakter dizisi olan dizgiler de std.array modl sayesinde hemen hemen btn aralk eitleri olarak kullanlabilirler. Bunun istisnalar, char ve wchar dizgilerinin RandomAccessRange tanmna giremiyor olmalardr. Ancak, std.array modlnn dizgilere zel nemli bir yarar daha vardr: Dizgilerde ileri ynde ilerlendiinde elemanlara UTF kod birimi olarak deil, Unicode karakteri olarak eriilir. Bunun anlam, ne tr dizgi olursa olsun dizgi elemanlarnn harf harf ilerlenmesidir. Aadaki dizgilerde char 'a smadklarn bildiimiz ve harflerinden baka wchar 'a smayan gotik ahsa harfi (?) de bulunuyor. Bu ortamda desteklenmiyorsa bir soru iareti olarak grnyor olabilir: import std.array; // ... yazdr("abcdefg?"c); yazdr("abcdefg?"w); yazdr("abcdefg?"d); Buna ramen, programn kts ou durumda zaten istemi olacamz gibidir: a b c d e f g ? a b c d e f g ? a b c d e f g ? Bu ktnn Karakterler ve Dizgiler derslerinde grdmz davranlara uymadna dikkat edin. Hatrlarsanz, char ve wchar dizgilerinin elemanlar UTF kod birimleridir. (Not: aslnda dchar da UTF-32 kod birimidir ama UTF-32 kod birimleri Unicode karakterlerine bire bir karlk gelirler.) Bunu hatrlamak iin dizgileri elemanlarna tek tek erierek yazdralm: void elemanlarnyazdr(T)(T dizgi) { for (int i = 0; i != dizgi.length; ++i) { write(' ', dizgi[i]); } } writeln();

// ... elemanlarnYazdr("abcdefg?"c); elemanlarnYazdr("abcdefg?"w); elemanlarnYazdr("abcdefg?"d);

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

507

Aralklar

Dorudan dizgi elemanlarna eriildii iin harflere deil, UTF kod birimlerine eriilmi olur. Programn benim ortammdaki kts aadaki gibi oluyor: a b c d e f g a b c d e f g a b c d e f g ?

76.5.4

Kendi elemanlar bulunmayan aralklar


Yukarda aralk rnei olarak kullandmz dizilerde ve Okul nesnelerinde hep gerek elemanlar bulunuyordu. rnein Okul.front , var olan bir renci nesnesine referans dndryordu. Aralklarn bir stnl, bu konuda da esneklik getirmeleridir: front 'un dndrd elemann bir topluluun gerek bir eleman olmas gerekmez. O szde eleman, rnein popFront() her arldnda hesaplanarak oluturulabilir ve front her arldnda dndrlebilir. Gerek elemanlar bulunmayan bir aralk rneiyle aslnda biraz yukarda da karlatk: Dizgiler aralk olarak kullanldklarnda UTF kod birimlerine deil, Unicode karakterlerine eriildiini grdk. Oysa; char ve wchar Unicode karakteri ifade edemeyeceklerinden, aralk olarak kullandmzda elde edilen Unicode karakterleri o dizgilerin gerek elemanlar olamazlar. front 'un dndrd karakter, dizgideki UTF kod birimlerinin bir araya getirilmelerinden oluturulan bir dchar 'dr: import std.array; void main() { dchar harf = "u".front; // front'un dndrd dchar, // 'yi oluturan iki char'n // bileimidir } Dizginin eleman tr char olduu halde yukardaki front 'un dn tr dchar 'dr. O dchar , dizgi iindeki iki UTF kod biriminden olumutur ama kendisi dizginin eleman deildir. Buna benzer olarak, baz aralklarn ise hi elemanlar yoktur; byle aralklar yalnzca baka aralklarn elemanlarna eriim salamak iin kullanlrlar. Bu, yukarda Okul aralnda ilerlerken karlatmz eleman kaybedilmesi sorununu da ortadan kaldrr. Bunun iin rnein Okul trnn kendisi deil, tek amac okuldaki rencilere eriim salamak olan zel bir tr InputRange olarak tanmlanr. Daha nce Okul iinde tanmlam olduumuz btn aralk ilevlerini yeni renciAral trne tayalm: struct Okul { renci[] renciler; } struct renciAral { renci[] renciler; this(Okul okul) { this.renciler = okul.renciler; }

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

508

Aralklar

@property bool empty() { return renciler.length == 0; } @property ref renci front() { return renciler[0]; } void popFront() { renciler = renciler[1 .. $]; }

Yeni aralk, kendisine verilen Okul 'un rencilerini gsteren bir dilim oluturur ve popFront() iinde o dilimi tketir. Bunun sonucunda da asl dizi deimemi olur: auto okul = Okul( [ renci("Ebru", 1), renci("Derya", 2) , renci("Damla", 3) ] ); yazdr(renciAral(okul)); assert(okul.renciler.length == 3); // asl dizi deimez Not: Btn ilerini dorudan yesi olan dilime yaptrd iin renciAral 'nn iyi bir rnek olmadn dnebiliriz. nk nasl olsa Okul.renciler dizisinin bir dilimini kendimiz de dorudan kullanabilirdik. te yandan, renciler dizisi Okul 'un zel bir yesi de olabilirdi ve renciAral en azndan o zel yeye eriim salamak iin yararl olabilirdi.
76.5.5

Sonsuz aralklar
Kendi elemanlar bulunmayan aralklarn baka bir yarar, sonsuz uzunlukta aralklar oluturabilmektir. Bir araln hi sonlanmamas, empty ilevinin her zaman iin false deerinde olmas ile salanr. Her zaman iin false deerinde olan empty 'nin ilev olmas da gerekmeyeceinden static bir ye olarak tanmlanr. Bunun bir rneini grmek iin Fibonacci serisini reten bir aralk dnelim. Aadaki aralk, yalnzca iki adet int yesi bulunmasna ramen sonsuz uzunluktaki Fibonacci serisi olarak kullanlabilir: struct FibonacciSerisi { int bataki = 0; int sonraki = 1; static immutable bool empty = false; @property int front() const { return bataki; } void popFront() { int ikiSonraki = bataki + sonraki; bataki = sonraki; sonraki = ikiSonraki; // sonsuz aralk

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

509

Aralklar

Not: Her ne kadar sonsuz olsa da, say tr olarak int kulland iin int.max 'tan daha byk deerlere gelindiinde FibonacciSerisi yanl alr. FibonacciSerisi nesneleri iin empty 'nin deeri hep false olduundan, parametre olarak gnderildiinde yazdr() 'n iindeki for dngs hi sonlanmaz: yazdr(FibonacciSerisi()); // hi sonlanmaz

Sonsuz aralklar ancak sonuna kadar ilerlemenin gerekmedii durumlarda kullanlabilirler. FibonacciSerisi 'nin yalnzca belirli adet elemannn nasl kullanlabildiini aada greceiz.
76.5.6

Aralk dndren ilevler


Bir renciAral nesnesini yukarda aka renciAral(okul) yazarak oluturmu ve kullanmtk. Baz durumlarda ise renciAral gibi trleri aka yazmak yerine, o trn nesnelerini dndren ilevlerden yararlanlr. rnein btn ii bir renciAral nesnesi dndrmek olan aadaki ilev, kodlamay kolaylatrabilir: renciAral rencileri(ref Okul okul) { return renciAral(okul); } // ... yazdr(rencileri(okul)); Bylece kullanclar baz durumlarda ok karmak olabilen zel aralk trlerinin isimlerini ve ablon parametrelerini bilmek ve aka yazmak yerine, onlar dndren ilevlerin ksa isimlerini hatrlayabilirler. Bunun bir rneini ok basit olan std.range.take ilevinde grebiliriz. "Al" anlamna gelen take() , kendisine verilen bir araln bandaki belirli adet elemana teker teker eriim salar. Aslnda bu ilem take() ilevi tarafndan deil, onun dndrm olduu zel bir aralk tr tarafndan gerekletirilir. Yine de biz take() 'i kullanrken bunu bilmek zorunda deilizdir: import std.range; // ... auto okul = Okul( [ renci("Ebru", 1), renci("Derya", 2) , renci("Damla", 3) ] ); yazdr(take(rencileri(okul), 2)); Yukardaki kullanmda take() , okul nesnesinin bandaki 2 elemana eriim salayacak olan geici bir aralk nesnesi dndrr. yazdr() da take() 'in dndrm olduu bu geici aralk nesnesini kullanr: Ebru(1) Derya(2)

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

510

Aralklar

Yukardaki ilemin sonucunda okul nesnesinde hibir deiiklik olmaz; onun hl 3 eleman vardr: yazdr(take(rencileri(okul), 2)); assert(okul.renciler.length == 3); take() gibi ilevlerin kendi amalar iin dndrdkleri aralklarn trleri ou durumda bizi ilgilendirmez. Onlarn isimleriyle bazen hata mesajlarnda karlaabiliriz; veya daha nce de yararlanm olduumuz typeof ve stringof ile kendimiz de yazdrabiliriz: writeln(typeof(take(rencileri(okul), 2)).stringof); kts, take() 'in dndrd trn Take isminde bir ablon olduunu gsteriyor: Take!(renciAral)

76.5.7

std.range ve std.algorithm modlleri


Kendi trlerimizi aralk olarak tanmlamann ok byk bir yarar; onlar yalnzca kendi ilevlerimizle deil, Phobos ve baka ktphanelerin aralk algoritmalaryla da kullanabilmemizdir. std.range modlnde zellikle aralklarla ilgili olan ok sayda olanak bulunur. std.algorithm modl ise baka dillerin ktphanelerinde de bulunan ok sayda tannm algoritma ierir. Bir rnek olarak std.algorithm.swapFront algoritmasn Okul tr ile kullanalm. "ndekini dei toku et" anlamna gelen swapFront , kendisine verilen iki InputRange aralnn ilk elemanlarn dei toku eder. auto trkOkulu = Okul( [ renci("Ebru", 1), renci("Derya", 2) , renci("Damla", 3) ] ); auto amerikanOkulu = Okul( [ renci("Mary", 10), renci("Jane", 20) ] ); swapFront(rencileri(trkOkulu), rencileri(amerikanOkulu)); yazdr(rencileri(trkOkulu)); yazdr(rencileri(amerikanOkulu)); ki okuldaki ilk renciler deimitir: Mary(10) Derya(2) Damla(3) Ebru(1) Jane(20) Baka bir rnek olarak std.algorithm.filter algoritmasna bakalm. filter() , elemanlarn belirli bir kstasa uymayanlarn elemekle grevli olan zel bir aralk dndrr. Bu ilem srasnda asl aralkta hibir deiiklik olmaz. filter() 'a verilen kstas ok genel olarak uyanlar iin true , uymayanlar iin false reten bir ifadedir. filter() 'a ablon parametresi olarak verilen kstas bildirmenin bir ka yolu vardr. Bir yol, daha nce de karlatmz gibi, elemann a harfi ile ifade edildii bir dizgi kullanmaktr: filter!"a.numara % 2"(rencileri(okul))

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

511

Aralklar

Yukardaki ifadedeki kstas, rencileri(okul) aralndaki elemanlarn numaras tek olanlarn seer. take() ilevinde olduu gibi, filter() da zel bir aralk nesnesi dndrr. Bylece, dndrlen aralk nesnesini de dorudan baka ilevlere gnderebiliriz. rnein, seilmi olan elemanlar retecek olan aralk nesnesi yazdr() 'a gnderilebilir: yazdr(filter!"a.numara % 2"(rencileri(okul))); O kodu sadan sola doru okuyarak yle aklayabiliriz: rencileri(okul) aralndaki elemanlarn tek numaral olanlarn seen bir aralk olutur ve yazdr() ilevine gnder. kts yalnzca tek numaral rencilerden oluur: Ebru(1) Damla(3) Seilecek olan elemanlar iin true retmesi kouluyla, kstas filter() 'a bir ilev olarak da bildirilebilir: import std.array; // ... bool baHarfiD_mi(renci renci) { return renci.isim.front == 'D'; } yazdr(filter!baHarfiD_mi(rencileri(okul))); Yukardaki rnekteki kstas ilevi, ald renci nesnesinin ba harfi D olanlar iin true , dierleri iin false dndrmektedir. Not: O ifadede ba harf iin renci.isim[0] yazmadma dikkat edin. yle yazsaydm ba harfini deil, ilk UTF-8 kod birimini elde ederdim. Yukarda da belirttiim gibi; front , isim 'i bir aralk olarak kullanr ve her zaman iin ilk Unicode karakterini, yani ilk harfini dndrr. O kodun sonucunda da ba harfi D olan renciler seilir ve yazdrlr: Derya(2) Damla(3)

76.5.8

Tembellik
Aralk dndren ilevlerin baka bir yarar, o aralklarn tembel olarak kullanlabilmeleridir. Bu hem program hz ve bellek kullanm asndan ok yararldr, hem de sonsuz aralklarn var olabilmeleri zaten btnyle tembellik olana sayesindedir. Tembel aralklar ilerini gerektike ve para para gerekletirirler. Bunun bir rneini FibonacciSerisi aralnda gryoruz: Elemanlar ancak gerektike popFront() ilevinde teker teker hesaplanrlar. FibonacciSerisi eer tembel yerine hevesli bir aralk olsayd, yani kullanlmadan nce btn aral retmeye alsayd, sonsuza kadar ilemeye devam ederdi. rettii elemanlar saklamas da gerekecei iin sonsuz saydaki elemana da yer bulamazd. Hevesli aralklarn baka bir sakncas, sonlu sayda bile olsalar belki de hi kullanlmayacak olan elemanlar iin bile gereksizce yer harcayacak olmalardr.

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

512

Aralklar

Phobos'taki ou aralk gibi take() ve filter() da tembellikten yararlanrlar. rnein FibonacciSerisi 'ni take() 'e vererek bu sonsuz araln belirli saydaki elemann kullanabiliriz: yazdr(take(FibonacciSerisi(), 10)); kts yalnzca ilk 10 sayy ierir: 0 1 1 2 3 5 8 13 21 34

76.6

ForwardRange , ilerleme aral


InputRange , elemanlar kartldka tkenen aralk kavramn ifade ediyordu. Baz aralklar ise InputRange gibi ileyebilmelerinin yannda, araln belirli bir durumunu hatrlama yeteneine de sahiptirler. FibonacciSerisi bunu salayabilir, nk FibonacciSerisi nesneleri serbeste kopyalanabilirler ve bu kopyalar birbirlerinden bamsz aralklar olarak yaamlarna devam edebilirler. ForwardRange aralklar, araln belirli bir andaki kopyasn dndren save() ilevini de sunan aralklardr. save() 'in dndrd kopyann asl aralktan bamsz olarak kullanlabilmesi arttr. rnein bir kopya zerinde ilerlemek dier kopyay ilerletmemelidir. std.array modlnn eklenmi olmas dizileri de otomatik olarak ForwardRange tanmna sokar. save() ilevini FibonacciSerisi iin gerekletirmek istediimizde nesnenin bir kopyasn dndrmek yeterlidir: struct FibonacciSerisi { // ... FibonacciSerisi save() const { return this; }

Dndrlen kopya, bu nesnenin kopyaland yerden devam edecek olan bamsz bir aralktr. save() 'in dndrd nesnenin asl aralktan bamsz olduunu aadaki gibi bir program yardmyla grebiliriz. Programda yararlandm std.range.popFrontN() , kendisine verilen araln bandan belirtilen sayda eleman kartr. bilgiVer() ilevi de k ksa tutmak iin yalnzca ilk be eleman gsteriyor: import std.range; // ... void bilgiVer(T)(const dchar[] balk, const ref T aralk) { writefln("%40s: %s", balk, take(aralk, 5)); } void main() { auto aralk = FibonacciSerisi(); bilgiVer("Balangtaki aralk", aralk);

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

513

Aralklar

popFrontN(aralk, 2); bilgiVer("ki eleman kartldktan sonra", aralk); auto kopyas = aralk.save(); bilgiVer("Kopyas", kopyas); popFrontN(aralk, 3); bilgiVer(" eleman daha kartldktan sonra", aralk); bilgiVer("Kopyas", kopyas);

O kodun kts, aralktan 'tan eleman kartlm olmasnn kopyas 'n etkilemediini gsterir.: Balangtaki aralk: ki eleman kartldktan sonra: Kopyas: eleman daha kartldktan sonra: Kopyas: [0, [1, [1, [5, [1, 1, 2, 2, 8, 2, 1, 2, 3] 3, 5, 8] 3, 5, 8] 13, 21, 34] 3, 5, 8]

bilgiVer() iinde aralklar dorudan writefln 'e gnderdiime ayrca dikkat edin. Kendi yazdmz yazdr() ilevinde olduu gibi, stdio modlnn k ilevleri de InputRange aralklarn kullanabilirler. Bundan sonraki rneklerde yazdr() yerine stdio 'nun k ilevlerini kullanacam. ForwardRange aralklaryla ileyen bir algoritma rnei olarak std.range.cycle 'a bakabiliriz. cycle() , kendisine verilen aral srekli olarak tekrarlar. Bandan tekrarlayabilmesi iin araln ilk durumunu saklamas gerekeceinden, bu araln bir ForwardRange olmas arttr. Artk bir ForwardRange de kabul edilen FibonacciSerisi nesnelerini cycle() ilevine gnderebiliriz: writeln(take(cycle(take(FibonacciSerisi(), 5)), 20)); Hem cycle() 'a verilen araln hem de cycle() 'n dndrd araln sonlu olmalar iin iki noktada take() 'ten yararlanldna dikkat edin. kts, FibonacciSerisi aralnn ilk be elemannn tekrarlanmasndan oluan araln ilk yirmi elemandr: [0, 1, 1, 2, 3, 0, 1, 1, 2, 3, 0, 1, 1, 2, 3, 0, 1, 1, 2, 3] Kodun anlalmasn kolaylatrmak iin ara deikenler de tanmlanabilir. Yukardaki tek satrlk kodun bir edeeri udur: auto auto auto auto seri baTaraf tekrarlanm tekrarlanmnnBaTaraf = = = = FibonacciSerisi(); take(seri, 5); cycle(baTaraf); take(tekrarlanm, 20);

writeln(tekrarlanmnnBaTaraf); Tembelliin yararn burada bir kere daha hatrlatmak istiyorum: lk drt satrda yalnzca asl ilemleri gerekletirecek olan geici aralk nesneleri oluturulur. Btn ifadenin retmi olduu saylar, FibonacciSerisi.popFront() ilevi iinde ve ancak gerektike hesaplanrlar.

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

514

Aralklar

76.7

BidirectionalRange , ift ulu aralk


BidirectionalRange aralklar, ForwardRange ilevlerine ek olarak iki ilev daha sunarlar. back , front 'un benzeri olarak araln sonundaki eleman dndrr. popBack() de popFront() 'un benzeri olarak aral sonundan daraltr. std.array modl eklendiinde diziler BidirectionalRange tanmna da girerler. rnek olarak BidirectionalRange aral gerektiren std.range.retro ilevini gstermek istiyorum. retro() , kendisine verilen araln front 'unu back 'ine, popFront() 'unu da popBack() 'ine balayarak aralktaki elemanlara ters srada eriilmesini salar: writeln(retro([ 1, 2, 3 ])); kts: [3, 2, 1] retro() 'nun dndrd zel araln bir benzerini ok basit olarak aadaki gibi tanmlayabiliriz. Yalnzca int dizileriyle iledii iin ok kstl olsa da aralklarn gcn gstermeye yetiyor: import std.array; import std.stdio; struct TersSrada { int[] aralk; this(int[] aralk) { this.aralk = aralk; } @property bool empty() const { return aralk.empty; } @property int front() const { return aralk.back; // ters (dier ilevler de yle) } @property int back() const { return aralk.front; } void popFront() { aralk.popBack(); } void popBack() { aralk.popFront(); }

void main() { writeln(TersSrada([ 1, 2, 3])); }

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

515

Aralklar

Aral ters srada kulland iin retro() ile ayn sonu elde edilir: [3, 2, 1]

76.8

RandomAccessRange , rastgele eriimli aralk


RandomAccessRange , belirli sradaki elemanlarna [] ileci ile eriilebilen aralklar ifade eder. le Ykleme dersinden hatrlayacanz gibi, [] ileci opIndex() ye ilevi ile tanmlanr. std.array modl genel olarak dizileri de RandomAccessRange tanmna sokar. Ancak; UTF-8 ve UFT-16 kodlamalar harflere sra numarasyla eriimi desteklemedikleri iin, char ve wchar dizgileri harf eriimi asndan RandomAccessRange aral olarak kullanlamazlar. te yandan, UTF-32 kodlamasnda kodlarla harfler bire bir karlk geldiklerinden, dchar dizgileri harf eriiminde RandomAccessRange olarak kullanlabilirler. Her trn opIndex() ilevini kendisine en uygun biimde tanmlayaca doaldr. Ancak, bilgisayar biliminin algoritma karmaklklar ile ilgili olarak bu konuda bir beklentisi vardr: Rastgele eriim, sabit zamanda gereklemelidir. Sabit zamanda eriim, eriim iin gereken ilemlerin aralktaki eleman adedinden bamsz olmas anlamna gelir. Aralkta ne kadar eleman olursa olsun, hibirisinin eriimi araln uzunluuna bal olmamaldr. RandomAccessRange tanmna girebilmek iin ek olarak aadaki koullardan birisinin daha salanm olmas gerekir: sonsuz bir ForwardRange olmak veya length niteliini de sunan bir BidirectionalRange olmak

76.8.1

Sonsuz RandomAccessRange
nce sonsuz ForwardRange tanm zerine kurulu olan bir RandomAccessRange rneine bakalm. Bu tanma girebilmek iin gereken ilevler unlardr: InputRange 'in gerektirdii empty , front ve popFront() ForwardRange 'in gerektirdii save() RandomAccessRange 'in gerektirdii opIndex() sonsuz olabilmek iin empty 'nin deerinin derleme zamannda false olarak belirlenmi olmas

FibonacciSerisi 'nin en son tanm onu bir ForwardRange yapmaya yetiyordu. Ancak, opIndex() ilevi FibonacciSerisi iin sabit zamanda ileyecek ekilde gerekletirilemez; nk belirli bir elemana eriebilmek iin o elemandan nceki elemanlarn da hesaplanmalar gerekir. Bunun anlam; N'inci sradaki elemann hesaplanmas iin ondan nceki N-1 elemann hesaplanmas gerektii, bu yzden de ilem adedinin N'ye bal olduudur. opIndex() ilevinin sabit zamanda iletilebildii bir rnek olarak tamsaylarn karelerinden oluan sonsuz bir aralk tanmlayalm. Byle bir aralk sonsuz olduu halde btn elemanlarnn deerlerine sabit zamanda eriilebilir: class KareAral { int bataki; this(int bataki = 0)

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

516

Aralklar

{ }

this.bataki = bataki;

static immutable bool empty = false; @property int front() const { return opIndex(0); } void popFront() { ++bataki; } KareAral save() const { return new KareAral(bataki); } int opIndex(size_t sraNumaras) const { /* Bu ilev sabit zamanda iler */ const elemanDeeri = bataki + sraNumaras; return elemanDeeri * elemanDeeri; }

Hibir eleman iin yer ayrlmad halde bu araln btn elemanlarna [] ileci ile eriilebilir: auto kareler = new KareAral(); writeln(kareler[5]); writeln(kareler[10]); kts 5 ve 10 sra numaral elemanlar ierir: 25 100 Sfrnc eleman her zaman iin araln ilk elemann temsil etmelidir. Bunu denemek iin yine popFrontN() 'den yararlanabiliriz: popFrontN(kareler, 5); writeln(kareler[0]); Araln ilk 5 eleman srasyla 0, 1, 2, 3 ve 4'n kareleri olan 0, 1, 4, 9 ve 16'dr. Onlar kartldktan sonraki ilk eleman artk bir sonraki saynn karesi olan 25'tir: 25 KareAral en ilevsel aralk olan RandomAccessRange olarak tanmland iin dier aralk eitleri olarak da kullanlabilir. rnein InputRange olarak: bool sonkiHaneAyn_m(int say) { /* Doru olabilmesi iin en az iki rakam bulunmal */ if (say < 10) { return false; }

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

517

Aralklar

/* Son iki hanesi 11'e tam olarak blnmeli */ const sonkiHane = say % 100; return (sonkiHane % 11) == 0;

writeln(filter!sonkiHaneAyn_m(take(kareler, 50))); kts, ilk 50 elemann son iki hanesi ayn olanlarn ierir: [100, 144, 400, 900, 1444, 1600]

76.8.2

Sonlu RandomAccessRange
imdi de sonlu uzunluklu BidirectionalRange tanm zerine kurulu olan bir RandomAccessRange rneine bakalm. Bu eit bir aralk olarak kabul edilmek iin gereken ilevler unlardr: InputRange 'in gerektirdii empty , front ve popFront() ForwardRange 'in gerektirdii save() BidirectionalRange 'in gerektirdii back ve popBack() RandomAccessRange 'in gerektirdii opIndex() araln uzunluunu bildiren length

Bu rnekte, kendisine verilen btn aralklardaki btn elemanlar sanki tek bir araln elemanlarym gibi sunan std.range.chain 'in bir benzerini tasarlayalm. chain() her tr elemanla ve farkl aralklarla ileyebilir. Bu rnei ksa tutabilmek iin biz yalnzca int dizileriyle ileyecek ekilde tanmlayacaz. nce adna BirArada diyeceimiz bu trn nasl kullanlacan gstermek istiyorum: auto aralk = BirArada([ 1, 2, 3 ], [ 101, 102, 103]); writeln(aralk[4]); ki farkl diziyle ilklenen aralk , [ 1, 2, 3, 101, 102, 103 ] elemanlarndan oluan tek bir diziymi gibi kullanlacak. rnein dizilerin ikisinde de 4 numaral eleman bulunmad halde diziler art arda dnldklerinde 102, 4 numaral eleman olarak kabul edilecek: 102 Btn aralk nesnesi yazdrldnda da elemanlar tek bir dizi gibi grnecekler: writeln(aralk); kts: [1, 2, 3, 101, 102, 103] BirArada trnn bir yarar, bu ilemler gerekletirilirken hibir elemann kopyalanmayacak olmasdr. Btn elemanlar kendi dizilerinde durmaya devam edecekler. Belirsiz sayda dizi ile ilklenecek olan bu aralk, Parametre Serbestlii dersinde grdmz belirsiz sayda parametre olanandan yararlanabilir: struct BirArada {

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

518

Aralklar

int[][] aralklar; this(int[][] aralklar ...) { this.aralklar = aralklar.dup; baTemizle(); sonuTemizle();

} // ... }

Kurucu iinde arldn grdmz baTemizle() ve sonuTemizle() ilevleri, dizilerin bataki ve sondaki bo olanlarn kartmak iin kullanlyorlar. Arala zaten bir katklar bulunmayan bo dizilerin ilemleri karmaklatrmalar bylece nlenmi olacak: struct BirArada { // ... private void baTemizle() { while (!aralklar.empty && aralklar.front.empty) { aralklar.popFront(); } } private void sonuTemizle() { while (!aralklar.empty && aralklar.back.empty) { aralklar.popBack(); } }

O ilevleri daha sonra popFront() ve popBack() iinden de aracaz. baTemizle() ve sonuTemizle() ilevlerinin bata ve sonda bo dizi brakmayacaklarn bildiimizden, tek bir alt araln bile kalm olmas btn araln henz tkenmedii anlamna gelir: struct BirArada { // ... @property bool empty() { return aralklar.empty; }

lk alt araln ilk eleman bu araln da ilk elemandr: struct BirArada { // ... @property int front() { return aralklar.front.front; }

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

519

Aralklar

lk araln ilk elemann kartmak, bu araln ilk elemann kartm olur. Bu ilem sonucunda ilk aralk boalm olabileceinden, gerektiinde o araln ve onu izleyen olas bo aralklarn da kartlmalar iin baTemizle() ilevinin arlmas gerekir: struct BirArada { // ... void popFront() { aralklar.front.popFront(); baTemizle(); }

Araln belirli bir durumunun kopyas, elimizde bulunan alt aralklarla ilklenen yeni bir BirArada nesnesi dndrerek salanabilir: struct BirArada { // ... BirArada save() { return BirArada(aralklar); }

Araln son tarafndaki ilemler ba tarafndakilerin benzerleridir: struct BirArada { // ... @property int back() { return aralklar.back.back; } void popBack() { aralklar.back.popBack(); sonuTemizle(); }

Btn araln uzunluu, alt aralklarn uzunluklarnn toplam olarak hesaplanabilir: struct BirArada { // ... @property size_t length() { size_t uzunluk = 0; foreach (aralk; aralklar) { uzunluk += aralk.length; } } return uzunluk;

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

520

Aralklar

Ayn ilem std.algorithm.reduce ilevi ile daha ksa olarak da gerekletirilebilir. reduce() , ablon parametresi olarak ald ilemi kendisine verilen aralktaki btn elemanlara uygular. import std.algorithm; // ... @property size_t length() { return reduce!"a + b.length"(0, aralklar); } ablon parametresindeki a imdiye kadarki toplam, b de aralktaki her bir eleman temsil eder. lk ilev parametresi toplamn ilk deerini (burada 0), ikinci ilev parametresi de hesabn hangi aralktaki elemanlara uygulanacan belirler. Not: length her arldnda uzunluun byle batan hesaplanmas yerine uzunluk isminde bir yeden de yararlanlabilir. Bu yenin deeri kurucu ilev iinde bir kere batan hesaplanabilir, ve ondan sonra popFront() ve popBack() ilevleri her arldklarnda teker teker azaltlabilir. Belirli bir sra numarasndaki elemann dndrlebilmesi iin btn alt aralklara batan sona doru baklmas ve sra numarasnn hangi aralktaki bir elemana denk geldiinin bulunmas gerekir: struct BirArada { // ... int opIndex(size_t sraNumaras) { /* Hata mesaj iin saklyoruz */ const size_t batakiSraNumaras = sraNumaras; foreach (aralk; aralklar) { if (aralk.length > sraNumaras) { return aralk[sraNumaras]; } else { sraNumaras -= aralk.length; }

throw new Exception( format("Geersiz sra numaras: %s (uzunluk: %s)", batakiSraNumaras, this.length));

Not: opIndex , yukardaki uyarnn aksine sabit zamanda gerekleemez. Bu araln kabul edilir derecede hzl ileyebilmesi iin aralklar yesinin fazla uzun olmamas gerekir. Tanmladmz bu aralk, istediimiz sayda int dizisiyle kullanlmaya hazrdr. Kendisine vereceimiz dizileri take() ve array() ilevleri yardmyla bu derste tanmladmz trlerden bile edinebiliriz: auto aralk = BirArada(array(take(FibonacciSerisi(), 10)), [ 777, 888 ], array(take(new KareAral(), 5))); writeln(aralk.save);

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

521

Aralklar

kts, dizinin tek diziymi gibi kullanlabildiini gsterir: [0, 1, 1, 2, 3, 5, 8, 13, 21, 34, 777, 888, 0, 1, 4, 9, 16] Bu aral baka eit aralk kullanan algoritmalara da gnderebiliriz. rnein BidirectionalRange gerektiren retro() 'ya: writeln(retro(aralk.save));

[16, 9, 4, 1, 0, 888, 777, 34, 21, 13, 8, 5, 3, 2, 1, 1, 0] BirArada 'y bu derste rendiklerimizin bir uygulamas olarak tasarladk. Programlarnzda daha kapsaml olan std.range.chain 'i kullanmanz neririm.

76.9

OutputRange , k aral
imdiye kadar grdmz aralklar hep elemanlara eriimle ilgili olan aralklardr. OutputRange ise k araldr. stdout 'ta olduu gibi elemanlarn belirli bir hedefe yazldklar akmlar temsil ederler. OutputRange aralklarnn gerektirdii ilemi yukarda ksaca put(aralk, eleman) olarak belirtmitim. put() , std.range modlnde tanmlanm olan bir ilevdir; k aralnn hangi olanaklara sahip olduunu static if yardmyla derleme zamannda belirler ve eleman arala gnderirken elemana ve arala en uygun olan yntemi kullanr. put() 'un srayla denedii durumlar ve setii yntemler aadaki tablodaki gibidir. Tablodaki durumlara yukardan aaya doru baklr ve uygun olan ilk durum seilir. Tabloda A , araln trn; aralk , bir aralk nesnesini; E , eleman trn; ve e de bir eleman nesnesini temsil ediyor: Olas Durum A trnn parametre olarak E alan put isminde bir ye ilevi varsa A trnn parametre olarak E dizisi alan put isminde bir ye ilevi varsa A bir InputRange aralysa ve e , a.front 'a atanabiliyorsa Seilen Yntem aralk.put(e);

aralk.put([ e ]);

aralk.front = e; aralk.popFront();

E bir InputRange aralysa ve A aralna kopyalanabiliyorsa

for (; !e.empty; e.popFront()) put(aralk, e.front);

A , parametre olarak E alabiliyorsa (A rnein bir delegate olabilir) A , parametre olarak E dizisi alabiliyorsa (A rnein bir delegate olabilir)

aralk(e);

aralk([ e ]);

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

522

Aralklar

Ben bu kullanmlardan birincisinin bir rneini gstereceim: Tanmlayacamz aralk trnn put() isminde bir ilevi olacak ve bu ilev k aralnn eleman trn parametre olarak alacak. Tanmlayacamz k aral, kurulurken belirsiz sayda dosya ismi alsn. Daha sonradan put() ilevi ile yazdrlan elemanlar hem bu dosyalarn hepsine, hem de stdout 'a yazdrsn. Ek olarak, her elemandan sonra yine kurucusunda ald ayrac yazdrsn. struct okHedefeYazan { string ayra; File[] dosyalar; this(string ayra, string[] dosyasimleri ...) { this.ayra = ayra; /* stdout her zaman dahil */ this.dosyalar ~= stdout; /* Belirtilen her dosya ismi iin yeni bir dosya foreach (dosyasmi; dosyasimleri) { this.dosyalar ~= File(dosyasmi, "w"); } } void put(T)(T eleman) { foreach (dosya; dosyalar) { dosya.write(eleman, ayra); } } */

Her trden k aral yerine geebilmesi iin put() ilevini de ablon olarak tanmladm. Bu sayede aada hem int hem de string aral olarak kullanabiliyoruz. Phobos'ta OutputRange kullanan bir algoritma std.algorithm.copy 'dir. copy() , bir InputRange aralnn elemanlarn bir OutputRange aralna kopyalayan ok basit bir ilevdir. import std.algorithm; // ... auto k = okHedefeYazan("\n", "deneme_0", "deneme_1"); copy([ 1, 2, 3], k); copy([ "krmz", "mavi", "yeil" ], k); Yukardaki kod, giri aralklarndaki elemanlar hem stdout 'a, hem de "deneme_0" ve "deneme_1" isimli dosyalara yazar: 1 2 3 krmz mavi yeil

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

523

Aralklar

76.9.1

Dizilerin OutputRange olarak kullanlmalar


std.range , dizileri OutputRange tanmna da sokar. (std.array ise yalnzca giri aralklar tanmna sokar). Ancak, dizilerin OutputRange olarak kullanlmalarnn beklenmedik bir etkisi vardr: OutputRange olarak kullanlan dizi, her put() ilemine karlk bir eleman kaybeder. stelik kaybedilen eleman, yeni atanm olan bataki elemandr. Bunun nedeni, put() ye ilevleri bulunmayan dizilerin yukardaki tablodaki u ynteme uymalardr: aralk.front = e; aralk.popFront(); Her bir put() iin yukardaki kod iletildiinde hem bataki elemana yeni deer atanr, hem de popFront() 'un etkisiyle bataki eleman diziden kartlr: import std.stdio; import std.range; void main() { int[] dizi = [ 1, 2, 3 ]; dizi.put(100); writeln(dizi); } Bir OutputRange olarak kullanld halde dizi eleman kaybetmitir: [2, 3] Bu yzden dizinin kendisi deil, bir dilimi OutputRange olarak kullanlmaldr: import std.stdio; import std.range; void main() { int[] dizi = [ 1, 2, 3 ]; int[] dilim = dizi; dilim.put(100); writeln(dilim); writeln(dizi);

Bu sefer dilim tkendii halde dizi istediimiz elemanlara sahiptir: [2, 3] [100, 2, 3]

istenen sonu

Burada nemli bir noktaya dikkat etmek gerekir: OutputRange olarak kullanlan dizinin uzunluu otomatik olarak artmaz. Dizide yeterli yer olmas programcnn sorumluluundadr: int[] dizi = [ 1, 2, 3 ]; int[] dilim = dizi; foreach (i; 0 .. 4) { dilim.put(i * 100); } // dizide 4 elemana yer yok

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

524

Aralklar

popFront() nedeniyle boalan dilimde yer kalmad iin program bo dizinin ilk eleman bulunmadn bildiren bir hatayla sonlanr: core.exception.AssertError@...: Attempting to fetch the front of an empty array std.array.appender , dizileri sonuna eklenen bir OutputRange olarak kullanmaya yarar. appender 'n dndrd zel aralk nesnesinin kendi put() ilevi, verilen eleman dizinin sonuna ekler: import std.array; // ... auto sonunaEkleyen = appender([ 1, 2, 3 ]); foreach (i; 0 .. 4) { sonunaEkleyen.put(i * 100); } Yukardaki koddaki appender bir dizi ile ilkleniyor, ve ondan sonra put() ilevi arlarak bir OutputRange olarak kullanlyor. appender 'n bir k olarak kullanldnda edindii elemanlara .data nitelii ile eriilir: writeln(sonunaEkleyen.data); kts: [1, 2, 3, 0, 100, 200, 300]

76.10

Aralk ablonlar
Bu derste kendi yazdmz ou rnekte int aralklar kullandk. Oysa aralklarn ve aralk kullanan algoritmalarn ablon olarak tasarlanmalar kullanllklarn byk lde arttrr. std.range modl aralklarla ilgili olan ok sayda yardmc ablon da tanmlar. Bunlarn nasl kullanldklarn bir sonraki derste gstereceim.

76.11

zet
aralklar veri yaplaryla algoritmalar birbirlerinden soyutlayan ve birbirleriyle uyumlu olarak kullanlmalarn salayan olanaktr aralklar D'ye zg bir kavramdr ve Phobos'ta ok kullanlr Phobos'taki ou algoritma kendisi ilem yapmak yerine zel bir aralk nesnesi dndrr ve tembellikten yararlanr dizgiler InputRange olarak kullanldklarnda elemanlarna harf harf eriilir InputRange 'in gerektirdii ilevler empty , front ve popFront() 'tur ForwardRange 'in gerektirdii ek ilev save() 'dir BidirectionalRange 'in gerektirdii ek ilevler back ve popBack() 'tir sonsuz RandomAccessRange 'in ForwardRange 'e ek olarak gerektirdii ilev opIndex() 'tir sonlu RandomAccessRange 'in BidirectionalRange 'e ek olarak gerektirdii ilevler opIndex() ve length 'tir std.array.appender , dizilerin sonuna ekleyen bir OutputRange olarak kullanlr

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

525

Baka Aralk Olanaklar

77

Baka Aralk Olanaklar


Bundan nceki blmdeki ou aralk rneinde int aralklar kullandk. Aslnda topluluklar, algoritmalar, ve aralklar, hep ablonlar olarak gerekletirilirler. Biz de bir nceki derste yazdr() ilevini ablon olarak tanmladmz iin farkl InputRange aralklaryla kullanabilmitik: void yazdr(T)(T aralk) { // ... } yazdr() 'n bir eksii, ablon parametresinin bir InputRange olmas gerektii halde bunu bir ablon kstlamas ile belirtmiyor olmasdr. (ablon kstlamalarn Ayrntl ablonlar dersinde grmtk.) std.range modl, hem ablon kstlamalarnda hem de static if deyimlerinde yararlanlmak zere ok sayda yardmc ablon ierir.

77.1

Aralk eidi ablonlar


Bu ablonlarn "yle midir" anlamna gelen is ile balayanlar, belirli bir trn o aralk eidinden olup olmadn belirtir. rnein isInputRange!T , "T bir InputRange midir" sorusunu yantlar. Aralk eidini sorgulayan ablonlar unlardr: isInputRange isForwardRange isBidirectionalRange isRandomAccessRange isOutputRange

yazdr() ilevinin ablon kstlamas isInputRange 'den yararlanarak yle yazlr: void yazdr(T)(T aralk) if (isInputRange!T) { // ... } Bunlar arasndan isOutputRange , dierlerinden farkl olarak iki ablon parametresi alr: Birincisi desteklenmesi gereken aralk trn, ikincisi ise desteklenmesi gereken eleman trn belirler. rnein araln double tr ile uyumlu olan bir k aral olmas gerektii yle belirtilir: void birlev(T)(T aralk) if (isOutputRange!(T, double)) { // ... } O kstlamay T bir OutputRange ise ve double tr ile kullanlabiliyorsa diye okuyabiliriz. static if ile birlikte kullanldklarnda bu ablonlar kendi yazdmz aralklarn ne derece yetenekli olabileceklerini de belirlerler. rnein asl aralk ForwardRange olduunda save() ilevine de sahip olacandan, kendi yazdmz zel aralk trnn de save() ilevini sunmasn o ilevden yararlanarak salayabiliriz.

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

526

Baka Aralk Olanaklar

Bunu grmek iin kendisine verilen aralktaki deerlerin eksi deerlilerini reten bir aral nce bir InputRange olarak tasarlayalm: import std.range; struct Eksili(T) if (isInputRange!T) { T aralk; this (T aralk) { this.aralk = aralk; } @property bool empty() { return aralk.empty; } @property auto front() { return -aralk.front; } void popFront() { aralk.popFront(); }

Not: front 'un dn tr olarak auto yerine biraz aada greceimiz ElementType!T de yazlabilir. Ayrca, T 'nin en azndan bir InputRange olmas gerektiini bildiren ablon kstlamasn da yazdm. Bu araln tek zellii, front ilevinde asl araln bandaki deerin eksi deerlisini dndrmesidir. ou aralk trnde olduu gibi, kullanm kolayl asndan bir de yardmc ilev yazalm: Eksili!T eksili(T)(T aralk) { return Eksili!T(aralk); } Bu aralk, rnein bir nceki derste grdmz Fibonacci aralyla birlikte kullanlmaya hazrdr: writeln(eksili(take(FibonacciSerisi(), 5))); kts, araln ilk 5 elemannn eksi deerlilerini ierir: [0, -1, -1, -2, -3] Doal olarak, Eksili bu tanmyla yalnzca bir InputRange olarak kullanlabilir ve rnein ForwardRange gerektiren cycle() gibi algoritmalara gnderilemez: auto eksiliSonu = eksili(take(FibonacciSerisi(), 5)); writeln(take(cycle(eksiliSonu), 10)); // derleme HATASI

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

527

Baka Aralk Olanaklar

Oysa, asl aralk FibonacciSerisi gibi bir ForwardRange olduunda Eksili 'nin de save() ilevini sunamamas iin bir neden yoktur. Bu durum derleme zamannda static if ile denetlenir ve ek ilevler asl araln yetenekleri dorultusunda tanmlanrlar: struct Eksili(T) if (isInputRange!T) { // ... static if (isForwardRange!T) { Eksili!T save() { return Eksili!T(aralk.save()); } }

Yukardaki ek ilev sayesinde Eksili!FibonacciSerisi de artk bir ForwardRange olarak kabul edilir ve yukardaki cycle() satr artk derlenir: auto eksiliSonu = eksili(take(FibonacciSerisi(), 5)); writeln(take(cycle(eksiliSonu), 10)); // artk derlenir kts, Fibonacci serisinin ilk 5 elemannn eksi deerlilerinin srekli olarak tekrarlanmasndan oluan araln ilk 10 elemandr: [0, -1, -1, -2, -3, 0, -1, -1, -2, -3] Eksili aralnn duruma gre BidirectionalRange ve RandomAccessRange olabilmesi de ayn yntemle salanr: struct Eksili(T) if (isInputRange!T) { // ... static if (isBidirectionalRange!T) { @property auto back() { return -aralk.back; } void popBack() { aralk.popBack(); }

static if (isRandomAccessRange!T) { auto opIndex(size_t sraNumaras) { return -aralk[sraNumaras]; } }

Bylece rnein dizilerle kullanldnda elemanlara [] ileci ile eriilebilir: auto d = [ 1.5, 2.75 ]; auto e = eksili(d); writeln(e[1]);

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

528

Baka Aralk Olanaklar

kts: -2.75

77.2

ElementType ve ElementEncodingType
ElementType , aralktaki elemanlarn trn bildiren bir ablondur. ElementType!T , "T aralnn eleman tr" anlamna gelir. rnein belirli trden iki aralk alan, ve bunlardan birincisinin elemanlarnn tr ile uyumlu olan bir k aral gerektiren bir ilevin ablon kstlamas yle belirtilebilir: void ile(G1, G2, )(G1 giri1, G2 giri2, k) if (isInputRange!G1 && isForwardRange!G2 && isOutputRange!(, ElementType!G1)) { // ... } Yukardaki ablon kstlamasn yle aklayabiliriz: G1 bir InputRange ve G2 bir ForwardRange olmaldr; ek olarak, de G1 'in elemanlarnn tr ile kullanlabilen bir OutputRange olmaldr. Dizgiler aralk olarak kullanldklarnda elemanlarna harf harf eriildii iin dizgi aralklarnn eleman tr her zaman iin dchar 'dr. Bu yzden dizgilerin UTF kodlama tr ElementType ile belirlenemez. UTF kodlama trn belirlemek iin ElementEncodingType kullanlr. rnein bir wchar dizgisinin ElementType ' dchar , ElementEncodingType ' da wchar 'dr.

77.3

Baka aralk nitelikleri


std.range modl aralklarla ilgili baka ablon olanaklar da sunar. Bunlar da ablon kstlamalarnda ve static if deyimlerinde kullanlrlar. isInfinite : Aralk sonsuzsa true retir. hasLength : Araln length nitelii varsa true retir. hasSlicing : Araln a[x..y] biiminde dilimi alnabiliyorsa true retir. hasAssignableElements : Araln elemanlarna deer atanabiliyorsa true retir. hasSwappableElements : Araln elemanlar std.algorithm.swap ile dei toku edilebiliyorsa true retir. hasMobileElements Araln elemanlar std.algorithm.move ile aktarlabiliyorsa true retir. Bu, aralk eidine bal olarak bataki eleman aktaran moveFront() 'un, sondaki eleman aktaran moveBack() 'in, veya rastgele bir eleman aktaran moveAt() 'in mevcut olduunu belirtir. Aktarma ilemi kopyalama ileminden daha hzl olduu iin hasMobileElements niteliinin sonucuna bal olarak baz ilemler move() ile daha hzl gerekletirilebilirler. hasLvalueElements Araln elemanlar sol deer (lvalue) olarak kullanlabiliyorsa true retir. Bu kavram, araln elemanlar gerekte var olan elemanlara referans iseler diye dnebilirsiniz. rnein hasLvalueElements!Fibonacci 'nin deeri false 'tur nk Fibonacci aralnn elemanlar gerekte var olan elemanlar deillerdir; hesaplanarak oluturulurlar. Benzer ekilde hasLvalueElements!(Eksili!(int[])) 'in deeri de

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

529

Baka Aralk Olanaklar

false 'tur nk o araln da gerek elemanlar yoktur. te yandan, hasLvalueElements!(int[]) 'in deeri true 'dur nk dilimler gerekte var olan elemanlara eriim salarlar. rnein empty , isInfinite!T 'nin deerine bal olarak farkl biimde tanmlanabilir. Bylece, asl aralk sonsuz olduunda Eksili!T 'nin de derleme zamannda sonsuz olmas salanm olur: struct Eksili(T) if (isInputRange!T) { // ... static if (isInfinite!T) { // Eksili!T de sonsuz olur static immutable bool empty = false; } else { @property bool empty() { return aralk.empty; } } // ... }

77.4

alma zaman ok ekillilii iin inputRangeObject() ve outputRangeObject()


Aralklar, ablonlarn getirdii derleme zaman ok ekilliliine sahiptirler. Biz de bir nceki ve bu dersteki ou rnekte bu olanaktan yararlandk. (Not: Derleme zaman ok ekillilii ile alma zaman ok ekilliliinin farklarn Ayrntl ablonlar dersindeki "Derleme zaman ok ekillilii" balnda grmtk.) Derleme zaman ok ekilliliinin bir etkisi, ablonun her farkl kullanmnn farkl bir ablon tr oluturmasdr. rnein take() algoritmasnn dndrd zel aralk nesnesinin tr take() 'e gnderilen aralk trne gre deiir: writeln(typeof(take(eksili([11, 22]), 1)).stringof); writeln(typeof(take(FibonacciSerisi(), 1)).stringof); kts: Take!(Eksili!(int[])) Take!(FibonacciSerisi) Bunun doal sonucu, farkl trlere sahip olan aralk nesnelerinin uyumsuz olduklar iin birbirlerine atanamamalardr. Bu uyumsuzluk iki InputRange nesnesi arasnda daha ak olarak da gsterilebilir: auto aralk = eksili([11, 22]); // ... sonraki bir zamanda ... aralk = FibonacciSerisi(); // derleme HATASI Beklenecei gibi, derleme hatas FibonacciSerisi trnn Eksili!(int[]) trne otomatik olarak dntrlemeyeceini bildirir:

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

530

Baka Aralk Olanaklar

Error: cannot implicitly convert expression (FibonacciSerisi(0,1)) of type FibonacciSerisi to Eksili!(int[]) Buna ramen, her ne kadar trleri uyumsuz olsalar da aslnda her ikisi de int aral olan bu aralk nesnelerinin birbirlerinin yerine kullanlabilmelerini bekleyebiliriz. nk kullanm asndan bakldnda, btn ileri int trnden elemanlara eritirmek olduundan, o elemanlarn hangi dzenek yoluyla retildikleri veya eritirildikleri nemli olmamaldr. Phobos, bu sorunu inputRangeObject() ve outputRangeObject() ilevleriyle giderir. inputRangeObject() , aralklar belirli trden elemanlara sahip belirli eit aralk tanmyla kullandrmaya yarar. Aralk nesnelerini trlerinden bamsz olarak, rnein elemanlar int olan InputRange aral genel tanm ile kullanabiliriz. inputRangeObject() btn eriim aralklarn destekleyecek kadar esnektir. Bu yzden nesnelerin tanm auto ile yaplamaz; aralk nesnesinin nasl kullanlacann aka belirtilmesi gerekir: // "int giri aral" anlamnda InputRange!int aralk = inputRangeObject(eksili([11, 22])); // ... sonraki bir zamanda ... // Bu atama artk derlenir aralk = inputRangeObject(FibonacciSerisi()); inputRangeObject() 'in dndrd nesnelerin ikisi de InputRange!int olarak kullanlabilmektedir. Araln rnein int elemanl bir ForwardRange olarak kullanlaca durumda ise aka ForwardRange!int yazmak gerekir: ForwardRange!int aralk = inputRangeObject(eksili([11, 22])); auto kopyas = aralk.save(); aralk = inputRangeObject(FibonacciSerisi()); writeln(take(aralk.save(), 10)); Nesnelerin ForwardRange olarak kullanlabildiklerini gstermek iin save() ilevlerini ararak kullandm. outputRangeObject() de OutputRange aralklar ile kullanlr ve onlar belirli tr elemanlarla kullanlabilen OutputRange aral genel tanmna uydurur.

77.5

zet
std.range modl ablon kstlamalarnda yararl olan baz ablonlar ierir std.range modlndeki ablonlar, tanmladmz aralklarn baka aralklarn yeteneklerinin el verdii lde yetenekli olabilmelerini salarlar inputRangeObject() ve outputRangeObject() , farkl trden aralk nesnelerinin elemanlar belirli trden olan belirli eitten aralk genel tanmna uymalarn salarlar

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

531

okuzlular

78

okuzlular
okuzlu, birden fazla deikeni bir araya getiren ve bir yap nesnesi gibi kullanlmalarn salayan olanaktr. Baz dillerin i olana olan okuzlular D'de std.typecons.Tuple ile bir ktphane olana olarak gerekletirilmitir. okuzlular ounlukla kolaylk ilevi olan tuple() ile oluturulurlar: import std.stdio; import std.typecons; void main() { auto okuzlu = tuple(42, "merhaba"); writeln(okuzlu); } Yukardaki tuple() ilevi, 42 deerindeki int 'in ve "merhaba" deerindeki string 'in bir araya gelmesinden oluan bir nesne oluturur. Bu nesnenin trn ve yelerinin deerlerini programn ktsnda gryoruz: Tuple!(int,string)(42, merhaba) O nesnenin aadaki szde yap gibi gerekletirilmi olduunu dnebilirsiniz: // Tuple!(int, string)'in edeeri struct __okuzlu_int_string { int __ye_0; string __ye_1; } okuzlularn yelerine normalde sra numarasyla eriilir. Bu adan bakldnda her bir eleman farkl trden olabilen kk bir dizi gibi dnebiliriz: writeln(okuzlu[0]); writeln(okuzlu[1]); kts: 42 merhaba okuzlularn yelerine sra numarasyla eriilmesi baka dillerde de yaygndr. Phobos okuzlularnn yelerine ise nitelik isimleriyle de eriilebilir. Bu, tuple() ilevi arlarak salanamaz; Tuple ablonunun aka yazlmas gerekir. yelerin trleri ve nitelik isimleri iftler halinde belirtilirler: auto okuzlu = Tuple!(int, "say", string, "mesaj")(42, "merhaba"); Yukardaki tanm, int trndeki sfrnc elemana ayrca .say niteliiyle ve string trndeki birinci elemana ayrca .mesaj niteliiyle eriilmesini salar: writeln("0 sra numarasyla : writeln(".say nitelii olarak : writeln("1 sra numarasyla : writeln(".mesaj nitelii olarak: ", ", ", ", okuzlu[0]); okuzlu.say); okuzlu[1]); okuzlu.mesaj);

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

532

okuzlular

kts: 0 sra numarasyla : .say nitelii olarak : 1 sra numarasyla : .mesaj nitelii olarak: 42 42 merhaba merhaba

78.1

Ne zaman kullanmal
Ayn ama iin yaplar da kullanlabildiinden okuzlular art deildir. te yandan, tr tanmlamak gerekmeden nesne oluturabildiklerinden kullanmlar daha kolaydr. okuzlular, ilevlerin tek deer dndrebilme yetersizliklerine kar basit bir zm olarak grlebilirler. rnein std.algorithm.findSplit , bir aral baka bir aralk iinde arar ve arama sonucunda bilgi retir. Bu bilgiler; bulunan aralktan ncesini, bulunan aral, ve bulunan aralktan sonrasn belirtirler. findSplit() , bu para bilgiyi bir okuzlu olarak dndrr: auto btnAralk = "merhaba"; auto aranan = "er"; auto sonu = findSplit(btnAralk, aranan); writeln("ncesi : ", sonu[0]); writeln("bulunan: ", sonu[1]); writeln("sonras: ", sonu[2]); kts: ncesi : m bulunan: er sonras: haba Bulunamad durumda sfrnc para btn aral ierir; dierleri botur: auto sonu = findSplit("merhaba", "yer"); writeln("ncesi : ", sonu[0]); writeln("bulunan: ", sonu[1]); writeln("sonras: ", sonu[2]); Aranan "yer" asl aralkta bulunmad iin son ikisi botur: ncesi : merhaba bulunan: sonras: Kendi programlarnzda kullanacanz okuzlu yelerinin findSplit() 'tekiler kadar yakn kavramlar olmalarn gzetmenizi neririm. Yoksa yap nesneleri dndrmek daha uygun olabilir.

78.2

Tr okuzlular
D'nin ok st dzey bir olana, tr okuzlulardr. Tr okuzlular, derleyiciden de destek alnarak tanmlanm olan std.typetuple.TypeTuple ile gerekletirilmitir.

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

533

okuzlular

TypeTuple 'un zellii, yelerinin belirli trlerden nesneler deil, belirli trlerin kendileri olmalardr. rnein TypeTuple!(int, string) okuzlusunun yeleri, int tr ve string trdr. Bu trlere foreach dngs iinde teker teker eriilebilir. ounlukla birim testlerinde ve ayn testin birden fazla tr iin tekrarlanmasnn gerektii durumlarda yararldr. rnein findSplit() algoritmasnn string , wstring , ve dstring trlerinin hepsiyle de doru altn denetleyen bir birim testi aadaki gibi yazlabilir: unittest { foreach (Tr; TypeTuple!(string, wstring, dstring)) { Tr btnAralk = "abcdefg"; Tr aranan = "d"; auto sonu = findSplit(btnAralk, aranan); Tr beklenen_ncesi = "abc"; Tr beklenen_bulunan = "d"; Tr beklenen_sonras = "efg"; assert(sonu[0] == beklenen_ncesi); assert(sonu[1] == beklenen_bulunan); assert(sonu[2] == beklenen_sonras);

Dng deikeni olan Tr , foreach dngsnn her ilerletiliinde TypeTuple 'un ierdii trlerden birisi haline gelir.

78.3

zet
tuple() , yelerine sra numarasyla eriilen yap benzeri deikenler oluturur aka Tuple kullanldnda yelere niteliklerle de eriilebilir TypeTuple , tr okuzlular oluturur

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

534

Kout lemler

79

Kout lemler
Gnmzdeki mikro ilemciler her birisi bal bana bir mikro ilemci olarak kullanlabilen birden fazla ekirdekten oluurlar. std.parallelism modl, bu ekirdeklerin ayn anda iletilmelerini ve programn bu sayede daha hzl almasn salayan olanaklar ierir. Daha nce kullandmz btn rneklerdeki btn kodlarda ilemlerin yazldklar srada iletildiklerini varsaydk: ++i; ++j; Yukardaki kodda nce i 'nin deerinin, ondan sonra da j 'nin deerinin arttrlacan biliyoruz. Aslnda bu her zaman doru deildir: Derleyicinin kodun daha hzl ilemesi iin uygulad eniyiletirmeler sonucunda, her iki deiken de rnein mikro ilemcinin yazmalarnda depolanm olabilirler. Bu yazmalar da birbirlerinden bamsz olduklarndan, mikro ilemci o iki ilemi ayn anda iletebilir. Bu tr eniyiletirmeler yararldrlar ama ok alt dzeydeki ilemlerden daha st dzey kapsamlarda otomatik olarak uygulanamazlar. Bir grup st dzey ilemin birbirlerinden bamsz olduklarna ve bu yzden de ayn anda iletilebileceklerine ou durumda yalnzca programc karar verebilir. Aadaki foreach dngsndeki elemanlarn bandan sonuna kadar ve teker teker iletileceklerini biliyoruz: auto renciler = [ renci(1), renci(2), renci(3), renci(4) ]; foreach (renci; renciler) { renci.uzunBirlem(); } Yukardaki kod, iletim sisteminin o program altrmak iin semi olduu tek ekirdek zerinde iletilir. foreach dngs de rencileri bandan sonuna kadar ilettii iin uzunBirlem() , rencilere srayla ve teker teker uygulanr. Oysa ou durumda bir rencinin iletilebilmesi iin nceki rencilerin ilemlerinin tamamlanm olmalar gerekmez. renci ilemlerinin birbirlerinden bamsz olduklar durumlarda dier ekirdeklerden yararlanlmyor olmas zaman kaybna yol aacaktr. Aadaki rneklerdeki ilemlerin hissedilir derecede uzun sren ilemlere benzemeleri iin core.thread modlndeki Thread.sleep() 'ten yararlanacam. Thread.sleep() ilemleri belirtilen sre kadar durdurur. Ne kadar bekleneceini bildirmenin bir yolu, "sre" anlamna gelen "duration"n ksaltmas olan dur 'u kullanmaktr. dur 'un ablon parametresi zaman birimini belirler: milisaniye iin "msecs", saniye iin "seconds". import std.stdio; import core.thread; struct renci { int numara; void uzunBirlem() { writeln(numara, " numaral rencinin ilemi balad"); /* Gerekte yava olduklarn varsaydmz ilemlerin

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

535

Kout lemler

* yavalklarna benzesin diye 1 saniye bekliyoruz */ Thread.sleep(dur!"seconds"(1)); } writeln(numara, " numaral rencinin ilemi bitti");

void main() { auto renciler = [ renci(1), renci(2), renci(3), renci(4) ]; foreach (renci; renciler) { renci.uzunBirlem(); }

Yukardaki programn alma sresini konsol komutu time ile lebiliriz: $ 1 1 2 2 3 3 4 4 time ./deneme numaral rencinin numaral rencinin numaral rencinin numaral rencinin numaral rencinin numaral rencinin numaral rencinin numaral rencinin 0m4.003s 0m0.000s 0m0.000s

ilemi ilemi ilemi ilemi ilemi ilemi ilemi ilemi

balad bitti balad bitti balad bitti balad bitti

real user sys

toplam 4 saniye

renciler srayla iletildiklerinden ve her ilem 1 saniye tuttuundan toplam sre, beklendii gibi yaklak olarak 4 saniye tutmaktadr. Oysa 4 rencinin ilemleri rnein 4 ekirdein bulunduu bir ortamda ayn anda ve tek seferde iletilebilseler btn ilem 1 saniye tutabilir. Bunun nasl gerekletirildiine gemeden nce, programn altrld ortamda ka ekirdek bulunduunun std.parallelism.totalCPUs 'un deeri ile belirlenebildiini gstermek istiyorum: import std.stdio; import std.parallelism; void main() { writefln("Bu ortamda toplam %s ekirdek var.", totalCPUs); } Bu dersi yazdm ortamda u kty alyorum: Bu ortamda toplam 4 ekirdek var.

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

536

Kout lemler

79.1

parallel()
parallel() , foreach dngsndeki ilemleri btn ekirdekler zerinde iletmeye yarar. Yukardaki programa std.parallelism modln eklemek ve renciler yerine parallel(renciler) yazmak btn ekirdeklerden yararlanmak iin yeterlidir: import std.parallelism; // ... foreach (renci; parallel(renciler)) { Yap ve Snflarda foreach dersinde grdmz gibi, foreach dngsnn kapsam nesnelerin opApply ilevlerine bir delegate olarak gnderilir. parallel() 'in dndrd geici nesne, bu delegate 'i her eleman iin farkl bir ekirdek zerinde ileten bir aralk nesnesidir. Bunun sonucunda da asl topluluu parallel() ilevine gndererek kullanmak, programn 4 ekirdek bulunan bu ortamda 1 saniyede tamamlanmas iin yeterli olur: $ time ./deneme 2 1 3 4 2 3 1 4 numaral numaral numaral numaral numaral numaral numaral numaral rencinin rencinin rencinin rencinin rencinin rencinin rencinin rencinin ilemi ilemi ilemi ilemi ilemi ilemi ilemi ilemi balad balad balad balad bitti bitti bitti bitti

real user sys

0m1.004s 0m0.000s 0m0.000s

imdi 1 saniye

Not: Programn alma sresi sizin ortamnzda farkl olabilir; kabaca "4 saniye bl ekirdek says" hesabnn sonucu kadar srede tamamlanacan bekleyebiliriz. letim sistemindeki btn i paracklar ayn ekirdekleri paylarlar. Bu i paracklarnn hangi srayla balatldklarna ve hangi koullarda duraksatldklarna iletim sistemi karar verir. Bu yzden uzunBirlem() iinde yazdrdmz mesajlarn sralarnn kark olarak ktklarn gryoruz. Dng iindeki ilemler her renci iin bamsz olduklar srece hangisinin daha nce sonlandnn programn ileyii asndan bir nemi yoktur. parallel() yardmyla ayn anda iletilen ilemlerin gerekten birbirlerinden bamsz olduklar programcnn sorumluluundadr. rnein yukardaki mesajlarn kta belirli bir srada grnmeleri gerekseydi, bunu salamak elimizde olmadndan parallel() 'in kullanlmas bir hata olarak kabul edilirdi. (Not: paracklarnn birbirlerine baml olduklar e zamanl programlamay bir sonraki derste greceiz.) foreach tamamlandnda btn ilemler de tamamlanmtr. Program, ileyiine btn renci ilemlerinin tamamlanm olduklar garantisiyle devam edebilir. lemlerin i paracklarna datlmalarnn kk de olsa bir bedeli vardr. Bu bedel zellikle ilemlerin ksa srd durumlarda farkedilir dzeyde olabilir. Bunun nne gemek gerektiinde her i paracna birden fazla eleman vermek daha hzl olabilir. Her i paracnn ka eleman iletecei parallel() 'in ikinci ilev parametresi ile belirtilir:

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

537

Kout lemler

foreach (renci; parallel(renciler, 2)) { Yukardaki kod, elemanlarn i paracklarna ikier ikier datlmalarn salar.

79.2

Grev tr Task
Programdaki baka ilemlerle ayn anda iletilebilen ilemlere grev denir. Grevler std.parallelism.Task tr ile ifade edilirler. parallel() , her renci iin foreach bloundaki ilemlerden oluan farkl bir Task nesnesi kurar ve o grevi otomatik olarak balatr. foreach dngsnden kmadan nce de balatt btn grevlerin tamamlanmalarn bekler. Kurma, balatma ve bekleme ilemlerini otomatik olarak yrtt iin ok yararldr. Ancak, parallel() yalnzca foreach dngsyle kullanlabilir. Ayn anda iletilebilen ilemlerin topluluk elemanlar olarak ifade edilemedikleri durumlarda bu ilevi aka arabiliriz. Grev nesnesi kurmak iin task() , grevi balatmak iin executeInNewThread() , ve grevin tamamlanmasn beklemek iin yieldForce() kullanlr. Bu ilevleri aadaki programn aklama satrlarnda anlatyorum. Aadaki programdaki birlem() ilevi, iki farkl i iin iki kere balatlmaktadr. Hangi i ile ilgili olarak ilediini gsterebilmek iin kimlik 'in ba harfini ka yazdryor. Not: Standart ka yazdrlan bilgiler ou durumda kta hemen belirmezler; satr sonu karakteri gelene kadar bir ara bellekte bekletilirler. write satr sonu karakteri yazdrmadndan, programn ileyiini izleyebilmek iin o ara bellein hemen ka gnderilmesini stdout.flush() ile salyoruz. import import import import std.stdio; std.parallelism; core.thread; std.array;

/* kimlik'in ba harfini yarm saniyede bir ka yazdrr */ int birlem(string kimlik, int sre) { writefln("%s %s saniye srecek", kimlik, sre); foreach (i; 0 .. (sre * 2)) { Thread.sleep(dur!"msecs"(500)); write(kimlik.front); stdout.flush(); } } return 1; /* yarm saniye */

void main() { /* Grev kuruluyor */ auto grev = task!birlem("grev", 5); /* 'grev' balatlyor */ grev.executeInNewThread(); /* 'grev' iine devam ederken baka bir ilem * balatlyor */ auto sonu = birlem("main iindeki ilem", 3); /* * * * Bu noktada main iinde balatlan ilemin tamamlandndan eminiz; nk onu grev olarak deil, her zaman yaptmz gibi bir ilev ars olarak balattk. */

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

538

Kout lemler

/* te yandan, bu noktada 'grev'in iini tamamlayp * tamamlamadndan emin olamayz. Gerekiyorsa * tamamlanana kadar beklemek iin yieldForce()'u * aryoruz. */ auto koutSonu = grev.yieldForce(); writeln(); writefln("Hepsi tamam; sonu: %s", sonu + koutSonu);

Programn kts benim denediim ortamda aadakine benziyor. lemlerin ayn anda gerekletiklerini m ve g harflerinin kark olarak yazdrlmalarndan anlyoruz: main iindeki ilem 3 saniye srecek grev 5 saniye srecek mgmggmmgmgmggggg Hepsi tamam; sonu: 2 Yukardaki kullanmdaki grev ilevi, task 'in ablon parametresi olarak belirtiliyor. Onun yerine task 'in ilk ilev parametresi olarak da belirtilebilir: void ilem(int say) { /* ... */ } auto grev = task(&ilem, 42); Gerektiinde isimsiz bir ilev veya opCall() ilecini tanmlam olan bir trn bir nesnesi de kullanlabilir. rnein bir isimsiz ilev ile yle arlabilir: auto grev = task((int say) { /* ... */ }, 42);

79.3

Atlan hatalar
Grevler farkl i paracklarnda iletildiklerinden, attklar hatalar onlar balatan i parac tarafndan yakalanamaz. Bu yzden, atlan hatay grevin kendisi yakalar ve yieldForce() arlana kadar bekletir. Ayn hata yieldForce() arldnda tekrar atlr ve bylece grevi balatm olan i parac tarafndan yakalanabilir. import std.stdio; import std.parallelism; import core.thread; void hataAtanlem() { writeln("hataAtanlem() balad"); Thread.sleep(dur!"seconds"(1)); writeln("hataAtanlem() hata atyor"); throw new Exception("Atlan hata"); } void main() { auto grev = task!hataAtanlem(); grev.executeInNewThread(); writeln("main devam ediyor");

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

539

Kout lemler

Thread.sleep(dur!"seconds"(3)); writeln("main, grev'in sonucunu alyor"); grev.yieldForce();

Grev srasnda atlan hatann program hemen sonlandrmadn programn ktsnda gryoruz: main devam ediyor hataAtanlem() balad hataAtanlem() hata atyor main, grev'in sonucunu alyor object.Exception@deneme.d(10): Atlan hata

atld zaman farkedildii zaman

Grevin att hata, istendiinde yieldForce() 'u sarmalayan bir try-catch blou ile yakalanabilir. Bunun allmn dnda bir kullanm olduuna dikkat edin: try-catch blou normalde hatay atan kodu sarmalar. Grevlerde ise yieldForce() 'u sarmalar: try { grev.yieldForce(); } catch (Exception hata) { writefln("grev srasnda bir hata olmu: '%s'", hata.msg); } Programn imdiki kts: main devam ediyor hataAtanlem() balad hataAtanlem() hata atyor atld zaman main, grev'in sonucunu alyor grev srasnda bir hata olmu: 'Atlan hata' yakaland zaman

79.4

Task ilevleri
done : Grevin tamamlanp tamamlanmadn bildirir; grev srasnda hata atlmsa o hatay atar. if (grev.done) { writeln("Tamamlanm"); } else { writeln("lemeye devam ediyor"); } executeInNewThread() : Grevi, yeni balatt i paracnda iletir. executeInNewThread(int ncelik) : Grevi, yeni balatt i paracnda ve belirtilen ncelikle (priority) iletir. (ncelik deeri iletim sistemine bal bir kavramdr.) Grevin tamamlanmasn beklemek iin farkl ilev vardr: yieldForce() : Henz balatlmamsa grevi bu i paracnda balatr; zaten tamamlanmsa dn deerini dndrr; hl ilemekteyse bitmesini mikro ilemciyi megul etmeden bekler; hata atlmsa tekrar atar.

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

540

Kout lemler

spinForce() : yieldForce() 'tan fark, gerektiinde mikro ilemciyi megul ederek beklemesidir. workForce() : yieldForce() 'tan fark, grevin tamamlanmasn beklemek yerine yeni bir grevi iletmeye balamasdr. Bunlar arasndan ou durumda en uygun olan yieldForce() 'tur. spinForce() , her ne kadar mikro ilemciyi megul etse de grevin ok ksa bir sre sonra tamamlanacann bilindii durumlarda yararldr. workForce() , grev beklenene kadar baka bir grevin balatlmasnn istendii durumlara uygundur. std.parallelism modlnde baka olanaklar da bulunur ama bu kadar hemen hemen btn programlar iin yeterlidir.

79.5

zet
parallel() , foreach dngsndeki ilemlerin birden fazla ekirdekte ayn anda iletilmelerini salar Gerektiinde grevler task() ile oluturulabilirler, executeInNewThread() ile balatlabilirler ve yieldForce() ile beklenebilirler

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

541

E Zamanl Programlama

80

E Zamanl Programlama
E zamanl programlama, bir nceki derste grdmz kout ilemlere ok benzer. kisi de ilemlerin farkl i paracklar zerinde ayn anda iletilmeleri ile ilgilidir ve aslnda kout ilemler de perde arkasnda e zamanl programlama ile gerekletirilir. Bu iki kavram bu yzden ok kartrlr. Kout ilemlerle e zamanl programlama arasndaki farklar unlardr: Kout ilemlerin temel amac mikro ilemci ekirdeklerinden yararlanarak programn hzn arttrmaktr. E zamanl programlama ise yalnzca tek ekirdei bulunan ortamlarda bile gerekebilir ve programn ayn anda birden fazla i yrtmesini salar. rnein bir sunucu program her istemcinin iini farkl bir i paracnda yrtebilir. Kout ilemler birbirlerinden bamszdrlar; hatta, birbirlerine bal olan ilemlerin kout olarak iletilmeleri hata olarak kabul edilir. E zamanl programlamada ise ou zaman i paracklar birbirlerine baldrlar; rnein devam edebilmek iin baka i paracklarnn rettikleri verilere gerek duyabilirler. Her iki yntem de iletim sisteminin i paracklarn kullanrlar. Kout ilemler i paracklarn grev kavramnn arkasna gizlerler; e zamanl programlama ise dorudan i paracklarn kullanr. Kout ilemler ok kolay kullanlrlar ve grevler bamsz olduklar srece program doruluu asndan gvenlidirler. E zamanl programlama ise ancak mesajlama yntemi kullanldnda gvenlidir. Veri paylamna dayanan geleneksel e zamanl programlamada programn doru alaca kantlanamayabilir. D hem mesajlamaya dayanan hem de veri paylamna dayanan e zamanl programlamay destekler. Veri paylam ile hatasz programlar retmek ok zor olduundan modern programclkta mesajlama yntemi benimsenmitir. Bu derste nce std.concurrency modlnn salad mesajlama olanaklarn gstereceim.

80.1

Kavramlar
parac (thread): letim sistemi, btn programlar i parac ad verilen ilem birimleri ile iletir. altrlan her D programnn main() ile balayan ilemleri, iletim sisteminin o program altrmak iin semi olduu bir i parac zerinde balatlr. main() 'in ilettii btn ilemler normalde hep ayn i parac zerinde iletilirler. Program gerektiinde kendisi baka i paracklar balatabilir ve bylece ayn anda birden fazla i yrtebilir. rnein bir nceki derste grdmz her grev, std.parallelism 'in olanaklar tarafndan balatlm olan bir i paracn kullanr. letim sistemi i paracklarn nceden kestirilemeyecek anlarda duraksatr ve tekrar balatr. Bunun sonucunda rnein aadaki kadar basit ilemler bile bir sre yarm kalm olabilirler: ++i; Yukardaki ilem aslnda admdan oluur: Deikenin deerinin okunmas, deerin arttrlmas ve tekrar deikene atanmas. letim sisteminin bu i paracn duraksatt bir anda bu admlar sonradan devam edilmek zere yarm kalm olabilirler. Mesaj (message): paracklarnn ileyileri srasnda birbirlerine gnderdikleri bilgilere mesaj denir. Mesaj her trden ve her sayda deikenden oluabilir. parac kimlii (Tid): Her i paracnn bir kimlii vardr. Kimlik, gnderilen mesajn alcs olan i paracn belirler.

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

542

E Zamanl Programlama

Sahip (owner): Yeni bir i parac balatan her i parac, balatlan i paracnn sahibi olarak anlr. i (worker): Balatlan i paracna ii denir.

80.2

paracklarn balatmak
Yeni bir i parac balatmak iin spawn() kullanlr. spawn() parametre olarak bir ilev alr ve yeni i paracn o ilevden balatr. O ilevin belki de baka ilevlere dallanarak ilettii btn ilemler artk yeni i parac zerindedir. Bu noktadan sonra sahip ve ii birbirlerinden bamsz iki alt program gibi ilemeye devam ederler: import std.stdio; import std.concurrency; import core.thread; void ii() { foreach (i; 0 .. 5) { Thread.sleep(dur!"msecs"(500)); writeln(i, " (birlem)"); } } void main() { spawn(&ii); foreach (i; 0 .. 5) { Thread.sleep(dur!"msecs"(300)); writeln(i, " (main)"); } } writeln("main tamam");

lemlerin ayn anda iletildiklerini gsterebilmek iin buradaki rneklerde de Thread.sleep 'ten yararlanyorum. Programn kts, main() 'den ve ii() 'den balam olan iki i paracnn dierinden habersiz olarak ilediini gsteriyor: 0 (main) 0 (birlem) 1 (main) 2 (main) 1 (birlem) 3 (main) 2 (birlem) 4 (main) main tamam 3 (birlem) 4 (birlem) Program btn i paracklarnn tamamlanmasn otomatik olarak bekler. Bunu yukardaki ktda gryoruz: main() 'in sonundaki "main tamam" yazdrld halde birlem() 'in de tamamlanmas beklenmitir. (Not: Bu dersi yazdm srada kullandm dmd'nin 2.053 srm bu konuda hatal; i paracklar erken sonlanabiliyorlar.)

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

543

E Zamanl Programlama

paracn balatan ilevin ald parametreler spawn() 'a ilev isminden sonra verilirler. Aadaki programdaki iki ii, ka drder tane say yazdryor. Hangi deerden balayacan parametre olarak alyor: import std.stdio; import std.concurrency; import core.thread; void ii(int balangDeeri) { foreach (i; 0 .. 4) { Thread.sleep(dur!"msecs"(500)); writeln(balangDeeri + i); } } void main() { foreach (i; 1 .. 3) { spawn(&ii, i * 10); } } paracklarndan birisinin yazdrdklarn maviyle belirtiyorum. letim sisteminin i paracklarn balatmasna ve duraklatmasna bal olarak bu ktdaki satrlar farkl srada da olabilirler: 10 20 11 21 12 22 13 23

80.3

paracklarnn kimlikleri
Evrensel alanda tanmlanm olan thisTid , her i paracnn kendi kimliidir. smi, "bu i paracnn kimlii" anlamna gelen "this thread's identifier"dan tremitir. import std.stdio; import std.concurrency; void kimlikBilgisi(string aklama) { writefln("%s: %s, adresi: %s", aklama, thisTid, &thisTid); } void ii() { kimlikBilgisi("ii "); } void main() { spawn(&ii); kimlikBilgisi("sahip"); }

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

544

E Zamanl Programlama

Tid trndeki kimliin deerinin program asndan bir nemi olmad iin bu trn toString ilevi bile tanmlanmamtr. Bu yzden programn aadaki ktsnda yalnzca trn ismini gryoruz. Ek olarak, bu iki deerin adresleri bile ayn kar: sahip: Tid, adresi: 46AFC8 ii : Tid, adresi: 46AFC8 Buna ramen o iki kimlik farkldr. Bunun nedeni, D'de i paracklarnn evrensel alanlarnn gvenlik nedeniyle birbirlerinden bamsz olmalardr. Her i parac tarafndan ayn adresteymi gibi alglanmasna ramen bu deikenler her i paracnda farkldr. Sahip i paracnn kendi kimliini yeni balatt i paracna parametre olarak gndermesi ve bylece kendisini tantmas yaygndr: import std.concurrency; void ii(Tid sahip) { // ... } void main() { spawn(&ii, thisTid); } spawn() 'n bu noktaya kadar gzard etmi olduum dn deeri de iinin kimliini sahibe bildirir: void ii(Tid sahip) { // ... } void main() { Tid ii = spawn(&ii, thisTid); } Sahip ve ii birbirlerinin kimliklerini bylece elde etmi olurlar.

80.4

Mesajlama
Mesaj gndermek iin send() , belirli trden mesaj beklemek iin de receiveOnly() kullanlr. (eitli trlerden mesaj bekleyen receive() 'i ve belirli sreye kadar bekleyen receiveTimeout() 'u daha aada gstereceim.) Aadaki programdaki sahip i parac iisine int trnde bir mesaj gndermekte ve ondan double trnde bir mesaj beklemektedir. Bu i paracklar sahip sfrdan kk bir deer gnderene kadar mesajlamaya devam edecekler. nce sahip i paracn gsteriyorum: void main() { Tid ii = spawn(&iilevi, thisTid); foreach (deer; 1 .. 5) { ii.send(deer); double sonu = receiveOnly!double(); writefln("gnderilen: %s, alnan: %s", deer, sonu);

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

545

E Zamanl Programlama

} /* Sonlanmasn salamak iin iiye sfrdan kk bir * deer gnderiyoruz */ ii.send(-1);

main() , spawn() 'n dndrd i paracnn kimliini ii ismiyle saklamakta ve ona send() ile mesaj gnderirken kullanmaktadr. i ise kullanaca int 'i bir mesaj olarak alyor, onu bir hesapta kullanyor ve rettii double ' yine bir mesaj olarak sahibine gnderiyor: void iilevi(Tid sahip) { int deer = 0; while (deer >= 0) { deer = receiveOnly!int(); double sonu = cast(double)deer / 5; sahip.send(sonu); }

Yukardaki i parac mesajdaki deerin bete birini hesaplar. Programn kts yle: gnderilen: gnderilen: gnderilen: gnderilen: 1, 2, 3, 4, alnan: alnan: alnan: alnan: 0.2 0.4 0.6 0.8

Birden fazla deer ayn mesajn paras olarak gnderilebilir: sahip.send(thisTid, 42, 1.5); Ayn mesajn paras olarak gnderilen deerler alc tarafta bir okuzlunun yeleri olarak belirirler. receiveOnly() 'nin ablon parametrelerinin mesaj oluturan trlere uymalar arttr: /* Tid, int, ve double trlerinden oluan bir mesaj * bekliyoruz */ auto mesaj = receiveOnly!(Tid, int, double)(); auto gnderen = mesaj[0]; auto tamsay = mesaj[1]; auto kesirli = mesaj[2]; // Tid trnde // int trnde // double trnde

Trler uymadnda "mesaj uyumsuzluu" anlamna gelen MessageMismatch hatas atlr: import std.concurrency; void iilevi(Tid sahip) { // string gnderiyor sahip.send("merhaba"); } void main() { spawn(&iilevi, thisTid); // double bekliyor

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

546

E Zamanl Programlama

auto mesaj = receiveOnly!double();

kts: std.concurrency.MessageMismatch@std/concurrency.d(202): Unexpected message type ilerin attklar hatalar sahip tarafndan otomatik olarak yakalanamazlar. Bir yntem, hatann ii tarafndan yakalanmas ve bir mesaj olarak sahibe gnderilmesidir. Bunu aada gstereceim.
80.4.1

rnek
imdiye kadar grdmz kavramlar kullanan basit bir benzetim program tasarlayalm. Bu rnek iki boyutlu dzlemdeki robotlarn birbirlerinden bamsz ve rasgele hareketlerini belirliyor. Her robotun hareketini farkl bir i parac belirliyor. Her i parac balatlrken bilgi alyor: Sahibinin kimlii: Bu bilgiyi, robotun hareketiyle ilgili olan mesajlar gnderirken kullanyor. Robotun numaras: Bu bilgiyi, gnderdii mesajn hangi robotla ilgili olduunu bildirmek iin kullanyor. Robotun yaval: Bu bilgiyi, robotun ka milisaniyede bir yer deitireceini belirlemek iin kullanyor. Bu i paracnn yapt tek i, robotun numarasn ve hareketini bir sonsuz dng iinde sahibine gndermek: void gezdirici( Tid sahip, int robotNumaras, Yer nereden, int yavalk) { while (true) { Thread.sleep(dur!"msecs"(yavalk)); Yer nereye = rasgeleAdm(nereden); Hareket hareket = Hareket(nereden, nereye); sahip.send(robotNumaras, hareket); } nereden = nereye;

Sahip de sonsuz bir dng iinde bu mesajlar bekliyor. Ald mesajlarn hangi robotla ilgili olduunu her mesajn paras olan robot numarasndan anlyor: auto mesaj = receiveOnly!(int, Hareket)(); /* Mesajn yelerine kullanl isimler veriyoruz */ auto robotNumaras = mesaj[0]; auto hareket = mesaj[1]; Bu rnekteki btn mesajlar iilerden sahibe gnderiliyor. Daha karmak programlarda her iki ynde ve ok eitli trlerden mesajlar da gnderilebilir. Programn tamam yle: import std.stdio; import std.random; import std.string;

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

547

E Zamanl Programlama

import std.concurrency; import core.thread; struct Yer { int satr; int stun; string toString() { return format("%s,%s", satr, stun); }

struct Hareket { Yer nereden; Yer nereye; string toString() { return ((nereden == nereye) ? format("%s (durgun)", nereden) : format("%s -> %s", nereden, nereye)); }

class Robot { string grnm; int yavalk;

/* milisaniye */

this(string grnm, int yavalk) { this.grnm = grnm; this.yavalk = yavalk; } override string toString() { return format("%s(%s)", grnm, yavalk); }

/* 0,0 noktas etrafnda rasgele bir yer dndrr */ Yer rasgeleYer() { return Yer(uniform!"[]"(-10, 10), uniform!"[]"(-10, 10)); } /* Verilen deerin en fazla bir adm tesinde bir deer * dndrr */ int rasgeleAdm(int imdiki) { return imdiki + uniform!"[]"(-1, 1); } /* Verilen Yer'in komusu olan bir Yer dndrr; apraz * komusu olabilecei gibi tesadfen ayn yer de olabilir. */ Yer rasgeleKomu(Yer yer) { return Yer(rasgeleAdm(yer.satr), rasgeleAdm(yer.stun)); } void gezdirici( Tid sahip, int robotNumaras, Yer nereden, int yavalk) { while (true) { Thread.sleep(dur!"msecs"(yavalk));

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

548

E Zamanl Programlama

Yer nereye = rasgeleKomu(nereden); Hareket hareket = Hareket(nereden, nereye); sahip.send(robotNumaras, hareket); } nereden = nereye;

void main() { /* Farkl hzlardaki robotlar */ Robot[] robotlar = [ new Robot("A", 600), new Robot("B", 2000), new Robot("C", 5000) ]; /* Her birisi iin bir i parac balatlyor */ foreach (int robotNumaras, robot; robotlar) { spawn(&gezdirici, thisTid, robotNumaras, rasgeleYer(), robot.yavalk); } /* Artk hareket bilgilerini iilerden toplamaya * balayabiliriz */ while (true) { /* Burada iki yeden oluan okuzlu mesaj * bekliyoruz. Beklenen trlerin gnderilen mesajdaki * trlere uymalar arttr. */ auto mesaj = receiveOnly!(int, Hareket)(); /* Mesajn yelerine kullanl isimler veriyoruz */ auto robotNumaras = mesaj[0]; auto hareket = mesaj[1]; /* Bu robotla ilgili yeni bilgiyi ka * yazdryoruz */ writefln("%s %s", robotlar[robotNumaras], hareket);

Program, sonlandrlana kadar robotlarn konumlarn ka yazdrr: A(600) -6,8 -> -6,9 A(600) -6,9 -> -7,8 A(600) -7,8 -> -6,8 B(2000) -5,-6 -> -6,-5 A(600) -6,8 -> -6,7 A(600) -6,7 -> -5,7 A(600) -5,7 -> -5,6 B(2000) -6,-5 -> -7,-4 A(600) -5,6 (durgun) A(600) -5,6 -> -5,5 C(5000) -5,0 -> -4,0 A(600) -5,5 -> -6,4 ... okuzlular dersinde de grdmz gibi, mesajlar bu programdaki gibi okuzlular olarak gndermek yerine ayn okuzlunun edeeri olan bir yap da kullanabiliriz. rnein i paracnn greviyle ilgili bilgi yle bir yap olarak ifade edilebilir:

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

549

E Zamanl Programlama

struct GrevBilgisi { int robotNumaras; Yer balang; int yavalk; } void gezdirici(Tid sahip, GrevBilgisi grevBilgisi) { // ... } // ... spawn(&gezdirici, thisTid, GrevBilgisi(robotNumaras, rasgeleYer(), robot.yavalk));

Benzer ekilde, gezdirici() 'nin gnderdii mesaj da o mesaj ifade eden bir yap olarak tanmlanabilir: struct HareketMesaj { int robotNumaras; Hareket hareket; } // ... // ... sahip.send(HareketMesaj(grevBilgisi.robotNumaras, hareket)); auto mesaj = receiveOnly!HareketMesaj();

Mesajlamaya dayanan e zamanl programlamann yararn bu rnekte grebiliyoruz. Her robotun hareketi ayn anda ve dierlerinden bamsz olarak hesaplanyor. Sahip, bu basit rnekte yalnzca robotlarn hareketlerini ka yazdryor; btn robotlar ilgilendiren baka ilemler de uygulayabilir.

80.5

Farkl eitlerden mesaj beklemek


receiveOnly() yalnzca belirtilen trden mesaj bekleyebilir. Yukarda da grdmz gibi tek mesaj iinde birden fazla bilgi gnderilebilse de bunlarn receiveOnly() 'de belirtilen trlere uymalar arttr. rnein yukardaki rnekte geen aadaki satrdaki okuzlu mesaj int ve Hareket trlerinden olumaldr: auto mesaj = receiveOnly!(int, Hareket)(); receive() ise farkl eitlerden mesajlar beklemek iin kullanlr. Parametre olarak belirsiz sayda mesajc ilev alr. Gelen mesaj bu mesajc ilevlere srayla uydurulmaya allr ve mesaj, mesajn trnn uyduu ilk ileve gnderilir. rnein aadaki receive() ars ilki int , ikincisi de string bekleyen iki mesajc ilev kullanmaktadr: void ii() { bool tamam_m = false; while (!tamam_m) { void intleyen(int mesaj)

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

550

E Zamanl Programlama

writeln("int mesaj: ", mesaj); if (mesaj == -1) { writeln("kyorum"); tamam_m = true; }

void stringleyen(string mesaj) { writeln("string mesaj: ", mesaj); } } receive(&intleyen, &stringleyen);

Gnderilen int mesajlar intleyen() 'e, string mesajlar da stringleyen() 'e uyarlar. O i paracn yle bir kodla deneyebiliriz: import std.stdio; import std.concurrency; // ... void main() { auto ii = spawn(&iilevi); ii.send(10); ii.send(42); ii.send("merhaba"); ii.send(-1); // iinin sonlanmas iin

Mesajlar alc taraftaki uygun mesajc ilevlere gnderilirler: int mesaj: 10 int mesaj: 42 string mesaj: merhaba int mesaj: -1 kyorum receive() , yukardaki normal ilevler yerine isimsiz ilevler veya opCall() ye ilevi tanmlanm olan trlerin nesnelerini de kullanabilir. Bunun bir rneini grmek iin program isimsiz ilevler kullanacak ekilde deitireceiz. Ek olarak, iinin sonlanmasn da -1 gibi zel bir deer yerine ismi aka Sonlan olan zel bir tr ile bildireceiz. Aada receive() 'e parametre olarak isimsiz ilev gnderildiine dikkat edin. Bu ilevlerin ama ve kapama parantezlerini sar ile belirtiyorum: import std.stdio; import std.concurrency; struct Sonlan {} void iilevi() { bool tamam_m = false; while (!tamam_m) {

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

551

E Zamanl Programlama

receive( (int mesaj) { writeln("int mesaj: ", mesaj); }, (string mesaj) { writeln("string mesaj: ", mesaj); }, (Sonlan mesaj) { writeln("kyorum"); tamam_m = true; });

void main() { auto ii = spawn(&iilevi); ii.send(10); ii.send(42); ii.send("merhaba"); ii.send(Sonlan());

80.6

Mesajlar belirli sreye kadar beklemek


Mesajlarn belirli bir sreden daha fazla beklenmesi istenmeyebilir. Gnderen i parac geici olarak megul olmu olabilir veya bir hata ile sonlanm olabilir. Mesaj bekleyen i paracnn belki de hi gelmeyecek olan bir mesaj sonsuza kadar beklemesini nlemek iin receiveTimeout() arlr. receiveTimeout() 'un ilk parametresi mesajn en ok ka milisaniye bekleneceini bildirir. Dn deeri de mesajn o sre iinde gelip gelmediini belirtir: Mesaj alndnda true , alnmadnda false . import std.stdio; import std.concurrency; import core.thread; void ii(Tid sahip) { Thread.sleep(dur!"seconds"(3)); sahip.send("merhaba"); } void main() { spawn(&ii, thisTid); writeln("mesaj bekliyorum"); bool alnd = false; while (!alnd) { alnd = receiveTimeout(600, (string mesaj) { writeln("geldi: ", mesaj); }); if (!alnd) { writeln("... imdilik yok"); } /* ... burada baka ilere devam edilebilir ... */

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

552

E Zamanl Programlama

Yukardaki sahip, gereken mesaj en fazla 600 milisaniye bekliyor. Mesaj o sre iinde gelmezse baka ilerine devam edebilir: mesaj bekliyorum ... imdilik yok ... imdilik yok ... imdilik yok ... imdilik yok geldi: merhaba Mesajn belirli sreden uzun srmesi sonucunda eitli durumlarda baka kararlar da verilebilir. rnein i paracnn beklenmedik bir ekilde sonlanm olduuna karar verilerek yeni bir i parac balatlabilir veya mesaj ge gelse bile artk bir anlam yoktur, vs.

80.7

Hatalar
std.parallelism modlnn ou olana, grevler srasnda atlan hatalar yakalar ve grevle ilgili bir sonraki ilem srasnda tekrar atmak zere saklar. Bylece rnein bir grevin ilemesi srasnda atlm olan hata daha sonra yieldForce() arldnda yakalanabilir: try { grev.yieldForce(); } catch (Exception hata) { writefln("grev srasnda bir hata olmu: '%s'", hata.msg); } Bu kolaylk std.concurrency 'de yoktur. Atlan olas bir hatann sahip i paracna iletilebilmesi iin aka yakalanmas ve bir mesaj halinde gnderilmesi gerekir. Aadaki hesap() ilevi string trnde mesajlar alyor; onlar double trne eviriyor, 0.5 deerini ekliyor ve sonucu bir mesaj olarak gnderiyor: void hesap(Tid sahip) { while (true) { auto mesaj = receiveOnly!string(); sahip.send(to!double(mesaj) + 0.5); } } Yukardaki to!double() , "merhaba" gibi double 'a dntrlemeyen bir dizgi ile arldnda hata atar. Atlan o hata hesap() 'dan hemen klmasna neden olacandan aadaki mesajdan yalnzca birincisinin yant alnabilir: import std.stdio; import std.concurrency; import std.conv; // ... void main() { Tid hesap = spawn(&hesap, thisTid); hesap.send("1.2");

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

553

E Zamanl Programlama

hesap.send("merhaba"); hesap.send("3.4");

// hatal veri

foreach (i; 0 .. 3) { auto mesaj = receiveOnly!double(); writefln("sonu %s: %s", i, mesaj); }

Bu yzden, sahip 1.2'nin sonucunu 1.7 olarak alr ama ii sonlanm olduundan bir sonraki mesaj alamaz: sonu 0: 1.7 hi gelmeyecek olan mesaj bekleyerek taklr Hesap i paracnn bu konuda yapabilecei bir ey, kendi ilemleri srasnda atlabilecek olan hatay try-catch ile yakalamak ve zel bir mesaj olarak iletmektir. Program hatann nedenini bir HesapHatas nesnesi olarak gnderecek ekilde aada deitiriyoruz. Ek olarak, i paracnn sonlanmas da zel Sonlan tr ile salanyor: import std.stdio; import std.concurrency; import std.conv; struct HesapHatas { string neden; } struct Sonlan {} void hesap(Tid sahip) { bool tamam_m = false; while (!tamam_m) { receive( (string mesaj) { try { sahip.send(to!double(mesaj) + 0.5); } catch (Throwable hata) { sahip.send(HesapHatas(hata.msg)); }

},

(Sonlan) { tamam_m = true; });

void main() { Tid hesap = spawn(&hesap, thisTid); hesap.send("1.2"); hesap.send("merhaba"); hesap.send("3.4"); hesap.send(Sonlan()); // hatal veri

foreach (i; 0 .. 3) { writef("sonu %s: ", i); receive(

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

554

E Zamanl Programlama

(double mesaj) { writeln(mesaj); }, (HesapHatas hata) { writefln("HATA! '%s'", hata.neden); });

Hatann nedeninin "rakam bulunamad" anlamna gelen "no digits seen" olduunu bu sefer grebiliyoruz: sonu 0: 1.7 sonu 1: HATA! 'no digits seen' sonu 2: 3.9 Bu konuda dier bir yntem, iinin yakalad hatann olduu gibi sahibe gnderilmesidir. Ayn hata sahip tarafnda kullanlabilecei gibi, tekrar atlabilir de: // ... ii tarafta ... try { // ... } catch (Throwable hata) { sahip.send(cast(shared Throwable)hata); }}, // ... sahip tarafta ... receive( // ... (shared Throwable hata) { throw hata; }); Yukardaki shared belirtelerine neden gerek olduunu aada anlatyorum.

80.8

Veri paylam
Yukardaki yntemler i paracklarnn mesajlaarak bilgi al veriinde bulunmalarn salyordu. Daha nce de sylediim gibi, programn doru almas asnda gvenli olan yntem odur. Dier yntem, i paracklarnn ayn verilere dorudan erimelerine dayanr. paracklar ayn veriyi dorudan okuyabilirler ve deitirebilirler. rnein sahip, iiyi bool bir deikenin adresi ile balatabilir ve ii de sonlanp sonlanmayacan dorudan o deikenin deerini okuyarak anlayabilir. Baka bir rnek olarak, sahip bir ka tane i paracn hesaplarnn sonularn ekleyecekleri bir deikenin adresi ile balatabilir ve iiler de o deikenin deerini arttrabilirler. Veri paylamnn gvenli olmamasnn bir nedeni, i paracklarnn veriyi okurken ve deitirirken birbirlerinden habersizce yar halinde (race condition) olmalardr. letim sisteminin i paracklarn ne zaman duraksataca ve ne zaman tekrar balataca konusunda hibir tahminde bulunulamayacandan programn davran bu yzden son derece kark olabilir. Hatta, bilgisayar bilimi veri paylamna dayanan e zamanl programlarn doruluklarnn kantlanamayacan kantlar.

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

555

E Zamanl Programlama

Bu balk altnda kullanacam rnekleri fazlaca basit ve anlamsz bulabilirsiniz. Bu ramen, veri paylamnn burada greceimiz sakncalar gerek programlarda da bulunur. ou dilin tersine, D'de i paracklar verileri aka paylaamazlar. rnein, sonlanmasn bildirebilmek iin iiye bool trndeki bir deikenin adresini gndermeye alan aadaki kod D'de derlenemez: import std.concurrency; void ii(bool * devam_m) { while (*devam_m) { // ... } } void main() { bool devam_m = true; spawn(&ii, &devam_m); // ... // Daha sonra ii'nin sonlanmasn salamak iin devam_m = false; } // ...

// derleme HATASI

src/phobos/std/concurrency.d(329): Error: static assert "Aliases to mutable thread-local data not allowed." std.concurrency modlndeki bir static assert ifadesinden kaynaklanan yukardaki hata, i paracklarnn deiebilen (mutable) verilerinin referanslarnn baka i paracklarna geirilemeyeceini bildirir. main() iindeki devam_m deiebilen bir veri olduundan ona eriim salayan adresi hibir i paracna geirilemez. te yandan, deimez verilerin adreslerini i paracklarna geirmekte bir saknca yoktur nk o veriler kesinlikle deiemeyeceklerinden verinin deeri konusunda bir belirsizlik olamaz: import std.stdio; import std.concurrency; import core.thread; void ii(immutable int * veri) { writeln("veri: ", *veri); } void main() { immutable int bilgi = 42; spawn(&ii, &bilgi); } thread_joinAll();

// derlenir

bilgi 'nin yaam main() ile snrl olduundan, ona erimekte olan i parac sonlanmadan main() 'den klmamaldr. Programn sonunda ardm core.thread.thread_joinAll() , iilerin tamamlanmalarn bekler. ii() iledii srece bilgi geerli kalr. Yukardaki program bu sefer derlenir ve beklenen kty retir:

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

556

E Zamanl Programlama

veri: 42

80.8.1

Veri paylamak iin shared


Gerektiinde deiebilen veriler de paylalabilirler. Bu durumda programn davrannn doruluunu salamak bazen ok g olsa da programcnn sorumluluundadr. Ortaklaa eriilecek olan verilerin shared belirteciyle tanmlanmalar yeterlidir: import std.stdio; import std.concurrency; import core.thread; void deiTokuu(shared int * birinci, shared int * ikinci) { foreach (saya; 0 .. 10_000) { int geici = *ikinci; *ikinci = *birinci; *birinci = geici; } } void main() { shared int i = 1; shared int j = 2; writefln("nce : %s ve %s", i, j); foreach (adet; 0 .. 10) { spawn(&deiTokuu, &i, &j); } // Btn ilemlerin bitmesini bekliyoruz thread_joinAll(); } writefln("sonra: %s ve %s", i, j);

Yukardaki program artk derlenir ama yanl alr. Programda 10 tane i parac balatlyor. Bu i paracklar kendilerine verilen iki gsterge ile eritikleri iki int 'in deerlerini dei toku ediyorlar. Hepsi de main() iindeki i ve j isimli ayn deikenlere eritiklerinden farknda olmadan birbirlerinin ilerini bozuyorlar. Yukardaki programdaki toplam dei toku adedi 10 arp 10 bindir. Bu deer bir ift say olduundan, i 'nin ve j 'nin deerlerinin program sonunda yine balangtaki gibi 1 ve 2 olmalarn bekleriz: nce : 1 ve 2 sonra: 1 ve 2

beklenen sonu

Program baka zamanlarda veya ortamlarda gerekten de o sonucu retebilir, ama benim denediim ortamda rasgele olarak aadaki iki ktdan birisini retiyor: nce : 1 ve 2 sonra: 1 ve 1

yanl sonu

nce : 1 ve 2 sonra: 2 ve 2

yanl sonu

Baka zamanlarda sonu "2 ve 1" de kabilir.

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

557

E Zamanl Programlama

Bunun nedenini A ve B olarak isimlendireceimiz yalnzca iki i paracnn ileyiiyle bile aklayabiliriz. letim sistemi i paracklarn belirsiz zamanlarda duraksatp tekrar balatt iin bu iki i parac, verileri birbirlerinden habersiz olarak aadaki biimde deitirebilirler. Balangta i 'nin 1 ve j 'nin 2 olduklar bir duruma bakalm. Ayn deiTokuu() ilevini ilettikleri halde A ve B i paracklarnn yerel geici deikenleri farkldr. Ayrt edebilmek iin onlar aada geiciA ve geiciB olarak belirtiyorum. Her iki i paracnn iletti ayn 3 satrlk kodun zaman ilerledike nasl iletildiklerini de yukardan aaya doru gsteriyorum: 1 numaral ilem ilk ilem, 6 numaral ilem de son ilem. Her ilemde i ve j 'den hangisinin deitiini de saryla iaretliyorum:
Zaman 1: 2: parac A int geici = *ikinci; (geiciA==2) *ikinci = *birinci; (i==1, j==1) parac B

(A duraksatlm ve B balatlm olsun) 3: 4: int geici = *ikinci; (geiciB==1) *ikinci = *birinci; (i==1, j==1) (B duraksatlm ve A tekrar balatlm olsun) 5: *birinci = geici; (i==2, j==1)

(A duraksatlm ve B tekrar balatlm olsun) 6: *birinci = geici; (i==1, j==1)

Yukardaki, hatay aklamaya yeten yalnzca bir durumdur. Onun yerine 10 i paracnn etkileimlerinden oluan ok sayda baka karmak durum da dnlebilir.
80.8.2

Veri korumak iin synchronized


Yukardaki hatal durum, ayn verinin birden fazla i parac tarafndan okunmas ve yazlmas nedeniyle olumaktadr. Bu tr hatalar nlemenin yolu, belirli bir anda yalnzca tek i parac tarafndan iletilmesi gereken kod blounu synchronized olarak iaretlemektir. Yukardaki programda yaplacak tek deiiklik, o programn doru sonu retmesi iin yeterlidir: foreach (saya; 0 .. 10_000) { synchronized { int geici = *ikinci; *ikinci = *birinci; *birinci = geici; } } kts: nce : 1 ve 2 sonra: 1 ve 2

doru sonu

synchronized , isimsiz bir kilit oluturur ve bu kilidi belirli bir anda yalnzca tek i paracna verir. Yukardaki kod blou da bu sayede belirli bir anda tek i parac tarafndan iletilir ve i ve j 'nin deerleri her seferinde doru olarak dei toku edilir. Kullanaca kilit veya kilitler synchronized 'a aka da verilebilir. Bu, belirli bir anda farkl bloklardan yalnzca birisinin ilemesi gereken durumlarda gerekir. Bunun bir rneini grmek iin aadaki programa bakalm. Bu programda paylalan veriyi deitiren iki kod blou bulunuyor. Bu bloklar ayn int trndeki deikenin adresi ile aracaz. Birisi bu deikenin deerini arttracak, dieri ise azaltacak:

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

558

E Zamanl Programlama

void arttrc(shared int * deer) { foreach (saya; 0 .. 1000) { ++(*deer); } } void azaltc(shared int * deer) { foreach (saya; 0 .. 1000) { --(*deer); } } Ayn veriyi deitiren bu iki blou da synchronized olarak iaretlemeyi dnebiliriz. Ancak bu bloklar farkl olduklarndan, her birisi farkl bir kilit ile korunur ve deikenin tek i parac tarafndan deitirilmesi yine salanamam olur: import std.stdio; import std.concurrency; import core.thread; void arttrc(shared int * deer) { foreach (saya; 0 .. 1000) { synchronized { // bu kilit aadakinden farkldr ++(*deer); } } } void azaltc(shared int * deer) { foreach (saya; 0 .. 1000) { synchronized { // bu kilit yukardakinden farkldr --(*deer); } } } void main() { shared int ortak = 0; foreach (i; 0 .. 100) { spawn(&arttrc, &ortak); spawn(&azaltc, &ortak); } thread_joinAll(); writeln("son deeri: ", ortak);

Eit sayda arttrc ve azaltc i parac balatlm olduundan ortak isimli deikenin son deerinin sfr olmasn bekleriz ama byk olaslkla sfrdan farkl kar: son deeri: -3325 sfr deil

Farkl bloklarn ayn kilidi (veya kilitleri) paylamalar iin kilidin (veya kilitlerin) synchronized 'a parantez iinde bildirilmesi gerekir: synchronized (kilit_nesnesi, baka_kilit_nesnesi, ...) D'de zel bir kilit nesnesi yoktur, herhangi bir snf trnn herhangi bir nesnesi kilit olarak kullanlabilir. Yukardaki programdaki i paracklarnn ayn kilidi kullanmalar iin main()

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

559

E Zamanl Programlama

iinde bir nesne oluturulabilir ve onu i paracklarna parametre olarak gnderebiliriz. Programn deien yerlerini iaretliyorum: import std.stdio; import std.concurrency; import core.thread; class Kilit {} void arttrc(shared int * deer, shared Kilit kilit) { foreach (saya; 0 .. 1000) { synchronized (kilit) { ++(*deer); } } } void azaltc(shared int * deer, shared Kilit kilit) { foreach (saya; 0 .. 1000) { synchronized (kilit) { --(*deer); } } } void main() { shared Kilit kilit = new shared(Kilit)(); shared int ortak = 0; foreach (i; 0 .. 100) { spawn(&arttrc, &ortak, kilit); spawn(&azaltc, &ortak, kilit); } thread_joinAll(); writeln("son deeri: ", ortak);

Btn i paracklar main() iinde tanmlanm olan ayn kilidi kullandklarndan belirli bir anda bu iki synchronized bloundan yalnzca bir tanesi iletilir ve sonu beklendii gibi sfr kar: son deeri: 0 doru sonu

Bu dersi yazdm srada kullandm dmd 2.053, D'nin veri paylamna dayanan e zamanl programlama olanaklarnn hepsini desteklemiyor. Bu olanaklardan bazlarn fazla ayrntya girmeden gstereceim. Snf trleri synchronized olarak iaretlenebilirler. Bunun anlam, o trn btn ye ilevlerinin o trn belirli bir nesnesi ile ifade edilen ayn kilidi kullanacaklardr: synchronized class Snf { void foo() { // ... } void bar() { // ...

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

560

E Zamanl Programlama

synchronized olarak iaretlenen trlerin btn ye ilevleri nesnenin kendisini kilit olarak kullanrlar. Yukardaki snfn edeeri aadaki snftr: class Snf { void foo() { synchronized (this) { // ... } } void bar() { synchronized (this) { // ... } }

Birden fazla nesnenin kilitlenmesi gerektiinde btn nesneler ayn synchronized deyimine yazlmaldrlar. Aksi taktirde farkl i paracklar farkl nesnelerin kilitlerini ele geirmi olabileceklerinden, sonsuza kadar birbirlerini bekleyerek taklp kalabilirler. Bunun tannm bir rnei, bir banka hesabndan dierine para aktaran ilevdir. Byle bir ilemin hatasz gereklemesi iin her iki banka hesabnn da kilitlenmesinin gerekecei aktr. Bu durumda yukarda grdmz synchronized kullanmn aadaki gibi uygulamak hatal olur: void paraAktar(shared BankaHesab kimden, shared BankaHesab kime) { synchronized (kimden) { // HATALI synchronized (kime) { // ... } } } Yanlln nedenini yle basit bir durumla aklayabiliriz: Bir i paracnn A hesabndan B hesabna para aktardn, baka bir i paracnn da B hesabndan A hesabna para aktardn dnelim. letim sisteminin i paracklarn belirsiz zamanlarda duraksatmas sonucunda; kimden olarak A hesabn ilemekte olan i parac A nesnesini, kimden olarak B nesnesini ilemekte olan i parac da B nesnesini kilitlemi olabilir. Bu durumda her ikisi de dierinin elinde tuttuu nesneyi kilitlemeyi bekleyeceklerinden sonsuza kadar taklp kalacaklardr. Bu sorunun zm synchronized deyiminde birden fazla nesne belirtmektir: void paraAktar(shared BankaHesab kimden, shared BankaHesab kime) { synchronized (kimden, kime) { // doru // ... } } Derleyici ya nesnelerin ikisinin birden kilitleneceini ya da hibirisinin kilitlenmeyeceini garanti eder.

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

561

E Zamanl Programlama

80.9

zet
paracklarnn birbirlerine bal olmadklar durumlarda std.parallelism 'in sunduu kout programlamay kullann. Ancak i paracklar birbirlerine bal olduklarnda std.concurrency 'nin sunduu e zamanl programlamay dnn. E zamanl programlama gerekten gerektiinde mesajlamay yeleyin nk veri paylam eitli program hatalarna aktr. spawn() i parac balatr. thisTid bu i paracnn kimliidir. send() baka bir i paracna mesaj gnderir. receiveOnly() , receive() ve receiveTimeout() mesaj bekler. Sahip, iinin att hatay otomatik olarak yakalayamaz. thread_joinAll() , balatlm olan btn i paracklarnn tamamlanmalarn bekler. Yalnzca immutable ve shared olan veriler paylalabilirler. synchronized belirli bir kod blounun belirli bir anda tek i parac tarafndan iletilmesini salar. Bir snf tr synchronized olarak tanmlandnda, belirli bir nesnesi zerinde belirli bir anda ye ilevlerinden yalnzca birisi iletilir.

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

562

Tr Nitelikleri

81

Tr Nitelikleri
__traits anahtar szc ve std.traits modl, trlerin niteliklerine ynelik olan st dzey olanaklar sunarlar. ounlukla ktphaneler yazarken yararl olan bu olanaklar baka derlemeli dillerde ya hi bulunmazlar ya da C++'ta olduu gibi kullanmlar ok zordur. __traits 'in sunduu bilgiler dilin baka olanaklar tarafndan edinilemeyen ve ounlukla derleyicinin toplam olduu bilgilerdir. Bu bilgiler zellikle ablon kodlarnda ve koullu derleme srasnda yararl olabilirler. rnein yalnzca aritmetik trlerle ileyebilen bir ilev ablonu, "aritmetik mi" anlanma gelen isArithmetic 'i ablon kstlamasnda yle kullanabilir: void birlev(T)() if (__traits(isArithmetic, T)) { // ... } Baka bir rnek olarak, T gibi bir ablon parametresinin aritmetik bir tr olup olmamasna gre kod farkl biimde gerekletirilebilir: static if (__traits(isArithmetic, T)) { /* ... aritmetik bir trm ... */ } else { /* ... deilmi ... */ }

81.1

__traits
__traits , derleyicinin koddan edinmi olduu bilgileri sorgulamaya yarar. Sz dizimi aadaki gibidir: __traits(szck, parametreler) szck, aada listelenen szcklerden birisidir ve __traits 'in hangi amala kullanldn belirler. parametreler ise bir veya daha fazla sayda olmak zere tr ismi veya ifadedir. Parametrelerin anlamlar kullanlan szce baldr.

81.1.1

__traits szckleri
Bu szckler sorgulanmakta olan bilgiyi belirlerler. Aadaki rneklerdeki trler zaten bariz olduklarndan __traits 'in yarar bu blmde tam olarak anlalamyor. rnein ulong 'un aritmetik bir tr olup olmadn __traits ile sorgulamaya gerek yoktur. Bu szckler zellikle ablon kodlarnda ve ounlukla T olarak isimlendirdiimiz trler hakknda bilgi edinmek iin yararldrlar. Ek olarak, aadaki ou rnekte parametre olarak trler kullandm halde aslnda ou durumda herhangi bir ifade de kullanlabilir. isAbstractClass : Soyut snflar iin true , dierleri iin false retir. import std.stdio; abstract class BtnlevleriSoyutSnf {

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

563

Tr Nitelikleri

void foo();

class EnAzBirleviSoyutSnf { abstract void foo(); } class SomutSnf { void foo() {} } void main() { writeln(__traits(isAbstractClass, BtnlevleriSoyutSnf)); writeln(__traits(isAbstractClass, EnAzBirleviSoyutSnf)); } writeln(__traits(isAbstractClass, SomutSnf));

true true false isArithmetic : Aritmetik trler iin true retir. struct renci {} // ... writeln(__traits(isArithmetic, ulong)); writeln(__traits(isArithmetic, string)); writeln(__traits(isArithmetic, renci));

true false false isAssociativeArray : Eleme tablolar iin true retir. writeln(__traits(isAssociativeArray, int[string])); writeln(__traits(isAssociativeArray, int[42]));

true false isFinalClass : Kendilerinden tretilemeyen snflar iin true retir. class TretilebilenSnf {} final class TretilemeyenSnf {} // ...

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

564

Tr Nitelikleri

writeln(__traits(isFinalClass, TretilebilenSnf)); writeln(__traits(isFinalClass, TretilemeyenSnf));

false true isFloating : Kesirli saylar iin true retir. writeln(__traits(isFloating, double)); writeln(__traits(isFloating, 7));

true false isIntegral : Tamsaylar iin true retir. writeln(__traits(isIntegral, double)); writeln(__traits(isIntegral, 7));

false true isScalar : Bykl olan trler iin true retir. Aritmetik trler olmadklar halde gstergelerin de byklkleri vardr: writeln(__traits(isArithmetic, int*)); writeln(__traits(isScalar, int*));

false true isStaticArray : Sabit uzunluklu diziler iin true retir. writeln(__traits(isStaticArray, int[10])); writeln(__traits(isStaticArray, int[]));

true false isUnsigned : aretsiz trler iin true retir. writeln(__traits(isUnsigned, ulong)); writeln(__traits(isUnsigned, long));

true false isVirtualFunction : Snf ilevleri iin true retir. struct Yap { void foo() {}

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

565

Tr Nitelikleri

} class Snf { void foo() {} } // ... writeln(__traits(isVirtualFunction, Yap.foo)); writeln(__traits(isVirtualFunction, Snf.foo));

false true isAbstractFunction : abstract ilevler iin true retir. class Snf { abstract void altSnflardaTanmlanmaldr(); } // ... writeln(__traits(isAbstractFunction, Snf.altSnflardaTanmlanmaldr));

true isFinalFunction : final ilevler iin true retir. class Snf { final void tanmDeitirilemez() {} } // ... writeln(__traits(isFinalFunction, Snf.tanmDeitirilemez));

true isStaticFunction : static ilevler iin true retir. class Snf { static void nesneyesineErimesiGerekmeyenlev() {} } // ... writeln( __traits(isStaticFunction, Snf.nesneyesineErimesiGerekmeyenlev));

true

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

566

Tr Nitelikleri

isRef : ref parametreler iin true retir. void birlev(ref int refParametre, out int outParametre, lazy int lazyParametre) { writeln(__traits(isRef, refParametre)); // ... }

true isOut : out parametreler iin true retir. void birlev(ref int refParametre, out int outParametre, lazy int lazyParametre) { writeln(__traits(isOut, outParametre)); // ... }

true isLazy : lazy parametreler iin true retir. void birlev(ref int refParametre, out int outParametre, lazy int lazyParametre) { writeln(__traits(isLazy, lazyParametre)); // ... }

true hasMember : Trn belirtilen yesi varsa true retir. struct BirTr { int birye; void birlev() {}

// ... writeln(__traits(hasMember, BirTr, "birye")); writeln(__traits(hasMember, BirTr, "birlev")); writeln(__traits(hasMember, BirTr, "bulunmayanye"));

true true false identifier : Bir ifadenin programdaki ismini retir.

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

567

Tr Nitelikleri

rnein bir alias ablon parametresinin yerine kullanlm olan asl isim elde edilebilir: struct BirYap(alias T) { static string bilgi = __traits(identifier, T) ~ " ile kullanldm"; void bilgiVer() { writeln(bilgi); }

// ... int birDeiken; auto nesne = BirYap!birDeiken(); nesne.bilgiVer();

birDeiken ile kullanldm getMember : Belirtilen yeye eritirir. struct Yap { int i; } // ... auto nesne = Yap(); // nesne.i = 42 ifadesinin edeeri: __traits(getMember, nesne, "i") = 42; // writeln(nesne.i) ifadesinin edeeri: writeln(__traits(getMember, nesne, "i"));

42 getOverloads : Belirtilen ilevin yklemelerinden oluan dizi retir. struct Yap { void foo() {} double foo(int i) { return i; }

// ... foreach (i, ykleme; __traits(getOverloads, Yap, "foo")) { writefln("%s: %s", i, typeid(typeof(ykleme))); }

0: void() 1: double()

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

568

Tr Nitelikleri

getVirtualFunctions : Belirtilen ilevin, tanmlar alt snflar tarafndan deitirilebilen yklemelerinden oluan dizi retir. class Snf { void foo() {} double foo(int i) { return i; }

// ... foreach (i, ykleme; __traits(getVirtualFunctions, Snf, "foo")) { writefln("%s: %s", i, typeid(typeof(ykleme))); }

0: void() 1: double() parent : Belirtilen ismi ieren ortam retir. void foo(alias deiken)() { writeln(typeid(typeof(__traits(parent, deiken)))); } void main() { double ieren() { int i; foo!i(); } } return 0;

ieren();

foo() 'nun ablon parametresi olarak kullanlan i 'yi ieren ortamn tr yazdrlr: double() classInstanceSize : Snf nesnesinin bykln retir. class Snf { int[10] dizi; } // ... writeln("deikenin bykl: ", Snf.sizeof); writeln("nesnenin bykl : ", __traits(classInstanceSize, Snf)); Snf deikenleri yalnzca bir referans olduklarndan ou durumda snf nesnelerinden kktrler:

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

569

Tr Nitelikleri

deikenin bykl: 4 nesnenin bykl : 48 allMembers : Btn yelerin isimlerinden oluan dizi retir. class stSnf { double stSnfyesi; void stSnflevi() {}

class AltSnf : stSnf { int altSnfyesi; void altSnflevi() {}

// ... foreach (ye; __traits(allMembers, AltSnf)) { writeln(ye); }

altSnfyesi altSnflevi stSnfyesi stSnflevi toString toHash opCmp opEquals Monitor factory derivedMembers : Alt snf yelerinden oluan dizi retir. foreach (ye; __traits(derivedMembers, AltSnf)) { writeln(ye); }

altSnfyesi altSnflevi isSame : ki parametre de ayn isim iseler true retir. void foo(alias isim0, alias isim1)() { writeln(__traits(isSame, isim0, isim1)); } // ... int i; foo!(i, i)();

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

570

Tr Nitelikleri

true compiles : Parametreleri derlenebiliyorsa true retir. struct Yap { int foo() { return 0; } } class Snf { double bar() { return 1.5; } } class BakaSnf { void bar() {} } void birlev(T0, T1)(T0 birinci, T1 ikinci) { static if (__traits(compiles, birinci.foo() / ikinci.bar())) { writeln("blme ilemi uygulanabiliyor"); } else { writeln("blme ilemi uygulanamyor"); }

// ... auto yap = Yap(); auto snf = new Snf(); auto bakaSnf = new BakaSnf(); birlev(yap, snf); birlev(yap, bakaSnf); Snf.bar 'n dn tr double olduu iin blme ilemi derlenebilir ama BakaSnf.bar 'n dn tr void olduu iin derlenemez: blme ilemi uygulanabiliyor blme ilemi uygulanamyor

81.1.2

Ne zaman kullanmal
Bu olanaklardan ounlukla ablon ieren ktphanelerde yararlanlr. isArithmetic , isIntegral , hasMember ve compiles gibi basit olanlarn dndakilerle gnlk programlamada fazla karlalmaz.

81.2

std.traits modl
Bu modldeki ablonlar, __traits 'in rettii bilgilerin benzerlerini ktphane olanaklar olarak ve yine derleme zamannda retirler.

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

571

Tr Nitelikleri

Burada rnek olarak yalnzca karakter ve dizgi trleriyle ilgili olanlarn gstereceim. Bu modlde ok sayda baka olanak da bulunur. isSomeChar : Bir karakter tryse true retir. import std.traits; void foo(T)(T parametre) if (isSomeChar!T) { // ... } // ... foo('a'); foo(42); // derlenir // derleme HATASI

isSomeString : Herhangi bir dizgi tryse true retir. static if (isSomeString!T) { // ... bir dizgi trym ... } else { // ... deilmi ... } isNarrowString : char ve wchar dizgileri iin true retir. void foo(T)(T parametre) if (isNarrowString!T) { // ... } std.traits modlnn dier olanaklar da benzer biimde kullanlrlar.

81.3

zet
__traits ve std.traits trler hakknda derleme srasnda bilgi edinmeye yararlar. zellikle __traits , derleyicinin derleme srasnda toplam olduu bilgileri edinmeye yarayan gl bir olanaktr.

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

572

Bellek Ynetimi

82

Bellek Ynetimi
Bu blmde p toplaycy tantacam ve ondan alnan bellek blgelerine deikenlerin nasl yerletirilebildiklerini gstereceim. imdiye kadarki programlarda hi bellek ynetimiyle ilgilenmek zorunda kalmadk. D, programlarn byk bir ounluu asndan bellek ynetimi gerektirmeyen bir dildir. Burada anlatlanlara byk olaslkla hi ihtiya duymayacaksnz. Buna ramen D gibi sistem dillerinde alt dzey bellek ilemlerini ynetmek gerekebilir. nceki baz blmlerde olduu gibi, aada ksaca yalnzca deiken yazdm yerlerde yap ve snf nesneleri de dahil olmak zere her trden deikeni kastedeceim.

82.1

Bellek
Bellek hem programn kendisini hem de kulland verileri barndrr. Bu yzden dier bilgisayar kaynaklarndan daha nemlidir. Bu kaynak temelde iletim sistemine aittir. letim sistemi bellei programlarn ihtiyalar dorultusunda paylatrr. Her programn kulland bellek, o programn belirli zamanlardaki ihtiyalar dorultusunda artabilir veya azalabilir. Belirli bir programn kulland bellek, o program sonlandnda tekrar iletim sistemine geer. Bellek, deikenlerin aklda tutulmak iin kullanld bir defter gibi dnlebilir. Her deiken bellekte belirli bir yere yazlr. Her deikenin deeri gerektike ayn yerden okunur ve kullanlr. Yaam sona eren deikenlerin yerleri daha sonradan baka deikenler iin kullanlmak zere geri alnr. Bellekle ilgili deneyler yaparken deikenlerin adres deerlerini veren & ilecinden yararlanabiliriz. rnein aadaki programdaki iki deikenin adresleri & ileciyle yazdrlyor: import std.stdio; void main() { int i; int j; writeln("i: ", &i); writeln("j: ", &j);

Not: Adresler programn her altrlnda byk olaslkla farkl olacaktr. Ek olarak, adres deerini edinmi olmak, normalde mikro ilemci yazmacnda yaayacak olan bir deikenin bile bellekte yaamasna neden olur. kts: i: 7FFF2B633E28 j: 7FFF2B633E2C Adreslerin son hanelerine bakarak i 'nin bellekte j 'den hemen nce bulunduunu grebiliyoruz: 8'e int 'in bykl olan 4' eklersek on altl say dzeninde C elde edilir.

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

573

Bellek Ynetimi

82.2

p toplayc
D programlarndaki dinamik deikenler p toplaycya ait olan bellek blgelerinde yaarlar. Yaamlar sona eren deikenler p toplaycnn ilettii bir algoritma ile sonlandrlrlar. Bu deikenlerin yerleri tekrar kullanlmak zere geri alnr. Bu ileme aada bazen p toplama, bazen de temizlik diyeceim. p toplaycnn ilettii algoritma ok kabaca yle aklanabilir: Program yt ile ilgili olan btn blgeler taranr ve o blgelerdeki deikenler araclyla dorudan veya dolayl olarak eriilebilen btn bellek blgeleri belirlenir. Eriilebilen blgelerin hl kullanmda olduklarna karar verilir. Dier bellek blgelerindeki deikenlerin sonlandrclar iletilir ve o bellek blgeleri sonradan baka deikenler iin kullanlmak zere geri alnr. Temizlik ilemlerinin hangi srada iletildikleri belirsizdir. rnein nesnelerin referans trndeki yeleri kendilerini barndran nesneden daha nce sonlanm olabilirler. Bu yzden, referans trnden olan yelerin sonlandrc ilevler iinde kullanlmalar hataldr. Bu, sonlanma sralarnn kesin olarak belirli olduu C++ gibi baz dillerden ok farkldr. Temizlik ilemleri, bo yerin azalmaya balamas gibi nedenlerle ve nceden kestirilemeyecek zamanlarda iletilebilir. Temizlik ilemleri devam ederken yeni yer ayrlmas p toplama dzeneinde karklk yaratabileceinden, programa ait olan btn i paracklar temizlik srasnda ksa sreliine durdurulabilirler. Bu yzden programn tutukluk yapt hissedilebilir. Normalde programcnn p toplaycnn iine karmas gerekmese de p toplaycnn temizlik ilemlerinin hemen iletilmeleri veya ertelenmeleri salanabilir. p toplaycnn olanaklar core.memory modlnde tanmlanmtr.

82.2.1

Temizlik ilemleri
Temizlik ilemleri programn ilerideki belirsiz bir zamanda durdurulmasna neden olmak yerine daha uygun olduu dnlen bir zamanda GC.collect() ile balatlabilir: import core.memory; // ... GC.collect(); // temizlik balatr

Bazen, programn ileyiinin hz asndan hassas olduu noktalarda p toplaycnn ilemesi istenmeyebilir. GC.disable() temizlik ilemlerini erteler, GC.enable() da tekrar etkinletirir: GC.disable(); // ... tutukluk hissedilmeden ilemesi gereken ilemler ... GC.enable(); Ancak, temizlik ilemlerinin kesinlikle iletilmeyecekleri garanti deildir: p toplayc bellein ok azaldn farkettii durumlarda bo yer bulmak iin yine de iletebilir. p toplayc bo kalan bellek blgelerini iletim sistemine geri vermez ve ileride oluturulacak olan deikenler iin elinde tutmaya devam eder. Bunun bir sorun oluturduunun bilindii programlarda bo bellek blgeleri GC.minimize() ile iletim sistemine geri verilebilir:

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

574

Bellek Ynetimi

GC.minimize();

82.3

Deikenler ve bellek blgeleri


Yaam sreleri asndan eit deiken vardr: Otomatik Dinamik Modlde veya trde tek (statik) Bu eit deiken farkl bellek blgelerinde yaarlar.

82.3.1

Otomatik deikenler iin program yt


Baz deikenlerin bellekteki yerleri derleyicinin oluturduu kodlar tarafndan program ytnda ve otomatik olarak ayrlr. Program yt mikro ilemcinin sunduu bir olanaktr ve deikenler iin yer ayrma konusunda en hzl dzenektir. Aadaki listedeki deikenler otomatiktir: new ile oluturulmayan yerel deikenler lev parametreleri lev dn deerleri lemler srasnda otomatik olarak oluturulan geici deikenler Otomatik deikenlerin yerleri hibir anahtar szck veya ilev ars gerekmeden otomatik olarak ayrlr. Aadaki kodda sar ile iaretlediim btn deikenler otomatiktir: int ilev(int parametre) { int yerel; Yap nesne; int hesap = bakalev(parametre + yerel); return hesap * 2; } bakalev 'e parametre olarak gnderilen parametre + yerel ve ilevin dn deeri olan hesap * 2 ifadelerinin geici deerleri de otomatik deikenlerle ayn eittendir; onlarn yerleri de program ytndan otomatik olarak ayrlr. Not: Derleyici aslnda baz deikenler iin program ytnda hi yer ayrmayabilir; onlar ok daha hzl olan mikro ilemci yazmalarnda gerekletirebilir. Buna ramen yaam sreci bakmndan o deikenler yine de otomatiktirler. std.typecons.scoped ile oluturulmu olan snf nesneleri clear ve scoped dersinde grdmz scoped , normalde new anahtar szc ile oluturulan ve bu yzden dinamik olan snf nesnelerinin otomatik olmalarn salyordu: auto nesne = scoped!BirSnf(42); Yukardaki nesne de program ytnda yer alr. Otomatik deikenlerin bellekte bir arada bulunduklarn adres deerlerinin birbirlerine yakn olmalarna bakarak grebiliriz:

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

575

Bellek Ynetimi

import std.stdio; struct Yap { int ye; } void main() { int yerel; Yap yapNesnesi; writeln("yerel: ", &yerel); writeln("nesne: ", &yapNesnesi); writeln("ye : ", &yapNesnesi.ye);

kts, adreslerin birbirlerine olduka yakn olduklarn gsteriyor: yerel: 7FFF9FD6AFB8 nesne: 7FFF9FD6AFBC ye : 7FFF9FD6AFBC Yap nesnesinin ve tek yesinin ayn adreste bulunduunu da gryorsunuz. Yap deikenleri iin yelerinden baka yer ayrlmasna gerek yoktur. lk yesinin adresi, her zaman iin nesnenin de adresidir. O deikenlerin adres deerlerinden anlaldna gre, main() almaya baladnda program yt kabaca 7FFF9FD6A000 adresi yaknlarndaym. Ayn rnei yap yerine snf kullanacak ekilde deitirelim: import std.stdio; class Snf { int ye; } void main() { int yerel; auto snfDeikeni = new Snf; writeln("yerel : ", &yerel); writeln("deiken: ", &snfDeikeni); writeln("ye : ", &snfDeikeni.ye);

Bu sefer yerel deiken ve snf deikeninin ayn blgede, new ile oluturulan snf nesnesinin ise ok farkl bir yerde bulunduu grlyor: yerel : 7FFF2D7E6FE0 deiken: 7FFF2D7E6FE8 ye : 7FF2C0566F30 Bu, snf deikeni ile snf nesnesi kavramlarnn farkl olmalarndandr. Snf deikeni ytta olduu halde, new ile ayrlm olduu iin nesne dinamik bellek blgesindedir.

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

576

Bellek Ynetimi

82.3.2

Dinamik deikenler iin dinamik bellek blgesi


Dinamik deikenler new anahtar szc ile oluturulan deikenlerdir. new 'n aslnda her trden deiken olutururken kullanlabildiini Gstergeler dersinde grmtk. new ; snflarda oluturulmu olan nesneye eriim salayan bir snf deikeni, dier trlerde ise oluturulmu olan deikene eriim salayan bir gsterge dndrr: Snf snfDeikeni = new Snf; int * intGstergesi = new int; Yap * nesneGstergesi = new Yap; // snf deikeni // gsterge // gsterge

Dinamik deikenler p toplaycya aittir. Onlarn programc tarafndan sonlandrlmalar gerekmez. Artk kendisine hibir yolla eriilemeyen dinamik deikenler p toplayc tarafndan ilerideki belirsiz bir zamanda sonlandrlrlar. (Not: stendiinde nesneler clear() ile de sonlandrlabilirler. Bunu aada gstereceim.) Sonlandrlm olan dinamik deikenlerin yerleri de yine ilerideki belirsiz bir zamanda geri verilir.
82.3.3

Modlde veya belirli bir trde tek olan deikenler iin statik bellek blgesi
Modllerin, yaplarn ve snflarn baz yelerinden yalnzca bir tane bulunabilir: Modl dzeyindeki evrensel deikenler static olarak iaretlenmi olan yap ve snf yeleri Bu eit deikenler static this() isimli kurucu ilevler iinde kurulurlar. Bu kurucu ilevler modldeki dier ilevler almaya balamadan nce iletilirler. Bu eit deikenler program alt srece geerliliklerini korurlar ve main 'den kldktan sonra sonlandrlrlar. Eer tanmlanmsa, bu tr deikenlerin sonlandrlmalar srasnda static ~this() isimli sonlandrc ilev iletilir: import std.stdio; int modldeTek; static this() { writeln("modln static this'i iletiliyor"); modldeTek = 42; } static ~this() { writeln("modln static ~this'i iletiliyor"); } void main() { writeln("main'e girildi"); // ... writeln("main'den klyor"); } kts:

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

577

Bellek Ynetimi

modln static this'i iletiliyor main'e girildi main'den klyor modln static ~this'i iletiliyor static this , main 'e girilmeden nce; static ~this de main 'den kldktan sonra iletilmitir. Btn bir yap veya snf tr iin tek olan deikenler de static belirteci ile tanmlanrlar. Onlarn kurulmalar ve sonlandrlmalar da o trn static this ve static ~this ilevleri ile salanr. Bu ilevler, o trden ka nesne oluturulduundan bamsz olarak yalnzca bir kere iletilirler. static ye rneklerini daha nce Yaplar dersinde grmtk. Buradaki rnekte ise bir snf tanmlayalm. static yelere hem deiken ismi ile deiken.ye yazarak hem de trn ismi ile Snf.ye yazarak eriilebilir: import std.stdio; class Snf { static int snftaTek; static this() { writeln("Snf'n static this'i iletiliyor"); } static ~this() { writeln("Snf'n static ~this'i iletiliyor"); }

void main() { writeln("main'e girildi"); auto birinci = new Snf; auto ikinci = new Snf; writeln("birinci.snftaTek: ", &birinci.snftaTek); writeln("ikinci.snftaTek : ", &ikinci.snftaTek); writeln("Snf.snftaTek : ", &Snf.snftaTek); } writeln("main'den klyor");

kts: Snf'n static this'i iletiliyor main'e girildi birinci.snftaTek: 7F5EEDF95770 ikinci.snftaTek : 7F5EEDF95770 Snf.snftaTek : 7F5EEDF95770 main'den klyor Snf'n static ~this'i iletiliyor

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

578

Bellek Ynetimi

Programda iki nesne oluturulmu olmasna ramen hem onlarn yesi olarak eriilen hem de snfn ismiyle eriilen snftaTek yesinden yalnzca bir tane bulunmaktadr. Bunu her yolla yazdrlan adres deerinin ayn olmasndan anlayabiliyoruz. Ek olarak, static this 'in ve static ~this 'in nesne adedinden bamsz olarak yalnzca bir kere iletildikleri grlyor. Modlde veya bir yap veya snf trnde tek olan deikenler statik bellek blgesinde bulunurlar. Bunu yine adres deerlerinin yakn olmalarna bakarak grmeye alalm: import std.stdio; int modldeTek; class Snf { int normalye; static int snftaTek; } void main() { int yerel; auto nesne = new Snf; writeln("yerel : writeln("nesne.normalye : writeln("Snf.snftaTek: writeln("modldeTek : ", ", ", ", &yerel); &nesne.normalye); &Snf.snftaTek); &modldeTek);

Bu programn kts, modlde veya bir trde tek olan iki deikenin bellekte yan yana durduklarn gsteriyor: yerel : nesne.normalye : Snf.snftaTek: modldeTek : 7FFFC9C096D0 7F4E5E0DFF30 7F4E5E1D9774 7F4E5E1D9770

82.4

Bellekten yer ayrmak


Deikenlerin yerletirilecekleri bellei kendimiz belirlemek isteyebiliriz. Bellekten bu amala yer ayrmann eitli yollar vardr. Belirli sayda bayttan oluan bir bellek blgesi sabit uzunluklu bir dizi olarak ayrlabilir: ubyte[100] yer; // 100 baytlk yer

Yukardaki dizi 100 baytlk bellek blgesi olarak kullanlmaya hazrdr. Bazen bu blgenin uybte gibi bir trle ilgisi olmas yerine, hibir trden olmas istenebilir. Bunun iin eleman tr olarak void seilebilir. void tr herhangi bir deer alamadndan, byle dizilerin = void ile ilklenmeleri arttr. void[100] yer = void; // 100 baytlk yer

D programlar C ktphanelerini arabildiklerinden C'nin bellek ayrmaya yarayan std.c.stdlib.malloc ilevinden de yararlanlabilir:

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

579

Bellek Ynetimi

import std.c.stdlib; // ... void * p = malloc(100);

// 100 baytlk yer

Hibir trden nesne ile bants olmayan bu adresin tr de void* 'dir. void* trnn her tr gsterebilen gsterge tr olduunu Gstergeler dersinde grmtk. Byle bir adres trnden nasl yararlanlabildiini aada GC.calloc() 'u tantrken gstereceim. malloc() 'un dndrd bellek blgesi sfrlanm deildir. std.c.stdlib.malloc 'un bir sorunu, p toplaycnn onun ayrd bellekten haberinin olmamasdr. p toplayc onun ayrm olduu blgenin iindeki referanslara normalde bakmaz ve o referanslarn eritirdikleri nesneleri gerekenden daha erken sonlandrabilir. p toplaycnn std.c.stdlib.malloc ile ayrlm olan bellek blgesinden haberinin olmas iin GC.addRange() arlr. Bellek std.c.stdlib.free ile geri verilirken de GC.removeRange() 'in arlmas unutulmamaldr. Yukardakilerden daha gvenli yntem, bellei core.memory.GC.calloc ile ayrmaktr. calloc() 'un ayrd bellek blgesi nceden sfrlanm olduundan calloc() 'un kullanm gvenlidir. calloc() bellekten ka bayt istendiini parametre olarak alr ve ayrd bellek blgesinin balang adresini dndrr: import core.memory; // ... void * yer = GC.calloc(100);

// 100 baytlk yer

void* tr herhangi baka bir trn gstergesine dntrlebilir: int * intYeri = cast(int*)yer; ounlukla o ara adm atlanr ve calloc() 'un dndrd adres istenen tre dorudan dntrlr: int * intYeri = cast(int*)GC.calloc(100); ylesine semi olduum 100 gibi hazr deerler yerine, trn uzunluu ile kendileri iin yer ayrlmakta olan nesnelerin adedi arplr: // 10 int iin yer ayr int * yer = cast(int*)GC.calloc(int.sizeof * 10); Snflarda snf deikeninin uzunluu snf nesnesinin uzunluundan farkldr. .sizeof snf deikeninin uzunluudur; snf nesnesinin uzunluu ise Tr Nitelikleri dersinde grdmz classInstanceSize ile renilir: // 10 Snf nesnesi iin yer ayr Snf * yer = cast(Snf*)GC.calloc( __traits(classInstanceSize, Snf) * 10); p toplaycnn bellek ayran iki ilevi daha vardr: C'nin malloc() 'unun edeeri olan GC.malloc() ve bellek blou ile ilgili bilgi de dndren GC.qalloc() . Ben bu blmde GC.calloc() 'u kullanmaya devam edeceim. stenen byklkte bellek ayrlamadnda core.exception.OutOfMemoryError trnde bir hata atlr:

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

580

Bellek Ynetimi

void * yer = GC.calloc(10_000_000_000); O kadar bellei bulunmayan bir sistemdeki kts: core.exception.OutOfMemoryError p toplaycdan ayrlm olan bellek blgeleri GC.free() ile geri verilir: GC.free(yer); Ayrlm olan bellek blgesinde yer kalmayabilir. Byle bir bellein uzatlmas mmkndr. GC.realloc() , daha nce p toplaycdan alnm olan bellek gstergesini ve istenen yeni uzunluu parametre olarak alr ve yeni uzunlukta bir yer dndrr: void * eskiYer = GC.calloc(100); // ... void * yeniYer = GC.realloc(eskiYer, 200); realloc() gerekten gerekmedike yeni yer ayrmaz: Eski yerin hemen sonras yeni uzunluu karlayacak kadar bosa, realloc() oray da eski yere ekleyerek bir anlamda eski bellei uzatr. Eski yerin hemen sonras bo deilse veya oradaki boluk yeterli deilse, istenen miktar karlayacak kadar byk olan yeni bir bellek blgesi ayrr ve eski bellein ieriini oraya kopyalar. Eski yer olarak null gnderilebilir; o durumda yalnzca yeni bir yer ayrr. Yeni uzunluk olarak eski uzunluktan daha kk bir deer gnderilebilir; o durumda yalnzca bellek blgesinin geri kalan p toplaycya verilmi olur. Yeni uzunluk olarak 0 gnderilebilir; o durumda GC.free() arlm gibi yalnzca eski bellek geri verilir. realloc() ilevini kullanan bir rnei biraz aada gstereceim.
82.4.1

Ayrlan bellein temizlik ilemlerinin belirlenmesi


Ayrlan blgedeki bayt deerleri tesadfen ilgisiz baka deikenlerin adreslerine karlk gelebilirler. yle bir durumda p toplayc hl kullanmda olduklarn sanacandan, aslnda yaamlar sona ermi bile olsa o baka deikenleri sonlandrmaz. Byle bir bellein gerekten baka deiken referanslar tamad bilindiinde o blgenin taranmasn nlemek iin bellek ayrlrken GC.BlkAttr.NO_SCAN belirteci kullanlr: int * intYeri = cast(int*)GC.calloc(100, GC.BlkAttr.NO_SCAN); Yukardaki bellek blgesine yerletirilecek olan int deerlerinin tesadfen baka deikenlerin adreslerine eit olmalar bylece artk sorun oluturmaz. p toplayc, kendisinden ayrlm olan bellekteki nesnelerin yaam srelerinin artk programcnn sorumluluuna girdiini dnr. Bu yzden bu blgedeki nesnelerin sonlandrclarn normalde iletmez. p toplaycnn sonlandrclar yine de iletmesi istendiinde GC.BlkAttr.FINALIZE belirteci kullanlr: Snf * yer = cast(Snf*)GC.calloc(

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

581

Bellek Ynetimi

__traits(classInstanceSize, Snf) * 10, GC.BlkAttr.FINALIZE);

82.4.2

Gstergeyi dilime dntrmek


D'nin Baka Dizi Olanaklar dersinde anlatmadm bir olana, art arda bulunduklarndan emin olunan elemanlarn balang adresinin bir D dilimine dntrlebilmesidir. Byle bir gstergeden D dilimi oluturan sz dizimi udur: elemanlarn_ilkini_gsteren_gsterge[0 .. eleman_adedi]; Buna gre, GC.calloc() 'un art arda baytlardan olutuunu bildiimiz dn deerini 100 bayttan oluan bir void[] dilimine yle dntrebiliriz: void[] dilim = GC.calloc(100)[0..100]; Benzer ekilde, 10 adet int iin yer ayran yukarda koddan 10 elemanl bir int[] dilimi yle elde edilir: int * yer = cast(int*)GC.calloc(int.sizeof * 10); int[] dilim = yer[0..10];

82.4.3

realloc() rnei
realloc() 'un kullanmn gstermek iin dizi gibi ileyen ok basit bir yap tasarlayacam. ok kstl olan bu yapda yalnzca eleman ekleme ve elemana erime olanaklar bulunuyor. D dizilerinde olduu gibi bu yapnn da kapasitesi var; eklenecek olan elemanlar iin hazrda yer bulunduruyor. Bu yer tkendiinde ise kapasite realloc() ile arttrlyor: struct Dizi(T) { T * yer; size_t kapasite; size_t uzunluk;

// elemanlarn bulunduu yer // toplam ka elemanlk yer olduu // eklenmi olan eleman adedi

~this() { writeln("Bellei geri veriyoruz"); GC.free(yer); } /* * Belirtilen numaral eleman dndrr */ @property T eleman(size_t numara) { if ((numara < 0) || (numara >= uzunluk)) { throw new Exception("ndeks hatas"); } } return *(yer + numara);

/* * Eleman dizinin sonuna ekler */ void ekle(T eleman) { writefln("%s numaral eleman ekleniyor", uzunluk); if ((yer is null) || (uzunluk == kapasite)) { /* Yeni eleman iin yer yok; kapasiteyi arttrmak

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

582

Bellek Ynetimi

* gerekiyor. */ size_t yeniKapasite = kapasite + (kapasite / 2) + 1; kapasiteArttr(yeniKapasite);

/* Eleman en sona yerletiriyoruz */ *(yer + uzunluk) = eleman; ++uzunluk;

void kapasiteArttr(size_t yeniKapasite) { writefln("Kapasite artyor: %s -> %s", kapasite, yeniKapasite); size_t bellekUzunluu = yeniKapasite * T.sizeof; yer = cast(T*)GC.realloc( yer, bellekUzunluu, GC.BlkAttr.NO_SCAN); } kapasite = yeniKapasite;

Yukardaki kullanmda da grld gibi, realloc() 'un nc parametresi GC.BlkAttr belirteleri iindir. Bu dizinin kapasitesi yaklak olarak %50 orannda arttrlyor; rnein 100 elemanlk yer tkendiinde yeni kapasite 150 oluyor. Yeni kapasite hesaplanrken eklenen 1 deeri, balang durumunda sfr olan kapasite iin zel bir ilem gerekmesini nlemek iindir. Yoksa sfrn %50 fazlas da sfr olacandan kapasite hi artamaz. Bu yapy double trnde elemanlarla yle deneyebiliriz: import std.stdio; import core.memory; // ... void main() { auto dizi = Dizi!double(); size_t adet = 10; foreach (i; 0 .. adet) { double elemanDeeri = i * 1.1; dizi.ekle(elemanDeeri); } writeln("Btn elemanlar:"); foreach (i; 0 .. adet) { write(dizi.eleman(i), ' '); } } writeln();

kts:

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

583

Bellek Ynetimi

0 numaral eleman ekleniyor Kapasite artyor: 0 -> 1 1 numaral eleman ekleniyor Kapasite artyor: 1 -> 2 2 numaral eleman ekleniyor Kapasite artyor: 2 -> 4 3 numaral eleman ekleniyor 4 numaral eleman ekleniyor Kapasite artyor: 4 -> 7 5 numaral eleman ekleniyor 6 numaral eleman ekleniyor 7 numaral eleman ekleniyor Kapasite artyor: 7 -> 11 8 numaral eleman ekleniyor 9 numaral eleman ekleniyor Btn elemanlar: 0 1.1 2.2 3.3 4.4 5.5 6.6 7.7 8.8 9.9 Bellei geri veriyoruz

82.5

Deikenleri belirli bir yerde kurmak


new ifadesi iki ilem gerekletirir: 1. Bellekten nesnenin saca kadar yer ayrr. 2. Nesnenin kurucu ilevini arr. Bu ilemlerden birincisinin GC.calloc() ve baka ilevlerle gerekletirilebildiini yukarda grdk. Bir sistem dili olan D, normalde otomatik olarak iletilen ikinci admn da programc tarafndan belirlenmesine izin verir. Nesnelerin belirli bir adreste kurulmas iin "yerletir" anlamna gelen std.conv.emplace() kullanlr. emplace() 'in kullanmn gstermeden nce trlerin hizalama birimi kavramn anlatmam gerekiyor.

82.5.1

Trlerin .alignof nitelii


Deikenlerin kurulabilecekleri adres deerleri ile ilgili bir kstlama vardr: Her tr, ancak belirli bir deere tam olarak blnebilen adreslerde bulunabilir. Buna o trn hizalama birimi (alignment) denir. rnein int deikenler her zaman iin drde tam olarak blnebilen adreslerde bulunurlar: 0, 4, 8, 12, vs. Trlerin .alignof nitelii hizalama birimini dndrr. Aadaki program baz trlerin hizalama birimlerini yazdryor: import std.stdio; import std.conv; import std.typetuple; struct BoYap {} struct Yap { char c; double d;

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

584

Bellek Ynetimi

} class BoSnf {} class Snf { char karakter; } void main() { alias TypeTuple!(char, short, int, long, double, real, string, int[int], int *, BoYap, Yap, BoSnf, Snf) Trler; /* Uzunluu, hizalama birimi, ve trn ismi */ writeln(" Uz. Hiz. Tr\n", "================="); foreach (Tr; Trler) { static if (is (Tr == class)) { size_t uzunluk = __traits(classInstanceSize, Tr); } else { size_t uzunluk = Tr.sizeof; } writefln("%4s%4s %s", uzunluk, Tr.alignof, to!dstring(Tr.stringof));

Bu programn kts farkl ortamlarda farkl olabilir: Uz. Hiz. Tr ================= 1 1 char 2 2 short 4 4 int 8 8 long 8 8 double 16 16 real 16 8 string 8 8 AssociativeArray!(int,int) 8 8 int* 1 1 BoYap 16 8 Yap 16 8 BoSnf 17 8 Snf Nesneleri belirli adreslerde kurarken hizalama birimlerine uymak gerekir. Bunun rneini grmek iin yukardaki 17 bayt uzunluundaki Snf trnn iki nesnesinin bellekte yan yana nasl durabileceklerine bakalm. Her ne kadar yasal bir adres olmasa da, rnei kolaylatrmak iin birinci nesnenin 0 adresinde bulunduunu varsayalm. Bu nesneyi oluturan baytlar, 0'dan 16'ya kadar olan adreslerdedir:

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

585

Bellek Ynetimi

0 1 16 +----+----+- ... -+----+- ... | <- birinci nesne --> | +----+----+- ... -+----+- ...

Bir sonraki bo yerin adresi 17 olduu halde, o adres deeri Snf 'n hizalama birimi olan 8'e tam olarak blnemedii iin ikinci nesne orada kurulamaz. kinci nesnenin 8'e tam olarak blnebilen bir sonraki adrese, yani 24 adresine yerletirilmesi gerekir. Aradaki kullanlmayan baytlara doldurma (padding) baytlar denir:
0 1 16 17 23 24 25 30 +----+----+- ... -+----+----+- ... -+----+----+----+- ... -+----+- ... | <- birinci nesne --> | <- doldurma --> | <-- ikinci nesne --> | +----+----+- ... -+----+----+- ... -+----+----+----+- ... -+----+- ...

Bir nesnenin belirli bir adresten sonra yasal olarak kurulabilecei ilk adresi elde etmek iin u hesap kullanlabilir: (adres + hizalama_birimi - 1) / hizalama_birimi * hizalama_birimi Yukardaki hesabn doru olarak ilemesi iin blme ileminden kalann gzard edilmesi arttr. O yzden o hesapta tamsay trleri kullanlr. Aada emplace() 'in rneklerini gsterirken yukardaki hesab uygulayan u ilevden yararlanacam: T * hizalAdres(T)(T * adayAdres) out (sonu) { /* Dndrdmz adresin T.alignof'a gerekten de tam * olarak blndn denetleyelim. */ assert((cast(size_t)sonu % T.alignof) == 0); } body { return cast(T*)((cast(size_t)adayAdres + T.alignof - 1) / T.alignof * T.alignof); } Yukardaki ilev nesnenin trn ablon parametresinden otomatik olarak karsyor. Onun void* adresleri ile ileyen yklemesini de yle yazabiliriz: void * hizalAdres(T)(void * adayAdres) { return hizalAdres(cast(T*)adayAdres); } Bu ilev, aada emplace() ile snf nesneleri olutururken yararl olacak. Son olarak, yukardaki ilevden yararlanan yardmc bir ilev daha tanmlayalm. Bu ilev, nesnenin boluklarla birlikte ka bayt yer tuttuunu dndrr: size_t bolukluUzunluk(T)() { static if (is (T == class)) { size_t uzunluk = __traits(classInstanceSize, T); } else { size_t uzunluk = T.sizeof; } } return cast(size_t)hizalAdres(cast(T*)uzunluk);

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

586

Bellek Ynetimi

82.5.2

Yap nesnelerini belirli bir yerde kurmak


emplace() , nesnenin kurulaca adresi parametre olarak alr ve o adreste bir nesne kurar. Eer varsa, nesnenin kurucu ilevinin parametreleri bu adresten sonra bildirilir: import std.conv; // ... emplace(adres, /* ... kurucu parametreleri ... */); Yap nesneleri kurarken trn ayrca belirtilmesi gerekmez; emplace() hangi trden nesne kuracan kendisine verilen gstergenin trnden anlar. rnein aadaki emplace() arsnda renciAdresi 'nin tr bir renci* olduu iin emplace() o adreste bir renci nesnesi kurar: renci * renciAdresi = hizalAdres(adayAdres); emplace(renciAdresi, isim, numara);

// ...

Aadaki program, btn nesneler iin gereken miktarda yer ayryor ve nesneleri o blge iindeki hizal adreslerde kuruyor: import import import import std.stdio; std.string; core.memory; std.conv;

struct renci { string isim; int numara; string toString() { return format("%s(%s)", isim, numara); }

void main() { /* nce bu trle ilgili bilgi yazdryoruz. */ writefln("renci.sizeof: %#x (%s bayt)", renci.sizeof, renci.sizeof); writefln("renci.alignof: %#x (%s bayt)", renci.alignof, renci.alignof); string[] isimler = [ "Deniz", "Pnar", "Irmak" ]; auto toplamBayt = bolukluUzunluk!renci() * isimler.length; /* * Btn renci nesnelerine yetecek kadar yer ayryoruz. * * UYARI! Bu dilimin eritirdii nesneler henz * kurulmamlardr. */ renci[] renciler = (cast(renci*)GC.calloc(toplamBayt)) [0 .. isimler.length]; foreach (int i, isim; isimler) { renci * adayAdres = renciler.ptr + i; renci * renciAdresi = hizalAdres(adayAdres); writefln("adres %s: %s", i, renciAdresi); auto numara = 100 + i; emplace(renciAdresi, isim, numara);

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

587

Bellek Ynetimi

} /* Btn elemanlar kurulmu olduundan bir renci dilimi * olarak kullanmakta artk bir saknca yoktur. */ writeln(renciler);

Yukardaki program renci trnn uzunluunu, hizalama birimini ve her rencinin kurulduu adresi de yazdryor: renci.sizeof: 0x18 (24 bayt) renci.alignof: 0x8 (8 bayt) adres 0: 7FCF0B0F2F00 adres 1: 7FCF0B0F2F18 adres 2: 7FCF0B0F2F30 [Deniz(100), Pnar(101), Irmak(102)]

82.5.3

Snf nesnelerini belirli bir yerde kurmak


Snf gstergelerinin nesnenin tam trnden olmas gerekmez. rnein Hayvan gstergeleri Kedi nesnelerine de eriim salayabilirler. Bu yzden emplace() , kuraca nesnenin trn gstergenin trnden anlayamaz. Asl trn emplace() 'e ablon parametresi olarak bildirilmesi gerekir. Ek olarak, snf nesnelerinin kurulaca yer void[] trnde bir dilim olarak belirtilir. Bunlara gre snf nesneleri kurarken u sz dizimi kullanlr: Tr deiken = emplace!Tr(voidDilimi, /* ... kurucu parametreleri ... */); emplace() , belirtilen yerde bir nesne kurar ve o nesneye eriim salayan bir snf deikeni dndrr. Bunlar denemek iin bir Hayvan sradzeninden yararlanacam. Bu sradzene ait olan nesneleri GC.calloc() ile ayrlm olan bir bellee yan yana yerletireceim. Alt snflar zellikle farkl uzunlukta seiyorum. Bylece her nesnenin yerinin bir ncekinin uzunluuna bal olarak nasl hesaplanabileceini gstereceim. interface Hayvan { string arkSyle(); } class Kedi : Hayvan { string arkSyle() { return "miyav"; } } class Papaan : Hayvan { string[] szler; this(string[] szler) { this.szler = szler; }

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

588

Bellek Ynetimi

string arkSyle() { /* std.algorithm.joiner, belirtilen aralktaki * elemanlar belirtilen ayrala birletirir. */ return to!string(joiner(szler, ", ")); }

Nesnelerin yerletirilecekleri blgeyi GC.calloc() ile ayracam. Bu blgedeki nesnelerin sonlandrclarnn iletilmelerine engel olmamak iin FINALIZE belirtecini de kullanyorum: auto kapasite = 10_000; void * boYer = GC.calloc(kapasite, GC.BlkAttr.FINALIZE); Normalde nesneler kurulduka o blgenin tkenmediinden emin olunmas da gerekir. rnei ksa tutmak iin bu konuyu gzard edeceim; yalnzca iki nesne kuracam ve bu iki nesnenin oraya sacaklarn varsayacam. O blgede nce bir Kedi nesnesi sonra da bir Papaan nesnesi kuracam: Kedi kedi = emplace!Kedi(kediYeri); // ... Papaan papaan = emplace!Papaan(papaanYeri, [ "merrba", "aloo" ]); Dikkat ederseniz Papaan 'n kurucusunun gerektirdii parametreler nesnenin yerinden sonra belirtiliyorlar. emplace() arlarnn dndrdkleri deikenler bir Hayvan dizisine eklenecekler ve daha sonra bir foreach dngsnde kullanlacaklar: Hayvan[] hayvanlar; // ... hayvanlar ~= kedi; // ... hayvanlar ~= papaan; foreach (hayvan; hayvanlar) { writeln(hayvan.arkSyle()); } Dier aklamalar programn iine yazyorum: import import import import // ... void main() { /* Bu bir Hayvan deikeni dizisidir; Hayvan nesnesi * dizisi deildir. */ Hayvan[] hayvanlar; /* On bin baytn bu rnekte yeterli olduunu * varsayyorum. Normalde nesnelerin buraya gerekten * sacaklarnn denetlenmesi de gerekir. */ auto kapasite = 10_000; void * boYer = GC.calloc(kapasite, GC.BlkAttr.FINALIZE); /* lk nce bir Kedi nesnesi yerletireceiz. */ void * kediAdayAdresi = boYer; std.stdio; std.algorithm; std.conv; core.memory;

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

589

Bellek Ynetimi

void * kediAdresi = hizalAdres!Kedi(kediAdayAdresi); writeln("Kedi adresi : ", kediAdresi); /* Snflarda emplace()'e void[] verildii iin adresten * dilim elde etmemiz gerekiyor. */ size_t kediUzunluu = __traits(classInstanceSize, Kedi); void[] kediYeri = kediAdresi[0..kediUzunluu]; /* Kedi'yi o yerde kuruyoruz ve dndrlen deikeni * diziye ekliyoruz. */ Kedi kedi = emplace!Kedi(kediYeri); hayvanlar ~= kedi; /* Papaan' Kedi nesnesinden bir sonraki uygun adreste * kuracaz. */ void * papaanAdayAdresi = kediAdresi + kediUzunluu; void * papaanAdresi = hizalAdres!Papaan(papaanAdayAdresi); writeln("Papaan adresi: ", papaanAdresi); size_t papaanUzunluu = __traits(classInstanceSize, Papaan); void[] papaanYeri = papaanAdresi[0..papaanUzunluu]; Papaan papaan = emplace!Papaan(papaanYeri, [ "merrba", "aloo" ]); hayvanlar ~= papaan; foreach (hayvan; hayvanlar) { writeln(hayvan.arkSyle()); }

kts: Kedi adresi : 7F869469E000 Papaan adresi: 7F869469E018 miyav merrba, aloo Programn admlarn aka gsterebilmek iin btn ilemleri main iinde ve belirli trlere bal olarak yazdm. O ilemlerin iyi yazlm bir programda yeniNesne(T) gibi bir ablon iinde bulunacaklarn dnebiliriz.

82.6

Nesneyi belirli bir zamanda sonlandrmak


new ilecinin tersi, sonlandrc ilevin iletilmesi ve nesne iin ayrlm olan bellein p toplayc tarafndan geri alnmasdr. Bu ilemler belirsiz bir zamanda otomatik olarak iletilir. Baz durumlarda sonlandrc ilevin programcnn istedii bir zamanda iletilmesi gerekebilir. rnein am olduu bir dosyay sonlandrc ilevinde kapatan bir nesnenin sonlandrcsnn hemen iletilmesi gerekebilir. Buradaki kullanmnda "temizle, iini boalt" anlamna gelen clear() , nesnenin sonlandrc ilevinin hemen iletilmesini salar: clear(nesne); clear , yalnzca sonlandrc ilevi iletir. Bellein gerekten ne zaman geri verilecei p toplaycnn kararna kalmtr.

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

590

Bellek Ynetimi

82.7

Nesneyi alma zamannda ismiyle kurmak


Object snfnn factory() isimli ye ilevi trn ismini parametre olarak alr, o trden bir nesne kurar ve adresini dndrr. factory() , trn kurucusu iin parametre almaz; bu yzden trn parametresiz olarak kurulabilmesi arttr: module deneme; import std.stdio; interface Hayvan { string ses(); } class Kedi : Hayvan { string ses() { return "miyav"; } } class Kpek : Hayvan { string ses() { return "hav"; } } void main() { string[] kurulacaklar = [ "Kedi", "Kpek", "Kedi" ]; Hayvan[] hayvanlar; foreach (trsmi; kurulacaklar) { hayvanlar ~= cast(Hayvan)Object.factory("deneme." ~ trsmi); } foreach (hayvan; hayvanlar) { writeln(hayvan.ses()); }

O programda hi new kullanlmad halde adet Hayvan nesnesi oluturulmu ve hayvanlar dizisine eklenmitir: miyav hav miyav Object.factory() 'ye trn tam isminin verilmesi gerekir. O yzden yukardaki tr isimleri "Kedi" ve "Kpek" gibi ksa olarak deil, modln ismi ile birlikte "deneme.Kedi" ve "deneme.Kpek" olarak belirtiliyorlar. factory 'nin dn tr Object 'tir; bu trn yukardaki cast(Hayvan) kullanmnda olduu gibi doru tre aka dntrlmesi gerekir.

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

591

Bellek Ynetimi

82.8

zet
p toplayc bellei belirsiz zamanlarda tarar, artk kullanlmayan nesneleri belirler, onlar sonlandrr ve yerlerini geri alr. p toplaycnn temizlik ilemleri GC.collect() , GC.disable() , GC.enable() , GC.minimize() , vs. ile bir lye kadar ynetilebilir. static this ve static ~this modlde, yapda ve snfta tek olan deikenlerin kurulmalarn ve sonlandrlmalarn salar. p toplaycdan yer ayrmak iin GC.calloc() , ayrlm olan bellei uzatmak iin GC.realloc() , geri vermek iin de GC.free() kullanlr. p toplaycdan ayrlan bellein GC.BlkAttr.NO_SCAN , GC.BlkAttr.FINALIZE , vs. olarak iaretlenmesi gerekebilir. Bir gstergenin eriim salad elemanlar dilim=gsterge[0..adet] sz dizimi ile dilime dntrlebilirler. .alignof trn hizalama birimini verir. emplace() yap nesnesi kurarken gsterge, snf nesnesi kurarken void[] alr. Object.factory() tr ismi ile nesne kurar.

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

592

Problem zmleri

83
83.3

Problem zmleri
Merhaba Dnya zmleri
1. Bu noktada bir D 2.0 derleyicinizin ve bir gelitirme ortamnzn olmas gerekiyor. Bu konuda bilgi iin kurulum sayfasndan yararlanabilirsiniz. 2. import std.stdio; void main() { writeln("Baka bir ey... :p"); } 3. import std.stdio; void main() { writeln("Bir satr..."); writeln("Baka bir satr..."); } 4. Bu program writeln satrnn sonunda noktal virgl olmad iin derlenemez: import std.stdio; void main() { writeln("Merhaba dnya!") }

83.4

writeln ve write zmleri


1. Bir yntem, arada bir parametre daha kullanmaktr: writeln("Merhaba dnya!", " ", "Merhaba balklar!"); 2. write da birden fazla parametre alabilir: write("bir", " iki", " ");

83.5

Temel Trler zmleri


1. int yerine baka bir tr ismi kullanmak yeter. ki tanesi: import std.stdio; void main() { writeln("Tr : writeln("Bayt olarak uzunluu: writeln("En kk deeri : writeln("En byk deeri : writeln();

", ", ", ",

real.stringof); real.sizeof); real.min); real.max);

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

593

Problem zmleri

writeln("Tr : writeln("Bayt olarak uzunluu: writeln("En kk deeri : writeln("En byk deeri :

", ", ", ",

ulong.stringof); ulong.sizeof); ulong.min); ulong.max);

83.6

Deikenler zmleri
1. import std.stdio; void main() { double kur = 2.11; int adet = 20; } writeln(kur, " kurundan ", adet, " avro bozdurdum.");

83.7

Standart Giri ve k Akmlar zmleri


1. import std.stdio; void main() { stdout.writeln(1, ",", 2); }

83.8

Giriten Bilgi Almak zmleri


1. Dviz kuru kesirli bir deer olduu iin daha nce de yaptmz gibi onun iin double trn seebiliriz. Giriin taklmamas iin boluklu olarak " %s" yazmay da unutmadan: import std.stdio; void main() { write("Avro kurunu giriniz: "); double avro_kuru; readf(" %s", &avro_kuru); write("Ne kadar avronuz var: "); int adet; readf(" %s", &adet); writeln(avro_kuru, " kurundan ", adet, " avro bozdurmak ister misiniz?");

2. stdin , gelen karakterleri istenen tre dntremeyince kullanlamaz duruma girer. rnein "abc" harflerinin tamsay karl olmad iin; int trnde bilgi beklenen bir durumda girite belirmeleri, stdin 'in baarsz olmasna neden olur.

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

594

Problem zmleri

83.9

Mantksal fadeler zmleri


1. Derleyici 10 < say 'y bir ifade olarak tand iin, ondan sonra bir virgl bekliyor. Btn ifadenin etrafna parantezler koyulduunda da sorun zlmyor, nk bu sefer de 10 < say ifadesinden sonra bir kapama parantezi bekliyor. 2. (10 < say) > 20 eklinde gruplama kullanldnda derleme hatas yok, nk derleyici nce 10 < say ifadesini iletir, ondan sonra onun sonucunu > 20 ile kullanr. Bu durumda 10 < say gibi bir mantksal ifadenin sonucunun false veya true olduunu biliyoruz. Bu deerler tamsay ilemlerinde kullanldklarnda otomatik olarak 0 ve 1 deerlerine dnrler. (Otomatik tr dnmlerini daha sonra greceiz.) O yzden de btn ifade ya 0 > 20 ya da 1 > 20 haline gelir. Ve ikisinin sonucu da her zaman iin false 'tur... 3. "Alt snrdan byktr ve st snrdan kktr" mantksal ifadesini yle kurarz: writeln("Arasnda: ", (say > 10) && (say < 20)); 4. "Yeterince bisiklet var" ifadesini kii_says <= bisiklet_says veya bisiklet_says >= kii_says olarak yazabiliriz. Bunun dndakiler aynen sorudaki gibi yazlabilirler: writeln("Plaja gidiyoruz: ", ((mesafe < 10) && (bisiklet_says >= kii_says)) || ((kii_says <= 5) && araba_var && ehliyet_var) ); Okumay kolaylatrmak iin || ilecinin ayr bir satra yazldna dikkat edin. Bylece sorudaki iki koulu temiz bir ekilde iki ayr satrda grebiliyoruz.

83.10

if Koulu zmleri
1. Bu programda writeln("Taba kaldryorum") ifadesi sanki else kapsamndaym gibi ierletilerek yazlm. Oysa else 'ten sonra kme parantezleri kullanlmad iin, kurallar gerei bu else kapsamnda tek bir ifade vardr: writeln("Baklava yiyorum") . Programdaki boluklarn nemi de olmad iin (yazm hatalarna neden olmadklar srece) tabakl ifade aslnda main iinde serbest bir ifadedir ve hibir koula bal olmadan her zaman iin iletilir. erletildii iin okuyan yanltabiliyor. Eer tabakl ifade de else kapsamnda olacaksa, o zaman kme parantezlerini unutmamak gerekir. Bu, beklenen sonucu verir: import std.stdio; void main() { bool limonata_var = true; if (limonata_var) { writeln("Limonata iiyorum"); writeln("Barda ykyorum"); } else { writeln("Baklava yiyorum"); writeln("Taba kaldryorum"); }

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

595

Problem zmleri

2. Bu oyundaki koullar tasarlamak iin birden ok zm dnebiliriz. Ben iki tane gstereceim. nce soruda verilen bilgiyi bire bir uygulayarak: import std.stdio; void main() { write("Zar ka geldi? "); int zar; readf(" %s", &zar); if (zar == 1) { writeln("Siz kazandnz"); } else if (zar == 2) { writeln("Siz kazandnz"); } else if (zar == 3) { writeln("Siz kazandnz"); } else if (zar == 4) { writeln("Ben kazandm"); } else if (zar == 5) { writeln("Ben kazandm"); } else if (zar == 6) { writeln("Ben kazandm"); } else { writeln("HATA: Geersiz deer: ", zar); }

Ama o programda ok tekrar var. Ayn sonucu baka ekillerle de elde edebiliriz. Bir tanesi: import std.stdio; void main() { write("Zar ka geldi? "); int zar; readf(" %s", &zar); if ((zar == 1) || (zar == 2) || (zar == 3)) { writeln("Siz kazandnz"); } else if ((zar == 4) || (zar == 5) || (zar == 6)) { writeln("Ben kazandm"); } else { writeln("HATA: Geersiz deer: ", zar); }

3. Artk yukarda gsterilen zmleri kullanamayz. Kimse 1000 deiik deeri yle aka yazmaz: ar emek gerektirir, doruluundan emin olunamaz, okuyan bir ey anlamaz, vs. O yzden burada "bu say iki snrn arasnda m" karlatrmasn kullanrz: if ((say >= 1) && (say <= 500))

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

596

Problem zmleri

83.11

while Dngs zmleri


1. say 'nn ilk deeri 0 olduu iin while dngsnn mantksal ifadesi en batan false oluyor ve dngye bir kere bile girilmiyor. Bunun iin programclkta ok kullanlan bir yntem, dngye girmeyi salayacak bir ilk deer kullanmaktr: int say = 3; 2. Bu programda aka ilk deerler verilmiyor nk saylarn 0 olan ilk deerleri her iki dngye de mutlaka girileceini garanti ediyorlar: import std.stdio; void main() { int gizli_say; while ((gizli_say < 1) || (gizli_say > 10)) { write("1-10 aralndaki gizli sayy bildirin: "); readf(" %s", &gizli_say); } int tahmin; while (tahmin != gizli_say) { write("Tahmin? "); readf(" %s", &tahmin); } } writeln("Doru!");

83.12

Tamsaylar ve Aritmetik lemler zmleri


1. / ilecini blm iin, % ilecini de kalan iin kullanabiliriz: import std.stdio; void main() { int birinci_say; write("Birinci say: "); readf(" %s", &birinci_say); int ikinci_say; write("kinci say : "); readf(" %s", &ikinci_say); int blm = birinci_say / ikinci_say; int kalan = birinci_say % ikinci_say; writeln(birinci_say, " = ", ikinci_say, " * ", blm, " + ", kalan);

2. Kalann 0 olup olmadn if koulu ile denetleyebiliriz: import std.stdio; void main() { int birinci_say; write("Birinci say: "); readf(" %s", &birinci_say);

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

597

Problem zmleri

int ikinci_say; write("kinci say : "); readf(" %s", &ikinci_say); int blm = birinci_say / ikinci_say; int kalan = birinci_say % ikinci_say; // Burada artk writeln kullanamayacamza dikkat // edin. Satr daha sonra sonlandrmak zorundayz. write(birinci_say, " = ", ikinci_say, " * ", blm); // Bu ksmn ancak kalan 0 olmad zaman yazdryoruz if (kalan != 0) { write(" + ", kalan); } // Artk satr sonlandryoruz writeln();

} 3.

import std.stdio; void main() { while (true) { write("0: k, 1: Toplama, 2: karma, ", "3: arpma, 4: Blme - lem? "); int ilem; readf(" %s", &ilem); // nce ilemi denetleyelim if ((ilem < 0) || (ilem > 4)) { writeln("Bu ilemi daha renmedim"); continue; } if (ilem == 0){ writeln("Gle gle!"); break; } // Eer bu noktaya gelmisek, bildiimiz 4 ilemden // birisi ile ilgilendiimizden eminiz. Artk // kullancdan 2 sayy isteyebiliriz: int birinci; int ikinci; write("Birinci say? "); readf(" %s", &birinci); write(" kinci say? "); readf(" %s", &ikinci); // lemin sonucunu bu deikene yerletireceiz int sonu; if (ilem == 1) { sonu = birinci + ikinci; } else if (ilem == 2) { sonu = birinci - ikinci; } else if (ilem == 3) { sonu = birinci * ikinci;

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

598

Problem zmleri

} else if (ilem == 4) { sonu = birinci / ikinci; } else { writeln( "Programda bir hata var! ", "Bu noktaya kesinlikle gelmemeliydik..."); break; Sonu: ", sonu);

} }

writeln("

} 4.

import std.stdio; void main() { int say = 1; while (say <= 10) { if (say != 7) { writeln(say); } } ++say;

83.13

Kesirli Saylar zmleri


1. nceki blmdeki hesap makinesi programndaki satrdaki int 'leri double yapmak yeter: double birinci; double ikinci; // ... double sonu; 2. Problemde 5 yerine daha fazla say girilmesi istenseydi programn nasl daha da iinden klmaz bir hale geleceini gryor musunuz: import std.stdio; void main() { double say_1; double say_2; double say_3; double say_4; double say_5; write("Say 1: "); readf(" %s", &say_1); write("Say 2: "); readf(" %s", &say_2); write("Say 3: "); readf(" %s", &say_3); write("Say 4: "); readf(" %s", &say_4); write("Say 5: ");

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

599

Problem zmleri

readf(" %s", &say_5); writeln("ki katlar:"); writeln(say_1 * 2); writeln(say_2 * 2); writeln(say_3 * 2); writeln(say_4 * 2); writeln(say_5 * 2); writeln("Bete writeln(say_1 writeln(say_2 writeln(say_3 writeln(say_4 writeln(say_5 birleri:"); / 5); / 5); / 5); / 5); / 5);

83.14

Diziler zmleri
1. import std.stdio; void main() { write("Ka say var? "); int adet; readf(" %s", &adet); double[] saylar; saylar.length = adet; int saya; while (saya < adet) { write("Say ", saya, ": "); readf(" %s", &saylar[saya]); ++saya; } writeln("Sral olarak:"); saylar.sort; saya = 0; while (saya < adet) { write(saylar[saya], " "); ++saya; } writeln(); writeln("Ters srada:"); saylar.reverse; saya = 0; while (saya < adet) { write(saylar[saya], " "); ++saya; } writeln();

2. Aklamalar kodun iinde... import std.stdio; void main() { // Ka tane say geleceini batan bilmediimiz iin

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

600

Problem zmleri

// dinamik diziler kullanyoruz int[] tekler; int[] iftler; writeln("Ltfen tamsaylar girin (sonlandrmak iin -1)"); while (true) { // Sayy okuyoruz int say; readf(" %s", &say); // Say zellikle -1 olduunda dngden kyoruz if (say == -1) { break; } // // // if Tek veya ift olmas durumuna gre farkl dizinin sonuna yerletiriyoruz; ikiye blmnden kalan 0 ise ifttir, deilse tektir ((say % 2) == 0) { iftler ~= say;

} else { tekler ~= say; }

// nce tekleri ve iftleri ayr ayr sralyoruz tekler.sort; iftler.sort; // Ondan sonra birletiriyoruz int[] sonu; sonu = tekler ~ iftler; writeln("nce tekler, sonra iftler; sral olarak:"); // Daha nce grdmz gibi bir dng kurarak dizinin // btn elemanlarn ka yazdryoruz int i; while (i < sonu.length) { write(sonu[i], " "); ++i; } } writeln();

3. Bu programda tane hata var: ki hata while dngleriyle ilgili: her ikisinde de < ileci yerine <= kullanlm. O yzden program yasal olmayan bir indeks kullanarak dizinin dna tayor. nc hatay kendiniz uraarak gidermeniz nemli olduu iin zm hemen vermek istemiyorum. Yukardaki iki hatay giderdikten sonra program tekrar derleyin ve neden sonucu yazdrmadn kendiniz zmeye aln. Eer kendiniz bulamazsanz, zm grmek iin aadaki iki yatay izgi arasndaki paragraf fareyle sein:

83.15

Baka Dizi Olanaklar zmleri


Aadaki dilimdeki gibi bandan ksaltarak tketmek, D'de ok yaygndr. Bu yntem, daha ileride greceimiz Phobos aralklarnn da temelini oluturur.

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

601

Problem zmleri

import std.stdio; void main() { double[] dizi = [ 1, 20, 2, 30, 7, 11 ]; double[] dilim = dizi; // // // // iimize dizinin btn elemanlarna eriim salayan bir dilimle balyoruz

while (dilim.length) { if (dilim[0] > 10) { dilim[0] /= 2; } } }

// o dilimde eleman bulunduu // srece ... // ilemlerde yalnzca ilk // eleman kullanyoruz

dilim = dilim[1 .. $]; // dilimi bandan ksaltyoruz // asl dizi deimi oluyor

writeln(dizi);

83.16

Dizgiler zmleri
1. Ktphane bavuru belgelerinin amalar retmek deildir. Ktphane belgelerini caydrc derecede ksa ve anlalmaz bulabilirsiniz. Siz de zamanla alacaksnz ve uzun yazlardan ok z belgeler yeleyeceksiniz. 2. import std.stdio; import std.string; void main() { char[] ad; write("Adnz? "); readln(ad); ad = chomp(ad); char[] soyad; write("Soyadnz? "); readln(soyad); soyad = chomp(soyad); char[] ad_soyad = ad ~ " " ~ soyad; writeln(capwords(ad_soyad));

Yaz iinde de deindiim gibi, dizgileri tek satrda da okuyabilirsiniz: import std.stdio; import std.string; void main() { write("Adnz? "); string ad = chomp(readln()); write("Soyadnz? "); string soyad = chomp(readln()); string ad_soyad = ad ~ " " ~ soyad; writeln(capwords(ad_soyad));

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

602

Problem zmleri

ad_soyad ' da sa tarafa uymas iin string olarak tanmladm. 3. import std.stdio; import std.string; void main() { write("Satr giriniz: "); string satr = chomp(readln()); sizediff_t ilk_a = indexOf(satr, 'a'); if (ilk_a == -1) { writeln("Bu satrda a harfi yok."); } else { sizediff_t son_a = lastIndexOf(satr, 'a'); writeln(satr[ilk_a .. son_a + 1]); }

83.17

Standart Akmlar Dosyalara Balamak zmleri


1. Programlarn giri ve klarnn birbirlerine balanabilmeleri zellikle Unix trevi iletim sistemlerinde ok kullanlr. Buna olanak vermek iin, programlarn olabildiince standart giri ve k akmlaryla etkileecek ekilde yazlmalarna allr. rnein ismi deneme.d olan bir dosyann hangi klasrde olduu find ve grep programlar ile u ekilde bulunabilir: find | grep deneme.d find , iinde bulunulan klasrden itibaren btn klasrlerin iindeki btn dosyalarn isimlerini kna gnderir. Onun kts | ile grep 'e verilir ve o da iinde deneme.d bulunan satrlar kendi kna yazdrr.

83.18

Dosyalar zmleri
import std.stdio; import std.string; void main() { write("Dosya ismi? "); string giriDosyassmi = chomp(readln()); File giri = File(giriDosyassmi, "r"); string kDosyassmi = giriDosyassmi ~ ".bak"; File k = File(kDosyassmi, "w"); while (!giri.eof()) { string satr = chomp(giri.readln()); if (satr.length != 0) { k.writeln(satr); }

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

603

Problem zmleri

writeln(kDosyassmi, " dosyasn oluturdum");

83.19

auto ve typeof zmleri


1. Trn bulmak istediimiz hazr deeri typeof 'a vererek trn retebiliriz, ve o trn .stringof nitelii ile de trn ismini yazdrabiliriz: import std.stdio; void main() { writeln(typeof(1.2).stringof); }

83.20

for Dngs zmleri


1. import std.stdio; void main() { for (int satr = 0; satr != 9; ++satr) { for (int stun = 0; stun != 9; ++stun) { write(satr, ',', stun, ' '); } } writeln();

} 2. gen:

import std.stdio; void main() { for (int satr = 0; satr != 9; ++satr) { int uzunluk = satr + 1; for (int i = 0; i != uzunluk; ++i) { write('*'); } } writeln();

Paralelkenar: import std.stdio; void main() { for (int satr = 0; satr != 9; ++satr) { for (int i = 0; i != satr; ++i) { write(' '); } } writeln("********");

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

604

Problem zmleri

Baklava dilimi izdirebilir misiniz? * *** ***** ******* ***** *** *

83.21

l le ?: zmleri
1. Soruda istendii iin ?: ilecini kullanyoruz; siz burada if deyiminin daha kullanl olduunu dnebilirsiniz. Dikkat ederseniz, bu zmde iki tane ?: ileci kullanlmaktadr: import std.stdio; void main() { write("Ltfen net miktar girin: "); int net; readf(" %s", &net); writeln(net < 0 ? -net : net, " lira ", net < 0 ? "zarardasnz" : "kazanlsnz");

83.22

Hazr Deerler zmleri


1. Buradaki sorun; hazr deerin bir int 'e smayacak kadar byk olduu iin long trnde olmasdr. Bu yzden soldaki int trndeki deikene uymaz. Burada en az iki zm vardr. Bir zm, aka int yazmak yerine, deikenin tr iin auto kullanmak ve tr seimini derleyiciye brakmaktr: auto kasadakiPara = 10_000_000_000; Bylece kasadakiPara deikeninin deeri de long olarak seilir. Dier zm, deikenin trn de aka long yazmaktr: long kasadakiPara = 10_000_000_000; 2. Burada satrn bana gtren '\r' karakteri kullanlabilir. Bylece hep ayn satrn stne yazlr. import std.stdio; void main() { for (int say = 0; ; ++say) { write("\rSay: ", say); } }

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

605

Problem zmleri

83.23

kt Dzeni zmleri
1. Bunun dzen belirteciyle nasl yapldn zaten grdnz. Hibir hesap yapmaya gerek kalmadan: import std.stdio; void main() { writeln( "Onaltl say dzeni program (k iin 0 girin)"); while (true) { write("Say? "); long say; readf(" %s", &say); if (say == 0) { break; } } writefln("%d <=> %#x", say, say);

2. % karakterinin kendisini yazdrmak iin ift yazmak gerektiini hatrlayarak: import std.stdio; void main() { write("Yzde deeri? "); double yzde; readf(" %s", &yzde); } writefln("%%%.2f", yzde);

83.24

Giri Dzeni zmleri


1. Tarihin yazmndaki her bir tamsaynn yerine %s yerletirmek iimize yarayan dzen dizgisini oluturmaya yeter: import std.stdio; void main() { int yl; int ay; int gn; readf("%s.%s.%s", &yl, &ay, &gn); } writeln("Ay: ", ay);

83.25

do-while Dngs zmleri


1. Bu programn do-while ile zellikle bir ilgisi yok; ama while yerine kesinlikle do-while ile yaplmas gereken bir rnek de bulunamaz. Program, tuttuunuz sayy stten ve alttan kstrarak bulur. rnein ilk tahmini 50 olsa, ve siz "k" diye yant verseniz; artk saynn [51,100] aralnda olduunu

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

606

Problem zmleri

renir. Ondan sonra bu iki saynn tam ortasnda bir deer tutsa, ve "in" deseniz; bu sefer de rnein [51,75] aralnda olduunu bilir. O ekilde kstra kstra tek bir say kaldnda, tuttuunuz sayy da bulmu olur.

83.26

enum zmleri
1. Aklamalar kodun iine yerletirdim: import std.stdio; import std.conv; enum lem { k, Toplama, karma, arpma, Blme } void main() { // Seenekleri yazdryoruz for (lem ilem; ilem <= lem.max; ++ilem) { write(ilem, ": ", to!string(ilem), " "); } while (true) { write("lem? "); // Giriten yine de int olarak okumak zorundayz int ilemKodu; readf(" %s", &ilemKodu); /* Bu noktadan sonra sihirli sabitler yerine enum deerler kullanacaz. Giriten int olarak okuduumuz iin, bu int deerin trn lem'e dntryoruz (Bu konuyu daha sonraki bir derste greceiz.) */ lem ilem = cast(lem)ilemKodu; if (ilem == lem.k) { writeln("Gle gle!"); break; } double birinci; double ikinci; double sonu; if ((ilem < lem.min) || (ilem > lem.max)) { writeln("HATA: Geersiz ilem"); continue; } write("Birinci say? "); readf(" %s", &birinci); write(" kinci say? "); readf(" %s", &ikinci); switch (ilem) { case lem.Toplama: sonu = birinci + ikinci; break; case lem.karma: sonu = birinci - ikinci; break;

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

607

Problem zmleri

case lem.arpma: sonu = birinci * ikinci; break; case lem.Blme: sonu = birinci / ikinci; break; default: throw new Exception( "HATA: Programda bir hata var;" "buraya hi gelmemeliydik"); } } writeln(" Sonu: ", sonu);

83.27

levler zmleri
1. import std.stdio; void menyGster(string[] seenekler, int ilkNumara) { foreach (i, seenek; seenekler) { writeln(' ', i + ilkNumara, ' ', seenek); } } void main() { string[] seenekler = [ "Siyah", "Krmz", "Yeil", "Mavi", "Beyaz" ]; menyGster(seenekler, 1); } 2. Bu program ok farkl ekillerde ve hi bu kadar ilev kullanmadan da yazlabilir. Ben biraz da kendimi elendirmek iin byle yazdm... :o) Mors kodu yalnzca ASCII karakterleri kulland iin char trn kullanmakta bir saknca grmedim. Bu programn bir yetersizlii de yalnzca kk harfleri tanyor olmasdr. import std.stdio; import std.string; string boluk() { return "."; } string iaret() { return "="; } string dt() { return iaret() ~ boluk(); } string dt() { return iaret() ~ iaret() ~ iaret() ~ boluk();

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

608

Problem zmleri

} string harfAras() { return boluk() ~ boluk(); } string morsKarl(char harf) { string[char] morsAlfabesi = [ ' ' : boluk() ~ boluk(), 'a' : dt() ~ dt(), 'b' : dt() ~ dt() ~ dt() ~ 'c' : dt() ~ dt() ~ dt() ~ 'd' : dt() ~ dt() ~ dt(), 'e' : dt(), 'f' : dt() ~ dt() ~ dt() ~ 'g' : dt() ~ dt() ~ dt(), 'h' : dt() ~ dt() ~ dt() ~ 'i' : dt() ~ dt(), 'j' : dt() ~ dt() ~ dt() ~ 'k' : dt() ~ dt() ~ dt(), 'l' : dt() ~ dt() ~ dt() ~ 'm' : dt() ~ dt(), 'n' : dt() ~ dt(), 'o' : dt() ~ dt() ~ dt(), 'p' : dt() ~ dt() ~ dt() ~ 'q' : dt() ~ dt() ~ dt() ~ 'r' : dt() ~ dt() ~ dt(), 's' : dt() ~ dt() ~ dt(), 't' : dt(), 'u' : dt() ~ dt() ~ dt(), 'v' : dt() ~ dt() ~ dt() ~ 'w' : dt() ~ dt() ~ dt(), 'x' : dt() ~ dt() ~ dt() ~ 'y' : dt() ~ dt() ~ dt() ~ 'z' : dt() ~ dt() ~ dt() ~ '1' : dt() ~ dt() ~ dt() ~ '2' : dt() ~ dt() ~ dt() ~ '3' : dt() ~ dt() ~ dt() ~ '4' : dt() ~ dt() ~ dt() ~ '5' : dt() ~ dt() ~ dt() ~ '6' : dt() ~ dt() ~ dt() ~ '7' : dt() ~ dt() ~ dt() ~ '8' : dt() ~ dt() ~ dt() ~ '9' : dt() ~ dt() ~ dt() ~ '0' : dt() ~ dt() ~ dt() ~ ];

dt(), dt(), dt(), dt(), dt(), dt(),

dt(), dt(),

dt(), dt(), dt(), dt(), dt() ~ dt() ~ dt() ~ dt() ~ dt() ~ dt() ~ dt() ~ dt() ~ dt() ~ dt() ~

dt(), dt(), dt(), dt(), dt(), dt(), dt(), dt(), dt(), dt(),

if (harf !in morsAlfabesi) { throw new Exception("Harf tannmyor: " ~ harf); } } return morsAlfabesi[harf];

string dizgiyiMorsaevir(const char[] dizgi) { string sonu; foreach (harf; dizgi) { sonu ~= morsKarl(harf); sonu ~= harfAras(); } } return sonu;

string dizgiOku(string mesaj) {

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

609

Problem zmleri

write(mesaj, "? "); return chomp(readln());

void main() { writeln( dizgiyiMorsaevir( dizgiOku("Mors koduna evrilecek dizgi"))); } 3. Program nasl deitirmek istediiniz size kalm, ama bir ka fikir: yatayizgiiz gibi bir ilev izim ilevlerine boyarken hangi karakteri kullanacaklarn da bir parametreyle belirtebiliriz: void benekKoy(Kat kat, int satr, int stun, dchar boya) { kat[satr][stun] = boya; } Bylece ilev deiik karakterle arlabilir ve kat zerinde her ekil kendi karakteriyle izilebilir.

83.28

Eleme Tablolar zmleri


1. Eleme tablosunun .keys nitelii, btn indeksleri ieren bir dizi dndrr. Bu dizinin elemanlarn bir for dngsnde gezersek, ve her birisi iin eleme tablosunun .remove niteliini kullanrsak btn elemanlar eleme tablosundan silinmi olurlar ve sonuta tablo boalr: import std.stdio; void main() { string[int] isimleSaylar = [ 1 : "bir", 10: "on", 100:"yz", ]; writeln("Balangtaki tablo bykl isimleSaylar.length); int[] indeksler = isimleSaylar.keys; for (int i = 0; i != indeksler.length; ++i) { writeln(indeksler[i], " indeksinin elemann siliyorum"); isimleSaylar.remove(indeksler[i]); } writeln("Sildikten sonraki tablo bykl: ", isimleSaylar.length); : ",

O zm zellikle byk tablolarda yava olacaktr. Aadaki zmlerin ikisi de tabloyu bir seferde boaltrlar. Baka bir zm, eleme tablosuna kendisiyle ayn trden bo bir tablo atamaktr:

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

610

Problem zmleri

string[int] boTablo; isimleSaylar = boTablo; Her trn .init nitelii, o trn ilk deeri anlamndadr. Bir eleme tablosunun ilk deeri de bo tablo olduu iin, bir nceki zmn de edeeri olan unu kullanabiliriz: isimleSaylar = isimleSaylar.init; 2. Burada renci ismine karlk birden fazla not tutmak istiyoruz. Yani bir dizi not... Eer eleme tablomuzu string 'den int[] trne eleyecek ekilde tanmlarsak, isimle eritiimiz eleman, bir int dizisi olur. O dizinin sonuna not ekleyerek de amacmza eriiriz: import std.stdio; void main() { /* * Eleme tablosunun indeks tr string; eleman tr ise * int[], yani bir int dizisi... Belirginletirmek iin * aralarnda bolukla tanmlyorum: */ int[] [string] notlar; /* * Artk "emre" indeksine karlk gelen eleman bir int * dizisi gibi kullanabiliriz */ // Diziye notlar eklemek: notlar["emre"] ~= 90; notlar["emre"] ~= 85; // Diziyi yazdrmak writeln(notlar["emre"]);

Notlar teker teker eklemek yerine hepsini bir dizi olarak da atayabiliriz: import std.stdio; void main() { int[][string] notlar; notlar["emre"] = [ 90, 85, 95 ]; } writeln(notlar["emre"]);

83.29

foreach Dngs zmleri


1. isimle tablosunun tersi olarak alabilmesi iin indeks tr yerine eleman tr, eleman tr yerine de indeks tr kullanmak gerekir. Yani int[string] ... Asl dizginin elemanlarn foreach ile gezerek indeks olarak eleman deerini, eleman olarak da indeks deerini kullanrsak, ters ynde alan bir eleme tablosu elde ederiz: import std.stdio; void main() {

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

611

Problem zmleri

string[int] isimle = [ 1:"bir", 7:"yedi", 20:"yirmi" ]; int[string] rakamla; foreach (indeks, eleman; isimle) { rakamla[eleman] = indeks; } } writeln(rakamla["yirmi"]);

83.30

switch ve case zmleri


1. import std.stdio; import std.string; void main() { string ilem; double birinci; double ikinci; write("lem? "); ilem = chomp(readln()); write("ki sayy aralarnda bolukla yazn: "); readf(" %s %s", &birinci, &ikinci); double sonu; final switch (ilem) { case "topla": sonu = birinci + ikinci; break; case "kart": sonu = birinci - ikinci; break; case "arp": sonu = birinci * ikinci; break; case "bl": sonu = birinci / ikinci; break; } } writeln(sonu);

2. case deerlerinin virgllerle belirlenebilmesi olanan kullanarak: final switch (ilem) { case "topla", "+": sonu = birinci + ikinci; break; case "kart", "-": sonu = birinci - ikinci; break; case "arp", "*": sonu = birinci * ikinci; break;

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

612

Problem zmleri

case "bl", "/": sonu = birinci / ikinci; break; } 3. Bu durumda default blmn eklemek gerekecei iin final switch kullanamayz. Programn deien yerleri: // ... switch (ilem) { // ... default: throw new Exception("Geersiz ilem"); } // ...

83.31

lev Parametreleri zmleri


1. Bu ilevin parametreleri kopyalanan trden olduklarndan, ilev iindeki dei toku ilemi, yalnzca bu kopyalar dei toku ediyor. Program dzeltmek iin, parametrelerin referans olarak gnderilmeleri gerekir: void deiToku(ref int birinci, ref int ikinci) { const int geici = birinci; birinci = ikinci; ikinci = geici; } Artk istendii gibi, main iindeki deikenler etkilenmi olurlar: 2 1 Not: Ek olarak, geici deikenini de const olarak belirledim; nk onun deeri de ilklendikten sonra bir daha deitirilmiyor.

83.32

Tembel Deerlendirmeler zmleri


1. Bu durumda tek sayl indeksli elemanlar sabit bir deerle deitirilecekler. Bu yzden o parametreyi bir giri deeri olarak kullanlaca iin in belirteciyle tanmlayabiliriz: import std.stdio; void tekndeksleriDeitir(int[] dizi, in int deer) { foreach (i, ref eleman; dizi) { const bool tekndeks = ((i % 2) == 1); if (tekndeks) { eleman = deer; }

void main()

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

613

Problem zmleri

int[] dizi = [ 0, 11, 22, 33, 44, 55 ]; tekndeksleriDeitir(dizi, 7); writeln(dizi);

foreach iinde dizinin asl elemanlarn deitirmek iin ref kullanldna da dikkat edin. Yoksa elemanlarn kendileri deil, kopyalar deitirilirdi. 2. Sabit deerler kullanabilmeye ek olarak, bir ilevin dndrd deerleri de kullanabilmek iin deerin gnderildii parametreyi lazy olarak tanmlayabiliriz. Bylece ilev iinde deer parametresine her eriildiinde o ilev arlr ve o ilevin dndrd deer kullanlr. import std.stdio; import std.random; void tekndeksleriDeitir(int[] dizi, lazy int deer) { foreach (i, ref eleman; dizi) { const bool tekndeks = ((i % 2) == 1); if (tekndeks) { eleman = deer; }

void main() { int[] dizi = [ 0, 11, 22, 33, 44, 55 ]; // Sabit deerler de hl alrlar tekndeksleriDeitir(dizi, 7); writeln(dizi); // imdi bir ilevin dndrd deerleri de // kullandrabiliyoruz tekndeksleriDeitir(dizi, uniform(100, 200)); writeln(dizi);

83.33

main 'in Parametreleri ve Dn Deeri zmleri


1. import std.stdio; import std.conv; int main(string[] parametreler) { if (parametreler.length != 4) { stderr.writeln( "HATA! Doru kullanm: \n ", parametreler[0], " bir_say ilem baka_say"); return 1; } double birinci = to!double(parametreler[1]); string ilem = parametreler[2]; double ikinci = to!double(parametreler[3]); switch (ilem) { case "+": writeln(birinci + ikinci);

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

614

Problem zmleri

break; case "-": writeln(birinci - ikinci); break; case "x": writeln(birinci * ikinci); break; case "/": writeln(birinci / ikinci); break; default: throw new Exception("Geersiz ilem: " ~ ilem); } } 2. import std.stdio; import std.process; void main() { write("Balatmam istediiniz program satrn yazn: "); string komutSatr = readln(); } writeln("Dn deeri: ", system(komutSatr)); return 0;

83.34

assert fadesi zmleri


1. Bu program 6:9 ve 1:2 vererek altrdnzda hata atmadn greceksiniz. Buna ramen, sonucun doru olmadn da farkedebilirsiniz: 9:6'da balayan ve 1 saat 2 dakika sren ilem, 10:8'de sonlanr Grld gibi, 6:9 girildii halde, ka 9:6 yazdrlmaktadr. Bu hata, bir sonraki problemde bir assert ifadesi yardmyla yakalanacak. 2. Programa 6:9 ve 15:2 verildiinde atlan hata, bizi u satra gtrr: dstring zamanDizgisi(in int saat, in int dakika) { assert((saat >= 0) && (saat <= 23)); // ... Saat bilgisinin 0 ile 23 arasnda bir deerde olmasn denetleyen bu assert ifadesinin yanl kmas, ancak bu ilev programn baka yerinden yanl saat deeriyle arldnda mmkndr. zamanDizgisi ilevinin arld sonucuYazdr ilevine baktmzda bir yanllk gremiyoruz: void sonucuYazdr( in int balangSaati, in int balangDakikas, in int ilemSaati, in int ilemDakikas, in int bitiSaati, in int bitiDakikas)

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

615

Problem zmleri

writef("%s'%s balayan", zamanDizgisi(balangSaati, balangDakikas), daEki(balangDakikas)); writef(" ve %s saat %s dakika sren ilem,", ilemSaati, ilemDakikas); writef(" %s'%s sonlanr", zamanDizgisi(bitiSaati, bitiDakikas), daEki(bitiDakikas));

writeln();

Bu durumda sonucuYazdr ilevini aran noktalardan phelenir ve onun programda main iinden ve tek bir noktadan arldn grrz: void main() { // ... sonucuYazdr(balangSaati, balangDakikas, ilemSaati, ilemDakikas, bitiSaati, bitiDakikas); } aran noktada da bir sorun yok gibi grnyor. Biraz daha dikkat ve zaman harcayarak sonunda balang zamannn ters srada okunduunu farkederiz: zamanOku("Balang zaman", balangDakikas, balangSaati); Programcnn yapt o dikkatsizlik nedeniyle 6:9 olarak girilen bilgi aslnda 9:6 olarak alglanmakta, ve daha sonra buna 15:2 sresi eklenmektedir. zamanDizgisi ilevindeki assert de saat deerini 24 olarak grr ve bu yzden hata atlmasna neden olur. Burada zm, balang zamannn okunduu noktada parametreleri doru srada yazmaktr: zamanOku("Balang zaman", balangSaati, balangDakikas); Artk kt doru: Balang zaman? (SS:DD) 6:9 lem sresi? (SS:DD) 15:2 6:9'da balayan ve 15 saat 2 dakika sren ilem, 21:11'de sonlanr 3. Bu seferki hata, daEki ilevindeki assert ile ilgili: assert(ek.length != 0); O denetimin hatal kmas, da ekinin uzunluunun 0 olduunu, yani ekin bo olduunu gsteriyor. Dikkat ederseniz, 6:9 ve 1:1 zamanlarn toplaynca sonu 7:10 olur. Yani bu sonucun dakika deerinin son hanesi 0'dr. daEki ilevine dikkat

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

616

Problem zmleri

ederseniz, 0'n hangi eki alaca bildirilmemi. zm, 0'n case blounu da switch ifadesine eklemektir: case 6: case 9: case 0: ek = "da"; break; Bu hatay da bir assert sayesinde yakalam ve gidermi olduk: Balang zaman? (SS:DD) 6:9 lem sresi? (SS:DD) 1:1 6:9'da balayan ve 1 saat 1 dakika sren ilem, 7:10'da sonlanr 4. Daha nce de karlatmz assert yine doru kmyor: assert((saat >= 0) && (saat <= 23)); Bunun nedeni, zamanEkle ilevinin saat deerini 23'ten byk yapabilmesidir. Bu ilevin sonuna, saat deerinin her zaman iin 0 ve 23 aralnda olmasn salayan bir kalan ilemi ekleyebiliriz: void zamanEkle( in int balangSaati, in int balangDakikas, in int eklenecekSaat, in int eklenecekDakika, out int sonuSaati, out int sonuDakikas) { sonuSaati = balangSaati + eklenecekSaat; sonuDakikas = balangDakikas + eklenecekDakika; if (sonuDakikas > 59) { ++sonuSaati; } } sonuSaati %= 24;

Yukardaki ilevdeki dier hatay da gryor musunuz? sonuDakikas 59'dan byk bir deer olduunda sonuSaati bir arttrlyor, ama sonuDakikas 'nn deeri 59'dan byk olarak kalyor. Belki de u daha doru bir ilev olur: void zamanEkle( in int balangSaati, in int balangDakikas, in int eklenecekSaat, in int eklenecekDakika, out int sonuSaati, out int sonuDakikas) { sonuSaati = balangSaati + eklenecekSaat; sonuDakikas = balangDakikas + eklenecekDakika; sonuSaati += sonuDakikas / 60; sonuSaati %= 24; assert((sonuSaati >= 0) && (sonuSaati <= 23)); assert((sonuDakikas >= 0) && (sonuDakikas <= 59));

Aslnda sonuDakikas hl bozuk... Ona da 60'tan kalan atamak gerekir. Ama imdi iin gzel taraf, artk bu ilevin hatal saat ve dakika deerleri retmesi assert ifadeleri nedeniyle olanakszdr.

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

617

Problem zmleri

Yukardaki ilevi rnein 6:9 ve 1:55 deerleriyle arrsanz, sonuDakikas 'n denetleyen assert ifadesinin hata vereceini greceksiniz. 5. Burada sorun, son hanenin 0 olmasndan kaynaklanyor. Son hane sfr olunca onlar hanesini de katarak "on", "krk", "elli", vs. diye okuyunca 0'a verilmi olan "da" eki her durumda doru almyor. Bu problemin zmn size brakyorum... Ayrca dikkat ederseniz, sonu 3 ile biten dakika deerlerinde de sorun var: Balang zaman? (SS:DD) 1:2 lem sresi? (SS:DD) 1:1 1:2'de balayan ve 1 saat 1 dakika sren ilem, 2:3'da sonlanr O da unutulmu bir break anahtar szcnden kaynaklanyor. Kolay gelsin! :o)

83.35

Birim Testleri zmleri


Program nce bu haliyle balatyor ve hata atldndan emin oluyoruz: $ dmd deneme.d -ofdeneme -w -unittest $ ./deneme core.exception.AssertError@deneme.d(10): Assertion failure Bylece testlerin altndan eminiz; bizi ileride yaplabilecek hatalara kar koruyacaklar. Bu durumdaki hata mesajndaki satr numarasna (10) bakarak, birim testlerinden ilkinin baarsz olduunu gryoruz. imdi, gstermek amacyla bilerek hatal bir gerekletirmesini deneyelim. Bu gerekletirme, zel harfe hi dikkat etmez ve girilen dizinin aynsn dndrr: dchar[] harfBaa(in dchar[] dizgi, in dchar harf) { dchar[] sonu; foreach (eleman; dizgi) { sonu ~= eleman; } return sonu; } unittest { dchar[] dizgi = "merhaba"d.dup; assert(harfBaa(dizgi, 'm') == "merhaba"); assert(harfBaa(dizgi, 'e') == "emrhaba"); assert(harfBaa(dizgi, 'a') == "aamerhb");

void main() {} Bu sefer birinci assert denetimi tesadfen baarl olur, ama ikincisi hata atar: $ ./deneme core.exception.AssertError@deneme.d(16): Assertion failure Tesadfn nedeni, o hatal gerekletirmede "merhaba" girildii zaman yine "merhaba" dndrlmesi ve birim testinin beklentisine uymasdr. Tekrar deneyelim:

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

618

Problem zmleri

dchar[] harfBaa(in dchar[] dizgi, in dchar harf) { dchar[] baTaraf; dchar[] sonTaraf; foreach (eleman; dizgi) { if (eleman == harf) { baTaraf ~= eleman; } else { sonTaraf ~= eleman; } } return baTaraf ~ sonTaraf; } unittest { dchar[] dizgi = "merhaba"d.dup; assert(harfBaa(dizgi, 'm') == "merhaba"); assert(harfBaa(dizgi, 'e') == "emrhaba"); assert(harfBaa(dizgi, 'a') == "aamerhb");

void main() {} imdi testlerin tm geer: $ ./deneme $ Artk bu noktadan sonra gvendeyiz; ilevi, testlerine gvenerek istediimiz gibi deitirebiliriz. Aada iki farkl gerekletirmesini daha gryorsunuz. Bunlarn ikisi de ayn testlerden geerler. std.algorithm modlndeki partition ilevini kullanan bir gerekletirme: import std.algorithm; dchar[] harfBaa(in dchar[] dizgi, in dchar harf) { bool uyar(in dchar eleman) { return eleman == harf; } dchar[] sonu = dizgi.dup; partition!(uyar, SwapStrategy.stable)(sonu); return sonu; } unittest { dchar[] dizgi = "merhaba"d.dup; assert(harfBaa(dizgi, 'm') == "merhaba"); assert(harfBaa(dizgi, 'e') == "emrhaba"); assert(harfBaa(dizgi, 'a') == "aamerhb");

void main() {}

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

619

Problem zmleri

nce zel harften ka tane bulunduunu sayan bir gerekletirme... Bu, sonucun ba tarafn daha sonra tekrarlDizi isimli baka bir ileve yaptryor. Salam programclk gerei, o ilevin de kendi birim testleri yazlm: dchar[] tekrarlDizi(int adet, dchar harf) { dchar[] dizi; foreach (i; 0..adet) { dizi ~= harf; } return dizi; } unittest { assert(tekrarlDizi(3, 'z') == "zzz"); assert(tekrarlDizi(10, '') == ""); } dchar[] harfBaa(in dchar[] dizgi, in dchar harf) { int zelHarfAdedi; dchar[] sonTaraf; foreach (eleman; dizgi) { if (eleman == harf) { ++zelHarfAdedi; } else { sonTaraf ~= eleman; } } return tekrarlDizi(zelHarfAdedi, harf) ~ sonTaraf; } unittest { dchar[] dizgi = "merhaba"d.dup; assert(harfBaa(dizgi, 'm') == "merhaba"); assert(harfBaa(dizgi, 'e') == "emrhaba"); assert(harfBaa(dizgi, 'a') == "aamerhb");

void main() {}

83.36

Szlemeli Programlama zmleri


Birim testlerini yazmaya main 'deki kodlar kopyalayarak baladm ve bir tek ikinci takmn yendii durumu ekledim. int puanEkle(in int goller1, in int goller2, ref int puan1, ref int puan2) in { assert(goller1 >= 0); assert(goller2 >= 0); assert(puan1 >= 0); assert(puan2 >= 0); } out (sonu) { assert((sonu >= 0) && (sonu <= 2)); }

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

620

Problem zmleri

body {

int kazanan; if (goller1 > goller2) { puan1 += 3; kazanan = 1; } else if (goller1 < goller2) { puan2 += 3; kazanan = 2; } else { ++puan1; ++puan2; kazanan = 0; }

return kazanan; } unittest { int birincininPuan = 10; int ikincininPuan = 7; int kazananTaraf; kazananTaraf = puanEkle(3, 1, birincininPuan, ikincininPuan); assert(birincininPuan == 13); assert(ikincininPuan == 7); assert(kazananTaraf == 1); kazananTaraf = puanEkle(2, 2, birincininPuan, ikincininPuan); assert(birincininPuan == 14); assert(ikincininPuan == 8); assert(kazananTaraf == 0); kazananTaraf = puanEkle(0, 1, birincininPuan, ikincininPuan); assert(birincininPuan == 14); assert(ikincininPuan == 11); assert(kazananTaraf == 2);

83.37

Yaplar zmleri
1. Aksine bir neden olmad iin, en basit olarak iki tane karakter ile: struct OyunKad { dchar renk; dchar deer; } 2. Yine ok basit olarak, yap nesnesinin yelerini yan yana ka gndermek yeterli olur: void oyunKadYazdr(in OyunKad kat) { write(kat.renk, kat.deer); } 3. Eer yeniSeri isminde baka bir ilevin yazlm olduunu kabul edersek, yeniDeste ilevini de onu her renk iin drt kere ararak kolayca yazabiliriz:

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

621

Problem zmleri

OyunKad[] yeniDeste() out (sonu) { assert(sonu.length == 52); } body { OyunKad[] deste; deste deste deste deste } ~= ~= ~= ~= yeniSeri(''); yeniSeri(''); yeniSeri(''); yeniSeri('');

return deste;

Asl i, yararlandmz yeniSeri tarafndan halledilir. Bu ilev btn deerleri ieren bir dizi tanmlyor, ve verilen renk bilgisini onun elemanlaryla srayla birletirerek bir seri oluturuyor: OyunKad[] yeniSeri(dchar renk) in { assert((renk == '') || (renk == '') || (renk == '') || (renk == '')); } out (sonu) { assert(sonu.length == 13); } body { OyunKad[] seri; dchar btnDeerler[] = [ '2', '3', '4', '5', '6', '7', '8', '9', '0', 'J', 'Q', 'K', 'A' ]; foreach (deer; btnDeerler) { seri ~= OyunKad(renk, deer); } } return seri;

Program hatalarn nlemek iin ilevlerin giri ve k koullarn da yazdma dikkat edin. 4. Rasgele seilen iki eleman dei toku etmek, sonuta destenin karmasn da salar. Rastgele seim srasnda, kk de olsa ayn eleman seme olasl da vardr. Ama bu nemli bir sorun oluturmaz, nk eleman kendisiyle deitirmenin etkisi yoktur. void kartr(OyunKad[] deste, in int deiTokuAdedi) { /* Not: Daha etkin bir yntem, desteyi bandan sonuna * kadar ilerlemek ve her eleman destenin sonuna * doru rasgele bir elemanla deitirmektir. */ foreach (i; 0 .. deiTokuAdedi) { // Rasgele iki tanesini se const int birinci = uniform(0, deste.length); const int ikinci = uniform(0, deste.length);

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

622

Problem zmleri

// Dei toku et swap(deste[birinci], deste[ikinci]);

O ilevde std.algorithm modlndeki swap ilevinden yararlandm. swap , kendisine verilen iki deeri dei toku eder. Temelde u ilev gibi alr: void deiToku(ref OyunKad soldaki, ref OyunKad sadaki) { auto geici = soldaki; soldaki = sadaki; sadaki = geici; } Programn tamam yle: import std.stdio; import std.random; import std.algorithm; struct OyunKad { dchar renk; dchar deer; } void oyunKadYazdr(in OyunKad kat) { write(kat.renk, kat.deer); } OyunKad[] yeniSeri(dchar renk) in { assert((renk == '') || (renk == '') || (renk == '') || (renk == '')); } out (sonu) { assert(sonu.length == 13); } body { OyunKad[] seri; dchar btnDeerler[] = [ '2', '3', '4', '5', '6', '7', '8', '9', '0', 'J', 'Q', 'K', 'A' ]; foreach (deer; btnDeerler) { seri ~= OyunKad(renk, deer); } } return seri;

OyunKad[] yeniDeste() out (sonu) { assert(sonu.length == 52); } body { OyunKad[] deste;

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

623

Problem zmleri

deste deste deste deste }

~= ~= ~= ~=

yeniSeri(''); yeniSeri(''); yeniSeri(''); yeniSeri('');

return deste;

void kartr(OyunKad[] deste, in int deiTokuAdedi) { /* Not: Daha etkin bir yntem, desteyi bandan sonuna * kadar ilerlemek ve her eleman destenin sonuna * doru rasgele bir elemanla deitirmektir. */ foreach (i; 0 .. deiTokuAdedi) { // Rasgele iki tanesini se const int birinci = uniform(0, deste.length); const int ikinci = uniform(0, deste.length); // Dei toku et swap(deste[birinci], deste[ikinci]);

void main() { OyunKad[] deste = yeniDeste(); kartr(deste, 100); foreach (kat; deste) { oyunKadYazdr(kat); write(' '); } } writeln();

83.38

Parametre Serbestlii zmleri


hesapla ilevinin belirsiz sayda Hesap nesnesi alabilmesi iin parametre listesinin Hesap[] ... eklinde tanmlanmas gerekir: double[] hesapla(Hesap[] hesaplar ...) { double[] sonular; foreach (hesap; hesaplar) { final switch (hesap.ilem) { case lem.Toplama: sonular ~= hesap.birinci + hesap.ikinci; break; case lem.karma: sonular ~= hesap.birinci - hesap.ikinci; break; case lem.arpma: sonular ~= hesap.birinci * hesap.ikinci; break; case lem.Blme: sonular ~= hesap.birinci / hesap.ikinci; break; }

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

624

Problem zmleri

return sonular;

leve gnderilen btn parametre deerleri hesaplar dizisinde bulunur. Btn hesap nesnelerini bir dngde teker teker kullanarak sonular da bir double dizisine yerletiriyoruz ve ilevin sonucu olarak dndryoruz. Btn program: import std.stdio; enum lem { Toplama, karma, arpma, Blme } struct Hesap { lem ilem; double birinci; double ikinci; } double[] hesapla(Hesap[] hesaplar ...) { double[] sonular; foreach (hesap; hesaplar) { final switch (hesap.ilem) { case lem.Toplama: sonular ~= hesap.birinci + hesap.ikinci; break; case lem.karma: sonular ~= hesap.birinci - hesap.ikinci; break; case lem.arpma: sonular ~= hesap.birinci * hesap.ikinci; break; case lem.Blme: sonular ~= hesap.birinci / hesap.ikinci; break; }

} }

return sonular;

void main() { writeln(hesapla(Hesap(lem.Toplama, 1.1, 2.2), Hesap(lem.karma, 3.3, 4.4), Hesap(lem.arpma, 5.5, 6.6), Hesap(lem.Blme, 7.7, 8.8))); } kts: [3.3, -1.1, 36.3, 0.875]

83.39

lev Ykleme zmleri


1. Daha nce yazlan bilgiVer ilevlerinden yararlanan iki yklemesi yle yazlabilir:

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

625

Problem zmleri

void bilgiVer(in Yemek yemek) { bilgiVer(yemek.zaman); write('-'); bilgiVer(zamanEkle(yemek.zaman, GnnSaati(1, 30))); } write(" Yemek, Yer: ", yemek.adres);

void bilgiVer(in GnlkPlan plan) { bilgiVer(plan.sabahToplants); writeln(); bilgiVer(plan.leYemei); writeln(); bilgiVer(plan.akamToplants); } Btn bu trleri kullanan programn tamam: import std.stdio; struct GnnSaati { int saat; int dakika; } void bilgiVer(in GnnSaati zaman) { writef("%02s:%02s", zaman.saat, zaman.dakika); } GnnSaati zamanEkle(in GnnSaati balang, in GnnSaati eklenecek) { GnnSaati sonu; sonu.dakika = balang.dakika + eklenecek.dakika; sonu.saat = balang.saat + eklenecek.saat; sonu.saat += sonu.dakika / 60; sonu.dakika %= 60; sonu.saat %= 24; } return sonu;

struct Toplant { string konu; int katlmcSays; GnnSaati balang; GnnSaati biti; } void bilgiVer(in Toplant toplant) { bilgiVer(toplant.balang); write('-'); bilgiVer(toplant.biti); writef(" \"%s\" toplants (%s katlmc)", toplant.konu, toplant.katlmcSays);

struct Yemek {

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

626

Problem zmleri

GnnSaati zaman; string adres;

void bilgiVer(in Yemek yemek) { bilgiVer(yemek.zaman); write('-'); bilgiVer(zamanEkle(yemek.zaman, GnnSaati(1, 30))); } write(" Yemek, Yer: ", yemek.adres);

struct GnlkPlan { Toplant sabahToplants; Yemek leYemei; Toplant akamToplants; } void bilgiVer(in GnlkPlan plan) { bilgiVer(plan.sabahToplants); writeln(); bilgiVer(plan.leYemei); writeln(); bilgiVer(plan.akamToplants); } void main() { auto geziToplants = Toplant("Bisiklet gezisi", 4, GnnSaati(10, 30), GnnSaati(11, 45)); auto leYemei = Yemek(GnnSaati(12, 30), "Taksim"); auto bteToplants = Toplant("Bte", 8, GnnSaati(15, 30), GnnSaati(17, 30)); auto bugnnPlan = GnlkPlan(geziToplants, leYemei, bteToplants); bilgiVer(bugnnPlan); writeln();

Yukardaki main , nesneler tanmlamak yerine yalnzca yap hazr deerleri ile yle de yazlabilir: void main() { bilgiVer(GnlkPlan(Toplant("Bisiklet gezisi", 4, GnnSaati(10, 30), GnnSaati(11, 45)), Yemek(GnnSaati(12, 30), "Taksim"), Toplant("Bte", 8, GnnSaati(15, 30), GnnSaati(17, 30))));

writeln();

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

627

Problem zmleri

83.40

ye levler zmleri
1. Azaltan ilev, eksi deerler nedeniyle daha karmak oluyor: struct GnnSaati { // ... void azalt(in Sre sre) { int azalanDakika = sre.dakika % 60; int azalanSaat = sre.dakika / 60; dakika -= azalanDakika; if (dakika < 0) { dakika += 60; ++azalanSaat; } saat -= azalanSaat; if (saat < 0) { saat = 24 - (-saat % 24); }

} }

// ...

2. toString 'in program ok daha ksa ve kullanl hale getirdiini greceksiniz. Karlatrma amacyla, programn nceki halinde Toplant nesnesini yazdran ilevi tekrar gstermek istiyorum: void bilgiVer(in Toplant toplant) { bilgiVer(toplant.balang); write('-'); bilgiVer(toplant.biti); writef(" \"%s\" toplants (%s katlmc)", toplant.konu, toplant.katlmcSays);

Aadaki programdaki Toplant.toString ise ksaca yle yazlabiliyor: string toString() { return format("%s-%s \"%s\" toplants (%s katlmc)", balang, biti, konu, katlmcSays); } Programn tamam: import std.stdio; import std.string; struct Sre { int dakika; } struct GnnSaati {

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

628

Problem zmleri

int saat; int dakika; string toString() { return format("%02s:%02s", saat, dakika); } void ekle(in Sre sre) { dakika += sre.dakika; saat += dakika / 60; dakika %= 60; saat %= 24;

struct Toplant { string konu; int katlmcSays; GnnSaati balang; GnnSaati biti; string toString() { return format("%s-%s \"%s\" toplants (%s katlmc)", balang, biti, konu, katlmcSays); }

struct Yemek { GnnSaati zaman; string adres; string toString() { GnnSaati biti = zaman; biti.ekle(Sre(90)); return format("%s-%s Yemek, Yer: %s", zaman, biti, adres);

struct GnlkPlan { Toplant sabahToplants; Yemek leYemei; Toplant akamToplants; string toString() { return format("%s\n%s\n%s", sabahToplants, leYemei, akamToplants); }

void main() { auto geziToplants = Toplant("Bisiklet gezisi", 4, GnnSaati(10, 30), GnnSaati(11, 45)); auto leYemei = Yemek(GnnSaati(12, 30), "Taksim");

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

629

Problem zmleri

auto bteToplants = Toplant("Bte", 8, GnnSaati(15, 30), GnnSaati(17, 30)); auto bugnnPlan = GnlkPlan(geziToplants, leYemei, bteToplants); } writeln(bugnnPlan);

Programn kts da eski halinin ayns: 10:30-11:45 "Bisiklet gezisi" toplants (4 katlmc) 12:30-14:00 Yemek, Yer: Taksim 15:30-17:30 "Bte" toplants (8 katlmc)

83.41

le Ykleme zmleri
1. Daha nce yazdmz GnnSaati.opCmp yleydi: int opCmp(const ref GnnSaati sadaki) const { return (saat == sadaki.saat ? dakika - sadaki.dakika : saat - sadaki.saat); } Soldaki nesnenin (ilevin zerinde arld nesnenin) sralamada daha nce olmas iin eksi bir deer dndrlmesi gerektiini hatrlayn. Bu nesnenin zamanda sonra olduunda eksi bir deer retmesi iin karma ilemlerinin ters srada yaplmalar gerekir: int opCmp(const ref GnnSaati sadaki) const { return (saat == sadaki.saat ? sadaki.dakika - dakika : sadaki.saat - saat); } Dersteki programda bu ilevi kullandmzda ktnn bykten ke doru (zaman iinde sonradan nceye doru) olduunu gryoruz: [22:33,19:54,19:50,17:01,13:11,13:03,07:46,07:12,01:11,00:42] 2. ZamanAral iin <<= ve >>= ilelerinin yazm srasnda GnnSaati 'nin baz ilelerinden yararlanabiliriz. Onlar ve toString ilevlerini de gstererek: import std.stdio; import std.string; struct Sre { int dakika; } struct GnnSaati { int saat; int dakika;

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

630

Problem zmleri

void opOpAssign(string ile)(in Sre sre) if (ile == "+") { dakika += sre.dakika; saat += dakika / 60; dakika %= 60; saat %= 24;

void opOpAssign(string ile)(in Sre sre) if (ile == "-") { int azalanDakika = sre.dakika % 60; int azalanSaat = sre.dakika / 60; dakika -= azalanDakika; if (dakika < 0) { dakika += 60; ++azalanSaat; } saat -= azalanSaat; if (saat < 0) { saat = 24 - (-saat % 24); }

string toString() { return format("%02s:%02s", saat, dakika); }

struct ZamanAral { GnnSaati ba; GnnSaati son;

// araln dnda kabul edilir

void opOpAssign(string ile)(in Sre sre) if (ile == ">>") { ba += sre; son += sre; } void opOpAssign(string ile)(in Sre sre) if (ile == "<<") { ba -= sre; son -= sre; } string toString() { return format("%s-%s", ba, son); }

void main() { auto aralk = ZamanAral(GnnSaati(14, 30), GnnSaati(15, 30)); writeln("Balangta : ", aralk); aralk >>= Sre(10); assert(aralk == ZamanAral(GnnSaati(14, 40), GnnSaati(15, 40)));

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

631

Problem zmleri

writeln("Saa teleyince: ", aralk); aralk <<= Sre(1); assert(aralk == ZamanAral(GnnSaati(14, 39), GnnSaati(15, 39))); writeln("Sola teleyince: ", aralk);

kts: Balangta : 14:30-15:30 Saa teleyince: 14:40-15:40 Sola teleyince: 14:39-15:39 3. ~ ilecini ZamanAral 'nn her iki taraftaki kullanm iin tanmlamak iin opBinary!"~" ve opBinaryRight!"~" ye ilevlerinin ayr olarak tanmlanmalar gerekir. opBinaryRight!"~" , ZamanAral 'nn sada kullanld durum iindir. struct GnnSaati { int saat; int dakika; } struct ZamanAral { GnnSaati ba; GnnSaati son;

// araln dnda kabul edilir

ZamanAral opBinary(string ile) (const ref GnnSaati yeniSon) if (ile == "~") { return ZamanAral(ba, yeniSon); } ZamanAral opBinaryRight(string ile) (const ref GnnSaati yeniBa) if (ile == "~") { return ZamanAral(yeniBa, son); }

void main() { auto aralk = ZamanAral(GnnSaati(1, 23), GnnSaati(4, 56)); auto yeniAralk = aralk ~ GnnSaati(7, 0); assert(yeniAralk == ZamanAral(GnnSaati(1, 23), GnnSaati(7, 0))); yeniAralk = GnnSaati(0, 30) ~ aralk; assert(yeniAralk == ZamanAral(GnnSaati(0, 30), GnnSaati(4, 56)));

83.42

Treme zmleri
1. st snfn abstract olarak belirttii ilev alt snflar tarafndan override anahtar szc ile tanmlanr:

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

632

Problem zmleri

import std.stdio; class DemirYoluArac { void ilerle(in int kilometre) { write(kilometre, " kilometre: "); foreach (i; 0 .. kilometre) { write(ses(), ' '); } } } writeln();

abstract string ses();

class Drezin : DemirYoluArac { override string ses() { return "of puf"; } } class Vagon : DemirYoluArac { override string ses() { return "taktak tukutak"; } } class Lokomotif : DemirYoluArac { override string ses() { return "uf uf"; } } void main() { auto drezin = new Drezin; drezin.ilerle(2); auto vagon = new Vagon; vagon.ilerle(3); auto lokomotif = new Lokomotif; lokomotif.ilerle(4);

2. Bu rnekte Tren 'in sesinin yalnzca lokomotif yesine bal olduunu varsayyoruz: // ... class Tren : DemirYoluArac { Lokomotif lokomotif; Vagon[] vagonlar; abstract void indir(); abstract void bindir(); override string ses() { return lokomotif.ses(); }

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

633

Problem zmleri

} class Kondktr {} class YolcuTreni : Tren { Kondktr[] kondktrler; override void indir() { writeln("yolcular iniyor"); } override void bindir() { writeln("yolcular biniyor"); }

void main() { auto tren = new YolcuTreni; tren.ilerle(1); } Tren 'in lokomotif yesi henz ilklenmediinden program ker: Segmentation fault lokomotif Tren 'in kurucusu iinde ilklenebilir: class Tren : DemirYoluArac { // ... this() { lokomotif = new Lokomotif; } // ... } Baka bir tasarm, Lokomotif nesnesinin Tren 'e dardan verilmesidir: class Tren : DemirYoluArac { // ... this(Lokomotif lokomotif) { this.lokomotif = lokomotif; } // ... } class YolcuTreni : Tren { // ... this(Lokomotif lokomotif) { super(lokomotif); } // ... } void main() { auto lokomotif = new Lokomotif;

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

634

Problem zmleri

auto tren = new YolcuTreni(lokomotif); tren.ilerle(1);

Dikkat ederseniz main() iinde oluturulan Lokomotif nce YolcuTreni 'ne, onun tarafndan da kendi st snf olan Tren 'e geirilmektedir.

83.43

Object zmleri
1. Eitlik karlatrmasnda ncelikle sadaki 'nin null olmadna ve yalnzca x ve y yelerine bakmak yeterli olur: enum Renk { mavi, yeil, krmz }; class Nokta { int x; int y; Renk renk; // ... override const bool opEquals(Object o) { auto sadaki = cast(const Nokta)o; return sadaki && (x == sadaki.x) && (y == sadaki.y);

2. Sadaki nesnenin bu nesnenin trnden olmad durumda nesnelerin typeid 'lerini karlatrarak sralamay derleyiciye brakyoruz. Ayn olduklarnda ise nce x 'e sonra y 'ye gre karlatryoruz: class Nokta { int x; int y; Renk renk; // ... override const int opCmp(Object o) { if (typeid(this) != typeid(o)) { return typeid(this).opCmp(typeid(o)); } auto sadaki = cast(const Nokta)o; return (x != sadaki.x ? x - sadaki.x : y - sadaki.y);

3. Aklamalar kodun iine yazdm: class genBlge { Nokta[3] noktalar; // ...

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

635

Problem zmleri

override const bool opEquals(Object o) { auto sadaki = cast(const genBlge)o; return sadaki && (noktalar == sadaki.noktalar); } override const int opCmp(Object o) { if (typeid(this) != typeid(o)) { return typeid(this).opCmp(typeid(o)); } auto sadaki = cast(const genBlge)o; foreach (i, nokta; noktalar) { const int karlatrma = nokta.opCmp(sadaki.noktalar[i]); if (karlatrma != 0) { /* Sralamalar bu noktada belli oldu. */ return karlatrma; }

/* Buraya kadar gelinmise eitler demektir. */ return 0;

override const hash_t toHash() { /* 'noktalar' yesini bir dizi olarak tanmladmz * iin dizilerin toHash algoritmasndan * yararlanabiliriz. */ return typeid(noktalar).getHash(&noktalar); }

83.44

Gstergeler zmleri
1. Parametre trleri yalnzca int olduunda ileve main() iindeki deikenlerin kopyalarnn gnderildiklerini biliyorsunuz. main() iindeki deikenlerin referanslarn edinmenin bir yolu, parametreleri ref int olarak tanmlamaktr. Dier bir yol, o deikenlere eriim salayan gstergeler gndermektir. Programn deien yerlerini sar ile iaretliyorum: void deiToku(int * birinci, int * ikinci) { int geici = *birinci; *birinci = *ikinci; *ikinci = geici; } void main() { int i = 1; int j = 2; deiToku(&i, &j); assert(i == 2); assert(j == 1);

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

636

Problem zmleri

2. Hem Dm hem de Liste int trne bal olarak yazlmlar. Bu iki yapy ablona dntrmek iin tanmlanrken isimlerinden sonra (T) eklemek ve ilerindeki int 'leri T ile deitirmek yeter. Deien yerlerini saryla iaretliyorum: struct Dm(T) { T eleman; Dm * sonraki; this(T eleman, Dm * sonraki) { this.eleman = eleman; this.sonraki = sonraki; } string toString() const { string sonu = to!string(eleman); if (sonraki) { sonu ~= " -> " ~ to!string(*sonraki); } } return sonu;

struct Liste(T) { Dm!T * ba; void banaEkle(T eleman) { ba = new Dm!T(eleman, ba); } string toString() const { return format("(%s)", ba ? to!string(*ba) : ""); }

Liste 'yi artk int 'ten baka trlerle de deneyebiliriz: import std.stdio; import std.conv; import std.string; // ... struct Nokta { double x; double y; string toString() const { return format("(%s,%s)", x, y); }

void main() { Liste!Nokta noktalar; noktalar.banaEkle(Nokta(1.1, 2.2)); noktalar.banaEkle(Nokta(3.3, 4.4)); noktalar.banaEkle(Nokta(5.5, 6.6));

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

637

Problem zmleri

writeln(noktalar);

kts: ((5.5,6.6) -> (3.3,4.4) -> (1.1,2.2)) 3. Bu durumda sondaki dm gsteren bir yeye daha ihtiyacmz olacak. Aklamalar programn iine yerletirdim: struct Liste(T) { Dm!T * ba; Dm!T * son; void sonunaEkle(T eleman) { /* Sona eklenen elemandan sonra dm bulunmad iin * 'sonraki' dm olarak 'null' gnderiyoruz. */ auto yeniDm = new Dm!T(eleman, null); if (!ba) { /* Liste bomu. imdi 'ba' bu dmdr. */ ba = yeniDm; } if (son) { /* u andaki 'son'dan sonraya bu dm * yerletiriyoruz. */ son.sonraki = yeniDm; } /* Bu dm yeni 'son' oluyor. */ son = yeniDm;

void banaEkle(T eleman) { auto yeniDm = new Dm!T(eleman, ba); /* Bu dm yeni 'ba' oluyor. */ ba = yeniDm; if (!son) { /* Liste bomu. imdi 'son' bu dmdr. */ son = yeniDm; }

string toString() const { return format("(%s)", ba ? to!string(*ba) : ""); }

Yukardaki Nokta nesnelerinin tek deerli olanlarn baa, ift deerli olanlarn sona ekleyen bir deneme: void main() { Liste!Nokta noktalar; foreach (i; 1 .. 7) { if (i % 2) { noktalar.banaEkle(Nokta(i, i));

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

638

Problem zmleri

} }

} else { noktalar.sonunaEkle(Nokta(i, i)); }

writeln(noktalar);

kts: ((5,5) -> (3,3) -> (1,1) -> (2,2) -> (4,4) -> (6,6))

83.45

Bit lemleri zmleri


1. IP adreslerinin her zaman 4 paradan olutuu bilindiinden bu kadar ksa bir ilevde sihirli sabitler kullanlabilir. Bunun nedeni, aksi taktirde aralara nokta karakterlerinin yerletirilmesinin ek bir karmaklk getireceidir. string noktalOlarak(uint ipAdresi) { return format("%s.%s.%s.%s", (ipAdresi >> 24) & (ipAdresi >> 16) & (ipAdresi >> 8) & (ipAdresi >> 0) & }

0xff, 0xff, 0xff, 0xff);

Kullanlan tr iaretsiz bir tr olduu iin soldan her zaman iin 0 deerli bitler geleceini hatrlarsak, 24 bit kaydrldnda ayrca maskelemeye gerek yoktur. Ek olarak, sfr kere kaydrmann da hibir etkisi olmadndan o ilevi biraz daha ksa olarak yazabiliriz: string noktalOlarak(uint ipAdresi) { return format("%s.%s.%s.%s", ipAdresi >> 24, (ipAdresi >> 16) & 0xff, (ipAdresi >> 8) & 0xff, ipAdresi & 0xff); } Buna ramen daha okunakl olduu iin birinci ilev yelenebilir nk etkisiz olan ilemler baz durumlarda zaten derleyici tarafndan elenebilir. 2. Her bayt IP adresinde bulunduu yere kaydrlabilir ve bu deerler "veya"lanabilir: uint ipAdresi(ubyte bayt3, ubyte bayt2, ubyte bayt1, ubyte bayt0) { return (bayt3 << 24) | (bayt2 << 16) | (bayt1 << 8) | (bayt0 << 0); } // en yksek deerli bayt // en dk deerli bayt

3. Aadaki yntem btn bitlerin 1 olduu deerle balyor. nce bitleri saa kaydrarak st bitlerin 0 olmalarn, daha sonra da sola kaydrarak alt bitlerin 0 olmalarn salyor:

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

639

Problem zmleri

uint maskeYap(int dkBit, int uzunluk) { uint maske = uint.max; maske >>= (uint.sizeof * 8) - uzunluk; maske <<= dkBit; return maske; } uint.max , btn bitlerin 1 olduu deerdir. Onun yerine 0 deerinin tmleyeni de kullanlabilir: uint maske = ~0;

83.46

Yap ve Snflarda foreach zmleri


1. Araln ba ve sonuna ek olarak adm miktarnn da saklanmas gerekir. opApply iindeki dngdeki deer bu durumda adm kadar arttrlr: struct Aralk { int ba; int son; int adm; this(int ba, int son, int adm) { this.ba = ba; this.son = son; this.adm = adm; } int opApply(int delegate(ref int) ilemler) const { int sonu; for (int say = ba; say != son; say += adm) { sonu = ilemler(say); if (sonu) { break; } } } return sonu;

import std.stdio; void main() { foreach (eleman; Aralk(0, 10, 2)) { write(eleman, ' '); } } 2. import std.stdio; import std.string; class renci { string isim; writeln();

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

640

Problem zmleri

int numara; this(string isim, int numara) { this.isim = isim; this.numara = numara; } override string toString() { return format("%s(%s)", isim, numara); }

class retmen { string isim; string ders; this(string isim, string ders) { this.isim = isim; this.ders = ders; } override string toString() { return format("%s dersine %s retmen", ders, isim); }

class Okul { private: renci[] renciler; retmen[] retmenler; public: this(const renci[] renciler, const retmen[] retmenler) { this.renciler = renciler.dup; this.retmenler = retmenler.dup; } /* * Parametresi renci olduu iin bu 'delegate'i kullanan * opApply, foreach dng deikeninin renci olduu * durumda arlr. */ int opApply(int delegate(ref renci) ilemler) { int sonu; foreach (renci; renciler) { sonu = ilemler(renci); if (sonu) { break; }

} }

return sonu;

/* * Benzer ekilde, bu opApply da foreach dng deikeni * retmen olduunda arlr. */

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

641

Problem zmleri

int opApply(int delegate(ref retmen) ilemler) { int sonu; foreach (retmen; retmenler) { sonu = ilemler(retmen); if (sonu) { break; }

} }

return sonu;

void girintiliYazdr(T)(T nesne) { writeln(" ", nesne); } void main() { auto okul [ new new new new

= new Okul( renci("Can", 1), renci("Canan", 10), renci("Cem", 42), renci("Cemile", 100) ],

[ new retmen("Nazmiye", "Matematik"), new retmen("Makbule", "Trke") ]); writeln("renci dngs"); foreach (renci renci; okul) { girintiliYazdr(renci); } writeln("retmen dngs"); foreach (retmen retmen; okul) { girintiliYazdr(retmen); }

kts: renci dngs Can(1) Canan(10) Cem(42) Cemile(100) retmen dngs Matematik dersine Nazmiye retmen Trke dersine Makbule retmen ki ilevin dizi trleri dnda ayn olduklarn gryoruz. Buradaki ortak ilemleri dizinin trne gre deien bir ilev ablonu olarak yazabilir ve iki opApply 'dan bu ortak ilevi arabiliriz: class Okul { // ... int opApplyOrtak(T)(T[] dizi, int delegate(ref T) ilemler) { int sonu;

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

642

Problem zmleri

foreach (eleman; dizi) { sonu = ilemler(eleman); if (sonu) { break; }

} }

return sonu;

int opApply(int delegate(ref renci) ilemler) { return opApplyOrtak(renciler, ilemler); } int opApply(int delegate(ref retmen) ilemler) { return opApplyOrtak(retmenler, ilemler); }

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

643

Szlk

84

Szlk
Bu szlk D programlama dili dersleri D.ershane'de karlalan terimleri ierir. D.ershane hl yazm aamasnda olduu iin, dersler arttka buradaki terimler de zamanla oalacak. aka elle yaplan: [explicit], programc tarafndan ak olarak yaplan aklama satr: [comment], program aklamak iin programn iine yazlan satr adres: [address], deikenin (veya nesnenin) bellekte bulunduu yer akm: [stream], nesnelerin art arda eriildii giri k birimi algoritma: [algorithm], verilerin ilenme admlar, ilev alt dzey: [low level], donanma yakn olanak alt snf: [subclass], baka snftan tretilen snf anahtar szck: [keyword], dilin kendisi iin ayrm olduu ve i olanaklar iin kulland szck aralk: [range], belirli biimde eriilen bir grup eleman arayz: [interface], yapnn, snfn, veya modln sunduu ilevler arttrma: [increment], deerini bir arttrmak atama: [assign], deikene yeni bir deer vermek azaltma: [decrement], deerini bir azaltmak balayc: [linker], derleyicinin oluturduu program paralarn bir araya getiren program bal liste: [linked list], her eleman bir sonraki eleman gsteren veri yaps belirsiz sayda parametre: [variadic], ayn ilevi deiik sayda parametreyle arabilme olana bayrak: [flag], bir ilemin veya sonucun geerli olup olmadn bildiren bit bayt: [byte], 8 bitlik tr bayt sras: [endianness], veriyi oluturan baytlarn bellekte sralanma dzeni bildirim: [declare], tanmn vermeden belirtmek birim testi: [unit test], programn alt birimlerinin bamsz olarak denetlenmeleri birlik: [union], birden fazla deikeni ayn bellek blgesinde depolayan veri yaps bit: [bit], 0 ve 1 deerlerini alabilen en temel bilgi birimi blok: [block], kme parantezleriyle gruplanm ifadelerin tm BOM: [BOM, byte order mark], dosyann en bana yazlan Unicode kodlama belirteci bool: [bool], Bool ifadelerinin sonularn tayan tr; doruluk kavram iin true , yanllk kavram iin false break: [break], dngden veya switch'ten kartan deyim

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

644

Szlk

byk soncul: [big endian], deerin st bitlerini oluturan baytn bellekte nceki adreste bulunduu ilemci mimarisi continue: [continue], dngnn bana gtren deyim CTFE: [Compile Time Function Evaluation], derleme zamannda ilev iletme karsama: [deduction], derleyicinin kendiliinden anlamas kt: [output], programn bilgi olarak rettii herey ok ekillilik: [polymorphism], baka bir tr gibi davranabilmek okuzlu: [tuple], bir ka parann diziye benzer ekilde bir araya gelmesinden oluan yap kme: [crash], programn hata ile sonlanmas p toplayc: [garbage collector], ii biten nesneleri sonlandran dzenek deer: [value], ay adedi 12 gibi isimsiz bir byklk deer tr: [value type], deer tayan tr deiken: [variable], kavramlar temsil eden veya snf nesnesine eriim salayan program yaps deimez: [immutable], programn almas sresince kesinlikle deimeyen derleyici: [compiler], programlama dili kodunu bilgisayarn anlad makine koduna eviren program deyim: [statement], ifadelerin iletilmelerini ve sralarn etkileyen program yaps dilim: [slice], baka bir dizinin bir blmne eriim salayan yap dinamik: [dynamic], alma zamannda deiebilen dizgi: [string], "merhaba" gibi bir karakter dizisi dizi: [array], elemanlar yan yana duran ve indeksle eriilen topluluk do-while: [do-while], ilemleri en az bir kere ileten dng dng: [loop], tekrarlanan program yaps dn deeri: [return value], ilevin reterek dndrd deer duyarlk: [precision], saynn belirgin hane says dzen: [format], bilginin giri ve kta nasl dzenlendii eleman: [element], topluluktaki verilerin her biri emekliye ayrlan: [deprecated], hl kullanlan ama yaknda geersiz olacak olanak emirli programlama: [imperative programming], ilemlerin deyimler halinde adm adm belirlendikleri programlama yntemi eniyiletirme: [optimization], kodun daha hzl alacak ekilde davran bozulmadan deitirilmesi enum: [enum], isimli sabit deer olana eleme tablosu: [associative array], elemanlarna tamsay olmayan indekslerle de eriilebilen veri yaps (bir 'hash table' gerekletirmesi)

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

645

Szlk

e zamanl programlama: [concurrency], i paracklarnn birbirlerine baml olarak ilemeleri etiket: [label], kod satrlarna isimler vermeye yarayan olanak evrensel: [global], evrensel isim alannda tanmlanm, eriimi kstlanmam fonksiyonel programlama: [functional programming], yan etki retmeyen programlama yntemi for: [for], while'n birimlerinin hepsini ayn yerde tanmlayan deyim geici: [temporary], bir ilem iin geici olarak oluturulan ve yaam ksa sren deiken veya nesne genel eriim: [public], herkese ak eriim gerekletirme: [implementation], kodun oluturulmas grev: [task], programn geri kalanyla kout iletilebilen ilem birimi gsterge: [pointer], bir deikeni gsteren deiken hata atma: [exceptions], devam edilemeyecei iin ilemin sonlandrlmas hata ayklama: [debug], programn hatalarn bulma ve giderme hazr deer: [literal], kod iinde hazr olarak yazlan deerler i olanak: [core feature], dilin ktphane gerektirmeyen bir olana ifade: [expression], programn deer oluturan veya yan etki reten bir blm ikili say sistemi: [binary system], iki rakamdan oluan say sistemi ilklemek: [initialize], ilk deerini vermek indeks: [index], topluluk elemanlarna erimek iin kullanlan bilgi infinity: [infinity], sonsuzluk isim alan: [name space], ismin geerli olduu kapsam isim gizleme: [name hiding], st snfn ayn isimli yelerinin alt snftakiler tarafndan gizlenmeleri istemci: [client], sunucu programn hizmetlerinden yararlanan program iaretli tr: [signed type], eksi ve art deer alabilen tr iaretsiz tr: [unsigned type], yalnzca art deer alabilen tr ile: [operator], bir veya daha fazla deikenle i yapan zel iaret (||, &&, +, -, =, vs.) ilev: [function], programdaki bir ka adm bir araya getiren program paras i parac: [thread], iletim sisteminin program iletme birimi kalan: [modulus], blme ileminin kalan deeri kaltm: [inheritance], baka bir trn yelerini treme yoluyla edinmek kapama: [closure], ilemleri ve iledikleri kapsam bir arada saklayan program yaps kapsam: [scope], kme parantezleriyle belirlenen bir alan

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

646

Szlk

karakter: [character], 'a', '', '\n', gibi en alt dzey metin paras karakter kodlamas: [character encoding], karakter kodlarnn ifade edilme yntemi katma: [mixin], program iine otomatik olarak kod yerletirme kayan noktal say: [floating point], kesirli say kaynak dosya: [source file], programcnn yazd kodu ieren dosya krplma: [truncate], saynn virglden sonrasnn kaybedilmesi kstlama: [constraint], ablon parametrelerinin uymas gereken koullarn belirlenmesi klasr: [directory], dosyalar barndran dosya sistemi yaps (dizin) kod tablosu: [code page], 127'den byk karakter deerlerinin bir dnya dili iin tanmlanmalar kontrol karakteri: [control character], yeni satr aan '\n' , yatay sekme karakteri '\t' , gibi zel karakterler kopya sonras: [post blit], yelerin kopyalanmalarndan sonraki ilemler kopyalama: [copy construct], nesneyi baka bir nesnenin kopyas olarak kurmak korumal eriim: [protected], belirli lde korumal eriim kout ilemler: [parallelization], bamsz ilemlerin ayn anda iletilmeleri kurma: [construct], yap veya snf nesnesini kullanlabilir duruma getirmek kurucu ilev: [constructor], nesneyi kuran ilev kk soncul: [little endian], deerin alt bitlerini oluturan baytn bellekte nceki adreste bulunduu ilemci mimarisi ktphane: [library], belirli bir konuda zm getiren tr tanmlarnn ve ilevlerin bir araya gelmesi makine kodu: [machine code], mikro ilemcinin dili mantksal ifade: [logical expression], deeri false veya true olan ifade metin dzenleyici: [text editor], metin yazmaya yarayan program mikro ilemci: [CPU], bilgisayarn beyni mikro ilemci ekirdei: [CPU core], bal bana mikro ilemci olarak kullanlabilen ilemci birimi modl: [module], programn veya ktphanenin ilev ve tr tanmlarndan oluan bir alt birimi mutlak deimez: [invariant], nesnenin tutarll asndan her zaman iin doru olan nan: [nan], "not a number", geerli bir say gsterimi deil nesne: [object], belirli bir snf veya yap trnden olan deer nitelik: [property], bir trn veya nesnenin bir zellii null: [null], hibir nesneye eriim salamayan on altl say sistemi: [hexadecimal system], on alt rakamdan oluan say sistemi

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

647

Szlk

ortam deikeni: [environment variable], program balatan ortamn sunduu PATH gibi deiken otomatik: [implicit], derleyici tarafndan otomatik olarak yaplan nceki deerli arttrma: [post-increment], sayy arttran ama nceki deerini reten ile nceki deerli azaltma: [post-decrement], sayy azaltan ama nceki deerini reten ile ncelik: [precedence], ilelerin hangi srada iletilecekleri zel eriim: [private], bakalarna kapal eriim zelleme: [specialization], ablonun bir zel tanm zyineleme: [recursion], bir ilevin dorudan veya dolayl olarak kendisini armas paket: [package], ayn klasrde bulunan modller parametre: [parameter], ileve iini yapmas iin verilen bilgi parametre deeri: [argument], ileve parametre olarak verilen bir deer Phobos: [Phobos], D dilinin standart ktphanesi program: [program], bilgisayara yapaca ileri bildiren bir dizi ifade program yt: [program stack], bellein ksa mrl deikenler iin kullanlan blgesi referans: [reference], asl nesneye, onun takma ismi gibi eriim salayan program yaps referans tr: [reference type], baka bir nesneye eriim salayan tr sabit: [const], bir balamda deitirilmeyen sanal say: [imaginary number], salt sanal deerden oluan karmak say sarma: [encapsulation], yelere dardan eriimi kstlamak sekme: [tab], kt dzeni iin kullanlan hayali stunlar sradzen: [hierarchy], snflarn treyerek oluturduklar aile aac sraszlk: [unordered], sra ilikisi olmama durumu snf: [class], kendi zerinde kullanlan ilevleri de tanmlayan veri yaps sihirli sabit: [magic constant], ne anlama geldii anlalmayan sabit deer sol deer: [lvalue], adresi alnabilen deer soyut: [abstract], somut gerekletirmesi verilmemi olan standart k: [standard output], program ktsnn normalde gnderildii akm standart giri: [standard input], program giriinin normalde okunduu akm statik: [static], derleme zamannda belirli olan sonlandrma: [destruct], nesneyi kullanmdan kaldrrken gereken ilemleri yapmak sonlandrc ilev: [destructor], nesneyi sonlandran ilev szlemeli programlama: [contract programming], ilevlerin giri k koullarn ve nesnelerin tutarlln denetleyen dil olana

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

648

Szlk

sunucu: [server], baka programlara hizmet eden program srm: [version], programn, olanaklarna gre farklar ieren hali ablon: [template], derleyicinin rnein 'trden bamsz programlama' iin kod retme dzenei takma isim: [alias], trn baka bir ismi tanm: [definition], bir ismin neyi ifade ettiinin belirtilmesi tanmsz davran: [undefined behavior], programn ne yapacann dil tarafndan tanmlanmam olmas tama; stten veya alttan: [overflow veya underflow], deerin bir tre samayacak kadar byk veya kk olmas tama: [move], bir yerden bir yere kopyalamadan aktarma tembel deerlendirme: [lazy evaluation], ilemlerin gerekten gerekene kadar geciktirilmesi topluluk: [container], ayn trden birden fazla veriyi bir araya getiren veri yaps tr dnm: [type conversion], bir deeri kullanarak baka bir trden deer elde etmek tretmek: [inherit], bir snf baka snfn alt tr olarak tanmlamak typeof: [typeof], ifadenin trn reten program yaps u birim: [terminal], bilgisayar sistemlerinin kullancyla etkileen giri/k birimi; "DOS ekran", "komut satr", "konsol" l ile: [ternary operator], ifadenin deerine gre ya birinci, ya da ikinci deeri reten ifade st dzey: [high level], donanmdan bamsz kavramlar temsil etmeye elverili st snf: [super class], kendisinden snf tretilen snf ye: [member], yap veya snfn zel deikenleri ve nesneleri ye ilev: [member function], yap veya snfn zel ilemleri varsaylan: [default], zellikle belirtilmediinde kullanlan veri yaplar: [data structures], verilerin bilgisayar biliminin tanmlad ekilde saklanmalar ve ilenmeleri while: [while], ifadeleri baka bir mantksal ifade doru olduu srece ileten kapsam yan etki: [side effect], bir ifadenin, rettii deer dndaki etkisi yap: [struct], baka verileri bir araya getiren veri yaps yaam sreci: [object lifetime], bir deikenin veya nesnenin tanmlanmasndan iinin bitmesine kadar geen sre yazma: [register], mikro ilemcinin en temel i depolama ve ilem birimi ykleme: [overloading], ayn isimde birden ok ilev tanmlama

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

649

You might also like