Go öğreniyorum - Gün 4

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

Bugün array, slice ve map’i, veri tiplerini çevirmeyi ve range’i öğrendim.

Chapter 5

Array

Sabit boyu olan, içinde sıralı şekilde elemanların durduğu bir taşıyıcı. Diğer dillerde olduğu gibi 0 indeksli. Yani ilk elemanı almak için 0.’yı istiyoruz.

var x [5]int // [0 0 0 0 0]

x bir array. 5 elemanı var. her eleman integer tipinde.

package main

import "fmt"

func main() {
    var x [5]int         // [0 0 0 0 0] default init etti.
    x[4] = 100           // 0 index’li, 4. eleman yani aslında 5. elemanı 100 yaptı
    fmt.Println(x)       // sonuç: [0 0 0 0 100]
}

Array içindeki sayıların ortalamasını bulalım:

package main

import "fmt"

func main() {
    var x [5]float64            // [0  0  0  0  0]
    x[0] = 98                   // [98 0  0  0  0]
    x[1] = 22                   // [98 22 0  0  0]
    x[2] = 31                   // [98 22 31 0  0]
    x[3] = 91                   // [98 22 31 91 0]
    x[4] = 7                    // [98 22 31 91 7]

    var toplam float64 = 0      // toplam: 0
    for i := 0; i < 5; i++ {
        toplam += x[i]          // toplam = 98+22+31+91+7 = 249
    }

    fmt.Println(toplam / 5)     // 249/5 = 49.8
}

Ortalamayı almak için, toplam / 5 yerine array’in boyunu alalım;

fmt.Println(toplam / len(x))

len bize array’in boyunu veriyor. Yani length. Bu kod çalışınca hata mesajı alırız.

invalid operation: toplam / len(x) (mismatched types float64 and int)

len bize integer (int) dönüyor. Oysa biz float64 çalışıyoruz. Bu bakımdan derleyci bizi uyardı. Uyumsuz iki tip var dedi. Öyleyse len’den gelen int’i float64’e çevirmemiz gerek;

fmt.Println(toplam / float64(len(x)))

Tip dönüşümlerinde aynı fonksiyon çağırır gibi yapıyoruz. float64(dönüşecek tip) gibi. Şimdi, döngü işini biraz daha geliştiriyoruz;

var toplam float64 = 0
for i, eleman := range x {                 // kendime not: 
    toplam += eleman                       // ruby’deki foreach_with_index
}                                          // Enumerator :)
fmt.Println(toplam / float64(len(x)))

Bu kod çalışınca hata mesajı alırız:

i declared and not used

Yani, derleyici derki i diye bir şey tanımladın ama kullanmadın. Boşa akan su misali musluğu kapat diyor :))) O halde;

var toplam float64 = 0
for _, eleman := range x {
    toplam += eleman
}
fmt.Println(toplam / float64(len(x)))

Single underscore yani _ derleyiciye der ki:

An itibariyle buna ihtiyacım yok, bu iteratör değişkeni bize lazım değil!

Keza, Go bize array tanımlamak için daha da basit bir syntax sağlar:

x := [5]float64{ 98, 22, 31, 91, 7 }

Hatta;

x := [5]float64{
    98,
    22,
    31,
    91,
    7,
}

Dikkat ettiyseniz sonda , kalan bir virgül var. Aynı Python ve Ruby’deki gibi, bu bize kolay modifiye etme imkanı sağlıyor. Örneğin son elemanı comment out edersek;

x := [5]float64{
    98,
    22,
    31,
    91,
//  7,
}

şeklinde yapabiliyoruz. Array'lerde hep boyut tanımlaması yapmak gerektiği için bazen bu can sıkıcı olabiliyor. Dinamik dillerden alışık olduğumuz rahatlık ne yazıkki derlenen Go dünyasında olamayabiliyor.

Bu tür durumlarda imdada kim geliyor?

Slice

Aslında Array’in bir segmentidir (kesiti/parçasıdır). Aynı Array gibi indeksi ve boyu var ama bir farkla. Boyu değişebilir:

var x []float64   // x, boyu 0 olan bir array gibi

Eğer boyut vermek istersek make kullanıyoruz:

x := make([]float64, 5) // 5 tane float64'den oluşan array

Neticede slice (Türkçesi: dilim), array’den bir kesit. Yani bir array’den kesilmiş parça. Bu bakımdan bağlı olduğu array’dan büyük olamıyor:

x := make(float64, 5, 10) // 3 parametre aldı make...

Kapasitesi 10 olan bir array’den 5’lik slice çıkart…

array := [5]float64{1, 2, 3, 4, 5}
x := array[0:3]  // [1 2 3]

[low:high] ya da [start:end] yan, [0:3] ifadesinde, array’in 0.elemanından itibaren, 0. dahil toplamaya başla. İndeksi 1 arttır. Ne zamana kadar ? İndeks 3’ten küçük olduğu sürece.

array[0:5]   // [1 2 3 4 5]
array[1:4]   // [2 3 4]

[1:4]: array’in 1.elemanı: 2, 2’den itibaren yürü. İndeks’i 1 arttır, 4’ten küçük olduğu sürece devam et… Python ile birebir aynı:

[1,2,3,4,5][1:4] # [2, 3, 4]

Diğer kolay yollar:

array[:]  // [1 2 3 4 5] -> start: olabilecek en küçük, end? en büyük -> 0:5
array[0:] // [1 2 3 4 5] -> İndeks 0’den array’in sonuna kadar.
array[2:] // [3 4 5]     -> İndeks 2’den array’in sonuna kadar.
array[:2] // [1 2]       -> start: olabilecek en küçük, end: 2 -> 0:2

