Introduction

Logo

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

  1. Temel bilgiler konusunda dijital kitap boyunca hiçbir konuyu atlamamak adına ChatGPT ile oluşturulmuş bir konu listesi (topiclist) takip edilecektir.
  2. Bu konularla ilgili, konu düzeni değişebilir ve eklemeler olabilir.
  3. Bazı konular hakkında bilgiler ise yeri geldikçe örnek projelerin içerisinde verilecektir.
  4. 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:

gokayburuc@yandex.com

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

golang-logo

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

golang

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

VersiyonYayın TarihiYılıAçıklama
go1.02012-03-282012
go1.12013-05-132013
go1.22013-12-012013
go1.32014-06-182014
go1.42014-12-102014
go1.52015-08-192015
go1.62016-02-172016
go1.72016-08-152016
go1.82017-02-162017
go1.92017-08-242017
go1.102018-02-162018
go1.112018-08-242018
go1.122019-02-252019
go1.132019-09-032019
go1.142020-02-252020
go1.152020-08-112020
go1.162021-02-162021
go1.172021-08-162021
go1.182022-03-152022
go1.192022-08-022022
go1.202023-02-012023
go1.212023-08-082023
go1.222024-02-062024

GO Dili Kullanan Büyük Sirketler ve Projeleri

Kaynak: brainhub.eu - Companies Using Golang - Top 7 Popular Apps You Use Everyday

Google

google-logo

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

uber-logo

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
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-logo

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-logo 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

dropbox-logo

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

soundcloud-logo

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

setup-website

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-install

Linux Terminal

  1. https://go.dev/dl/ web sitesine gidin
  2. 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.

  1. İ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/go klasörünü tamamen silip kaldırmadan aynı klasöre tar dosyasını açmayın.

  1. .profile dosyanızda dosyanızda PATH ortam değişkenine /usr/local/go/bin ekleyin. Bunu $HOME/.profile veya /etc/profile dosyanı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/.profile gibi bir komut kullanarak profilden yürütün.

  1. 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

  1. İ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
  1. snap üzerinden GO indir.
snap install go
  1. Sistem üzerinden go versiyonu kontrol edilir.
go version

Mac

  1. İndirdiğiniz paket dosyasını açın ve Go'yu yüklemek için talimatları izleyin.
  2. Paket, Go dağıtımını /usr/local/go konumuna kurar. Paket /usr/local/go/bindizinini PATH ortam değişkeninize konulmalıdır.
  3. Değişikliğin etkili olması için tüm açık Terminal oturumlarını yeniden başlatmanız gerekebilir.
  4. Bir komut istemi açıp aşağıdaki komutu yazarak Go'yu yüklediğinizi doğrulayın:
go version

Windows

  1. İlgili windows MSI dosyasını indirin.
  2. İndirdiğiniz MSI dosyasını açın ve Go'yu yüklemek için talimatları izleyin.
  3. Varsayılan olarak yükleyici, Program Dosyalarına Git veya Program Dosyalarına (x86) kuracaktır. Konumu gerektiği gibi değiştirebilirsiniz.
  4. 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.
  5. Go'yu yüklediğinizi doğrulayın.
  6. Windows'ta, Başlat menüsüne tıklayın.
  7. Menünün arama kutusuna cmd yazın ve ardından Enter tuşuna basın.
  8. Görünen Komut İstemi penceresinde aşağıdaki komutu yazın:
go version
  1. Komutun yüklü Go sürümünü yazdırdığını onaylayın.

WEBSITE: https://go.dev/dl/

Değişkenler - Variables

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 TipiAçıklama
floatondalıklı sayı değerleri
inttam sayı değerleri
booltrue ve false değerleri
stringmetin değerleri
runekarakter değerleri
bytebyte cinsinden veri degerleri
slicedizi parçaları
arraydizi değerleri
mapanahtar - değer (key-value) ikililerinden oluşan veri kümeleri
structyapısal veri türleri
interfacekapsayı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: string olarak saklanan bir değişkene, bir tam sayı integer veya ondalıklı sayı float değer verirsek, veyahut mantıksal bool bir 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: const ifadeleri scope iç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, float64 vs.
  • String - string
  • Boolean - bool
  • Character - rune byte() 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.

  1. Metin Sabitleri - String Literals
  2. Sayısal Sabitler - (Integer, Float , Complex )
  3. Boolean Sabitleri

Metin Sabitleri

  • Go, iki tür değişmez stringyi destekler, yani ” ” (çift tırnak stili) ve ' '(geri alıntı).
  • string ifadeler + 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.
  • string türlerinin sıfır değerleri, değişmez olarak ” ” veya ” ile gösterilebilen boş stringlerdir.
  • string tü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.

