Bölüm 2 : Kontrol Yapıları ve Fonksiyonlar

Bu bölümde condition,loop yapılarını ve fonksiyonları inceleyeceğiz.Ve incelemeyi mümkün olduğunca C++ ve Java dilleriyle karşılaştırarak yapacağız.

1- Conditional Expressions

Scala’daki if/else yapısı Java’daki ile aynıdır.Fakat Scala’daki if/else yapısının bir değeri vardır.Yani if/else geriye bir değer dönderiyor.

Önemli ! : Scala’da hemen hemen tüm yapıların bir değeri vardır.

val s = if (x  > 0 ) 10 else 20

Yukarıdaki if’in değeri x’in değerine göre 10 veya 20’dir.Yani s değişkeni 10 veya 20 olabilir.

Yukarıdaki kodu Java’da gerçeklemek için üçlü operatörü (?:) kullanırız.Scala üçlü operatöre ihtiyaç duymadan bu operatörün işlevini if/else ile gerçekleyebiliyor.

if/else yapısının bir dönüş değeri var dedik.Peki Scala bu dönüş değerinin tipini nasıl belirliyor ? Scala geri dönüş değerinin tipini her bir branchin dönderdiği değerin tipine bakarak anlıyor.İlk örneğimizde her iki branchte dönen değer Int olduğundan if/else‘nin dönerdiği değerin tipi Int olur.Fakat diyelim ki bir branch String bir branch ise Int dönderiyor.Bu durumda dönen değerin tipi her tipinde ortak super class’ı olan Any tipinde bir değer döner.

if(x > 0) “test” else 10         // Bu durumda bu if/else ‘nin geri dönüş değeri Any olur.

Şimdiye kadar ki if/else yapılarında her iki branchinde bir değeri vardı.Peki bir branch es geçilirse geri dönüş değeri ne olacak ? Yani else yapısı yazılmamış ise.Bu durumda geri dönüş değeri Unit olabilecektir.Örneğin

if(x > 0) 10     //  x, 0’dan büyükse if yapısının dönüş tipi Int olacaktır.Fakat eğer x, 0’dan
küçükse bu if yapısının dönüş tipi Unit olacaktır.

Unit, Java’daki void tipine denk geliyor.İkisi tam olarak aynı şey değil.Scala,Unit tipi ile bir değerin işe yarar bir bilgi taşımadığını belirtir.

Daha akılda kalması açısından şöyle düşünebiliriz.Void boş bir cüzdanı ifade ediyorsa,Unit cüzdanın içerisinde “para yok” diyen bir notu işaret eder 🙂

Scala’da switch yapısı yoktur.Buna ihtiyaç duyulmuyor.Sonraki yazılarda bunu işleyeceğiz.

2-Statement Termination

Java ve C++ dillerinde her kod satırı noktalı virgül ile bitmek zorundadır.Scala’da bu opsiyoneldir.İsteğe bağlı olarak kullanabilir veya kullanmayabilirsiniz.

Ben özellikle noktalı virgül kullanmamaya çalışıyorum.Eski dillerden kalma alışkanlıklarımızı devam ettirmenin bir manası yok 🙂

3- Input ve Output

Bir değeri konsola basmak için print veya println fonksiyonlarını kullanabilirsiniz.println fonksiyonu değeri bastıktan sonra bir sonraki satıra geçer.Java ile benzer.

readLine fonksiyonu ile consoldan bir değeri okuyabilirsiniz.Numeric,Boolean,character okumak için readInt,readDouble,readByte,readBoolean,readChar fonksiyonları kullanabilirsiniz.

4- Loops

Scala’da while yapısı Java’daki ile benzerdir.

while(x>0){
r =r * x
n-=1
}

Scala’daki for yapısı Java’dakinin gelişmişi olarak düşünebiliriz. for(ilk_deger,test,güncelle) yapısı bulunmuyor Scalada.Bu gerçeklemek içib ya while yapısı ya da aşağıdaki gibi bir for yapısı kullanabiliriz.

for( i  <-  1 to n)  // Scala
r = r + i

for(i = 1;i <= n; i++) // Java
r = r + i

1 to n, bir number aralığı döner. Örneğin , 1 to 5 demek kısaca (1,2,3,4,5) demektir.

for yapısı içerisinde birden fazla değişkeni noktalı virgül ile ayırarak kullanabilirsiniz.Örneğin;

for( i  <- 1 to 3; j <- 1 to 3 )  // Consola 11 12 13 21 22 23 31 32 33 değerlerini basar
print(10 * i + j + ” “)

Hatta for içerisinde condition bile kullanabiliyorsunuz.

for(i  <- 1 to 3; j <- 1 to 3 if i != j ) // Consola 12 13 21 23 31 32 değerlerini basar
print(10 * i + j + ” “)

