Professional Documents
Culture Documents
Programlama Dili
Ali ehreli
D.ershane Serisi
0.1
0.2
0.2.1
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
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.
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.
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
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
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...
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.
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.
10
11
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
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
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.
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
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
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
creal
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
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 : }
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
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:
17
Deikenler
import std.stdio; void main() { // Hem tanm, hem atama: int renci_says = 100; } writeln("Bu okulda ", renci_says, " 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
18
10
10.1
Problem
Yukardaki programda stdout 'u yine writeln ilemiyle kullann ama bir seferde birden fazla deiken yazdrn. ... zm
19
11
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.
20
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
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]
21
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
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
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);
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
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.
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
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);
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
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.
29
if Koulu
13.2
13.3
13.4
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"); }
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
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
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.
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.
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...
36
while Dngs
... zmler
37
15
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.
38
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
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:
39
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
...
40
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
41
en dk int deeri : en yksek int deeri : saynn nceki deeri : saynn sonraki deeri:
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);
42
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;
43
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); }
44
81
15.1.15
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:
45
import std.stdio; void main() { int say_1 = 1; int say_2 = -2; writeln(+say_1); writeln(+say_2);
1 -2
15.1.18
// 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.
46
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
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.
47
Aslnda byle bir durumda en doru zm; bir tamsay tr deil, kesirli say trlerinden birisini kullanmaktr: float , double , veya real .
15.1.22
9 Yine, ilemlerin srasn deitirince krplma olmayaca iin sonu doru kar: writeln(10 * 9 / 9);
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);
48
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
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:
50
Kesirli Saylar
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
", ", ", ", ", ", ", ", ", ", ", ",
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);
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
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
16.9
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
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
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;
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.
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
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");
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.
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
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
// 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.
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();
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.
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
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.
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
18.3
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:
65
Karakterler
wchar para_sembol = '\€'; 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\€");
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
66
Karakterler
18.6
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("\€ harf midir? ", isUniAlpha('\€')); writeln("'nin k: ", toUniLower('')); writeln("'nin k: ", toUniLower('')); writeln("'nin by: ", toUniUpper('')); writeln("'nn by: ", toUniUpper(''));
kts:
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
18.8.1
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
68
Karakterler
18.9
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.
69
Karakterler
18.10
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
70
19
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;
71
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];
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
72
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
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
73
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:
74
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
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 |= .
75
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
76
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 = [
];
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 = [
];
[ [ [ [
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 = [
77
];
[ 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:
78
[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
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
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:
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!
readln ve chomp zincirleme biimde daha ksa olarak da yazlabilirler: string isim = chomp(readln()); O yazm string trn tanttktan sonra kullanmaya balayacam.
20.2
20.3
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
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);
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);
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);
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
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.
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
86
21
21.1
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
87
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
21.4
21.5
Problem
kiden fazla program art arda balamay deneyin:
88
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
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
22.1.6
22.1.7
91
Dosyalar
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.
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
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
94
auto ve typeof
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
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
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
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.
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 dngs srasnda bu blmler u srada iletilirler: hazrlk koul denetimi asl ilemler ilerletilmesi koul denetimi asl ilemler ilerletilmesi ...
25.2
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
25.4
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.
100
for Dngs
... zmler
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");
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
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
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
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.
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
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:
107
Hazr Deerler
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 '\€' , '\♥' , ve '\©' karakteridir.
27.4
Dizgiler
Hazr dizgiler sabit karakterlerin bileimlerinden oluurlar ve ok sayda farkl sz dizimiyle yazlabilirler.
27.4.1
27.4.2
c:\nurten c: urten
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
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);
27.4.6
27.5
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
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
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);
1100 14 c 12
e : Kesirli say, aadaki blmlerden oluacak ekilde yazdrlr. virglden nce tek hane
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
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");
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);
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);
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
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);
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);
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
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
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, ¬); "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
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
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; "); }
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
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
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.
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
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
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" ];
: : : : :
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;
123
Eleme Tablolar
Ne yapabilirsiniz? Her renci iin birden fazla not tutabilen bir eleme tablosu tanmlayn. ... zmler
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
125
foreach Dngs
32.2
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
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:
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
32.6
32.7
127
foreach Dngs
32.8
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
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
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.
130
switch ve case
33.1
33.2
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
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
33.6
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
133
enum
34
enum
enum , "numaralandrmak" anlamna gelen "enumerate"in ksaltlmdr. simli sabit deerler retmek iin kullanlr.
34.1
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
135
enum
34.4
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);
136
enum
34.7
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
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");
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) {
139
levler
0 1 2 3 4
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.
140
levler
35.3
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:
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.
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
// 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) {
143
levler
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); }
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:
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.
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' : "-.-.",
147
levler
];
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"
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);
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.
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
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
36.1.2
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);
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
153
const ve immutable
36.2.1
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); }
Parametresinin programn almas sresince kesinlikle deimeyecek olmasn isteyen ve bu yzden de onu immutable olarak belirlemi olan bir ilevi de .idup ile armamz gerekebilir:
154
const ve immutable
void foo(string dizgi) { // ... } void main() { char[] selam; foo(selam); foo(selam.idup); }
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; }
abahrem
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.
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);
157
lev Parametreleri
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);
levi armadan nce : leve girildiinde : levden klrken : levden dnldkten sonra:
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
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.
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:
160
lev Parametreleri
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() {
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;
162
lev Parametreleri
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;
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
164
lev Parametreleri
37.3.7
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);
165
lev Parametreleri
1 2
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() {
167
Tembel Deerlendirmeler
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:
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) {
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
170
39
39.1
39.1.1
171
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!"); }
39.1.2
Programn isminin deneme olduunu kabul edersek ve istenen aralkta bir sayyla balatrsak, programn dn deeri 0 olur:
172
# ./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
39.3
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)
173
# ./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
174
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:
175
# ./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")); }
176
# ./deneme /usr/local/bin:/usr/bin:/home/acehreli/dmd/linux/bin
39.6
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
177
40
178
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
40.1.1
179
40.1.2
180
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:
181
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);
182
// ... 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.
183
40.1.3
40.2
184
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;
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
185
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
186
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
187
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.
188
40.2.3
40.3
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;
189
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.
190
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
191
* 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
192
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
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:
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
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
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
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)
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);
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
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
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
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
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:
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
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
204
Birim Testleri
$ ./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() {}
205
Birim Testleri
O tanmdan balayn; ilk test yznden hata atldn grn; ve ilevi hatay giderecek ekilde yazn. ... zm
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
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
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
// ...
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
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);
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
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 { // ... }
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
211
44
44.1
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.
212
44.2
// deeri ileve kopyalanr // ileve kendisi olarak ve kendi // deeriyle gnderilir // ileve kendisi olarak gnderilir; // ileve girildii an deeri sfrlanr
// 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
{ }
// ...
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.
213
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.
214
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.
215
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.
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 | ---+-----+---
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
45.2
Referans deikenleri
Referans trlerini anlatmaya gemeden nce referans deikenlerini tantmam gerekiyor.
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:
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:
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
221
Deerler ve Referanslar
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
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;
222
Deerler ve Referanslar
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
45.4
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();
void bilgiSatr(const dchar[] balk, bool deerEitlii, bool adresEitlii) { writefln("%50s%9s%9s", balk, to!string(deerEitlii), to!string(adresEitlii)); }
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, ¶metre == &evrenselDeiken); } void refParametre(ref int parametre) { bilgiSatr("ref parametreli ilev", parametre == evrenselDeiken, ¶metre == &evrenselDeiken);
225
Deerler ve Referanslar
} void inParametre(in int parametre) { bilgiSatr("in parametreli ilev", parametre == evrenselDeiken, ¶metre == &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.
226
46
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:
227
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 }
228
46.4
---+-------------------+--| | ---+-------------------+---
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
229
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
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
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:
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
// 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:
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;
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
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
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:
235
Tr Dnmleri
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.
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
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, daha sonradan yap nesneleri oluturulduunda ne tr ye deikenlerinin olacan belirler: struct GnnSaati { int saat; int dakika; }
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) { // ... }
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:
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:
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
48.3.2
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
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
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
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.
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.
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:
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
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
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:
249
Parametre Serbestlii
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
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
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
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,
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
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
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);
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:
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:
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
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:
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
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); }
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
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
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
263
52
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
52.3
264
// 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
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 {
265
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.
266
53
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:
267
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
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
268
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
269
// 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
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
270
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
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
271
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) {
272
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:
273
<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
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) {
274
// ...
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
275
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;
// 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.
276
53.4.1
ref Sre opAssign(int dakika) { writeln( "dakika, bir tamsay deer ile deitiriliyor"); this.dakika = dakika; } return this;
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.
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
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
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
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;
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
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);
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
// sttekinin edeeri
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; }
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
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]
285
le Ykleme
54.9
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.
286
le Ykleme
54.10
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
// 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
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
54.12
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
54.14
290
le Ykleme
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); }
bool opBinaryRight(string ile) (const ref GnnSaati zaman) const if (ile == "in") { return (zaman >= ba) && (zaman < son); }
void main()
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
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
293
Snflar
55.1.3
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; }
294
Snflar
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; }
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
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.
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.
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;
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:
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
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
300
Treme
// 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
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
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; }
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
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.
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
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.
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
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
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 ...
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:
308
Treme
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() {
309
Treme
writeln("yolcular iniyor");
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.
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);
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
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
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
313
Object
{ } }
// ...
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.
// // // //
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)
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.
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.
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:
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 {
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() {
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
} }
// ...
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
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
57.4.3
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.
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
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));
323
Object
57.4.5
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:
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
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(); }
O arayz ile kullanlabilmeleri iin, interface 'ten treyen snflarn interface 'in bildirdii ilevleri tanmlamalar gerekir.
58.2
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
O tanm u iki ilikiyi birden salar: "telefon bir sesli alettir", ve "telefon bir haberleme aletidir".
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
58.5
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 ""; }
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:
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 ... }
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;
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
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 )
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
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()
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
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
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:
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>
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;
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, '>');
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:
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>
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:
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>
59.5
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.
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
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 {
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
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
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
// 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
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.
344
61
345
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.
346
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.
347
61.4
// 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
348
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
349
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);
350
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.
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
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
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
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_;
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.
356
63
} out (sonu) { assert(sonu >= 0); } body { const double yarevre = return sqrt(yarevre * (yarevre * (yarevre * (yarevre }
(a + b + c) / 2; - a) - b) - c));
63.1
357
{ } body {
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
} 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:
358
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
359
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.
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.
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);
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) {
362
ablonlar
writefln("(%s)", deer);
Programn kts da o farkl ilevin etkisini gsterecek ekilde, her tr iin farkldr: (42) (1.2) (merhaba)
64.2
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, "-=", "=-");
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
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:
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:
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
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:
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
367
ablonlar
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
368
ablonlar
64.8
64.9
64.10
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;
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); }
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(); }
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
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); }
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
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
375
ablonlar
376
65
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
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.
377
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:
378
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
379
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;
380
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; }
381
class AltSnf : stSnf { string ehir() const { return "Kayseri"; } } alias stSnf.ehir ehirKodu;
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.
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
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:
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
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:
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
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.
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
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);
387
Gstergeler
66.5
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);
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
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.)
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:
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
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
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
Byle ilemlerde kullanlabilmesi iin, void* 'nin deerinin nce doru tr gsteren bir gstergeye aktarlmas gerekir: int tamsay = 42; void * herTrGsterebilen = &tamsay; // (1) // (2)
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
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
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 }
ktdan anlald gibi; new , temel tr ve yaplar iin gsterge trnde bir deer dndrmektedir: int* int[] Yap* Snf
66.13
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
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.
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
Tabii ayn ilemi gsterge kullanmadan da gerekletirebiliriz: uniform(0, 2) ? ++yazAdedi : ++turaAdedi; Veya bir if kouluyla: if (uniform(0, 2)) { ++yazAdedi; } else { ++turaAdedi; }
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
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; } // ...
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) : ""); }
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
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)
} 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);
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
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:
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
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
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");
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
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:
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
-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
408
Bit lemleri
Basamak 7 6 5 4 3 2 1 0
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:
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
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.
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:
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
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);
67.4.6
67.4.7
413
Bit lemleri
En soldaki bit deerleri kaybedilir ve sa taraftan 0 deerli bitler gelir: 00000111010110111100110100010101 075bcd15 123456789 01110101101111001101000101010000 75bcd150 1975308624
67.4.8
// 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
67.5.2
414
Bit lemleri
67.5.3
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
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
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
123456789 4096
Sorgulama ifadesinde birden fazla 1 kullanarak o bitlerin hepsinin birden asl ifadede 1 olup olmadklar da sorgulanabilir.
67.5.6
1000 Ayrntsna girmediim ikiye tmleyen sistemi nedeniyle, saa kaydrmak iaretli trlerde de ikiye blmektir: int deer = -8000; writeln(deer >> 3);
-1000
67.5.7
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:
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
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:
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); //
420
Bit lemleri
} ... zmler
// //
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:
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 {
423
Koullu Derleme
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
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); }
425
Koullu Derleme
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:
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
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
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() {
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.
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();
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
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:
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
68.6.6
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
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:
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
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
436
69
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); }
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;
437
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;
438
// ... 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:
439
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; }
440
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.
441
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.
442
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() {
443
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:
444
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
445
70
70.1
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();
446
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:
447
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
Kodun ktsndan anlald gibi, retro yukardaki ye ilevlerden yararlanarak bu aral ters srada kullanr: 6 5 4 3
70.2
448
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
449
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
450
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) { // ... }
451
Derleyici, deikenin trne uyan bir kapama retecek, ve o kapamaya uyan opApply ilevini aracaktr.
70.3
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
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 ---------------> | ---+------+------+------+------+------+------+------+------+---
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
454
Birlikler
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:
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.
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
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
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
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.
458
Etiketler
72.1.3
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.
459
Etiketler
ie dnglerden veya switch deyimlerinden hangisinin etkileneceini belirtmek iin break ve continue deyimlerinde etiket belirtilebilir.
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
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:
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
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
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:
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))
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.
465
Ayrntl ablonlar
73.3
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.
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);
467
Ayrntl ablonlar
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
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(); }
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:
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:
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;
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; }
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
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"; } }
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) {
476
Ayrntl ablonlar
// 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:
477
Ayrntl ablonlar
(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
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(); }())); }
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.
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);
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:
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
481
74
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
482
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;
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 ~ ')';
483
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
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));
484
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;
485
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:
486
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:
487
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.
488
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
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';
489
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 = " " " "
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:
490
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
491
6. bir istisna olarak, u nitelikler derleme zamannda iletilebilirler: .dup .length .keys .values
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:
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); }
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:
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");
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;
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);
kts: sre : sonu1: sonu2: sonu3: sonu4: 3 dakika 6 dakika -1 dakika 0 dakika 3 dakika
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:
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.
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.
500
Aralklar
76.2
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);
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:
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
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.
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
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
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]; }
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
506
Aralklar
76.5.3
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
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
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
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
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.
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
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);
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.
514
Aralklar
76.7
515
Aralklar
Aral ters srada kulland iin retro() ile ayn sonu elde edilir: [3, 2, 1]
76.8
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)
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; }
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 {
518
Aralklar
} // ... }
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; }
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;
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);
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();
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 ]);
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
523
Aralklar
76.9.1
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
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
525
77
77.1
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.
526
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
527
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(); }
Bylece rnein dizilerle kullanldnda elemanlara [] ileci ile eriilebilir: auto d = [ 1.5, 2.75 ]; auto e = eksili(d); writeln(e[1]);
528
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
529
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
530
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
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);
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.
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
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
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
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.
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
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:
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. */
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");
539
Kout lemler
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
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.
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
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.
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.)
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"); }
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);
545
E Zamanl Programlama
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
546
E Zamanl Programlama
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;
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)); }
/* 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));
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:
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
550
E Zamanl Programlama
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) {
551
E Zamanl Programlama
receive( (int mesaj) { writeln("int mesaj: ", mesaj); }, (string mesaj) { writeln("string mesaj: ", mesaj); }, (Sonlan mesaj) { writeln("kyorum"); tamam_m = true; });
80.6
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");
553
E Zamanl Programlama
hesap.send("merhaba"); hesap.send("3.4");
// hatal veri
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)); }
},
void main() { Tid hesap = spawn(&hesap, thisTid); hesap.send("1.2"); hesap.send("merhaba"); hesap.send("3.4"); hesap.send(Sonlan()); // hatal veri
554
E Zamanl Programlama
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.
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:
556
E Zamanl Programlama
veri: 42
80.8.1
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
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)
Yukardaki, hatay aklamaya yeten yalnzca bir durumdur. Onun yerine 10 i paracnn etkileimlerinden oluan ok sayda baka karmak durum da dnlebilir.
80.8.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:
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()
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() { // ...
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.
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.
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 {
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 {} // ...
564
Tr Nitelikleri
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() {}
565
Tr Nitelikleri
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
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"));
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); }
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()
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:
569
Tr Nitelikleri
deikenin bykl: 4 nesnenin bykl : 48 allMembers : Btn yelerin isimlerinden oluan dizi retir. class stSnf { double stSnfyesi; void stSnflevi() {}
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)();
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.
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.
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.
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:
574
Bellek Ynetimi
GC.minimize();
82.3
82.3.1
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.
576
Bellek Ynetimi
82.3.2
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:
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
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
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:
579
Bellek Ynetimi
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);
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:
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
581
Bellek Ynetimi
82.4.2
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
582
Bellek Ynetimi
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:
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
82.5.1
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:
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);
586
Bellek Ynetimi
82.5.2
// ...
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);
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
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;
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
590
Bellek Ynetimi
82.7
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.
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.
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
83.5
593
Problem zmleri
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
83.8
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.
594
Problem zmleri
83.9
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"); }
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))
596
Problem zmleri
83.11
83.12
2. Kalann 0 olup olmadn if koulu ile denetleyebiliriz: import std.stdio; void main() { int birinci_say; write("Birinci say: "); readf(" %s", &birinci_say);
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;
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
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
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;
// 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
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
// 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));
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
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); }
603
Problem zmleri
83.19
83.20
} 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("********");
604
Problem zmleri
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
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
83.25
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;
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();
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(),
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;
609
Problem zmleri
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
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:
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
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
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;
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
83.32
void main()
613
Problem zmleri
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
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
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
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.
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
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:
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() {}
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
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:
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);
622
Problem zmleri
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;
623
Problem zmleri
~= ~= ~= ~=
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
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
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 {
626
Problem zmleri
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();
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 {
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");
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;
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); }
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)));
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;
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:
632
Problem zmleri
import std.stdio; class DemirYoluArac { void ilerle(in int kilometre) { write(kilometre, " kilometre: "); foreach (i; 0 .. kilometre) { write(ses(), ' '); } } } writeln();
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(); }
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;
634
Problem zmleri
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);
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; }
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);
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));
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; }
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));
638
Problem zmleri
} }
writeln(noktalar);
kts: ((5,5) -> (3,3) -> (1,1) -> (2,2) -> (4,4) -> (6,6))
83.45
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:
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
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();
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;
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;
642
Problem zmleri
} }
return sonu;
int opApply(int delegate(ref renci) ilemler) { return opApplyOrtak(renciler, ilemler); } int opApply(int delegate(ref retmen) ilemler) { return opApplyOrtak(retmenler, ilemler); }
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
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)
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
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
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
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
649