5# Go Programlama Dili — Slice

Nailcan Küçük
6 min readMay 2, 2020

Slice’lar, dizilere benzer şekilde içerisinde birden fazla veri tutabileceğimiz, dizilerin üst kümesi olarak görebileceğimiz tiplerdir. Dizilerdeki gibi, tek veri tipinde elemanlara sahiptir ve elemanların veri tipi tanım esnasında belirtilir. Dizilerden ayıran en önemli özelliği boyutunun değişken olmasıdır. Tanım esnasında bir boyut belirtilmez ve tanım sonrasında istediğiniz kadar eleman ekleyebiliriz.

func main() {
carArray := [3]string {“BMW”, “Ferrari”, “Opel”}
carSlice := []string {“BMW”, “Ferrari”, “Opel”}
}

İterasyon işlemlerinde dizilerde olduğu gibi dile ait tüm döngü işlemlerini (for, range, while..) kullanabiliriz.

for index, carBrand := range carSlice {
fmt.Println(index, carBrand)
}

Slice’lar, yapısal olarak 3 temel bilgiye sahiptir. Bunlar slice’ın uzunluğu, kapasitesi ve içerdiği verileri tutan dizidir. Eğer yeni bir tip olarak oluşturacak olsak şu şekilde tanımlayabilirdik:

type slice struct {
Length int
Capacity int
Array [10]array
}

Aslında slice tipinin ana yapısı bu kadar sade değil ancak daha rahat anlamak açısından en basit haliyle bu şekilde düşünebiliriz.

Slice’ların kapasiteleri 0 dan başlayarak 2’nin katları olarak ve içeriğindeki eleman sayısını kapsayacak şekilde artar. (0,1,2,4,8,16….).

Slice’a yeni elemanlar eklemek için append() fonksiyonunu kullanırız. Bu fonksiyon ilk parametre olarak içerisine eleman eklenecek slice’ı alır. Sonrasında ise eklenmek istenen elemanlar aralarında virgül konularak tek tek veya bir dizi/slice olarak fonksiyona geçilir.

Slice’a yeni elemanlar eklendiğinde sonra eğer slice’ın kapasitesi toplam eleman sayısını karşılayabilecek boyuttaysa elemanlar uygun indekslere yerleştirilir. Fakat mevcut kapasitesini aşacak ise, slice’ın yeni boyutunu kapsayabilecek, 2 nin katı boyutlarında, en küçük boyutlu yeni bir dizi oluşturulur ve elemanlar bu diziye taşınır.

Şimdi slice’a farklı yöntemlerle yeni elemanlar ekleyelim. Bu esnada da slice’ın boyutu ve kapasite değeri nasıl değişiyor gözlemleyelim.

Burada iki yeni fonksiyondan faydalanacağız. Bunlardan len() bize slice’ın boyutunu, cap() ise kapasitesini veren fonksiyonlardır.

package mainimport “fmt”func main() {
teams := []string{}
fmt.Println(“Baslangıçta:”)
fmt.Printf(“uzunluk: %d, kapasite: %d \n”, len(teams), cap(teams))
teams = append(teams, “Beşiktaş”)
fmt.Println(“Eklenen takımlar: Beşiktaş”)
fmt.Printf(“uzunluk: %d, kapasite: %d \n”, len(teams), cap(teams))
teams = append(teams, “Fenerbahçe”)
fmt.Println(“Eklenen takımlar: Fenerbahçe”)
fmt.Printf(“uzunluk: %d, kapasite: %d \n”, len(teams), cap(teams))
teams = append(teams, “Galatasaray”)
fmt.Println(“Eklenen takımlar: Galatasaray”)
fmt.Printf(“uzunluk: %d, kapasite: %d \n”, len(teams), cap(teams))
teams = append(teams, “Trabzon”, “Sivas”)
fmt.Println(“Eklenen takımlar: Trabzon, Sivas”)
fmt.Printf(“uzunluk: %d, kapasite: %d \n”, len(teams), cap(teams))
otherTeams := []string{“Kayseri”, “Göztepe”, “Malatya”, “Manisa”}
teams = append(teams, otherTeams…)
fmt.Println(“Eklenen takımlar: Kayseri, Göztepe, Malatya, Manisa”)
fmt.Printf(“uzunluk: %d, kapasite: %d \n”, len(teams), cap(teams))
}

Çıktısı:

Baslangıçta:
uzunluk: 0, kapasite: 0
Eklenen takımlar: Beşiktaş
uzunluk: 1, kapasite: 1
Eklenen takımlar: Fenerbahçe
uzunluk: 2, kapasite: 2
Eklenen takımlar: Galatasaray
uzunluk: 3, kapasite: 4
Eklenen takımlar: Trabzon, Sivas
uzunluk: 5, kapasite: 8
Eklenen takımlar: Kayseri, Göztepe, Malatya, Manisa
uzunluk: 9, kapasite: 16

