Bu bölümde değişken ve sabit değişken tanımlama öğreneceğiz, aynı zamanda Go programala yeteneklerimizi geliştireceğiz.
Go'da değişken tanımlamanın birden fazla yolu bulunmakta.
var
anahtar kelimesi en basit anlamda değişken tanımlamak için kullanılır, Go'da değişken tipinin değişken isminden sonra yazıldığına dikkat edin.
// "variableName" adında "type" tipinde bir değişken tanımlayalım
var variableName type
Birden fazla değişken tanımlama.
// tipi "type" olan 3 değişken tanımlayalım
var vname1, vname2, vname3 type
Öntanımlı değeri olan bir değişken tanımlayalım.
// “variableName” adında, tipi"type" olan ve değeri "value" olan bir değişken tanımlayalım
var variableName type = value
Öntanımlı değeri olan birden fazla değişken tanımlayalım.
//tipi "type" olan ve öntanımlı değerleri sahip üç değişken tanımlayalım.
var vname1, vname2, vname3 type = v1, v2, v3
Yukarıdaki gibi değişken tanımlamak sıkıcı mı geldi? Üzülmeyin, Go ekibide bunu bir problem olarak görüyor. Bu yüzden dolayı değişken tanımlarken değişken tipini ihmal edebiliyoruz. Yani kısaca kod aşağıdaki gibi bir hal alıyor:
//tipi "type" olan ve öntanımlı değerleri sahip üç değişken tanımlayalım.
var vname1, vname2, vname3 = v1, v2, v3
Aslında bu çözümünde yeterince basit olmadığını biliyorum. Bakalım daha neler yapabiliriz.
//tipi "type" olan ve öntanımlı değerleri sahip üç değişken tanımlayalım.
vname1, vname2, vname3 := v1, v2, v3
Şimdi daha iyi gibi.:=
kullanarak var
ve type
ihmal edilebiliyor, buna öz ifade deniyor. Ama bir dakika, tek bir şart var: bu yöntemi sadece fonksiyon içinde kullanabilirsiniz. Fonksiyon gövdesi dışında kullanırsanız derleme hatası alırsnız. Bundan dolayı, global değişkenler için var
kullanıyoruz ve kısa tanımalar içinde var()
anahtar kelimesini.
_
(boş) özel bir değişken ismi. Verilen her değer yoksayılır. Örneğin, 35
değerini b
'ye' atıyoruz ve34
değerini yoksayıyoruz.( Bu örnek sadece nasıl çalıştığını gösteriyor. Genelde fonksiyonlardan dönen değerleri yoksaymak için kullanıyoruz )
_, b := 34, 35
Eğer programınızda kullanmadığınız değişkenler varsa, derleme esnasında hata alırsnız. Aşağıdaki kodu derlemeyi deneyin.
package main
func main() {
var i int
}
Sabitler, derleme zamanında belirlenen ve çalışma esnasında da değeri değişmeyen değerlerdir. Go'da integer, string ve boolean değerleri sabit olarak tanımlayabilirsiniz.
Sabitleri şu şekilde tanımlayabilirsiniz.
const constantName = value
// eğer gerekliyse ön tanımlı değerde verebilirsiniz
const Pi float32 = 3.1415926
Örnekler.
const Pi = 3.1415926
const i = 10000
const MaxThread = 10
const prefix = "astaxie_"
Go'da, boolean tipinde bir değişken tanımlamak için bool
anahtar kelimesini kullanıyoruz, değer sadece true
ya da false
olabilir, ve false
öntanımlı değerdir. ( Integer tipi ve boolean tipi arasında tip dönüşümü yapamazsınız! )
// örnek kod
var isActive bool // global değişken
var enabled, disabled = true, false // tipleri yoksayalım
func test() {
var available bool // yerel değişken
valid := false // kısa atama
available = true // değişkene değer atama
}
Integer tipi, işaretli ve işaretsiz olarak iki farklı şekilde kullanılabiliyor. Go'da int
ve uint
tipleri bulunmakta, ikisininde uzunlukları aynı, tabi bu uzunluk işletim sisteminize göre farklılık gösterebilir. Uzunlukları 32-bit'lik işletim sistemlerinde 32-bit, 64-bit'lik sitemlerde ise 64-bit boyutunda oluyor. Go, aynı zamanda özel boyutları sahip rune
, int8
, int16
, int32
, int64
, byte
, uint8
, uint16
, uint32
, uint64
tiplerinede sahiptir.rune
veri tipi int32
'e', byte
ise uint8
'e karşılık geliyor.
Bilmeniz gereken önemli bir konuda bu tipler arası atama yapılamıyor olması, aşağıdaki örnek derleme hatası verecektir.
var a int8
var b int32
c := a + b
Int uint8'den daha uzun olduğu halde, hatta int32 ile aynı boyutta olmasına rağmen, ikisi arasında bir atama yapmak mümkün değil. ( c değişkenin tipinin int
olduğu varsayılıyor )
Kayan noktalı sayılar için float32
ve float64
tipleri var, fakat float
adında bir tip yoktur... Eğer tip belirtmezseniz öntamımlı olarak float64 tipinde bir değşken oluşturmuş olursunuz.
Peki hepsi bu kadar mı ? Tabi ki hayır! Go kompleks sayılarıda destekliyor. complex128
(64-bit gerçel ve 64-bit sanal kısma sahip) öntanımlı tip, eğer daha küçük boyutta istiyorsanız, complex64
(32-bit gerçel ve 32-bit sanal kısma sahip). RE+IMi
formatında yazılabiliyor, RE
gerçel ve IM
'de sanal kısım olmak üzere, i
sanal kısmı belirtiyor. Örnek:
var c complex64 = 5+5i
//output: (5+5i)
fmt.Printf("Value is: %v", c)
Go'nun UTF-8'i desteklediğini daha önce bahsetmiştik. String'ler çift tırnak ""
ya da backtickler ``
gösterilebiliyor.
// örnek kod parçası
var frenchHello string // temel tanımala formu
var emptyString string = "" // boş string tanımlama
func test() {
no, yes, maybe := "no", "yes", "maybe" // kısa atama formu
japaneseHello := "Ohaiou"
frenchHello = "Bonjour" // temel değer atama formu
}
Index kullanarak stringlerin değerlerini değiştirmezsiniz. Aşağıdaki kod derleme esnasında hata verecektir.
var s string = "hello"
s[0] = 'c'
Eğer string'in içindeki bir karakteri değişitirmek istersem bunu nasıl yapacağım? Aşağıdaki kod işinizi görecektir.
s := "hello"
c := []byte(s) // string'i []byte tipine çevir
c[0] = 'c'
s2 := string(c) // tekrar string tipine çevir
fmt.Printf("%s\n", s2)
`+` operator to combine two strings.
+
operatörü ile iki string'i birleştirebilirsiniz.
s := "hello,"
m := " world"
a := s + m
fmt.Printf("%s\n", a)
aynı zamanda.
s := "hello"
s = "c" + s[1:] // indexle karakter değiştiremezsiniz, ama değerleri okuyabilirsiniz.
fmt.Printf("%s\n", s)
Ya string bir satıra sığmayacak kadar büyükse?
m := `hello
world`
`` ` hiç bir karakteri escape etmeyecektir.
Go error
adında, hata mesajlarını kontrol etmek için, bir veri tipine sahiptir. Bununla beraber hataları idate etmek için errors
adında bir pakete sahip.
err := errors.New("emit macho dwarf: elf header corrupted")
if err != nil {
fmt.Print(err)
}
Aşağıdaki resim Go veri yapısı adlı yazıdan, Russ Cox Blog'undan. Gördüğünüz üzere, Go verileri saklamak için bellek bloklarını kullanıyor.
Şekil 2.1 Go temel veri yapıları
Birden fazla tanımlama yapacaksanız grup formunu kullanabilirsiniz.
Temel form.
import "fmt"
import "os"
const i = 100
const pi = 3.1415
const prefix = "Go_"
var i int
var pi float32
var prefix string
Grup form.
import(
"fmt"
"os"
)
const(
i = 100
pi = 3.1415
prefix = "Go_"
)
var(
i int
pi float32
prefix string
)
iota
belirtimi yapmazsanız, sabit olarak tanımladığınız const()
grubundaki ilk değer 0
dan başlayacaktır. Eğer sabitlerin değerleri açıkça belirtilmemişse, hepsinin değeri sondaki ile aynı olacaktır. Eğer son değişkenin değeri iota
olarak verilmişse, önceki değişkenlerin değerleri iota
olarak atanmaycaktır.
Go'daki iota
anahtar kelimesi enum
oluşturmak için kullanılır, 0
ile başlar, 1
artarak gider.
const(
x = iota // x == 0
y = iota // y == 1
z = iota // z == 2
w // Sabit isminden sonra bir ifade yoksa, en son ifade öntanımlı olarak kullanlacaktır, yani w = iota şeklinde arkaplanda atanacaktır. Buyüzden w == 3 değerine sahip olacaktır.
)
const v = iota // `const` tekrar iota ile karşılaşırsa, tekrar `0` dan başlar, yani v = 0.
const (
e, f, g = iota, iota, iota // e=0,f=0,g=0 hepsi aynı satırda olduğu için.
)
Go'nun kısa ve öz olması, bir takım öntanımlı davranışlara sahip olmasından kaynaklanmakta.
- Değişken ismi büyük harfle başlarsa dışarı açılmış demektir, aksi takdirde private olur.
- Aynı kural fonksiyon ve sabit isimleri içinde geçerli, Go'da
public
ya daprivate
kelimesi yoktur.
array
anahtar kelimesi dizi oluşturmak için kullanılır:
var arr [n]tip
[n]tip
= > n
diznin uzunluğu, tip
ise tutacağı elemanların tipi. Diğer dillerde olduğu gibi,[]
kullanak değer okuma ya da set etme işlemi yapabiliriz.
var arr [10]int // int tipinde bir dizi
arr[0] = 42 // 0. elemanı 42
arr[1] = 13 // 1. elamanı
fmt.Printf("Dizinin ilk elemanı: %d\n", arr[0]) // 42 dönecektir
fmt.Printf("Son eleman %d\n", arr[9]) // 9. elamanın değerini döncektir,bu örnekte 0(öntanımlı Go veriyor bu değeri).
Boyutta bir dizinin tipini etkilediği için, [3]int
ve [4]int
farklı tiplerdir, yani dizilerin boyutları değiştirilemez. Eğer dizileri parametre olarak verirseniz, fonksiyonlara kopyalanarak geçerler(referans bazlı değil)! Eğer referans olarak vermek isterseniz, slice
veri tipini kullanabilirsiniz. Daha sonra ayrıntılı açıklanacaktır.
Dizi tanımlarken :=
operatörünü kullanabilirsiniz.
a := [3]int{1, 2, 3} // 3 elemantlı int tipin bir dizi
b := [10]int{1, 2, 3} // 10 elemanlı bir int dizisi, ilk 3 eleman atanmış. Geri kalan elemanların değeri öntanımlı 0.
c := [...]int{4, 5, 6} //`…` kullanarak dizinin uzunluğunu Go'ya hesaplatabilirsiniz.
Dizi tutan diziler tanımalamak isteyebilirsiniz. Bakalım nasıl yapılıyormuş:
// 2 elemantlı 2 boyutlu bir dizi, her elemanı 4 elaman tutuyor.
doubleArray := [2][4]int{[4]int{1, 2, 3, 4}, [4]int{5, 6, 7, 8}}
// Yukarıdaki atama kısa olarak bu şekildede yapılabilir.
easyArray := [2][4]int{{1, 2, 3, 4}, {5, 6, 7, 8}}
Array temel veri yapılarındandır.
Şekil 2.2 Çok boyutlu diziler
Bir çok durumda dizi kullanmak pek uygun olmayabilir, örneğin tutacağımız elemanların sayısı belli olamayabilir. Bu yüzden , "dinamik dizilere" ihtiyacımız var. Go'da bu tip dizilere slice
deniyor.
slice
tam olarak dinamik dizi
değil. Daha çok referans tipli bir veri yapısı. slice
aslında boyut gerektirmeyen array
dir. Arkaplanda bir array
'e işaret ederler.
// array tanımlar gibi, tek fark boyut bilgisi yok
var fslice []int
slice
tanımlayıp, ilklendiriyoruz.
slice := []byte {'a', 'b', 'c', 'd'}
slice
daha önce oluşturulmuş array
ya da slice
kullanılarak tanımlanabilir. slice
array[i:j]
formunu kullanarak bir diziden tanımlanabilir, i
başlangıç ve j
bitiş indeksi, ama array[j]
formunun bir slice
oluşturmayacağına dikkat edin.
// 10 elemanlı bir dizi tanımlayalım
var ar = [10]byte {'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j'}
// []byte tipinde iki tane slice tanımla
var a, b []byte
// 'a' ar dizisinin 2'den 5'e kadar olan kısmını içeriyor.
a = ar[2:5]
// 'a' ar[3],ar[4] ve ar[5] elemanlarına sahip
// 'b'de ar dizininden oluşan ikinci bir slice
b = ar[3:5]
// 'b' ar[4] ve ar[4] elemanlarını içeriyor
slice
ve array
tipinin farkına dikkat edin. Go'nun boyutu otomatik ayarlamsı için […]
, slice
tanımalamak için []
kullanıyoruz.
Arakaplandaki veri yapısı şeması.
Şekil 2.3 slice ve array
slice bir kaç pratik özelliğe sahip.
slice
ta indeksler 0 dan başlar,ar[:n]
=ar[0:n]
- İkinci parametrede
slice
'ın uzunluğu olacaktır,ar[n:]
=ar[n:len(ar)]
. ar[:]
array'i slice'a çevirir, ilk iki özellikten nedeni çıkarabilir.
slice
ile ilgili örnekler
// array tanımla
var array = [10]byte{'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j'}
// iki tane slice tanımla
var aSlice, bSlice []byte
// bazı pratik işlemler
aSlice = array[:3] // aSlice = array[0:3] aSlice a,b,c elemanlarına sahip
aSlice = array[5:] // aSlice = array[5:10] aSlice f,g,h,i,j elemanlarına sahip
aSlice = array[:] // aSlice = array[0:10] aSlice bütün elemanlara sahip
// slice'tan slice oluşturmak
aSlice = array[3:7] // aSlice d,e,f,g elemanlarına sahip,uzunluk=4,kapasite=7
bSlice = aSlice[1:3] // bSlice aSlice[1], aSlice[2] elemanlarını içeriyor, yani e,f
bSlice = aSlice[:3] // bSlice aSlice[0], aSlice[1], aSlice[2] elemanlarını içeriyor, yani d,e,f
bSlice = aSlice[0:5] // slice kapasite kadar genişleyebilir, bSlice d,e,f,g,h elemanlarını içeriyor
bSlice = aSlice[:] // bSlice aSlice'ın sahip olduğu elemanlara sahip, yani d,e,f,g
slice
bir referans tipi, bu yüzden yapılan değişiklikler işaret ettiği dizi ya da slice'ı da etkiler. Örneğin, yukarıdaki aSlice
ve bSlice
ele alalım, aSlice
daki elemanları değiştirirseniz, bSlice
'sındaki elemanlarda değişecektir.
slice
veri yapısıda struct
gibi 3 kısımdan oluşur.
-
slice
'ın başlagıcını göstern bir pointer'. -
slice
'ın uzunluğu'. -
Kapasite, baslangıç indeksiden
slice
'ın uzunluğuna kadar.Array_a := [10]byte{'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j'} Slice_a := Array_a[2:5]
Yukarıdaki kodun bellekte gösterilmesi aşağıdaki resimde görülebilir.
Şekil 2.4 Slice'ın dizi bilgisi
Slice için dilden gelen yerleşik metodlar mevcut:
len
,slice
'ın boyutunu verir'.cap
,slice
'ın kapasitesini verir.'append
,slice
'a bir ya da daha fazla eleman ekler ve slice`'ı döner .copy
, bir slice'ın içerğini diğer bir slice'a kopyalar ve kopyalanan elemanları döner.
Dikkat: append
, slice
'ın göstediği diziyi değiştirecektir ve aynı diziyi gösteren slice'larıda etkileyecektir. Eğer slice'ın kapasitesi yetmez ise ((cap-len) == 0
), append
slice için yeni bir dizi oluşturacaktır. Bu durumda eski diziyi gösteren slice'lar etkilenmeyecektir.
map
, Python'daki sözlük yapısının karşılığıdır.map[keyType]valueType
formunu kullanarak tanımlanabilir.
Örnek üzerinden gidelim. 'set' ve 'get' işlemleri slice
ile aynıdır, ancak slice
'ın indeksi sadece int
olabilir, map
'te ise bir çok tip olabilir: int
, string
ya da ne isterseniz. Karşılaştırma işlemleri için ==
ve !=
operatörleri kullanılabilir.
// anahtar tipi string, değer tipi int olan `map`'i' `make` ile oluştur.
var numbers map[string] int
// oluşturmak için başka bir yol
numbers := make(map[string]int)
numbers["one"] = 1 // atamalar
numbers["ten"] = 10
numbers["three"] = 3
fmt.Println("The third number is: ", numbers["three"]) // değerleri oku
// Çıktı: The third number is: 3
map kullanırken bilmeneniz gerekenler:
map
'ler sıralı değildir. Bir map'i her yazdırdığınızda farklı sonuçlar alırsınız.index
kullanarak değer okuyamazsınız,key
kullanmanız gerekir.map
'in sabit bir boyutu yoktur.slice
gibi referans tiplidir.len
fonskyionumap
içinde çalışır. map'tekikey
sayısını döner.map
'te değer değiştirmek oldukça kolaydır.numbers["one"]=11
formunu kullanarak atama yapılabilir.
key:val
formunu kullanarak map'in değerlerini ilkleyebilirsiniz, map
'in bir key
'in var olup olmadığını kontrol etmek için tümleşik fonksiyonlar mevcuttur.
delete
kullanarak istediğiniz elemanı silebilirsiniz.
// map'i ilklendir
rating := map[string]float32 {"C":5, "Go":4.5, "Python":4.5, "C++":2 }
// map iki değer döner. İkinci dönen değer aranılan key'in var olup olmadığı bilgisidir. Var ise true yok ise false döner.
csharpRating, ok := rating["C#"]
if ok {
fmt.Println("C# is in the map and its rating is ", csharpRating)
} else {
fmt.Println("We have no rating associated with C# in the map")
}
delete(rating, "C") // "c" elemanını sil
Yukarıda da belirttiğim gibi, map
referans tipli bir veri yapısıdır. Eğer iki map
aynı yeri gösteriyorsa, herhangi bir değişiklik ikisinide etkileycektir.
m := make(map[string]string)
m["Hello"] = "Bonjour"
m1 := m
m1["Hello"] = "Salut" // m["hello"]'nin değeri' Salut oldu
make
; map
, slice
ve channel
gibi tümleşik modeller için bellek ayırma işlemini yapar, new
ise tiplerin için çalışır.
new(T)
bellekte T
tipinde sıfırlanmış yer ayırır ve bellekteki adresini döner, o da *T
ki bir pointer. Go'da, öntanımlı olarak T
tipinde sıfırla ilklendirilmiş adresi döner.
new
pointer döner.
make(T, args)
fonksiyonu ise new(T)
'den daha farklı işler yapar'. make
; slice
, map
, ve channel
için kullanılabilir, bir T
tipi döner. Bunun nedeni ise bu 3 yapının bellekte bir alanı gösterbilmesi için ilklendirilmesi gerekir. Örneğin, bir slice
bellekte bir array
'e işaret eden pointer, boyut ve kapasiteye sahiptir. Bu veriler ilklendirilmeden, slice
'ın değeri nil
dir, bu yüzden slice
, map
ve channel
bellekte oluşturulurken make
bu veri yapıları için değerlerini ilklendirip atamalarını yapar.
make
boş olmayan değerler döner.
Aşağıdaki resimde new
ve make
arasındaki fark gösteriliyor.
Şekil 2.5 make ve new bellek ayırma
Sıfır değer, boş değerler demek değildir. Bir değişken tipinin öntanımlı değerleride denebilir. Aşağıda bazı tipler için sıfır değerleri listelnmiştir.
int 0
int8 0
int32 0
int64 0
uint 0x0
rune 0 // rune'un gerçek tipi int32'dir
byte 0x0 // byte'ın gerçek tipi uint8'dir
float32 0 // 4 byte uzunluğunda
float64 0 // 8 byte uzunluğunda
bool false
string ""
- İçerik
- Önceki bölüm: "Hello, Go"
- Sonraki bölüm: Kontrol yapıları ve fonksiyonlar