array[0:len(array)] // [1 2 3 4 5]
array[:len(array)]  // [1 2 3 4 5]

Slice için iki tane yardımcı var: append ve copy

append

Tahmin edileceği gibi, slice’ın sonuna eleman eklemek içindir. Bu slice’ın boyunu da büyütür:

slice1 := []int{1, 2, 3}          // [1 2 3]
slice2 := append(slice1, 4, 5)    // [1 2 3 4 5]
fmt.Println(slice2)

copy

İki argüman alır: dst (Destination) ve src (Source). src dakiler, dst’a kopyalanır. Keza direkt üzerine yazılır. Varolan silinir. Eğer src, dst’dan büyükse, dst’nin kapasitesi kadar yere kopya yapılır.

godoc builtin copy # copy, builtin fonksiyondur.

func copy(dst, src []Type) int
    The copy built-in function copies elements from a source slice into a
    destination slice. (As a special case, it also will copy bytes from a
    string to a slice of bytes.) The source and destination may overlap.
    Copy returns the number of elements copied, which will be the minimum of
    len(src) and len(dst).

Bence, kopya işleminde, neredeyse her yerde src -> dst mantığı vardır. Neyi? nereye? kopyalıyoruz gibi… Go’da bu ters olmuş…

package main

import "fmt"

func main() {
    slice1 := []int{1, 2, 3}    // kapasite: 3
    slice2 := make([]int, 2)    // kapasite: 2
    copy(slice2, slice1)        // slice2’yi slice1’e kopyala
    fmt.Println(slice2)         // [1 2] -> ancak 2 elemanlık yer vardı :)
}

Genel olarak mantık hep n.İndeksdeki elemanı ver şeklindedir. İndeks hep sayısal bir değerdir. Peki indeks olarak sayısal değer yerine başka birşey gerekse? Bir takımdaki oyuncuyu soyadına göre aramak gerekse? İşte o zaman devreye map girer…

Maps

Bu, Python / Javascript / Ruby ve hatta Php’den bildiğimiz hash / dictionary ya da Associative Array’lerdir. Bildiğiniz key/value ikilisi:

var x map[string]int
x["key"] = 4
fmt.Println(x)        // runtime error!

map kullanmak için önce initialize etmemiz gerekiyor:

package main

import "fmt"

func main() {
    x := make(map[string]int)
    x["key"] = 4
    fmt.Println(x)                 // map[key:4]
    fmt.Println(x["key"])          // 4
}

key illa string olmak zorunda değil:

x := make(map[int]int)
x[1] = 10
fmt.Println(x[1]) // 10

Önemli iki hatırlatma:

  1. key/value atandıkça map büyür.
  2. Sıralı yani sequential değildir.

Eğer eleman silmek istersek; delete kullanırız:

package main

import "fmt"

func main() {
    x := make(map[string]string)
    x["username"] = "vigo"
    x["password"] = "secret"
    fmt.Println(x["username"])  // vigo
    fmt.Println(x["password"])  // secret
    delete(x, "password")       // x’den key’i "password" olanı sil
    fmt.Println(x)              // map[username:vigo]
}

Şimdi, aşağıdaki programı bakalım:

package main

import "fmt"

func main() {
    elements := make(map[string]string)
    elements["H"] = "Hydrogen"
    elements["He"] = "Helium"
    elements["C"] = "Carbon"

    fmt.Println(elements["He"]) // Helium
}

Şimdi, olmayan bir key çağıralım ve kontrol yapalım:

name, ok := elements["XX"]
fmt.Println(name, ok)       // false

O halde;

package main

import "fmt"

func main() {
    elements := make(map[string]string)
    elements["H"] = "Hydrogen"
    elements["He"] = "Helium"
    elements["C"] = "Carbon"

    // eğer ok, true ise çalışır!
    if name, ok := elements["XX"]; ok {
        fmt.Println(name, ok)
    }

}

Aynı array gibi, map’in de kolay yazılımı var;

elements := map[string]string{
    "H": "Hydrogen",
    "He": "Helium",
    "C": "Carbon",
}

Biraz daha geliştiriyoruz:

package main

import "fmt"

func main() {
    elements := map[string]map[string]string{
        "H": map[string]string{
            "name":  "Hydrogen",
            "state": "gas",
        },
        "He": map[string]string{
            "name":  "Helium",
            "state": "gas",
        },
        "Li": map[string]string{
            "name":  "Lithium",
            "state": "solid",
        },
    }

    if el, ok := elements["Li"]; ok {
        fmt.Println(el["name"], el["state"]) // Lithium solid
    }

}

elements tipi: map[string]map[string]string oldu. Yani,

map[string]string
      |      |
     key    value


map[string]map[string]string
      |    \_______________/
     key          |
    ("H")       value
                  |
             [string]string
                 |       |
                key     value
              ("name")  ("Hydrogen")
              ("state") ("gas")

Ödev

  • Bir array ya da slice’ın 4.elemanına nasıl erişirsin?
  • make([]int, 3, 9) boyutu nedir? (lenght)
  • Aşağıdaki örneğe göre x[2:5] ?
x := [6]string{ "a", "b", "c", "d", "e", "f" }
  • Listedeki en küçük sayıyı bul!
x := []int{
    48, 96, 86, 68,
    57, 82, 63, 70,
    37, 34, 83, 27,
    19, 97, 9, 17,
}