Slice tanımlarken make() fonksiyonunu da kullanabiliriz. Bu fonksiyon ilk olarak slice elemanlarının hangi veri tipininde olacağını, ikinci olarak eleman sayısını, son olarak da kapasite değerini parametre olarak alır. Kapasite parametresi opsiyoneldir. İlk tanım sonrası elemanların değeri, slice elemanları için seçtiğimiz veri tipinin varsayılan (zero) değeridir. Bu değer int için 0, string için boş string (“”), bool için false değeridir.

package mainimport “fmt”func main() {
intSlice := make([]int, 2)
fmt.Printf(“%v, boyut: %d, kapasite: %d \n”, intSlice, len(intSlice), cap(intSlice))
stringSlice := make([]string, 3, 4)
fmt.Printf(“%q, boyut: %d, kapasite: %d \n”, stringSlice, len(stringSlice), cap(stringSlice))
stringSlice[0] = “Skoda”
stringSlice = append(stringSlice, “BMW”, “Ferrari”)
fmt.Printf(“%q, boyut: %d, kapasite: %d \n”, stringSlice, len(stringSlice), cap(stringSlice))
}

Çıktısı:

[0 0], boyut: 2, kapasite: 2 
[“” “” “”], boyut: 3, kapasite: 4
[“Skoda” “” “” “BMW” “Ferrari”], boyut: 5, kapasite: 8

2 boyutlu dizi tanımlanabildiği gibi 2 boyutlu slice da tanımlanabilmektedir. Aslında içerisinde 2 adet slice olan yeni bir slice tanımlamış oluyorsunuz. Bu nedenle 2 boyutlu slice içerisindeki her bir slice’ın boyutu ve kapasitesi farklı olabilir.

package mainimport “fmt”func main() {
_2DStringSlice := [][]string{{“one”, “two”, “three”}, {“a”, “b”}}
fmt.Printf(“%q \n”, _2DStringSlice)
_2DStringSlice[0][2] = “four”
_2DStringSlice[1][0] = “x”
fmt.Printf(“%q \n”, _2DStringSlice)
type _2DIntSlice [][]int
notes := _2DIntSlice{[]int{1, 2, 3}, []int{4, 6}}
fmt.Printf(“%v \n”, notes)
fmt.Println(notes[0][1])
}

Çıktısı:

[[“one” “two” “three”] [“a” “b”]] 
[[“one” “two” “four”] [“x” “b”]]
[[1 2 3] [4 6]]
2

Slice’ları belli bir kısmını alarak yeni bir slice oluşturabiliriz. Bunun için slice adından sonra köşeli parantez içersinde oluşacak yeni slice’ın başlangıç indeksini ve bitiş indeksinin bir fazlasını yazarak yaparız [ilk indeks:son indeks).

Oluşan yeni slice, aslında ilk slice’daki dizinin ilgili elemanlarını referans etmektedir. Bu nedenle yeni slice elemanlarının değeri değiştiğinde ana slice’da da karşılık gelen indeksteki değer değişecektir. Bu durumun nedeni slice’ların referans taşıyan veri tiplerinden (pass by reference) olmasıdır.

package mainimport “fmt”func main() {
cars := []string {“BMW”, “Ferrari”, “Opel”, “Skoda”}
fmt.Println(cars)
subsetOfCars := cars[1:3]
fmt.Println(subsetOfCars)
fmt.Println(“yeni slice’ımızın ilk indeksindeki değeri \”Honda\” olarak değiştirelim”)
subsetOfCars[0] = “Honda”
fmt.Println(subsetOfCars)
fmt.Println(“Bu durumda cars slice’ımızda da bu değer değişecektir.”)
fmt.Println(cars)
}

Çıktısı:

[BMW Ferrari Opel Skoda]
[Ferrari Opel]
yeni slice’ımızın ilk indeksindeki değeri “Honda” olarak değiştirelim
[Honda Opel]
Bu durumda cars slice’ımızda da bu değer değişecektir.
[BMW Honda Opel Skoda]

Burada hazır yeri gelmişken pass by value ve pass by reference kavramlarının ne olduğunu kısaca açıklamaya çalışayım. Öncelikle go dilindeki bu grupların içerisine giren veri tipleri/tanımlamaları şu şekilde örnekleyebiliriz.

Pass By Value: int, float, string, bool, structs
Pass By Reference: slices, maps, channels, pointers, functions

