15 Döngüler Belirli bir iş birden çok kez tekrarlanacaksa, programda bu iş bir kez yazılır ve döngü yapıları o deyim(ler)i istenildiği kadar tekrarlar. Ruby de bu işi yapan çok sayıda döngü yapıları vardır. Onlar için eksiksiz bir liste yapmak zordur. Ama öteki dillerde de var olan başlıca döngü yapılarını açıkladıktan sonra, bu yapıların değişik biçimleri olan ya da bağımsız olan bazı döngülere örnekler verecek ya da alıştırmalarda öğrenciyi keşfe heveslireceğiz. Ruby nin başlıca döngü yapıları şunlardır: while, until, for. Bu gruptakilerden birisinin değişik biçimi olan ya da grubun dışında kalan başka döngü yapıları vardır. Onları örneklerle açıklayacağız. Döngü Türü while döngüsü until döngüsü for döngüsü Açıklama Belirli bir mantıksal deyim true olduğu sürece döngü deyimleri tekrarlanır. Mantıksl deyimin denetimini döngünün başında yapar. While döngüsünün işlevini yapar; ama mantıksal deyimin denetimini döngünün sonunda yapar. O nedenle, döndü deyimleri en az bir kez yürütlür. Döngü deyimlerinin kaç kez tekrarlanacağını bir sayaç belirler Tablo 15.1: Ruby de Başlıca Döngü Yapıları
184 BÖLÜM 15. DÖNGÜLER 15.1 while döngüsü Bu döngü yapısı hemen her dilde var olan bir yapıdır. Bir ya da bir grup deyimin, belli bir koşul sağlandığı sürece tekrarlanması için kullanılan bir denetim yapısıdır. Sözdizimi şöyledir: Tanım 15.1. w h i l e ( boolean ) [ do ] deyim ( l e r ) 3 Deyim sayısı birden çoksa, onları blok parantezi içine almak gerekmez; ama okunurluğu kolaylaştırmak için do_ içindeki döngü deyimleri { } bloku içine alınabilir. Bunun nasıl yapılacağı aşağıda yan yana gösterimiştir. Bütün satırlar tek bir satıra yazılmadığı zaman boolean dan sonra gelen do sözcüğü konulmayabilir. Ama bütün döngü tek satıra yazıldığında do sözcüğü mutlaka gereklidir. Liste 15.1. w h i l e ( boolean ) w h i l e ( boolean ) do { deyim1 deyim1 deyim deyim deyimn} deyimn boolean (mantıksal deyim) true (doğru) ise deyim ya da blok içindeki deyimler yürütülür. Sonra program akışı başladığı while deyimine döner ve tekrar boolean mantıksal deyimini denetler. Mantıksal deyim doğru ise deyim ya da blok içindeki deyimler yeniden yürütülür. Bu döngü, mantıksal deyim false (yanlış) değerini alana kadar yinelenir. Örnekler: Program 15.1. # Yukarıya doğru sayma x = 0 4 w h i l e x <= 10 puts x x = x + 1 do
15.1. WHILE DÖNGÜSÜ 185 0 1 10 7 7.satır yerine x+ = 1 yazılabilir. Program 15.. # Geriye doğru sayma 3 x = 10 w h i l e x >= 0 puts x x = x 1 8 do 10 9 8 0 7 7.satır yerine x = 1 yazılabilir. Program 15.3. 3 8 i = 0 sum = 0 w h i l e i < 10 do i = i + 1 sum = sum + i puts " 0.. 9 a r a l ı ğ ı n d a k i s a y ı l a r ı n toplamı : #{sum} " 1 55 Program 15.4 while döngüsünde global değişken kullanıyor. Program 15.4.
186 BÖLÜM 15. DÖNGÜLER $x = 0 $sayaç = 5 w h i l e $x < $sayaç do puts ( " Döngünün i = #$x adımı " ) $x +=1 7 döngünün i = 0. adımı 3 döngünün i = 1. adımı döngünün i =. adımı döngünün i = 3. adımı döngünün i = 4. adımı Daha tıkız yazılış için, while döngüsü tek satıra yazılabilir. Bu durumda [do] anahtarına gerek vardır. Program 15.5 onun yapılışını gösteriyor. Program 15.5. # while ; tek s a t ı r 3 x = 0 w h i l e x < 10 do puts x = x + 1 1 1 3 6 10 Döngü tek satıra yazıldığında deyim(ler)in do_ bloku içine yazıldığına dikkat ediniz. Program 15.6. x = 100 w h i l e x > 0 3 x = 1 puts "Bu döngü #{x} kez t e k r a r l a r " #=> Bu döngü 99 kez t e k r a r l a r #=> Bu döngü 98 kez t e k r a r l a r 5 #=> #=> Bu döngü 0 kez t e k r a r l a r
15.1. WHILE DÖNGÜSÜ 187 Program 15.7. i =1 w h i l e i < 11 3 p r i n t "#{i } " i+=1 1 4 5 6 7 8 9 10 Array Üzerinde While Döngüsü Her dilde array üzerinde döngü vardır. Ruby de Program 15.8 deki gibi yapılır. Program 15.8. arr = [ "Can", " Melis ", " Pınar ", " Recep " ] i = 0 w h i l e arr [ i ] puts arr [ i ] i += 1 7 Can 3 Melis Pınar Recep 15.1.1 Sonsuz Döngü Döngü adımlarının bağlı olduğu boolean değer değiştirmezse (true iken false, false iken true olmazsa), döngü deyimleri sürekli tekrarlanır.buna sonsuz döngü denilir. Ruby nin irb kabuğunda sonsuz döngüyü kesmek için Ctrl + C tusuna basınız. Aşağıdaki örneklerde while döngüsünün her adımında boolean ifade değişmiyor. O nedenle döngü sona ermiyor, döngü adımları durmadan tekrarlanıyor. Program 15.9.
188 BÖLÜM 15. DÖNGÜLER w h i l e 1 puts " Merhaba, a d ı n ı z nedir? ( A d ı n ı z ı g i r i n c e Enter e b a s ı n ı z ) " ad = g e t s. chomp 4 puts "Bu döngüden a s l a çıkamazsınız, #{ad }!! \ n\n " Burada true koşulu hiç değişmiyor. Dolayısıyla, döngü sona ermez. Program 15.10. x = 1 w h i l e t r u e puts ( " Giderek sonsuza y a k l a ş ı y o r u z : #{x} " ) x += 1 5 15. until until döngüsü do-while döngüsüne denk olan bir denetim yapısıdır. until döngüsü, while döngüsünün işlevini görür. Ancak mantıksal deyim, döngü deyimlerinden sonra denetlenir. O nedenle, bu yapıda döngü deyimleri en az bir kez yürütülür. Genel sözdizimi şöyledir: Liste 15.. u n t i l boolean [ do ] deyim ( l e r ) Program 15.11, 10 dan başlayıp yukarı doğru 14 e kadar sayıyor. Program 15.11. # Yukarıya doğru sayma x = 10 u n t i l x > 15 do puts x x += 1 7 10 3 11 14 Program 15.1, döngünün adımlarını sayıyor.
15.. UNTIL 189 Program 15.1. 4 # g l o b a l değişken $ i = 0 $num = 5 u n t i l $ i > $num do puts ( " Until döngüsünün i = #$ i. adımı " ) $ i +=1; Until döngüsünün i = 0. adımı Until döngüsünün i = 1. adımı Until döngüsünün i =. adımı Until döngüsünün i = 3. adımı Until döngüsünün i = 4. adımı 7 Until döngüsünün i = 5. adımı Program 15.13. # İ l e r i y e doğru sayma x = 0 u n t i l x >= 10 do puts x 7 x += 1 Program 15.14. i =1 u n t i l i > 10 p r i n t "#{i } " i+=1 1 4 5 6 7 8 9 10 Program 15.15. # encoding utf 8 gün_say = 7 ; u n t i l gün_say == 0 puts " Şimdi haftanın #{gün_say} günü var " 7 gün_say = 1
190 BÖLÜM 15. DÖNGÜLER Şimdi haftanın 7 günü var Şimdi haftanın 6 günü var Şimdi haftanın 5 günü var Şimdi haftanın 4 günü var Şimdi haftanın 3 günü var 7 Şimdi haftanın günü var Şimdi haftanın 1 günü var Bu program daha tıkız yazılabilir. Program 15.16. 1 # encoding utf 8 gün_ sayısı = 8 puts " Henüz haftadan kalan #{gün_ sayısı = 1} gün var " u n t i l gün_ sayısı == 1 1 Henüz haftadan kalan 7 gün var Henüz haftadan kalan 6 gün var Henüz haftadan kalan 5 gün var Henüz haftadan kalan 4 gün var 6 Henüz haftadan kalan 3 gün var Henüz haftadan kalan gün var Henüz haftadan kalan 1 gün var chomp chomp metodu String in sonundaki \n, \r ve \r\n karakterlerini yokeder. Program 15.17 de puts metodunun koyduğu \r\n karakterlerini yokeder..satır, gets ile kullanıcıdan alınan string biçimindeki sayıyı ortaya çıkarır. Program 15.17. 1 p r i n t Bir s a y ı g i r i n i z x = g e t s. chomp. to_i u n t i l x < 0 puts x 6 x = 1 puts "Tamam! " Program kullanıcıdan bir sayı girmesini istiyor. chomp metodu satırbaşı karakterini siliyor. Kullanıcının girdiği stringi to_i metodu Integer tipine dönüştürüyor. Sayısısal olmayan değer girilirse 0 yazar. 1 " abc ". chomp. to_i # => 0
15.. UNTIL 191 15..1 until Döngüsünün Değişik Biçemi until anahtar sözcüğü, deyimlerin başı yerine begin- blokunun sonuna da konulabilir. Genel sözdizimi şöyledir: Liste 15.3. begin deyimler u n t i l boolean Program 15.16, sayının karesi 5 den büyük olana kadar yukarı doğru devam ediyor. Program 15.18. x = 0 begin x += 1 puts x u n t i l x > 5 1 5 6 Program 15.11, 10 dan başlayıp yukarı doğru 14 e kadar sayıyor. Program 15.19. # Aşağıya doğru sayma $ i = 10 4 $num = 5 begin puts ( " u n t i l s a y a c ı i = #$ i " ) $ i = 1 ; u n t i l $ i < $num 10 9. 5
19 BÖLÜM 15. DÖNGÜLER 15.. unless döngüsü unless döngüsü de until döngüsü yerine geçer. Program 15.0 onun nasıl olduğunu gösteriyor. Program 15.0. x = 0 begin x += 1 4 puts x u n l e s s x % 9 == 0 break unless break anahtar sözcüğü, belirli bir koşul sağlanınca döngüyü sonlandırır. Program 15.1, 10 dan başlayıp geriye doğru sayarken, 0 a inince duruyor. Program 15.1. x = 10 loop do puts x x = x 1 break u n l e s s x > 0 10 9 4... 1 15..3 do-while Döngüsü Kesim 15. de Ruby de öteki dillerde olan do-while yapısının olmadığını söylemiştik. Ama Ruby de do while döngüsünün işlevini gören ve ona benzeyen bir yapı kurabiliriz.
15.. UNTIL 193 Öteki dillerden anımsayacak olursak, do-while döngüsü esas olarak, while döngüsünün yaptığı işi yapar. Aralarındaki fark, denetlenecek mantıksal deyimin, döngünün sonuna konulmuş olmasıdır. O nedenle, bu yapıda döngü deyimleri en az bir kez yürütülür. Ruby de do-while yapısı için tek bir biçem vermek mümkün değildir 1 ama Liste 15.4 yapısını, do-while yapısına en yakın biçem olarak niteleyebiliriz: Liste 15.4. begin deyim ( l e r ) w h i l e boolean Program 15.. arr = [ "Can", " Melis ", " Pınar ", " Recep " ] i = 1 puts arr [ i += 1 ] w h i l e arr [ i ] Program 15.3 oda sıcaklığını her adımda 0.1 celsius derece artırıyor Program 15.3. 1 # do while c e l s i u s _ d e r e c e = 0.1 begin puts " Şimdi oda s ı c a k l ı ğ ı " + c e l s i u s _ d e r e c e. to_s + " C e l s i u s d e r e c e d i r. " 6 c e l s i u s _ d e r e c e += 0.1 w h i l e c e l s i u s _ d e r e c e < 0.5 puts " Şimdi y e t e r l i s a y ı l a b i l i r. " Şimdi oda s ı c a k l ı ğ ı 0.1 C e l s i u s d e r e c e d i r. Şimdi oda s ı c a k l ı ğ ı 0.00000000000003 C e l s i u s d e r e c e d i r. Şimdi oda s ı c a k l ı ğ ı 0.300000000000004 C e l s i u s d e r e c e d i r. Şimdi oda s ı c a k l ı ğ ı 0.400000000000006 C e l s i u s d e r e c e d i r. Şimdi y e t e r l i s a y ı l a b i l i r. 7 Program 15.4, 3 den başlayıp geriye doğru 1 e kadar sayıyor. Program 15.4. 1 Ruby nin genel stratejisini anımsayını: Bir işi yapmanın birden çok yolu vardır. Ruby, o yolları programcıya daima açık tutar.
194 BÖLÜM 15. DÖNGÜLER #! / usr / bin /ruby 3 n = 3 begin puts n n = 1 w h i l e n > 0 3 3 1 Program 15.5, 1 den başlayıp artan yönde birer birer sayarken, 6 ile tam bölünebilen ilk sayıya ulaşınca duruyor. Program 15.5. x = 0 begin x += 1 puts x 5 w h i l e x % 6!= 0 1 5 6 Daha tıkız yazılış için, döngünün gövdesi tek satırdan oluşturulabilir. Program 15.6 onun yapılışını gösteriyor. Program 15.6. # yukarıya doğru sayma x = 0 4 puts x = x + 1 w h i l e x < 10 1 1 10 6
15.3. FOR DÖNGÜSÜ 195 15.3 for Döngüsü 15.3.1 Aralık (range) İçinde For Döngüsü Ruby de for döngüsü, sıralı bir veri ambarının öğelerini baştan sona tarayan bir sayaca bağlı deyimlerin tekrarlanmasıyla yapılır. Aslında, Ruby for döngüsü java dilindeki foreach döngüsü gibidir ([7]. Bu nedenle Ruby for döngüsü, C ve Pascal da olduğu gibi, yalnızca bir aritmetik dizi üzerinde kayan sayaca bağlı döngülerden farklıdır. Tabii, Array yapısı her türlü nesnelerden oluşabildiği için, Ruby for döngüsü daha geneldir. Gerçekten, array bir aritmetik dizim olarak alınırsa, öteki dillerdeki for döngüleri özel durum olarak elde edilebilir. Döngünün kaç kez olacağını biliyorsak, geleneksel olarak for döngüsünü kullanırız. Program 15.7. # f o r döngüsü f o r i i n 0.. 9 do 4 p r i n t "#{i } " 0 1 3 9 Genel olarak, bir aralıktaki öğeleri yazdırmak için aşağıdaki yapıyı kullanırız: Program 15.8. f o r n i n 0.. 4 do p n 1 4 3 4 Burada p karakteri print metodu yerine geçer. Program 15.9, 5..7 aralığındaki sayıları 11 ile çarpıyor. Program 15.9.
196 BÖLÜM 15. DÖNGÜLER f o r i i n 5.. 7 p r i n t ( " #{i } x 11 = #{i 11} \n " ) 5 x 11 = 55 6 x 11 = 66 7 x 11 = 77 Program 15.30, 0..3 içindeki sayıların herbirisi için aynı stringi yazıyor. Program 15.30. 1 f o r x i n 0.. 3 p r i n t ( " Sen s e n i b i l sen s e n i : #{x} \n" ) Sen s e n i b i l sen s e n i : 0 Sen s e n i b i l sen s e n i : 1 Sen s e n i b i l sen s e n i : Sen s e n i b i l sen s e n i : 3 next next deneticisi bazı dillerdeki continue yerine geçer. Döngü içinde belli bir deyimi atlayıp, akışı sonraki deyime gönderir. Program 15.31. f o r i i n 0.. 5 i f i < then next 4 puts " Yerel d e ğ i ş k e n i n d e ğ e r i : #{i } " Yerel d e ğ i ş k e n i n d e ğ e r i : Yerel d e ğ i ş k e n i n d e ğ e r i : 3 4 Yerel d e ğ i ş k e n i n d e ğ e r i : 4 Yerel d e ğ i ş k e n i n d e ğ e r i : 5 Açıklama: Bu döngüde, i < ise puts metodu çalışmıyor. puts metodu her argümanı farklı satıra yazar. O nedenle döngülerde, çıktılar alt alta satırlar biçiminde çıkar. Bazen, yer kazanmak için, çıktıları yan yana yazmak isteriz. O zaman puts yerine print metodunu kullanmak sorunu çözecektir.
15.3. FOR DÖNGÜSÜ 197 Örnekler Program 15.3. f o r i i n 1.. 1 0 0 0 puts i i f i % 7==0 7 14 1 987 7 994 Program 15.33, 1..5 aralığındaki sayılara ilk 1000 sayı arasında kaç tanesinin bölünebildiğini buluyor. Program 15.33. f o r y i n 1.. 5 sayaç = 0 f o r x i n 1.. 1 0 0 0 sayaç += 1 i f x % y == 0 puts " 1 den 1000 e kadar #{y} i l e b ö l ü n e b i l e n #{sayaç } s a y ı v a r d ı r " 7 1 den 1000 e kadar 1 i l e b ö l ü n e b i l e n 1000 s a y ı v a r d ı r 3 1 den 1000 e kadar i l e b ö l ü n e b i l e n 500 s a y ı v a r d ı r 1 den 1000 e kadar 3 i l e b ö l ü n e b i l e n 333 s a y ı v a r d ı r 1 den 1000 e kadar 3 i l e b ö l ü n e b i l e n 43 s a y ı v a r d ı r 1 den 1000 e kadar 4 i l e b ö l ü n e b i l e n 41 s a y ı v a r d ı r 8 1 den 1000 e kadar 5 i l e b ö l ü n e b i l e n 40 s a y ı v a r d ı r Program 15.34 içindeki üç döngü birbirlerine denk iş yaparlar. Program 15.34. 1 f o r k i n 1.. 1 0 do puts "Number #{k} " 10. times do k 6 puts "Number #{k+1}" # döngü 0 dn b a ş l a r 9 da b i t e r 1. upto (10) { k puts "Number #{k} " }
198 BÖLÜM 15. DÖNGÜLER 15.3. Array üzerinde for döngüsü Program 15.35. # encoding UTF 8 5 g e z e g e n l e r = [ " Merkür ", " Venüs ", "Dünya", " Mars ", " J ü p i t e r ", " Satürn ", " Uranüs ", " Neptün " ] f o r i i n 0 g e z e g e n l e r. length puts g e z e g e n l e r [ i ] 1 Merkür Venüs Dünya Mars 6 J ü p i t e r Satürn Uranüs Neptün Program 15.36 bir liste içindeki sayıları topluyor. Program 15.36. arr = [ 1,, 3, 4, 5, 6, 7, 8, 9, 1 0 ] toplam = 0 f o r n i n arr toplam = toplam + n 5 p r i n t ( "#{n} i ç i n Toplam = #{toplam} \n " ) 1 i ç i n Toplam = 1 i ç i n Toplam = 3 4 3 i ç i n Toplam = 6 10 i ç i n Toplam = 55 Program 15.37 farklı nesnelerden oluşan bir listenin öğelerini yazıyor. Program 15.37. # array üzerinde f o r döngüsü ( l i s t üzerinde ) arr = [ " Ankara ", " Elma ", " Otomobil ", 1,. 0, t r u e ] 3 f o r x i n arr p r i n t ( "#{x} " )
15.4. ENUMERATORS 199 Ankara Elma Otomobil 1.0 true Program 15.38 str tipi nesnelerden oluşan bir listenin öğelerini yazıyor. Program 15.38. # array üzerinde f o r döngüsü ( l i s t üzerinde ) arr = [ Uzun, i n c e, b i r, yoldayım, Veysel ] f o r x i n arr p r i n t ( "#{x} " ) Uzun i n c e b i r yoldayım Veysel Program 15.39 bir array içindeki sözcüklerin uzunluklarını buluyor. Program 15.39. k e l i m e l e r = [ ev, izmir, k e d i g i l l e r ] f o r x i n k e l i m e l e r puts ( "#{x} in uzunluğu #{x. length } d i r " ) ev in uzunluğu d i r izmir in uzunluğu 5 d i r k e d i g i l l e r in uzunluğu 10 d i r 5 15.4 Enumerators Enumerator sınıfı veri ambarında gezinme (traverse), arama, sıralama gibi işleri yapan metotlara sahiptir. Döngüler ile array ve hash koleksiyonlarını bilenler için Enumerable modülünün metotlarının yaptığı işler bilindik işlerdir. Bu tür koleksyonların each metodu vardır. Enumerator nesnesi, ambarda gezinirken onun each metodunu çağırır. Böylece, ambardaki her öğe tek tek ziyaret edilebilir. each metodu Array sınıfının bir metodudur; ama başka koleksiyonlara da gezer (iterator) olarak uygulanabilir. Koleksiyondaki ya da bloktaki bütün öğeleri tek tek ziyaret eder.
00 BÖLÜM 15. DÖNGÜLER Liste 15.5. e v c i l l e r = [ kedi, at, koyun, kuzu ] f o r i i n e v c i l l e r puts e v c i l l e r [ i ] ifadesi evciller arrayinin öğelerini verir. Aynı işi yapmak için Liste 15.6. e v c i l l e r = [ kedi, at, koyun, kuzu ] e v c i l l e r. each do i puts i ifadesi tercih edilebilir. 15.5 each index Liste 15.7. e v c i l l e r = [ kedi, at, koyun, kuzu ]. each { i puts i } kedi at 4 koyun kuzu Liste 15.8. array = [ Superman, Batman, Gariban ] array. each_with_index do item, index 4 puts "#{index } > #{item } " 0 > Superman 1 > Batman > Gariban 5 15.6 Sıralama Klasik sıralama metotlarının hespsi Ruby kodları ile yazılabilir. Onlara ek olarak, sort_by metodu, enumerator ile numaralanmış koleksiyonları kolayca sıralayabilir. %w{ apple pear f i g }. sort_by { word word. length } #=> [ " f i g ", " pear ", " apple " ]