10 Aralık 2024 Salı

Go Programlama Dili

 Selam bu yazımda Go programlama dilini öğrenmeye çalışacağım.

Go popüler bir programlama dilidir. Go bilgisayar programları oluşturmak için kullanılır. 


Go Nedir?

  • Go her platformda çalışan , açık kaynak kodlu bir programlama dilidir. Mesela ben bu yazıyı hazırlarken Windows, WSL üzerinde Ubuntu ve Linux Lite işletim sistemlerinde çalıştım.
  • Go , performansı yüksek uygulamalar geliştirmekte kullanılabilir. 
  • Go , basitliği ve verimliliği ile öne çıkan hızlı, static typed (yani değişken tiplerini bildirmek zorundasınız) ve derlenerek kullanılan bir programlama dilidir.
  • Go , Google'da Robert Griesemer, Rob Pike, ve Ken Thompson tarafından 2007'de geliştirilmiş. 
  • Go'nun deyim yapısı biraz C++ 'a benzer.


Go Nerede Kullanılır?

  • Web geliştirme (server tarafı)
  • Network-temelli programlar yazmak
  • Her platformda çalışan uygulamalar geliştirme
  • Bulut-tabanlı geliştirme

Neden Go Kullanmalı?

  • Go , eğlenceli ve öğrenmesi kolaydır
  • Go , derleme süresi ve çalışması çok hızlıdır
  • Go , eşzamanlı çalışmayı destekler
  • Go , bellek yönetimine sahiptir
  • Go , birçok platformda çalışır (Windows, Mac, Linux, Raspberry Pi, vs.)


Python ve C++ ile Karşılaştırınca Go


Go Python C++
Statically typed Dynamically typed Statically typed
Hızlı çalışma Yavaş çalışma Hızlı çalışma
Derlenen Yorumlanan Derlenen
Hızlı derleme zamanı Yorumlanan Yavaş derleme zamanı
Goroutine'ler ve channel ile eşzamanlı çalışma yapar Öntanımlı bir eşzamanlı çalışma mekanizması yoktur Thread'lar ile eşzamanlı çalışma yapar
Artıkları otomatik toplar Artıkları otomatik toplar Artıkları otomatik toplaması yoktur
class(sınıf) ve object(nesne) desteklemez class(sınıf) ve object(nesne) destekler class(sınıf) ve object(nesne) destekler
Kalıtımı (inheritance) desteklemez Kalıtımı (inheritance) destekler Kalıtımı (inheritance) destekler


Hadi Başlayalım

Go derleyicisi bilgisayarınızda yüklü mü terminalde şunu yazıp cevapına bakalım. 

go version

buna cevap olarak örnek

 go1.22.2 linux/amd64

gibi birşey gelmişse hemen başlayabilirsiniz. Sisteminizde Go programlama dili yüklü değilse İndirme Sayfasına gidip oradaki yönergeleri takip edebilirsiniz. Linux sistemlerde paket yöneticisi ile kolayca yüklenebilir.

Bir kod editörüne ihtiyacınız olacak , ben hem Windows hem Linux sistemlerimde VsCode editörünü kullanıyorum örneğin. 


İlk Program