İlk olarak pass by value tanımını anlatmaya çalışayım. Örneğin string tipinde bir değişken tanımladınız ve bu değişkene bir değer atadınız. Ram üzerinde bir alanda bu string değişkene ait bir pointer ve bu pointer’ın işaret ettiği value alanı oluşur.

Siz bu değişkeni başka bir fonksiyona parametre olarak geçtiğinizde arka tarafta/ram üzerinde aslında var olan kaydın bir kopyası oluşturulur. Yeni bir pointer değerinin value alanına aynı değer yazılır.

Siz fonksiyon içerisinde bu değişkenin değerini değiştirseniz bile ilk tanımladığınız değişkenin değeri değişmez.

Aynı şekilde pass by reference olan slice’ı tanımladığınızda ise ram üzerinde bir alan da pointer ve bu pointer’ın işaret ettiği value alanı doldurulur. Value alanında slice’a ait ana bilgiler tutulur. Bunlar slice’ın boyutu, kapasitesi ve içeriğindeki eleman dizisinin ram’de tutulduğu alanın pointer değeridir. Ram üzerinde bir diğer alanda ise slice içerisindeki dizi tutulur.

Siz bu slice değişkenini bir fonksiyona parametre olarak geçtiğinizde, ram üzerinde slice’ın ana bilgilerini tutan alan kopyalanır ve yeni bir alana yazılır. Ancak slice’a ait diziyi tutan alan kopyalanmaz. Ram üzerindeki yeni kopya alan da diziyi tutan alanı referans olarak gösterir.

Bu nedenle siz fonksiyon içerisinde slice üzerinde yaptığınız değişiklikler, ilk tanımlandığı slice değişkenin de değerini değiştirmiş olursunuz. Şimdi bu anlattıklarımı bir de kod üzerinde görelim.

package mainimport “fmt”func main() {
car := “BMW”
fmt.Println(“car string’i oluştuğunda:”)
fmt.Println(car)
changeCarString(car)
fmt.Println(“changeCarString fonksiyonundan sonra”)
fmt.Println(car)
fmt.Println(“/////////////////”)cars := []string{“BMW”, “Ferrari”, “Opel”}
fmt.Println(“cars slice’ı oluştuğunda:”)
fmt.Println(cars)
changeCarsSlice(cars)
fmt.Println(“changeCarsSlice fonksiyonundan sonra”)
fmt.Println(cars)
}
func changeCarString(car string) {
car = “Ferrari”
fmt.Println(“changeCarString fonksiyonu içerisinde”)
fmt.Println(car)
}
func changeCarsSlice(cars []string) {
cars[0] = “Skoda”
fmt.Println(“changeCarsSlice fonksiyonu içerisinde”)
fmt.Println(cars)
}

Çıktısı:

car string’i oluştuğunda:
BMW
changeCarString fonksiyonu içerisinde
Ferrari
changeCarString fonksiyonundan sonra
BMW
/////////////////
cars slice’ı oluştuğunda:
[BMW Ferrari Opel]
changeCarsSlice fonksiyonu içerisinde
[Skoda Ferrari Opel]
changeCarsSlice fonksiyonundan sonra
[Skoda Ferrari Opel]

Son olarak da copy() fonksiyonundan bahsetmek istiyorum. Eğer elimizdeki bir slice’dan aynı değerlere sahip yeni bir slice oluşturmak istiyor, ancak ana slice’a referans etmesini istemiyorsak copy() fonksiyonunu kullanmalıyız. Copy fonksiyonu aynı değerlere sahip ancak yepyeni bir slice oluşturmaya yarar. İki parametre alır. Bunlardan ilki içerisine verilerin kopyalanacağı yeni slice, ikincisi ise kopyalanacak kaynak slice’dır.

package mainimport “fmt”func main() {
cars := []string {“BMW”, “Ferrari”, “Opel”, “Skoda”}
fmt.Println(“Başlangıçta”)
fmt.Printf(“cars: %q \n”, cars)
copyCars := make([]string, len(cars))
copy(copyCars, cars)
copyCars[0] = “Bugatti”
copyCars = append(copyCars, “Ford”)
fmt.Println(“Copy fonksiyonundan sonra”)
fmt.Printf(“cars: %q \n”, cars)
fmt.Printf(“copyCars: %q \n”, cars)
}

Çıktısı:

Başlangıçta
cars: [“BMW” “Ferrari” “Opel” “Skoda”]
Copy fonksiyonundan sonra
cars: [“BMW” “Ferrari” “Opel” “Skoda”]
copyCars: [“BMW” “Ferrari” “Opel” “Skoda”]

Go Programlama Dili ile tanıştığım slicelar hakkında kısaca yazmaya çalıştım. Umarım sizler için de faydalı olmuştur :)

İletişimde kalmak isterseniz linkedin üzerinden haberleşebiliriz.

--

--