Swift 4 Fonksiyonlar ve Değişkenler #8

Bildirim İşlemi

Bir değişkenin kullanılmadan önce derleyiciye tanıtılması işlemine bildirim (declaration) denilmektedir. Tıpkı C, C++, Java ve C#'ta olduğu gibi Swift'te de değişkenler kullanılmadan önce bildirilmek zorundadır.

Bildirim işleminin genel biçimi kabaca şöyledir:

var <isim listesi> : <tür> [, ...];
var <isim> = <ilkdeğer> [, ...]
var <isim>: <tür> = <ilkdeğer> [, ...]
let <isim> : <tür> = <ilkdeğer [, ...]
let <isim> = <ilkdeğer>, [, ...]

Örneğin:

var a = 100, b = 200; print(a)
print(b)

Örneğin:

var a: Int = 100, b: Double = 200; print(a)
print(b)

Örneğin:

var a = 100, b: Double = 10.2; print(a)
print(b)

Örneğin:

var a: Int, b: Double; a = 10;
b = 20.3;
print(a)
print(b)

Görüldüğü var ile bildirim yapılırken değişken isminden sonra tür belirtilmiyorsa ilkdeğer verme zorunludur. Çünkü değişkenin türü bu durumda ancak ilkdeğer ifadesinden çıkarsanabilmektedir. Fakat değişken isminden sonra tür belirtilirse bu durumda ilkdeğer verme zorunlu olmaz. let ile yapılan bildirimlerde tür belirtilse bile ilkdeğer verme zorunludur.

Örneğin:

let x: Int; // error x = 10;

Eğer bildirimde değişkenin türü belirtilmemişse verilen ilkdeğerden tür tespiti yapılır. Verilen ilkdeğer nokta içeriyorsa tür Double olarak, içermiyorsa Int olarak belirlenmektedir.

Örneğin:

var a = 100, b = 12.2;

Burada a Int türden b ise Double türdendir. İki tırnakla ilkdeğer verme durumunda tür String olarak belirlenir.

Örneğin:

var s = "ankara"; // s String türünden

Swift'te Character türü de iki tırnakla (tabi iki tırnak içerisinde tek bir karakter bulunmalı) ifade edilmektedir. Ancak iki tırnak ifadeleri otomatik olarak String türü olarak tespit edilirler.

Örneğin:

var x = "a" // x Character türünden değil String türünden

Tabi bildirim sırasında değişkenin türü belirtilerek iki tırnak ifadesinin Character biçiminde ele alınması sağlanabilir.

Örneğin:

var ch: Character = "a" // ch Character türünden

Ya da aynı işlem şöyle de yapılabilirdi:

var ch = Character("a") // ch Character türünden

true ya da false olabilecek Bool türden ilkdeğerler için değişkenin türü Bool olarak belirlenir.

Örneğin:

var a = 33 == 34; // a Bool türden

Değişken let ile bildirilirse const hale gelir. Dolayısıyla bu değişkene bir daha değer atayamayız.

Örneğin:

let a = 100
a = 200 // error!

Anahtar Notlar:print fonksiyonunda parametrik yazım için \(değişken ismi) kalıbı kullanılmaktadır. Önce bir \ karakteri sonra da parantezler içerisinde değişken ismi belirtilirse bu o değişkenin değeri anlamına gelir.

Örneğin:

var a = 10; print("a = \(a)")

Örneğin:

var a: Int = 10, b: Double = 10.5; print("a = \(a), b = \(b)")

Fonksiyon Bildirimleri

Swift'te fonksiyonlar C# ve Java'da olduğu gibi bir sınıfın içerisinde bulunmak zorunda değildir. Bu bakımdan Swift C++'a benzetilmiştir. Böylece Swift'te istersek biz hiç sınıf kullanmadan yalnızca fonksiyonlarla prosedürel teknikte kod yazabiliriz.

Swift'te fonksiyon bildiriminin genel biçimi şöyledir:

