Go Programa Dili
“Hello World”
Öğrenilen her programlama dilinin ilk örneği “Hello World” ile bizde Go diline giriş yapalım.
Go dilinde “Hello World” aşağıdaki şekilde yazılır.
package main
import “fmt”
func main() {
fmt.Println(“hello world”)
}
Şimdi bunun ne anlama geldiğini açıklamaya çalışalım.
package: Bu paket bildirimidir. Her Go programı paketler ile başlamalıdır. Paketler Go’nun kodu düzenleme ve yeniden kullanma yöntemidir. “Main” bağımsız bir çalıştırılabilir dosyalar için gereklidir.
Import “fmt”: Bu ifade ile ekrana çıktısını almak istediğimizi sağlayan Go paketidir. Burada “fm” yi tırnak içine almışız. Bunun gibi çift tırnak kullanımı “dizi değişmezi” olarak bilinir. Burada fmt’yi “import fmt” diyerek çağırdık. Yani dahil ettik.
func:Fonksiyonlar Go programlama dilinin yapı taşıdır. Tüm işlevler “func” ile başlar. Main ismi özeldir çünkü fonksiyonları çalıştırmak istediğimizde buradan çağrılır . “()” bu fonksiyonda kullabacağımız argümanları bu parentezler içerisine dahil ederiz şayet var ise. { süslü parentez ise fonksiyonun başladığı ve bittiği yeri ifade eder.
fmt.Println(“Hello world”): Burada fmt paketinin içinden ekrana yazdırmak için “Println” formatını çağırıyor ve parentez içerisine yazdığımız “Hello World” cümlesini ekrana yazdırıyor.
Temel Veri Tipleri
Go static tip bir progralama dilidir. Kısaca Go çalışmadan önce, değişkenlerinin aldığı veri tipleri bilir ve ona göre hareker eder. Go’nun hızlı olmasının sebeblerinden biri.
Bu yazımızda veri tipleri ile ilgili yüzeysel bir bilgi veriyor olacağım.
<![if !supportLineBreakNewLine]>
<![endif]>
Veri Tipleri için bir örnek verecek olursam;
”Int16” keyword’nün alacağı max değer 32767, min değer -32768.
<![if !supportLineBreakNewLine]>
<![endif]>
package main
import “fmt”
func main() {
var number int16 = 32767
fmt.Println(“%T\n“, number)
}
Yukarıdaki örneğimizi derlediğimiz zaman sorunsuz çalıştığını göreceksiniz. Ama değişkenimize (number) int16’nın alacağı max değerinden daha fazla bir değer verdiğimizde aşağıdaki şekilde hata alacağız.
var number int16 = 32768
.\hello.go:7:6: constant 32768 overflows int16
Burada go bizlere hafızamda 16BIT’lik bir yer ayırdım diyor ama biz daha fazla bir değer verdiğimiz için hafızada ayrılan yerden daha fazla yer istiyoruz bundan dolayı bizlere hata olarak dönüyor. Değişkenlere verdiğimiz veri tipi değerlerinin max/min değerlerini aşmamamız gerekli.
Birde tanımladığımız veri tiplerini kullanmalıyız. Aksi halde go bizlere “tanımladığın değişkeni kullanmadığın diye” uyarı veriyor.
Örneğimiz;
func main() {
var number int16 = 32767
var age int = 40
fmt.Println(“%T \n“, number)
}
Aldığımız uyarı;
.\hello.go:8:6: age declared but not used
String Type: Karakterlerden oluşan veri tipleri. String’in uzunluğu asla negatif olamaz.
Örnek bir tanımlama
package main
import “fmt”
func main() {
var name string = “Go”
fmt.Println(name)
}
Aşağıdaki şekilde çift tırnak içinde boş bir string tanımlaması yapabiliriz.
var name string = ” “
Bool Type: Bool veri tipleri true veya false döndürür.
var isprime bool = true
Yukarıdaki örnekte isprime bizlere true döndürür.
Değişken Tanımlama
Bazen bir ismi, bazen bir yaşı, bazen kullanıcıdan aldığımız bir girişi bir yerde saklamak isteriz. İşte programlama dillerinde bu bilgiler bir değişkende tutulur. Değişkenler static tanımlama yapmadıysak programımız bunları geçiçi bellekte tutar (RAM).
Değişken tanımlama;
var name string=“GO”
Burada name isimli string bir değişken tanımladık ve bu değişkenimize “Go” değerini atatık.
Aşağıdaki şekilde bir tanımlamada yapılabilir;
var name string
name=“GO”
2.tanımlama yöntemi;
var name = “Raskolnikov”
3.tanımlama yöntemi;
Bu tanımlamaya short declaration diyebiliriz.
name := “Raskolnikov”
Short declaration’u global değişkenlerde kullanamayız.
4.tanımlama yöntemi;
var (
name string = “Raskolnikov”
age int = 30
)
5.tanımlama yöntemi;
var firstname, lastname, age = “Raskolnikov”, “L.Raskolnikov”, 30
6.tanımlama yöntemi;
firstname, lastname, age := “Raskolnikov”, “L.Raskolnikov”, 30
Go’da Scope
Sınıf, işlev veya değişken gibi bir program öğesi bildirdiğinizde, adı yalnızca “görünebilir” olabilir ve programınızın belirli bölümlerinde kullanılabilir. Bir adın görünür olduğu bağlam kapsamı olarak adlandırılır. Örneğin, bir fonksiyon içinde bir değişken bildirirseniz “a” , “a” Bu fonksiyon gövdesinde yalnızca görünür. Yerel kapsama sahiptir.
func main() {
var car=“fonksiyon Scope”
fmt.Println(“Hello World”)
}
“Car” değişkeni belirtmiş olduğumuz main fonksiyonu içinde erişilebilir. Kendini kapsayan fonksiyon üzerinden sadece erişilebilir. Buna yerel scope
package main
import “fmt”
var name = “Package Scope”
func main() {
var car=“fonksiyon Scope”
fmt.Println(“Hello World”)
}
Burada name değişkeni global scope alanında. Bundan dolayı name değişkenine farklı fonksiyonlarda erişebilir.
Örnek;
package main
import “fmt”
var name = “Package Scope”
func main() {
var car = “fonksiyon Scope”
if true {
}
fmt.Println(car)
test()
}
func test() {
fmt.Println(name)
}
Aşağıdaki örnekte “blockName” isimli değişkenimizi if bloğu içerisinde tanımladık. Fakat bunu süslü parantezler dışında kullanmak yani erişmek istedik.
package main
import “fmt”
var name = “Package Scope”
func main() {
var car = “fonksiyon Scope”
if true {
var blockName = “Block Scope”
}
fmt.Println(blockName)
}
Bu program çalıştırdığımızda aşağıdaki şekilde hata alırız.
.\hello.go:15:14: undefined: blockName
Çünkü bizler “blockName” sadece if içerisinde kullanmak üzere bir tanımlama yaptık. Şayet bunu “car”I tanımladığımız gibi yerel bir tanımlama yapsaydık bu programızda çalışıyor olacaktı.
Constants
Bir programın çalışması süresi boyunca değişmeyen ve sonradan değiştirilmeyen veri değerledir. Değişkenler oluşturduğunuz şekilde oluşturulurlar, ancak “var” anahtar sözcüğünü kullanmak yerine “const” anahtar sözcüğünü kullanırız:
package main
import “fmt“
func main() {
const x string = “Hello, World”
fmt.Println(x)
}
Aşağıdaki örnekte const veri değerimizi değiştirmeye çalışıyoruz
<![if !supportLineBreakNewLine]>
<![endif]>
package main
import “fmt“
func main() {
const x string = “Hello, World”
x = “Raskolnikov”
fmt.Println(x)
}
Burada derleyicimiz bizlere aşağıdaki şekilde hata veriyor olacak. Const değerimize sonradan atama yapılamaz.
cannot assign to x (declared const)
Kontrol Yapıları
For döngüsü;
For döngüsü, bir kod bloğunun birden çok kez tekrarlamamıza izin veren bir ifadedir. Yani 1’den 10’a kadar sayıları ekrana yazdıran bir program yasmak istediğimizde bunu şu ana kadar öğrediklerimiz ile ancak Printf(“1,2,3….”) şeklinde yazabilirdik. Ama for döngüsü ile bunun daha doğru ve daha sağlıklı bir yolu var.
package main
import “fmt”
func main() {
i := 1
for i <= 10 {
fmt.Println(i)
i = i + 1
}
}
Öncelikle yazdırmak istediğimiz sayıyı kullanmak için “I”adında bir değişken oluşturuyoruz. Daha sonra for döngüsü ile kaça kadar yazdırmak istiyorsak koşulumuzu veriyoruz. Println(i) ile bunu ekrana yazdırıp, döngümüzün devam etmesi için i değişkenimizin değerini 1 artırıyoruz (tek sayıları yazdırmak isteseydik, i=i+2 yazmamız gerekliydi).
i ye verdiğimiz ilk değer olan 1 for döngüsüne giriyor. Koşulumuzda 1 değerinin 10’dan küçük olup olmadığının kontrolü yapılıyor. 10’dan küçük olduğu için ekrana bir yazdırılıp, bir sonraki ifadeye gelip 1 olan I değişkenimizin değeri,1 artırılarak 2 yapılıyor ve tekrar for döngüsüne giriyor. Döngümüz false olana kadar devam eder.
Go’da while döngü deyimi bulanmamaktadır. Bunun yerine aşağıdaki şekilde bir yapı kullanılır.
package main
import “fmt“
func main() {
i := 1
for i < 10 {
fmt.Println(i)
i = i + 1
}
}
Go da sonsuz döngüyü aşağıdaki şekilde tanımlarız.
package main
import “fmt“
func main() {
x := 1
for {
if x == 5 {
}
fmt.Println(x)
x += 1
}
}
If deyimi
Bir uygulamada koşulun kontrolünü sağlayan deyimdir. Örnek ile açıklamaya çalışalım.
Az önce 1’den 10’a kadar yazdırdığımız sayıları bu sefer tek mi çift mi olduğunu control ederek yazdıralım.
package main
import “fmt”
func main() {
i := 1
for i <= 10 {
if i%2 == 0 {
fmt.Println(“Çift”, i)
} else {
fmt.Println(“Tek”, i)
}
i = i + 1
}
}
Burada 1 değerine sahip i değişkenimiz for döngüsü giriyor. 1 değerimiz 10’dan küçük eşit olduğu için if deyimine giriyor. İf içerisinde i’değerimizin çift olup olmadığının kontrolü yapılıyor. 1 değerimiz tek olduğu için İf deyiminin else bloğuna giriyor ve ekrana Tek 1 değerini yazıyor ve if deyiminden çıkıp i değerimizi +1 artırıyoruz.
Aşağıdaki şekildede yazabiliriz.
package main
import “fmt”
func main() {
for i := 1; i <= 10; i++ {
if i%2 == 0 {
fmt.Println(i, “çift”)
} else {
fmt.Println(i, “tek”)
}
}
}
Switch deyimi
package main
import “fmt”
func main() {
var note int
fmt.Printf(“Lütfen Ders Notunuzu Giriniz”)
fmt.Scan(¬e)
switch {
case note >= 0 && note <= 20:
fmt.Println(“E aldınız”)
case note >= 21 && note <= 40:
fmt.Println(“D Aldınız”)
case note >= 41 && note <= 60:
fmt.Println(“C aldınız”)
case note >= 61 && note <= 80:
fmt.Println(“B aldınız”)
case note >= 81 && note <= 100:
fmt.Println(“A aldınız”)
default:
fmt.Println(“Hatalı Giriş Yaptınız”)
}
}
Yukarıdaki örnekte kullanıcıdan bir değer girmesini istiyoruz. Kullanıcı ders notunu yazdıktan sonra, girdiği değer switch case ifadesine giriyor. Kullanıcının girdiği değer case’lerde control ediliyor ve buna göre aldığı ders notuna karşılık o harf getiriliyor. Şayet kullanıcı 0 ile 100 arasında farklı bir sayı girerse yazmış olduğu değer “default” değerine düşer ve ekranımıza hatalı giriş olarak yazar.
Fonksiyonlar
Fonksiyon belirli bir işlevi yapan kod blokları diyebiliriz. Fonksiyonlar yardımı ile programlarımızı daha işlevsel hale getirebiliriz. Mesela bir toplama işlemi yaptığımızda bunu kodumuzun farklı yerlerinde tekrar kullanmak istediğimizde, tekrar tekrar tanımlamalar yaparız. Ama bir toplama işlemi yapan fonksiyon yazdığımızda, toplama işlemini yapacağımız yerde toplama fonksiyonunu çağırırız. Ayrıca fonksiyonlar yazdığımız için kodlarımızda çıkacak hataları daha kolay analiz eder, daha rahat çözeriz.
O zaman ilk fonksiyon tanımlama ile başlayalım:
func sum(x, y int) int {
return x + y
}
İlk önce fonksiyon olduğunu belirttiğimiz keyword olan func’ı yazarız. Daha sonra yazacağımız fonksiyonun işlevi ne ise fonksiyonumuza o ismi yazarız. Fonksiyonumuz parametre alacak ise benim yazdığımız tanımlamada fonksiyon x ve y int türü iki parametre alıyor. Bu fonksiyonumuzdan int türü bir geri dönüş almak istediğimiz için “int” yazıyoruz. Fonksiyonumuz kendisine verilen iki parametreyi alıp topluyor.
package main
import “fmt“
func main() {
fmt.Println(sum(5, 6))
}
func sum(x, y int) int {
return x + y
}
Her zaman parametre ve geri dönüş vermek zorundamıyız? Tabi ki hayır;
package main
import “fmt“
func main() {
sayHi()
}
func sayHi() {
fmt.Println(“Hello World”)
}
Variadik Fonksiyonlar
Ne kadar argüman alacağını bilemediğimiz bir bir fonksiyonu “variadic” olarak tanımlayabiliriz.
Aslında programlamaya başladığımızdan beri farkında olmadan varayedik fonksiyonlar kullanıyoruz. Örneğin “Println” fonksiyonu variadik bir fonksiyondur. Aşağıdaki şekilde tanımlarız.
func add(args …int) int { }
“…” 3 nokta bizlerin ne kadar argüman alacağımızı bilmediğimizi ifade ederiz. Parametrelerimiz int türünden ve geri dönüşüde int türünden olduğunu belirtiyoruz.
package main
import “fmt“
func add(args …int) int {
total := 0
for _, v := range args {
total += v
}
return total
}
func main() {
fmt.Println(add(1, 2, 3))
}
“ for _, v := range args “ bu ifadeyi for döngü deyimi konusunda işlemedim. Kısaca bu döngü hakkında da bahsedelim. Bu aslında foreach fonksiyonu. Go’da tanımı yukarıdaki şekilde.
Error
Bir yazılımda beklenilmeyen tüm sonuçlara hata denir.Go’da aşağıdaki şekilde tanımlama yapılır.
package main
import (
“errors”
“fmt“
)
func main() {
err := errors.New(“error message”)
fmt.Println(err)
}
Arrays (Diziler)
Diziler, sabit uzunlukta tek bir türdeki öğelerin numaralandırması diyebiliriz.
Go’da aşağıdaki şekilçe tanımlaması yapılır;
1)
var len[10]int
Yukarıda len isimli 10 elemana sahip bir dizi tanımlaması yapıldı. Dizilerde, dizimizin uzunluğunu belirtmeliyiz.
Yukarıdaki örnekte int veri tipli bir dizi tanımladık. Bunun içerisine string bir eleman ekleyemeyiz..
2-)Aşağıdaki şekilde bir tanımlama yapabiliriz
x := [5]int{1,2, 3, 4, 5}
package main
import “fmt”
func main() {
var cities [3]string
var (
city1 string
city2 string
city3 string
)
fmt.Printf(“Lütfen birinci şehri giriniz “)
fmt.Scanln(&city1)
cities[0] = city1
fmt.Printf(“Lütfen ikinci şehri giriniz “)
fmt.Scanln(&city2)
cities[1] = city2
fmt.Printf(“Lütfen üçüncü şehri giriniz “)
fmt.Scanln(&city3)
cities[2] = city3
fmt.Println(cities)
}
Aşağıdaki şekilde çıktı alırız;
[İstanbul Ankara İzmir]
Slice
Slice’lar, dizilere benzer şekilde içerisinde birden fazla veri tutabileceğimiz, dizilerin üst kümesi olarak görebileceğimiz tiplerdir. Dizilerden farklı olarak, bu uzunluğunun değişmesine izin verirler. Tanım esnasında bir boyut belirtilmez ve tanım sonrasında istediğiniz kadar eleman ekleyebiliriz.
package main
import “fmt”
func main() {
var x1 = []int{0, 2, 4, 6, 8}
x2 := []int{1, 3, 5, 7, 9}
fmt.Println(“x1:”, x1)
fmt.Println(“x2:”, x2)
}
Yukarıdaki örnekte int tipinde bir Slice bildiririz. Slice tıpkı bir dizi gibi bildirilir, ancak parantez [] içinde herhangi bir boyut belirtmeyiz. Ayrıca iki farklı tanımlama ile slice’lar oluşturuldu.
Peki Slices’lar Arraylerden daha fonksiyonel. O zaman neden Arraylere ihtiyaç var go’da?
1-Arraylerin uzunluğunu verdiğimiz ve bu değişmeyeceği için bu daha hızlı ve daha güvenli.
2-Bir Array’in kopyasını aldığımızda, biz Array’in kendisinin kopyasını alıyoruz. Ancak Slice’lerde referansının kopyasını alıyoruz. Array’ler pass by value, Slices ise Pass by Reference
3-Slice’lar Array’lerin üzerine oluşturulmuş bir yapıdır.
Append
append, bir dilimin sonuna öğeler ekler. Yeterli kapasite varsa altta yatan dizide, eleman son elemandan sonra yerleştirilir ve uzunluk artırılır. Ancak yeterli kapasite yoksa yeni bir dizi oluşturulur, tüm mevcut elemanlar kopyalanır, yeni eleman sona eklenir ve yeni dilim döndürülür.
package main
import “fmt”
func main() {
var myArr1 = []int{0, 2, 4, 6, 8}
fmt.Println(“myArr1”, myArr1)
myArr2 := append(myArr1, 1, 4)
fmt.Println(“myArr2”, myArr2)
}
Çıktısı;
myArr1 [0 2 4 6 8]
myArr2 [0 2 4 6 8 1 4]
İki Slice’yi tek bir Slice’ye atamak için aşağıdaki yöntem uygulanır.
package main
import “fmt”
func main() {
fSlc := []int{0, 2, 4, 6, 8}
sSlc := []int{1, 3, 5, 7, 9}
eSlc := append(fSlc, sSlc…)
fmt.Println(“eSlc”, eSlc)
}
Burada “sSlc” Slice’ye dikkat. Burada şayet “…” (spread operatorü) kullanmassak, derleyici bizlere hata verecek.
Slice’ye eleman eklemeyi gördük. Peki eleman silme işlemini nasıl yapacağız;
Aşağıdaki örnekte xSlc isimli Slice’mizden ilk 3 elemanı siliyoruz. Bunun için xSlc[3:] yaparak belirtiyoruz.
Burada 3 index numarasından başlayarak “:” koyduğumuz için sonuna kadar gider ve son elemanına kadar yazar.
Şayet biz başından sonuna kadar tüm elemanları silmek ( ya da eklemek ) istersek bunun için;
xSlx[ : ] uygularız.
xSlc[ :3 ]-> uygularsak, burada ilk elemandan başlayarak 3 indeksdeki elemana kadar gider ama 3 indeksdeki elemanı dahil etmez.
package main
import “fmt”
func main() {
xSlc := []int{0, 2, 4, 6, 8, 10}
xSlc = xSlc[3:]
fmt.Println(“eSlc”, xSlc)
}
Çıktısı: eSlc [6 8 10]
Sondaki iki elemanı silmek için;
package main
import “fmt”
func main() {
xSlc := []int{0, 2, 4, 6, 8, 10}
xSlc = xSlc[:len(xSlc)-2]
fmt.Println(“xSlc”, xSlc)
}
Çıktısı: xSlc [0 2 4 6]
Baştan bir eleman sondan iki eleman silmek için;
package main
import “fmt”
func main() {
xSlc := []int{0, 2, 4, 6, 8, 10}
xSlc = xSlc[1:]
xSlc = xSlc[:len(xSlc)-2]
fmt.Println(“xSlc”, xSlc)
}
Çıktısı: xSlc [2 4 6]
Maps
Map’ler Key-Value çiftler şeklinde değerlerini tutmamızı sağlayan veri türleridir.
package main
import “fmt”
func main() {
firstMap := map[int]string{
10: “Cat”,
11: “Dog”,
12: “Penguin”,
13: “Bird”,
14: “Lion”,
}
fmt.Println(“firstMap”, firstMap)
fmt.Println(firstMap[12], firstMap[14])
}
Burada ilk elemanlarımızın hepsi int türünden olmalı ve ikinci elemanlarımızda string türlerinden olmalı. İnt türünden listede olmayan bir değere ulaşmaya çalışırcak hata almayız ekrana bir şey yazmaz.
Fakat 0 döndürdüğü için biz bunun olup olmadığını nasıl kontrol edeceğiz;
Örnek;
package main
import “fmt”
func main() {
firstMap := map[int]string{
10: “Cat”,
11: “Dog”,
12: “Penguin”,
13: “Bird”,
14: “Lion”,
}
fmt.Println(“firstMap”, firstMap)
fmt.Println(firstMap[12], firstMap[14])
value, number := firstMap[1]
fmt.Println(value, number)
}
1 değerini number isimli değişkene atayarak ve bunu ekrana yazdırarak öğrenebiliriz.
Eleman eklemek için;
package main
import “fmt”
func main() {
firstMap := map[int]string{
10: “Cat”,
11: “Dog”,
12: “Penguin”,
13: “Bird”,
14: “Lion”,
}
fmt.Println(“firstMap”, firstMap)
fmt.Println(firstMap[12], firstMap[14])
firstMap[15] = “Bear”
fmt.Println(“firstMap”, firstMap)
}
Eleman silmek için;
package main
import “fmt”
func main() {
firstMap := map[int]string{
10: “Cat”,
11: “Dog”,
12: “Penguin”,
13: “Bird”,
14: “Lion”,
}
fmt.Println(“firstMap”, firstMap)
fmt.Println(firstMap[12], firstMap[14])
firstMap[15] = “Bear”
fmt.Println(“firstMap”, firstMap)
delete(firstMap, 13)
fmt.Println(“firstMap”, firstMap)
fmt.Println(“Eleman Sayısı”, len(firstMap))
}
PACKET(PACKAGE)
Go, iyi yazılım mühendisliği uygulamalarını teşvik eden bir dil olarak tasarlanmıştır. Yüksek kaliteli yazılımın önemli bir parçası, “Kendinizi Tekrar Etme” ilkesinde somutlaşan kodun yeniden kullanılmasıdır.
Kodun yeniden kullanımına izin vermek için kullandığımız ilk katman fonksiyonlardır. Go ayrıca kodun yeniden kullanımı için başka bir mekanizma sağlar: paketler. Şimdiye kadar gördüğümüz hemen hemen her program bu satırı içeriyordu:
import “fmt” fmt, biçimlendirme ve ekrana çıktı verme ile ilgili çeşitli foknsiyonları içeren bir paketin adıdır. Paketleme kodu bu şekilde üç amaca hizmet eder:
• Örtüşen adlara sahip olma şansını azaltır ve sırayla foknsiyon adlarımızı kısa ve özlü tutar.
• Yeniden kullanmak istediğiniz kodu daha kolay bulmanız için kodu düzenler.
• Yalnızca bir programın daha küçük parçalarının yeniden derlenmesini gerektirerek derleyiciyi hızlandırır. fmt paketini kullanmamıza rağmen, programımızı her değiştirdiğimizde onu yeniden derlememiz gerekmiyor.
import (
“fmt“
“math”
)
func main() {
x := math.Sqrt(64)
fmt.Println(x)
}
Yukarıdaki örnekte “fmt” paketinin yanında ayrıca math paketini ekledik. Bu paketinin içinde karakök alan fonksiyonu çağırıyoruz.
Structs
Go’da class’lar yoktur. Bunun yerine struct’lar ( yapılar) mevcuttur. Struct’lar ile, farklı veri tipleri oluşturulabilir, nesneler oluşturulabiliriz.
Aşağıdaki şekilde tanımlama yapabiliriz
var employee struct {
name string
lastName string
age int
department string
}
Struct’a herhangi bir öğesine ulaşmak için “.” Kullanılır.
fmt.Println(employee.age)
Herhangi bir öğesine değer vermek için;
package main
import “fmt”
func main() {
var employee struct {
name string
lastName string
age int
career string
}
employee.name = “Amedeo”
employee.lastName = “Modigliani”
employee.age = 36
employee.department = “Painter”
fmt.Println(employee.name)
fmt.Println(employee.lastName)
fmt.Println(employee.age)
fmt.Println(employee.career)
}
Çıktısı;
Amedeo
36
Modigliani
Painter
Bizler her yeni eleman oluşturmak istediğimizde yeni bir struct mı oluşturacağız? Evet buda bir yöntem ama aşağıdaki şekilde bir tip oluşturulup, daha sonra bu veri tipi ile kendi veri tipimizi oluşturup yeni öğeler ekleyebiliriz.
func main() {
type employee struct {
name string
lastName string
age int
career string
}
var e1 employee
e1.name = “Amedeo”
e1.lastName = “Modigliani”
e1.age = 36
e1.career = “Painter”
fmt.Println(e1.name)
fmt.Println(e1.lastName)
fmt.Println(e1.age)
fmt.Println(e1.career)
var e2 employee
e2.name = “Salvador “
e2.lastName = “Dali”
e2.age = 85
e1.career = “Painter”
fmt.Println(e2.name)
fmt.Println(e2.lastName)
fmt.Println(e2.age)
fmt.Println(e2.career)
}
Yukarıda ”employee” isimli bir tip oluşturuyoruz. Daha sonra employee’den bir e1 ve e2 isimli employee veri tipi oluşturuyoruz. Böylece istediğimiz elemanları ekliyor oluruz .e1 ve e2’nin veri tipi struct değil, bunların veri tipi employee
Aşağıdaki şekilde de bir tanımlama yapabiliriz
e3:=employee{
name:“”,
lastName:“”,
age:25,
career:“”,
}
GO’da Pointer
İşaretçiler, bir değişkenin bellekteki adresini tutan değişkenlerdir. Bir değişkenin bellekteki adresine erişmek için değişkenin önüne “&” işareti eklenir.
package main
import “fmt“
func main() {
name := “Raskolnikov”
fmt.Println(&name)
}
Çıktısı: 0xc000088230
“&” ile değişkenimizin adresini görüntülüyoruz. Benim çıktım sizler yaptığınızda sizin pc’nizde farklı adreste tutabilir.”&” adress operatördür.
package main
import “fmt“
func main() {
name := “Raskolnikov”
fmt.Println(&name)
fmt.Printf(“%T,%v\n“, name, name)
fmt.Printf(“%T,%v\n“, &name, &name)
}
Çıktısı: string,Raskolnikov
*string,0xc000042240
İlk “name” şu ana kadar yaptığımız ve beklediğimiz değerler. Ancak ikinci &name çıktımızın veri tipi *string ve değeri 0xc000042240’dir
package main
import “fmt“
func main() {
age:=20
var number int = &age
fmt.Println(number)
}
Yukarıdaki şekilde number veri tipli değişkenimize “&age” atarsak hat alırız. Çünkü ikisinin veri tipi farklı. Number “int “ veri tipine sahipken “&age” ise *int veri tipine sahip.
Aşağıdaki şekilde bir atama yapabiliriz
package main
import “fmt“
func main() {
age := 20
var number *int = &age
fmt.Println(number)
}
Number’a kısa yol ile atama yaparsak;
number := &age
“*” operatörü kullandık. Bu ne işe yarar? Bu bizlere depolanan ya da hafızada tutulan adresin değerini temsil eden bir türü ifade ederB tür bbilgisi nesnenin kendi türünden türetilidr. İnt türden bir nesnenin adresi aşağıdaki şekilde gösterilebilir;
*int
Interface
interface ile fonksiyonlardan dönen değerin type’ni başka bir yerde kullanmak için şekillendirebilir. Arayüzler neslere arasındaki iletişimi sağlamak için kullanılır.
type Shape interface {
area() float64
}
Yukarıda oluşturduğumuz interface özelinde;
Bir struct gibi, interface’ler bir type keyword’ü ve isim ve “interface” keyword’ü kullanılarak oluşturulur.
package main
import “fmt“
type rectangle struct {
a, b float64
}
func (r rectangle) area() float64 {
return (r.a * r.b)
}
func (r rectangle) circumference() float64 {
return 2 * (r.a + r.b)
}
type shape interface {
area() float64
circumference() float64
}
func interfaceFunc(i shape) {
fmt.Println(i)
fmt.Println(i.area())
fmt.Println(i.circumference())
fmt.Printf(“%T“, i)
fmt.Println()
}
func main() {
r1 := rectangle{8, 4}
fmt.Println(“Area”, r1.area())
fmt.Println(“Circumference: “, r1.circumference())
interfaceFunc(r1)
}
Neden İnterface’leri kullanırız? Bir örnek vermek gerekirse gittigidiyor, hepsiburada, n11 gibi sitelerden sipariş bilgilerini çekmek istiyoruz. Bu sitelerin birinden bu bilgileri json olarak bir diğerinden xml olarak alıyor diyelim. Biz bir interface kullanarak bunlar arasında ortak bir yapı belirleriz. array dönmek zorunda olan bir getOrderno gibi ortak bir func oluşturur, bu siteler için tanımladığımız nesnelerimiz, oluşturduğumuz interface’yi implement eder. Böylelikle farklı response dönen veriler için ortak bir yapıda veri geleceğinden dolayı, bahsettiğimiz sitelerden gelen verileri işleyecek logicimizi hepsi için farklı farklı kodlar yazmayacak ve test edilebilir bir kod yazmış olacağız.
func main() {
t := triangle{3, 4}
s := square{4}
r := rectangle{4, 5}
printArea(t, s, r)
}
Goroutines
Eş zamanlı olarak yapılan görevlerim tümüne “Goroutines”denir. Diğer func’lar ile eş zamanlı çalışan diyebiliriz.Bir goroutine oluşturmak için, go anahtar sözcüğünü ve ardından bir func çağrısı kullanırız:
package main
import “fmt“
func pNumber(n int) {
for i := 0; i < 10; i++ {
fmt.Println(n, “:”, i)
}
}
func main() {
go pNumber(1)
var input int
fmt.Scanln(&input)
}
Bu program iki goroutinden oluşmaktadır. İlk goroutin örtüktür ve ana işlevin kendisidir. İkinci goroutin, go f(0)’ı çağırdığımızda oluşturulur. Normalde, bir fonksiyon çağırdığımızda, programımız bir fonksiyondaki tüm deyimleri yürütür ve çağrıyı takiben bir sonraki satıra döner. Bir goroutine ile hemen bir sonraki satıra dönüyoruz ve fonksiyonun tamamlanmasını beklemiyoruz. Bu nedenle Scanln işlevine yapılan çağrı dahil edilmiştir; onsuz, program tüm sayıları yazdırma fırsatı verilmeden önce çıkacaktır.
Waitgroup
Bazı senaryolarda, bu GoRoutine’lerin ihtiyaçlarınıza göre yürütmelerini tamamlamalarına izin vermek için kodun belirli bölümlerini engellemeniz gerekebilir. WaitGroup’un yaygın bir kullanımı ana işlevi engellemektir, çünkü ana işlevin kendisinin de bir GoRoutine olduğunu biliyoruz.
WaitGroup’u kullanabilmeniz için önce eşitleme paketini içe aktarmanız gerekir. WaitGroup struct türündedir ve kullanabileceğiniz üç işlevi vardır: Add(int), Wait() ve Done(). WaitGroup’un mekaniği, sıfırdan başlayan bir sayaca sahip olmasıdır. Add(int)’i her çağırdığınızda, sayacı Add(int) işlevinde belirtilen parametreyle artırmış olursunuz.
Öte yandan, her Done() işlevi sayacı 1 azaltır. Sayaç 0’dan küçükse WaitGroup panikler ve bu nedenle Done()’un gereğinden fazla çağrılmadığını kontrol etmeniz gerekir. Ayrıca, Add()’den önce Done()’u kullanırsanız, paniğe de neden olur. Wait() işlevi kodu engeller ve sayaç sıfır olana kadar serbest bırakılır.
Örnekte, önce bir wg değişkeni tanımladık ve yeni bir sync.WaitGroup{} örneğini başlattık. go print()’imizi çalıştırmadan önce Add(1)’i çağırırız. Ardından, yazdırma görevi tamamlandığında Done() işlevini kullanabilmemiz için işaretçiyi print()’de wg öğesine iletiriz. Ana GoRoutine’i engellemek için wg.Wait()’i çağırırız ve go print() bitene kadar bloğu serbest bırakır.
package main
import (
“fmt“
“sync”
)
func main() {
wg := sync.WaitGroup{}
wg.Add(1)
go print(&wg)
wg.Wait()
fmt.Println(“hello world”)
}
func print(wg *sync.WaitGroup) {
fmt.Println(“hello Mars”)
wg.Done()
}
Channels
Channels’lar, iki goroutinin birbirleriyle iletişim kurması ve yürütmelerini senkronize etmesi için bir yol sağlar. Aşağıda bunu kullanarak yapılan örnek bir program;
package main
import (
“fmt“
“time”
)
func pinger(c chan string) {
for i := 0; ; i++ {
c <- “ping”
}
}
func printer(c chan string) {
for {
msg := <-c
fmt.Println(msg)
time.Sleep(time.Second * 1)
}
}
func main() {
var c chan string = make(chan string)
go pinger(c)
go printer(c)
var input string
fmt.Scanln(&input)
}
Bu program sonsuza kadar ping yazdıracaktır (durdurmak için Enter’a basın). Bir channel türü, channel’da iletilen şeylerin türü tarafından takip edilen chan anahtar sözcüğüyle temsil edilir (bu durumda, dizeleri geçiyoruz). Sol ok operatörü (<-) kanalda mesaj göndermek ve almak için kullanılır. c <- “ping”, “ping” göndermek anlamına gelir. msg := <- c, bir mesaj almak ve onu msg’de saklamak anlamına gelir.
fmt.Println(<-c) gibi yazılmıştır, bu durumda önceki satırı kaldırabiliriz. Bunun gibi bir kanal kullanmak iki goroutini senkronize eder. Pinger kanalda bir mesaj göndermeye çalıştığında, yazıcı mesajı almaya hazır olana kadar bekler (buna engelleme denir). Programa bir gönderici daha ekleyelim bakalım ne olacak
olur. Bu fonksiyonuda programımıza ekleyelim.
func ponger(c chan string) {
for i := 0; ; i++ {
c <- “pong”
}
}
Programızın son hali;
package main
import (
“fmt“
“time”
)
func pinger(c chan string) {
for i := 0; ; i++ {
c <- “ping”
}
}
func printer(c chan string) {
for {
msg := <-c
fmt.Println(msg)
time.Sleep(time.Second * 1)
}
}
func ponger(c chan string) {
for i := 0; ; i++ {
c <- “pong”
}
}
func main() {
var c chan string = make(chan string)
go pinger(c)
go ponger(c)
go printer(c)
var input string
fmt.Scanln(&input)
}
Programımız şimdi sırayla ping ve pong yazdıracak