Introduction
![]()
Bu dijital kitap Türkçe dilinde GO programlama diline giriş ve temel kavramları anlatmak ve örnek projelerle dilin işleyişini kavratmak amacıyla yazılmıştır.
Yöntem
- Temel bilgiler konusunda dijital kitap boyunca hiçbir konuyu atlamamak adına ChatGPT ile oluşturulmuş bir konu listesi (topiclist) takip edilecektir.
- Bu konularla ilgili, konu düzeni değişebilir ve eklemeler olabilir.
- Bazı konular hakkında bilgiler ise yeri geldikçe örnek projelerin içerisinde verilecektir.
- Temel bilgiler sonrasında uygulamalar verilecek ve açıklamalar yapılacaktır.
İletişim
Kitap ile ilgili yorum soru ve detaylarınız için aşağıdaki mail adresinden bana ulaşabilirsiniz:
Fundamentals
GO programlama diline ait temel yapılar ve bilgilerin yer aldığı konu listesi
GO Fundamentals Topic List
- Introduction to Go
- Setting up Go Environment
- Syntax and Basic Structure
- Variables and Constants
-
Data Types
- Primitive Types
- Composite Types (Arrays, Slices, Maps, Structs)
-
Control Structures
- Conditional Statements (if, else, switch)
- Looping Constructs (for)
-
Functions
- Declaring Functions
- Parameters and Return Values
- Variadic Functions
- Anonymous Functions (Closures)
-
Packages and Imports
- Creating Packages
- Importing Packages
- Using Standard Library Packages
-
Error Handling
- Errors in Go
- Error Handling Strategies (if err != nil, panic, defer, etc.)
-
Pointers and References
- Pointer Basics
- Passing by Value vs. Passing by Reference
-
Structs and Methods
- Struct Definition
- Methods on Structs
-
Interfaces
- Interface Declaration
- Implementing Interfaces
- Interface Composition
-
Concurrency
- Goroutines
- Channels
- Select Statement
- Mutexes and Locks
-
Testing
- Writing Tests
- Testing Framework
- Benchmarking
-
Best Practices and Idioms
- Idiomatic Go Code
- Code Organization
- Error Handling Patterns
- Concurrency Patterns
-
Advanced Topics (Optional)
- Reflection
- Memory Management
- Using cgo for C Interoperability
- Unsafe Package
- Context Package
-
Web Development with Go (Optional)
- Introduction to Web Servers in Go
- HTTP Routing
- Middleware
- Templating
-
Working with Databases
- Deployment and Packaging (Optional)
- Creating Deployable Binaries
- Dockerization
- Deploying on Cloud Platforms
-
Additional Resources
- Official Documentation
- Online Tutorials and Courses
- Books and References
- Community Resources and Forums
GO Programlama Dili Hakkında
GO ya da bilinen diğer adıyla Golang 2007 Yılında Google tarafından geliştirilmeye başlanmış açık kaynak kodlu bir programlama dilidir.
Neden GO ?
- Açık kaynak kodludur. (open-source)
- Birçok projede (project) üründe (product) ve toplulukta (community) kullanılmaktadır.
- Google tarafından desteklenmektedir.
- Takım çalışmaları için elverişlidir.
- Yerleşik olarak concurrency desteği vardır.
- Zengin Standart Kütüphaneleri ( built-in-standart-libraries ) mevcuttur.
- Öğrenimi kolaydır.
GO Tarihi
Dil Gelistiricileri

Soldan Sağa: Robert Griesemer, Rob Pike, Ken Thompson
Go'nun geliştirilme süreci, Google'da çalışan üç önemli isim olan Robert Griesemer, Rob Pike ve Ken Thompson tarafından başlatılmıştır.
Diğer programlama dillerinde karşılaşılan zorluklar ile ilgili tartışmalar bu üçlüyü kendi dillerini icat etmeye teşvik etmiştir.
GO programlama dili derlenerek çalışan ikinci nesil bir programlama dilidir. İkinci nesil denilmesinin sebebi yazılan kod assembly (derleme ya da montaj) edildiğinde binary (makine seviyesi) komutlara dönüştürülür.
Tasarım aşamasında C++ ve Python gibi dillerden etkilenilmiştir.
GO Versiyon Tarihi
| Versiyon | Yayın Tarihi | Yılı | Açıklama |
|---|---|---|---|
| go1.0 | 2012-03-28 | 2012 | |
| go1.1 | 2013-05-13 | 2013 | |
| go1.2 | 2013-12-01 | 2013 | |
| go1.3 | 2014-06-18 | 2014 | |
| go1.4 | 2014-12-10 | 2014 | |
| go1.5 | 2015-08-19 | 2015 | |
| go1.6 | 2016-02-17 | 2016 | |
| go1.7 | 2016-08-15 | 2016 | |
| go1.8 | 2017-02-16 | 2017 | |
| go1.9 | 2017-08-24 | 2017 | |
| go1.10 | 2018-02-16 | 2018 | |
| go1.11 | 2018-08-24 | 2018 | |
| go1.12 | 2019-02-25 | 2019 | |
| go1.13 | 2019-09-03 | 2019 | |
| go1.14 | 2020-02-25 | 2020 | |
| go1.15 | 2020-08-11 | 2020 | |
| go1.16 | 2021-02-16 | 2021 | |
| go1.17 | 2021-08-16 | 2021 | |
| go1.18 | 2022-03-15 | 2022 | |
| go1.19 | 2022-08-02 | 2022 | |
| go1.20 | 2023-02-01 | 2023 | |
| go1.21 | 2023-08-08 | 2023 | |
| go1.22 | 2024-02-06 | 2024 |
GO Dili Kullanan Büyük Sirketler ve Projeleri
Kaynak: brainhub.eu - Companies Using Golang - Top 7 Popular Apps You Use Everyday

Golang, Google mühendisleri tarafından tasarlandı ve burada genellikle şirket içi projeler için kullanılıyor. Google Chrome ve Google Earth bu şekilde oluşturuldu. Ayrıca YouTube ve Google App Engine'de de kullanılır.
- Google Earth
- Youtube
- Google Chrome
Uber

Golang'ı kullanan en büyük şirketlerden biri Uber'dir. Burada kullanıcının konumunu ve ürünün kullanılabilirliğini sağlayan coğrafi sınır hizmeti için kullanılır. Geofence, özel gereksinimlere sahip alanın hassas bir şekilde tanımlanmasına (örneğin, havaalanları gibi yerler dikkate alınarak) ve dinamik fiyatların uygulanmasına olanak sağlar.
Uber'den Kai Wei'ye göre şirket, Golang ile çalışırken çok olumlu deneyimler yaşadı. C++, Java veya Node programcılarının Go'ya geçme kolaylığı ve yüksek bant genişliği ve gecikme performansı takdir edilmiştir.
Geofence: Haritalar üzerinde sanal çitle çevrilmiş bölgeler oluşturmak anlamına gelir. Böylece belirli bölgeleri belirli amaçlarla işaretleme ve kullanma özelliği gösterir.
Hizmet garantili yüksek güvenilirliğe sahiptir. Devreye alınmasından bu yana %99,99 oranında sürekli olarak çalışmaktadır. Dil açısından herhangi bir sorun yaşanmazken, üçüncü taraf kütüphanelerden kaynaklanan nadir kesintiler yaşandı.
Twitch

Twitch'te en çok yüklü sistemler için Go kullanılıyor. Sadeliği, güvenliği, verimliliği ve okunabilirliği nedeniyle takdir edilmektedir; bu, çok sayıda kullanıcının canlı video ve eşzamanlı sohbetlerini görüntülerken karşılaşılan sorunları mükemmel bir şekilde yönettiği anlamına gelir.
Ancak hepsinden önemlisi Go, Twitch'in dinamik olarak ayrılmış belleğin otomatik olarak yönetilmesinden sorumlu olan GC (çöp toplama) faktörünü 20 kat geliştirmesini sağladı.
Dailymotion
Dailymotion bir video akışı sitesidir. Diğerlerinin yanı sıra Golang sayesinde API'lerin otomasyonu iyileştirildi.
Bu dil, aksi takdirde büyük yüklere neden olacak çok sayıda otomasyon testinin gerçekleştirilmesine olanak tanır. Hepsi Golang'ın basitliği ve performansının yanı sıra statik tip kontrolü olasılığı nedeniyle.
Dropbox

Bulut bilişim hizmetlerinde liderlerden biri olan Dropbox, Golang'ı kullanan büyük bir şirketin bir başka harika örneğini oluşturuyor. Sistemlerini daha verimli bir şekilde ölçeklendirmek için bu dile ihtiyacı var. Bu amaçla şirket, kritik sistemlerini Python'a taşıdı.
Go kütüphanelerinin derinliğinin yetersiz olması sorununu çözmek ve daha büyük sistemler kurabilmek için Dropbox kendi kütüphanelerini kurmaya başladı.
Bu onların önbelleğe almayı geliştirmelerine, standart hata arayüzünü geliştirmelerine, programcıların programlı olarak SQL ifadeleri oluşturmalarına olanak sağlamalarına ve tamamen işlevsel memcache istemci kitaplıkları uygulamalarına olanak sağladı.
Dropbox, Golang kitaplıklarını genişletmeyi ve dahili olarak kullanılan kitaplıkları GitHub'da herkese açık hale getirmeyi planlıyor.
Soundcloud

Golang'ı kullanan en büyük şirketlerden biri SoundCloud'dur. Ruby on Rails'in yanı sıra Go dilini de kullanmaya karar verdi.
Mühendisleri Golang'ı bir WYSIWYG dili olarak tanımladı; bu, girilen kodun tam olarak sayfada görünen şeyi yaptığı anlamına geliyor. Ayrıca, kod üzerinde düşünmeye daha az zaman harcanması anlamına gelen "tek sorun - tek çözüm" felsefesini de takdir ediyorlar.
SoundCloud için gerçek zamanlı statik analiz gerçekleştirme yeteneği de önemliydi; bu, Go'nun sağladığı statik yazma ve hızlı derleme sayesinde mümkün oldu.
Bu, uygulamalar üzerindeki çalışmayı önemli ölçüde hızlandırdı.
Şu anda SoundCloud, tamamı Go'da yazılmış 6 hizmet ve bir düzine kadar depo kullanıyor.
GO Kurulumu

Ingilizce seviyesi iyi olanlar setup-instructions resmi web sayfasındaki kurulumları izleyerek sistem kurulumunu gerçekleştirebilir. Eğer Ingilizce ile ilgili bir probleminiz varsa aşağıda bu yönergelerin Türkçe'ye çevrilmiş halleri mevcuttur
Linux