func <isim>([parametre bildirimi]) [-> <geri dönüş değerinin türü>] {

//... }

Örneğin:

func add(a: Int, b: Int) -> Int {
return a + b 
}
var x = add(10, b: 20); print(x)

Örneğin:

func foo() {
print("foo") }
foo()

Eğer bildirimde parametre parantezinin içi boş bırakılırsa bu durum fonksiyonun parametreye sahip olmadığı anlamına gelir. Eğer parametre parantezinden sonra -> atomu kullanılmamışsa bu durum da fonksiyonun geri dönüş değerinin olmadığı anlamına gelmektedir. Fonksiyonun tek bir geri dönüş değeri vardır. Ancak birden fazla değer geri döndürmenin çeşitli yolları (örneğin tuple) bulunmaktadır.

return deyimi hem fonksiyonu sonlandırmak için hem de geri dönüş değerini oluşturmak için kullanılır. return deyiminin genel biçimi şöyledir:

return [ifade] [;]
func add(a: Double, b: Double, c: Double) -> Double {
return a + b + c
}
var result = add(100, b: 200, c: 300)
print(result)

Eğer fonksiyonun geri dönüş değeri yoksa return kullanılabilir; ancak yanına bir ifade yazılamaz.

Geri dönüş değeri olmayan bir fonksiyonda biz return kullanmak zorunda değiliz. Zaten akış fonksiyonun sonuna geldiğinde fonksiyon sonlanacaktır. Ancak geri dönüş değeri mevcut olan bir fonksiyonda return kullanılmak zorundadır. Fonksiyonun akışı onun her mümkün akışı için return deyimini görmesi gerekir.

Örneğin:
 Fonksiyon parametre bildiriminde parametrenin türleri kesinlikle belirtilmek zorundadır. Örneğin:

func foo(var a) // error! {
//... }

Parametre değişken bildiriminde var ya da let anahtar sözcükleri kullanılabilir. Bunların kullanılmadığı durumda default olarak sanki let kullanılmış gibi işlem görür. var anahtar sözcüğü parametre değişkeninin fonksiyon içerisinde değiştirilebileceğini, let ise değiştirilemeyeceğini belirtir. Default olarak parametre değişkenleri let durumdadır yani fonksiyon içerisinde onların değerleri değiştirilemez. (Örneğin C, C++, Java ve C#'ta default olarak biz parametre değişkenlerinin değerlerini istersek değiştirebiliriz.)

Fonksiyonların Çağrılması

Bir fonksiyon çağrılırken parametre değişkeni sayısı kadar argüman girilmek zorundadır. Argümanlar aynı türden herhangi bir ifade olabilirler. Parametrelerin birer yerel ismi (local name) ve birer de dışsal ismi (external name) bulunur. Fonksiyon çağrılırken argümanda etiket (label) olarak dışsal isim belirtilmek zorundadır. Dışsal isimler fonksiyon parametre bildiriminde yerel isimlerin solunda belirtilirler. Örneğin:

func foo(width w: Double, height h: Double) -> Double {
return w * h }
var result = foo(width: 10, height: 200); print(result)

Görüldüğü gibi fonksiyon çağrılırken dışsal isimler etiket olarak belirtilmek zorundadır. Fonksiyon çağırma sırasında argüman oluşturma işleminin genel biçimi şöyledir:

[dışsal isim etiketi][:]<ifade>

Dışsal isim yerine '_' karakteri kullanılırsa bu durumda çağırma sırasında dışsal isim etiketi bulundurulmaz. Örneğin:

func foo(width w: Double, _ h: Double) -> Double {
return w * h; }
var result = foo(width: 10, 200); print(result)

Fonksiyonların birinci parametrelerinde default olarak '_' belirlemesi yapılmış gibidir. Yani biz birinci parametreye dışsal isim vermezsek bunun için argümanda etiketleme yapmamalıyız. Örneğin:

func foo(w: Double, height h: Double) -> Double {
return w * h; 
}

Bu bildirim şununla eşdeğerdir:

func foo(_ w: Double, height h: Double) -> Double {
return w * h; }

Fonksiyonun çağrımı şöyle yapılabilir:

var result = foo(10, height: 200) print(result)
// birinci argümanda etiketleme yapamayız

Eğer fonksiyonun birinci parametresine dışsal isim verilmişse artık çağırırken bunun etiketlendirilmesi gerekir. Örneğin:

func foo(width w: Double, height h: Double) -> Double {
return w * h; }
var result = foo(10, height: 200) // error! birinci argümanda etiketleme yapmak zorundayız
print(result)
Anahtar Notlar: İstisna olarak Sınıfların başlangıç metotlarının (init metotlarının) birinci parametresi '_' biçiminde değildir. init metotlarını çağırırken ilk argüman da etiketlendirmek zorundadır. Sınıflar ileride ayrıntılarıyla ele alınacaktır.

Fonksiyon bildirimi sırasında parametre değişkenlerine dışsal isim hiç verilmeyebilir. Bu durumda yerel isim aynı zamanda dışsal isim olarak kullanılır. Örneğin:

func foo(w: Double, h: Double) -> Double {
return w * h; }
var result = foo(10, h: 200); print(result)

Burada yine birinci parametre için dışsal isim olarak '_' belirleyicisi kullanılmıştır. Ancak ikinci parametrede dışsal isim kullanılmadığı için yerel isim aynı zamanda dışsal isim olarak ele alınmaktadır.

Swift'te Fonksiyon Overload İşlemleri

Bilindiği gibi programlama dillerinde aynı isimli fonksiyon bulunabilme özelliğine "function overloading" ya da "method overloading" denilmektedir. "Overload" "aşırı yükleme" anlamına geldiği için Türkçe kaynakların bir bölümünde böyle ifade edilmektedir.

Swift'te aynı isimli fonksiyonlar bulunabilir. Ancak onların arasında aşağıdaki farklılıkların biri ya da birden fazlasının olması gerekir:

- Parametre sayılarının farklı olması - Parametre türlerinin farklılığı
 - Dışsal isimlerin farklılığı
 - Geri dönüş değerinin farklılığı

Örneğin:

func foo(a: Int) {
print("foo1")
}
func foo(b: Int) {
print("foo2")
}

Burada yukarıda belirtilen farklılıkların hiçbiri söz konusu değildir. İki foo fonksiyonunun da dışsal isimleri aynıdır (her ikisinde _ biçimindedir). Örneğin:

import Swift
func foo(x a: Int) {
   print("foo 1") 
}
func foo(y b: Int) {
   print("foo 2") 
}
foo(x:  10) // foo 1
foo(y: 20) // foo 2

Yukarıdaki iki foo'nun parametre değişkenlerinin dışsal isimleri farklıdır. Örneğin:

func foo(x a: Int) {
   print("foo 1") 
}
func foo(x b: Double) {
    print("foo 2") 
}
var result: Int 
foo(x: 10)
foo(x: 20.4)
// foo 1
// foo 2

Fonksiyon Parametrelerinin Default Değer Alması Durumu

Default argüman C++'ta öteden beri vardı. C#'a Microsoft Language Specification 3.0 versiyonuyla sokulmuştur. Bu özellik Swift'te de aktarılmıştır.

Fonksiyon bildiriminde parametre değişkenine değer atanırsa biz o fonksiyonu çağırırken ona karşı gelen argümanı hiç girmeyebiliriz. Bu durumda sanki o argüman için ilkdeğer olarak atanmış değer girilmiş gibi işlem söz konusu olur.

Örneğin:

func foo(a: Int, b: Int = 100) {

print("a = \(a), b = \(b)") }

foo(10, b: 20)
 foo(10) // foo(10, b: 100)

Buradaki foo her zaman iki parametrelidir. Ve her zaman çağırma sırasında iki parametre aktarımı yapılır. Default argüman yalnızca gösterimi kolaylaştırmaktadır. Örneğin:

func foo(a: Int = 100, b: Int = 200) {
print("a = \(a), b = \(b)")
}
foo(a: 10, b: 20) 
// foo(a: 10, b: 20)
foo(a: 10)
// foo(a: 10, b: 200)
foo()
// foo(a: 100, b: 200)

Default değer alan parametre değişkeni için argüman girersek artık o default değerin bir önemi kalmaz. Örneğin:

func printError(message msg: String = "Ok") {
print("Error: \(msg)") }
printError(message: "invalid name") 
printError()

Default argümanlı fonksiyonlar Swift'in standart kütüphanesinde de karşımıza çıkmaktadır. Örneğin aslında print fonksiyonu da terminator dışsal isimli bir parametreye sahiptir. Bu parametrenin default değeri "\n" biçimdedir. Bu parametre yazdırma işleminden sonra ekstra olarak kangi karakterlerin ekrana basılacağını belirtir. Başka bir deyişle:

print(a)

ile

print(a, terminator: "\n")

eşdeğer işleme yol açar.

Örneğin:

var a = 10, b = 20 print(a, terminator: "") print(b)

Default argümana sahip fonksiyonla aynı isimli daha az parametreye sahip fonksiyonlar overload edilebilir. Fakat bunların çağrılması sırasında iki anlamlılık (ambiguity) oluşabilir. Örneğin:

func foo(a: Int = 100, b: Int = 200) {
   print("a = \(a), b = \(b)") 
}
func foo() {
   print("void") 
}
foo() // hangi foo?

C++ ve C#'ta fonksiyonun bir parametresi default değer almışsa onun sağındakilerin hepsinin default değer

alması zorunludur. Swift'te dışsal isimler nedeniyle böyle bir zorunluluk yoktur. Örneğin:

func foo(a: Int = 100, b: Int) {
   print("a = \(a), b = \(b)")
}
foo(b: 200) // foo(100, 200)

İyi bir teknik bakımından parametre değişkeninde default değer vermek için o değerin çok yaygın kullanılıyor

olması gerekir. Aksi taktirde kodu inceleyen kişiler yanlış izlenimlere kapılabilirler.

İç İçe Fonksiyon Bildirimleri

C türevi dillerde iç içe fonksiyonlar bildirilemez. Her fonksiyon diğerinin dışında bildirilmek zorundadır. Fakat (Pascal gibi bazı dillerde öteden beri iç içe fonksiyon bildirimi zaten vardır) Swift'te bir fonksiyonun içerisinde başka bir fonksiyon bildirilebilir. Örneğin:

func foo() {
bar() // yanlış
func bar() {
  //...
}
bar() // doğru
}
// geçerli

İçerde bildirilmiş fonksiyon ancak içinde bildirildiği fonksiyon tarafından çağrılabilir. Yani yukarıdaki örnekte bar fonksiyonu yalnızca foo fonksiyonu tarafından çağrılır.

İç fonksiyon dış fonksiyonun herhangi bir yerinde bildirilebilir. Ancak bildirildiği yer önemlidir. Şöyle ki: İç fonksiyon dış fonksiyonun başından kendi bildirimine kadar olan bölgede (yani kendisinden daha yukarısında) bildirilmiş olan dış fonksiyonun yerel değişkenlerini ve parametre değişkenlerini kullanabilir.

Örneğin:

func foo() {
var x = 10
func bar() {
x = 20 
}
print(x) 
}
foo() // 10

Bu örnekte bar fonksiyonu foo fonksiyonunun içerisinde bildirilmiştir fakat çağrılmamıştır. Tabi foo fonksiyonu bar( ) çağıracaksa ancak bildiriminden sonra çağırabilir. Örneğin:

func foo() {
var x = 10
func bar() {
x = 20 }
bar()
print(x)
}
foo() // 20
One clap, two clap, three clap, forty?

By clapping more or less, you can signal to us which stories really stand out.