byte-and-bytes

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.

byte

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
floatOndalıklı Sayı ifadelerivar x float64 = 13.21
intTam Sayı ifadelerivar y int = 5
boolMantıksal Sınama ifadelerivar existence bool = true
stringMetin ifadelerivar selamlama string = "Merhaba dünya!"
runeKarakter ifadeleriItem3.2
byteByte Cinsinden ifadelerItem3.2

Karmaşık Veri Tipleri

Değiken AdıDeğişken AçıklamasıÖrnek İfade
arrayDizi ifadelerivar myarr = [3]int{3,12,21}
sliceDizi kesiti ifadelerivar myslice := myarr[0:2]
mapHashmap ifadelerivar city = [string]int{"Edirne":22, "Kırklareli":39,"Tekirdağ":59 }
structStruct ifadelerivar person = Person{"Gokay","BURUC",1987}
interfaceInterface ifadelerItem3.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 TipiBit / Bayt Cinsinden KonumuDeğer Aralığı
int32bit sistem veya 64bit sisteme göre GO tarafından otomatik seçilir-2147483648 ile 2147483647 aralığında (32bit) -9223372036854775808 ile 9223372036854775807 aralığında(64bit)
int88bit / 1 bayt-128 ile 127 aralığında
int1616bit / 2 bayt-32768 ile 32767
int3232bit /4 bayt-2147483648 ile 2147483647
int6464bit / 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 TipiAçı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.

  1. 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
  2. Aggregate type (Toplanmış Veri Tipi): Dizi ve yapılar bu kategoriye girer. array, struct
  3. Reference type (Referans Veri Tipi): İşaretçiler, dilimler, haritalar, işlevler ve kanallar bu kategoriye girer. pointer, slice, map, function, channel
  4. Interface Type (Arayüz Veri Tipi) : Arayüzler bu veri tipinde yer alır. interface

Veri TipiAçıklamaVeri Tipi İsimleri
Temel Veri TipiSayılar,ondalıklı ifadeler, kompleks sayılar, metinler ve mantıksal ifadelerint, float, string,bool,complex
Toplanmış Veri TipleriDiziler ve yapı ifadeleri bu kategoriye girerarray,struct
Referans Veri Tipleriİşaretçiler, dilimler, haritalar, işlevler, kanallar bu veri tipine girerpointer,slice,map,function,channel
Arayüz veri TipiArayüz ifadeleri bu veri tipine girerinterface

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.

  1. String (string)
  2. Int (int8, int16, int32, int64)
  3. Float ( float32, float64)
  4. Boolean (bool)
  5. 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 TipiAçıklamaVeri Aralığı
int88-bit signed integer-127 ve 128 değerleri arasında yer alır ( signed integer)
int1616-bit signed integer-32,768 ve +32,767 değerleri arasında yer alır
int3232-bit signed integer-2147483648 to 2147483647 değerleri arasında yer alır
int6464-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.
uint88-bit unsigned integer0 ve 255 değerleri arasında yer alır
uint1616-bit unsigned integer0 ve 65535 değerleri arasında yer alır
uint3232-bit unsigned integer0 ve 4294967295 değerleri arasında yer alır
uint6464-bit unsigned integer0 ve (2^64) değerleri arasında yer alır
intHem int hem de uint, 32 veya 64 bit olmak üzere aynı boyutu içerir.
uintHem 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.
byteuint8 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.