if‘den önce noktalı virgül kullanmadığımıza dikkat edelim.

Scala for yapısı o kadar güçlüdür ki,bu yapı içerisinde döngü içerisinde kullanılmak üzere değişken tanımlayabilirsiniz.

for(i  <- 1 to 3; from = 4 -i ; j <- from to 3 )  // Consola 13 21 23 31 32 33 değerlerini basar
print(10 * i + j + ” “)

5- Functions

Scala’da methodlara ek olarak fonksiyon da tanımlayabiliriz.Bir method, object üzerinde işlem yapar.Java’da bir methodu static tanımlayarak fonksiyon gibi davranmasını sağlıyoruz.Scala’da ise def keyword‘u ile fonksiyonumuz basitçe tanımlayabiliyoruz.

Bir fonksiyon tanımlamak için,fonksiyonun ismini,parametrelerini ve içeriğini (body) belirtmemiz gerekiyor.

def abs (x : Double ) = if( x > 0) x else -x

Fonksiyonun tüm parametrelerinin tipini belirtmemiz gerekiyor.Fonksiyon recursive olmadığı sürece geri dönüş tipini belirtmemiz gerekmiyor.Scala geri dönüş tipini fonksiyonun içeriğinden anlıyor.Fonksiyonun değeri, fonksiyon içerisindeki son kod satırının değeridir.

Aşağıdaki fonksiyonun değeri,for bittikten sonraki r‘nin değeridir.

def fac(n: Int ) = {
var r = 1
for(i <- 1 to n)  r = r * i
}

Eğer recursive bir fonksiyon tanımlıyorsak mutlaka geri dönüş tipini Scala’ya söylememiz gerekiyor.Aksi takdirde compiler hata verecektir.

def fac(n: Int): Int = if( n <= 0) 1 else n *  fac(n-1)

Fonsiyon tanımlarken parametreler için default değerler tanımlayabiliyoruz.

def decorate(str : String, left : String = “[” ,right : String =”]”)=
left + str + right

left ve right parametleri default değerlere sahip.

Java’da bir foknsiyonu çağırırken parametrelerin fonksiyon tanımındaki gibi aynı sırada olması gerekiyor.Scala’da böyle bir zorunluluk yok.Örneğin yukarıdaki fonksiyonu aşağıdaki gibi çağırabiliriz.

decorate( left = “<>”, str = “hello”)     // Dikkat ederseniz fonksiyon tanımındaki parametre
sırasına göre fonksiyonu çağırmadık.

decorate( “Hello”,right = “<<<” )                // calls decorate(“Hello” , “[“, “<<<” )

Bazı durumlarda parametre olarak number of arguments alan bir fonksiyon tanımlamak isteriz.

def sum(args : Int*) = {
var result = 0
for( arg <- args ) result += arg
result
}

Yukarıdaki fonksiyonu aşağıdaki şekillerde çağırabiliriz.

val s = sum(1,2,3,4,5)

val s = sum(1 to 5:_*)

6- Procedures