Hadi ilk programımızı yazalım

  • VS Code editörünü açın (Linux için çalışmak istediğiniz klasörde terminalde "code ." yazarak ya da Windows'ta gezginde klasör içinde sağ tıklayıp "Code ile aç" seçeneğini tıklayarak)
  • Eklenti yöneticisini açın 
  • Arama kutusuna "go" yazıp enter basın
  • Google'ın Go takımının yazdığı eklentiyi bulup yükleyin. Bu size kod yazarken çok yardımcı olacaktır.


Şimdi yeni bir dosya üretelim adı da "merhaba.go" olsun. İçine şu kodu yazalım:

merhaba.go

package main

import (
    "fmt"
)

func main() {
    fmt.Println("Merhaba dünya!")
}

Şimdi terminalde şu komutu girip programımızı çalıştırabiliriz:

go run ./merhaba.go

Terminal için VS Code editörünün terminalini de kullanabilirsiniz, sizin tercihiniz. Program çalışınca terminalde şöyle bir çıktı yazacaktır:

Merhaba dünya!

Tebrikler! ilk Go programınız çalışıyor. Programınızı derleyip direk çalışan program haline getirmek için terminalde şu komutu girin:

go build ./merhaba.go

Artık programınız derlenmiş ve çok daha hızlı çalışmaya hazırdır. Programınız çalıştırmak için terminalde:

./merhaba

yazmanız yeterli olacaktır. Derleme işlemi Linux sistemlerde "merhaba" adında çalıştırılabilir bir dosya Windows sistemlerde ise "merhaba.exe" adında çalıştırılabilir bir dosya oluşturur. Artık bu dosyayı Go programlama dili olmayan başka bilgisayarlarda da kopyalayarak kullanabilirsiniz. 


Go Deyim Yapısı (syntax)

Bir Go program dosyası şu parçalardan oluşur:

  • package tanımlaması
  • import ile paket yüklemeleri
  • Fonksiyonlar
  • İfadeler ve eşitlikler


Örnek programımıza geri dönelim

merhaba.go

package main

import (
    "fmt"
)

func main() {
    fmt.Println("Merhaba dünya!")
}

Satır 1 : Her Go programı bir paketin parçasıdır.  Biz bunu package kelimesini kullanarak belirtiriz. Bu örnekte program main paketine aitttir.

Satır 2 : import ("fmt") ile fmt paketinde bulunan fonksiyonları programımızda kullanacağımızı belirtiyoruz. Bu Go dilinde kullanılan standart paketlerden biri.

Boş satırlar : Go derleyicisi boş satırlarda hiç bir işlem yapmaz. Bunlar sadece programın kodunun daha okunabilir olması amacıyla bırakılır. 

Satır 7 : func main() { } yapısı bir fonksiyon tanımlamasıdır. Süslü parantez içinde yazılan her kod çalıştırılacaktır. 

Satır 8 : fmt.Println() bizim daha önce import ettiğimiz (programa dahil ettiğimiz) fmt paketinde tanımlı olan bir fonksiyondur. Terminale bir yazı çıktısı yazdırmak için kullanılır, örneğimizde bu fonksiyon terminale Merhaba dünya! yazacaktır.


Not : Go programlama dilinde çalıştırılabilir her program main paketine ait olmalıdır.


Go İfadeleri (Statements)

fmt.Println("Merhaba dünya!") satırı bir ifadedir. 

Go'da ifadeler bir alt satıra geçerek ya da sonuna ";" noktalı virgül eklenerek bitirilir. 

İfade sonrası enter tuşuna basmak aslında oraya görünmeyen bir noktalı virgül koymak gibidir. 

Süslü parantez açılışı { satırda ilk karakter olamaz. 

Aşağıdaki kodu çalıştırıp ne olduğuna bir bakın:

package main

import (
    "fmt"
)

func main()
{
    fmt.Println("Merhaba dünya!")
}

karşınıza bir hata mesajı çıkar:

./merhaba.go:8:1: syntax error: unexpected semicolon or newline before {


Kompakt Kod Yazmak

Kodunuzu aşağıdaki gibi noktalı virgüllerle ayırarak tek satırda da yazabilirsiniz ancak pek okunabilir bir şey olmaz. Zaten VS Code kullanıyorsanız bu kodu hemen standart okunabilir hale dönüştürür. 

package main; import ("fmt"); func main() { fmt.Println("Merhaba dünya!");}

Bunu ancak sizden sonra kodu okuyacaklara eziyet olsun diye yapabilirsiniz..


Go Yorumları

Bir yorum , kod çalıştırılırken anlam ifade etmeyecek olan yazıdır. 

Yorumlar kodunuzda yaptıklarınızı ve yapacaklarınızı anlatmak amaçlı kısa yazılar olabilir.

Yorum yapısı aynı zamanda kodun bir kısmının çalışmasını istemediğinizde, silmeden o kodları yoruma çevirerek işlemesini engellemek için de kullanılır.

Go dili tek ya da çok satırlı yorumları destekler.


Tek Satır Yorumlar

Tek satır yorumlar iki tane bölü işareti ile başlar ( // ).

// karakterlerinden sonra satır sonuna kadar olan yazılar derleyici tarafından işlenmez.

// Bu bir yorum
package main

import (
    "fmt"
)

func main() {
    // Bu da bir yorum
    fmt.Println("Merhaba dünya!")
}

Ayrıca kod satırı sonrasına da aynı şekilde yorum eklenebilir.

package main

import (
    "fmt"
)

func main() {
    fmt.Println("Merhaba dünya!") // Bu bir yorum
}


Çok Satırlı Yorumlar

Çok satırlı yorumlar /* ile başlar ve */ ile biter.

/* ve */ arasındaki tüm yazılanlar derleyici tarafından kaale alınmaz.

func main() {
    /* Ekrana "Merhaba dünya"
    yazan komut */
    fmt.Println("Merhaba dünya!")
}

gibi.

Not : Her şey size göre değişir ama genelde // kısa yorumlarda ve /* - */ yapısı uzun yorumlarda kullanılır.


Kodun bir kısmının çalışmasını engellemek için yoruma atmak

Denemeler yaparken yorumları, kodunuzun bir kısmının çalışmasını engellemek için de kullanabilirsiniz. 

"Yoruma atmak" denilen bu işlem daha sonra lazım olacak kodları ya da iptal edilen kısımları silmeden saklamak için kullanılabilir.

func main() {
    fmt.Println("Merhaba dünya!")
    // fmt.Println("Bu kod satırı çalışmayacak")
}


Go Değişkenleri

Her programlama dilinde verilerin değerlerini saklamak için değişkenler kullanılır. 

Değişken Tipleri

Go dilinde bir çok değişken tipi vardır, örnek:

  • int - Tamsayı (integer) değerleri saklamak için kullanılan tipdir. Örneğin 123 ya da -789
  • float32 - Kayan noktalı sayıları saklamak için kullanılır. Örneğin 19.99 ya da -123.45
  • string - Yazıları saklar. Mesela "Merhaba dünya!". String değerler çift tırnak ile çevrelenmiş ifade edilmelidir.
  • bool - Doğru/yanlış ifade eden değerleri saklar, değeri true ya da false olur.

Daha bir çok değişken tipi var ancak şimdilik onlara girmiyoruz.


Bir Değişken Tanımlamak

Go dilinde yeni bir değişkeni tanımlamak için 2 yöntem vardır. 


1 - var kelimesi kullanarak

var kelimesini ardından değişken adı ve tipi gelecek şekilde kullanırsınız.

var değişkenismi tipi = değeri

 Not : Her zaman tipi ya da değeri (veya her ikisini de) girmeniz gereklidir.

 

2 - := işareti ile 

:= işaretini bir değer girerek kullanırsınız.

değişkenismi := değeri

Not : Bu durumda değişken tipi tahmin edilir ( yani derleyici girilen değere göre değişken tipini tahmin eder ).

Not : Bu şekil tanımlamada mutlaka bir değer girilmesi zorunludur , tabii ki..


İlk Değer Vererek Değişken Tanımlama

Eğer değişkenin değerinin program başladığında ne olması gerektiğini biliyorsanız tanımlama yaparken bir değer vererek yaparsınız:

package main

import (
    "fmt"
)

func main() {
    var öğrenci1 string = "Ümit" //tipi string
    var öğrenci2 = "Ayşe"        //tipi tahmin edilir
    x := 2                       //tipi tahmin edilir

    fmt.Println(öğrenci1)
    fmt.Println(öğrenci2)
    fmt.Println(x)
}

Not : öğrenci2 ve x değişkenlerinin tipleri değerlerinden tahmin edilir.


İlk Değer Girmeden Değişken Tanımlamak

Go dilinde tüm değişkenler başlangıç değerine sahiptir. Eğer siz bir değer girmediyseniz derleyici değişken tipine ait default değeri verecektir. 

package main

import (
    "fmt"
)

func main() {
    var a string
    var b int
    var c bool

    fmt.Println(a)
    fmt.Println(b)
    fmt.Println(c)
}

Bu örnekte 3 tane değişken tanımlanmış

  • a
  • b
  • c

Bu değişkenler tanımlanmış fakat ilk değerleri verilmemiş.

Bu kodu çalıştırınca ekrana bu 3 değişkenin default değerleri yazılacaktır.

  • a için "" (yani boş string)
  • b için 0
  • c için false


Tanımlamadan Sonra Değer Atamak

Bir değişkeni tanımladıktan sonraki satırlarda da değer atamak mümkündür. Bu başlangıçta değeri bilinemeyen değişkenler için kullanılır.

...
func main() {
    var öğrenci1 string
    öğrenci1 = "Ümit"

    fmt.Println(öğrenci1)
}

Not : Bir kez daha belirtelim ":=" işareti ile değişken tanımlarsak bir değer girmek zorundayız.


var Kelimesi ve := İşareti Arasındaki Fark

var kelimesi ve := işareti arasında küçük farklar vardır.

var:=
Fonksiyonların içinde ya da dışında kullanılabilir Fonksiyonların sadece içinde kullanılabilir
Değişken tanımlaması ve değer ataması ayrı yapılabilir Değişken tanımlaması ve değer ataması ayrı yapılamaz (aynı satırda yapılmalıdır)


Bu örnek var kelimesinin fonksiyon dışında kullanımını gösteriyor.

package main

import (
    "fmt"
)

var a int
var b int = 2
var c = 3

func main() {
    a = 1

    fmt.Println(a)
    fmt.Println(b)
    fmt.Println(c)
}


Aşağıdaki örnekte := fonksiyon dışında kullanıldığı için hata verecektir.

package main

import (
    "fmt"
)

a := 1

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

Kodu çalıştırınca alacağımız hata mesajı:

> go run ./var.go
./var.go:7:1: syntax error: non-declaration statement outside function body


Çoklu Değişken Tanımlamaları

Go dilinde aynı satırda birden çok değişkeni tanımlamak  da mümkündür. 

package main

import ("fmt")

func main() {
    var a, b, c, d int = 1, 2, 3, 4
    fmt.Println(a)
    fmt.Println(b)
    fmt.Println(c)
    fmt.Println(d)
}

Not : Eğer tip belirtme kelimesi kullanırsanız satırda tanımlanan tüm değişkenler tek bir tipte olabilir. 


Eğer tip belirtilmezse aynı satırda değişik tiplerde değişken tanımlanabilir.

package main

import ("fmt")

func main() {
    var a, b = 6, "Merhaba"
    c, d := 7, "Dünya"
    fmt.Println(a)
    fmt.Println(b)
    fmt.Println(c)
    fmt.Println(d)
}



Blok İçerisinde Değişkenleri Tanımlama

Kodunuzun okunurluğunu arttırmak için değişken tanımlarınızı bir blok içinde de yapabilirsiniz.

package main

import ("fmt")

func main() {
    var (
        a int
        b int = 1
        c string = "Merhaba"
    )

    fmt.Println(a)
    fmt.Println(b)
    fmt.Println(c)
}



Değişken İsimlendirme Kuralları

Bir değişkenin kısa bir adı olabilir (mesela x ya da y) veya daha açıklayıcı bir adı da olabilir (yaş, fiyat, isim gibi).

İsimlendirme yaparken belli kurallara uymamız gerekir:

  • Bir değişken ismi , bir harf ya da alt çizgi ( _ ) ile başlamalıdır.
  • Bir değişken ismi rakamla başlayamaz.
  • Bir değişken isminde sadece alfa-nümerik karakterler ve alt çizgi olabilir ( a-z, A-Z, 0-9, ve _ ).
  • Değişken isimleri büyük/küçük harf duyarlıdır ( fiyat, Fiyat ve FIYAT ayrı değişkenlerdir).
  • Değişken ismi uzunluğunda sınırlama yoktur (uzunkavaklaraltındayataruyumazoğlu adında değişkeniniz olabilir mesela).
  • Bir değişken ismi içinde boşluk bırakılmaz.
  • Go dilinde kullanılan deyimler değişken ismi olamaz.


Çok Kelimeli Değişken İsimleri

Birden fazla kelimeden oluşan değişken isimlerini aralara boşluk koymadan yazmak için çeşitli öneriler vardır:

Camel Case

İlk kelime hariç tüm kelimelerin ilk harfleri büyük yazılır.

    uzunİsimliDeğişkenim = 888

Pascal Case

Tüm kelimeler büyük harfle başlar, küçük harf devam eder.

    UzunİsimliDeğişkenim = 888

Snake Case

Kelimeler arasına alt çizgi konarak yazılır.

    uzun_isimli_değişkenim = 888

Herhalde Ruby programlama dilinde çok aşina olduğum için ben bu snake case'i daha okunabilir buluyorum.




Go Sabitleri

Bir değer program çalışırken hiç değişmeyecek ve aynı kalacaksa bunu bir değişken değil sabit olarak tanımlarız. Sabit değerleri program içinde kullanıldığı yerlere de direk yazabiliriz ama ileride değeri değiştirmek için nerelerde kullanıldığını tek tek hatırlamamız zor olur. Bu yüzden bir isim vererek program başında tanımlamamız doğru olacaktır. 

Sabit değerleri tanımlamak için const kelimesi kullanılır. const kelimesi bu değerin constant-sabit olduğunu ve bir daha değiştirilemeyeceğini belirtir.

const CONSTADI tipi = değeri

Not : Sabitin değeri mutlaka tanımlamasının yapıldığı satırda girilmelidir.


Go Bir Sabit Tanımlamak

Örnekte Go dilinde bir sabitin tanımlanması ve kullanımı görülüyor.

package main

import ("fmt")

const PI = 3.14

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


Go Sabit İsim Kuralları

  • Sabit isimleri için değişken isimleri için verilen kurallar aynen geçerlidir.
  • Sabit isimlerinin tümü büyük harf yazılması tavsiye edilir (değişkenlerden ayrı algılanması için)
  • Sabitler fonksiyon içinde veya dışında tanımlanabilirler.


Sabitlerin Veri Tipleri

İki çeşit sabit vardır

  • Tipi girilmiş sabitler
  • Tipi girilmemiş sabitler


Go Tipi Girilmiş Sabitler

Bunları tanımlarken değerleri için bir tip belirtilir.

package main

import ("fmt")

const A int = 1

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


Go Tipi Girilmemiş Sabitler

Eğer tanımlamada bir veri tipi belirtmezsek derleyici atadığımız değere göre bir tip belirleyecektir.

...
const A = 1

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


Go Sabitler: Değiştirilemez ve Sadece-Okunur

Bir sabiti tanımladıktan sonra değerini değiştirmek mümkün değildir.

package main
import ("fmt")

func main() {
  const A = 1
  A = 2
  fmt.Println(A)
}

Bu programı çalıştırınca bize bir hata mesajı verir:

> go run ./const.go
./const.go:9:2: cannot assign to A (neither addressable nor a map index expression)


Go Çoklu Sabit Tanımlaması

Birçok sabiti bir blok içinde tanımlayarak kodumuzun okunabilirliğini arttırabiliriz:

package main
import ("fmt")

const (
  A int = 1
  B = 3.14
  C = "Hey!"
)

func main() {
  fmt.Println(A)
  fmt.Println(B)
  fmt.Println(C)
}




Go Çıktı Fonksiyonları

Terminale yazı çıktısı yapmak için 3 temel fonksiyon kullanılır.

  • Print()
  • Println()
  • Printf()


Go Print() Fonksiyonu

Print() fonksiyonu argümanında verilen değeri (parantez içinde girilenler) default formatında çıktıya (terminale) yazar.

Örnekte değişkenler i ve j değerleri yazdırılıyor:

package main
import ("fmt")

func main() {
  var i,j string = "Merhaba","Dünya"

  fmt.Print(i)
  fmt.Print(j)
}

Programı çalıştırınca şuna benzer çıktı verir:

root@UJK-LAPTOP:~/go# go run ./output.go
MerhabaDünyaroot@UJK-LAPTOP:~/go#

Print() fonksiyonu yazımı bitince kursörü alt satıra geçirmez. Bu yüzden alt satıra geçirme işlemini biz kodumuza ilave ile yapmalıyız. Bu amaçla alt satıra geçilmesini istediğimiz yerlere "\n" karakterlerini ekleriz.

...
func main() {
    var i, j string = "Merhaba", "Dünya"

    fmt.Print(i, "\n")
    fmt.Print(j, "\n")
}

Şimdi çıktımız şöyle görünür:

Merhaba
Dünya

\n karakterleri terminale yazdırılırken sanki kendimiz yazarken enter basmışız gibi yeni satıra geçirecektir. Böyle başka özel karakter dizilimleri de var, ilerde öğreniriz inşallah.

Şimdi şunu deneyelim:

...
func main() {
    var i, j string = "Merhaba", "Dünya"

    fmt.Print(i, j, "\n")
}

ve çıktımız:

MerhabaDünya


Kelimelerin arasına boşluk eklememiz de gerekecek.

func main() {
    var i, j string = "Merhaba", "Dünya"

    fmt.Print(i, " ", j, "\n")
}

çıktısı:

Merhaba Dünya

evet oldu şimdi. 

Print() fonksiyonu eğer parametrelerde verilen değerlerin hepsi de string değilse araya otomatik boşluk ekler:

...
func main() {
    var i, j = 10, 20

    fmt.Print(i, j)
}

Bir örnek daha:

func main() {
    var i, j = 10, "20"

    fmt.Print(7, i, j, 5, "\n")
}

ve çıktısı:

7 10205

Sadece her ikisi de string olmayan 7 değeri ve i değişkeni değerini yazarken araya boşluk koydu. Herhangi bir tarafta string varsa arada boşluk verilmiyor.


Go Println() Fonksiyonu

Println() fonksiyonunun yaptığı Print() fonksiyonuna benzer ama bu parametrelerin arasına yazarken boşluk verdiği gibi en sona da alt satıra geçiş ekler.

func main() {
    var i, j string = "Merhaba", "Dünya"

    fmt.Println(i, j)
}

Bu kodun çıktısı:

Merhaba Dünya

Madem böyle oluyordu niye uğraştık o kadar az önce? 

Her ikisinin de kullanım yeri ayrı. Println() ile terminalde satırı işgal eden çıktılar alırken Print() fonksiyonlarını akıllı kullanarak ekranda tablo şeklinde yerleştirmeler yapabiliriz mesela.


Go Printf() Fonksiyonu

Bu çok daha kapsamlı bir fonksiyon. Bu fonksiyonu kullanarak değişken değerlerini yazdırırken formatı (yazım düzenini) belirterek istediğimiz şekilde görünmesini sağlarız.

Örnek 2 format yazımı verelim:

  • %v ile parametrede verilen değişkenin değeri yazılır
  • %T ile parametrede verilen değişkenin tipi yazılır

Örnek:

package main
import ("fmt")

func main() {
  var i string = "Merhaba"
  var j int = 15

  fmt.Printf("i değeri: %v ve tipi: %T\n", i, i)
  fmt.Printf("j değeri: %v ve tipi: %T\n", j, j)
}

önce kodu çalıştıralım, çıktı şöyle olur:

i değeri: Merhaba ve tipi: string
j değeri: 15 ve tipi: int

İlk parametre olan yazının içinde %v olan yere yazının ardından gelen ilk değer olan i değişkeni değeri yazılıyor. %T olan yere ise yazının ardından gelen ikinci değer olan yine i değişkenin tipi geliyor. İlk parametrede verilen yazının içine formatlama karakterlerimize göre daha sonra verilen değerler enjekte ediliyor. 

Şöyle de yazsak nasıl olur?

...
func main() {
    var i string = "Merhaba"
    var j int = 15

    fmt.Printf("i : %v ve j : %v\ni-tip : %T ve j-tip : %T\n", i, j, i, j)
}

Çıktısı:

i : Merhaba ve j : 15
i-tip : string ve j-tip : int

Sırasıyla i, j, i ve j değerlerini yazının içine enjekte ediyor. Kullanıcıya daha anlamlı mesajlar verirken çok kullanacağımız bir fonksiyon.


Go Printf() ile Kullanılan Formatlama Terimleri

Go dili Printf() fonksiyonu ile kullanılacak bir çok formatlama terimine sahip.


Go Genel Formatlama Terimleri

Aşağıdaki ifadeler tüm veri tipleri ile kullanılabilir:

TerimAçıklaması
%vDeğeri default yazım formatında yazar
%#vDeğeri Go deyimi formatında yazar
%T (büyük T!)Değerin veri tipini yazar
%%Yazı olarak bir tane "%" karakteri yazmak için böyle 2 tane konur


Örnek verirsek:

package main
import ("fmt")

func main() {
  var i = 15.5
  var txt = "Merhaba Dünya!"

  fmt.Printf("%v\n", i)
  fmt.Printf("%#v\n", i)
  fmt.Printf("%v%%\n", i)
  fmt.Printf("%T\n", i)

  fmt.Printf("%v\n", txt)
  fmt.Printf("%#v\n", txt)
  fmt.Printf("%T\n", txt)
}

çıktı şöyle olacaktır:

15.5
15.5
15.5%
float64
Merhaba Dünya!
"Merhaba Dünya!"
string


Go Tamsayı Formatlama Terimleri

Aşağıdaki ifadeler tamsayı (int) veri tiplerinde Printf() fonksiyonu ile kullanılabilir:

TerimAçıklaması
%b2'li sistem (binary)
%d10'luk sistem
%+d10'luk sistem ve her zaman işareti yazılır
%o8'li sistem (octal)
%O8'li sistem ve öncesinde "0o" yazısı ile
%x16'lı sistem ve küçük harf (hexadecimal)
%X16'lı sistem ve büyük harf
%#X16'lı sistem ve öncesinde "0x" yazısı ile
%4dDeğer 4 karakter genişliğinde yazılır, rakam olmayan yer boşluk bırakılır (sağa hizalanmış)
%-4dDeğer 4 karakter genişliğinde yazılır, rakam olmayan yer boşluk bırakılır (sola hizalanmış)
%04dDeğer 4 karakter genişliğinde yazılır, rakam olmayan yere solda sıfırlar yazılır

Örnek Verelim.

package main
import ("fmt")

func main() {
  var i = 15
 
  fmt.Printf("%b\n", i)
  fmt.Printf("%d\n", i)
  fmt.Printf("%+d\n", i)
  fmt.Printf("%o\n", i)
  fmt.Printf("%O\n", i)
  fmt.Printf("%x\n", i)
  fmt.Printf("%X\n", i)
  fmt.Printf("%#x\n", i)
  fmt.Printf("%4d\n", i)
  fmt.Printf("%-4d\n", i)
  fmt.Printf("%04d\n", i)
}

ve çıktısı:

1111
15
+15
17
0o17
f
F
0xf
  15
15
0015


Go String Formatlama Terimleri

Aşağıdaki ifadeler string veri tipleri yazdırılırken kullanılabilir.

TerimAçıklaması
%sDeğeri düz yazı olarak yazar
%qDeğeri çift tırnak içinde yazar
%8sDeğeri 8 karakter genişliğinde yazar ve sağa hizalar
%-8sDeğeri 8 karakter genişliğinde yazar ve sola hizalar
%xDeğerdeki karakterlerin hex kodlarını yazar
% xDeğerdeki karakterlerin hex kodlarını aralarda boşluk ile yazar

Örnek verelim:

package main
import ("fmt")

func main() {
  var txt = "Merhaba"
 
  fmt.Printf("%s\n", txt)
  fmt.Printf("%q\n", txt)
  fmt.Printf("%8s\n", txt)
  fmt.Printf("%-8s\n", txt)
  fmt.Printf("%x\n", txt)
  fmt.Printf("% x\n", txt)
}

ve çıktısı:

Merhaba
"Merhaba"
 Merhaba
Merhaba
4d657268616261
4d 65 72 68 61 62 61


Go Boolean Formatlama Terimleri

Aşağıdaki ifade boolean değerleri göstermek için kullanılır.

TerimAçıklaması
%tDeğeri true ya da false olarak yazar (%v kullanımı gibidir)

Örnek.

package main
import ("fmt")

func main() {
  var i = true
  var j = false

  fmt.Printf("%t\n", i)
  fmt.Printf("%t\n", j)
}

ve sonuç:

true
false


Go Kayan Noktalı Sayı (Float) Format Terimleri

Float veri tipiyle aşağıdaki formatlama terimleri kullanılabilir.

TerimAçıklaması
%eBilimsel logaritmik ifade. 10 üzeri şeklinde ifade
%fDesimal noktalı standart ifade (10 üzeri yok)
%.2fDesimal noktalı standart ifade ve virgülden sonra 2 basamak gösterilir
%6.2f6 karakter genişliğinde ve virgülden sonra 2 basamak gösterilir
%gDesimal noktalı standart ifade ve gerekirse 10 üzeri bilgisi

Örnek verelim.

package main
import ("fmt")

func main() {
  var i = 3.141

  fmt.Printf("%e\n", i)
  fmt.Printf("%f\n", i)
  fmt.Printf("%.2f\n", i)
  fmt.Printf("%6.2f\n", i)
  fmt.Printf("%g\n", i)
}

ve çıktısı:

3.141000e+00
3.141000
3.14
  3.14
3.141




Go Veri Tipleri

Bir programlama dilinde veri tipleri önemli konulardan biridir. Veri tipi bir değişkenin değer boyutunu ve tipini belirler. 

Go dili static typed bir dildir, yani bir değişkeni bir veri tipinde belirttikten sonra artık onun veri tipini değiştiremez , içine başka tipte bir değer yazamazsınız. 

Go'da 3 tane basit veri tipi vardır:

  • bool : Boolean bir değer ifade eder true ya da false olabilir (doğru ya da yanlış)
  • Sayısal : Bunlar tamsayı, kayan noktalı sayı ya da komplex sayılar olabilir
  • string : Yazı içeren veri tipidir

Aşağıdaki örnek Go dilindeki çeşitli veri tiplerini gösteriyor.

package main
import ("fmt")

func main() {
  var a bool = true     // Boolean
  var b int = 5         // Integer
  var c float32 = 3.14  // Floating point
  var d string = "Hi!"  // String

  fmt.Println("Boolean: ", a)
  fmt.Println("Integer: ", b)
  fmt.Println("Float:   ", c)
  fmt.Println("String:  ", d)
}


Go Boolean Veri Tipi

Bir boolean veri tipi değişken bool kelimesi ile ifade edilir ve sadece 2 değeri olabilir - true ya da false.

Boolean veri tipleri için default değer false'dur.

Örnekte değişik boolean veri tipi tanımlama teknikleri görülüyor.

package main
import ("fmt")

func main() {
  var b1 bool = true    // tip ve ilk değer verilmiş
  var b2 = true         // tip verilmemiş ama değer verilmiş
  var b3 bool           // tip verilmiş ama değer verilmemiş
  b4 := true            // tip verilmemiş ama değer verilmiş

  fmt.Println(b1) //  true
  fmt.Println(b2) //  true
  fmt.Println(b3) //  false
  fmt.Println(b4) //  true
}

Boolean değerleri ileride karşılaştırmalar bölümünde oldukça çok kullanacağız.


Go Integer /Tamsayı) Veri Tipi

Integer veri tipleri virgülden sonrası olmayan tamsayıları saklamak için kullanılır. Örneğin  35, -50, veya 1345000.

Tamsayı veri tiplerinin iki kategorisi vardır:

  • İşaretli Tamsayılar (signed integer) : Pozitif veya negatif değer içerebilirler
  • İşaretsiz Tamsayılar (unsigned) : Sıfır ve pozitif değerler olabilir (yani negatif olmayan)

İpucu : Tamsayı için default tip int'dir, tip belirtmeden değer verince sistem onu int olarak alır.


Go İşaretli Tamsayılar

İşaretli tamsayılar birçok int kelimelerinden biri ile ifade edilir ve hem pozitif hem negatif (ki bizim için tamsayı zaten budur, pozitiflere "sayma sayısı" deriz) değer alabilir.

Örnek olarak:

package main
import ("fmt")

func main() {
  var x int = 500
  var y int = -4500
    fmt.Printf("Tipi: %T, değeri: %v\n", x, x)
    fmt.Printf("Tipi: %T, değeri: %v\n", y, y)
}

ve çıktısı:

Tipi: int, değeri: 500
Tipi: int, değeri: -4500


Tamsayılar için Go dilinde 5 değişik int kelimesi kullanılır.

TipBoyutSınırları
int Platforma bağlı 32 bit sistemde 32 bit , 64 bit sistemde 64 bit uzunluk 32 bit sistemlerde -2147483648 ila 2147483647 ve 64 bit sistemlerde -9223372036854775808 ila 9223372036854775807
int8 8 bit / 1 bayt -128 ila +127
int16 16 bit / 2 bayt -32768 ila +32767
int32 32 bit / 4 bayt -2147483648 ila 2147483647
int64 64 bit / 8 bayt -9223372036854775808 ila 9223372036854775807


Go İşaretsiz Tamsayılar

İşaretsiz tamsayılar birçok uint kelimelerinden biri ile ifade edilir ve negatif olmayan değerleri alabilir.

Örnek olarak:

package main
import ("fmt")

func main() {
  var x uint = 500
  var y uint = 4500
  fmt.Printf("Tipi: %T, değeri: %v\n", x, x)
  fmt.Printf("Tipi: %T, değeri: %v\n", y, y)
}

ve sonuç :

Tipi: uint, değeri: 500
Tipi: uint, değeri: 4500


İşaretsiz tamsayılar için Go dilinde 5 değişik uint kelimesi kullanılır.

TipBoyutSınırları
uint Platforma bağlı 32 bit sistemde 32 bit , 64 bit sistemde 64 bit uzunluk 32 bit sistemlerde 0 ila 4294967295 ve 64 bit sistemlerde 0 to 18446744073709551615
uint8 8 bit / 1 bayt 0 ila 255
uint16 16 bit / 2 bayt 0 ila 65535
uint32 32 bit / 4 bayt 0 ila 4294967295
uint64 64 bit / 8 bayt 0 ila 18446744073709551615


Hangi Tamsayı Tipini Kullanmalı?

Değişkene hangi tamsayı tipini seçeceğiniz içinde saklaması olası değerlere göre yapılmalıdır.

Örneğin aşağıdaki kod hata verir çünkü 1000 değeri int8 tipinin sınırlarından dışarıdadır.

package main
import ("fmt")

func main() {
  var x int8 = 1000
  fmt.Printf("Type: %T, value: %v", x, x)
}

Çalıştırınca şu hata mesajı verilir:

./output.go:8:15: cannot use 1000 (untyped int constant) as int8 value
in variable declaration (overflows)


Go Float (Kayan Noktalı) Veri Tipleri

Go dilinde float veri tipleri noktalı sayıları tanımlamak için kullanılır, örneğin 35.3, -2.34, or 3597.34987 gibi. 

Bu arada kısa bir not, hemen tüm programlama dillerinde desimal sayılar noktalı olarak yazılır. Türkçe'de bunu virgüllü kullanan ve uluslar arası standartlara da virgül ile ayrılır diye bildiren kim varsa rabbim bildiği gibi yapsın, her programlama dilinde illa başımıza bela olur, bu nokta-virgül meselesi..

float veri tipinin 2 tane ifadesi vardır.

TipBoyutSınırları
float32 32 bit -3.4e+38 ila 3.4e+38
float64 64 bit -1.7e+308 ila +1.7e+308

Not: Eğer tip belirtmezseniz default olarak sistem float64 kabul eder.


Go float32 Kelimesi

Aşağıdaki örnekte float32 veri tipinde tanımlamalar görülüyor.

package main
import ("fmt")

func main() {
  var x float32 = 123.78
  var y float32 = 3.4e+38
  fmt.Printf("Tipi: %T, değeri: %v\n", x, x)
  fmt.Printf("Tipi: %T, değeri: %v\n", y, y)
}

ve çıktısı:

Tipi: float32, değeri: 123.78
Tipi: float32, değeri: 3.4e+38


Go float64 Kelimesi

float64 tipi ile float32'den daha büyük değerleri saklayabilirsiniz.

Örnek:

package main
import ("fmt")

func main() {
  var x float64 = 1.7e+308
  fmt.Printf("Tipi: %T, değeri: %v\n", x, x)
}


Hangi float tipini Kullanmalı?

Tabii ki içinde saklanması olası değerlerin sınırına göre. Aşağıda hata verecek bir örnek var.

package main
import ("fmt")

func main() {
  var x float32= 3.4e+39
  fmt.Println(x)
}

ve hata mesajı:

./output.go:8:18: cannot use 3.4e+39 (untyped float constant)
as float32 value in variable declaration (overflows)


Go string Veri Tipi

string veri tipi karakter dizilerinden oluşan değerleri (yani yazıları) saklamaya yarar. String değerler girilirken çift tırnak içinde verilmelidir. Yoksa derleyici onları değişken adı ya da bir komut olarak bulmaya çalışacak, bulamazsa hata verecek, bulursa saçma şeyler yapacaktır.

Örnek verelim.

package main
import ("fmt")

func main() {
  var txt1 string = "Merhaba!"
  var txt2 string
  txt3 := "Dünya 1"

  fmt.Printf("Tipi: %T, değeri: %v\n", txt1, txt1)
  fmt.Printf("Tipi: %T, değeri: %v\n", txt2, txt2)
  fmt.Printf("Tipi: %T, değeri: %v\n", txt3, txt3)
}

ve sonucu:

Tipi: string, değeri: Merhaba!
Tipi: string, değeri:
Tipi: string, değeri: Dünya 1




Go Array'ler (değerler grubu)

Array'ler aynı veri tipine sahip birçok değeri her biri için ayrı değişken tanımlamak yerine bir tek değişken içinde saklamak için kullanılır. 

Go Array Tanımlamak

Go dilinde bir array tanımlamanın 2 ayrı yöntemi vardır. 

1 - var kelimesi kullanarak

var array_ismi = [uzunluk]veritipi{değerler} // burada uzunluk verilmiş

veya

var array_ismi = [...]veritipi{değerler} // uzunluk değerlerden bulunur

Uzunluk (length) array içindeki değerlerin sayısıdır. Uzunluk girilmeyecekse köşeli parantez içine "..." (üç nokta yan yana) yazılır. Her iki şekilde de değerler yazıldığına göre uzunluk girmeye gerek yok bence.

2 - := işareti kullanarak

array_ismi := [uzunluk]veritipi{değerler} // burada uzunluk verilmiş

veya

array_ismi := [...]veritipi{değerler} // uzunluk değerlerden bulunur

Not : uzunluk array içinde saklanan değerlerin sayısıdır. Go dilinde array'ler sabit uzunluktadır, belirlenen uzunluk sonradan değiştirilemez.

Bir örnek verelim.

package main
import ("fmt")

func main() {
  var arr1 = [3]int{1,2,3}
  arr2 := [5]int{4,5,6,7,8}

  fmt.Println(arr1)
  fmt.Println(arr2)
}

Çıktımız şöyle olacaktır:

[1 2 3]
[4 5 6 7 8]


Uzunluk değeri verilmeden bir örnek.

package main
import ("fmt")

func main() {
  var arr1 = [...]int{1,2,3}
  arr2 := [...]int{4,5,6,7,8}

  fmt.Println(arr1)
  fmt.Println(arr2)
}

ve çıktısı :

[1 2 3]
[4 5 6 7 8]


Bu örnekte de string değerlerden oluşan bir array kullanılıyor.

package main
import ("fmt")

func main() {
  var cars = [4]string{"Volvo", "BMW", "Ford", "Mazda"}
  fmt.Println(cars)
}

ve çıktısı :

[Volvo BMW Ford Mazda]


Go Array Elemanlarına Ulaşmak

Bir array'in elemanlarına (yani içinde saklanan değerlere) index değeri kullanarak erişilir. 

Go dilinde (ve bir çok diğer programlama dillerinde) index değeri sıfırdan başlar. Yani ilk eleman [0] index değeri ile ikinci ise [1] index değeri ile seçilir , sırayla devam eder. 

Aşağıdaki örnekte fiyatlar içinden birinci ve üçüncü değere (elemana) erişiliyor.

package main
import ("fmt")

func main() {
  fiyatlar := [3]int{10,20,30}

  fmt.Println(fiyatlar[0])
  fmt.Println(fiyatlar[2])
}

 ve sonuç :

10
30

Array ismi yanında bitişik olarak köşeli parantez içinde index numarası girilince o değer seçilmiş oluyor. Bu teknik hemen tüm dillerde kullanılır. 


Bir Array Elemanını Değiştirmek

Yine index kullanarak bir array elemanı değerini de değiştirebiliriz. 

Bu örnekte fiyatlar dizisinin üçüncü elemanı değiştiriliyor.

package main
import ("fmt")

func main() {
  fiyatlar := [3]int{10,20,30}

  fiyatlar[2] = 50
  fmt.Println(fiyatlar)
}

ve sonuç :

[10 20 50]


Go Array İlk Değerleri Vermek

Eğer array tanımlanırken değerler verilmezse (ki böyle yapmak da olasıdır) verilen veri tipine göre default değerler elemanlara otomatik olarak verilir.

İpucu : int tipi için default değer 0, string değeri için default değer "" dir.

Örnek verelim.

package main
import ("fmt")

func main() {
  arr1 := [5]int{}    //değerler verilmemiş
  arr2 := [5]int{1,2} //bir kısmı verilmiş
  arr3 := [5]int{1,2,3,4,5} //tamamı verilmiş

  fmt.Println(arr1)
  fmt.Println(arr2)
  fmt.Println(arr3)
}

Sonuçta verilmeyen değerler sıfır olarak gelir.

[0 0 0 0 0]
[1 2 0 0 0]
[1 2 3 4 5]


Sadece Belirli Elemanlar İlk Değer Vermek

Bir array içinde sadece istediğimiz elemanlara ilk değer vermek de mümkündür. 

Aşağıdaki örnek sadece ikinci ve üçüncü elemanlara ilk değer veriyor.

package main
import ("fmt")

func main() {
  arr1 := [5]int{1:10,2:40}

  fmt.Println(arr1)
}

Sonuç :

[0 10 40 0 0]

Yukarıdaki array 5 elemana sahip olarak tanımlanmış.

  • 1:10 anlamı : 10 değerini 1 indexli elemana (ikinci eleman) verir
  • 2:40 anlamı : 40 değerini 2 indexli elemana (üçüncü eleman) verir

 

Go Array Uzunluğunun Bulunması

len() fonksiyonu bir array'in uzunluğunu bilmek için kullanılır. 

Örnek :

package main
import ("fmt")

func main() {
  arr1 := [4]string{"Volvo", "BMW", "Ford", "Mazda"}
  arr2 := [...]int{1,2,3,4,5,6}

  fmt.Println(len(arr1))
  fmt.Println(len(arr2))
}

ve sonuç :

4
6

len() metoduna parametre olarak uzunluğunu öğrenmek istediğimiz array adını yazarız, dönen değeri amacımıza göre kullanırız.



Go Slice Tipi

Slice'lar array'lere benzer ancak daha güçlü ve esnektirler.

Array gibi slice da aynı veri tipinden bir çok değeri tek değişkende saklamak için kullanılır.

Bununla beraber , array'den farklı olarak , slice uzunluğu büyüyebilir ya da küçülebilir.

Go dilinde slice tanımlamak için çeşitli yöntemler kullanılır.

  • []veritipi{değerler} yapısı kullanarak
  • Bir array'den slice'a dönüştürerek
  • make() fonksiyonunu kullanarak


Go'da []veritipi{değerler} yapısı ile sclice tanımlamak

Deyim yapısı :

slice_ismi := []veritipi{değerler}

Genel kullanımına bir örnek :

slice1 := []int{}

Yukarıdaki kod, uzunluğu 0 ve kapasitesi 0 olan boş bir tamsayı slice tanımlar.

Değerler vererek tanımlamak istersek :

slice1 := []int{1,2,3}

Burada da uzunluğu ve kapasitesi 3 olan bir tamsayı slice tanımlanmış.

Go dilinde bir slice'ın uzunluğu ve kapasitesini bulmak için iki fonksiyon kullanılır.

  • len() fonksiyonu - Aynı array gibi slice'ın uzunluğunu (içindeki eleman sayısını) verir
  • cap() fonksiyonu - Slice'ın kapasitesini (genişleyebileceği veya küçülebileceği eleman sayısı) verir

Aşağıdaki örneklerde []veritipi{değerler} yapısı kullanarak yapılan slice tanımlamaları görülüyor.

package main
import ("fmt")

func main() {
  slice1 := []int{}
  fmt.Println(len(slice1))
  fmt.Println(cap(slice1))
  fmt.Println(slice1)

  slice2 := []string{"Go", "Slices", "Are", "Powerful"}
  fmt.Println(len(slice2))
  fmt.Println(cap(slice2))
  fmt.Println(slice2)
}

sonuç :

> go run ./slices.go
0
0
[]
4
4
[Go Slices Are Powerful]

Örnekte slice1 için değerler verilmemiş , yani hem uzunluk hem de kapasite sıfır oluyor. slice2'de ise eleman değerleri girilmiş, böylece uzunluk ve kapasite verilen değer sayısı kadar yani 4 oluyor.


Go bir Array'den Slice Tanımlamak

Daha önce verilmiş bir array değişkeni kullanarak da slice tanımlayabilirsiniz. 

Deyim yapısı şöyle :

    var array_ismi = [uzunluk]veritipi{değerler} // Bir array
    slice_ismi := array_ismi[ilk:son] // Array'den yapılmış bir slice

Aşağıdaki örnek bir array'den slice tanımlamasını gösteriyor.

package main
import ("fmt")

func main() {
  arr1 := [6]int{10, 11, 12, 13, 14,15}
  slice1 := arr1[2:4]

  fmt.Printf("slice1 = %v\n", slice1)
  fmt.Printf("uzunluk = %d\n", len(slice1))
  fmt.Printf("kapasite = %d\n", cap(slice1))
}

ve çıktısı :

slice1 = [12 13]
uzunluk = 2
kapasite = 4

Örnekte slice1 değişkeni uzunluğu 6 olan arr1 array'inden üretiliyor. 

[2:4] değerleri bize 2 indexli elemandan (yani 12 değerli olan) başlayarak 4 indexli elemana kadar değerlerin alınacağını belirtiyor. Önemli nokta 2 indexli eleman alınırken 4 indexli alınmıyor dikkat ediniz. Sadece 2 ve 3 indexliler alınıyor. Sonuçta elde edilen slice kapasitesi ise array'in 2 indexli elemanından sonuna kadar olan eleman sayısı yani 4 oluyor. 

Eğer slice index 1'den itibaren başlasa kapasite 5, eğer index 0'dan başlasa kapasite 6 olacaktı. 

Bu kapasite işi biraz karışık örneklerle göstermeden anlaması zor. İlk önce şunu söyleyelim biz aslında array'den dönüştürürken 2 indexliden itibaren hepsini aldık ama sadece 2 elemanla uzunluğu sınırladık.

Örneğin şöyle yapalım :

package main

import ("fmt")

func main() {
    arr1 := [6]int{10, 11, 12, 13, 14, 15}
    slice1 := arr1[2:4]

    fmt.Printf("slice1 = %v\n", slice1)
    fmt.Printf("uzunluk = %d\n", len(slice1))
    fmt.Printf("kapasite = %d\n", cap(slice1))
   
    slice1 = slice1[:4] //uzunluğu değiştirelim
    fmt.Printf("uzunluk = %d\n", len(slice1))
    fmt.Printf("kapasite = %d\n", cap(slice1))
    fmt.Printf("slice1 = %v\n", slice1)
}

çıktısı :

slice1 = [12 13]
uzunluk = 2
kapasite = 4
uzunluk = 4
kapasite = 4
slice1 = [12 13 14 15]

Ooo bizim değerler hala orada duruyormuş sadece uzunluk değerini değiştirince tüm değerler geldi. 

Go dilinin kendi sitesinde şöyle bir örnek de verilmiş.

package main

import "fmt"

func main() {
    s := []int{2, 3, 5, 7, 11, 13}
    fmt.Println(s)

    // Uzunluğu sıfır yap.
    s = s[:0]
    fmt.Println(s)

    // Uzunluğu 4 yap.
    s = s[:4]
    fmt.Println(s)

    // ilk 2 değeri sil.
    s = s[2:]
    fmt.Println(s)
}

ve sonuç :

[2 3 5 7 11 13]
[]
[2 3 5 7]
[5 7]

Sanırım biraz daha netleşti.


make() Fonksiyonu ile Slice Tanımlamak

make() fonksiyonu yardımıyla da bir slice tanımlanabilir.

Deyim yapısı :

    slice_ismi := make([]tip, uzunluk, kapasite)

Not : Eğer kapasite parametresi girilmezse uzunluk ile eşit yapılır.


Örnekte make() fonksiyonu ile slice tanımlamaları görülüyor.

package main

import "fmt"

func main() {
    slice1 := make([]int, 5, 10)
    fmt.Printf("slice1 = %v\n", slice1)
    fmt.Printf("uzunluk = %d\n", len(slice1))
    fmt.Printf("kapasite = %d\n", cap(slice1))

    // kapasite verilmeden
    slice2 := make([]int, 5)
    fmt.Printf("slice2 = %v\n", slice2)
    fmt.Printf("uzunluk = %d\n", len(slice2))
    fmt.Printf("kapasite = %d\n", cap(slice2))
}

sonuç :

slice1 = [0 0 0 0 0]
uzunluk = 5
kapasite = 10
slice2 = [0 0 0 0 0]
uzunluk = 5
kapasite = 5


Go Slice Elemanlarına Erişmek

Aynı array gibi slice elemanlarına da index değeri kullanarak erişilir. 

Go dilinde index değerleri sıfırdan başlar. Yani [0] ilk elemanı belirtir [1] ikinci elemanı vs.

 Örnekte fiyatlar slice değişkeninin ilk ve üçüncü elemanlarına erişiliyor.

package main

import "fmt"

func main() {
    fiyatlar := []int{10, 20, 30}

    fmt.Println(fiyatlar[0])
    fmt.Println(fiyatlar[2])
}

ve sonuç :

10
30


Go Slice Elemanı Değerini Değiştirmek

Aynı şekilde index kullanarak bir slice elemanı değerini değiştirebiliriz.

Örnekte slice'ın üçüncü elemanının değeri değiştiriliyor.

package main

import "fmt"

func main() {
    fiyatlar := []int{10, 20, 30}
    fmt.Println(fiyatlar)
    fiyatlar[2] = 50
    fmt.Println(fiyatlar)
}

ve çıktısı :

[10 20 30]
[10 20 50]


Go Bir Slice'a Eleman Eklemek

Bir slice'a eleman eklemek için append() fonksiyonu kullanılır.

Deyim yapısı :

    slice_ismi = append(slice_ismi, eleman1, eleman2, ...)

Örnek :

package main

import "fmt"

func main() {
    s1 := []int{1, 2, 3, 4, 5, 6}
    fmt.Printf("s1 = %v\n", s1)
    fmt.Printf("uzunluk = %d\n", len(s1))
    fmt.Printf("kapasite = %d\n", cap(s1))

    s1 = append(s1, 20, 21)
    fmt.Printf("s1 = %v\n", s1)
    fmt.Printf("uzunluk = %d\n", len(s1))
    fmt.Printf("kapasite = %d\n", cap(s1))
}

ve sonuç :

s1 = [1 2 3 4 5 6]
uzunluk = 6
kapasite = 6
s1 = [1 2 3 4 5 6 20 21]
uzunluk = 8
kapasite = 12

Burada kapasite 2 tane slice birleşmiş gibi artıyor. 6 elemanlı slice'a 2 tane değer ekleniyor ama kapasite 6'nın iki katı olan 12 oluyor. Peki, 8 tane değer eklesek? İşler tam çorba oluyor.

Biraz inceledim, slice'lar aslında hafızada kapasite kadar bir yer işgal ediyor ama uzunluk kadarına erişilebiliyor. Bir array'den slice üretirken de sadece array hafızasının bir kısmını gösteriyor, yani slice ürettikten sonra array elemanını değiştirirseniz bundan üretilen slice elemanı da değişiyor. 

Alın size daha kafa karıştıran örnek.

package main

import "fmt"

func main() {
    a1 := [...]int{1, 2, 3, 4, 5, 6}
    s1 := a1[1:3]
    fmt.Printf("s1 = %v\n", s1)
    fmt.Printf("uzunluk = %d\n", len(s1))
    fmt.Printf("kapasite = %d\n", cap(s1))

    a1[1] = 3
    a1[4] = 77
    fmt.Printf("a1 = %v\n", a1)
    fmt.Printf("s1 = %v\n", s1)
    s1 = s1[:5]
    fmt.Printf("s1 = %v\n", s1)
}

ve çıktısı :

s1 = [2 3]
uzunluk = 2
kapasite = 5
a1 = [1 3 3 4 77 6]
s1 = [3 3]
s1 = [3 3 4 77 6]

Sindire sindire inceleyin hala array ile aynı hafıza bölgesini kullanıyor. 

Neyse , geçelim..


Bir slice'ı Diğerine Eklemek

append() fonksiyonunun bir başka kullanımı da iki slice değişkeni birleştirmek.

Deyim yapısı :

    slice3 = append(slice1, slice2...)

Not : ikinci slice adının arkasına bitişik yazılan üç nokta (...) slice'ları birleştirirken yazılması zorunlu bir notasyondur. Herhalde derleyiciye slice'ları birleştiriyorsun değer eklemesi yapmıyorsun demek için.

Örnek :

package main

import "fmt"

func main() {
    s1 := []int{1, 2, 3}
    s2 := []int{4, 5, 6}
    s3 := append(s1, s2...)

    fmt.Printf("s3=%v\n", s3)
    fmt.Printf("uzunluk=%d\n", len(s3))
    fmt.Printf("kapasite=%d\n", cap(s3))
}

ve çıktısı :

s3=[1 2 3 4 5 6]
uzunluk=6
kapasite=6

O 3 noktayı oraya yazmazsanız derleyici hata mesajı verecektir.


Go bir sliceîn Uzunluğunu Değiştirmek

Bunu daha önce bir örnekte yapmıştık. Array'lerin aksine slice'ların uzunlukları değişebilir. 

Örneğimizde slice uzunluklarının değiştirilmesi gösteriliyor.

package main

import "fmt"

func main() {
    a1 := [6]int{9, 10, 11, 12, 13, 14} // bir array
    s1 := a1[1:5]                       // Slice array
    fmt.Printf("s1 = %v\n", s1)
    fmt.Printf("uzunluk = %d\n", len(s1))
    fmt.Printf("kapasite = %d\n", cap(s1))

    s1 = a1[1:3] // array'den tekrar slice ederek değişim
    fmt.Printf("s1 = %v\n", s1)
    fmt.Printf("uzunluk = %d\n", len(s1))
    fmt.Printf("kapasite = %d\n", cap(s1))

    s1 = append(s1, 20, 21, 22, 23) // append ile değişim
    fmt.Printf("s1 = %v\n", s1)
    fmt.Printf("uzunluk = %d\n", len(s1))
    fmt.Printf("kapasite = %d\n", cap(s1))
}

sonuç :

s1 = [10 11 12 13]
uzunluk = 4
kapasite = 5
s1 = [10 11]
uzunluk = 2
kapasite = 5
s1 = [10 11 20 21 22 23]
uzunluk = 6
kapasite = 10


Hafıza Verimliliği

Slice kullanırken daha önce incelerken fark ettiğimiz gibi Go dili tüm kapasite içindeki elemanları hafızada saklıyor. 

Eğer slice değişkeni elde ettiğiniz array çok büyük ama alacağınız birkaç değerse copy() fonksiyonunu kullanmanız hafıza kullanımı için daha verimli olacaktır. 

copy() fonksiyonu sadece gereken elemanlardan oluşan bir alt array oluşturarak ondaki değerleri kopyalar.

Deyim yapısı :

    copy(hedef, kaynak)

copy() fonksiyonu parametrelerinde hedef ve kaynak adı verilen iki slice alır ve kaynak içindeki elemanları hedef içine kopyalar.

Örnek :

package main

import "fmt"

func main() {
    sayılar := []int{1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15}
    // orjinal slice
    fmt.Println("// Orjinal")
    fmt.Printf("sayılar = %v\n", sayılar)
    fmt.Printf("uzunluk = %d\n", len(sayılar))
    fmt.Printf("kapasite = %d\n", cap(sayılar))

    // sadece gerekli sayılardan kopya oluştur
    gerekenler := sayılar[:len(sayılar)-10]
    kopyalananlar := make([]int, len(gerekenler))
    copy(kopyalananlar, gerekenler)

    fmt.Println("// Yeni slice")
    fmt.Printf("kopyalananlar = %v\n", kopyalananlar)
    fmt.Printf("uzunluk = %d\n", len(kopyalananlar))
    fmt.Printf("kapasite = %d\n", cap(kopyalananlar))
}

ve sonuç :

// Orjinal
sayılar = [1 2 3 4 5 6 7 8 9 10 11 12 13 14 15]
uzunluk = 15
kapasite = 15
// Yeni slice
kopyalananlar = [1 2 3 4 5]
uzunluk = 5
kapasite = 5

copy() fonksiyonu ile ayrıca yeni üretilen slice ile orjinal arasında değer bağlantısı da kesilir. Örneğin ilk slice değerlerinden birini değiştirelim.

package main

import "fmt"

func main() {
    sayılar := []int{1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15}
    // orjinal slice
    fmt.Println("// Orjinal")
    fmt.Printf("sayılar = %v\n", sayılar)
    fmt.Printf("uzunluk = %d\n", len(sayılar))
    fmt.Printf("kapasite = %d\n", cap(sayılar))

    // sadece gerekli sayılardan kopya oluştur
    gerekenler := sayılar[:len(sayılar)-10]
    kopyalananlar := make([]int, len(gerekenler))
    copy(kopyalananlar, gerekenler)

    fmt.Println("// Yeni slice")
    fmt.Printf("kopyalananlar = %v\n", kopyalananlar)
    fmt.Printf("uzunluk = %d\n", len(kopyalananlar))
    fmt.Printf("kapasite = %d\n", cap(kopyalananlar))

    fmt.Println("// orjinali değiştir")
    sayılar[0] = 111
    fmt.Printf("sayılar = %v\n", sayılar)
    fmt.Printf("gerekenler = %v\n", gerekenler)
    fmt.Printf("kopyalananlar = %v\n", kopyalananlar)
}

çıktısı :

// Orjinal
sayılar = [1 2 3 4 5 6 7 8 9 10 11 12 13 14 15]
uzunluk = 15
kapasite = 15
// Yeni slice
kopyalananlar = [1 2 3 4 5]
uzunluk = 5
kapasite = 5
// orjinali değiştir
sayılar = [111 2 3 4 5 6 7 8 9 10 11 12 13 14 15]
gerekenler = [111 2 3 4 5]
kopyalananlar = [1 2 3 4 5]

Gördüğümüz gibi sayılar elemanı değeri değişince gerekenler elemanı da beraberinde değişirken kopyalananlar elemanı değeri değişmiyor. O artık ilk slice'dan başka bir hafıza bölgesini gösteriyor.


Yazı çık uzadı , devam etmeden önce bu kadarını bir yayınlayalım sonra devam ederiz. Kalın sağlıcakla..















Hiç yorum yok:

Yorum Gönder