uint veri tipi yalnızca pozitif değerleri alabilen int veri tiplerini tanımlar. Bir veri tipinin başına u ifadesi 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 TipiAçıklamaDeğer Aralığı
float3232-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 .
float6464-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 TipiAçıklama
truekoşul sınamasının olumlu olması sonucu dönen değer
falsekoş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ı true ve false olarak 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 TipiAçıklama
complex64float32 değerini reel (j) ve imajiner (i) kısımda tutan deger
complex128float64 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

  1. https://www.geeksforgeeks.org/data-types-in-go/
  2. https://go.dev/tour/basics/11
  3. 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:

  1. Öğe sayısı belirterek dizi tanımlama
  2. Öğ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 a dizisinin ilk değeri a[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 :

  • int ve float değerler için varsayılan : 0
  • string ifadeler için varsayılan : null (boşluk karakteri)
  • bool ifadeler 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: cities ifadesinde tanımlama sonrasında append () 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 key ifadeleri veri tipi olarak string, float, int gibi yapılar alabilir. Fakat, array, slice,struct gibi ifadeler key olarak 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.

railroad-switch

Switch Türleri

Go dili, iki tür anahtar deyimini destekler:

  1. İfade Switch
  2. 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

  • switch ifadesi sonrasında yazılan optexpression ve optstatement opsiyonel ifadelerdir.
  • Eğer hem optexpression hem de optstatement ifadede mevcut ise ikisinin arasına noktalı virgül ; işareti koymak gereklidir.
  • Eğer switch herhangi bir ifade içermiyorsa, derleyici ifadenin true olduğunu varsayar.
  • optstatement değişken tanımlamaları, artış ya da atama durumları, fonksiyon çağrıları vb. gibi basit durumları içerir.
  • Eğer değişken optstatement içerisinde görüntülenirse, değişkenin aralığı switch ifadesi ile sınırlıdır.
  • switch ve case ifadeleri dahili olarak break ile durdurulmaz. Fakat şart sağlandığında otomatik olarak anahtar değeri değişir ve bir sonraki aşamaya geçilir.
  • default ifadesi switch ifadesinde isteğe bağlı olarak kullanılır.
  • case ifadesi birden fazla değişken içerirse bu ifadeler virgül (,) ile ayrılırlar.
  • Eğer case ifadesi herhangi bir ifade içermiyorsa, sistem bu ifadenin değerinin true kabul 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 optstatement birden fazla değer içeriyorsa ve bu değerler virgül(,) ile ayrılırlar.
  • switch deyimi türünde, case ve default deyimi herhangi bir break deyimi içermez. Ancak, programınız gerekliyse, break ve fallthrough ifadesini kullanmanıza izin verilir.
  • typeswitchexpression' de default isteğe bağlıdır.
  • typeswitchexpression, sonucu bir tür olan bir ifadedir.
  • typeswitchexpression iç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. case yan tümcesi iki veya daha fazla tür içeriyorsa, değişkenin türü, typeswitchexpression iç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.

method ve func arasındaki fark, method ifadeleri tanımlanmış bir alıcıya sahiptirler, yani bir nesneni örnek yapısında mevcut olan bir işlev olarak yer alırlar. func ifadeleri 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 Get ve Set gereklilik değil yalnızca açıklamadır. Tercih edilebilir. Örneğin obj.Owner() veya obj.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,variable adı 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(); veya fmt.Println()
  • Bir scope açılıp kapatılırken süslü parantez {} kullanılır.
  • struct ifadelerinde açıklayıcı ifadeler kullanılması tercih edilir String ifadesi yerine ToString ifadesi 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.txt dosyası 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: pointer ifadelerinde declaration ve initialization kavramları için iki farklı sembol yer alır:

  • & ifadesi : initialization ifadesidir. Yani pointer ifadesine ait adresi başlatmak için kullanılır.
  • '*' ifadesi : declaration ifadesidir. Bir ifade önüne bu konulduğunda bu ifadenin pointer olarak 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 assertion eğer sistem belirtilen yapıyı elde edemezse panic ifadesiyle 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:

  1. test işlemini yapacağımız dosya için dosya ile aynı isimli _test.go ile biten bir ifade yazılmalıdır. Örneğin hesapla.go ifadesinin testleri hesapla_test.go içerisinde yer almalıdır.
  2. _test.go ile biten ifademizdeki test fonksiyonu func Test... şeklinde yazılmalıdır. hesapla.go dosyamızda Ortalama() isimli bir fonksiyon varsa hesapla_test.go dosyamızda TestOrtalama() isminde bir fonksiyonumuz olmalıdır.
  3. test fonksiyonunun scope içeriğinde testing kütüphanesi kullanılmalıdır. Bu kütüphanede çağrılan veriler ve beklenenler tanımlanır.
  4. test fonksiyonlarınız farklı paketler içerisinde yer alsa dahil go test ./... -v ifadesiyle tüm paketlerdeki testleri aynı anda çalıştırılabilir.
  5. Belirli bir paketteki testleri çalıştırmak istediğimizde go test ./paketadı -v ifadesini 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 -v kullanmadan 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,package sistemleri 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

unicode-image

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: unicode dünya üzerinde kayıtlı olan tüm karakterlere hexadecimal (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.

connection-diagram

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 valuerune (character)
&#x2644

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 :

hexadecimalrune(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-table

ascii-table-two

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 KoduKarakter 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-8Binary
merhaba01101101 01100101 01110010 01101000 01100001 01100010 01100001

Yukarıdaki tabloyu harf karşılıklarına çevirirsek:

UTF-8Binary
m01101101
e01100101
r01110010
h01101000
a01100001
b01100010
a01100001

Ö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.