Scala’da Döngü İfadeleri

Herkese merhaba!

Bir önceki yazımda programlamanın en temel konularından olan koşul ifadelerinden bahsetmiştim. Bu yazımda da ikinci bir temel kavram olan döngülerden ve bunların Scala’daki işleyişlerinden bahsedeceğim. Scala’da while ve for döngüleri olmak üzere 2 çeşit döngü var.

Haydi başlayalım!

while Döngüsü

Bu döngüler while özel kelimesi, sonrasında bir koşul ve sonrasında bu koşul sağlandığı sürece tekrar tekrar çalıştırılacak olan ifade(ler)den oluşan yapılardır. Yapısı Java’daki ile aynıdır. Örneklere bakalım.


// Java
int i = 1;
while (i <= 10) {
System.out.println(i + "!");
i++;
}
System.out.println("Önüm, arkam, sağım, solum, sobe!");
// Scala
var i = 1
while (i <= 10) {
println(i + "!")
i += 1
}
println("Önüm, arkam, sağım, solum, sobe!")
// Romantik Scala geliştiricisi
while (true) {
println("Seni seviyorum!")
}

Yukarıdaki örnekte saklambaç oyununda ebe olan bir arkadaşın kodlarını görüyorsunuz. 🙂 Gördüğünüz gibi Scala’daki while döngülerinin Java’dakinden pek bir farkı yok. Bu örnekte koşulun zaman içinde değişmesi gerektiği için var kullandık. Java’daki i++” ifadesi yerine Scala’da i += 1″ ifadesini kullandığımızı görüyorsunuz. Scala’da ++ ve  işlemleri yok. Çünkü Scala fonksiyonel programlamayı öne çıkarıyor ve bir şeylerin değerini değiştirmek fonksiyonel programlama felsefesine aykırı. Benzer şekilde, Java’da bir döngüden break özel kelimesiyle çıkabiliyorken Scala’da bunu da yapamıyorsunuz. Uygulamanın akışını bu şekilde değiştirmek de fonksiyonel programlamaya ve Scala’ya göre hoş bir davranış değil. 🙂 Son olarak da bir sonsuz döngü görüyorsunuz. Java ile aralarında bir fark yok. Yalnız kullanırken dikkatli olmakta fayda var çünkü az önce belirttiğim gibi break kullanamıyorsunuz. Bunu çözmenin birkaç yolu var ama bence en güzeli while döngüsü kullanmaktan kaçınmak. Eğer gerçekten while kullanmanız ve döngüyü bir noktada kesmeniz gerekiyorsa bunu daha sonra detaylıca anlatabilirim.

for Döngüsü

Gelelim for döngüsüne. Scala’da for döngüsü oldukça gelişmiş özelliklere sahip. Hatta aslında sadece bir döngü değil. Scala’da basit bir döngüden daha fazlasını yapan for’lar için “for döngüsü” yerine “for comprehension yani “for anlaması/kavraması” da deniliyor. Şimdi en temel halinden daha gelişmiş özelliklerine doğru örneklere bakalım.


// Java
for (int i = 1; i <= 10; i++) {
System.out.println(i + "!");
}
System.out.println("Önüm, arkam, sağım, solum, sobe!");
// Scala
for (i <- 1 to 5) println(i + "!")
for (j <- 6 until 11) {
println(j + "!")
}
println("Önüm, arkam, sağım, solum, sobe!")

Yukarıdaki örnekte Java kısmında çok basit bir for döngüsü görüyorsunuz. Bir değişken tanımlayıp değerini 0 yapıyor, belirli bir koşul tanımlıyor ve döngünün her aşamasında bu değişkenin değerini artırıyor. Şimdi aynı döngünün/döngülerin Scala’daki karşılığına bakalım.