Linux Terminal
- https://go.dev/dl/ web sitesine gidin
- En güncel Linux sürümünü bulup indirin.
UYARI: Eğer sisteminizde daha önceden yüklü bir sürüm varsa bu sürümü silin.
- İndirme işlemi yaptığınız klasörde terminalinizi açarak aşağıdaki kodu komut satırına yapıştırın.
sudo rm -rf /usr/local/go && tar -C /usr/local -xzf go1.19.4.linux-amd64.tar.gz
Yukarıdaki Bash kod bloğu sisteminizde /usr/local/go adresi içerisinde yer alan mevcut sürümü kaldırır ve aynı klasörün içine go1.19.4.linux-amd64.tar.gz ismindeki indirdiğiniz tar dosyasını /usr/local/go klasöre açar.
DİKKAT: Asla mevcut sürümü
/usr/local/goklasörünü tamamen silip kaldırmadan aynı klasöre tar dosyasını açmayın.
.profiledosyanızda dosyanızda PATH ortam değişkenine/usr/local/go/binekleyin. Bunu$HOME/.profileveya/etc/profiledosyanıza aşağıdaki satırı ekleyerek yapabilirsiniz (sistem çapında bir kurulum için):
export PATH=$PATH:/usr/local/go/bin
Not: Bir profil dosyasında yapılan değişiklikler, bilgisayarınızda bir sonraki oturum açışınıza kadar geçerli olmayabilir. Değişiklikleri hemen uygulamak için doğrudan kabuk komutlarını çalıştırın veya bunları
$HOME/.profilegibi bir komut kullanarak profilden yürütün.
- Bir komut istemi açıp aşağıdaki komutu yazarak Go'yu yüklediğinizi doğrulayın.
go version
Komutun yüklü Go sürümünü yazdırdığını onaylayın.go version Ekran çıktısı aşağıdakine benzer olmalıdır.
go version go1.19.4 linux/amd64
Linux snap Kurulumu
- İlk olarak snap içerisinde ilgili go sürümünü arayın.
snap search go
Çıktı aşağıdakine benzer şekilde olmalıdır.
Name Version Publisher Notes Summary
go 1.19.4 mwhudson classic Go programming language compiler, linker, stdlib
- snap üzerinden GO indir.
snap install go
- Sistem üzerinden go versiyonu kontrol edilir.
go version
Mac
- İndirdiğiniz paket dosyasını açın ve Go'yu yüklemek için talimatları izleyin.
- Paket, Go dağıtımını
/usr/local/gokonumuna kurar. Paket/usr/local/go/bindizinini PATH ortam değişkeninize konulmalıdır. - Değişikliğin etkili olması için tüm açık Terminal oturumlarını yeniden başlatmanız gerekebilir.
- Bir komut istemi açıp aşağıdaki komutu yazarak Go'yu yüklediğinizi doğrulayın:
go version
Windows
- İlgili windows MSI dosyasını indirin.
- İndirdiğiniz MSI dosyasını açın ve Go'yu yüklemek için talimatları izleyin.
- Varsayılan olarak yükleyici, Program Dosyalarına Git veya Program Dosyalarına (x86) kuracaktır. Konumu gerektiği gibi değiştirebilirsiniz.
- Kurduktan sonra, yükleyici tarafından ortamda yapılan değişikliklerin komut istemine yansıtılması için tüm açık komut istemlerini kapatıp yeniden açmanız gerekir.
- Go'yu yüklediğinizi doğrulayın.
- Windows'ta, Başlat menüsüne tıklayın.
- Menünün arama kutusuna cmd yazın ve ardından Enter tuşuna basın.
- Görünen Komut İstemi penceresinde aşağıdaki komutu yazın:
go version
- Komutun yüklü Go sürümünü yazdırdığını onaylayın.
WEBSITE: https://go.dev/dl/
Değişkenler - Variables
Değişken (Variable): ifadeleri programlama dillerinde değer ataması yapıldıktan sonra program tarafından işlemlere tabi tutularak değiştirilebilen hafıza öğeleri için kullanılır.
Aşağıda string türünde bir değişken ifadesi verilmiştir:
// string (metin) degisken turu
var isim string = "Gokay"
Değişkenlere Atanabilen Veri Türleri
Değişkenler basit ve karmaşık veri tiplerinde değer alabilirler:
| Değişken Veri Tipi | Açıklama |
|---|---|
float | ondalıklı sayı değerleri |
int | tam sayı değerleri |
bool | true ve false değerleri |
string | metin değerleri |
rune | karakter değerleri |
byte | byte cinsinden veri degerleri |
slice | dizi parçaları |
array | dizi değerleri |
map | anahtar - değer (key-value) ikililerinden oluşan veri kümeleri |
struct | yapısal veri türleri |
interface | kapsayıcı arayüz veri türleri |
Değişkende izin verilen değerler
Bir değişkende kullanımına izin verilen değerler kümesi, değişkenin tanımlanması sırasında değişkene atanan veri türüne göre belirlenir.
Örneğin, elimizde isim diye bir değişken olduğunu var sayalım. İzin verilen deger olarak degisken türünün string type bir ifade olduğunu düşünürsek isim değişken ifadesi yalnızca string veri tipinde değer tutabilir.
DIKKAT:
stringolarak saklanan bir değişkene, bir tam sayıintegerveya ondalıklı sayıfloatdeğer verirsek, veyahut mantıksalboolbir ifade verirsek sistemden hata dönüşü alırız.
Birer örnekle neden hata döndüreceğini kısaca izah edelim
var isim string = 25
// string yerine integer deger atandığı için hata döndürür
var soyisim string = true
// string yerine boolean değer atandığı için hata döndürür
var yas integer = 25.5
// integer yerine float değer atandığı için hata döndürür
Değişkenlerin Özellikleri
Değişkenlerin özellikleri kısaca şunlardır:
- İşleme tabi tutulabilirler.
- Üzerlerine defalarca değer ataması yapılabilir.
- Sistemden çağrıldıkları zaman üzerlerine son atanan değeri döndürürler.
Değişken Tanımlama
Tip Belirterek Tanımlama
İlk tanımlama şeklimiz detaylı tanımlama şeklidir. Aşağıdaki şekilde tanımlanır.
// var degiskenAdı degiskenTur = deger
var country string = "Turkey"
Tanımlama işlemine var yani variable(değişken) kısaltması ile başlıyoruz. Burada tanımladığımız değişkenin bir string ifadesi olduğunu country ismini ve "Turkey" değerini aldığını görebiliriz.
Tip Belirtmeden Tanımlama
GO programlama dili otomatik biçimde değişken türü tanıma ve atama özelliğine sahiptir. Yani eğer string, float , integer, boolean gibi bir değer tipi belirtmeden değişken tanımlıyorsak, GO bu değişkene en uygun türü kendisi atayacaktır.
var country = "Turkey"
var province = 81
var hasPresident= true
Yukarıda tanımladığımız üç değişken sırasıyla string,integer ve boolean değişken tiplerini aldılar.
Fonksiyon Scope'u İçinde Değişken Tanımlama
Eğer ifademizi bir fonksiyon içinde tanımlıyorsak şimdiye kadar anlatılanlar dışında alternatif bir kısa yazım daha mevcuttur. Fonksiyon içinde değişken tanımlarken := karakterleri ile eşitlik yazımı mevcuttur.
DİKKAT: Bu tanımlama türü, yanlızca fonksiyon scope'u içinde mümkündür.
func main(){
country := "Turkey"
}
Hatırlarsanız daha önce country değişkenimizi aşağıdaki yöntemlerle tanımlamıştık:
var country string = "Turkey" // Tip Belirterek Tanımlama
var country = "Turkey" // Tip Belirtmeden tanımlama
Şimdi ise değişkenimizi country := "Turkey" şeklinde tanımlıyoruz. Bunun sebebi, tanımlamayı {} süslü parantezler ile kapalı bir fonksiyon scope'u içinde gerçekleştirmemizdir.
Birden Çok Değişkeni Tek Satırda Tanımlama
Eğer elimizde birden çok değişken varsa ve bunları tek satır içerisinde tanımlamak istiyorsak aşağıdaki yazım türünde faydalanırız:
var b, c int = 1, 2 // b = 1 ve c = 2
Burada b ve c olmak üzere, iki adet int türünde değişken ifadesini tek bir satır içerisinde virgül ile ayırarak tanımladık. Yorum ifadesinde de belirtildiği üzere b=1 ve c=2 değerlerini almıştır.
Aynı şekilde değişken tipini yazmadan da değişkene değer atama işlemi yapabiliyoruz. GO otomatik olarak değişken türünü tanımlayıp atamasını yapmaktadır.
var en, boy = 100, 50
Eğer değişken türleri farklı ise tek satırda tanımlama aşağıdaki şekilde yaılmaktadır.
var paraVar bool, miktar float64 = true, 55000.25
/*
var bool paraVar = true
var float64 miktar = 55000.25
*/
aynı ifadeyi bir de kısaltılmış olarak yazabiliriz.
var paraVar, miktar = true, 55000.25
/*
var paraVar =true
var miktar = 55000.25
*/
Biraz daha uzun bir şekilde tanımlama işlemi yapalım:
var isim ,soyisim , matematik , edebiyat , gecmeDurum , ortalama = "Ahmet","YILMAZ", 74, 83, true, 75.8
/*
isim = "Ahmet"
soyisim ="YILMAZ"
matematik = 74
edebiyat = 83
gecmeDurum = true
ortalama = 75.8
*/
Değişken Türlerinin Görüntülenmesi
Değişken türlerini görüntülemek için aşağıdaki kod yapısını kullanıyoruz.
func main() {
var kilo float32 = 70
var boy int32 = 175
// \n - bir alt satıra geç ve boşluk bırak
// anlamındaki kaçış karakteri (escape characters)
// %T - Öğe türüne ait bilgileri getiren argümandır
// fmt.Printf() - Formatlanmış olarak ekranda görüntüleme yapmayı sağlayan ifadedir
fmt.Printf("Kilo: %T\n", kilo)
// %f - float cinsinden değer görüntüler
// %d - integer cinsinden değer görüntüler
fmt.Printf("Boy: %d Kilo: %f\n", boy, kilo)
// boy için hata döndürür
fmt.Printf("Boy: %f Kilo: %f\n", boy, kilo)
//kilo için hata döndürür
fmt.Printf("Boy: %d Kilo: %d\n", boy, kilo)
// değişken türlerini görüntüler
fmt.Printf("Boy: %T Kilo: %T\n", boy, kilo)
}
Yukarıdaki script'i yazdığımzda aşağıdakine benzer bir çıktı alırsınız:
Kilo: float32
Boy: 175 Kilo: 70.000000
Boy: %!f(int32=175) Kilo: 70.000000
Boy: 175 Kilo: %!d(float32=70)
Boy: int32 Kilo: float32
Burada float32 ve int32 gibi veri tipleri gördünüz. 8,16,32,64 gibi değerler bit cinsinden değerler olup bu veri tiplerinin alabileceği minimum ve maksimum değerleri temsil ederler. Daha ayrıntılı olarak veri tipleri konusunda bilgilendirme yapılacaktır.
Uygulama: Değişkenlerin Tanımlanması
Aşağıda bir uygulamamız bulunuyor. Bu uygulamada birden çok değişken tanımlıyoruz ve fmt yardımı ile sonuçları ekrana yazdırıyoruz.
Yeni bir main.go dosyası oluşturup aşağıdaki kodları dosyanıza yapıştırıp döndürülen sonuçları inceleyebilirsiniz:
package main
import "fmt"
func main(){
// sehir isimli bir string degiskeni
sehir := "Edirne"
// kilo ve boy isimli bir float degiskenleri
var kilo float64 = 75.3
var boy float64 = 182.3
// yas isimli bir integer degiskeni
yas := 30
// mezuniyet ve askerlik isimli boolean degiskenleri
var mezuniyet,askerlik bool = true, true
// ifadeyi ekrana yazdır
fmt.Println(boy,kilo,yas,sehir)
fmt.Println(mezuniyet, askerlik)
// Tek satırda birden çok deger tanımlaması
var isim ,soyisim , matematik , edebiyat , gecmeDurum = "Ahmet","YILMAZ", 75, 83, true
// Çoklu değer tanımlaması kısa gösterim
//isim ,soyisim , matematik , edebiyat , gecmeDurum := "Ahmet","YILMAZ", 75, 83, true
// degerleri ekrana yazdır
fmt.Println(isim,soyisim,matematik,edebiyat,gecmeDurum)
}
Sabitler (Constants)
Constant ifadeler sabitler olarak bilinirler. Değişken ifadelerinden farklı olarak, bir defa değer atandığı an itibariyle sistemde sabit kalırlar. İşleme tabi tutulsalar dahi, işlem bitişinde yine değerleri sabit olmaktadır.
Bir const ifadesi aşağıdaki biçimde yazılır:
const ifade = deger
Aşağıda bir örnek vermek gerekirse:
// suyun kaynama noktası
const boiling_point = 100
const ifadeleri var ifadelerinden farklı olarak ikinci bir değer ataması alamazlar. Tanımlandıkları biçimde sabit kalırlar.
DİKKAT:
constifadeleriscopeiçinde ve dışında tanımlanabilir.
const İfadelerin Veri Türleri
const ifadeler aşağıda yer alan tüm veri tiplerinde veri taşıyabilirler.
- Integer -
int - Float -
float32,float64vs. - String -
string - Boolean -
bool - Character -
runebyte()gibi tek bir karakter
const İfadelerin Kullanımı
GeeksForGeeks web sitesinden alınan örnek ifade aşağıda verilmiştir.
package main
import "fmt"
const PI = 3.14
func main()
{
const GFG = "GeeksforGeeks"
fmt.Println("Hello", GFG)
fmt.Println("Happy", PI, "Day")
const Correct= true
fmt.Println("Go rules?", Correct)
}
ifadede görüldüğü üzere PI isimli float bir sabit değer tanımlanmıştır. Burada dikkat etmemiz gereken nokta const ifadelerinin scope (fonksiyon tanımlanan aralık) dışında da tanımlanabildiğidir.
Yazılmamış (Untyped) ve Yazılı (Typed) Sayısal Sabitler
Yazılan sabitler (typed constants), sabitlenmiş değişkenler gibi çalışır, yalnızca aynı türle birlikte çalışabilir ve tür bilgisi yazılmamış sabitler (untyped constants), değişmezler gibi çalışır, benzer türlerle birlikte çalışabilir.
Sabitler (constants), Go'da bir türle veya türsüz olarak bildirilebilir.
Aşağıda, hem typed hem de untyped biçimde belirtilmiş ifadeler örneklenmiştir.
//veri tipi belirtilmemiş
const untypedInteger = 123
const untypedFloating = 123.12
//veri tipi belirtilmiş
const typedInteger int = 123
const typedFloatingPoint float64 = 123.12
Yazılı ve Yazılı Olmayan Sabitlere Ait Örnek Uygulama
Aşağıda belirli sabitler tanımlanmıştır ve bu sabitlerle rastgele işlemler yapılmıştır.
package main
import "fmt"
const Pi = 3.14 // untyped
const Euro float32 = 18.78 // typed
const yil = 2022 // untyped
const kurulus int = 1923 //typed
func main() {
test := Euro * yil // tanımli sabit Euro ile tanımsiz sabit yil carpiliyor
test2 := Pi * Euro // tanımli sabit Euro ile tanımsız sabit Pi carpiliyor
test3 := Pi * yil // tanımsız sabit pi ile tanımsız sabit yil carpiliyor
/* a
test3 := kurulus * Euro
yukarıdaki ifade hata verecektir
cünkü kurulus tanımlı sabit Euro da tanımlı sabittir
ve veri tipleri farklıdır. tip dönüşümü gerekmektedir.
kurulus int degerini float32 'ye cevirerek isleme sokabiliriz.*/
test4 := float32(kurulus) * Euro
fmt.Println(test)
fmt.Println(test2)
fmt.Println(test3)
fmt.Println(test4)
}
Tip Dönüşümü
İfademizde notları incelediyseniz tanımlama sırasında bir noktada tip dönüşümü yaptık. Bunun sebebi ise bu değerin tip tanımlı bir sabit olmasıdır. Bu sebepten dolayı GO otomatik olarak uygun değere çeviremez ve yeniden tip elle tip tanımlaması yapmamız gerekir.
// tip dönüşümü
test4 := float32(kurulus) * Euro //int veri tipini float32 veri tipine cevirdik
Sabit Türleri
GOLANG içerisinde sabit türleri çeşitli olmakla birlikte temel başlıklar altında toplanabilir.
- Metin Sabitleri - String Literals
- Sayısal Sabitler - (Integer, Float , Complex )
- Boolean Sabitleri
Metin Sabitleri
- Go, iki tür değişmez
stringyi destekler, yani ” ” (çift tırnak stili) ve ' '(geri alıntı). stringifadeler + ve += operatörleriyle birleştirilebilir.- Bir
string, karakter değişmezlerine benzer karakterler içerir: düz karakterler (plain characters), kaçış dizileri (escape sequences ) ve evrensel karakterler (universal characters). Ve bu, yazılmamış karakterlerdendir. stringtürlerinin sıfır değerleri, değişmez olarak ” ” veya ” ile gösterilebilen boşstringlerdir.stringtürlerinin tümü, == (equal) , != (not equal) ve (aynı türlerin karşılaştırılması için) gibi operatörler kullanılarak karşılaştırılabilir.
package main
import "fmt"
func main()
{
const A = "GOKAYBURUC.DEV"
var B = "Medium Blog Page"
// Concat strings. - String ifadelerin birleştirilmesi
var karsilama = A+ " " + B
karsilama += "!"
fmt.Println(karsilama)
// Compare strings. - String ifadelerin karşılaştırılması
fmt.Println(A == "GOKAYBURUC")
fmt.Println(B < A)
}
Boolean Sabitleri
true ve false değeri alan sabitlerdir.
package main
import "fmt"
const Pi = 3.14
func main()
{
// degeri true olan bir const tanımladık
const trueConst = true
// Type definition using type keyword - Veri Tipi Tanımlama
type myBool bool
// const ifadeyi bir değişkene atadık
var defaultBool = trueConst // izin verilir
var customBool myBool = trueConst // izin verilir
// defaultBool = customBool // izin verilmez
fmt.Println(defaultBool)
fmt.Println(customBool)
}
Tamsayı (Integer) Sabitleri
Tamsayı integer sabitleri çeşitli değişkenlik göstermektedir.
Burada sayı yazılımları :
- Decimal : Ondalık ( 10 luk sayı sistemi )
- Octal : Sekizlik ( 8 'lik sayı sistemi )
- Hexadecimal : On altılık (16'lık sayı sistemi)
- Int : Tamsayı
- Unsigned Int : İmzasız integer
- Long : Uzun veri tipi - C Kökenli bir veri tipidir. ( 2^64 ifadesini getirir)
85 /* decimal */
0213 /* octal */
0x4b /* hexadecimal */
30 /* int */
30u /* unsigned int */
30l /* long */
30ul /* unsigned long */
212 /* Legal */
215u /* Legal */
0xFeeL /* Legal */
078 /* Illegal: 8 is not an octal digit */
032UU /* Illegal: cannot repeat a suffix */
Örnek ifademizi tanımlayalım.
package main
import "fmt"
const ekmek int = 5
func main() {
//const ekmek = 5
var sepet int = 30
yarimSepet := sepet / 2
yarimEkmek := ekmek / 2
fmt.Println(yarimEkmek)
fmt.Println(yarimSepet * ekmek)
// bu ifade hata verecektir. sabit bir deger ikinci bir atama kabul etmez
ekmek = ekmek / 2
fmt.Println(ekmek)
}
Ondalık (Float) Sabitleri
Ondalık sabitler olarak tanımlanır.
3.14159 /* Legal */
314159E-5L /* Legal */
510E /* Illegal: incomplete exponent */
210f /* Illegal: no decimal or exponent */
.e55 /* Illegal: missing integer or fraction */
Kompleks Sayı (Complex) Sabitleri
2 değerle tanımlanan komplex sayılar için kullanılır. Aşağıdaki iki tanımlama da geçerli bir tanımlamadır.
const ilk = (0.0, 0.0)
const sinirlar = (-123.456E+30, 987.654E-29)
Tip Tanımlama - Type Definition
Type Definition (Tip Tanımlama): GOLANG içinde eğer yeni bir tip tanımlamak istiyorsanız
//type tipAdı veriTürü
type benimVeriTipim string
sentaks değerini izlersiniz.
Runes
Go Dilinde Rune İfadeleri
GO programlama dilinde karakterler, Unicode karşılıkları ile rune ifadesi olarak saklanır. Biz bir karakteri GO dilinde yazdığımızda hafıza karşılığı Unicode değer olacak şekilde GO dilinde saklanır.
Bir örnekle izah etmek gerekirse:
package main
import (
"fmt"
"reflect"
)
func main() {
// yeni rune ifadeleri olusturma
rune1 := 'B'
rune2 := 'g'
rune3 := '\a'
rune4 := 'ف'
rune5 := 'Й'
// rune ifadelerini tip degerlerine gorse yazdirma
fmt.Printf("Rune 1: %c; Unicode: %U; Type: %s", rune1,
rune1, reflect.TypeOf(rune1))
fmt.Printf("\nRune 2: %c; Unicode: %U; Type: %s", rune2,
rune2, reflect.TypeOf(rune2))
fmt.Printf("\nRune 3:%c; Unicode: %U; Type: %s", rune3, rune3,
reflect.TypeOf(rune3))
fmt.Printf("\nRune 4: %c; Unicode: %U; Type: %s", rune4, rune4,
reflect.TypeOf(rune4))
fmt.Printf("\nRune 5:%c; Unicode: %U; Type: %s", rune5, rune5,
reflect.TypeOf(rune5))
}
Yukarıdaki kod blogu su ciktiyi verecektir:
Rune 1: B; Unicode: U+0042; Type: int32
Rune 2: g; Unicode: U+0067; Type: int32
Rune 3: ; Unicode: U+0007; Type: int32
Rune 4: ف; Unicode: U+0641; Type: int32
Rune 5: Й; Unicode: U+0419; Type: int32%
Öncelikle ekrana yazdırma sırasında neler olduğunu açıklayalım. Hatırladığınız üzere fmt isimli kütüphane yazdırma işlemleri ve metinsel işlem operasyonları için kullanılıyordu. Burada fmt.Printf ile formatlanmış olarak ekran yazdırması yaptık. Yani ekrana yönlendirilecek ifadeleri %c , %U , %s gibi notasyonlara göre biçimlendirerek yazdırdık. Bu ifadelerden ilki olan %c ekrana karakter bastırmaya yarar. İkinci ifademiz olan %U ise ekrana Unicode değer karşılığını yazdırır. Son ifademiz olan %s ise string ifade yazdırır. Burada rune olan ifademizin sayısal değerini binary den reflect.TypeOf() ifadesi ile çevrim yaparak ekrana bastırdık.
fmt.Printf("\nRune 5:%c; Unicode: %U; Type: %s", rune5, rune5,
reflect.TypeOf(rune5))
Daha Fazla Bilgi için :
Bytes
Go Programlama dilinde verilerin transferi sırasında bazı durumlarda karşımıza byte ifadeleri çıkmaktadır.

Farklı program yapılarından veri transferi sırasında kodlar derlenerek binary yapıya çevrilir.
BINARY : Makinelerde elektrik sinyallerinin gelip gelmemesine bağlı olarak 1 veya 0 değerlerini alabilen iki tabanında gösterim sistemidir.
Binary yapıdaki veriler bitlerden oluşur ve bitler yan yana gelerek baytları oluştururlar.
Özellikle internet üzerinden veri sağlanırken veri paketlerinin binary olarak yollandığı ortamlarla sık karşılaşılmaktadır.
GO Dilinde Byte
GO dilinde byte ifadesi uint8 bir ifadedir. Yani 8bit'lik pozitif integer(tamsayı) ifadedir. Byte ifadesnini değer aralığı 0 ile 255 arasında olabilir.

Görselde gördüğünüz üzere 170 sayısının bayt cinsinden yazımı 10101010 olacaktır. Sekiz adet 1 ve 0 değeri alabilen dijital haneden oluşur. En sağdan en sola doğru oluşabilecek ifadeler hesaplandığında her bir hane 2'nin bir kuvveti olacaktır.
unicode bir karakter 32bit karakter yapısında veriyi saklar yani 4 bayt bir değer ifade eder. Fakat, eğer ifademiz yalnızca 8bit yani 1 bayt ise yine de ifademizi bu şekilde fazladan 3 bayt ile mi göstermemiz gerekir? Misal, ASCII karakterler 1 bayt ile gösterilebilir.Tam da bu noktada GO dilinde byte ifadesi devreye girer.
String Ifadelerin Bayt Dilimleriyle Gösterimi
Teoriden pratiğe geçerek işimizi kolaylaştıralım :
func main() {
s := "Merhaba programlama!"
bs := []byte(s)
// byte string ifadesini metin olarak görüntüle
fmt.Printf("%s", bs)
// byte ifadesini deger olarak görüntüle
fmt.Printf("%d", bs)
}
Bu ifadeyi go run ./main.go diyerek çalıştırdığımızda ekranımzda şu şekilde bir çıktı elde edeceğiz:
Merhaba programlama!
[77 101 114 104 97 98 97 32 112 114 111 103 114 97
109 108 97 109 97 33]%
Her bir karakteri öncelikle 8bit değerlere çevirir. Bu değerlerin bir ve sıfır değerlerine göre yazımı sonucunda 0-255 arasında bir değer ataması yapar. Misal M harfi için 77 değeri ortaya çıkar. a harfi için ise 97 değeri atanır. space (Boşluk) için 32 değeri atanır. Her bir karaktere bir 8bit'lik yani 1bayt'lık değer ataması sağlanır.
non-ASCII Değerlerin Bayt Dilimleri Gösterimi
ASCII ifadeler haricinde kalan ifadelerin bayt cinsinden gösterimi ile ilgili bir örnek gerçekleştirelim:
func main() {
bs := []byte("◺")
fmt.Println(bs) // Output: [226 151 186]
s := string(bs)
fmt.Println(len(s)) // Output: 3
}
Bu ifadeyi ekrana bastırdığımızda aşağıdaki çıktıyı elde edeceğiz:
[226 151 186]
3
Burada dikkat etmemiz gereken ilk durum üçgen ifadesinin üç bayt bir ifade olarak gösterildiğidir. Yani 3 adet byte değeri ile gösterilir. Bunu da len(s) ifadesinin sonucunda görebilirsiniz.
Daha fazla bilgi için:
Scopes
GO programlama dilinde süslü parantez ifadeleri ile {} ifade edilen scope (kapsamlar) ile değişkenlerin fonksiyon içinden ve dışından erişimleri kontrol edilir.
Örneğin :
package main
import "fmt"
func main(){
CallFromOutside()
}
func ScopeSample() {
// ilk scope
var birinci_degisken, ikinci_degisken int = 88, 95
fmt.Printf("Birinci Degisken : %d", birinci_degisken)
fmt.Printf("İkinci Degisken : %d", ikinci_degisken)
}
func CallFromOutside() {
// ikinci scope
fmt.Printf("%d", birinci_degisken)
}
Bu kodu çalıştırmak istediğimizde undefined: birinci_degisken hata mesajı ile karşılaşırız.
Biz birinci_degisken ifadesini ScopeSample içerisinde tanımlamıştık ve diğer bir scope içerisinde yer alan CallFromOutside fonksiyonu ifadesinin scope'undan çağırdığımızda birinci_degisken isimli bir değişkenimizin olmadığını belirtir. Çünkü bu ifade yalnızca ScopeSample isimli fonksiyon çalıştırıldığında geçici olarak oluşturulur ve bu fonksiyonda bir return ifadesi olmadığından dolayı scope dışına değer verilmez. Bundan dolayı birinci_degisken (yerel) local bir ifadedir.
Global Scopes
global değişken ifadeleri scope dışında tanımlanır ve scope içinden erişim sağlanabilir.
Örnek :
package main
import "fmt"
// global variable
var fruit = "Avacado"
func CallGlobal() {
fmt.Printf("My favourite fruit is %s \n", fruit)
}
func CallGlobalSecondTime() {
fmt.Printf("I like %s very much. \n", fruit)
}
func main(){
CallGlobal()
CallGlobalSecondTime()
}
Bu ifadeyi çalıştırdığımızda ise fruit isimli global değişkenimiz her iki fonksiyondan da çağrılabilir. Bu sayede ekranda iki farklı çıktı görüyoruz:
My favourite fruit is Avacado
I like Avacado very much.
Nested Scopes
İç içe geçmiş nested ifadelerde global ifadeler ve kapsayıcı scope içinde yer alan diğer local ifadelere erişim sağlanabilir.
Örnek :
package main
import "fmt"
var fruit = "Avacado"
func main(){
}
func NestedScope() {
drink := "apple juice"
vegetable := "tomato"
needDrink := false
needFruit := true
if needDrink {
fmt.Printf("Drink %s \n", drink)
} else {
fmt.Printf("Dont Drink %s eat %s \n", drink, vegetable)
}
if needFruit {
fmt.Printf("Get me %s", fruit)
}
}
Bu ifade çalıştırıldığında ise şu çıktıyı alırız:
Dont Drink apple juice eat tomato
Get me Avacado
Yorum İfadeleri - Comments
Yorum ifadeleri, GO programlama dilinde iki farklı şekilde karşımıza çıkmaktadır.
- Tek Satır Yorum İfadeleri (Line Comments)
- Çok Satır Yorum İfadeleri (Multiline Comments)
Tek Satır Yorum İfadeleri
Tek satır yorum ifadeleri, işleme dahil olmasını istemediğiniz ve koda ait tek satırlık yorumların olduğunu belirtmek için kullanılır. Satırın başına // iki adet eğik çizgi (double slash) ile belirtilir.
// Tek satır yorum ifadesidir
Çok Satırlı Yorum İfadeleri
Çok satırlı yorum ifadeleri, geniş açıklama yapılmak istendiğinde ilk satıra /* ve son satırın bitirişine de */ eklemek suretiyle yazılan yorum ifadeleridir.
/* Ilk satır yorum
ikinci satır yorum
son satır yorum */
Yorum İfadelerinin Konumları
Yorum ifadeleri işleme dahil etmek istediğimiz kodun :
- alt
- üst
- sağ
yanında yer alabilir.
package main
import "fmt"
func main(){
EkranaYazdir()
}
func EkranaYazdir(){
/* Ekrana yazdır programımız,
Yorum ifadeleri haricinde
işleme dahil olan herşeyi
ekrana yazdıracaktır.*/
//isim ve soyisim degiskenleri tanımladık.
var name string = "Gokay" // ad degiskeni
var surname string = "BURUC"// soyad degiskeni
// ekrana isim ve soyisim degiskenlerini yazdırıyoruz
fmt.Println("ISIM:", name , "SOYISIM:",surname)
// islem bitisinde terminal üzerinden go run dosyaAdi yazarak calistiriyoruz.
}
Kodumuzu çalıştırdığımızda aşağıdaki ekran çıktısını alacağız.
ISIM: Gokay SOYISIM: BURUC
Yorum satırlarının hiçbir işlemimize dahil olmamıştır. Koda açıklayıcı yorum satırları bırakmanın önemini yeri geldikçe vurgulayacağız.
GO Dilinde Veri Tipleri
GO programlama dilinde standart veri tipleri varsayılan olarak mevcuttur. Ayrıca kullanıcılar tarafından da veri tipi tanımlaması yapılabilmektedir. Bir değişken tanımlanırken herhangi bir veri tipi belirtilmezse GO en uygun veri tipini otomatik olarak atar.
Basit Veri Tipleri
| Değiken Adı | Değişken Açıklaması | Örnek İfade |
|---|---|---|
float | Ondalıklı Sayı ifadeleri | var x float64 = 13.21 |
int | Tam Sayı ifadeleri | var y int = 5 |
bool | Mantıksal Sınama ifadeleri | var existence bool = true |
string | Metin ifadeleri | var selamlama string = "Merhaba dünya!" |
rune | Karakter ifadeleri | Item3.2 |
byte | Byte Cinsinden ifadeler | Item3.2 |
Karmaşık Veri Tipleri
| Değiken Adı | Değişken Açıklaması | Örnek İfade |
|---|---|---|
array | Dizi ifadeleri | var myarr = [3]int{3,12,21} |
slice | Dizi kesiti ifadeleri | var myslice := myarr[0:2] |
map | Hashmap ifadeleri | var city = [string]int{"Edirne":22, "Kırklareli":39,"Tekirdağ":59 } |
struct | Struct ifadeleri | var person = Person{"Gokay","BURUC",1987} |
interface | Interface ifadeler | Item3.2 |
Veri Tiplerinin Belirtilmesi
Veri tiplerinin tanımlanması, işleme girecek öğelerin hafızada hangi tipte tutulacaklarına, hangi noktada saklanacaklarına ve ne kadar hafıza kaplayacaklarına dair bilgi vermektedir. Yani bilgisayara gönderilecek veriye ait ön bilginin yer aldığı bir irsaliye fişi vermek gibi düşünebiliriz. Birazdan sana float32 tipinde bir veri göndereceğim ve bu hafızanda 32 bitlik verilerde -3.4e+38 to 3.4e+38. sayı aralığında saklanacaktır. Bundan büyük değerler için float64 ifadesi kullanılabilir.
| Veri Tipi | Bit / Bayt Cinsinden Konumu | Değer Aralığı |
|---|---|---|
int | 32bit sistem veya 64bit sisteme göre GO tarafından otomatik seçilir | -2147483648 ile 2147483647 aralığında (32bit) -9223372036854775808 ile 9223372036854775807 aralığında(64bit) |
int8 | 8bit / 1 bayt | -128 ile 127 aralığında |
int16 | 16bit / 2 bayt | -32768 ile 32767 |
int32 | 32bit /4 bayt | -2147483648 ile 2147483647 |
int64 | 64bit / 8 bayt | -9223372036854775808 ile 9223372036854775807 |
Yukarıdaki örnekte görüleceği üzere int cinsinden bir veri tanımlayıp değer ataması yapmazsak eğer 32bit bir sistem kullanıyorsak bu veri için 4bayt hafıza alanı ayrılacak ve bu veri -2147483648 ile 2147483647 aralığında olmak zorunda kalacaktır. Bu veri tipine daha büyük veya daha küçük bir değer atayamacağız. Bir ya da birkaç yüz tane veri ile çalışırken bu ufak hafıza değerleri çok büyük problemler yaratmayacaktır. Fakat milyonlarca verinin yer aldığı veri setleri ile çalışırken bu veri tiplerinin doğru tanımlanması hafıza ve performansı doğrudan etkileyecek öğeler haline gelecektir.
Veri Tiplerinin Çeşitleri
Tanımlanma yöntemine göre veri tipleri ikiye ayrılmaktadır:
| Veri Tipi | Açıklama |
|---|---|
| Varsayılan Veri Tipleri (Default) | Program kurulduğu an itibariyle tanımlı olan veri tipleridir. |
| Kullanıcı Tanımlı Veri Tipleri (User-Defined) | Kullanıcı tarafından sisteme tanıtılan ve kullanılan veri tipleridir. |
Veri Tiplerinin Gruplanması
Genel olarak veri tipleri dört ana başlık altında toplanabilir. Bu veri tipleri aşağıdaki gibidir.
- Basic type (Temel Veri Tipi ) : Sayılar, metin ve bool (mantıksal evet hayır ifadeleri) ifadeler bu kategoriye girer. int, float, string, bool, complex
- Aggregate type (Toplanmış Veri Tipi): Dizi ve yapılar bu kategoriye girer. array, struct
- Reference type (Referans Veri Tipi): İşaretçiler, dilimler, haritalar, işlevler ve kanallar bu kategoriye girer. pointer, slice, map, function, channel
- Interface Type (Arayüz Veri Tipi) : Arayüzler bu veri tipinde yer alır. interface
| Veri Tipi | Açıklama | Veri Tipi İsimleri |
|---|---|---|
| Temel Veri Tipi | Sayılar,ondalıklı ifadeler, kompleks sayılar, metinler ve mantıksal ifadeler | int, float, string,bool,complex |
| Toplanmış Veri Tipleri | Diziler ve yapı ifadeleri bu kategoriye girer | array,struct |
| Referans Veri Tipleri | İşaretçiler, dilimler, haritalar, işlevler, kanallar bu veri tipine girer | pointer,slice,map,function,channel |
| Arayüz veri Tipi | Arayüz ifadeleri bu veri tipine girer | interface |
1. Temel Veri Tipleri (Basic Types)
Temel veri tipleri, programlama dillerinde olmazsa olmaz veri tipleridir. Dört işlem, metin işlemleri, mantıksal sınamalar ve diğer işlemleri yapmak için mutlak gerekli veri tipleridir.
- String (string)
- Int (int8, int16, int32, int64)
- Float ( float32, float64)
- Boolean (bool)
- Complex(complex)
Int (Integer-Tamsayı)
Tam sayı değerlerlerinin program hafızasında tutulduğu veri tipidir.
int tanımlaması aşağıdaki şekilde yapılır.
var degiskenAdi int = 32
İkinci bir tanımlama yöntemi ise şu şekildedir.
degiskenAdi := 35
Tamsayı veri tipleri ile bir dört islem uygulaması:
package main
import ("fmt")
func main(){
var matematikNotu int8 = 85
var edebiyatNotu int8 = 92
fizikNotu = 65
fmt.Println(matematikNotu, edebiyatNotu, fizikNotu)
// ortalama
fmt.Println((matematikNotu + edebiyatNotu + fizikNotu)/3)
}
Yukarıda tanımlandığı gibi sisteme int ifadesi ile bir değişken tanımladığımızda bu değişken sistem hafızasında integer olarak tutulur ve bu şekilde işlemlere dahil edilir.
Yukarıdaki örnek incelendiğinde matematikNotu isimli bir int ifadesi tanımladık. Bu değerler 1 ile 100 arasında olacağından dolayı veri tipi olarak int8 seçtik. Bu seçim sayılarımızın -127 ve 128 arasında olacağından dolayıdır. Aynı şekilde edebiyatNotu isimli bir int8 ifade tanımladık.
Int Veri Tipinin Hafızada Kapladığı Yer
Tanımladığımız verilerilerin hafızada kapladığı yer, programcıların en çok kafasını kurcalayan sorulardan biridir. Yazdığınız programın performansını etkiler.
Hafızada kapladığı yere göre int veri tipleri aşağıda yer almaktadır.
| Veri Tipi | Açıklama | Veri Aralığı |
|---|---|---|
| int8 | 8-bit signed integer | -127 ve 128 değerleri arasında yer alır ( signed integer) |
| int16 | 16-bit signed integer | -32,768 ve +32,767 değerleri arasında yer alır |
| int32 | 32-bit signed integer | -2147483648 to 2147483647 değerleri arasında yer alır |
| int64 | 64-bit signed integer | -9,223,372,036,854,775,808 ve 9,223,372,036,854,775,807 değerleri arasında yer alır. |
| uint8 | 8-bit unsigned integer | 0 ve 255 değerleri arasında yer alır |
| uint16 | 16-bit unsigned integer | 0 ve 65535 değerleri arasında yer alır |
| uint32 | 32-bit unsigned integer | 0 ve 4294967295 değerleri arasında yer alır |
| uint64 | 64-bit unsigned integer | 0 ve (2^64) değerleri arasında yer alır |
| int | Hem int hem de uint, 32 veya 64 bit olmak üzere aynı boyutu içerir. | |
| uint | Hem int hem de uint, 32 veya 64 bit olmak üzere aynı boyutu içerir. | |
| rune | İnt32 ile eşanlamlıdır ve ayrıca Unicode kod noktalarını temsil eder. | |
| byte | uint8 ile eşanlamlıdır. | |
| uintptr | İşaretsiz bir tamsayı (unsigned integer) türüdür. Genişliği tanımlanmamıştır, ancak bir işaretçi değerinin tüm bitlerini tutabilir. | |
32 bit , 64 bit veri tiplerinin mevcut olmasının temel sebebi tutulacak verinin sistemde kaplayacağı yeri düzenlemek içindir.
Birden çok veri tipinin olması sistem hafızasında kaplanacak yer sorunu ile ilgilidir.Milyonlarca sayıdan oluşan bir veri setinin olduğunu düşünelim. Bu sayıların ise 1 ile 1000 arasında yer aldığını düşünelim. Eğer sisteme int16 ( 16 bit tamsayı ) veri tipinde veri girileceğini belirtmez isek sistem bu veri tipleri için int64 (64 bit tamsayı ) imiş gibi muamele edip her bir sayı için 2^64 'lük bir yer ayıracaktır. Böylece yapılan işlemler için ayrılan hafıza muazzam boyutlarda artacaktır.
uintveri tipi yalnızca pozitif değerleri alabilenintveri tiplerini tanımlar. Bir veri tipinin başınauifadesi geldiğinde bu unsigned anlamış taşır ve veri tipinde negatif değerler alınamayacağını belirtir.
Float (Ondalık)
Virgül ya da nokta ile ayrılmış ondalıklı değerleri ifade eder.
int ifadelerinde olduğu gibi float ifadelerde de hafızada kapladığı yere bakılarak bir seçim yapılır.
var sicaklik float32 = 33.25
Diğer bir tanımlama ise şu şekilde yapılabilir.
sicaklik := 33.25
Veri tipi tanımlaması aşağıdaki tabloya göre belirlenir.
| Veri Tipi | Açıklama | Değer Aralığı |
|---|---|---|
| float32 | 32-bit IEEE 754 floating-point number | İşaretli bir 32 bit tamsayı değişkeni maksimum 231 − 1 = 2,147,483,647 değerine sahipken, bir IEEE 754 32 bitlik taban-2 kayan noktalı değişkenin maksimum değeri (2 − 2−23) × 2127 ≈ 3.4028235 × 1038 . |
| float64 | 64-bit IEEE 754 floating-point number |
float ifadelerle dört işlem ile ilgili bir kod uygulaması aşağıda yer almaktadır.
package main
import "fmt"
func main() {
//float verilerin tanımlanması
a := 20.45
b := 34.89
//Cikartma islemi
c := b - a
// Sonuc Görüntülemesi
fmt.Printf("Result is: %f", c)
// Vier türünün görüntülenmesi
fmt.Printf("\nThe type of c is : %T", c)
}
Örneğimizde a ve b olmak üzere iki adet float değişken tanımlanmıştır. İki değere de float64 atanmıştır.
HATIRLATMA: Eğer float ifade tanımlanırken 32bit,64bit vs. seçilmeden tanımlama yapılırsa GO otomatik olarak float64 seçimi yapar.
String (Metin)
String veri türü metin verilerinin bir dize olarak tutulduğu veri tipidir. Bu veriler baytlardan oluşur.
string ifadeler aşağıdaki şekilde tanımlanır.
var degiskenAdi string = "metin ifadesi"
ikinci bir tanımlama yöntemi ise aşağıdaki şekildedir.
degiskenAdi := "metin ifadesi"
Şimdi bir uygulama örneği ile devam edelim.
package main
import "fmt"
func main() {
stringIfadesi := "GOKAYBURUC.DEV"
// String ifadesinin uzunluğunun görüntülenmesi
fmt.Printf("String ifadesinin uzunlugu:%d", len(stringIfadesi))
// String ifadesinin görüntülenmesi
fmt.Printf("String ifadesi: %s", stringIfadesi)
// String ifadesinin veri türünün görüntülenmesi
fmt.Printf("String ifadesinin tipi: %T", stringIfadesi)
}
Boolean (Mantıksal)
Bir veri tipinin mantıksal değerler olan true ya da false döndürdüğü ifadelerdir. Bir koşullu işlemin sonucu bilgisayar tarafından geçerli olduğu noktada true geçersiz olduğu noktada ise false değerini döndürür.
| Veri Tipi | Açıklama |
|---|---|
true | koşul sınamasının olumlu olması sonucu dönen değer |
false | koşul sınamasının olumsuz olması sonucu dönen değer |
Atama yöntemi :
var ehliyetMevcut = true
veya
ehliyetMevcut := false
şeklindedir.
Boolean Uygulaması
true ve false degerleri atanmış degiskenlere ait örnek aşağıda yer almaktadır.
package main
import "fmt"
func main(){
var a = 25
var b = 45
a=25
b=45
//mantıksal sorgu operatörleri
sorguBir := (a > b) // a büyüktür b
sorguİki := (a < b) // a kücüktür b
sorguUc := (a == b) // a eşittir b
sorguDort := (a != b) // a eşit değildir b
fmt.Println(sorguBir)
fmt.Println(sorguİki)
fmt.Println(sorguUc)
fmt.Println(sorguDort)
// Deger ataması
var ehliyet bool = true
var ruhsat bool = false
fmt.Printf("Ehliyet : %v\nRuhsat : %v \n", ehliyet,ruhsat)
}
Yukarıdaki örneğimizde mantıksal sorgu operatörleri kullanılmıştır.
Bu operatörler:
- Eşitlik ( == )
- Eşit Olmama (!=)
- Büyüklük ( > )
- Küçüklük ( < )
- Büyük ve Eşitlik ( >= )
- Küçük ve Eşitlik ( <= )
olarak sıralanabilir.
Bu sorguların sonuçları
truevefalseolarak döndürülür.
Bizim örneğimizde :
//mantıksal sorgu operatörleri
sorguBir := (a > b) // a büyüktür b
sorguİki := (a < b) // a kücüktür b
sorguUc := (a == b) // a eşittir b
sorguDort := (a != b) // a eşit değildir b
mantıksal sorguları mevcuttur ve bu sorgular önermenin doğruluğuna bağlı olarak true ve false olarak değer döndüreceklerdir. Misal örneğimizde a sayısı 25 ve b sayısı 45 olduğu için a < b önermesi true ve a > b önermesi false döndürecektir.
Complex ( Karmaşık )
Matematik gösterimi i ve j ye bağlı olan ve iki boyutlu bir analitik düzlem üstünde gösterilen sayılar için sistem hafızasında tutulan veri tipidir.
Komplek Sayi j + Xi seklinde yazılır. Yani sayının bir imajiner bir de reel kısmı mevcuttur. Sonuçlarımız da bu şekilde döndürülecektir.
Yazım şekili
var degiskenAdi complex64 = complex(2,15)
ikinci yazım şekli
var degiskenAdi := complex(2,15)
Komplex sayı veri tipine ait tablo aşağıda yer almaktadır.
| Veri Tipi | Açıklama |
|---|---|
| complex64 | float32 değerini reel (j) ve imajiner (i) kısımda tutan deger |
| complex128 | float64 değerini reel (j) ve imajiner(i) kısımda tutan değer |
Aşağıda açıklama örneğimiz yer almaktadır.
// Go program to illustrate
// the use of complex numbers
package main
import "fmt"
func main() {
var a complex128 = complex(6, 2)
var b complex64 = complex(9, 2)
// ilk cıktılar
fmt.Println(a)
fmt.Println(b)
// Display the type
fmt.Printf("The type of a is %T and "+
"the type of b is %T ", a, b)
}
Örneğimizde a ve b olarak iki adet kompleks sayı tanımladık.
// kompleks sayı tanımlamaları
var a complex128 = complex(6, 2)
var b complex64 = complex(9, 2)
Kodu çalıştırdığımızda ekran çıktılarımız aşağıdaki gibi olacaktır.
(6+2i)
(9+2i)
The type of a is complex128 and the type of b is complex64%
Kaynakça
- https://www.geeksforgeeks.org/data-types-in-go/
- https://go.dev/tour/basics/11
- https://www.programiz.com/golang/data-types
Arrays - Diziler
Dizi Kavramı
Dizi birden çok elemanın bir arada olduğu elemanlar kümesi olarak ifade edilir.
GO programlama dilinde diziler, aynı veri türüne ait elemanlar kümesidir. Yani string veri türündeki ifadelerin bir araya gelmesinden oluşan bir küme dizi string türünde bir dizidir. Aynı şekilde int , float , bool ifadeleri de birer dizi oluşturabilir.
DİKKAT: GO programlama dili farklı veri tiplerinin bir arada olduğu dizilere izin vermez.
Dizilerin Tanımlanması
Dizi tanımlanması sırasında iki farklı metod vardır:
- Öğe sayısı belirterek dizi tanımlama
- Öğe Sayısı belirtmeden dizi tanımlama
// oge sayısı belirterek dizi tanımlama
var arr = [3]string{}
var arr=[3]string{} ifadesinde bu dizi maksimum 3 ogeden olusacaktır diye oge sayisi belirttik. Dorduncu bir deger girilmeye calisildiginda dizi hata dondurecektir.
// oge sayisi belirtmeden dizi tanımlama
var arr2 = [...]int{111,209,301,498}
var arr2 = [...]int{111,209,301,498} ifadesinde ise girilecek oge miktarı önceden belirtilmemistir ve kullanıcıya bırakılmıştır. Ust limit yoktur.
Burada 3 elemanı olan ve string türünde veriler barındıracak bir dizi tanımladınız. Ama dizimizin henüz hiç elemanı yok.
Dizilerin Elemanları ve Dizi Indeksleri
Üç elemandan oluşan bir dizi tanımlayalım ve içine değerlerimizi atayalım.
func main(){
var firstArray = [4]string{"Edirne", "Kırklareli", "Tekirdağ", "Çanakkale"}
// array
fmt.Println(firstArray)
// zero indexed item
fmt.Println(firstArray[0])
// second indexed item
fmt.Println(firstArray[2])
fmt.Printf("Second indexed item in array:%s\n", firstArray[2])
}
Kod satırı çalıştırıldığında şöyle bir çıktı elde edeceksiniz:
[Edirne Kırklareli Tekirdağ Çanakkale]
Edirne
Tekirdağ
Second indexed item in array:Tekirdağ
Dizinin kendisi, sıfır index numaralı değeri, 2 index numaralı değeri ekranda görüntülenir.
HATIRLATMA: Programlama dillerinde, dizi elemanları sıfırdan başlayarak index değeri alırlar. Yani
adizisinin ilk değeria[0]değeridir.
Dizinin Öğelerini Değiştirmek
Dizi içinde bir değere yeni bir atama yapmak istediğimizde o değerin indeks numarasını belirterek doğrudan atama yapabiliriz. Dikkat etmemiz gereken en önemli konu dizi indeksinin 0 değeri ile başlamasıdır. Yani 4 elemanlı bir dizinin son elemanının dizi numarası indekslememiz 0 değerinden başladığı için 3 olacaktır.
package main
import "fmt"
func main() {
sehirler := [...]string{
"Kırklareli",
"Edirne",
"Çanakkale",
"Bursa",
"Tekirdağ",
"İstanbul",
"Kocaeli",
}
// dizinin boyutu
fmt.Println(len(sehirler))
sehirler[6] = "Paris"
fmt.Printf("sehirler 6 indexed item is : %s\n", sehirler[6])
}
Kodu çalıştırdığımızda ekranda şu şekilde bir çıktı alacaksınız:
7
sehirler 6 indexed item is : Paris
Yedi elemandan oluşan bir listemiz var ve indeks numarası 6 olan öğe paris olarak değiştirildi.
Out of Bounds (Sınırlama Dışında Değer) Hatası
Peki 3 elemanla sınırlanmış bir diziye bir tane dördüncü eleman atarsak ne olur? Kodumuzda dizi değerlerini yazdığımız kısma aşağıdaki kodu ilave edip tekrar çalıştıralım.
dizi[3] = "Amasya"
Kodumuzun son hali:
package main
import (
"fmt"
)
func main(){
var dizi = [3]string{}
dizi[0] = "Edirne"
dizi[1] = "Bursa"
dizi[2] = "Manisa"
dizi[3] = "Amasya" // yeni eklediğimiz değer
//dizinin goruntulenmesi
fmt.Println(dizi)
// dizinin birinci degeri
fmt.Println(dizi[1])
}
Kodu çalıştırdığımızda aşağıdaki hatayı alacaksınız.
invalid argument: index 3 out of bounds [0:3]
out of bounds hatası dizinin sınırlandığı değer sayısından fazla öğe girildiğini belirtir.
Siz dize tanımlarken 3 değerden oluşan bir dize tanımlayacağınızı belirttiniz fakat şimdi dördüncü elemanı tanımlıyorsunuz. Ya bu kuralı belirtmeyin açık uçlu bir dize tanımlayın ya da sayıyı arttırın.
Dizilerde Varsayılan Değerler
Bir diziye ait değerlere atama yapılmadığında GO bu atanma yapılmayan değerler için varsayılanlar atar. Siz eğer bu değerleri unuttuysanız ya da bilerek vermediyseniz GO buraya uygun değer atamasını kendiliğinden sağlar.
Bu varsayılanlar :
intvefloatdeğerler için varsayılan : 0stringifadeler için varsayılan : null (boşluk karakteri)boolifadeler icin varsayilan : false
değerleridir.
package main
func main() {
arr1 := [5]int{} //not initialized
arr2 := [5]int{1, 2} //partially initialized
arr3 := [5]int{1, 2, 3, 4, 5} //fully initialized
fmt.Println(arr1)
fmt.Println(arr2)
fmt.Println(arr3)
city1 := [3]string{}
city2 := [3]string{"Bursa", "İstanbul"}
city3 := [3]string{"Bursa", "İstanbul", "Edirne"}
fmt.Println(city1)
fmt.Println(city2)
fmt.Println(city3)
derece := [3]float64{0: 22.4, 2: 11.3}
fmt.Println(derece)
yoklama := [4]bool{true, false, 3: false}
fmt.Println(yoklama)
}
Kodu çalıştırdığımızda ekran çıktımız aşağıdaki gibi olacaktır:
[0 0 0 0 0]
[1 2 0 0 0]
[1 2 3 4 5]
-----------------------------
[ ]
[Bursa İstanbul ]
[Bursa İstanbul Edirne]
-----------------------------
[22.4 0 11.3]
-----------------------------
[true false false false]
Burada yeni bir ifade görüyoruz o da derece := [3]float64{0: 22.4, 2: 11.3} ifadesindeki 0:22.4 yazımıdır. Bu yazım sıfır numaralı indekse 22.4 ifadesinin ata anlamına gelir.
Çok Boyutlu Diziler - MultiDimensional Arrays
Peki bizim diziler hep tek şekilde mi olacak hiç dizi değerleri içerisinde birden çok dizi olan matrisleri kullanamayacak mıyız? Bu soruya da yanıt verelim.
diziAdı := [dimension1][dimension2][dimension3][dimension...]string
Bir çok boyutlu dizi tanımlarken boyut miktarı kadar [] ifadesi ve sonrasında string, int,float, bool gibi veri tipleri bildirilerek tanımlama yapılır.
hayvanlar := [3][2]string{
{"aslan", "kaplan"},
{"kedi", "köpek"},
{"güvercin", "kartal"}, // sondaki virgül gereklidir
}
3 satır ve 2 sütundan oluşan bir dize tanımladık. Dizimiz 2 boyutludur. Eğer başka bir köşeli parantez ve değer daha eklersek dizimiz 3 boyutlu bir dizi olacaktır.
DİKKAT:
{"güvercin", "kartal"},ifadesinde yer alan sondaki virgül,gereklidir.Alt alta satır yazarken en son satır virgül ile bitirilir. GO yazım kuralları gereğince dizi tanımlaması sırasında birden çok satır kullanılırsa virgül ile bitirilmelidir.
Şimdi dizimizi ekrana yazdıralım:
package main
import "fmt"
func main(){
hayvanlar := [3][2]string{
{"aslan", "kaplan"},
{"kedi", "köpek"},
{"güvercin", "kartal"}, // sondaki virgül gereklidir
}
// dizi
fmt.Println(hayvanlar)
// dizinin ikinci elemanının birinci degeri
fmt.Println(hayvanlar[2][1])
}
Ekran çıktımız:
[[aslan kaplan] [kedi köpek] [güvercin kartal]]
kartal
şeklinde olacaktır.
Dilimler (Slices)
slice ifadeleri array ifalerinin bir segmenti(alt bölümü)dür. array ifadelerinde olduğu gibi slice ifadeleri de indekslenebilir(numaralandırılabilir) ve belirli bir uzunlukları vardır. array ifadelerinden farklı olarak bu uzunluklar değiştirilebilirler.
Bir slice ifadesi aşağıdaki şekilde tanımlanır.
var slicename [slicelength]slicetype
Bir örnek ifade ile göstermek gerekirse :
package main
func main(){
var x []float64
fmt.Print(len(x))
}
Yukarıdaki ifadede sıfır öğeli bir slice ifadesi oluşturuldu. slice ifadeleri oluşturulurken değer ataması yapılmaz ise slice ifadesinin uzunluğu sıfır kabul edilir.
Diğer bir slice oluşturma yöntemi ise make() yardımıyla slice oluşturmaktır.
func main(){
myslice := make([]int)
}
slice ifadesini oluşturken eğer alacağı değerleri de belirlemek istiyorsanız {} ifadesini slice ifadesinin sonuna eklenmeniz gerekir.
func main(){
myarr := make([3]int{22,24,32})
// index 0 'dan index 2 değerine kadar
myslice := [0:2]
fmt.Print(myslice)
}
Slice ifadelerine Eleman eklenmesi
slice ifadelerine eleman ekleme işlemleri append() fonksiyonu aracılığıyla sağlanır.
func main() {
slice1 := []int{1,2,3}
slice2 := append(slice1, 4, 5)
fmt.Println("slice1 :" , slice1)
fmt.Println("slice2 :" , slice2)
}
append() fonksiyonu slice1 isimli ilk slice ifadesini temel alarak üstüne 4,5 değerlerini ekleyip yeni bir slice ifadesi olan slice2 ifadesini oluşturur.
Yukarıdaki ifadenin çıktısı :
slice1 : [1 2 3]
slice2 : [1 2 3 4 5]
şeklinde olacaktır.
Mevcut bir slice ifadesine tekrar atama yapmak için aşağıdaki şekilde bir yazım gerçekleştirmeniz gerekir.
func main(){
cities := []string{}
// WARN: Tanımlama sonrasında değer atamada : işareti kullanılmaz!
cities = append(cities, "Bursa", "Edirne", "İstanbul")
fmt.Println(cities)
}
DİKKAT:
citiesifadesinde tanımlama sonrasındaappend ()fonksiyonu kullanırken yalnızca=kullandık. değişken tanımlama sonrasında yapılan işlemlerde aynı değişken için:işareti kullanılmaz!
Yukarıdaki ifadeyi çalıştırdığımızda aşağıdaki çıktıyı elde ederiz :
["Bursa", "Edirne","İstanbul"]
slice ifadelerinin kopyalanması
slice ifadelerini kopyalamak için copy() isimli fonksiyon ifadesini kullanırız.
func main() {
slice1 := []int{1,2,3}
slice2 := make([]int, 2)
copy(slice2, slice1)
fmt.Println("slice1:", slice1)
fmt.Println("slice2:", slice2)
}
Bu fonksiyon slice1 isimli bir slice oluşturur ve slice2 ismiyle oluşturulan 2 adet değer alabilen fonksiyona slice ifadesini kopyalar. İki adet değer alabileceği için slice1 isimli slice öğelerinden 1,2 öğelerini kendi içine kopyalar. Kod çalıştırıldığında aşağıdaki sonuç elde edilir.
slice1: [1 2 3]
slice2: [1 2]
Bir slice ifadesinin içindeki değerleri fmt.Printf() aracılığıyla görüntülemek istedimizde %q argümanını kullanırız.
func main() {
cities := []string{}
cities = append(cities, "Edirne", "Bursa")
fmt.Printf("%q", cities)
}
Yukarıdaki işleme ait çıktı şu şekilde olacaktır:
["Edirne" "Bursa"]
Slice ifadelerini birbirine direkt olarak eklemek istedimizde aşağıdaki yazım biçimiyle bu işlemleri gerçekleştiriz.
func main() {
cities := []string{"Lüleburgaz", "Babaeski"}
otherCities := []string{"Havsa", "Uzunköprü"}
/* ... - ellipsis işaretiyle otherCities isimli slice
ifadesinin her elemanını tek tek
string olarak eklemek istediğimizi belirttik
*/
cities = append(cities, otherCities...)
fmt.Printf("%q", cities)
}
Çıktı şu şekilde olacaktır :
["Lüleburgaz" "Babaeski" "Havsa" "Uzunköprü"]
... (ellipsis) işareti ile dilimin her elemanını tek tek eklemek istediğimizi belirttik. Eğer böyle belirtmemiş olsak string ifadeleri içeren bir slice a slice ifadesi eklemeye çalışacağımız için hata mesajı alırdık. otherCities isimli slice içindeki string ifadelerini teker teker string ifadeleri olarak eklememiz için ifadeden sonra ... ellipsis işareti kullanmamız gerekir.
Slice ifadelerinin sıralanması
slice ifadelerinin sıralanması sırasında sort.Slice() isimli bir fonksiyon kullanılır.
func main(){
cities := []string{
"Bursa",
"İstanbul",
"Çanakkale",
"Balıkesir",
"Tekirdağ",
"Edirne",
"Kırklareli",
"Yalova",
"Kocaeli",
"Sakarya",
}
sort.Strings(cities)
// before sort cities
fmt.Printf("%q", cities)
}
Bu ifadeyi çalıştırdığımızda sort.Strings() fonksiyonu yardımıyla string ifadeleri alfabetik olarak dizilecektir. Burada dikkat etmeniz gereken konu Çanakkale ve İstanbul ifadelerindeki karakterler Türkçe karakterler olduğu için sıralamaya dahil edilmeyip sıralamanın en sonuna eklenir. Programlama dillerinde Türkçe karakterlerle sıralama işlemi yaptığınızda benzer durumlarla karşılaşabilirsiniz. Kodun çıktısı aşağıda verilmiştir.
["Balıkesir" "Bursa" "Edirne" "Kocaeli" "Kırklareli" "Sakarya" "Tekirdağ" "Yalova" "Çanakkale" "İstanbul"]%
Diğer ifadeleri sıralamak için sort kütüphanesindeki :
- sort.Strings()
- sort.Ints()
- sort.Float64s()
ifadelerini de pkg.go.dev - sort sayfasından araştırabilirsiniz.
slice ifadelerinin kapasitelerinin ölçülmesi
slice ifadelerinin kapasitelerinin ölçülmesi için cap() isimli bir fonksiyon kullanırız. cap() fonksiyonu ile len() fonksiyonunun farkı, cap() dizinin alabileceği azami eleman miktarını belirtirken, len() fonksiyonu dizinin mevcut eleman miktarını belirtir.
func main(){
firstslice := [4]int{12, 23, 44}
// myslice
myslice := firstslice[0:2]
fmt.Printf("%v \n", myslice)
fmt.Printf("Capacity : %d \n", cap(myslice))
fmt.Printf("Length: %d \n", len(myslice))
}
Yukarıdaki ifade çalıştırıldığında aşağıdaki ekran çıktısı ile karşılaşılır.
[12 23]
Capacity : 4
Length: 2
nil slice ifadeleri
Bir slice ifadesi oluşturulurken eğer sayısal bir değer belirtilmezse ifade nil yani elemansız kabul edilir. Bu slice ifadelerinin veri uzunluğu sıfır ve kapasiteleri sıfırdır.
func main() {
var z []int
fmt.Printf("Slice: %v \n", z)
fmt.Printf("len : %d \n", len(z))
fmt.Printf("cap : %d \n", cap(z))
if z == nil {
fmt.Println("Z is nil!")
} else {
fmt.Println("This is not nil!")
}
}
Bu kodun çıktısı aşağıdaki şekilde olacaktır.
Slice: []
len : 0
cap : 0
Z is nil!
Maps
map ifadeleri, eleman çiftlerinin sıralanmamış koleksiyonlarından oluşan özelleşmiş veri yapılarıdır.
Bir key (anahtar) ve bir value (değer) setinden oluşurlar.
Ayrıca associative array ve dictionary isimleriyle de bilinirler.Diğer programlama dillerinde bu yapılar hash
,dictionary, HashTable gibi isimlerle de bilinirler.
Değerleri hızlı bir şekilde aramak için idealdirler. Bir key verildiğinde value hızlı bir şekilde temin edilir.
Maplerin Tanımlanması
Bir map yapısı aşağıdaki formda tanımlanır.
var map1 map[keytype]valuetype
Bir örnek vermek gerekirse :
var map1 map[string]int
DİKKAT: Anahtar
keyifadeleri veri tipi olarakstring,float,intgibi yapılar alabilir. Fakat,array,slice,structgibi ifadelerkeyolarak kullanılmazlar.
Bir map ifadesi dinamik olarak genişletilebilir. Tanımlandığı sırada aldığı değer miktarı eğer başlangıçta belirtilmediyse istenilen miktarda öğe eklenebilir.
map ifadelerinin farklı kullanım alanları mevcuttur. Özellikle birden çok fonksiyonu değer ataması yoluyla şubelendirmek ve kollara ayırmak için de ideal yapılardır.
package main
import "fmt"
func main() {
// map1 isimli bir map
var map1 map[string]int
//map2 isimli bir map
var map2 = map[string]int{"one": 1, "two": 2}
// yeni bir map oluştur
mapCreated := make(map[string]float32)
// map1 i map2 ye kopyala
map1 = map2
// anahtar ve değer ciftleri ata
mapCreated["key1"] = 4.5
mapCreated["key2"] = 3.14159
// anahtar ve değer ciftleri ata
map1["two"] = 3
// Ekrana görselleri yazdır
fmt.Print(map1, "\n")
fmt.Print(map2, "\n")
fmt.Print(mapCreated, "\n")
}
Kodu parçalara ayırarak inceleyelim :
// map1 isimli bir map
var map1 map[string]int
Burada map1 isimli bir map ifadesi oluşturuyoruz.key değerleri string ve value değerleri int olacak şekilde veri girileceğini belirtiyoruz.
//map2 isimli bir map
var map2 = map[string]int{"one": 1, "two": 2}
Burada ise map2 isimli bir map oluşturuyoruz. key ifadeleri string ve value ifadeleri int olacağını belirtiyoruz. {} işareti ile değer çiftlerini atıyoruz.
// yeni bir map oluştur
mapCreated := make(map[string]float32)
Burada make() ile boş bir map oluşturuyoruz. mapCreated isimli bu boş map 'in key değerleri string ve value değerleri float32 olacak şekilde düzenleme yapıyoruz.
// map1 i map2 ye kopyala
map1 = map2
Burada ise map1 isimli boş map ifadesinin içerisine map2 deki ifadeleri kopyalıyoruz.
// anahtar ve değer ciftleri ata
mapCreated["key1"] = 4.5
mapCreated["key2"] = 3.14159
// anahtar ve değer ciftleri ata
map1["two"] = 3
Daha önceden boş bir map olarak oluşturduğumuz mapCreated içerisine key1 ve key2 anahtarlarına değer atayarak giriş yapıyoruz.Aynı işlemi map1 için de tekrarlıyoruz.
fmt.Print(map1, "\n")
fmt.Print(map2, "\n")
fmt.Print(mapCreated, "\n")
Oluşturduğumuz map ifadelerini fmt.Print() ifadesi yardımıyla görüntülüyoruz ve aşağıdaki gibi bir ifade ortaya çıkartıyoruz:
map[one:1 two:3]
map[one:1 two:3]
map[key1:4.5 key2:3.14159]
Map İfadelerinin Kapasitesi
array ifadelerinin aksine map ifadeleri değer eklendikçe artacak şekilde tasarlanmıştır. Eğer siz bir kapasite sınırı koymak isterseniz bu opsiyon için aşağıdaki yapıyı kullanmanız gerekir.
make(map[keytype]valuetype, cap)
Burada make() ile map ifadesi oluştururken aşağıdaki parametleri belirtiyoruz
keytype: Anahtar ifadesinin veri türüvaluetype: Değer ifadesinin veri türücap: Kapasite ifadesinin sayısal miktarı
Kapasite miktarını belirlemek bir diziye girilebilecek maksimum key:value çiftini belirleyeceği için performans uygulamalarında kullanılır.
Örnek ifade:
package main
import "fmt"
func main(){
trakya := make(map[string]int,5)
trakya["Edirne"] = 22
trakya["Kırklareli"] = 39
trakya["İstanbul"] = 34
trakya["Tekirdağ"] = 59
trakya["Çanakkale"] = 16
fmt.Print(trakya)
}
Bu ifadeyi çalıştırdığımızda aşağıdaki şekilde bir çıktı elde ederiz:
map[Edirne:22 Kırklareli:39 Tekirdağ:59 Çanakkale:16 İstanbul:34]%
slice İfadelerinin Veri Ataması
Bir map ifadesinde value lar int, float32, string gibi primitive type olarak yazılabilirler. Değer ifadeleri ise string, int,float32 gibi primitive type ifadelerin yanı sıra slice, array,map,func,struct gibi complex type ifadeler de olabilir.
// []int slice ifadesi döndürür
mp1 := make(map[int][]int)
// *[]int slice ifadesi döndürür
mp2 := make(map[int]*[]int)
Yukarıdaki örneklerdeki ifadlerde value ifadeleri slice tipinde değer alırlar.
map ifadelerinden öğe silinmesi
Bir map ifadesinden öğe silmek için delete() fonksiyonunu kullanırız. Kullanım şekli aşağıdaki gibidir :
delete(mapName, mapKey)
Örnek ifademiz ile konuyu pekiştirelim.
func DeleteItemMap() {
cities := map[string]int{
"Edirne": 22,
"Bursa": 16,
"Kırklareli": 39,
"Tekirdağ": 59,
"Çanakkale": 17,
}
fmt.Printf("%v \n ", cities)
// cities : mapName ve Tekirdağ : mapKey
delete(cities, "Tekirdağ")
fmt.Printf("%v \n ", cities)
}
Bu ifadede "Tekirdağ" isimli değişkeni cities isimli map ifademizden sildik. Ekran çıktımız ise aşağıdaki şekilde olmuştur.
map[Bursa:16 Edirne:22 Kırklareli:39 Tekirdağ:59 Çanakkale:17]
map[Bursa:16 Edirne:22 Kırklareli:39 Çanakkale:17]%
If Else Statement
if-else ifadeleri iki ya da daha fazla seçenekli şarta bağlı durumun olduğu olay yapılarında, programın işleyişini bu kollarda şarta uyan koldaki ifadeler ile devam edilmesini sağlamak amacıyla ortaya çıkarılmış ifadelerdir.
Bu ifadeler verilen değerin sağlandığı durumda true ve sağlanmadığı durumda false değeri verilecek şekilde düzenlenmiştir.
func main(){
var (
Temperature int = 42
)
if Temperature >= 30 {
fmt.Println("Çok sıcak!")
} else {
fmt.Println("Serin")
}
}
ifademizde Temperature isimli bir değişkenimiz mevcuttur. Bu değişkenin sayısal değeri 42'dir. conditional statement ifademiz olan if {} else {} bloklarında iki farklı şarta bağlı ifademiz mevcuttur.
İlk şartımız:
if Temperature >= 30 {
fmt.Println("Çok sıcak!")
}
şeklindedir. Eğer Temperature değişkeni 30 dereceye eşit ya da daha büyükse bu scope içinde kalan işlemleri gerçekleştir.Eğer ifademiz 30 değerinin altında olsaydı else bloğundaki ifadeleri gerçekleştirirdi:
else {
fmt.Println("Serin")
}
else if ifadeleri
Bu ifadeler genellikle birden çok seçeneği opsiyonel olarak seçmek amacıyla yazılırlar. Standart if else ifadelerinden farklı olarak, şartın ilk yakalandığı noktada seçim işlemi sonlanır.
Üniversite notlama sistemi örneği ile bunu açıklamak gerekirse:
AA: 90 - 100 / Başarılı
BA: 80 - 89 / Başarılı
BB: 75 - 79 / Başarılı
CB: 70 - 74 / Başarılı
CC: 60 - 69 / Başarılı
DC: 50 - 59 / Koşullu Başarılı - Başarısız
DD: 40 - 49 / Başarısız
FD: 30 - 39 / Başarısız
FF: 0 - 29 / Başarısız
Şimdi bunu programımıza yazalım
func main() {
// AA: 90 - 100 / Başarılı
// BA: 80 - 89 / Başarılı
// BB: 75 - 79 / Başarılı
// CB: 70 - 74 / Başarılı
// CC: 60 - 69 / Başarılı
// DC: 50 - 59 / Koşullu Başarılı - Başarısız
// DD: 40 - 49 / Başarısız
// FD: 30 - 39 / Başarısız
// FF: 0 - 29 / Başarısız
ogrenciNotu := 62
fmt.Println("ogrenciNotu: ", ogrenciNotu)
if ogrenciNotu >= 50 && ogrenciNotu <= 59 {
fmt.Println("DC - Koşullu Başarılı")
} else if ogrenciNotu >= 60 && ogrenciNotu <= 69 {
fmt.Println("CC - Başarılı")
} else if ogrenciNotu >= 70 && ogrenciNotu <= 74 {
fmt.Println("CB - Başarılı")
}
e}
Bu ifadeyi çalıştırdığımız anda aşağıdaki çıktıyı alırız :
ogrenciNotu: 62
CC - Başarılı
ifademizde ilk kol olan öğrencinin notu 50'den büyük ise ve ogrenci notu 59'dan kucuk ve eşit ise bu koldaki işlemi gerçekleştir ibaresi işledi ve 59'dan büyük olduğu için bir sonraki koldaki şart sınanmaya başladı:
if ogrenciNotu >= 50 && ogrenciNotu <= 59 {
fmt.Println("DC - Koşullu Başarılı")
}
İkinci koldaki ifade olan öğrencinin notu 60'dan büyük ya da eşit ise ve ogrencinin notu 69'dan kucuk ise bu koldaki işlemi gerçekleştir şartı sınandı ve not 62 olduğundan dolayı buradaki fmt.Println() ifadesindeki değer ekranda görüntülendi. else if ifadelerinde mevcut şart sağlandığında kodun çalışması ifadenin sağlandığı scope içinde sonlanır. Böylece bir sonraki scope içindeki şart işleme tabi olmaz. Yani ogrencinin notu 70'den büyük ve 74'ten kucuk eşit ise bu koldaki işlemi gerçekleştir ibaresi çalıştırılmadan işlem sonlanır.
Anahtar Durum İfadeleri (Switch Case)
Switch ifadesi,çoklu seçenekler içinde anahtar açıp kapatma usulüyle çalışan bir kodlama yapısıdır. Basit bir ifade ile demiryolu makası gibi düşünülebilir. Açık olan anahtar kısmından işlemi devam ettirir.

Switch Türleri
Go dili, iki tür anahtar deyimini destekler:
- İfade Switch
- Tip Switch
İfade Switch
İfade switch, C, C++, Java dilindeki switch ifadesine benzer. İfadenin değerine bağlı olarak yürütmeyi kodun farklı bölümlerine göndermenin kolay bir yolunu sağlar.
Syntax:
switch optstatement; optexpression{
case expression1: Statement..
case expression2: Statement..
...
default: Statement..
}
SWITCH YAZIM KURALLARI
switchifadesi sonrasında yazılanoptexpressionveoptstatementopsiyonel ifadelerdir.- Eğer hem
optexpressionhem deoptstatementifadede mevcut ise ikisinin arasına noktalı virgül;işareti koymak gereklidir.- Eğer
switchherhangi bir ifade içermiyorsa, derleyici ifadenintrueolduğunu varsayar.optstatementdeğişken tanımlamaları, artış ya da atama durumları, fonksiyon çağrıları vb. gibi basit durumları içerir.- Eğer değişken
optstatementiçerisinde görüntülenirse, değişkenin aralığı switch ifadesi ile sınırlıdır.switchvecaseifadeleri dahili olarakbreakile durdurulmaz. Fakat şart sağlandığında otomatik olarak anahtar değeri değişir ve bir sonraki aşamaya geçilir.defaultifadesiswitchifadesinde isteğe bağlı olarak kullanılır.caseifadesi birden fazla değişken içerirse bu ifadeler virgül (,) ile ayrılırlar.- Eğer
caseifadesi herhangi bir ifade içermiyorsa, sistem bu ifadenin değerinintruekabul eder.
İlk örnek ifademizi yazalım :
package main
import "fmt"
func main(){
// optstatement - day ifadesi day:=4 olarak tanımlandı
// optexpression - olculecek deger day ifadesi olarak belirlendi
switch day:=4; day {
case 1:
fmt.Println("Pazartesi")
case 2:
fmt.Println("Salı")
case 3:
fmt.Println("Çarşamba")
case 4:
fmt.Println("Perşembe")
case 5:
fmt.Println("Cuma")
case 6:
fmt.Println("Cumartesi")
case 7:
fmt.Println("Pazar")
default:
fmt.Println("Geçerli Bir gün Girmediniz!")
return
}
}
Kodumuzu çalıştırdığımızda aşağıdaki çıktıyı elde edeceğiz:
Perşembe
Yukarıdaki ifadede day isimli değişkene 4 değeri atanmıştır. switch - case ifadeleri ile kontrol edilmek üzere day ifadelerinin inceleneceği belirtilip {} ile case yani durumlar belirtilmiştir. case 4: durumu yani kontrol edilen değer olan day değişkeninin 4'e eşit olduğu durumda haftanın dördüncü günü olan Perşembe değeri ekranda görüntülenmiştir.
Şimdi ikinci bir ifade ile devam edelim :
package main
import (
"fmt"
)
func main(){
var secim int = 3
switch secim {
case 1:
fmt.Println("Cemil")
case 2:
fmt.Println("Ramazan")
case 3:
fmt.Println("Şaban")
case 4:
fmt.Println("Haydar")
default:
fmt.Println("Nuri")
return
}
}
Bu ifademizde secim isimli değişken işlem öncesinde int olarak 3 değeri ile tanımlanmıştır. switch ifademizde secim isimli değişkenin durumlarının değerlendirileceğini belirttikten sonra case ile kontrol ettiğimizde 3 değerinin olduğu koldaki fmt.Println("Şaban) isimli ismi ekranda görüntüleyecektir.
Şaban
Şimdi başka bir ifade ile devam edelim:
package main
import "fmt"
func main() {
var firstName string = "Gökay"
fmt.Printf("Sorgulanan İsim: %s \n", firstName)
switch firstName {
case "Ahmad", "Mohammad":
fmt.Println("Arap İsmi")
case "Gökay", "Alp", "Tolga":
fmt.Println("Türk İsmi")
case "Solomon", "David", "Benjamin":
fmt.Println("Yahudi İsmi")
case "George", "John":
fmt.Println("Amerikan İsmi")
case "Hans", "Jurgen":
fmt.Println("Alman İsmi")
case "Pierre", "Michel":
fmt.Println("Fransız İsmi")
}
}
Yukarıdaki ifadede firstName isimli değişkenimizi Gökay olarak tanımladık. case sorgularımız sırasında ifadeler kümesi olarak değerlendirmeye başladık. İlk case durumumuzda Ahmet veya Mehmet isimlerinden biriyse fmt.Println("Arap İsmi") olarak sonuç döndür dedik. her case ifadesi için birden çok seçenek sorguladık. İkinci case ifademizde verdiğimiz Gökay değeri yakalandığı için bu kolda yer alan fmt.Println("Türk ismi") seçeneği ekranda görüntülendi.
Sorgulanan isim : Gökay
Türk ismi
Tip Switch (Type Switch)
Tip switch anahtarı, türleri karşılaştırmak istediğinizde kullanılır. Bu anahtarda, case ifadesi , switch ifadesinde bulunan tür ile karşılaştırılacak olan türü içerir.
Syntax:
switch optstatement; typeswitchexpression{
case typelist 1: Statement..
case typelist 2: Statement..
...
default: Statement..
}
TYPE SWITCH KURALLARI:
- Opsiyonel ifade, yani optstatement, switch ifadesindeki ile benzerdir.
- Bir
optstatementbirden fazla değer içeriyorsa ve bu değerler virgül(,) ile ayrılırlar.switchdeyimi türünde, case ve default deyimi herhangi bir break deyimi içermez. Ancak, programınız gerekliyse,breakvefallthroughifadesini kullanmanıza izin verilir.typeswitchexpression' dedefaultisteğe bağlıdır.typeswitchexpression, sonucu bir tür olan bir ifadedir.typeswitchexpressioniçinde:=operatörü kullanılarak bir ifade atanırsa, bu değişkenin türü, case yan tümcesinde bulunan türe bağlıdır.caseyan tümcesi iki veya daha fazla tür içeriyorsa, değişkenin türü,typeswitchexpressioniçinde oluşturulduğu türdür.
Example:
package main
import "fmt"
func main() {
// interface yapısında myvalue isimli bir değişken tanımladık
var myvalue interface{}
// myvalue ifadesine ait type görüntüleniyor
switch q:= myvalue.(type) {
case bool:
fmt.Println("boolean type")
case string:
fmt.Println("string type")
case float64:
fmt.Println("float64 type")
case int:
fmt.Println("int type")
default:
fmt.Printf("type: %T", q)
}
}
Yukarıdaki ifadede ilk olarak myvalue isimli interface türünde bir değişken tanımladık.
q:= myvalue.(type)
Yukarıdaki ifade ile myvalue isimli değişkenin type değerini döndürdük. Burada değerimiz int, float64, string
gibi primitive data type ifadelerden biri olmadığından dolayı test edilen durumların hepsini geçerek default isimli ifadeye geldik. Bu ifade mevcut ifadelerden hiçbiriyle eşleşen bir durum yoksa döndürülecek case ifadesini barındırır. Burada da %T ile veri tipini görüntüledik. Sistem bize interface ifademiz için bir değer almadığından dolayı nil ifadesini döndürdü ve çıktımız aşağıdaki şekilde oldu.
type: <nil>[Finished in 634ms]
Methods
Metod ifadeleri OOP - Object Oriented Programming içerisinde yer alan ifadelerdir. GO programlama dili kendi içerisinde OOP özelliklerini tamamiyle desteklemese bile bu işlemleri için kendine has interface ve method yapılarına sahiptir. inheritence yani miras alma konusunu method ve interface kavramları ile karşılar.
methodvefuncarasındaki fark,methodifadeleri tanımlanmış bir alıcıya sahiptirler, yani bir nesneni örnek yapısında mevcut olan bir işlev olarak yer alırlar.funcifadeleri ise böyle bir duruma ihtiyac duymazlar.
GO programlama dilinde class ifadeleri yer almaz. Fakat struct tipleri üzerinde method ifadeleri tanımlanarak bu işlevler yerine getirilebilir.
Bir örnek ile konuyu kısaca açıklamaya çalışalım :
package lessons
import "fmt"
// struct
type User struct {
Ad, Soyad string
}
// method
func (u User) Selamlama() string {
return fmt.Sprintf("Merhaba %s %s", u.Ad, u.Soyad)
}
func main() {
u := User{"Gokay", "BURUC"}
fmt.Println(u.Selamlama())
}
Yukarıdaki ifadeyi çalıştırdığımızda ekran çıktısı şu şekilde olacaktır.
Merhaba Gokay BURUC
Şimdi burada neler oldu kademe kademe açıklayalım :
type User struct {
Ad, Soyad string
}
User isimli bir struct ifadesi tanımladık ve bu ifadenin alabileceği değerlere Ad ve Soyad değerlerini atadık. Bu değerleri tanımlayacağımız method ifadesinde kullanmak için atadık.
// method
func (u User) Selamlama() string {
return fmt.Sprintf("Merhaba %s %s", u.Ad, u.Soyad)
}
Şimdi method ifademizi inceleyelim. (u User) kısmında argüman olarak kullanılacak yapının şablonunu u ismiyle User isimli struct ifadesinden alacağımızı belirttik. Selamlama() ifadesi ise method ifademizin ismidir ve bu ifade string türünde bir veri döndürecektir.
return ile string ifademizi döndürürken fmt.Sprintf() ifadesi içerisinde ("Merhaba %s %s" , u.Ad , u.Soyad ) ifadesi ile u ile User struct ifadesine atanan değerlerin çağrılacağını belirttik.
func main() {
u := User{"Gokay", "BURUC"}
fmt.Println(u.Selamlama())
}
main isimli fonksiyon ifademizde u değeri olarak u := User{"Gokay","BURUC"} ifadesini atadık. Burada u.Ad ifadesine "Gokay" ve u.Soyad ifadesine "BURUC" tanımlanmıştır. Bu struct ifadesini Selamlama() isimli method ifadesiyle işleme tabi tutmak için u.Selamlama() şeklinde bir yazım yaptık ve u.Ad için atadığımız "Gokay" ve u.Soyad için atadığımız "BURUC" ifadeleri işleme method içinde kullanılmıştır.
İşlem sonucunda aşağıdaki şekilde bir ekran çıktısı elde ettik:
Merhaba Gokay BURUC
Variadic Functions
variadic functions kavramı bir fonksiyon yazılırken sınırsız sayıda fonksiyon argümanını sisteme dahil edebilen fonksiyon yapıları için kullanılır.
variadic function yazımı sırasında aşağıdaki yapı kullanılır:
// SYNTAX
func funcName(args...argType)returnType {}
Şimdi örneğimizi inceleyelim :
func Sum(eklenecekSayi...int)int{
toplam := 0
for _,sayi :=range eklenecekSayi{
toplam += sayi
}
return toplam
}
Şimdi çalıştırma denemesi için şu şekilde bir yazım yapıyoruz:
package main
import (
"fmt"
)
func Sum(eklenecekSayi...int)int{
toplam := 0
for _,sayi :=range eklenecekSayi{
toplam += sayi
}
return toplam
}
func main() {
sum :=Sum(22, 33, 44, 75)
fmt.Print(sum)
Ekran çıktımız 174% şeklinde olacaktır.Sisteme giriş yapacağımız sayı miktarını belirtmeden rastgele 4 adet sayı ekledik ve bu sayıların çıktılarını ekrana bastırdık.
Variadic Fonksiyonlarda kullanılan argüman tipleri
variadic function yapılarından başta primitive tipler (bool ,float64,int,string) gibi veri tipleri olmak üzere birçok veri tipi kullanılabilmektedir. Kompleks veri tipleri olan interface, array, map gibi veri yapıları da variadic function yapıları ile kullanılabilirler .
interface örneği olarka Println fonksiyon yapısını inceleyelim:
func
func Println(a ...interface{}) (n int, err
error)
Yukarıdaki fonksiyonda a isimli interface tipindeki argümanları Println isimli fonksiyona dahil edileceği belirtilmiştir ve bu fonksiyon (n int, err error) n isimli bir int ya da err isimli bir error döndürecektir. Böylece fonksiyona interface içinde ne değer verilirse verilsin fonksiyon bu değerleri işleme tabi tutup ya int yada error döndürür.
Slice ifadelerinin Variadic Fonksiyon ile Yazdırılması
func SliceVariadic() {
xs := []int{1,2,3}
fmt.Println(Sum(xs...))
}
Bir önceki örneğimize SliceVariadic isimli bir fonksiyon ekledik. Tam kod metnimiz şu şekilde oldu:
package main
import "fmt"
func Sum(eklenecekSayi ...int) int {
toplam := 0
for _, sayi := range eklenecekSayi {
toplam += sayi
}
return toplam
}
func SliceVariadic() {
xs := []int{1, 2, 3}
fmt.Println(Sum(xs...))
}
func main(){
SliceVariadic()
}
Yukarıdaki kodumuzu çalıştırdığımızda ekrana 6 şeklinde bir toplam gelecektir. SliceVariadic fonksiyonunda xs isimli slice ifademizde yer alan 1,2,3 değerleri Sum isimli fonksiyonumuza Sum(xs...) yazımı sayesinde argüman değeri olarak gönderilmiştir. Burada işleme tabi tutulup toplandıktan sonra ekrana çıktı olarak döndürülmüşlerdir.
Closures
GO programlama dilinde fonksiyon içerisinde çağrılabilen isimsiz fonksiyonlar closure ifadeleri olarak tanımlanırlar.
Closure ifadeleri genellikle tekrarlanan yapıları kısaca ifade etmek için kullanılırlar.
Yazım şekilleri isimsiz bir fonksiyon şeklindedir:
x := func()int{
// y degiskeni - işleme dahil olup çıktıya yansıtılacak degisken
return y
}
Bir örnek ile ifade etmek gerekirse:
func main() {
x := 0
arttir := func() int {
x++
return x
}
fmt.Println(arttir())
fmt.Println(arttir())
}
Yukarıdaki ifadede arttir() isimli closure ifadesi her çağrıldığında x değerini bir arttırıp sonuç döndürür. Bu yüzden başlangıçta 0 olan x değeri, bu ifadede iki defa arrtir() çağrıldığından dolayı 1 ve 2 değerleri ekrana yazdırılır.
Closure ifadelerinin Fonksiyon Çıktılarına Yazdırılması
Closure ifadeleri yalnızca değişkenlere atanmakla kalmaz aynı zamanda başka bir fonksiyonun return ifadesi sonrasında gelecek değerleri de işleme tabi tutmak amacıyla kullanılabilirler.
Örneğimizi inceleyelim :
func CiftYazdir() func() uint {
i := uint(0)
return func() (ret uint) {
ret = i
i += 2
return
}
Şimdi ifademizi main() fonksiyonumuzla çalıştıralım :
func main() {
yeniCift := CiftYazdir()
fmt.Println(yeniCift()) // 0
fmt.Println(yeniCift()) // 2
fmt.Println(yeniCift()) // 4
}
Toplu olarak kodumuzu görüntüleyelim :
func CiftYazdir() func() uint {
i := uint(0)
return func() (ret uint) {
ret = i
i += 2
return
}
func main() {
yeniCift := CiftYazdir()
fmt.Println(yeniCift()) // 0
fmt.Println(yeniCift()) // 2
fmt.Println(yeniCift()) // 4
}
Şimdi burada gerçekleşen işlemleri inceleyelim. Öncelikle CiftYazdir() isimli func ifademizi inceleyelim.
func CiftYazdir() func() uint {
i := uint(0) // unsigned integer olarak sıfır değeri i ye atandı
return func() (ret uint) {
ret = i // ret isimli değeri isleme girecek i degerine ata
i += 2 // i degerini her calistirmada 2 arttır
return
}
}
Fonksiyonumuzda öncelikle başlangıc değeri olan i := uint(0) değerini tanımladık. Fonksiyonumuz her çalıştığında i +=2 ile i değişkenine 2 ilave eder ve bu değeri ret isimli bir değişkene atar. closure ifademiz uint olarak ret değerini döndürür. Böylece CiftYazdir() isimli fonksiyonumuzun return değeri closure ifademizden gelen sayıya eşitlenir.
Programımızı çalıştırdığımızda ekrana aşağıdaki çıktı gelecektir:
0
2
4
Recursions
Recursion, kelime anlamıyla kendi kendini öz yineleme anlamına gelmektedir. Bir fonksiyon yaptığı işleri çağrıldığında yineliyorsa recursive function adını alır.
Faktöriyel işlemi ile açıklamak gerekirse :
func Faktoriyel(x uint) uint {
if x == 0 {
return 1
}
return x * Faktoriyel(x-1)
}
Yukarıda gördüğünüz fonksiyon kendi içerisinde her defasında x değerini bir önceki x değerine ait Faktoriyel(x-1) fonksiyonu ile çarpmaktadır. Geri doğru devam eden bu faktöriyel çarpım işlemi x==0 olduğunda 1 değerini alır ve işlem sonlanır.
Yani faktöriyel işlemini ters bir şekilde ifade edersek :
x * (x-1) * (x-1-1) * (x-1-1) * .... * 1
Bizim örneğimizde farz-ı misal x=5 dersek
//x * (x-1) * (x-1-1) * (x-1-1) * .... * 1
5 * 4 * 3 * 2 * 1
işleminin sonucu gelecektir
Şimdi kodumuzu çalıştıralım :
package main
import "fmt"
func Faktoriyel(x uint) uint {
if x == 0 {
return 1
}
return x * Faktoriyel(x-1)
}
func main() {
x := lessons.Faktoriyel(5)
fmt.Println(x)
}
Ekrana 120 değeri yazdırılacaktır.
Anonymous Functions
Anonymous Function ifadeleri isimsiz fonksiyon ifadeleridir. Fonksiyonların içinde satır içi işlemleri yapmak için idealdirler. function literal yani fonksiyon değişmezi olarak da isimlendirilirler.
Anonymous Function Yazım Şekilleri
Anonymous Function ifadelerinin yazım şekilleri aşağıdaki gibidir:
func(parameter_list){
// code..
}()
Eğer return tipi belirlenmişse aşağıdaki şekilde yazılırlar:
func(parameter_list)(return_type){
// code..
return
}()
Anonymous Function Uygulaması
İlk örneğimiz üzerinden kısaca açıklayalım :
package main
import "fmt"
func AnonSelamlama() {
// bir degiskene anonim fonksiyon atanması
deger := func() {
fmt.Println("Merhaba Dünya!")
}
deger() // value isimli degisken bir fonksiyon gibi davranır
}
func main(){
AnonSelamlama()
}
Bu örneğimizde deger isimli ifade bir anonim fonksiyon atandıktan sonra bir fonksiyon gibi çalışmakta ve bu değişken çağrıldığında anonim fonksiyon içindeki değerler görüntülenmektedir. deger isimli değişkenimiz, isimsiz bir fonksiyon atandıktan sonra fonksiyon işlemlerini gerçekleştirmiş ve fonksiyon içindeki değeri döndürmüştür. Bu örnekte zaten fmt.Println ile yaptığımız bir işlemi anonymous function ile tekrar yazıyoruz. Burada yalnızca bir örnek teşkil ettiği ve konuyu anlattığı için yazım açısından faydasını tartışmadan bir diğer örneğe geçiyoruz :
package main
import "fmt"
func main() {
// anonymous function with arguments
var toplama = func(sayi1, sayi2 int) {
toplam := sayi1 + sayi2
fmt.Println("toplam :", toplam)
}
// function call
toplama(5, 3)
}
Bu işlemde toplama isimli değişkenimize func(sayi1,sayi2 int) {} şeklinde bir anonymous function atadık. İşlemimiz 5 ve 3 değişkenlerinin anonymous function aracılığıyla toplayıp toplama isimli değişkene atıyoruz. toplama isimli değişken fonksiyona atanır ve değişkenimiz fonksiyon gibi davranmaya başlar. Çalıştırdığımız anda ekranda aşağıdaki çıktıyı elde ederiz:
toplam : 8
Anonymous Function ve return
Buraya kadar olan kısımda henüz return ifadesi ile birlikte bir anonymous function ifadesi kullanmadık. Yani bir değer döndürmesini istemedik. Fonksiyon sonucunu görüntüledik. Şimdi bu fonksiyondan return ile bir değer döndürmesini isteyeceğiz.
package main
import "fmt"
func main() {
// anonymous function
var sum = func(n1, n2 int) int {
sum := n1 + n2
return sum
}
result := sum(5, 3) // fonksiyon ifademizi cagırdık
fmt.Println("Toplam :", result)
}
Yukarıdaki işlemde sum isimli değişkene bir anonymous function atadık.
var sum = func(n1, n2 int) int {
sum := n1 + n2
return sum
}
Sonrasında ise sum ifadesini result isimli başka bir değişkene atadık.
result:= sum(5,3)
result isimli değişkeni ekrana yazdırdığımızda 5 ve 3 sayılarını sum() isimsiz fonksiyon ifadesine göre toplayıp sonucu ekrana sekiz olarak yazdıracaktır.
Yukarıdaki sum anonymous function ifadesini daha da kısa yazdırmak istersek aşağıdaki şekilde yazdırabiliriz:
var sum = func(n1,n2 int)int{
return n1 + n2
}
Anonymous Function ifadesini Başka Bir Fonksiyonun Argümanı Olarak Kullanma
Bir anonymous function ifadesini başka bir fonksiyon içerisinde parametre olarak kullanabiliriz.
package main
import "fmt"
// regular function to calculate square of numbers
func findSquare(num int) int {
square := num * num
return square
}
func main() {
// anonymous function that returns sum of numbers
sum := func(number1 int, number2 int) int {
return number1 + number2
}
// function call
result := findSquare(sum(6, 9))
fmt.Println("Result is:", result)
}
Yukarıdaki örnek daha önce yaptığımız örneğe benzerlik gösteriyor. Bu örnekte kare alma işlemi için bir fonksiyon yazılmış ve isimsiz fonksiyonla yapılan toplama işlemi sonucu buraya parametre olarak verilmiştir.
func findSquare(num int) int {
square := num * num
return square
}
fonksiyonu aracalığıyla kare alma işlemini gerçekleştiriyoruz. Ardından da main() isimli fonksiyonda result isimli fonksiyonumuza bir isimsiz fonksiyon anonymous function ifadesi atıyoruz.
func main() {
// anonymous function that returns sum of numbers
sum := func(number1 int, number2 int) int {
return number1 + number2
}
// function call
result := findSquare(sum(6, 9))
fmt.Println("Result is:", result)
}
Bir Fonksiyon ifadesinde Return ile Anonymous Function Döndürme
Bu vakte dek anonymous function ifadelerinin parametre olarak kullanımını, değişkene atanmasını gördük. Şimdi ise return ifadesine atanmalarını göreceğiz. Bu ifade GO programlama dilinde en çok karşınıza çıkacak ifadelerden biridir.
Yazımı aşağıdaki şekildedir:
func FuncName() func() int {
// code for FuncName() function
return func() int{
// code for anonymous function
return anonymous_function_return
}
}
Hemen örneğimiz ile başlayalım :
package main
import "fmt"
// anonim fonksiyon ifadesi
func SayiGoruntule() func() int {
sayi := 10
return func() int {
sayi++
return sayi
}
}
func main() {
degisken := SayiGoruntule()
fmt.Println(degisken())
fmt.Println(degisken())
fmt.Println(degisken())
fmt.Println(degisken())
fmt.Println(degisken())
}
Yukarıdaki ifadede neler yaptığımızı kesitlere bölerek anlatalım. İlk olarak fonksiyonun mantığından bahsedelim. Yukarıda yazdığımız fonksiyon her seferinde çıktı değerini sistem girdisi olarak alıp bir arttırmak üzere tasarlandı. Yani number isimli değişkenimiz eğer bir anonymous function ifadesine girdi olmasaydı ilk atandığı değer olan 10 değeriyle sistem dışına çıkacaktı. Fakat biz bu değeri her seferinde return ifadesi aracılığıyla sisteme tekrar dahil ediyoruz.
// anonim fonksiyon ifadesi
func SayiGoruntule() func() int {
sayi := 10
return func() int {
sayi++
return sayi
}
}
fonksiyonumuzu şimdi parçalara ayıralım :
func SayiGoruntule() func() int {
sayi := 10
return ...
}
Burada sistemin çıktısının bir anonymous function geleceğini ve bu fonksiyonun da int döndürüleceğini belirttik. Yukarıdaki gösterimde ... ile gösterilen yere anonymous function ifademizi yazdık. sayi isimli değişkeni anonymous function ifadesine girdi olarak alacağız. Şimdi ... içinde kalan kısmı inceleyelim:
func() int {
sayi++
return sayi
Sayı ifadesini girdi olarak al ve sayi++ ifadesiyle değeri 1 arttır ve return ifadesiyle çıktı olarak döndürür.
Bu fonksiyonun özelliği her çalıştırıldığı anda en son değişken değerini girdi olarak kabul etmesidir. Yani ilk çalıştırdığımızda 10 değerini 1 olarak arttırır ve 11 yapar. İkinci defa çalıştırdığımızda bu sefer en son değerimiz olan 11 değerini sistem hafızasından alır ve 1 arttırarak 12 olarak döndürür.
Şimdi kodumuzu çalıştırdığımız anda aşağıdaki çıktıyı elde ederiz:
11
12
13
14
15
Packages and Modules
GO programlama dili yapısı itibariyle package ve module mimarisiyle çalışan bir dildir.
Package Kavramı
Module Kavramı
Package Names
İsimlendirme Kuralları
Paket isimlendirmeleri sırasında GO programlama dili belirli kuralları izler.
- Paket isimleri küçük harfle başlamalıdır. Örneğin:
fmt,string,bytes,io - Paket içerisinde yer alan fonksiyon ve metod isimleri büyük harfle başlamalıdır. Örneğin:
fmt.Println,fmt.Printf - Paket içerisinde yer alan fonksiyon isimleri birleşik olarak her kelimenin ilk harfleri büyük olarak yazılması tercih edilir. Örneğin :
scraper.GetRequest(),mypackage.DoWhatISaid() - Getter ve Setter fonksiyonları için
GetveSetgereklilik değil yalnızca açıklamadır. Tercih edilebilir. Örneğinobj.Owner()veyaobj.GetOwner()şeklinde iki farklı kullanım ile yazım sağlanabilir. - GO dili içerisinde ayrılmış belirli ifadeler vardır ve bunlar
package,function,variableadı olarak kullanılamazlar. (Tablo.1) - GO dilinde satır sonuna
;işareti konulur fakat bu bir gereklilik değildir. Bir fonksiyonu çağırırken iki farklı şekilde çağırabilirsiniz:fmt.Println();veyafmt.Println() - Bir scope açılıp kapatılırken süslü parantez
{}kullanılır. structifadelerinde açıklayıcı ifadeler kullanılması tercih edilirStringifadesi yerineToStringifadesi ile yapının bir string ifadesine yerleştireliceği belirtilir.
Getter Methods : Bir sınıf içerisinde bir özelliğe erişim imkanı veren metodlardır. Setter Methods : Bir sınıf içerisindeki bir özelliği değiştirmeye ya da bir atama yapmaya imkan veren metodlardır.
Errors - Hatalar
panic
panic fonksiyonu temel işlev bakımından belirlenen bir durum gerçekleştiğinde hata mesajı döndürerek işlemi sonlandırmak görevini üstlenir.
Birden çok işlemi arka arkaya yaptığımızda işlemin bir noktasında sistem arızasına neden olabilecek bir durumla karşılaşmamak adına buraya panic fonksiyonu ilave edilebilir. İşlem hata döndüreceği anda panic işlemi sonlandırır.
Bir örnek ele alalım :
package main
import "fmt"
func PanicOlustur() {
fmt.Println("Panik öncesi işlem")
panic("Olağan dışı bir işlem gerçekleşti. İşlem sonlandırıldı")
fmt.Println("Panik sonrası işlem") // işlem sonlandırıldığından dolayı görüntülenmez
}
func main(){
PanicOlustur()
}
Bu fonksiyonu çalıştırdığımızda aşağıdaki ekran çıktısını elde ederiz:
Panik öncesi işlem
panic: Olağan dışı bir işlem gerçekleşti. İşlem sonlandırıldı
goroutine 1 [running]:
learn/cookbook_go/pdf_opts.PanicOlustur()
/home/gokayburuc/Documents/workspace/golang/learn_go/pdf_opts/panic_opts.go:32 +0x5f
main.main()
/home/gokayburuc/Documents/workspace/golang/learn_go/main.go:11 +0xf
exit status 2
Burada dikkat ederseniz panic işlemi sonrasında işlem durdu. Sonrasında ise goroutine ifadesi çalışırken sonlandırıldığına dair bir mesaj geldi.İfadenin sonlanması ile birlikte hata mesajı geldi. Bunu düzeltmek için ilerleyen örneklerde recover ifadesini kullanacağız.
Şimdi panik işlemi ile ilgili bir diğer örneğe geçelim. Bu örnekte bölme işlemi yapan bir fonksiyon yazacağız ve 3 ile bölünebilen sayıları ekrana yazdıracağız. Eğer bölen 0 a eşit olursa işlem panic ifadesiyle durdurulacaktır.
package main
import ("fmt")
func Bol(bolunen, bolen int) {
// bolen ifadesi sıfıra esit olursa bir ifadenin sıfıra bölümü sonsuz olacağından dolayı işlemi durdur
if bolen == 0 {
panic("Sıfıra bölüm gerçekleşemez")
} else {
sonuc := bolunen / bolen
fmt.Println("Sonuc: ", sonuc)
}
}
func main() {
Bol(22, 3)
Bol(17, 0) // if bolen == 0 ifadesinden dolayı panic calisir ve islem durur
Bol(21, 3)
}
İfademizi çalıştırdığımızda aşağıdaki sonucu elde ederiz:
Sonuc: 7
panic: Sıfıra bölüm gerçekleşemez
goroutine 1 [running]:
learn/cookbook_go/pdf_opts.Bol(0x4bc5e8?, 0xc0000a8008?)
/home/gokayburuc/Documents/workspace/golang/learn_go/pdf_opts/panic_opts.go:40 +0x9e
main.main()
/home/gokayburuc/Documents/workspace/golang/learn_go/main.go:17 +0x67
exit status 2
yani ikinci ifademiz olan Bol(17, 0) ifadesinden dolayı işlem sonlandı.
DIKKAT: Bu çıktıdaki veri yolları benim bilgisayarımda test klasörüme ait veri yollarıdır. Test işlemleri için oluşturduğum proje klasörüne ait yollardır. Siz çalıştırdığınızda kendi proje klasörünüze ait yolları göreceksiniz!
learn/cookbook_go/pdf_opts.Bol(0x4bc5e8?, 0xc0000a8008?)
/home/gokayburuc/Documents/workspace/golang/learn_go/pdf_opts/panic_opts.go:40 +0x9e
main.main()
/home/gokayburuc/Documents/workspace/golang/learn_go/main.go:17 +0x67
Şimdi panic ifadelerinin yalnızca görüntülenmesi fakat programın çalışmasını durdurmaması için ne yapmamız gerektiğini göreceğiz. Burada bahsedeceğimiz ifade recover ifadesidir.
Güncel örneğimizle devam edelim. Örneğimize bir adet panic yakalayıcı recover fonksiyonu yazalım :
func PanikYakala() {
panic_check := recover()
if panic_check != nil {
fmt.Println("RECOVER: ", panic_check)
}
Şimdi kodumuzu bu fonksiyon ile güncelleyelim :
package main
import "fmt"
func Bol(bolunen, bolen int) {
defer PanikYakala() // sistem durduğunda işlem sonunda PanikYakala() fonksiyonun çalıştır
// bolen ifadesi sıfıra esit olursa bir ifadenin sıfıra bölümü sonsuz olacağından dolayı işlemi durdur
if bolen == 0 {
panic("Sıfıra bölüm gerçekleşemez")
} else {
sonuc := bolunen / bolen
fmt.Println("Sonuc: ", sonuc)
}
}
func PanikYakala() {
panicYakala := recover()
if panicYakala != nil {
fmt.Println("RECOVER: ", panicYakala)
}
}
func main() {
Bol(22, 3)
Bol(17, 0) // if bolen == 0 ifadesinden dolayı panic calisir ve islem durur
Bol(21, 3)
}
Kodumuzu çalıştırdığımızda aşağıdaki çıktıyı elde edeceğiz :
Sonuc: 7
RECOVER: Sıfıra bölüm gerçekleşemez
Sonuc: 7
Görüldüğü üzere panic işlemi durdurmak istedi fakat recover fonksiyonumuz olayı ele alıp işlemi sonlandırdı.
defer
defer ifadesi bir fonksiyonun işlemi tamamlandıktan sonra gerçekleşecek işlemleri tanımlamak için kullanılan bir ifadedir. Yani işlem sırasını değiştirir. defer ifadesinin arkasına yazılan kod ve fonksiyonlar işlemin en sonunda çalışırlar.
Genellikle açılan dosyaları kapatmak, hafızayı dolduran öğeleri sonlandırmak gibi performans işlemlerini gerçekleştirmek adına yazılırlar.
İlk örneğimizle başlayalım :
package main
import "fmt"
func FirstFunction() {
fmt.Println("İlk fonksiyon çalıştı")
}
func SecondFunction() {
fmt.Println("İkinci fonksiyon çalıştı")
}
func main() {
defer SecondFunction()
FirstFunction()
}
İfademizi çalıştırdığımızda çıktımız aşağıdaki şekilde olacaktır.
İlk fonksiyon çalıştı
İkinci fonksiyon çalıştı
Dikkat ederseniz biz normal bir yazımda öncelikle ikinci fonksiyonun çalışmasını beklerdik. Fakat defer ifadesi ile yazıldığından dolayı, GO diğer fonksiyonların işlerinin yapıp bitirmesini bekledi ve sonrasında defer sonrasındaki ifadeleri çalıştırdı.
İkinci örneğimizde ise açık olan bir dosyayı kapatacağız :
İşlem öncesinde projemize bir
file.txtdosyası oluşturacağız. İçine dilediğiniz bir metni kopyalayıp yapıştırın.
func main(){
file,err := os.Open("file.txt")
defer file.Close() // açma işlemi ve dosyada yapacak değişiklikler bittikten sonra sonlandır
fmt.Println("Dosya açıldı.")
// file değişiklik işlemleri
}
Yukarıdaki fonksiyonu çalıştırdığımızda öncelikle file.txt isimli dosyayı ön hafızaya alacaktır. Burada işlem sonunda hafızanın boşalması için file.txt isimli dosyamızı kapatmamız gerekir. Burada işlemler gerçekleştikten sonra kapatması için defer file.Close() ifadesi ile en sonda gerçekleşecek işlemi ekledik.
_HATIRLATMA: _ Bu konunda text ifadeleri içerisine metin yazdırma işlemleri ile ilgili Text Reading With GO konusuna bakabilirsiniz.
Pointers - İşaretleyiciler
GO programlama dilinde her değişken hafızada bir yer tutar bu değişken için sistem hafızasında geçici ve benzersiz bir atama yapılır. Bir değere ilk atanan benzersiz hafıza ifadesine erişmek için o değere ait pointer ifadesini bilmemiz gerekir.
Örnek üzerinde inceleyelim :
func BasicPointers() {
var a int = 55 // Bir tamsayı değişkeni bildir
var p *int // Bir tamsayıya işaret eden pointer bildir
p = &a // 'a'nın adresini 'p'ye ata
fmt.Println("a'nın değeri:", a) // 'a'nın değerini yazdır
fmt.Println("a'nın adresi:", &a) // 'a'nın adresini yazdır
fmt.Println("p'nin değeri:", p) // 'p'nin değerini (a'nın adresi) yazdır
fmt.Println("p'deki değer:", *p) // 'p'yi dereference ederek 'a'nın değerini al
*p = 33 // 'p'nin işaret ettiği adresteki değeri değiştir
fmt.Println("a'nın yeni değeri:", a) // 'a'nın değiştirilmiş değerini yazdır
}
DIKKAT:
pointerifadelerindedeclarationveinitializationkavramları için iki farklı sembol yer alır:
&ifadesi :initializationifadesidir. Yanipointerifadesine ait adresi başlatmak için kullanılır.- '*' ifadesi :
declarationifadesidir. Bir ifade önüne bu konulduğunda bu ifadeninpointerolarak kullanılacılağını belirtir.
Yukarıdaki fonksiyonu inceleyeliyelim. Öncelikle a isimli int tipinde 55 değerinde bir değişken tanımladık ve GO bu değişken için bir hafıza birimi atadı. Daha sonra p isimli bir pointer ifadesi tanımladık ve bu ifadenin int türünde bir pointer ifadesi olduğunu var p *int biçiminde yazdık. Yani p ifadesi bir hafıza adresi görüntüleyecektir. Fakat mevcut tanımlı bir hafıza ataması olmadığından dolayı p = &a ifadesiyle a değişkenin hafıza değerini p isimli pointer'a atadık. Kodumuzu çalıştırdığımızda ekrana aşağıdaki gibi bir çıktı gelecektir.
a'nın değeri: 55
a'nın adresi: 0xc0000a6010
p'nin değeri: 0xc0000a6010
p'deki değer: 55
a'nın yeni değeri: 33
Bu ifadeleri eşleştirelim :
fmt.Println("a'nın değeri:", a)
//a'nın değeri: 55
fmt.Println("a'nın adresi:", &a)
// a'nın adresi: 0xc0000a6010
fmt.Println("p'nin değeri:", p)
// p'nin değeri: 0xc0000a6010
fmt.Println("p'deki değer:", *p)
// p'deki değer: 55
Yukarıdaki kod bloğunu incelediğimizde &a ifadesi 0xc0000a6010 gibi eşsiz bir hafıza konumunu görüntüler. Bu ifade sistemde a değişkeni için ayrılan hafıza bloğunun adresidir. a ifadesine 55 değerini atamıştık.
Hatırlarsanız ilk işlemde :
p = &a // 'a'nın adresini 'p'ye ata
şeklinde a'nın hafıza adresini p değişkenine atamıştık. Bu yüzden p değişkeni çağrıldığında hafıza adresi görüntüleyecektir.
fmt.Println("p'nin değeri:", p)
// p'nin değeri: 0xc0000a6010
Eğer p isimli pointer ifadesinin önüne asteriks koyarak *p şeklinde yazarsak, gerçekleşen işlem şöyle olacaktır. p değişkeni GO 'daki adrese gider, bu adresteki atanan değeri alır ve ekrana yazdırır . Bizim işlemimizde p değerine a ya ait adres atanmıştı. p isimli pointer ifadesi o adrese gider ve o adresteki 55 atammış değerini bize döndürür.
fmt.Println("p'deki değer:", *p)
// p'deki değer: 55
Fonksiyonlarda Pointer İfadeleri
Fonksiyonlarla pointer ifadeleri kullanılırken hafıza değer atamaları noktasında sıkça karşımıza çıkacak ifadeler mevcuttur. Fonksiyon argümanlarına pointer atamaları ya da fonksiyon çıktılarına pointer atamaları ile karşılacağız. Yazım biçimi aşağıdaki gibidir.
// func funcname(variable *datatype){}
func FonksiyonAdı(mypointer *string){}
Şimdi bir örnekle bu atamayı açıklayalım. İki adet fonksiyon oluşturuyoruz ve bu fonksiyonları main() isimli fonksiyonuzda çağıracağız.
func PointerDegerDegistirici() {
a := 58
fmt.Println("Fonksiyon çağrılmadan önceki a değeri =", a)
fmt.Println("DegerDegistir isimli Fonksiyon çağrıldı.")
DegerDegistir(&a)
fmt.Println("Fonksiyon çağrıldıktan sonraki a değeri =", a)
}
func DegerDegistir(num *int) {
*num = 99
}
Yukarıdaki örnekte DegerDegistir(num *int) isimli fonksiyonumuz a değişkenine ait adrese yeni bir değer atamaktadır. a değeri PointerDegerDegistirici() isimli fonksiyonumuzda 58 olarak belirlenmişti. &a ile bu değer atamasının hafızadaki konumu bulunur. Bu değeri DegerDegistir(&a) biçiminde ikinci fonksiyonumuza verdiğimizde buradaki değer ifadesi num *int formatına çevrilir. Yani hafızadaki 58 değeri * işareti ile işleme tabi tutulacak bir hale getirilir. *num = 99 ifadesiyle bulunduğu adresteki değerine 99 değeri atanır. Böylece sistem hafızasında a ifadesinin yeni değeri artık 99 olmuştur.
package main
import "fmt"
func main(){
PointerDegerDegistirici()
}
func PointerDegerDegistirici() {
a := 58
fmt.Println("Fonksiyon çağrılmadan önceki a değeri =", a)
fmt.Println("DegerDegistir isimli Fonksiyon çağrıldı.")
DegerDegistir(&a)
fmt.Println("Fonksiyon çağrıldıktan sonraki a değeri =", a)
}
func DegerDegistir(num *int) {
*num = 99
}
Bu ifademizi çağırdığımızda aşağıdaki çıktıyı elde edeceğiz:
Fonksiyon çağrılmadan önceki a değeri = 58
DegerDegistir isimli Fonksiyon çağrıldı.
Fonksiyon çağrıldıktan sonraki a değeri = 99
struct ifadelerinde pointer Kullanimi
struct ifadelerinde pointer kullanımı genellikle struct ifadesine ait bir özelliğe harici bir yapıyla erişip değiştirmek amacıyla kullanılır.
package main
import (
"fmt"
)
type Sahis struct {
isim string
yas int
}
func main() {
kisi1 := Sahis{"Cemil", 35}
fmt.Println("Fonksiyon çağrısından önce, kisi1 =", kisi1)
DuzenleSahis(&kisi1)
fmt.Println("Fonksiyon çağrısından sonra, kisi1 =", kisi1)
}
func DuzenleSahis(kisipointer *Sahis) {
kisipointer.yas = 32 // Kişinin yaşını değiştir
kisipointer.isim = "Esra"
}
Yukarıdaki örnekte Sahis isimli isim ve yas özellikleri olan bir struct yapısı kurulmuştur. kisi1 isimli bir struct nesnesi oluşturduk. Bu şahsın isim niteliği "Cemil" ve yas niteliği 35 olarak tanımlandı. Simdi bu sahsin
isim ve yas ozelliklerini pointer yardımıyla degistirelim.
DuzenleSahis(&kisi1)
Bu ifade kisi1 isimli öğeye ait adres değerine erişim sağlar. Bu yapı bir struct yapısı olduğundan dolayı kisi1.isim ve kisi1.yas niteliklerine erisim sağlar. Aşağıdaki fonksiyon aracılığıyla kişi1 nesnesine ait isim ve yas niteliklerini değiştirelim.
func DuzenleSahis(kisipointer *Sahis) {
kisipointer.yas = 32 // Kişinin yaşını değiştir
kisipointer.isim = "Esra"
}
Kodumuzu calistirdiğimizda aşağıdaki ekran çıktısını elde edeceğiz.
Fonksiyon çağrısından önce, kisi1 = {Cemil 35}
Fonksiyon çağrısından sonra, kisi1 = {Esra 32}
struct niteliklerini teker teker değiştirebileceğimiz gibi hepsini birden de örnekte olduğu gibi değiştirdik.
new ve make ifadelerinde pointer
nil ifadeler oluşturmaya yarayan new ve make gibi ifadeler de sistemde hafızada yer kaplar ve pointer ifadeler kullanılarak bu ifadelere de atamalar yapılabilir.
package main
import (
"fmt"
)
func main() {
sayi := new(int) // Bir tamsayı için bellek tahsis et ve ona bir pointer döndür
fmt.Println("sayi'nin adresi:", sayi) // adres : new(int) isimli degiskenin sayi icin ayırdığı adres
fmt.Println("sayi'ya tahsis edilen adresi:", &sayi) // DIKKAT : new(int) Adresine tahsis edilen adres
fmt.Println("sayi'daki mevcut değer:", *sayi) // Varsayılan değer (0)
*sayi = 100 // sayiya 100 degerini ata
fmt.Println("sayi'daki yeni değer:", *sayi) // atanmis degeri goruntule
fmt.Println("sayi'nin ilk adresi : ", sayi) // sayinin ilk adresi
}
Yukarıdaki örnekte sayi := new(int) ifadesi ile hafızada int tipinde değerler alabilen yer ayrılmış bir sayi degiskeni olusturduk.
Ekran çıktısı aşağıdaki gibi olacaktır.
sayi'nin adresi: 0xc000114010
sayi'ya tahsis edilen adresi: 0xc000116018
sayi'daki mevcut değer: 0
sayi'daki yeni değer: 100
sayi'nin ilk adresi : 0xc000114010
şimdi pointer ifadelerimizi inceleyelim. Öncelikli new ifadesi yeni oluşturulan degisken için bir pointer adresi tahsis eder. Bu pointer adresi, sayi isimli değişkenin adresinden farklıdır. 0xc000114010 adresi pointer icin tahsis edilen adrestir. 0xc000116018 adresi ise bu değişkene değişken olduğundan dolayı tahsis edilen hafıza adresidir. Yani iki farklı hafıza değeri olmasının sebebi, sayi isimli değişken için hafızada bir değer ve sayi değişkeninin hafızada tahsis ettiği pointer ifadesi için başka bir değer oluşmuştur. pointer ifademize *sayi = 100 şeklinde yaptığımız atamadan dolayı sayi ifademizin yeni değeri 100 olmuştur.
Structs
Struct ifadeleri veri alanları ve bunlara ait benzersiz özellikleri barındıran koleksiyon kitapçıklarıdır. Bir veri tipinin hangi şablona göre yerleştirileceğini gösteren yapılardır. Eğer nesne yönelimli programlama bilginiz varsa bu konuda kısaca struct yapıları object yani nesne oluşturmak için geliştirilmiş yapılardır denilebilir.
OOP - Object Oriented Programming : Nesneler benzersiz ve belirleyici özellikler içeren bir değerler kümesidir. Nesneler çağrılabilir, nesneye ait nitelikler işlemlere dahil edilebilir ve bir nesne şablonundan farklı nesneler yaratılabilir. Programlama yapıları içerisinde Nesne Tabanlı programlama öğeleri içeren bu yapılara OOP ismi verilir.
İlk örneğimizle struct yapısını ve çalışma mantığını inceleyelim:
type Coordinates struct {
Lat float64 // latitude
Long float64 // longitude
PlaceName string
}
func main() {
fmt.Println(Coordinates{
Lat: 41.67805,
Long: 26.55914,
PlaceName: "Selimiye Camii",
})
}
Yukarıdaki örneğimizde Coordinates isimli bir struct ifadesi tanımladık. Bu ifade Lat, Long, PlaceName ifadeleri içerir. Bu struct şablonuna bağlı olarak sonsuz miktarda Lat, Long, PlaceName içeren nesne tanımlanabilir. Örneğimizde Edirne Selimiye Camii koordinatlarını içeren bir nesne tanımladık. Kodumuzu çağırdığımızda ekran çıktımız aşağıdaki şekilde olacaktır.
{41.67805 26.55914 Selimiye Camii}
Aşağıda selimiye isimli bir nesne object tanımlamasını Coordinates struct yapısına bağlı olarak tanımladık. selimiye bu sayede eşsiz bir nesne haline geldi. Yalnızca Lat, Long, PlaceName değişkenlerini alabilen ve belirtilen değerlere sahip bir nesne olarak programın herhangi bir noktasından çağrılabilir hale geldi.
func main(){
fmt.Println(Coordinates{
Lat: 41.67805,
Long: 26.55914,
PlaceName: "Selimiye Camii",
})
// Coordinates yapısına bağlı selimiye isimli bir nesne tanımladık
selimiye := Coordinates{
Lat: 41.67805,
Long: 26.55914,
PlaceName: "Selimiye Camii",
}
// selimiye nesnesinin özellikleri teker teker çağrılabilir
fmt.Println(selimiye.Lat)
fmt.Println(selimiye.Long)
fmt.Println(selimiye.PlaceName)
}
Bu kod bloğu çalıştırıldığında aşağıdaki gibi bir çıktı elde ederiz.
{41.67805 26.55914 Selimiye Camii}
41.67805
26.55914
Selimiye Camii
struct ifadeleri primitive veri tipleri olan int, float, bool, string gibi değerler alabildikleri gibi kompleks veri tipleri olan array, slice, map, func,struct gibi yapıları da alabilirler. Yani bir struct ifadesi başka bir struct ifadesinin bir öğesi olabilir.
func main(){
type City struct {
CityName []string
}
type Province struct {
Cities City
population int
}
trakyaCity := City{CityName: []string{"Kirklareli",
"Edirne",
"Tekirdağ",
"Çanakkale",
"Istanbul"}}
trakya := Province{
Cities: trakyaCity,
population: 19500000,
}
fmt.Println(trakya)
fmt.Println(trakya.Cities)
}
Yukarıdaki örnekte City isimli struct ifadesinin CityName isimli özelliğini bir slice olarak tanımladık. City isimli struct ifadesini de Province isimli başka bir struct ifadesinin Cities isimli özelliğine atadık. Bu ifade çalıştırıldığında ise aşağıdaki çıktıyı ekranda görüntüleyeceğiz:
{{[Kirklareli Edirne Tekirdağ Çanakkale Istanbul]} 19500000}
{[Kirklareli Edirne Tekirdağ Çanakkale Istanbul]}
Interfaces
Go'daki interface türü bir nevi tanım gibidir. Başka bir türün sahip olması gereken method (yöntem) leri tam olarak tanımlar ve açıklar. Bir türün sahip olması gereken tüm metod yapıları kavramıyla OOP'deki class yapılarına benzerlik gösterir.
Bir örnek vermek gerekirse, daire, kare, üçgen, beşgen iki boyutlu geometrik şekiller olup geometrikSekil gibi bir interface türüne ait aittir diyelim. Bu şekillerin hepsi alan() ve çevreUzunluğu() gibi hesaplanabilir method özelliklere sahiptirler.
Bu ifadeyi bir interface haline dönüştürdüğümüzde aşağıdakine benzer bir yapı elde ederiz:
// geometrik sekiller turunun ortak metodları
type geometrikSekil interface {
alan() float64
cevreUzunlugu() float64
}
Bu ifade altında bir kare tanımlayalım :
type kare struct{
genislik, yukseklik float64
}
Şimdi bu karenin metod özelliğini oluşturalım :
func (r kare) alan() float64 {
return r.genislik * r.yukseklik
}
Aynı şekilde bir de çevre uzunluğu hesaplayalım :
func (r kare) cevreUzunlugu() float64{
return (r.genislik + r.yukseklik) * 2
}
şimdi bu tanımlamalarla interface icinde karemizin nasıl davranacağını belirttik. Bu özellikleri görüntülemek için ise başka bir fonksiyon yazıyoruz:
func olcum(g geometrikSekil){
fmt.Println(g.alan())
fmt.Println(g.cevreUzunlugu())
}
Bu yazdığımız tanımlayacağımız her geometrik sekil için genel gecer bir ifadedir. Misal bir daire tanımladık ve daire alanının hesaplaması daha farklı ve pi sayısını işleme dahil ettik. Burada hesaplama kareden farklı olacaktır ve yeni bir alan fonksiyonu belirtmemiz gerekir. Aynı şekilde çevre uzunluğu için de yeni bir fonksiyon belirtmeliyiz. Fakat ölçüm fonksiyonu her yeni şekil için aynı kalmaktadır ve ekrana alan ve cevre uzunluğu yazdırmaktadır.
empty interface tipi aslında hiçbir method'u tanımlamaz. Hiçbir kuralı yoktur. Ve bundan dolayı her nesnenin empty interface yapısını karşıladığı sonucu çıkar.
Polymorphism Kavramı
interface yapıları diğer programlama dillerinde var olan Polymorphism kavramını GO dilinde gerçekleştirmek amacıyla yazılırlar.
Polimorfizm, aynı isimli metodların farklı tiplerdeki nesneler üzerinde farklı şekillerde çalışabilmesini sağlayan bir özelliktir. Bu, aynı metod isminin, farklı sınıflar veya tipler için farklı işlevselliklere sahip olabileceği anlamına gelir. Polimorfizm sayesinde kodlar daha esnek hale gelir ve farklı tipler arasında geçişler daha kolay yapılabilir.
Polymorphism sayesinde daha az kodlama yaparak daha çok çıktı elde edebilirsiniz. interface yapıları GO dilinde bu amaca hizmet eder.
Interface Uygulaması: Selamlamalar
package main
import "fmt"
type Kisi struct {
Ad string
Soyad string
}
type Hayvan struct {
Ses string
}
type Selamlama interface {
merhabaDe() string
}
func (p Kisi) merhabaDe() string {
return "Merhaba benim adım " + p.Ad
}
func (a Hayvan) merhabaDe() string {
return a.Ses
}
func selamla(i Selamlama) {
fmt.Println(i.merhabaDe())
}
func main() {
man := Kisi{Ad: "Billy", Soyad: "Smith"}
dog := Hayvan{Ses: "Hav!"}
fmt.Println("Kisi:")
selamla(man)
fmt.Println("Hayvan:")
selamla(dog)
}
Örneğimizi incelediğimizde ilk olarak struct yapılarıyla nesneler yaratarak işe başlıyoruz. OOP Nesne Yönelimli Programlama'daki nesne yapılarına benzeyen bu yapılar, belirli özellikleri taşıyan nesneler oluşturmak için kullanılırlar. Bir nevi şablon görevi görürler. Kisi struct yapısında olusturulan tüm objeler Ad ve Soyad isimli string yapılarını içerirler. Benzer şekilde Hayvan isimli struct yapısı da Ses isimli özelliği taşır. Buraya kadar olanları structs konusunda anlatmıştık.
type Kisi struct {
Ad string
Soyad string
}
type Hayvan struct {
Ses string
}
Burada yeni olarak eklenen yapımız interface yapısıdır. Belirli çatı vazifesi görür ve bu interface yapısı içinde tanımlanan yapılar interface içindeki metod özelliklerine sahip olurlar.
type Selamlama interface {
merhabaDe() string
}
Bizim örneğimizde Selamlama isimli interface içinde tanımlanan tüm yapılar bu interface içinde yer alan merhabaDe() isimli metodun özelliklerine sahip olurlar.
func (p Kisi) merhabaDe() string {
return "Merhaba benim adım " + p.Ad
}
func (a Hayvan) merhabaDe() string {
return a.Ses
}
Yukarıdaki yazımda gördüğünüz üzere p isimli Kisi struct ifadesi merhabaDe() fonksiyonu altında Merhaba benim adım ... şeklinde Kisi isimli struct ifadesinin Ad isimli özelliğini döndürecek şekilde hareket eder.
Aynı şekilde a isimli Hayvan struct ifadesi de merhabaDe() ile çalıştırıldığında Hayvan isimli struct ifadesinin Ses isimli özelliğini döndürecek şekilde tanımlanmıştır.
func selamla(i Selamlama) {
fmt.Println(i.merhabaDe())
}
Şimdi ise Selamlama isimli interface yapısında çalışmak üzere bir fonksiyon yazıyoruz ve bu fonksiyon merhabaDe() isimli metodu çağırıp ekrana yazdıracaktır.
Bir sonraki aşamada ise nesne tanımlamalarımızı yazıyoruz:
func main() {
man := Kisi{Ad: "Billy", Soyad: "Smith"}
dog := Hayvan{Ses: "Hav!"}
fmt.Println("Kisi:")
selamla(man)
fmt.Println("Hayvan:")
selamla(dog)
}
Burada man isimli bir nesne ve dog isimli bir nesne değişkeni tanımladık. Bu nesneler Kisi ve Hayvan isimli struct yapılarına göre değer alırlar. Tanımlamalar sonrasında bu nesneleri selamla() isimli interface yapısında çağırdık. Böylece bu nesneler merhabaDe() isimli Selamlama interface'inin içindeki method'a sahip olmuş oldu.
fonksiyonu çalıştırdığımızda aşağıdaki çıktıyı elde ederiz.
Kisi:
Merhaba benim adım Billy
Hayvan:
Hav!
Interface Uygulaması: Geometrik Hesaplamalar
Şimdi başka bir örnekle devam edelim :
package main
import (
"fmt"
"math"
)
type geometri interface {
alan() float64
cevre() float64
}
type kare struct {
genislik, yukseklik float64
}
type daire struct {
yaricap float64
}
func (r kare) alan() float64 {
return r.genislik * r.yukseklik
}
func (r kare) cevre() float64 {
return 2*r.genislik + 2*r.yukseklik
}
func (c daire) alan() float64 {
return math.Pi * c.yaricap * c.yaricap
}
func (c daire) cevre() float64 {
return 2 * math.Pi * c.yaricap
}
func olcumle(g geometri) {
fmt.Println(g)
fmt.Println(g.alan())
fmt.Println(g.cevre())
}
func main() {
r := kare{genislik: 3, yukseklik: 4}
c := daire{yaricap: 5}
olcumle(r)
olcumle(c)
}
Bu örneğimizde kare ve daire için hesaplamalar yapıyoruz. Öncelikle geometri isimli bir interface oluşturalım.
type geometri interface {
alan() float64
cevre() float64
}
Bu interface içinde alan() isimli ve cevre() isimli float64 veri döndüren iki metod tanımlanmıştır.
type kare struct {
genislik, yukseklik float64
}
type daire struct {
yaricap float64
}
kare ve daire isimli struct ifadeleri tanımladık.Bu ifadelere ait genislik, yukseklik, yaricap isimli float64 veri tipli özellikler de tanımlanmıştır.
Şimdi alan ve çevre isimli metod ifadeleri tanımlıyoruz.
func (r kare) alan() float64 {
return r.genislik * r.yukseklik
}
func (r kare) cevre() float64 {
return 2*r.genislik + 2*r.yukseklik
}
func (c daire) alan() float64 {
return math.Pi * c.yaricap * c.yaricap
}
func (c daire) cevre() float64 {
return 2 * math.Pi * c.yaricap
}
Burada da hesaplama ifadeleri return kısmında hesaplamaları tanımlıyoruz.
func olcumle(g geometri) {
fmt.Println(g)
fmt.Println(g.alan())
fmt.Println(g.cevre())
}
interface icerisinde olcumleme(g geometri) isimli bir metod yazılmıştır. Burada ekranda görüntüleme işlemleri tanımlanmıştır.
func main() {
r := kare{genislik: 3, yukseklik: 4}
c := daire{yaricap: 5}
olcumle(r)
olcumle(c)
}
r ve c isimli nesneler tanımlayıp bunları olcumle() isimli metoda atıyoruz.
Kodu çalıştırdığımızda aşağıdaki ekran çıktısını elde edeceğiz:
{3 4}
12
14
{5}
78.53981633974483
31.41592653589793
Boş Interface Yapıları - Empty Interfaces
empty interface yani boş interface yapıları içine her şeyi koyabileceğiniz ve kendi tanımlamalarınızı yapabileceğiniz interface yapılarıdır. Bir nevi boş sepet gibidir ve içine koyarsanız o sepeti olurlar. Örnekle daha da pekişecektir.
package main
import (
"fmt"
)
func main() {
VeriTipiGoruntule(62)
VeriTipiGoruntule(5.14)
VeriTipiGoruntule("撃入朝工球全連幼運聞的受携。記決構欺張済新芸九今阪")
}
func VeriTipiGoruntule(i interface{}) {
switch veriTipi := i.(type) {
case int:
fmt.Printf("%d ifadesi bir integer ifadesidir.\n", veriTipi)
case float64:
fmt.Printf("%f ifadesi 64bit float ifadesidir.\n", veriTipi)
case string:
fmt.Printf("%s ifadesi bir string ifadesidir.\n", veriTipi)
default:
fmt.Printf("I don't know what %v is\n", veriTipi)
}
}
Bu ifadede i interface{} ifadesiyle her metod değerini alabilen boş bir interface oluşturduk ve i.(type) ifadesiyle veri tiplerini görüntüleyen bir yapı elde ettik. Hemen ardından switch ifadesiyle durum senaryoları oluşturup bunları test ettik. Eğer ifademiz int veri tipinde ise case int: yolu çalışır ve ekrana ... ifadesi bir integer ifadesidir. şeklinde yazdırır.
Kodumuzu çalıştırdığımızda aşağıdaki sonucu elde edeceğiz:
62 ifadesi bir integer ifadesidir.
5.140000 ifadesi 64bit float ifadesidir.
撃入朝工球全連幼運聞的受携。記決構欺張済新芸九今阪 ifadesi bir string ifadesidir.
Interface ile Type Assertation
Burada bir ifadeye ait veri türünü interface yardımıyla belirleme hakkında bilgi vereceğiz.
package main
import (
"fmt"
)
func main() {
var herhangiDegisken interface{} = "Edirne İstanbul Bursa"
birString := herhangiDegisken.(string) // type assertation
// tür görüntüleme
fmt.Printf("İfade : %s Tür : %T", birString, birString)
}
Komut çalıştırıldığında herhangiDegisken isimli interface{} tipinde değişkenimiz Edirne İstanbul Bursa değerlerini almıştır. Burada ifademis string ifadesi olduğu için type assertation ile ifademizin bir string ifadesi olduğunu belirttiğimizde hata almadan ekranda bir string ifadesi görüntüleriz.
Ekran çıktımız aşağıdaki şekilde olacaktır.
İfade : Edirne İstanbul Bursa Tür : string%
DIKKAT: Burada tür iddiası yardımıyla tür değişimi
type assertioneğer sistem belirtilen yapıyı elde edemezsepanicifadesiyle komutu sonlandırır.
Bu tip dönüşümlerini string ifadelerinden float64 ya da int ifadelerine dönüştürmek için kullanabilirsiniz. Tabi ki alternatifler de her zaman mevcuttur. Uzmanlaştıkça diğer veri tiplerine de dönüşümler gerçekleştirebilirsiniz.
Goroutines
Channels
Select Statement
Mutexes
Tests
GO programlama dilinde test ifadelerini yazmak için birkaç yazım kuralını izleriz:
- test işlemini yapacağımız dosya için dosya ile aynı isimli
_test.goile biten bir ifade yazılmalıdır. Örneğinhesapla.goifadesinin testlerihesapla_test.goiçerisinde yer almalıdır. _test.goile biten ifademizdeki test fonksiyonufunc Test...şeklinde yazılmalıdır.hesapla.godosyamızdaOrtalama()isimli bir fonksiyon varsahesapla_test.godosyamızdaTestOrtalama()isminde bir fonksiyonumuz olmalıdır.- test fonksiyonunun scope içeriğinde
testingkütüphanesi kullanılmalıdır. Bu kütüphanede çağrılan veriler ve beklenenler tanımlanır. - test fonksiyonlarınız farklı paketler içerisinde yer alsa dahil
go test ./... -vifadesiyle tüm paketlerdeki testleri aynı anda çalıştırılabilir. - Belirli bir paketteki testleri çalıştırmak istediğimizde
go test ./paketadı -vifadesini kullanırız.
Not Hesaplama Uygulaması Testi
Konunun daha iyi anlaşılabilmesi adına üniversite vize final bütünleme notlarına ait bir hesaplayıcı yazıp hesaplayıcının .alışmasını test edelim.
Yeni bir proje oluşturduktan sonra aşağıdaki şekilde bir dosya ağacı oluşturalım :
.
├── go.mod
├── hesapla
│ ├── hesapla.go
│ └── hesapla_test.go
└── main.go
Buradaki amacımız subdirectory ler yani alt dosya yolları içerisinde bulunan testleri nasıl çalıştıracağımızı öğrenmektir.
Şimdi hesapla.go isimli dosyamıza girerek içine ortalama hesaplayan bir fonksiyon yazalım.
package hesapla
import "fmt"
func Ortalama(ogrenciNot []int) float64 {
// toplam
sum := 0.0
for _, v := range ogrenciNot {
fmt.Printf("Ogrenci Notu: %d \n", v)
sum += float64(v)
fmt.Printf("Sum : %f \n", sum)
}
ortalama := float64(sum) / float64(len(ogrenciNot))
fmt.Println(ortalama)
return ortalama
}
Yukarıda dikkat ettiyseniz package hesapla olarak paket ismini tanımladık. İşlemlerimizi bu paket içinde gerçekleştireceğiz.
package hesapla
import "testing"
func TestOrtalama(t *testing.T) {
got := Ortalama([]int{65, 83})
want := 74.0
if got != want {
t.Errorf("got %f ,want %f", got, want)
}
}
Burada yazdığımız test fonksiyonunu detaylı olarak inceleyelim. package hesapla içinde yer alması gerekiyor ki hesapla.go içinde yer alan Ortalama() isimli fonksiyonu tespit edebilmesi için bunu belirtmemiz gerekiyor. Diğer adımda ise test fonksiyonumuzu yazmaya başlıyoruz.
import "testing"
func TestOrtalama(t *testing.T) {
got := Ortalama([]int{65, 83})
want := 74.0
if got != want {
t.Errorf("got %f ,want %f", got, want)
}
}
fonksiyon adımıza Test... ibaresini başına koyarak başlıyoruz. GO bunun bir test fonksiyonu olduğunu bu şekilde tespit eder. ikinci aşamada (t *testing.T) pointer ifadesiyle testing.T fonksiyonunun vereceğimiz değişkenleri argüman olarak alıp test işlemlerini gerçekleştireceğini belirtiyoruz. got ve want olmak üzere iki adet değişken alır. Bu değişkenlerin ilki yani got test edeceğimiz değerleri içerir. ikincisi ise yani want beklenen değerleri içerir. Bizim işlemimizde 65 ve 83 sayılarını ortalama fonksiyonuna argüman olarak verdiğimizde sonucun 74 olarak dönmesi gerekir.
Bir sonraki ifademizde ise if got != want {} mantıksal sınaması gelir. Yani beklenen değer karşılanmazsa t.Errorf() aracılığıyla ekrana bir hata mesajı döndürürüz.
Kodumuzu komut satırı aracılığıyla çalıştıralım.
go test ./... -v
Burada ./ ifadesi ile bir üst dizine çık ve ... ifadesiyle bu dizinin altında olan ne kadar test dosyası varsa çalıştır dedik. -v ifadesiyle ise çalıştırılan testlere ait ekran görsellerini görüntüle dedik. Eğer -v vermezsek ekranda yalnızca PASS veya FAIL ifadelerini ve test süresini görüntüleriz. -v ifadesi verbose anlamına gelir ve yapılan işlemleri ekranda görüntülemeye yarar.
? gkybrc/nothesaplama [no test files]
=== RUN TestOrtalama
Ogrenci Notu: 65
Sum : 65.000000
Ogrenci Notu: 83
Sum : 148.000000
74
--- PASS: TestOrtalama (0.00s)
PASS
ok gkybrc/nothesaplama/hesapla (cached)
TAVSİYE: Çok fazla testiniz mevcutsa ve hızlı olarak geçmeyen değeri tespit etmek istiyorsanız
-vkullanmadan testin olduğu fonksiyonu tespit edin. Ardından da yalnızca bu fonksiyona ait değerleri kontrol edin.
Mevcut Bir Testi Atlama
Birden çok test yazdınız fakat bir ya da bir kaç tanesini tüm testlere dahil etmek istemiyorsanız t.Skip() isimli aşağıdaki ifadeyi kullanmanızı öneriririm:
func TestAtlanacakTest(t *testing.T){
t.Skip("Bu Test Atlanmıştır!")
}
Mevcut Projemize bir fonksiyon daha ekledik diyelim. Buna ait yazdığımız testi silmeden diğer testler arasından çıkartabilmek için bu ifadeyi ekleyeceğiz.
Şimdi projemize geri dönelim ve test atlamayı projemize ekleyelim.
hesapla_test.go isimli dosyamıza aşağıdaki eklemeleri yapalım :
package hesapla
import "testing"
func TestOrtalama(t *testing.T) {
got := Ortalama([]int{65, 83})
want := 74.0
if got != want {
t.Errorf("got %f ,want %f", got, want)
}
}
func TestOrtalamaTwo(t *testing.T) {
got := Ortalama([]int{45, 43, 84})
want := 57.333333333333336
if got != want {
t.Errorf("got %f want %f", got, want)
}
}
func TestHarfCevir(t *testing.T) {
t.Skip("Harf Ceviri testi atlandı!")
}
Şimdi de hesapla.go isimli dosyamıza gereken eklemeleri yapalım :
package hesapla
import (
"fmt"
)
func Ortalama(ogrencinot []int) float64 {
sum := 0.0
for _, v := range ogrencinot {
fmt.Printf("ogrenci notu: %d \n", v)
sum += float64(v)
fmt.Printf("sum : %f \n", sum)
}
ortalama := float64(sum) / float64(len(ogrencinot))
fmt.Println(ortalama)
return ortalama
}
func HarfCevir(ortalama float64) string {
/*
aa: 90 - 100 / başarılı
ba: 80 - 89 / başarılı
bb: 75 - 79 / başarılı
cb: 70 - 74 / başarılı
cc: 60 - 69 / başarılı
dc: 50 - 59 / koşullu başarılı - başarısız
dd: 40 - 49 / başarısız
fd: 30 - 39 / başarısız
ff: 0 - 29 / başarısız
*/
if ortalama >= 90.0 && ortalama <= 100.00 {
return "AA"
} else if ortalama >= 80.0 && ortalama <= 89.0 {
return "BA"
} else if ortalama >= 75.0 && ortalama <= 79.0 {
return "BB"
} else if ortalama >= 70.0 && ortalama <= 74.0 {
return "CB"
} else if ortalama >= 60.0 && ortalama <= 69.0 {
return "CC"
} else if ortalama >= 50.0 && ortalama <= 59.0 {
return "DC"
} else if ortalama >= 40.0 && ortalama <= 49.0 {
return "DD"
} else if ortalama >= 30.0 && ortalama <= 39.0 {
return "FD"
} else {
return "FF"
}
}
Şimdi test komutumuzu çalıştıralım :
go test ./... -v
Ekran çıktımız aşağıdaki şekilde olacaktır.
? gkybrc/nothesaplama [no test files]
=== RUN TestOrtalama
ogrenci notu: 65
sum : 65.000000
ogrenci notu: 83
sum : 148.000000
74
--- PASS: TestOrtalama (0.00s)
=== RUN TestOrtalamaTwo
ogrenci notu: 45
sum : 45.000000
ogrenci notu: 43
sum : 88.000000
ogrenci notu: 84
sum : 172.000000
57.333333333333336
--- PASS: TestOrtalamaTwo (0.00s)
=== RUN TestHarfCevir
hesapla_test.go:24: Harf Ceviri testi atlandı!
--- SKIP: TestHarfCevir (0.00s)
PASS
ok gkybrc/nothesaplama/hesapla (cached)
Burada dikkat ederseniz TestHarfCevir isimli testimiz atlandı ve buna ait ifade aşağıdaki şekilde eklendi.
=== RUN TestHarfCevir
hesapla_test.go:24: Harf Ceviri testi atlandı!
--- SKIP: TestHarfCevir (0.00s)
PASS
Testler ile ilgili daha fazla bilgi almak için PKG.GO.DEV - testing adresini inceleyebilirsiniz.
Examples
Examples TOC
hello-world
package main
import "fmt"
func main(){
fmt.Println("Hello World!")
}
Txt Reader
Ön Hazırlık
HATIRLATMA: Eğer
project,module,packagesistemleri ile ilgili ön bilginiz varsa ve bu konulara aşinaysanız bu admımı geçebilirsiniz!
Öncelikle yeni bir proje oluşturalım.
mkdir textreader_project
# go mod init projeLokasyonu/projeAdı
go mod init myawesomeproject/textreader
Şimdi cd komutu yardımıyla projenin içine girelim ve burada file.txt isimli bir text dosyası oluşturalım.
cd textreader_project/
touch file.txt
oluşturduğumuz txt dosyası içerisine rastgele bir metin kopyalayalım. Ben kendi projemde büyük Türk Edebiyatçısı Ahmet Kabaklı'ya ait bir metin kopyaladım.
"İşte arkadaşlar, bir memleketin yabancı harslar, yabancı dinler, yabancı kültürler, yabancı filmler, yabancı türküler, yabancı şarkılar, yabancı danslar tarafından kuşatılmasına "Kültür emperyalizmi" denir. Bu en öldürücü emperyalizmdir.
Ahmet Kabaklı
Ön hazırlıklarımız tamamlandıktan sonra projemize ait kodları yazmaya başlayalım.Text dosyalarının go ile okunması için farklı yöntemler mevcuttur. Öncelikle ilk yöntemi inceleyelim :
bufio ve os kütüphanelerini kullanarak Text Dosyası Okuma
bufio ve os isimli standart kütüphaneleri kullanarak okuma işlemini gerçekleştiriyoruz. Ayrıca log kütüphanesi aracılığıyla sistem loglarından hata mesajı döndüreceğiz.
Bu kütüphanelere ait kaynaklar :
Standart kütüphaneleri inceledikten sonra şimdi kodumuzu inceleyelim:
import (
"bufio"
"fmt"
"log"
"os"
)
func ReadTextFile(filename string) {
file, err := os.Open(filename) // Dosyayı aç ve file isimli bir değişkene ata
defer file.Close() // dosyayı işlem bittiğinde kapat ve hafızayı boşalt
// dosyayı açamazsan hata döndür
if err != nil {
log.Fatal(err)
}
reader := bufio.NewReader(file)
for {
line, _, err := reader.ReadLine()
if len(line) > 0 {
fmt.Printf("Readline: %q \n", line)
}
if err != nil {
break
}
}
func main(){
// longhand writing
// var filename string = "file.txt"
filename := "file.txt"
ReadTextFile(filename)
}
}
Kodumuzu parçalara ayırarak incelemeye başlıyoruz. İlk olarak Dosyanın açılması ve okunması aşamasıyla başlıyoruz.
func ReadTextFile(filename string) {
file, err := os.Open(filename) // Dosyayı aç ve file isimli bir değişkene ata
defer file.Close() // dosyayı işlem bittiğinde kapat ve hafızayı boşalt
// dosyayı açamazsan hata döndür
if err != nil {
log.Fatal(err)
}
// diğer kodlar
}
os.Open() fonksiyonu []byte cinsinden bir dosya ve err hata döndürür. Eğer dosya okuma başarılı olursa file isimli değişkene verileri []byte cinsinden atar. Eğer başarısız olursa bu sefer de err isimli bir hata mesajı döndürür.Bu aşamada hata yakalamk için if err =! nil {} boilerplate kodunu kullanıyoruz. Bu yapı genel geçer hata yakalama yapısıdır. Bunun haricinde de hata yakalama yapıları mevcuttur fakat internette en sık karşılaşacağınız yapı bu yapıdır.
func (){
// önceki kodlar
reader := bufio.NewReader(file) // byte olarak okunan file değişkenini string ifadesine çevir
for {
line, _, err := reader.ReadLine()
if len(line) > 0 {
fmt.Printf("Readline: %q \n", line)
}
if err != nil {
break
}
}
Kodun bu kısmında reader isimli bir değişken oluşturuyoruz ve bu değişkene []byte cinsinde mevcut olan file isimli verimizi argüman olarak veriyoruz. bufio.NewReader() isimli fonksiyon []byte cinsinden gelen değeri insan tarafından okunabilir (human-readable) bir forma dönüştürür. reader isimli değişken bir yığın olarak mevcut olduğundan dolayı satır satır okuma işlemi gerçekleştirmek için bir for{} döngüsü yazıyoruz. reader.Readline() isimli fonksiyonumuz (line []byte, isPrefix bool, err error) olmak üzere 3 adet değer döndürür. isPrefix değeri işlemimizde bize gerekmediği için onun yerine _ ifadesi kullanıyoruz. Okunursa satır değeri, okunamazsa hata değeri döndürür.
if len(line) > 0 {
fmt.Printf("Readline: %q \n", line)
}
Bu satırda ise eğer satır ifadesinin uzunluğu 0 'dan büyükse ekrana yazdırma işlemi gerçekleştir diyoruz. Yani satırda herhangi bir değer varsa bu değerin içerdiği satırı yazdır komutu veriyoruz.
if err != nil {
break
}
Eğer sistemde bir hata ile karşılaşırsan döngüyü kır ve işlemi sonlandır komutunu da bu şekilde yazıyoruz. Şimdi de konu çalıştırmadan önceki son işleme gelelim :
func main(){
// longhand writing
// var filename string = "file.txt"
filename := "file.txt"
ReadTextFile(filename)
}
}
Burada dosyamıza ait veri girişini yapıyoruz. filename := "file.txt" ile dosyamızın adını belirtip bunu oluşturduğumuz ReadTextFile(filename) fonksiyonumuza argüman olarak veriyoruz. Ekran çıktımız aşağıdaki şekilde olacaktır.
Readline: "\"İşte arkadaşlar, bir memleketin yabancı harslar, yabancı dinler, yabancı kültürler, yabancı filml
er, yabancı türküler, yabancı şarkılar, yabancı danslar tarafından kuşatılmasına \"Kültür emperyalizmi\" denir
. Bu en öldürücü emperyalizmdir."
Readline: "Ahmet Kabaklı"
os.ReadFile komutu ile ekrana yazdırma
func TXTReader(filename string){
file, err := os.ReadFile(filename)
defer fmt.Println("Dosya Kapatıldı")
if err != nil {
log.Fatal(err)
}
fmt.Println("Dosya açıldı ve işlem görüyor")
strData := string(file)
fmt.Print(strData)
}
Unicode

Dünyada bir çok yazı dili ve de çeşitli alfabe ve notasyonlar bulunmaktadır.Çince, Japonca, Sanskritce,Arapça, Kril vb. alfabeler farklı yazı karakterlerini barındırmaktadırlar.
Bilgisayar bilimlerinde bu karakterlerin farklı dillerdeki kullanıcılara ait bilgisayarlarda görüntülenmesi esnasında karışıklık olmaması adına unicode denilen bir karakter sisteminde tutulmaktadır.
TANIM:
unicodedünya üzerinde kayıtlı olan tüm karakterlerehexadecimal (onaltılık)bir değer atayan bir sistemdir.
Her karaktere hexadecimal bir değer atanmasının sebebi farklı bilgisayar sistemlerinde doğru karakterin görüntülenebilmesidir.

Misal Sanskritçe bir karakteri bir İngiliz, bir Alman veya bir Rus bilgisayarlarında sorunsuz olarak bu sistem sayesinde görüntüleyebilmektedir.
Detaylı Bilgi : Wikipedia - Unicode
Unicode Çalışma Mantığı
| hexadecimal value | rune (character) |
|---|---|
| ♄ | ♄ |
Latin alfabesinde yer almayan herhangi bir karakter bilgisayar hafızasında hexadecimal karşılığı ile saklanır. Bu sayede ilgili karakterin içinde yer aldığı web sayfası, metin editörü gibi yapılarda karakter kayıpsız ve net olarak görüntülenebilir.
Bu karakter hem Unicode sisteminde yer almaktadır, hem de 140.000'den fazla karakter barındıran Unicode'un ilgili alt kümesinde yer almaktadır.
Başka bir örnek verelim :
| hexadecimal | rune(character) |
|---|---|
| 0634 | ش |
Unicode hexadecimal değeri 0634 olan Arapça şın karakteri aynı zamanda, ISO-8859-6(Arabic) kümesinin de bir elemanıdır.
şın ISO-8859-6 içinde farklı bir hexadecimal ile gösterilebilir. Bilinen tüm karakterlerin evrensel kodlaması Unicode ile yapıldığı için karakterler bu sisteme çevrilerek değer ataması yapılır.
Bir metin özellikle ISO-8859-6 kodlamasıyla yazılmadığı sürece bilgisayar sisteminde Unicode olarak saklanır.
Detaylı Bilgi: Wikipedia : ISO IEC 8859-6
ASCII


ASCII, bilgisayarlar arasındaki elektronik iletişim için standart bir veri kodlama formatıdır.
Web sayfalarından ve kodlama yaparken sık karşılaştığımız bu format, American Standard Code for Information Interchange (Bilgi Değişimi için Amerikan Standart Kodu) 'dur. Bilgisayar sistemlerinin temel gelişimi ve kodlama dillerinin ortaya çıkışı Amerika birleşik devletleri temelli olduğu için karakter setleri de bu notasyona göre kodlanmıştır.
ASCII karakter kodlarını en bariz olarak Türkçe uzantılı web sayfalarında görebilirsiniz:
https://www.google.com/search?q=ceviz+%C5%9Feker+balkaba%C4%9F%C4%B1&
Yukarıdaki ifadede türkçe karakterlere ait değerlerin hexadecimal karşılıkları verilmiştir.
URL ifadeleri yalnızca ASCII karakter ifadelerini kullanabilir. Amerikan dilinde mevcut olmayan ş,ğ,ı sesleri ASCII ifadelerine % işareti önüne konularak kodlanır. % ifadeleri karakterler arasındaki boşlukları doldurmak için kullanılır. %C5%9F in ASCII karşılığı C5 9F 'tir.
| ASCII Kodu | Karakter Karşılığı (Rune) |
|---|---|
| %C5%9F | ş |
| %C4%9F | ğ |
| %C4%B1 | ı |
Yukarıdaki ifademizi bir URLDecoder ile çözdüğümüzde karşımıza :
https://www.google.com/search?q=ceviz+şeker+balkabağı&
şeklinde bir ifade gelecektir.
Ayrıca URLEncoding sitesinden de hangi karakterin neye tekabül ettiğini de görebilirsiniz.
UTF-8 Kodlaması
Unicode ifadelerin alt kümesi olan bu küme genellikle bilgisayar metinlerinde karşımıza çıkmaktadır. Sebebi de bilgisayar anahtar ifadesi olan 1 ve 0 lardan oluşmasıdır.
UTF-8 Kodlamasına çevrilen karakterler bilgisayar dilinde mevcut olan 1 ve 0 lardan oluşan sekiz haneli kodlamalar halinde tutulmaktadır. Bu da binary kodlama türüdür.
Misal vermek gerekirse:
| UTF-8 | Binary |
|---|---|
| merhaba | 01101101 01100101 01110010 01101000 01100001 01100010 01100001 |
Yukarıdaki tabloyu harf karşılıklarına çevirirsek:
| UTF-8 | Binary |
|---|---|
| m | 01101101 |
| e | 01100101 |
| r | 01110010 |
| h | 01101000 |
| a | 01100001 |
| b | 01100010 |
| a | 01100001 |
Özellikle kod editörlerinde UTF-8 kodlaması görmemimizin sebebi makine kodlaması olan binary ye geçiş kolaylığı sağlaması ve kodun hızlı bir şekilde makine tarafından tanınabilmesi içindir.
