Go öğreniyorum - Gün 5 : Fonksiyonlar - 1

`Introducing Go` kitabından bugün öğrendiklerim

Yazılan kodun en bağımsız olan kısmıdır!

Chapter 6

Functions

0 ya da n tane parametreyi alır, işler ve 0 ya da n tane sonuç dönen şeydir fonksiyon. Prosedür (Procedure) ya da Alt-rutin (Subroutine) olarak da adlandırılır. Not: Subroutine kelimesini görünce 6502 Assembly yazdığım günler geldi aklıma. Fonksiyon için kara kutu ifadesi kullanılıyor.

             +-----------+
GİRDİ -----> | fonksiyon | -----> ÇIKTI
             +-----------+

Baştan beri func main() {} olarak zaten fonksiyon denen şeyle tanışmıştık. Şimdi ikinci fonksiyonumuzu yazalım. Ama önce Chapter 5’teki örneği hatırlayalım:

package main

import "fmt"

func main() {
    xs := []float64{98, 22, 54, 31, 22, 4}
    total := 0.0
    for _, v := range xs {
        total += v
    }
    fmt.Println(total / float64(len(xs)))
}

Ortalamayı hesaplayan kısmı fonksiyon haline getirelim:

func average(xs []float64) float64 {
    panic("Not implemented")
}

Fonksiyon, func anahtar kelimesiyle başlıyor. Daha sonra fonksiyonun adı geliyor: average, eğer parametre alıyorsa parametre adı ve tipi geliyor: xs []float64, son olarak fonksiyonun geriye döndüğü tip: float64 deklare ediliyor.

average fonksiyonu float64’lerden oluşan bir slice alacak, işlemi yapıp geriye yine float64 tipinde bir sayı dönecek. Bir fonksiyonun aldığı parametreler ve döndüğü tip o fonksiyonun imzasıdır ve function’s signature olarak ifade edilir. {} arasında kalan kısma da function’s body yani bir seri işlemlerin yapıldığı kısıma gövde denir.

Örnekteki fonksiyonu henüz yazmadığımız için panic fonksiyonunu çağırarak Not implemented hata mesajı üretiyoruz. Bu runtime error kategorisinde bir hata.

Şimdi fonksiyonu çalışır hale getirelim:

func average(xs []float64) float64 {
    total := 0.0
    for _, v := range xs {
        total += v
    }
    return total / float64(len(xs))
}

Şimdi örneği tamamlayalım:

package main

import "fmt"

func main() {
    xs := []float64{98, 22, 54, 31, 22, 4}
    fmt.Println(average(xs))
    // average fonksiyonunu xs’i parametre geçerek çağırdık
}

func average(xs []float64) float64 {
    total := 0.0
    for _, v := range xs {
        total += v
    }
    return total / float64(len(xs))
}

Aynı sonucu elde ettik. Fonksiyonlarda;

Parametre adları farklı olabilir

Yani func average(xs []float64) float64 geçeceğimiz parametrenin adı illaki xs olmak zorunda değil:

numbers := []float64{98, 22, 54, 31, 22, 4}
fmt.Println(average(numbers))  // gibi...

Değişkenler mutlaka fonksiyona geçilmeli

Fonksiyonlar ne yazıkki herşeye erişemeyebilir (scope) Eğer içeride kullanacağınız veriyi parametre olarak fonksiyona geçmezseniz sıkıntı olur:

func f() {
    fmt.Println(x)
}

func main() {
    x := 5
    f()
}

Bu çalışmaz. undefined: x hatası olur. Doğrusu;

package main

import "fmt"

func f(x int) {
    fmt.Println(x)
}

func main() {
    x := 5
    f(x)  // x’i fonksiyona geçiyoruz
}

ya da;

package main

import "fmt"

var x int = 5
// en dışta (scope) x’i tanımladık ve global olarak
// erişilebilir olmasını sağladık

func f() {
    fmt.Println(x)
}

func main() {
    f()
}

Fonksiyonlar bir yığında sıralı duran çağrılar gibidir: call stack

Örneğe bakalım:

package main

import "fmt"

func main() {
    fmt.Println(f1())
}

func f1() int {
    return f2()
}

func f2() int {
    return 1
}

Yığında sırasıyla;

                           | main | f1()  -> push stack (yığına ekle)
                                                  |
  push stack <- f2() <---- | f1   | <-------------+
        |                  | main |
        |
        +----------------> | f2   | return 1 (stack’ten çık!)---+
                           | f1   |                             |
                           | main |                             |
                                                                |
    return f2() yani 1 <-- | f1   | <---------------------------+
    (stack’ten çık!)       | main |
        |
        |
        +----------------> | main |
  • Her fonksiyon çağırdığımız zaman stack’e push yapıyoruz.
  • Her return yaptığımızda da stack’ten pop ediyoruz.

Geri dönen tipin adı olabilir

func f2() (r int) {
    r = 1
    return
}

Fonksiyon birden fazla değer dönebilir

func f() (int, int) {
    return 5,6
}

func main() {
    x, y := f()
}

Bu tür kullanım genelde aynı anda hem state hem de sonuç dönmek için idealdir: x, err := f() ya da x, ok := f() gibi…