8. ve 9. satırlardaki döngüler aslında fonksiyonel döngüler. Bir değişken tanımlayıp bunun değerini değiştirerek çalışmıyorlar. to ve until kelimelerine dikkat edin. to kelimesi ile 1’den 5’e kadar sayı aralığı üretiyoruz. Aslında buradaki to, 1 değeri üzerinde çağrılan bir metod. Bu tür metod çağrılarına ileride değineceğim. Böylece “1 to 5” ifadesi scala.collection.immutable.Range.Inclusive türünde, içinde 1’den 5’e kadar (5 dahil) sayılar olan bir Range, yani aralık, oluşturuyor. Daha sonra da for döngüsü, bu Range üzerindeki her eleman için, elemanlara i ismiyle erişecek şekilde çalışıyor. Burada üzerinde döndüğümüz Range içinde Int türünde sayılar olduğundan, i‘nin türü, tür çıkarımı sonucunda Int olarak belirleniyor. Döngünün gövdesi tek bir ifadeden oluşacaksa 8. satırda görebileceğiniz gibi süslü parantezleri yazmayabiliyoruz ama bu şekilde yazım pek tavsiye edilmiyor. Süslü parantezleri hep kullanmakta fayda var.

9. satırda da to yerine until kullanımına örnek olması için yazdığım başka bir döngü var. 10’a kadar saymaya kaldığımız yerden devam ediyoruz. Bu sefer (önceki döngüde 5’i dahil ettiğimiz için) 6 üzerinde until‘i çağırarak 6’dan 11’e kadar (11 hariç), scala.collection.immutable.Range türünde bir Range üretiyoruz. Bu seferki döngümüzde her elemana j ismiyle ulaşacağız.

Buradaki Scala for döngüleri aslında Java’daki gelişmiş for döngüleri gibi bir koleksiyon üzerindeki her bir elemanı ziyaret ederek çalışıyor ve her elemana ulaşacağımız bir değişken adı tanımlıyoruz. Bunu yaparken kullandığımız <- (küçüktür ve kısa çizgi) işaretine dikkat edin. 🙂

Yukarıdaki örnek basit bir sayma işlemine karşılık geliyordu. Java’da bunun için de koşullar yazıyor olsak da Scala’da to ve until ifadeleriyle saymayı kolayca yapabildiğimizi gördük. Şimdi biraz daha ilerleyelim ve döngümüze farklı koşullar ekleyelim.


// Java
for (int i = 1; i <= 20; i += 2) {
for (j = i; j <= i * 2 && j < 14; j++) {
System.out.print("[" + i + ", " + j + "] ");
}
}
// Scala
for {
i <- 1 to 20 by 2
j <- i to (i * 2) if j < 14
} {
print("[" + i + ", " + j + "] ")
}

Burada daha karmaşık koşulları ve ilerleme ifadeleri olan, değerleri birbiriyle bağlantılı olan iç içe 2 döngü yazdık. Şimdi Scala’daki halini incelemeye başlayalım.

Scala birden fazla boyutta for döngüleri yazacağımız zaman for kelimesinden sonraki parantezlerin yerini süslü parantezler alıyor. Her bir döngüyü ayrı bir satıra gelecek şekilde yazıyoruz.

Buradaki örnekte 10. satırda 1 to 20 ifadesini biliyoruz ama bu sefer bir ekleme var. “1 to 20 by 2” ifadesi 1’den 20’ye kadar Range oluşturuyor ama ikişer artırarak oluşturuyor. Yani sonuçta (1, 3, 5, …, 17, 19) şeklinde bir Range oluşuyor. Bunun Java örneğindeki i += 2 ifadesine karşılık geldiğini görebilirsiniz.

11. satırda da ilk döngümüzün içindeki ikinci döngümüzü tanımlıyoruz. Burada j‘ler, i‘nin o anki değerinden, (i * 2)‘ye kadar ilerleyecek ama sonrasında gelen j < 14 koşulunu sağladığı sürece. Burada gördüğünüz gibi, for döngüsünde döngüyü sadece belirli koşullarda ilerleyecek şekilde yazabiliyoruz. Bunun için de bir koşul yazmak için en mantıklı olan şeyi, yani if kelimesini kullanıyoruz. if’ten sonra parantez kullanma zorunluluğu yok. (match … case ifadelerindeki koşulları hatırlayın.) Bu döngü çalıştığında aşağıdaki sonucu alacağız.