Scala’da değeri olmayan fonksiyonlar için özel bir notasyon vardır.Eğer bir fonksiyon tanımında { parantezinden önce = karakteri kullanılmıyorsa bu fonksiyonun geri dönüş tipi Unit olur.Bu tip fonksiyonlar procedure olarak adlandırılır.Bir procedure fonksiyonun geri dönüş değeri yoktur.Örneğin aşağıdaki gibi string print etmek istiyoruz.

——-
|Hello|
——-

def box( s :  String  ) {                                                     // = karaktrerinin olmadığına dikkat edin.
val border = “-” * s.lenght + “–\n”
println(border + “|” + s + “|\n” + border)
}

def box(s : String) : Unit = {                            //Bu fonksiyon ve yukarıdaki fonksiyon eşdeğerdir.
val border = “-” * s.lenght + “–\n”
println(border + “|” + s + “|\n” + border)
}

7- Lazy Values

Bir val değişkeni lazy olarak işaretlenmiş ise bu değere erişilmediği sürece initialization yapılmaz.

lazy val words = scala.io.Source.fromFile(“/usr/share/words”).mkString

Uygulama, words değişkenine erişmediği sürece words initialize edilmez.

Diyelim ki bir ton değişken tanımının olduğu bir class’ınız var.Ve bazı değişkenler belki hiç kullanılmayacak uygulama içerisinde.Bu durumda ilk baştaki initialize maliyetini azaltmak için lazy değişkenler kullanabilirsiniz.

Lazy değişkenleri val ve def arasında bir yerde düşünebiliriz.

val words = scala.io.Source.fromFile(“/usr/share/words”).mkString
// Compiler words değişken tanımını gördüğü an işler

lazy val words = scala.io.Source.fromFile(“/usr/share/words”).mkString
// Compiler,words değişkeni ilk defa kullanıldığında işler

def words = scala.io.Source.fromFile(“/usr/share/words”).mkString
// Compiler,words değişkeni her kullanıldığında işler

8- Exceptions

Scala’daki exception işleme mantığı Java’daki gibidir.Aşağıdaki gibi bir exception fırlatıldığında,

throw new IllegalArgumentException(“x should not be negative”)

o anki işlemler durur,ve runtime system bir exception handler bekler.Eğer exception handler yoksa uygulama durur.

Scala’da checked exception yok.Bildiğimiz gibi checked exception‘lar compile time’da check edilir.Şöyle ki eğer bir method IOException fırlatıyorsa bu exception’u method tanımında tanımlamanız gerekiyor.

if( x >= 0 )  {  sqrt(x)
else throw new IllegalArgumentException(“x should not be negative”)

İlk branch’in tipi Double.İkinci brach’in tipi ise Nothing‘tir.

val in = new URL(“mesutozen.wordpress.com/test.gif”).openStream()
try {
process(in)
} catch {
case _ : MalformedURLException => println(“Exception occured”)
case ex: IOException => ex.printStackTrace()
} finally {
in.close
}

Java’da olduğu gibi daha spesifik hatalar genel hatalardan önce gelir,yakalanır.

Eğer hata değişkenine ihtiyacımız yok ise _ karakterini kullanabiliriz.

finally bloğu Java’daki gibidir.Hata alınsada alınmasa da finally bloğu çalışır.

 

EOF

Bölüm 2 : Kontrol Yapıları ve Fonksiyonlar” üzerine 2 düşünce

  1. Bahadır AKIN

    Merhaba Mesut Bey;

    Yazılarınızı ilgiyle takip ediyorum. Yanlız foksiyonlarla ilgili aklıma takılan bir iki soru var.

    Öncelikle bir fonksiyonun scop’u nedir?

    Java’daki gibi düşünecek olursak fonksiyon nasıl import ediliyor?
    1. Bölümde pakate bağlı olduğunu belirtmiştiniz (scala.math örneğinde), sadece paket scope’unda mı tanımlayabiliyoruz? İki farklı fonksiyon aynı imzaya sahipse örneğin biri scala.math diğeri com.bahadirakin paketinde ise ben bu iki paketide import edersem ne olur?

    Java’da (hemen hemen) her nesne bir dosyaya karşılık geliyor. Fonksiyonlarda böyle bir durum var mı? Yani mesela benim com.bahadirakin paketi içerisinde A diye bir sınıfım var bunun içerisine foksiyon tanımladım, böyle benzer sınıflarım ve fonksiyonlarımda var. import com.bahadirakin dersem tüm fonksiyonları mı alır?

    Teşekkürler

    Liked by 1 kişi

    Cevapla
  2. Mesut Özen Yazıyı Yazan

    Selam Bahadırcım,

    Response time bu haftaki exceptional durumlardan dolayı uzun oldu kusura bakma 🙂

    Öncelikle şunu belirteyim.Nasıl ki java’da default accessor package-private ise Scala’da default accessor public’tir.

    Bir de şunu belirteyim ki scala’daki fonksiyonları aynen java’daki static methodlar gibi düşünebiliriz.

    1-) Bu durumda compiler “reference to xxx is ambiguous;it is imported twice in the same scope by” şeklinde bir hata verecektir.Bunun önüne geçmek için fonksiyon çağrımı yaptığın yerde hangi sınıfın fonksiyonu çağırdığını compiler’a söylemen gerekecektir.

    2-)Scalada da böyle bir durum var diyebiliriz.Hayır, bu durumda sadece paketi import etmen yeterli olmayak,tüm class’ların içerisindeki fonksiyonları import etmeyecektir.Sadece class’lar kullanılabilir olacak.
    Örneğin,
    import com.bahadirakin._ // ‘_’ karakteri javadaki ‘*’ wildcard karakteridir.

    Bu durumda ilgili paket altındaki tüm class’lar import edilmiş olunacak ve bu class’ları paket belirtmeden import ettiğin sınıf içerisinde kullanabiliyor olacaksın.

    import com.bahadirakin.Test._

    Bu import ile Test class’ı içerisindeki tüm fonksiyonları paket ve sınıf belirtmeden kullanabiliyor olacaksın.

    Beğen

    Cevapla

Bir Cevap Yazın

Aşağıya bilgilerinizi girin veya oturum açmak için bir simgeye tıklayın:

WordPress.com Logosu

WordPress.com hesabınızı kullanarak yorum yapıyorsunuz. Çıkış  Yap /  Değiştir )

Twitter resmi

Twitter hesabınızı kullanarak yorum yapıyorsunuz. Çıkış  Yap /  Değiştir )

Facebook fotoğrafı

Facebook hesabınızı kullanarak yorum yapıyorsunuz. Çıkış  Yap /  Değiştir )

Connecting to %s