Screenshot 2015-12-15 22.44.16

Gördüğünüz gibi i değerleri ikişer artıyor. j değerleri de 14’ten her zaman küçük. Bu koşul bozulduğu anda döngünün durduğunu görüyorsunuz. Yukarıda break olmamasından ve while kullanmaktan mümkün olduğunca kaçınmak gerektiğinden bahsetmiştim. Gördüğünüz gibi sorununuzu buna benzer for döngüleri ile büyük ihtimalle çözebilirsiniz. 🙂

Şimdi son bir örnekle Java’daki gelişmiş for döngülerine benzer bir döngü yazalım. Ama sırf yapabildiğimiz için bu döngü bir değer de üretsin. (if bloğundan değer üretmeyi hatırlayın.)


// Java
String[] isimler = new String[] {"Ali", "Veli", "Ahmet", "Hüseyin", "Akif"};
String[] secilenIsimler = new String[isimler.length];
int i = 0;
for (String isim : isimler) {
if (isim.startsWith("A") && isim.length > 3) {
System.out.println("Seçilen isim: " + isim);
secilenİsimler[i] = isim;
i++;
}
}
// Scala
val isimler: Array[String] = Array("Ali", "Veli", "Ahmet", "Hüseyin", "Akif")
val secilenIsimler: Array[String] = for (isim <- isimler if isim.startsWith("A") && isim.length > 3) yield {
println("Seçilen isim: " + isim)
isim
}

Buradaki örneğimiz tanımlanmış bazı isimlerden A harfi ile başlayan ve 3 karakterden uzun olan isimleri buluyor. Java’daki örnekte, 6. satırda gördüğünüz gibi bir koleksiyon üzerinde gelişmiş for döngüsü kullanıyoruz. Ayrı bir Array’de de sonucu saklıyoruz. Şimdi Scala’daki örneğe bakalım.

Scala’da bir koleksiyon üzerinde for döngüsü yapmak yukarıdaki örneklerle aynı. Bir değişken ismi veriyoruz, <- işaretini kullanıyoruz ve koleksiyonu yazıyoruz. 15. satırda bir Array tanımlanışı görüyorsunuz. Koleksiyonları anlatacağım yazımda daha detaylı bahsedeceğim için şu an bunun detaylarına girmiyorum. Şimdi 17. satırda başlayan döngüye bakalım.

17. satır bir val tanımlamasıyla başlıyor. Değer olarak da doğrudan döngünün kendisini yazdık. Buradaki döngümüz (tıpkı daha önce if bloklarında anlattığım gibi) çalıştırılıp bir değer üretiyor. Bir for döngüsünün değer üretmesi için gövdesi yerine yield kelimesini kullanıyoruz. Burada isimler isimli Array’imiz üzerinde isim diye adlandırdığımız bir değişkenle dönüyoruz. Koşullarımızı da doğrudan döngüye ekleyebildiğimizi görüyorsunuz. Bu sayede eğer o anki eleman koşula uymuyorsa atlanacak ve üretilen değere dahil edilmeyecek. yield kelimesinden sonra gelen blokta da hangi isim olduğu yazıyoruz ve ismi geri dönüyoruz. Burada bir Array[String] üzerinde döndüğümüz için döngüde tanımladığımız isim değişkeninin türü Stringyield ifadesininden de geriye isim, yani String türünden bir değer döndürdüğümüz için döngünün tamamı Array[String] değeri üretmiş oluyor. Bu değeri de secilenIsimler val’ına atıyoruz.

for döngüleriyle ilgili başka örnekler de yazıp, deneyerek alıştırma yapmanızı tavsiye ederim. Özellikle özel koşulları olan for döngüleri ve yield ile değer üreten döngüler, tam anlamıyla hakim olduğunuzda gerçekten güçlü araçlar. 🙂

Sonuç

Bu yazımda sizlere Scala’da döngü ifadelerinden bahsetmeye çalıştım. Farklı yönlerini görebilmeniz adına Java’daki halleriyle karşılaştırmalar da yaptım. İnşallah anlaşılır olmuştur.

Görüşmek üzere. Hoşça kalın!