Programimi në Go
Programimi në Go
Tahir Hoxha
Buy on Leanpub

Table of Contents

Parathënie

Kontakti me autorin: tahir.hoxha@gmail.com
Linkedin: https://www.linkedin.com/in/tahirhoxha/

Ky libër shërben si material ndihmës gjatë mbajtjes së kursit të Go në AUK/TDI. Përmbajtja ndryshon në vazhdimësi sepse shtohen materiale të reja në mënyrë periodike.

Hyrje

Gjuha programore Go është gjuhë programore e re e krijuar nga Google, së pari e prezantuar në vitin 2009. Autorë të Go janë Robert Griesemer, Rob Pike dhe Ken Thompson.

Go është gjuhë programore me dedikim të përgjithshëm që karakterizohet me një sintaksë të pastër dhe të thjeshtë. Është i disponueshëm në sisteme operative të ndryshme, posedon dokumentacion të shkëlqyeshëm dhe komunitet në rritje e sipër.

Go adopton koncepte të ndryshme nga gjuhët e tjera programore, gjithnjë duke i ikur kompleksitetit të panevojshëm dhe krijimit të një kodi sa më efiçent dhe të menaxhueshëm. Implementon një koncept të veçantë të OOP (object oriented programming) dhe ofron menaxhim automatik të memorjes (garbage collection).

Duke qenë më i thjeshtë se shumica e gjuhëve të tjera, është lehtë i kuptueshëm nga ana e programuesve që njohin ndonjë gjuhë tjetër programore. Përdoret për programim procedural, ka karakteristika të programimit funksional, por siç u cek më sipër - edhe një variant të thjeshtuar të programimit të bazuar në objekte.

Go po tregohet i shkëlqyeshëm në veçanti në ndërtimin e Web aplikacioneve dhe mikroserviseve, ku karakterizohet me ekzekutim të shpejtë dhe procesim të një numri të madh të HTTP kërkesave (HTTP requests) dhe HTTP përgjigjeve (HTTP responses). Megjithatë, Go nuk është i kufizuar në aplikacione të këtij lloji - ai gjithsesi mund të tregohet shumë i mirë edhe me grafikë, mësim makinerik (machine learning), aplikacione mobile, etj.

Karakteristikë tjetër e Go është se ai është open source, kështu që të gjitha mjetet e nevojshme për punë u janë në dispozicion gjithkujt dhe janë pa pagesë.

Go si gjuhë e re progamore është krijuar në radhë të parë për të qenë gjuhë e thjeshtë edhe efiçente për ndërtimin e back-end sistemeve. Go në veçanti ka fituar popullaritet si gjuhë për shkrimin e Web aplikacioneve dhe mikroserviseve.

Go një set të begatshëm dhe gjithpërfshirës të librarive standarde.

Në sintaksë është i ngjashëm me C, por ka veti unike të cilat nuk i hasim në gjuhët e tjera. Ndonëse konsiderohet si trashëgimtar i C, Go në fakt trashëgon edhe karakteristika nga gjuhë të ndryshme programore. Është i ngjashëm me C për sa i përket strukturave kontrolluese (control-flow statements), tipeve bazike të të dhënave (basic data types), sintaksën e shprehjeve (expression syntax), etj.

Ndër objektivat primare të Go është që për nga performanca të jetë i krahasueshëm me C, gjë që nuk është arritur në tërësi. Megjithatë, Go tregon performancë superiore në krahasim me gjuhët e interpretuara, në veçanti kur kemi të bëjmë me ekzekutimin konkurrent të kodit nëpërmes goroutines, me anë të të cilave mundësohet procesimi i një numri të madh të kërkesave njëkohësisht. Kjo në veçanti është e rëndësishme për Web aplikacionet, të cilat duhet t’iu nënshtrohen kërkesave të shumëfishta në të njëjtën kohë.

Tri qëllime kryesore të dizajnuesve të Go kanë qenë:

  • Tipizimi statik (static type) dhe efiçenca gjatë kohës së ekzekutimit (run-time effeciency)
  • Lexueshmëria dhe përdorshmëria
  • Performanca e lartë në operacionet rrjetor dhe multiprocesim

Është gjuhë statically typed, veti kjo që e diferencon nga gjuhët me tipe dinamike siç janë PHP, Python, Ruby, etj. Te gjuhët programore statically typed, variablat deklarohen në mënyrë eksplicite përfshirë këtu edhe tipin e variablit (nëse është numër i plotë, numër me presje dhjetore, tekst, etj). Këtij grupimi të gjuhëve programore i përkasin edhe gjuhët: Java, C, C#, C++, Scala, etj.

Programi në Go duhet të kompajlohet para se të ekzekutohet. Kjo nënkupton që kodi shkruhet në një tekst editor, më pas thirret kompajleri i cili nga kodi burimor (source code), e krijon kodin ekzekutues në gjuhën makinerike. Kjo procedurë duhet të ndiqet pas çdo ndryshimi në kodin burimor.

Ekzektimi i programit të shkruar në Go është shumë më i shpejtë se i atyre të shkruara në gjuhët e interpretuara siç janë PHP apo Python dhe për shkak se është statically typed, shumë më rrallë mund të paraqiten gabime gjatë ekzekutimit, sepse shumë prej gabimeve mund të detektohen gjatë fazës së kompajlimit, pra para ekzekutimit.

Go mundëson gjenerimin e kodit ekzekutues për sisteme operative të ndryshme: Windows, Linux apo macOS, pa marrë parasysh në cilin sistem operativ jeni duke e shkruar kodin burimor. Pa marrë parasysh sa fajlla me kod burimor keni krijuar, gjatë procesit të kompajlimit në Go do të krijohet një fajll me kod ekzekutues.

Kompanitë që e përdorin gjuhën programore Go në projektet e tyre:

https://github.com/golang/go/wiki/GoUsers

Instalimi

Go mund të instalohet nga adresa:

https://golang.org/dl/

ku e zgjedhni platformën e dëshiruar: Microsoft Windows, Apple macOS apo Linux. Rekomandohet të zgjedhet versioni i fundit, që në momentin e shkrimit është 1.12.1.

Për distribuimin binar të instaluesit për Windows 64 bitësh zgjedhet go1.12.1.windows-amd64.msi, ndërsa për versionin 32 bitësh zgjedhet go1.12.1.windows-386.msi.

Në mënyrë standarde, Go instalohet në folderin C:Go .

Për të kaluar në një version më të ri, shkarkohet instaluesi i versionit të ri dhe gjatë ekzekutimit ai automatikisht do ta largojë versionin ekzistues nga sistemi për ta instaluar versionin e ri, pa i prekur projektet ekzistuese. I njëjti instalues mund të përdoret edhe për deinstalimin e Go nga sistemi.

Pas instalimit, kujdesemi që variabli i ambientit GOPATH të tregojë lokacionin e dëshiruar për projektet tona. Në shembullin e më poshtëm është zgjedhur folderi go brenda folderit të përcaktuar me variablin %USERPROFILE%, që në shumicën e rasteve nënkupton C:UsersPerdoruesi , por mund ta zgjedhim cilindo folder, sipas preferencës tonë.

GOPATH

Editorët

Për ta shkruar kodin në Go, mund të përdoret cilido tekst editor. Megjithatë, ekzistojnë programe të cilat janë të specializuar për lehtësimin e programimit në këtë gjuhë, siç janë:

  • GoLand nga JetBrains. Komercial.
  • Visual Studio Code. Pa pagesë.
  • Atom. Pa pagesë.

Në Visual Studio Code duhet të instalohet plugin-i për Go duke shkuar në File - Extensions - Go.

Për testimin e programeve të thjeshta, mund të përdoret Go Playground.

https://play.golang.org/

Go Playground

Hello World në Go

1 package main
2 import "fmt"
3 
4 func main() {
5    fmt.Println("Përshëndetje")
6 }

https://play.golang.org/p/vc4MvaHUPo2

Funksioni main() është pika hyrëse (entry point) e programit, respektivisht nga këtu fillon ekzekutimi i kodit, prej nga pastaj mund të thirren funksionet e tjera, me çka vazhdon rrjedha e ekzekutimit të programit.

Udhëzime

Nëse jeni të detyruar ta kopjoni kodin nga libri, dhe me atë rast ju dalin edhe numrat rendorë të rreshtave programorë, rreshtat mund t’i largoni automatikisht duke e kopjuar kodin dhe bartur në faqen:

http://remove-line-numbers.ruurtjan.com/

Variablat e ambientit

$GOROOT

$GOROOT_FINAL

$GOOS and $GOARCH

Sistemet operative dhe arkitekturat e kompajlimit.

$GOOS - $GOARCH kombinimet

  • android / arm
  • darwin / 386
  • darwin / amd64
  • darwin / arm
  • darwin / arm64
  • dragonfly / amd64
  • freebsd / 386
  • freebsd / amd64
  • freebsd / arm
  • linux / 386
  • linux / amd64
  • linux / arm
  • linux / arm64
  • linux / ppc64
  • linux / ppc64le
  • linux / mips
  • linux / mipsle
  • linux / mips64
  • linux / mips64le
  • linux / s390x
  • netbsd / 386
  • netbsd / amd64
  • netbsd / arm
  • openbsd / 386
  • openbsd / amd64
  • openbsd / arm
  • plan9 / 386
  • plan9 / amd64
  • solaris / amd64
  • windows / 386
  • windows / amd64

$GOHOSTOS / $GOHOSTARCH

$GOBIN

Rregullat e përgjithshme

Sintaksa e Go kërkon përdorimin e pikëpresjes (semicolons ;) si statement terminator. Mirëpo vendosja e pikëpresjes nuk është e domosdoshme sepse këtë e bën kompajleri në mënyrë automatike në fund të rreshtave programorë.

Vendosja manuale e pikëpresjes është e domosdoshme kur dëshirojmë t’i vendosim disa statements në një rresht programor. Po ashtu edhe kur kemi të bëjmë me ciklet for, me atë ai rresht të përfundojë me {, shenjë që përdoret për hapjen e bllokut të ciklit. Në të kundërtën, kompajleri do të vendosë automatikisht pikëpresje në fund të inkrementit (i++), me çka vie deri te gabimi gjatë kompajlimit.

1 	for i := 0; i < 5; i++ {
2 		fmt.Println(i)
3 	}

https://play.golang.org/p/4sgL67gpCBh

Gabim:

1 	for i := 0; i < 5; i++ 
2 	{
3 		fmt.Println(i)
4 	}

Në këtë rast, lajmërohet gabimi: “syntax error: unexpected newline, expecting { after for clause”.

Në përgjithësi, kllapa hapëse e bllokut duhet të vendoset në fund të rreshtit dhe jo në rresht të ri. Nëse kllapat hapëse i kemi vendosur në rresht të ri, në editor mund ta bëjmë ri-formatimin e kodit për të qenë konform me rregullat e Go. Në GoLand kjo bëhet me Code - Reformat Code.

Emërtimet e fajllave dhe identifikatorëve

Programet përbëhen nga:

  • Fjalët kyçe (keywords),
  • Konstantat (constants),
  • Variablat (variables),
  • Operatorët (operators),
  • Tipet (types), dhe
  • Funksionet (functions).

Go i përket gjuhëve programore case-sensitive, pra është ka rëndësi nëse shkronjat janë të mëdha (uppercase) apo të vogla (lowercase).

Emërtimet e variablave fillojnë patjetër me shkronjë, ku si shkronjë konsiderohet cilido Unicode UTF-8 karakter. Në vazhdim të shkronjës së parë mund të përdorim shkronja të tjera apo numra.

Shenja _ e shënuar si e vetme ka kuptim të veçantë dhe quhet blank identifier. Përdoret në rastet kur ndonjë nga vlerat kthyese të funksionit nuk dëshirojmë t’i ruajmë si vlerë në ndonjë variabël, respektivisht kur dëshirojmë ta injorojmë një vlerë kthyese të funksionit.

Ka raste kur variablat, tipet dhe funksionet nuk kanë nevojë fare të kenë emra dhe këto quhen anonime (anonymous).

Në vijim janë të shënuara 25 fjalët kyçe (keywords) të Go:

  • break
  • case
  • chan
  • const
  • continue
  • default
  • defer
  • else
  • fallthrough
  • for
  • func
  • go
  • goto
  • if
  • import
  • interface
  • map
  • package
  • range
  • return
  • select
  • struct
  • switch
  • type
  • var

Një fjalë kyçe nuk mund të përdoret si identifikator.

Përveç fjalëve kyçe, Go ka dhe 36 identifikatorë të paradefinuar, të cilët paraqesin emrat tipeve elementare dhe të funksioneve interne (built-in).

  • append
  • bool
  • byte
  • cap
  • close
  • complex
  • complex64
  • complex128
  • uint16
  • copy
  • false
  • float32
  • float64
  • imag
  • int
  • int8
  • int16
  • uint32
  • int32
  • int64
  • iota
  • len
  • make
  • new
  • nil
  • panic
  • uint64
  • print
  • println
  • real
  • recover
  • string
  • true
  • uint
  • uint8
  • uintptr

Delimiterët

Në Go përdoren delimiterët vijues:

  • Kllapat (parentheses) ( ),
  • Kllapat e mesme (brackets) [ ]
  • Kllapat e mëdha (braces) { }.

Shenjat e pikësimit Si shenja të pikësimit përdoren:

  • .
  • ,
  • ;

Strukturimi i kodit

Kodi strukturohet në formë të formulimeve (statements). Formulimet nuk është e domosdoshme të përfundojnë me pikëpresje (;) si në disa gjuhë të tjera, sepse kompajeli i Go e bën futjen automatike të pikëpresjes në fund të çdo formulimi. Në rastet kur i vendosim disa formulime në të njëjtin rresht, atëherë formulimet patjetër duhet të ndahen me pikëpresje.

Struktura bazike dhe komponentet e një Go programi

1 package main
2 
3 import (
4 	"fmt"
5 )
6 
7 func main() {
8 	fmt.Println("Hello, playground")
9 }

Struktura e përgjithshme e një programi në Go

  • Bëhet importimi i pakove
  • Pas importimit, deklarohen konstantat, variablat dhe tipet
  • Pastaj vie funksioni init() nëse është i nevojshëm dhe ky është një funksion special që e përmban çdo pako dhe që ekzekutohet i pari.
  • Më pas vie funksioni main() dhe atë vetëm në kuadër të pakos main.
  • Pastaj renditen të gjitha funksionet tjera. Metodat ndaj tipeve në fillim ose funksionet me atë renditje si thirren brenda funksionit main(), ose metodat dhe funksionet të renditura sipas alfabetit nëse numri i funksioneve është i madh.

Pakot

Me anë të pakove bëhet strukturimi i kodit. Programi ndërtohet si pako që mund t’i pakot tjetra për funksione të caktuara.

Çdo Go fajll i përket një pakoje, analogjikisht me bibliotekat apo namespace në gjuhët tjera.
Shumë Go fajlla të ndryshëm mund t’i përkasin një pakoje.

Emri i pakos shënohet të rreshtin e parë të programit, për shembull: package main.

Fajlli i pavarur ekzekutabil i përket pakos main. Çdo Go aplikacion përmban një pako të quajtu main.

Një aplikacion mund të përbëhet nga pako të ndryshme, por edhe në rastet kur përdoret pakoja main, nuk është e domosdoshme që i tërë kodi të vendoset brenda një fajlli të madh. Mund të krijohen disa fajlla të vegjël, ku secili duhet ta ketë package main në rreshtin e parë.

Variablat

Deklarimi dhe inicializimi i variablave

Variabli deklarohet me var, pas të cilit shënohet emri i variablit e pas emrit shënohet tipi i variablit.

var x int

Në këtë rast është deklaruar variabli x i tipit integer, pra numër i plotë.

Shohim se ka dallim prej gjuhëve të tjera siç është për shembull Java, ku njëherë ceket tipi e pastaj emri i variablit.

Variabli nuk mund të deklarohet me var pa e cekur tipin.

Variabli nuk është inicializuar, pra nuk i është dhënë një vlerë fillestare. Megjithatë, në Go, çdo tip i të dhënave kanë vlera iniciale standarde edhe kur nuk janë të inicializuara në mënyrë eksplicite, që për integer është 0.

 1 package main
 2 
 3 import (
 4 	"fmt"
 5 )
 6 
 7 func main() {
 8 	var x int
 9 	fmt.Println(x)
10 }

https://play.golang.org/p/EoS6ka-viIh

Rezultati:

1 0

Deklarimi dhe inicializimi i variablit mund të bëhet në një rresht:

var x int = 5

Në këtë rast, është deklaruar variabli x, i tipit integer, me vlerë 5.

Variabli mund të deklarohet në një rresht, e të inicializohet në një rresht tjetër më poshtë përgjatë rrjedhës së ekzekutimit të programit:

1 var x int
2 int = 5

Tipe int ka disa: int, int8, int16, int32, int64, uint, uint8, uint16, uint32, uint64. Nuk mund të kryhen operacione ndërmjet tipeve të ndryshme, për shembull të kryhet operacioni i mbledhjes ndërmjet numrit të tipit int8 dhe një numri tjetër të tipit int16.

 1 package main
 2 
 3 import (
 4 	"fmt"
 5 )
 6 
 7 func main() {
 8 	var x int = 5
 9 	var y int8 = 9
10 
11 	fmt.Println(x + y)
12 }

https://play.golang.org/p/1SqAca5tPkC

Rezultati:

1 invalid operation: x + y (mismatched types int and int8)

Për ta mundësuar operacionin e mbledhjes në shembullin e mësipërm, duhet ta konvertojmë vlerën int8int, ashtu që të dy numrat t’i përkasin tipit të njëjtë. Kjo bëhet me funksionin int().

 1 package main
 2 
 3 import (
 4 	"fmt"
 5 )
 6 
 7 func main() {
 8 	var x int = 5
 9 	var y int8 = 9
10 
11 	fmt.Println(x + int(y))
12 }

https://play.golang.org/p/TzThfTDL0YN

Rezultati:

1 14

Pra, Go jo vetëm që nuk lejon operacione ndërmjet tipeve të ndryshme (float dhe int), por nuk lejon as ndërmjet nëntipeve të tipit të njëjtë (int8, int16,…).

Ekziston edhe një formë e shkurtër e deklarimit dhe inicializimit të një variabli:

 1 package main
 2 
 3 import (
 4 	"fmt"
 5 )
 6 
 7 func main() {
 8 	x := 5
 9 	y := 9
10 
11 	fmt.Println(x + int(y))
12 }

https://play.golang.org/p/QzuRvnaXmfx

Rezultati:

1 14

Go do ta determinojë tipin e variablit në bazë të tipit të vlerës (type inference). Për shkak se 5 është tip int, edhe variabli x do të jetë i po atij tipi.

Kjo formë e deklarimit dhe inicializimit është e lejuar vetëm brenda funksioneve.

Me këtë sintaksë, Go do ta deklarojë variablin gjithmonë si int, nëse numri është i plotë dhe vlera e numrit është brenda intervalit: -2.147.483.648 deri 2.147.483.647, në platformat 32 bitëshe. Në platformat 64 bitëshe vlera duhet të jetë ndërmjet -9.223.372.036.854.775.808 dhe 9.223.372.036.854.775.807.

 1 package main
 2 
 3 import (
 4 	"fmt"
 5 )
 6 
 7 func main() {
 8 	x := 2147483647
 9 	fmt.Printf("%T", x)
10 }

https://play.golang.org/p/_UU6nZazVIF

Rezultati:

1 int

Me fmt.Printf("%T", x) kërkojmë të na tregohet tipi i vlerës së variablit x.

Nëse vlera e numrit është jashtë intervalit të cekur, në varësi të platformës (32 bit apo 64 bit), Go do ta shfaqë gabimin

constant 2147483648 overflows int

gjatë kompajlimit. Në vend të vlerës 2147483648 do të jetë cilado vlerë e shënuar si vlerë e variablit e që është jashtë intervalit të lejuar.

Pas deklarimit, variabli mund të marrë çfarëdo vlere të tipit përkatës dhe intervalit të lejuar të vlerave.

 1 package main
 2 
 3 import "fmt"
 4 
 5 func main() {
 6 	x := 5
 7 	x = 9
 8 
 9 	fmt.Println(x)
10 }

https://play.golang.org/p/MfgOksB1dHW

Rezultati:

1 9

Në Go është i lejuar deklarimi i disa variablave në rreshtin e njëjtë. Nëse është bërë inicializimi në të njëjtin rresht, mund të mos e shënojmë tipin sepse Go ia jep variablës tipin sipas vlerës (type inference).

 1 package main
 2 
 3 import "fmt"
 4 
 5 func main() {
 6 	var x, y int
 7 	x, y = 1, 3
 8 
 9 	var a, b, c = 5.0, 8.5, 3.8
10 	// Njëjtë sikur
11 	// var a, b, c float32 = 5.0, 8.5, 3.8
12 
13 	fmt.Println("x=", x, "y=", y, "a=", a, "b=", b, "c=", c)
14 }

https://play.golang.org/p/hc0WAre2L-E

Rezultati

1 x= 1 y= 3 a= 5 b= 8.5 c= 3.8

Variablat mund të deklarohen dhe inicializohen edhe në grupe, të paraprira me var, ndërsa brenda kllapave vendosen rreshtat me deklarime/inicializime.

 1 package main
 2 
 3 import "fmt"
 4 
 5 func main() {
 6 	var (
 7 		a = 5
 8 		b = 3.2
 9 		c = 8
10 	)
11 	fmt.Println("a=", a, "b=", b, "c=", c)
12 }

https://play.golang.org/p/1eWPvTFQizm

Rezultati:

1 a= 5 b= 3.2 c= 8

Rideklarimi i variablave

Variabli nuk mund të deklarohet më shumë se njëherë brenda një blloku.

1 package main
2 
3 import "fmt"
4 
5 func main() {
6 	x := 5
7 	x := 9
8 	fmt.Println(x)
9 }

https://play.golang.org/p/E2jTAsCTBX-

Gjatë kompajlimit të këtij programi, lajmërohet gabimi:

no new variables on left side of :=

me çka na bën me dije se tashmë ekziston një variabël x i deklaruar dhe se nuk mund ta deklarojmë për së dyti.

Rideklarimi nuk mund të bëhet as me var.

1 package main
2 
3 import "fmt"
4 
5 func main() {
6 	var x int = 5
7 	var x int = 9
8 	fmt.Println(x)
9 }

https://play.golang.org/p/hZVXUR7Cw4d

Gjatë kompajlimit, lajmërohet gabimi:

1 ./prog.go:7:6: x redeclared in this block
2 previous declaration at ./prog.go:6:6

që na bën me dije se variabli x nuk mund të rideklarohet në bllokun e njëjtë.

Vizibiliteti

Nëse e zhvendosim deklarimin e parë jashtë bllokut të funksionit, atëherë nuk do të lajmërohet kurrfarë gabimi, sepse në këtë rast kemi 2 variabla x me vizibilitet (visibility, scope) të ndryshëm, i pari në zonën globale prej nga e “shohin” të gjitha funksionet, ndërsa i dyti vetëm brenda funsionit main().

 1 package main
 2 
 3 import "fmt"
 4 
 5 var x int = 5
 6 
 7 func main() {
 8 	var x int = 9
 9 	fmt.Println(x)
10 }

https://play.golang.org/p/b9lbFfLQZxg

Rezultati:

1 9

Nëse e fshijmë deklarimin e dytë (var x int = 9), atëherë funksioni main() do ta “shohë” vlerën e x që është deklaruar në rreshtin 5.

1 package main
2 
3 import "fmt"
4 
5 var x int = 5
6 
7 func main() {
8 	fmt.Println(x)
9 }

https://play.golang.org/p/TXzGGOHQIfr

Rezultati:

1 5

Variablat e deklaruara / rideklaruara brenda një blloku, nuk mund të “shihen” në blloqet që janë të nivelit më të lartë.

 1 package main
 2 
 3 import "fmt"
 4 
 5 var x int = 1
 6 
 7 func main() {
 8 	fmt.Println(x)
 9 	var x int = 2
10 	{
11 		var x int = 3
12 		fmt.Println(x)
13 	}
14 	fmt.Println(x)
15 }

https://play.golang.org/p/xzKC7ACUONj

Rezultati:

1 1
2 3
3 2

Në rreshtin 5 është bërë deklarimi i variablit x në nivel “global”, respektivisht brenda package main.
Nga rreshti 7 deri në rreshtin 15 është blloku i funksionit main(), brenda të cilit është rideklaruar variabli x.
Nga rreshti 10 deri në rreshtin 13 është një bllok në vete, brenda të cilit është rideklaruar variabli x. Ky bllok në rastin konkret nuk bën asgjë të veçantë, përveç që e ndërron vizibilitetin e variablave brenda saj.

Kur kemi blloqe të strukturave, siç është për shembull struktura for, variablat e deklaruara brenda atyre blloqeve, nuk do të shihen jashtë bllokut. Gjithçka që deklarohet brenda bllokut, ngelet brenda bllokut dhe vlerat e variablave të tillë nuk mund të lexohen pas mbylljes së bllokut.

 1 package main
 2 
 3 import "fmt"
 4 
 5 func main() {
 6 	for i:= 1; i < 3; i++{
 7 		var x int = 3
 8 		fmt.Println(x)
 9 	}
10 	
11 	fmt.Println(x)
12 }

https://play.golang.org/p/_rjZMM8lJmx

Rezultati:

1 ./prog.go:11:14: undefined: x

Për rreshtin 11, variabli x asnjëherë nuk është deklaruar, sepse deklarimi ka ndodhur brenda bllokut të strukturës for.

E njëjta ndodh edhe me vetë inkrementin e strukturës for; variabli i nuk do të jetë i aksesueshëm jashtë bllokut.

 1 package main
 2 
 3 import "fmt"
 4 
 5 func main() {
 6 	for i := 1; i < 3; i++ {
 7 		fmt.Println(i)
 8 	}
 9 
10 	fmt.Println(i)
11 }

https://play.golang.org/p/f8ngsHoWbpl

Rezultati:

1 ./prog.go:10:14: undefined: i

Ky gabim nuk do të lajmërohet nëse variablin i do ta kishim tashmë të deklaruar para bllokut të strukturës for.

 1 package main
 2 
 3 import "fmt"
 4 
 5 func main() {
 6 	var i int = 8
 7 	for i := 1; i < 3; i++ {
 8 		fmt.Println(i)
 9 	}
10 
11 	fmt.Println(i)
12 }

https://play.golang.org/p/QSLdTzlW2h8

Rezultati:

1 1
2 2
3 8

Pra, Println në rreshtin 11 na e tregon vlerën e variablit i të deklaruar në rreshtin 6, ndërsa Println në rreshtin 8 tregon vlerat e variablit i që është iterator i ciklit të hapur në rreshtin 7.

Thënë ndryshe, variabli i në rreshtin 8 nuk është i njëjti variabël me atë në rreshtin 11, edhe pse kanë emër të njëjtë!

Nëse e ndryshojmë rreshtin 7 dhe në vend të:

for i := 1; i < 3; i++ {

shkruajmë:

for i = 1; i < 3; i++ {

atëherë rezultati do të ndryshojë, sepse variabli i i ciklit do të jetë tashmë i inicializuar dhe i padeklaruar për së dyti. Prandaj, Println(i) i rreshtit 11 do ta tregojë vlerën 3 (vlera e fundit e iteratorit), dhe jo 8.

 1 package main
 2 
 3 import "fmt"
 4 
 5 func main() {
 6 	var i int = 8
 7 	for i = 1; i < 3; i++ {
 8 		fmt.Println(i)
 9 	}
10 
11 	fmt.Println(i)
12 }

https://play.golang.org/p/rgo1hvJDkBb

Rezultati:

1 1
2 2
3 3

Deklarimi i përsëritur brenda ciklit nuk konsiderohet rideklarim.

 1 package main
 2 
 3 import (
 4 	"fmt"
 5 )
 6 
 7 func main() {
 8 	for i := 1; i <= 5; i++ {
 9 		x := 5
10 		y := x + i
11 		fmt.Println(y)
12 	}
13 }

https://play.golang.org/p/YfjQ_0hrnWX

Rezultati:

1 6
2 7
3 8
4 9
5 10

Konvencionet mbi emërtimin e variablave

Konvertimet ndërmjet tipeve

Konstantat

Konstantat në Go emërtohen njëjtë sikurse edhe variablat, pra nuk bëhet si p.sh. në PHP ku për emërtimet e konstantave përdoret vetëm shkronjat kapitale dhe nënvizimi (sikurse që është FILTER_SANITIZE_STRING).

Duhet marrë parasysh se të gjitha variablat dhe konstantat që fillojnë me shkronjë kapitale, në Go e kanë kuptimin se ato variabla/konstanta do të eksportohen, ndërsa nëse janë me të vogla - do të jenë variabla/konstanta lokale në raport me pakon.

Kontantat deklarohen me fjalën const.

 1 package main
 2 
 3 import (
 4 	"fmt"
 5 )
 6 
 7 func main() {
 8 	// numriPi nuk eksportohet
 9 	const numriPi float64 = 3.141592653589793
10 	fmt.Println(numriPi)
11 	
12 	// NumriPi eksportohet
13 	const NumriPi float64 = 3.141592653589793
14 	fmt.Println(NumriPi)
15 }

https://play.golang.org/p/VK3ar1gF60i

Rezultati:

1 3.141592653589793
2 3.141592653589793

Programi vijues nuk do të kompajlohet, për shkak se në rreshtin 11 përpiqemi t’ia ndryshojmë vlerën konstantës, me ç’rast lajmërohet gabimi cannot assign to numriPi.

 1 package main
 2 
 3 import (
 4 	"fmt"
 5 )
 6 
 7 func main() {
 8 	const numriPi float64 = 3.141592653589793
 9 	fmt.Println(numriPi)
10 
11 	numriPi = 3.14
12 	fmt.Println(numriPi)
13 }

https://play.golang.org/p/oUNUv_8-Sbr

Rezultati:

1 ./prog.go:11:10: cannot assign to numriPi

Karakteristikë e konstantave është se caktimi i vlerave të tyre duhet të jetë i verifikueshëm qysh në fazën e kompajlimit. Kjo do të thotë se konstantës nuk mundemi t’i japim vlerë që kthehet nga një funksion gjatë ekzekutimit të programit (runtime).

 1 package main
 2 
 3 import (
 4 	"fmt"
 5 	"math"
 6 )
 7 
 8 func main() {
 9 	const n = math.Sqrt(9)
10 	fmt.Println(n)
11 }

https://play.golang.org/p/tvBSOxAdml4

Rezultati:

1 ./prog.go:9:8: const initializer math.Sqrt(9) is not a constant

Edhe pse e dijmë që vlera do të ishte 3, konstanta n nuk e merr atë vlerë dhe lajmërohet gabimi const initializer math.Sqrt(9) is not a constant, sepse ajo vlerë është rezultat kthyes i një funksioni gjatë ekzekutimit të programit dhe kompajleri nuk mund ta “dijë” paraprakisht atë vlerë.

Po ashtu, nuk lejohen as shprehjet, rezultati i të cilave mund të dihet vetëm gjatë kohës së ekzekutimit, siç është rasti me krahasimin e vlerave të dy variablave si në shembullin vijues:

 1 package main
 2 
 3 import (
 4 	"fmt"
 5 )
 6 
 7 func main() {
 8 	var a, b = 5, 7
 9 	const n = a < b
10 	fmt.Println(n)
11 }

https://play.golang.org/p/WdCrkF70PIB

Rezultati:

1 ./prog.go:9:8: const initializer a < b is not a constant

Konstantave mund t’iu caktohen vetëm vlera primitive:

  • int
  • float
  • boolean
  • string

Nuk mund të përdoren tipet më komplekse siç janë:

  • vargjet
  • segmentet
  • struktet
  • mapat

sepse këto për nga natyra janë variabile.

E përbashkëta e variablave dhe konstantave është se me të dyja mund të zbatohet “shadowing”, që do të thotë ridefinimi i një vlere të re të variablit/konstantës brenda një blloku që është brenda një blloku tjetër ku tashmë është deklaruar po ai variabël apo konstantë. Blloku që është me hiearki më të ultë mund të bëjë “shadow” (t’i zëvendësojë vlerat) një variable/konstante tashmë të definuar në bllokun me hierarki më të lartë.

 1 package main
 2 
 3 import (
 4 	"fmt"
 5 )
 6 
 7 const n = 7
 8 
 9 func main() {
10 	const n = 5
11 	fmt.Println(n)
12 }

https://play.golang.org/p/bv_obpquhTf

Rezultati:

1 5

E shohim që deklarimi i konstantës në rreshtin 10, “e mbulon” vlerën e konstantës së njëjtë të deklaruar në rreshtin 7. Kjo ndodh për shkak se blloku ku gjendet const n=7 me hierarki është më “sipër” prej bllokut ku gjendet const n = 5. Me fjalë të tjera, blloku i brendshëm ka prioritet më të lartë.

Megjithëse i mundur, ndryshimi i vlerës së konstantës në bllokun e brendshëm mund të jetë burim potencial i ndonjë “bug” , nëse rastësisht në ato dy blloqe kemi deklaruar dy konstanta me emër të njëjtë por me dedikime të ndryshme.

Për më tepër, “shadowing”, përveç vlerës lejon edhe ndryshimin e tipit të konstantës! Kjo potencialisht mund të shkaktojë probleme gjatë kompajlimit ose ekzekutimit të programit.

 1 package main
 2 
 3 import (
 4 	"fmt"
 5 )
 6 
 7 const n int = 7
 8 
 9 func main() {
10 	const n string = "test"
11 	fmt.Println(n)
12 }

https://play.golang.org/p/W_HO2UUq-TF

Rezultati:

1 test

Vërejmë se konstanta n, në rreshtin 7 është integer, ndërsa në rreshtin 10 është string.

Lirisht mund të kryejmë operacione ndërmjet variablave dhe konstantave nëse janë të tipit të njëjtë.

 1 package main
 2 
 3 import (
 4 	"fmt"
 5 	"math"
 6 )
 7 
 8 func main() {
 9 	const a float64 = 7
10 	var b float64 = 9
11 
12 	// Teorema e Pitagorës
13 	fmt.Println(math.Sqrt(math.Pow(a, 2) + math.Pow(b, 2)))
14 }

https://play.golang.org/p/2e8cbW5l-UZ

Rezultati:

1 11.40175425099138

Në Go, nuk lejohen operacione ndërmjet dy tipeve të ndryshme, as ndërmjet dy nëntipeve, si për shembull operacioni i mbledhjes ndërmjet int8 dhe int16.

 1 package main
 2 
 3 import (
 4 	"fmt"
 5 )
 6 
 7 func main() {
 8 	var a int8 = 7
 9 	var b int16 = 12
10 	fmt.Println(a + b)
11 }

https://play.golang.org/p/hx5eKdK0orw

Rezultati:

1 ./prog.go:10:16: invalid operation: a + b (mismatched types int8 and int16)

Gabim i ngjashëm paraqitet edhe kur lejojmë që Go ta konkludojë vetë tipin e një variabli, si në rreshtin 8 të shembullit vijues:

 1 package main
 2 
 3 import (
 4 	"fmt"
 5 )
 6 
 7 func main() {
 8 	var a = 7
 9 	var b int16 = 12
10 	fmt.Println(a + b)
11 }

Rezultati:

1 ./prog.go:10:16: invalid operation: a + b (mismatched types int and int16)

Dy shembujt e fundit i dhamë me variabla, për të potencuar dallimin me rastin kur përdorim konkludimin e tipit (type inference) të konstantë, me ç’rast Go nuk do të “ankohet” për faktin që janë dy nëntipe të ndryshme dhe vetë do ta bëjë konvertimin e nëntipeve asisoj që të përputhen.

 1 package main
 2 
 3 import (
 4 	"fmt"
 5 )
 6 
 7 func main() {
 8 	const a = 7
 9 	var b int8 = 12
10 	fmt.Printf("%T, %T\n", a, b)
11 
12 	c := a + b
13 	fmt.Printf("%T, %v\n", c, c)
14 }

https://play.golang.org/p/7cr_-7Exu7d

Rezultati:

1 int, int8
2 int8, 19

Pra, Go e sheh konstantën a si int, ndërsa variablin b si int8. Mirëpo, pas mbledhjes së vlerave të tyre, rezultati (19) është i tipit int8.

Pra, ndërmjet int dhe int8, për rezultat ka zgjedhur tipin int8, i cili ka diapazon më të ngushtë të vlerave. Kjo është në rregull për aq kohë sa vlera e rezultatit gjatë ekzekutimit të programit nuk e tejkalon vlerën 127, sa është vlera maksimale e tipit int8.

Nëse vlera e rezultatit e tejkalon vlerën maksimale të nëntipit të caktuar, ndodh overflow pa kurrfarë lajmërimi mbi tejkalimin e vlerës maksimale, që shpie drejt rezultateve të gabuara.

 1 package main
 2 
 3 import (
 4 	"fmt"
 5 )
 6 
 7 func main() {
 8 	const a = 12
 9 	var b int8 = 120
10 	fmt.Printf("%T, %T\n", a, b)
11 
12 	c := a + b
13 	fmt.Printf("%T, %v\n", c, c)
14 }

https://play.golang.org/p/tAkX2-YhJln

Rezultati:

1 int, int8
2 int8, -124

Shohim se 120 + 12, në vend të 132 është -124. Kjo për shkak se është bërë një “rotacion” nëpër vlerat e int8 (-128 deri 127).

+0 +1 +2 +3 +4 +5 +6 +7 +8 +9 +10 +11 +12
120 121 122 123 124 125 126 127 -128 -127 -126 -125 -124

Enumerated constants

 1 package main
 2 
 3 import (
 4 	"fmt"
 5 )
 6 
 7 func main() {
 8 	const (
 9 		x = iota
10 		y = iota
11 		z = iota
12 	)
13 
14 	fmt.Printf("%T, %T, %T\n", x, y, z)
15 	fmt.Printf("%v, %v, %v\n", x, y, z)
16 }

https://play.golang.org/p/Kl5LHx9Uo0o

Rezultati:

1 int, int, int
2 0, 1, 2

Rezultat të njëjtë na jep edhe ky kod:

 1 package main
 2 
 3 import (
 4 	"fmt"
 5 )
 6 
 7 func main() {
 8 	const (
 9 		x = iota
10 		y
11 		z
12 	)
13 
14 	fmt.Printf("%T, %T, %T\n", x, y, z)
15 	fmt.Printf("%v, %v, %v\n", x, y, z)
16 }

https://play.golang.org/p/aKN4PxzypFD

Kjo për shkak se kompajleri nënkupton se edhe konstantat në vijim marrin vlerën iota.

Vlera iota është vlerë speciale për numërim duke filluar nga 0 dhe është e tipit int. Çdo konstantë vijuese brenda bllokut, merr vlerë për 1 më të madhe se konstanta paraprake.

Vlen vetëm për blloqe konstantash të deklaruara me:

1 const (
2 
3 )

Por nuk vlen nëse konstantat janë deklaruar si vijon:

1 	const x = iota
2 	const y = iota
3 	const z = iota

Me këtë mënyrë te deklarimit, të tre konstantat (x, y dhe z) do të kenë vlerë 0.

Çdo bllok konstantash me vlerë iota, fillon prej zeros:

 1 package main
 2 
 3 import (
 4 	"fmt"
 5 )
 6 
 7 func main() {
 8 	const (
 9 		x = iota
10 		y
11 		z
12 	)
13 
14 	const (
15 		a = iota
16 		b
17 	)
18 
19 	fmt.Printf("%v, %v, %v\n", x, y, z)
20 	fmt.Printf("%v, %v\n", a, b)
21 }

https://play.golang.org/p/ker4s4okHwW

Rezultati:

1 0, 1, 2
2 0, 1

Listë të tillë konstantash të paradefinuara me vlerë inkrementale mund të përdorim kur dëshirojmë në program të bëjmë krahasime.

 1 package main
 2 
 3 import (
 4 	"fmt"
 5 )
 6 
 7 func main() {
 8 	const (
 9 		a = iota
10 		b
11 		c
12 	)
13 
14 	x := 2
15 
16 	fmt.Printf("%v\n", x == a)
17 	fmt.Printf("%v\n", x == b)
18 	fmt.Printf("%v\n", x == c)
19 }

https://play.golang.org/p/oYSJVpMUw1l

Rezultati:

1 false
2 false
3 true

Nëse fare s’na interson vlera 0, ndërsa këto konstanta gjithmonë fillojnë nga 0, atëherë vlerën e parë e vendosim në _, që është* write only variable*, pra variabël në të cilin mund të vendosim vlerë, por asaj vlere nuk mund t’i referohemi më pas, pra praktikisht ajo vlerë do të humbet.

 1 package main
 2 
 3 import (
 4 	"fmt"
 5 )
 6 
 7 func main() {
 8 	const (
 9 		_ = iota
10 		a
11 		b
12 	)
13 
14 	x := 1
15 
16 	fmt.Printf("%v\n", x == a)
17 	fmt.Printf("%v\n", x == b)
18 }

https://play.golang.org/p/dsBbDsodtap

Rezultati:

1 true
2 false

Nëse numërimin dëshirojmë ta fillojmë nga një numër tjetër, atëherë konstantës së parë ia rrisim apo zvogëlojmë vlerën, në varësi prej vlerës së dëshiruar.

 1 package main
 2 
 3 import (
 4 	"fmt"
 5 )
 6 
 7 func main() {
 8 	const (
 9 		_ = iota - 4
10 		a
11 		b
12 	)
13 
14 	const (
15 		_ = iota + 4
16 		x
17 		y
18 	)
19 
20 	fmt.Printf("a=%v, b=%v\n", a, b)
21 	fmt.Printf("x=%v, y=%v\n", x, y)
22 }

https://play.golang.org/p/PZ51jnTUzp7

Rezultati:

1 a=-3, b=-2
2 x=5, y=6

Vërejmë se kontanta a fillon nga -3, ndërsa konstanta x nga 5.

Përveç mbledhjes dhe zbritjes, mund të përdorim të gjithë operatorët që janë në dispozicion për vlerat primitive.

Tipet e të dhënave

Janë 4 kategori të veçanta të tipeve të të dhënave në Go:

  • Tipet bazike (Basic Types)
  • Tipet agregate (Aggregate Types) - vargjejt (arrays) dhe strukturat (structs)
  • Tipet referenciale (Reference Types) - Treguesit (pointers) dhe slices
  • Tipet interface (Interface Types) - Interfejsat standard
  • Tipet bazike të integruara në Go

Go përdor tipet bazike të integruara si vijon:

  • Një tip logjik (boolean): bool.
  • 11 tipe numerike: int8, uint8, int16, uint16, int32, uint32, int64, uint64, int, uint, dhe uintptr.
  • Dy tipe numerike me presje dhjetore: float32 dhe float64.
  • Dy tipe numerike komplekse: complex64 dhe complex128.
  • Një tip tekstual: string.

Këto tipe mund të përdoren drejtpërsëdrejti në kod, pa pasur nevojë të importohet ndonjë pako.
Tipet numerike përfshijnë numrat e plotë, numrat me presje dhjetore dhe numrat kompleks.

Numrat

Numrat ndahen në dy lloje kryesore:

  • Numra të plotë (integers), dhe
  • Numra me presje dhjetore (floating-point numbers).

Numrat e plotë

Numrat e plotë janë numra pa komponentën decimale. Të gjithë numrat e plotë kanë madhësi të caktuar kur ruhen apo procesohen në kompjuter.

Për shembull, një bajt (byte) përbëhet nga 8 bita, ku secili bit mund ta ketë vlerën 0 ose 1.
Kështu, në një bajt (8 bit) mund të ruhen 256 (28) vlera të ndryshme pozitive, prej 0 deri 255, respektivisht prej 00000000 deri 11111111 nëse e shprehim numrin në formë binare.

Nëse duhet ta ruajmë një numër pozitiv më të madh se 255, na nevojitet edhe një bajt tjetër, pra gjithsej 2 bajtë (16 bit). Në këtë rast, vlerat do të shkojnë nga 0000000000000000 deri në 1111111111111111, që në numra me bazë 10 do të jetë nga 0 deri në 65.535, pra gjithsej 65.536 vlera (216).

Me katër bajtë (32 bit), diapazoni i vlerave pozitive është 0 deri në 4.294.967.295, gjithsej 4.294.967.296 vlera (232), e kështu me radhë, për çdo bajt të shtuar diapazoni i vlerave zgjerohet 256 herë.

Kur duhet të ruhen numrat e plotë negativë, biti i parë i bajtit të parë (most significant bit) shfrytëzohet si indikator: vlera 0 tregon se është vlerë pozitive, ndërsa vlera 1 se është vlerë negative, prandaj diapazoni i vlerave përgjysmohet, ashtu siç është paraqitur në tabelë.

Kur ruajmë vetëm vlera pozitive, kemi të bëjmë me unsigned integers, ndërsa për pozitive dhe negative – signed integers.
Tipet e numrave të plotë, emrat e të cilëve fillojnë me u janë tipe pa parashenjë (unsigned types), të cilat përmbajnë vetëm vlera jonegative.

Numri pas emrit tregon numrin e bitave binarë (8, 16, 32, 64) që nevojiten për ruajtjen e vlerës në memorje. Për shembull uint8 zbërthehet si: numër i plotë jonegativ që okupon 8 bit në memorje, me çka mund të reprezentohen vlerat prej 0 deri 255 (28-1). Ndërsa tipi int8: numër i plotë që okupon 8 bit n memorje dhe merr vlera prej -128 (-27) deri në 127 (27-1).

Vlera jonegative (unsigned)

  • Bit: 8, Bajt:1, Prej: 0, Deri: 255 (28-1)
  • Bit: 16, Bajt:2, Prej: 0, Deri: 65.535 (216-1)
  • Bit: 32, Bajt:4, Prej: 0, Deri: 4.294.967.295 (232-1)
  • Bit: 64, Bajt:8, Prej: 0, Deri: 18.446.744.073.709.551.615 (264-1)

Vlera negative dhe jonegative (signed)

  • Bit: 8, Bajt:1, Prej: -128 (-27), Deri: 127 (27-1)
  • Bit: 16, Bajt:2, Prej: -32.768 (-215), Deri: 32,767 (215-1)
  • Bit: 32, Bajt:4, Prej: -2.147.483.648 (-231), Deri: 2.147.483.647 (231-1)
  • Bit: 64, Bajt:8, Prej: -9.223.372.036.854.775.808 (-263), Deri: 9,223,372,036,854,775,807 (263-1)

Go përdor tipet vijuese të numrave të plotë:

8 bitë unsigned

  • uint8 (byte)

16 bitë unsigned

  • uint16

32 bitë unsigned

  • uint32
  • uint
  • uintptr

64 bitë unsigned

burim vështirë i detektueshëm
8 bitë signed

  • int8

16 bitë signed

  • int16

32 bitë signed

  • int32 (rune)
  • int

64 bitë signed

onjë gabim eventu
Tipi byte është alias i uint8, d.m.th. janë të njëjtë.

Tipi rune është alias i int32.

Madhësitë e tipeve uint, int dhe uintptr varen nga platforma, respektivisht arkitektura e procesorit; në platformat 32 bitëshe kanë madhësi 32 bitëshe, ndërsa në platformat 64 bitëshe - madhësi 64 bitëshe.
Madhësia e vlerës uintptr duhet të jetë mjaft e madhe që të ruajë bitat e painterpretuar të një adrese memorike.

Duhet të kemi parasysh që vlerat që ia japim një tipi t’i përgjigjen diapazonit të vlerave të lejuara për atë tip. Për shembull, tipi int8 mund të përmbajë vlera prej -128 deri 127. Nëse në program i japim një vlerë më të madhe se 127 apo më të vogël se -128, kompajleri do të lajmërojë gabim dhe programi nuk do të kompajlohet fare.

1 var muaji int8
2 muaji = 500

Mirëpo, nëse vlera e një tipi caktohet gjatë ekzekutimit të programit, me ç’rast kompajleri nuk e di paraprakisht vlerën, do të ndodhin situata konfuze, me ç’rast programi nuk lajmëron gabim por ndodh overflow. Për shembull, meqë vlera maksimale e int8 është 127 dhe nëse gjatë kohës së ekzekutimit merr vlerën 128, vlera rezultuese do të jetë -127! Kjo për shkak se pas arritjes së vlerës maksimale, programi fillon prej vlerës më të vogël për aq sa sa vlera e re dallon nga vlera maksimale.

Me fjalë të tjera, nëse int8 merr vlerën 150, në fakt ai do ta ketë vlerën -105 sepse 150-127=23 dhe -128+23 = -105. Ky mund të jetë burim vështirë i detektueshëm i gabimeve në rezultatet e programit sepse sistemi i kohës së ekzekutimit (runtime system) i Go nuk do të na lajmërojë për ndonjë gabim eventual.

Duhet të kemi parasysh edhe faktin që ndaj numrave të plotë të tipeve të ndryshme nuk mund të kryejmë operacione aritmetikore. Për shembull, nuk mund ta mbledhim një numër të tipit uint8 me një numër të tipit int16. Për ta realizuar operacionin e mbledhjes, të dy tipet duhet më parë t’i konvertojmë në një tip të njëjtë e më pas ta kryejmë veprimin aritmetikor. Në shembullin vijues, konvertimi bëhet në tipin int nëpërmes funksionit int(), i cili ka madhësinë 32 apo 64 bitë, në varësi prej arkitekturës, por gjithsesi është më i madh se 8 bitë (tipi uint8) dhe 16 bitë (int16).

 1 var m uint8
 2 m = 5
 3 var f int16
 4 f = 6
 5 
 6 var n int
 7 // kjo shkakton gabim kompajlimi
 8 n = m + f
 9 // prandaj variablat m dhe f duhet te konvertohen
10 // me funksionin int()
11 n = int(m) + int(f)

Numrat me presje dhjetore

Numrat me presje dhjetore (floating-point numbers) janë numra që përmbajnë komponentën decimale. Paraqitja e tyre në kompjuter është më e ndërlikuar se e numrave të plotë. Kjo bën që saktësia e këtyre numrave të jetë e kufizuar dhe rrjedhimisht edhe e llogaritjeve që bëhen me këto numra.

Në memorje, të gjitha vlerat e numrave me presje dhjetore ruhen në formatin IEEE-754.

Numrat me presje dhjetore kanë madhësi (numra bitash) të caktuar, 32 bitë (single precision) apo 64 bitë (double precision), sikurse edhe numrat e plotë.

Numrat më të mëdhenj kanë saktësi më të vogël, sepse duke qenë se kanë madhësi fikse (32/64 bitë), sa më e madhe pjesa e plotë, aq më pak shifra ngelen në dispozicion djathtas prej presjes dhjetore, me çka zvogëlohet saktësia e pjesës decimale.

Mund të përmbajnë edhe vlera jonumerike siç janë:

  • NaN (not a number)
  • Infinit pozitiv (+∞)
  • Infininit negativ (−∞)

Shembull me infinit:

 1 package main
 2 
 3 import (
 4 	"fmt"
 5 )
 6 
 7 func main() {
 8 	var c float32 = 0
 9 	var d float32 = 0
10 
11 
12 	var a float32 = 24
13 	d = a / c
14 	fmt.Printf("result = %.2f \n", d)
15 
16 	var b float32 = -15
17 	d = b / c
18 	fmt.Printf("result = %.2f \n", d)
19 }

https://play.golang.org/p/ZrzSGo-MnzA

Rezultati:

1 result = +Inf 
2 result = -Inf 

Go ka dy tipe për paraqitjen e numrave kompleksë:

  • complex64, dhe
  • complex128.

Pjesa reale dhe imagjinare e një vlere complex64 janë të dyja vlera float32, ndërsa pjesët reale dhe imagjinare të një vlere complex128 janë të dyja vlera float64.

Në Go përdoren operatorët standard vijues:

  • Mbledhja +
  • Zbritja -
  • Shumëzimi *
  • Pjestimi /
  • Modulusi %

Numrat kompleks

Numrat kompleks përbëhen nga dy pjesë:

  • pjesa reale, dhe
  • pjesa imagjinare.

Go mundëson kryerjen e drejtpërsëdrejtë të operacioneve aritmetikore ndaj numrave kompleks. Numri kompleks është 128 bitësh, float64 për pjesën reale, float64 për pjesën imagjinare, apo 64 bitësh - float32 për pjesën reale, float32 për pjesën imagjinare.

 1 package main
 2 
 3 import "fmt"
 4 
 5 func main() {
 6 	var a = 4 + 3i
 7 	var b = 6 + 2i
 8 
 9 	fmt.Println(a + b)
10 	fmt.Println(a - b)
11 	fmt.Println(a * b)
12 	fmt.Println(a / b)
13 }

https://play.golang.org/p/-qHz2eUw9B8

Rezultati:

1 (10+5i)
2 (-2+1i)
3 (18+26i)
4 (0.75+0.25i)

Me përdorimin e pakos “math/cmplx”, mund të kryejmë edhe veprime të tjera matematikore.

  • Abs(x complex128) float64
  • Acos(x complex128) complex128
  • Acosh(x complex128) complex128
  • Asin(x complex128) complex128
  • Asinh(x complex128) complex128
  • Atan(x complex128) complex128
  • Atanh(x complex128) complex128
  • Conj(x complex128) complex128
  • Cos(x complex128) complex128
  • Cosh(x complex128) complex128
  • Cot(x complex128) complex128
  • Exp(x complex128) complex128
  • Inf() complex128
  • IsInf(x complex128) bool
  • IsNaN(x complex128) bool
  • Log(x complex128) complex128
  • Log10(x complex128) complex128
  • NaN() complex128
  • Phase(x complex128) float64
  • Polar(x complex128) (r, θ float64)
  • Pow(x, y complex128) complex128
  • Rect(r, θ float64) complex128
  • Sin(x complex128) complex128
  • Sinh(x complex128) complex128
  • Sqrt(x complex128) complex128
  • Tan(x complex128) complex128
  • Tanh(x complex128) complex128

Rrënja katrore e numri kompleks:

 1 package main
 2 
 3 import (
 4 	"fmt"
 5 	"math/cmplx"
 6 )
 7 
 8 func main() {
 9 	var a = 4 + 3i
10 	fmt.Println(cmplx.Sqrt(a))
11 }

https://play.golang.org/p/8aXVwGwWjW1

Rezultati:

1 (2.1213203435596424+0.7071067811865476i)

Po të përdorej funksioni Sqrt() nga pakoja math:

1 	var a = 4 + 3i
2 	fmt.Println(math.Sqrt(a))

do të lajmërohej gabimi:

1 cannot use a (type complex128) as type float64 in argument to math.Sqrt

Ngritja në fuqi e numrit kompleks

E marrim rezultatin e shembullit me rrënjë katrore të numrave kompleks, dhe e ngrisim në katror, për të verifikuar a e fitojmë rezultatin e pritur (4 + 3i).

 1 package main
 2 
 3 import (
 4 	"fmt"
 5 	"math/cmplx"
 6 )
 7 
 8 func main() {
 9 	var a = 2.1213203435596424 + 0.7071067811865476i
10 	fmt.Println(cmplx.Pow(a,2 ))
11 }

https://play.golang.org/p/pmQpPC2q8k5

Rezultati:

1 (4+3.000000000000001i)

Shohim që e kemi fituar rezultatin përafërsisht të njëjtë, diferenca është 0.000000000000001 apo 10-14, diferencë kjo që mund të jetë e papërfillshme në varësi prej natyrës së problemit që zgjidhet.

Pasaktësia buron nga fakti e komponentet e numrit kompleks janë float64 dhe domosdo do të ndodhin përafrime të vlerave sepse këtë e imponon vetë natyra e numrave të tipit float.

Bitwise operators

Bitwise AND operator

Kryhet operacioni AND ndaj vlerave binare të dy numrave bit për bit në pozita të njëjta. Rezultati i fituar është:

  • 0 & 0 = 0
  • 0 & 1 = 0
  • 1 & 0 = 0
  • 1 & 1 = 1
 1 package main
 2 
 3 import (
 4 	"fmt"
 5 )
 6 
 7 func main() {
 8 	var x = 202
 9 	var y = 107
10 	fmt.Printf("%4v| %08b\n", x, x)
11 	fmt.Printf("%4v| %08b\n", y, y)
12 	fmt.Println("______________")
13 	fmt.Printf("%4v| %08b\n", x&y, x&y)
14 }

https://play.golang.org/p/fE6bLqAKff5

Rezultati:

1  202| 11001010
2  107| 01101011
3 ______________
4   74| 01001010

Bitwise OR operator

Kryhet operacioni OR ndaj vlerave binare të dy numrave bit për bit në pozita të njëjta. Rezultati i fituar është:

  • 0 | 0 = 0
  • 0 | 1 = 1
  • 1 | 0 = 1
  • 1 | 1 = 1
 1 package main
 2 
 3 import (
 4 	"fmt"
 5 )
 6 
 7 func main() {
 8 	var x = 202
 9 	var y = 107
10 	fmt.Printf("%4v| %08b\n", x, x)
11 	fmt.Printf("%4v| %08b\n", y, y)
12 	fmt.Println("______________")
13 	fmt.Printf("%4v| %08b\n", x|y, x|y)
14 }

https://play.golang.org/p/zjE0213pbil

Rezultati:

1  202| 11001010
2  107| 01101011
3 ______________
4  235| 11101011

### Bitwise XOR operator

Kryhet operacioni XOR ndaj vlerave binare të dy numrave bit për bit në pozita të njëjta. Rezultati i fituar është:

  • 0 | 0 = 0
  • 0 | 1 = 1
  • 1 | 0 = 1
  • 1 | 1 = 0
 1 package main
 2 
 3 import (
 4 	"fmt"
 5 )
 6 
 7 func main() {
 8 	var x = 202
 9 	var y = 107
10 	fmt.Printf("%4v| %08b\n", x, x)
11 	fmt.Printf("%4v| %08b\n", y, y)
12 	fmt.Println("______________")
13 	fmt.Printf("%4v| %08b\n", x^y, x^y)
14 }

https://play.golang.org/p/nmpYzMQdCWi

Rezultati:

1  202| 11001010
2  107| 01101011
3 ______________
4  161| 10100001

Bitwise NOT operator

Ky operator, zerot i kthen në njësha, njëshat në zero.

  • ^0 = 1
  • ^1 = 0
 1 package main
 2 
 3 import (
 4 	"fmt"
 5 )
 6 
 7 func main() {
 8 	var x uint8 = 123
 9 	fmt.Printf("%4v| %08b\n", x, x)
10 	fmt.Println("______________")
11 	fmt.Printf("%4v| %08b\n", ^x, ^x)
12 }

https://play.golang.org/p/hie6IHjoRDu

Rezultati:

1  123| 01111011
2 ______________
3  132| 10000100

Bitwise AND NOT operator

Biti i numrit të parë kryen operacionin AND me NOT-in e numrit të dytë.

  • 0 &^ 0 = 0
  • 0 &^ 1 = 0
  • 1 &^ 0 = 1
  • 1 &^ 1 = 0
 1 package main
 2 
 3 import (
 4 	"fmt"
 5 )
 6 
 7 func main() {
 8 	var x = 123
 9 	var y = 6
10 	fmt.Printf("%4v| %08b\n", x, x)
11 	fmt.Printf("%4v| %08b\n", y, y)
12 	fmt.Println("______________")
13 	fmt.Printf("%4v| %08b\n", x&^y, x&^y)
14 }

https://play.golang.org/p/c7UyUxnCKUz

Rezultati:

1  123| 01111011
2    6| 00000110
3 ______________
4  121| 01111001

Bitwise shift operators

Operatorët për zhvendosje të bitave (bitwise shift operators) bëjnë zhvendosjen e vlerës së një objekti binar. Operandi i majtë paraqet vlerën, ndërsa i djathti numrin e pozitave për zhvendosjen e bitave të vlerës.

 1 package main
 2 
 3 import (
 4 	"fmt"
 5 )
 6 
 7 func main() {
 8 	fmt.Printf("%3v << %v  | %3v (%08b)\n", 2, 5, 2<<5, 2<<5)
 9 	fmt.Printf("%3v >> %v  | %3v (%08b)\n", 64, 5, 64>>5, 64>>5)
10 }

https://play.golang.org/p/Rknmaievs2w

Rezultati:

1   2 << 5  |  64 (01000000)
2  64 >> 5  |   2 (00000010)

Në rreshtin 8 kryhet operacioni 2<< 5, që do të thotë që vlera e numrit 2 (që në formë binare është 00000010), të zhvendoset për 5 pozita majtas. Pra, njësi do të zhvendoset 5 herë majtas, me ç’rast formohet numri binar 01000000, e që është vlera 64.

Në rreshtin 9 kryhet veprim i kundërt: vlera e numrit 64 (01000000) do të zhvendoset djathtas, pra njëshi do të zhvendoset për pesë pozita më djathtas, me çka fitohet numri binar 00000010, që është numri 2.

Bitwise left shift operator

 1 package main
 2 
 3 import (
 4 	"fmt"
 5 )
 6 
 7 func main() {
 8 	var x uint = 1
 9 	fmt.Printf("%v = %08b\n", x, x)
10 	fmt.Println("________________________")
11 	var i uint
12 	for i = 0; i < 8; i++ {
13 		r := x << i
14 		fmt.Printf("%v << %v  | %3v (%08b)\n", x, i, r, r)
15 	}
16 }

https://play.golang.org/p/yIC7d1CT5NF

Rezultati:

 1 1 = 00000001
 2 ________________________
 3 1 << 0  |   1 (00000001)
 4 1 << 1  |   2 (00000010)
 5 1 << 2  |   4 (00000100)
 6 1 << 3  |   8 (00001000)
 7 1 << 4  |  16 (00010000)
 8 1 << 5  |  32 (00100000)
 9 1 << 6  |  64 (01000000)
10 1 << 7  | 128 (10000000)

E vërejmë njëshin binar si zhvendoset nga një pozitë majtas, prej vlerës binare 00000001 deri në vlerën binare 10000000.

Bitwise right shift operator

 1 package main
 2 
 3 import (
 4 	"fmt"
 5 )
 6 
 7 func main() {
 8 	var x uint = 128
 9 	fmt.Printf("%v = %08b\n", x, x)
10 	fmt.Println("________________________")
11 	var i uint
12 	for i = 0; i < 8; i++ {
13 		r := x >> i
14 		fmt.Printf("%v >> %v  | %3v (%08b)\n", x, i, r, r)
15 	}
16 }

https://play.golang.org/p/Yl3YULGqkdj

Rezultati:

 1 128 = 10000000
 2 ________________________
 3 128 >> 0  | 128 (10000000)
 4 128 >> 1  |  64 (01000000)
 5 128 >> 2  |  32 (00100000)
 6 128 >> 3  |  16 (00010000)
 7 128 >> 4  |   8 (00001000)
 8 128 >> 5  |   4 (00000100)
 9 128 >> 6  |   2 (00000010)
10 128 >> 7  |   1 (00000001)

Stringjet

Një string është sekuencë karakteresh e gjatësisë së caktuar që përdoret për ruajtjen e tekstit. Go ofron suport për karakteret Unicode, me çka mundësohet përdorimi i shkronjave të alfabeteve të ndryshme si dhe shenjave të shumta speciale.

Një string vendoset ndërmjet thonjëzave apo shenjave backtick (`):
“Hello, World”

`Hello,
World`

Nëse stringu vendoset brenda thonjëzave, stringu nuk mund të përmbajë rreshta të rinj (newlines) ndërsa special escape sequences janë të lejuara.

Shenja \n zëvendësohet me newline ndërsa \t me tabulator.

Nëse stringu vendoset brenda backticks, stringun mund ta ndajmë në disa rreshta.

Karaktereve individuale të stringut mund t’iu qasemi sikur të ishte varg, duke përdorur indeksin, për aq kohë sa karakteret i takojnë tabelës ASCII.

 1 package main
 2 
 3 import (
 4 	"fmt"
 5 )
 6 
 7 func main() {
 8 	a := "Golang"
 9 	fmt.Println(a[0])
10 	fmt.Println(string(a[0]))
11 }

https://play.golang.org/p/W8Fbgr2NLKK

Rezultati:

1 71
2 G

Si rezultat i fmt.Println(a[0]) fitohet kodi i karakterit të parë (indeksi 0) e që është 71. Me anë të funksionit string() mund ta konvertojmë në karakter (G).

Nëse një karakter e vendosim brenda apostrofave, vlerë e variablit do të jetë kodi i atij karakteri. Nëse një karakter e vendosim brenda thonjëzave, atëherë vlerë do të jetë karakteri.

 1 package main
 2 
 3 import (
 4 	"fmt"
 5 )
 6 
 7 func main() {
 8 	a := 'A'
 9 	b := "B"
10 	fmt.Printf("%T %T \n", a, b)
11 	fmt.Printf("%v %v \n", a, b)
12 	fmt.Printf("%c %v", a, b)
13 }

https://play.golang.org/p/WL392-lPQ7w

Rezultati:

1 int32 string 
2 65 B 
3 A B

Pra, kur kemi deklaruar a := 'A', tipi i variablit a do të jetë int32, gjegjësisht rune, që do të jetë një numër, pra 65. Për ta printuar me fmt.Printf, përdoret formatimi “%c”, ose e konvertojmë variablin me funksionin string():

1 fmt.Printf("%v %v", string(a), b)

Mund t’iu qasemi edhe një sekuence të karakterëve duke e cekur numrin e karakterëve të dëshiruar.

 1 package main
 2 
 3 import (
 4 	"fmt"
 5 )
 6 
 7 func main() {
 8 	a := "Golang"
 9 	fmt.Println(a[0:3])
10 }

https://play.golang.org/p/9I_gQlAwJw1

Rezultati:

1 Gol

Me a[0:3] kemi kërkuar 3 karakteret e njëpasnjëshëm duke filluar nga karakteri i parë [0], pastaj [1], pastaj [2], duke mos e përfshirë indeksin e fundit [3]. Në Go, kur ceket një diapazon i indekseve, nuk merret përfshihet indeksi i fundit, por përfshirja bëhet prej indeksit të cekur fillestar deri te ai i parafundit.

Vërejmë se në këtë rast fitojmë stringun “Gol” dhe jo kodet e karakterëve, pra nuk kemi nevojë të bëjmë konvertim eksplicit me funksionin string().

Karaktereve individualë mund t’iu qasemi edhe me strukturën for-range:

 1 package main
 2 
 3 import (
 4 	"fmt"
 5 )
 6 
 7 func main() {
 8 	a := "Golang"
 9 
10 	// Mënyra 1
11 	// i -> indeksi, a[i] -> kodi i karakterit, string(a[i]) -> karakteri
12 
13 	for i := range a {
14 		fmt.Println(i, a[i], string(a[i]))
15 	}
16 
17 	// Mënyra 2
18 	// i -> indeksi, c -> kodi i karakterit, string(c) -> karakteri
19 
20 	for i, c := range a {
21 		fmt.Println(i, c, string(c))
22 	}
23 }

https://play.golang.org/p/shIDT8i9w4W

Rezultati:

 1 0 71 G
 2 1 111 o
 3 2 108 l
 4 3 97 a
 5 4 110 n
 6 5 103 g
 7 0 71 G
 8 1 111 o
 9 2 108 l
10 3 97 a
11 4 110 n
12 5 103 g

Kur kemi të bëjmë me Unicod karaktere, ka dallim esencial ndërmjet përdorimit të strukturës for dhe asaj for-range për ekstaktimin e karaktereve. Struktura for e zbërthen stringun bajt për bajt, ndërsa struktura for-range do ta zbërthejë sipas Unicode karakterve.

Një Unicode karakter zë ndërmjet 1 dhe 4 bajtëve dhe përfshin pothuajse të gjitha alfabetet e botës. Karakteret nga tabela ASCII konsumojnë nga 1 bajt, kështu që për shembull për alfabetin e gjuhës angleze, si struktura for si ajo for-range do të japin rezultat të njëjtë. Mirëpo, në rastin e gjuhës kineze në shembullin vijues, 1 karakter zë 3 bajtë dhe struktura for është e papërshtatshme për t’u përdorur për një string në gjuhën kineze. Në shembullin e mëposhtëm, zbërthimi i stringut në gjuhën kineze bëhet njëherë me for, pastaj me for-range.

 1 package main
 2 
 3 import (
 4 	"fmt"
 5 )
 6 
 7 func main() {
 8 	text := "新年快乐"
 9 	
10 	// bajt per bajt
11 	for i := 0; i < len(text); i++ {
12 		fmt.Println(string(text[i]))
13 	}
14 	
15 	// shkronje per shkronje
16 	for _, v := range text {
17 		fmt.Println(string(v))
18 	}
19 }

https://play.golang.org/p/wRc_nHvLqKr

Rezultati:

 1 æ
 2 
 3 °
 4 å
 5 ¹
 6 ´
 7 å
 8 ¿
 9 «
10 ä
11 ¹
12 
13 
14 
15 
16 

Po ashtu, funksioni len() për një string që përmban Unicode karaktere do të raportojë gjatësi të gabuar, sepse len() tregon numrin e bajtëve, jo numrin e karaktereve. Një karakter ruhet si rune, që është int32. Pra, kur flasim për rune, flasim për një hapësirë 32 bitëshe, respektivisht 4 bajtëshe, hapësire kjo e mjaftueshme për reprezentimin e të gjitha karaktereve të Unicode. Për ta llogaritur saktë numrin e karaktereve, do ta përdorim funksionin RuneCountInString() nga pakoja utf8.

 1 package main
 2 
 3 import (
 4 	"fmt"
 5 	"unicode/utf8"
 6 )
 7 
 8 func main() {
 9 	fmt.Println(len("新年快乐"))
10 	fmt.Println(utf8.RuneCountInString("新年快乐"))
11 }

https://play.golang.org/p/kJNEwwKKMHU

Rezultati:

1 12
2 4

Karakteristikë e stringjeve në Go është se janë immutable, gjegjësisht nuk mund t’iu ndryshohet përmbajtja, pra nuk mund ta ndryshojmë drejtpërsëdrejti ndonjë karakter apo sekuencë të karaktereve të stringut .

 1 package main
 2 
 3 import (
 4 	"fmt"
 5 )
 6 
 7 func main() {
 8 	a := "Golang"
 9 
10 	fmt.Println(string(a[4]))
11 	a[4] = "m"
12 }

https://play.golang.org/p/j1bLHKiHiQt

Rezultati:

1 ./prog.go:11:7: cannot assign to a[4]

Pra, kur kemi tentuar ta ndryshojmë karakterin e pestë (indeksi 4), lajmërohet gabimi cannot assign to a[4].

Bashkangjitja e stringjeve (string concationation) kryhet me operatorin +.

 1 package main
 2 
 3 import (
 4 	"fmt"
 5 )
 6 
 7 func main() {
 8 	a := "Golang"
 9 	fmt.Println(a)
10 
11 	b := a + " programming"
12 	fmt.Println(b)
13 
14 	b += " book"
15 	fmt.Println(b)
16 }

https://play.golang.org/p/ZqRiAmTl7Mc

Rezultati:

1 Golang
2 Golang programming
3 Golang programming book

Në shembullin vijues është një program i thjeshtë i cili do ta shifruar një tekst duke e përdorur “Caesar cipher”, tek i cili bëhet zhvendosja e shkronjave për disa pozita ta zëmë djathtas kur shifrojmë, pastaj për po aq pozita majtas kur deshifrojmë.

Do ta përdorim kodin e karakterit që është numër i plotë për ta kryer operacionit të mbledhjes p.sh. me 3 (për zhvendosje 3 pozita djathtas), ndërsa gjatë dekodimit do ta kryejmë operacionin e zbritjes për zhvendosje majtas.

Programi është fare i thjeshtë dhe në këtë formë funksionin vetëm me ASCII karaktere.

 1 package main
 2 
 3 import (
 4 	"fmt"
 5 )
 6 
 7 func main() {
 8 	mesazhi := "ABCabcxyzXYZ"
 9 	fmt.Println("Origjinali: ", mesazhi)
10 	koduar := ""
11 
12 	for i := 0; i < len(mesazhi); i++ {
13 		koduar += string(mesazhi[i] + 3)
14 	}
15 	fmt.Println("Forma e koduar: ", koduar)
16 
17 	dekoduar := ""
18 	for i := 0; i < len(koduar); i++ {
19 		dekoduar += string(koduar[i] - 3)
20 	}
21 	fmt.Println("Dekoduar: ", dekoduar)
22 }

https://play.golang.org/p/tMwdc-NsN2R

Rezultati:

1 Origjinali:  ABCabcxyzXYZ
2 Forma e koduar:  DEFdef{|}[\]
3 Dekoduar:  ABCabcxyzXYZ

Funksionet

len()

Tregon numrin e karaktereve që i përmban një string.

 1 package main
 2 
 3 import (
 4 	"fmt"
 5 )
 6 
 7 func main() {
 8 	a := "Golang"
 9 	fmt.Println(len(a))
10 }

https://play.golang.org/p/oUCmOQW7M5L

Rezultati:

1 6

string()

E konverton kodin e karakterit në karakter.

 1 package main
 2 
 3 import (
 4 	"fmt"
 5 )
 6 
 7 func main() {
 8 	a := "P"
 9 	fmt.Println(a[0])
10 	fmt.Println(string(a[0]))
11 }

https://play.golang.org/p/G-gGIV08wU1

Rezultati:

1 80
2 P

Degëzimi (if)

if else

 1 package main
 2 
 3 import "fmt"
 4 
 5 func main() {
 6 	x, y := 6, 9
 7 	if x < y {
 8 		fmt.Println("x më i vogël se y")
 9 	} else if x > y {
10 		fmt.Println("x më i madh se y")
11 	} else {
12 		fmt.Println("x baraz me y")
13 	}
14 }

https://play.golang.org/p/yS-PKpRjRo1

Rezultati:

1 x  i vogël se y

Shembulli i mësipërm mund të rishkruhet kështu:

 1 package main
 2 
 3 import "fmt"
 4 
 5 func main() {
 6 	if x, y := 6, 9; x < y {
 7 		fmt.Println("x më i vogël se y")
 8 	} else if x > y {
 9 		fmt.Println("x më i madh se y")
10 	} else {
11 		fmt.Println("x baraz me y")
12 	}
13 }

https://play.golang.org/p/ehMSSCiex-l

Rezultati:

1 x  i vogël se y

Ndryshimi ndërmjet dy shembujve të fundit qëndron në faktin se në shembullin e dytë variablat x dhe y janë variabla lokale brenda skopit të strukturës if, pra nuk do të jenë të qasshme kur dilet nga kjo strukturë. Kështu, nëse e shtojmë në fund një rresht që i printon vlerat e x dhe y, do të shohim se lajmërohet gabimi ku thuhet se x dhe y nuk janë të definuar.

Kodi i modifikuar, që e përmban edhe rreshtin fmt.Println(x, y):

 1 package main
 2 
 3 import "fmt"
 4 
 5 func main() {
 6 	if x, y := 6, 9; x < y {
 7 		fmt.Println("x më i vogël se y")
 8 	} else if x > y {
 9 		fmt.Println("x më i madh se y")
10 	} else {
11 		fmt.Println("x baraz me y")
12 	}
13 
14 	fmt.Println(x, y)
15 }

https://play.golang.org/p/3xrivR6WJ0a

Rezultati:

1 undefined: x
2 undefined: y

Degëzimi (switch)

 1 package main
 2 
 3 import "fmt"
 4 
 5 func main() {
 6 	x := 20000
 7 
 8 	switch x {
 9 	case 10000:
10 		fmt.Println("Prishtina")
11 	case 20000:
12 		fmt.Println("Prizreni")
13 	case 70000:
14 		fmt.Println("Ferizaji")
15 	default:
16 		fmt.Println("Kod postar i panjohur")
17 	}
18 }

https://play.golang.org/p/Je35FFMxj6_-

Rezultati:

1 Prizreni

Blloku i kodit pas default ekzekutohet nëse nuk është plotësuar asnjë prej kushteve me case. Urdhëri default nuk është i domosdoshëm nëse logjika e kodit nuk e kërkon. Urdhëro default mund të vendoset kudo brenda switch, nuk është e domosdoshme të jetë në fund.

Tipi i vlerës që evaluuar në switch dhe tipi i vlerës në case duhet të përputhen. Nëse nuk përputhen, do të lajmërohet gabim gjatë kompajlimit. Vlerat në case nëse janë numerike, lejohet që në switch ta kemi variablin të tipit qoftë float, qoftë int.

 1 package main
 2 
 3 import "fmt"
 4 
 5 func main() {
 6 	x := 100.00
 7 
 8 	switch x {
 9 	case 100.00:
10 		fmt.Println("Prishtina")
11 	case 20000:
12 		fmt.Println("Prizreni")
13 	case 70000:
14 		fmt.Println("Ferizaji")
15 	default:
16 		fmt.Println("Kod postar i panjohur")
17 	}
18 }

https://play.golang.org/p/lFEFtH7CsbQ

Rezultati:

1 Prishtina

Kjo “tolerancë” nga ana e Go ndodh për shkak se krahasimin e vlerës së x e bëjmë ndaj vlerave numerike të cilat konsiderohen konstanta. Nëse në vend të atyre numrave vendosim variabla me të deklaruara me tip, atëherë do të kemi problem sepse Go do të kërkojë që variabli x dhe variabli me të cilin krahasohet të jenë të tipit identik, përndryshe do të raportohet gabimi për mospërputhjen e tipeve.

 1 package main
 2 
 3 import "fmt"
 4 
 5 func main() {
 6 	x := 10000
 7 	var a int16 = 20000
 8 
 9 	switch x {
10 	case 10000:
11 		fmt.Println("Prishtina")
12 	case a:
13 		fmt.Println("Prizreni")
14 	case 70000:
15 		fmt.Println("Ferizaji")
16 	default:
17 		fmt.Println("Kod postar i panjohur")
18 	}
19 }

https://play.golang.org/p/NtmdE9uh652

Rezultati:

1 invalid case a in switch on x (mismatched types int16 and int)

Secili case është scope në vete, kështu që variablat e deklaruara dhe inicializuara brenda atij blloku, nuk do të jenë të qasshëm jashtë strukturës switch.

Scope i case nuk ndryshon edhe në rastet kur kemi fallthrough nga një casecase-in vijues.

 1 package main
 2 
 3 import "fmt"
 4 
 5 func main() {
 6 	x := 10000
 7 
 8 	switch x {
 9 	case 10000:
10 		fmt.Println("Prishtina")
11 		a := 500
12 		fallthrough
13 	case 20000:
14 		fmt.Println(a)
15 		fmt.Println("Prizreni")
16 	case 70000:
17 		fmt.Println("Ferizaji")
18 	default:
19 		fmt.Println("Kod postar i panjohur")
20 	}
21 }

https://play.golang.org/p/OgGBkdIS3rX

Rezultati:

1 undefined: a

Pra, secili case është scope në vete dhe variablat e definuara në një case, nuk janë të qasshëm në case tjetër edhe kur bëjmë fallthrough.

Nëse nuk ka fare shprehje pas switch, vlera që evaluohet është boolean, prandaj dhe në case duhet të kemi shprehje boolean, siç janë operatorët e krahasimit.

 1 package main
 2 
 3 import "fmt"
 4 
 5 func main() {
 6 	i := 1
 7 	switch {
 8 	case i < 5:
 9 		fmt.Println("i me i vogel se 5")
10 	case i == 5:
11 		fmt.Println("i baras 5")
12 	default:
13 		fmt.Println("i me i madh 5")
14 	}
15 }

https://play.golang.org/p/rtdtszn6ZrR

Rezultati:

1 i me i vogel se 5

Nëse në më tepër se një case plotësohet kushti, ekzekutohet vetëm kodi pas case të parë.

 1 package main
 2 
 3 import "fmt"
 4 
 5 func main() {
 6 	i := 1
 7 	switch {
 8 	case i < 5:
 9 		fmt.Println("i me i vogel se 5")
10 	case i < 7:
11 		fmt.Println("i me i vogel se 7")
12 	}
13 }

https://play.golang.org/p/jfczKTCcaSU

Rezultati:

1 i me i vogel se 5

Switch në Go nuk ka nevojë për break.
Nëse duhet që nga një case të kalohet te tjetri case pa e evaluuar kushtin, përdoret urdhëri fallthrough.

 1 package main
 2 
 3 import "fmt"
 4 
 5 func main() {
 6 	x := 20000
 7 
 8 	switch x {
 9 	case 10000:
10 		fmt.Println("Prishtina")
11 	case 20000:
12 		fmt.Println("Prizreni")
13 		fallthrough
14 	case 70000:
15 		fmt.Println("Ferizaji")
16 	default:
17 		fmt.Println("Kod postar i panjohur")
18 	}
19 }

https://play.golang.org/p/elGAO1aAXjC

Rezultati:

1 Prizreni
2 Ferizaji

Kllapat e mëdha (curly braces) nuk janë të domosdoshëm për definimin e bllokut të kodit pas case.

 1 package main
 2 
 3 import "fmt"
 4 
 5 func main() {
 6 	i := 1
 7 	switch {
 8 	case i < 5:
 9 		{
10 			fmt.Println("i me i vogel se 5")
11 		}
12 	case i == 5:
13 		{
14 			fmt.Println("i baras 5")
15 		}
16 	default:
17 		{
18 			fmt.Println("i me i madh 5")
19 		}
20 	}
21 
22 	// Njëjtë si
23 
24 	switch {
25 	case i < 5:
26 		fmt.Println("i me i vogel se 5")
27 	case i == 5:
28 		fmt.Println("i baras 5")
29 
30 	default:
31 		fmt.Println("i me i madh 5")
32 	}
33 }

https://play.golang.org/p/qj3X-PyUAME

Rezultati:

1 i me i vogel se 5
2 i me i vogel se 5

Shprehjet në case mund të jenë vlera apo shprehje.

 1 package main
 2 
 3 import "fmt"
 4 
 5 func main() {
 6 	a := 2
 7 	i := 8
 8 
 9 	switch i {
10 	case 5:
11 		fmt.Println("i baras 5")
12 	case a + 6:
13 		fmt.Println("i baras 8")
14 	}
15 }

https://play.golang.org/p/Z147M9xT7Co

Rezultati:

1 i baras 8

Mund të përdorim çfarëdo numri të case.

Brenda case mund të vendosen disa shprehje të përputhjes, të ndarë me presje.

 1 package main
 2 
 3 import "fmt"
 4 
 5 func main() {
 6 	a := 5
 7 
 8 	switch a {
 9 	case 5, 6, 7:
10 		fmt.Println("a baras me 5, 6 ose 7")
11 	case 8:
12 		fmt.Println("a baras me 8")
13 	}
14 }

https://play.golang.org/p/WW4022ZHACD

Rezultati:

1 a baras me 5, 6 ose 7

Switch initializer

 1 package main
 2 
 3 import (
 4 	"fmt"
 5 	"time"
 6 )
 7 
 8 func main() {
 9 	switch m := time.Now(); {
10 	case m.Month() >= 12 || m.Month() < 3:
11 		fmt.Println("Dimër")
12 	case m.Month() >= 3 && m.Month() < 6:
13 		fmt.Println("Pranverë")
14 	case m.Month() >= 6 && m.Month() < 9:
15 		fmt.Println("Verë")
16 	case m.Month() >= 9 && m.Month() < 12:
17 		fmt.Println("Vjeshtë")
18 	default:
19 		fmt.Println("Nuk di cila stinë është")
20 	}
21 }

https://play.golang.org/p/ZvvHIkLzc6p

Rezultati:

1 Vjeshtë

Shembulli në vijim ka të bëjë me ditët e javës. Lexohet dita e javës nga sistemi nëpërmes pakos time dhe pastaj bëhet degëzimi në varësi prej vlerës së kthyer. Për vlerat ` time.Saturday dhe time.Sunday` do të raportohet “Fundjavë”, ndërsa për vlerat tjera do të raportohet “Ditë pune”.

 1 package main
 2 
 3 import (
 4 	"fmt"
 5 	"time"
 6 )
 7 
 8 func main() {
 9 	switch time.Now().Weekday() {
10 	case time.Saturday, time.Sunday:
11 		fmt.Println("Fundjavë")
12 	default:
13 		fmt.Println("Ditë pune")
14 	}
15 }

https://play.golang.org/p/U3Yu8KGbY6-

Rezultati:

1 Ditë pune

Switch mund të përdoret edhe për determinimin e tipeve të variablave, në rastet kur funksioni pranon si parametër një interfejs të zbrazët. Interfejsat e zbrazët përdoren për pranimin e vlerave tipeve të ndryshme. Pas pranimit të vlerës, analizojmë me switch v := i.(type) cilit tip konkret i përket variabli i bartur në funksion.

 1 package main
 2 
 3 import "fmt"
 4 
 5 func main() {
 6 	printo("Tung")
 7 	printo(67)
 8 	printo(true)
 9 }
10 
11 func printo(i interface{}) {
12 	switch v := i.(type) {
13 	case int:
14 		fmt.Printf("Tipi është integer për %v\n", v)
15 	case string:
16 		fmt.Printf("Tipi është string për %v\n", v)
17 	case bool:
18 		fmt.Printf("Tipi është bool për %v\n", v)
19 	default:
20 		fmt.Printf("Nuk di çfarë tipi është %v \n", v)
21 	}
22 }

https://play.golang.org/p/flsKeobzBA2

Rezultati:

1 Tipi është string për Tung
2 Tipi është integer për 67
3 Tipi është bool për true

Ciklet (for)

for

 1 package main
 2 
 3 import (
 4 	"fmt"
 5 )
 6 
 7 func main() {
 8 	sum := 0
 9 	for i := 0; i < 100; i++ {
10 		sum += i
11 	}
12 	fmt.Println(sum)
13 }

https://play.golang.org/p/iWDx5k3TGZ8

break

 1 package main
 2 
 3 import (
 4 	"fmt"
 5 )
 6 
 7 func main() {
 8 	i := 0
 9 	for {
10 		if i == 10 {
11 			break
12 		}
13 		fmt.Println("Vlera e i është:", i)
14 		i++
15 	}
16 	fmt.Println("Dalja nga cikli")
17 }

https://play.golang.org/p/hkZAYJ_rVtN

continue

 1 package main
 2 
 3 import (
 4 	"fmt"
 5 )
 6 
 7 func main() {
 8 	for i := 1; i <= 5; i++ {
 9 		if i == 3 {
10 			continue
11 		}
12 		fmt.Println(i)
13 	}
14 }

https://play.golang.org/p/5yyYPx2XEYs

Tipet kompozite: Vargjet

Vargjet (arrays) dhe struktet (structs) janë tipe agregate, vlerat e tyre janë lista të vlerave të tjera në memorje.
Të gjitha elementet e një vargu janë të një tipi.

Vargjet

Vargu është një sekuencë e gjatësisë fikse të vlerave të tipit të caktuar. Për shkak të gjatësisë fikse, vargjet shumë rrallë përdoren drejtpërsëdrejti. Në vend të përdorimit direkt të vargjeve, në Go rekomandohet përdorimi i segmenteve (slices), të cilave mund t’u shtohen apo largohen elementet, sipas nevojës.

Anëtarëve individualë të vargut mund t’i qasemi duke përdorur indeksin, dhe indeksi i elementit të parë është zeroja. Numrin e elementeve të një vargu e lexojmë me funksionin len().

1 var a  = [5]int{7, 2, 3, 4, 9} // Vargu a me 5 numra të plotë
2 fmt.Println(a[0]) // Shtype anëtarin e parë të vargut a
3 fmt.Println(len(a)) // Trego sa anëtarë i ka vargu a 
4 fmt.Println(a[len(a)-1]) // Shtype anëtarin e fundit të vargut a

https://play.golang.org/p/r_en5mgRAHS

Me përdorimin e strukturës for mund t’iu qasemi në mënyrë sekuencionale indekseve dhe anëtarëve të një vargu.

 1 package main
 2 
 3 import (
 4 	"fmt"
 5 )
 6 
 7 func main() {
 8 	var a = [5]int{7, 2, 3, 4, 9}
 9 
10 	// Shtypi indekset dhe vlerat e anëtarëve të vargut
11 	for i, v := range a {
12 		fmt.Printf("%d %d\n", i, v)
13 	}
14 
15 	// Shtypi vlerat e anëtarëve të vargut
16 	for _, v := range a {
17 		fmt.Printf("%d\n", v)
18 	}
19 }

https://play.golang.org/p/WQu60tVtTpq

Elementet e një vargu të ri kanë “zero vlerë” , që në rastin e numrave është zero, për stringjet - string i zbrazët (“”). Në shembullin vijues, anëtarit të tretë (me indeksin 2), nuk i është dhënë vlerë, prandaj x[2] ka vlerë 0.

 1 package main
 2 
 3 import (
 4 	"fmt"
 5 )
 6 
 7 func main() {
 8 	var x [3]int = [3]int{1, 2}
 9 	fmt.Println(x[2]) 
10 }

https://play.golang.org/p/yJ3X4GPspOD

Nëse i qasemi një indeksi inekzistent, lajmërohet gabimi qysh në fazën e kompajlimit.

 1 package main
 2 
 3 import (
 4 	"fmt"
 5 )
 6 
 7 func main() {
 8 	var x [3]int = [3]int{1, 2}
 9 	fmt.Println(x[4]) 
10 }

https://play.golang.org/p/A-rjdARpHMw

Rezultati:

1 invalid array index 4 (out of bounds for 3-element array)

Nuk është e domosdoshme që të ceket numri i elementeve, sepse duke përdorur ... në vend të numrit, do të formohet një varg me aq vende sa vlera janë dhënë gjatë inicializimit.

 1 package main
 2 
 3 import (
 4 	"fmt"
 5 )
 6 
 7 func main() {
 8 	x := [...]int{5, 2, 6, 4}
 9 	fmt.Println(x)
10 }

https://play.golang.org/p/zGwLoriX2FI

Rezultati:

1 [5 2 6 4]

Kompajleri do ta deklarojë variablin x si varg me 4 elemente, sepse po aq elemente kemi shënuar gjatë inicializimit.

Një varg mund të përmbajë elemente të tipit të njëjtë, por kjo nuk do të thotë se mund të përmbajë vetëm tipet primitive. Në shembullin e mëposhtëm paraqitet një varg me 3 elemente që përmban vlera të tipit struct, i cili përmban 3 fusha të tipit string dhe një të tipit int.

 1 package main
 2 
 3 import (
 4 	"fmt"
 5 )
 6 
 7 type Libri struct {
 8 	Titulli, Autori, ISBN string
 9 	Faqe                  int
10 }
11 
12 func main() {
13 	a := [3]Libri{
14 		Libri{Autori: "Dan Brown",
15 			Titulli: "Origjina",
16 			ISBN:    "9789994305353",
17 			Faqe:    616,
18 		},
19 		Libri{Autori: "Gabriel Garcia Marquez",
20 			Titulli: "Për dashurinë dhe demonë të tjerë",
21 			ISBN:    "9789992731741",
22 			Faqe:    192,
23 		},
24 		Libri{Autori: "Isabel Allende",
25 			Titulli: "Shuma e ditëve",
26 			ISBN:    "9789994305155",
27 			Faqe:    368},
28 	}
29 
30 	for _, v := range a {
31 		fmt.Println(v.Autori, v.Titulli, v.ISBN, v.Faqe)
32 	}
33 }

https://play.golang.org/p/gIcygI4736H

Rezultati:

1 Dan Brown Origjina 9789994305353 616
2 Gabriel Garcia Marquez Për dashurinë dhe demonë  tjerë 9789992731741 192
3 Isabel Allende Shuma e ditëve 9789994305155 368

Vargu multidimensional

 1 package main
 2 
 3 import (
 4 	"fmt"
 5 )
 6 
 7 func main() {
 8 	var vargu [4][3]int
 9 	for i := 0; i <= 3; i++ {
10 		for j := 0; j <= 2; j++ {
11 			vargu[i][j] = i + j
12 		}
13 	}
14 
15 	fmt.Println(vargu)
16 }

https://play.golang.org/p/dbDO_0YG0mo

Rezultati:

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

Tipet kompozite: Segmentet

Duke qenë se vargjet në Go kanë gjatësi fikse dhe vlerat mund të jenë vetëm të një tipi të njëjtë, nuk përdoren shpesh drejtpërsëdrejti. Në anën tjetër, segmentet (slices) i hasim kudo nëpër kod për shkak të fleksibilitetit të tyre.

Segmentet paraqesin një pjesë të vargut, dhe njëjtë sikurse vargu, edhe segmentet kanë indekse dhe gjatësi (numër elementesh). Për dallim nga vargjet, segmenteve mund t’ua ndryshojmë gjatësinë, respektivisht t’u shtojmë anëtarë të rinj apo ta largojmë ndonjë anëtar ekzistues.

Një segment deklarohet njëjtë sikurse një varg, me dallimin që nuk ceket numri i anëtarëve.

var x []int

Në këtë rast, është krijuar një segment me gjatësi 0.

Në shembullin vijues, fillimisht krijohet segmenti x me 0 anëtarë, e më pas me funksionin append() shtohet nga një anëtarë tri herë radhazi.

 1 package main
 2 
 3 import (
 4 	"fmt"
 5 )
 6 
 7 func main() {
 8 	var x []int
 9 
10 	x = append(x, 5)
11 	fmt.Println(x)
12 
13 	x = append(x, 3)
14 	fmt.Println(x)
15 
16 	x = append(x, 8)
17 	fmt.Println(x)
18 
19 }

https://play.golang.org/p/DKLRlKjpW50

Rezultati:

1 [5]
2 [5 3]
3 [5 3 8]

Tipet kompozite: Mapat

Mapa është koleksion i vlerave çift indeks-vlerë. Në gjuhët tjera iu referohemi si varg asociativ (associative array), hash table apo dictionary. Vlerat në një mapë kërkohen sipas çelësit/indeksit.

Mapa e zbrazët krijohet me sintaksën:

make(map[key-type]val-type)

Për shembull, për indeks të tipit int dhe vlera të tipit string, shkruajmë:

make(map[int]string)

Si indeks mund të përdoren tipet: int, float, numër kompleks, string, pointer, ose interfejs.

Mund të krijohen edhe mapa që përmbajnë mapa të tjera, p.sh.:

map[string]map[string]string

 1 package main
 2 
 3 import "fmt"
 4 
 5 func main() {
 6 	m := make(map[string]float32)
 7 	m["jan"] = 430.75
 8 	m["feb"] = 230.45
 9 	m["mar"] = 130.25
10 	fmt.Println("map:", m)
11 }

https://play.golang.org/p/Mqk5n33s8JX

Rezultati:

1 map: map[feb:230.45 jan:430.75 mar:130.25]

Paraqitja e vlerave me një for loop:

 1 package main
 2 
 3 import "fmt"
 4 
 5 func main() {
 6 	m := make(map[string]float32)
 7 	m["jan"] = 430.75
 8 	m["feb"] = 230.45
 9 	m["mar"] = 130.25
10 	for i, v := range m {
11 		fmt.Println(i, v)
12 	}
13 }

https://play.golang.org/p/t2Q0z-4kqOe

Rezultati:

1 jan 430.75
2 feb 230.45
3 mar 130.25

Numri i elementeve të mapës, përdoret funksioni len():

 1 package main
 2 
 3 import "fmt"
 4 
 5 func main() {
 6 	m := make(map[string]float32)
 7 	m["jan"] = 430.75
 8 	m["feb"] = 230.45
 9 	m["mar"] = 130.25
10 	fmt.Println("len:", len(m))
11 }

https://play.golang.org/p/sYZNn4OJfEf

Rezultati:

1 Numri i elementeve: 3

Fshirja e një anëtari të mapës. Përdoret funksioni delete():

 1 package main
 2 
 3 import "fmt"
 4 
 5 func main() {
 6 	m := make(map[string]float32)
 7 	m["jan"] = 430.75
 8 	m["feb"] = 230.45
 9 	m["mar"] = 130.25
10 	
11 	delete(m, "feb")
12 	for i, v := range m {
13 		fmt.Println(i, v)
14 	}
15 }

https://play.golang.org/p/syj0PtA5DbH

Rezultati:

1 mar 130.25
2 jan 430.75

Kur kërkojmë një anëtar të mapës me indeks që nuk ekziston, vlera e kthyer është zero, kështu që mund të jemi në konfuzion: a është vlera e anëtarit zero, apo nuk u gjet anëtari me atë indeks. Në situata të këtilla, i përdorim dy variabla, ku variabla e dytë do të ketë vlerë të tipit boolean (true ose false).

Nëse është true, anëtari me atë indeks ekziston, dhe nëse kthen false - anëtari me atë indeks nuk ekziston.

Nëse na nevojitet vetëm verifikimi i ekzistencës së një anëtari me indeks të caktuar, atëherë si variabël të parë e përdorim shenjën _.

 1 package main
 2 
 3 import "fmt"
 4 
 5 func main() {
 6 	m := make(map[string]float32)
 7 	m["jan"] = 430.75
 8 	m["feb"] = 230.45
 9 	m["mar"] = 130.25
10 
11 	v, ok := m["feb"]
12 	fmt.Println("Vlera e anëtarit m[\"feb\"]", v)
13 
14 	if ok {
15 		fmt.Println("Anëtari me indeksin 'feb' ekziston")
16 	} else {
17 		fmt.Println("Anëtari me indeksin 'feb' nuk ekziston")
18 	}
19 
20 	// Leximi i vlerës dhe verifikimi i ekzistencës së anëtarit
21 	v, ok = m["apr"]
22 	fmt.Println("Vlera e anëtarit m[\"apr\"]", v)
23 
24 	if ok {
25 		fmt.Println("Anëtari me indeksin 'apr' ekziston")
26 	} else {
27 		fmt.Println("Anëtari me indeksin 'apr' nuk ekziston")
28 	}
29 
30 	// Verifikimi i ekzistncës së anëtarit
31 	_, ok = m["may"]
32 
33 	if ok {
34 		fmt.Println("Anëtari me indeksin 'may' ekziston")
35 	} else {
36 		fmt.Println("Anëtari me indeksin 'may' nuk ekziston")
37 	}
38 }

https://play.golang.org/p/__jke7ynjYE

Rezultati:

1 Vlera e anëtarit m["feb"] 230.45
2 Anëtari me indeksin 'feb' ekziston
3 Vlera e anëtarit m["apr"] 0
4 Anëtari me indeksin 'apr' nuk ekziston
5 Anëtari me indeksin 'may' nuk ekziston

Deklarimi dhe inicializimi i mapës:

1 package main
2 
3 import "fmt"
4 
5 func main() {
6 	m := map[string]float32{"jan": 430.75, "feb": 230.45, "mar": 130.25}
7 	fmt.Println(m)
8 }

https://play.golang.org/p/BKwfqr7omWk

Rezultati:

1 map[feb:230.45 jan:430.75 mar:130.25]

Tipet kompozite: Struktet

Strukti është një tip i definuar (user-defined type) që përmban koleksion të fushave të emërtuara. Përdoret për grupimin e të dhënave brenda një njësie të vetme. Për shembull, një strukt mund të përmbajë të dhëna mbi një libër: titulli, autori, numri ISBN, numri i faqeve, etj.

1 type Libri struct {
2 	Titulli string
3 	Autori  string
4 	ISBN     string
5 	Faqe    int
6 }

Me fjalën kyçe type e krijojmë një tip të ri të të dhënave; në vijim e shënojmë emrin për tipin e ri që në këtë rast është Libri, ndërsa në fund shënojmë struct për të treguar se jemi duke definuar një strukt.

Mund ta rishkruajmë edhe duke i grupuar fushat që i përkasinë tipit të njëjtë:

1 type Libri struct {
2 	Titulli, Autori, ISBN string
3 	Faqe                 int
4 }

Deklarimi dhe inicializimi i struktit

Deklarimi i një variable të tipit struct

Deklarimi i variablit bëhet sikurse edhe me tipet e tjera:

1 var lb Libri

Në kodin e mësipërm kemi deklaruar një variabël lb të jetë e tipit Libri, duke mos e inicializuar. Në këtë rast, të gjitha fushat e struktit Libri (Titulli, Autori, ISBN dhe Faqe) marrin vlerat iniciale sipas tipit:

  • Fushat e tipeve numerike marrin vlerën zero (0)
  • Fushat e tipit string marrin vlerën e stringut bosh (“”)
  • Fushat e tipit boolean marrin vlerën false

Një strukt mund ta deklarojmë dhe inicializojmë në mënyrën vijuese:

1 var lb = Libri{"Origjina", "Dan Brown", "9789994305353", 616}

Duhet të kemi parasysh që renditja e vlerave të korrespondojë me renditjen e fushave të struktit.

Në këtë formëm, duhet të jenë prezente vlerat për të gjitha fushat, pra nuk mund të përdorim:

1 var lb = Libri{"Origjina"}

Në këtë rast, lajmërohet gabimi too few values in Libri literal.

Cekja e fushave gjatë inicializimit të struktit

Për inicializim, mund ta përdorim fomën fusha:vlera, me ç’rast renditja e fushave nuk luan rol.

1 	var lb = Libri{Autori: "Dan Brown", Titulli: "Origjina", Faqe: 616, ISBN: "97899943\
2 05353"}

Për ta bërë struktin më të lexueshëm, mund të shkruajmë edhe në formën vijuese:

1 	var lb = Libri{Autori: "Dan Brown",
2 		Titulli: "Origjina",
3 		Faqe:    616,
4 		ISBN:    "9789994305353",
5 		}

Nëse e përdorim këtë formë, rreshti i fundit patjetër duhet të përfundojë me presje. Nëse kllapën e madhe mbyllëse } e vendosim në fund të rreshtit të fundit, atëherë presja nuk shënohet.

1 	var lb = Libri{Autori: "Dan Brown",
2 		Titulli: "Origjina",
3 		Faqe:    616,
4 		ISBN:    "9789994305353"}

Me përdorimin e emrave të fushave, mund ta bëjmë edhe inicializimin e pjesshëm të struktit, ku ato fusha që mungojnë do të marrin zero vlerat përkatëse.

1 	var lb = Libri{Autori: "Dan Brown",
2 		Titulli: "Origjina"}

Në këtë rast, fusha ISBN merr vlerën “”, ndërsa fusha Faqe merr vlerën 0.

Qasja vlerave të fushave të struktit

 1 package main
 2 
 3 import (
 4 	"fmt"
 5 )
 6 
 7 type Libri struct {
 8 	Titulli, Autori, ISBN string
 9 	Faqe                  int
10 }
11 
12 func main() {
13 	var lb = Libri{Autori: "Dan Brown",
14 		Titulli: "Origjina",
15 		Faqe:    616,
16 		ISBN:    "9789994305353"}
17 
18 	fmt.Println("Titull i librit:", lb.Titulli)
19 	fmt.Println("Autori:", lb.Autori)
20 	fmt.Println("ISBN:", lb.ISBN)
21 	fmt.Println("Gjithsej faqe:", lb.Faqe)
22 }

https://play.golang.org/p/lBpe2ryajLw

Rezultati:

1 Titull i librit: Origjina
2 Autori: Dan Brown
3 ISBN: 9789994305353
4 Gjithsej faqe: 616

Siç e shohim, fushave individuale të struktit i qasemi ashtu që e shënojmë variablin që e përmban struktit, pikën e pastaj emrin e fushës: lb.Autori.

Përdorimi i pointerëve me strukt

 1 package main
 2 
 3 import (
 4 	"fmt"
 5 )
 6 
 7 type Libri struct {
 8 	Titulli, Autori, ISBN string
 9 	Faqe                  int
10 }
11 
12 func main() {
13 	var lb = Libri{Autori: "Dan Brown",
14 		Titulli: "Origjina",
15 		Faqe:    616,
16 		ISBN:    "9789994305353"}
17 
18 	fmt.Println(lb.Titulli)
19 
20 	lb2 := &lb
21 	fmt.Println(lb2.Titulli)
22 
23 	lb2.Titulli = "Zanafilla"
24 	fmt.Println(lb.Titulli)
25 }

https://play.golang.org/p/R-8D8LZZLCa

Rezultati:

1 Origjina
2 Origjina
3 Zanafilla

Vërejmë se nuk kemi nevojë të bëjmë deferencim eksplicit me *.

Krijimi i structit me new()

 1 package main
 2 
 3 import (
 4 	"fmt"
 5 )
 6 
 7 type Libri struct {
 8 	Titulli, Autori, ISBN string
 9 	Faqe                  int
10 }
11 
12 func main() {
13 	var lb = new(Libri)
14 
15 	lb.Titulli = "Origjina"
16 	lb.Autori = "Dan Brown"
17 	lb.ISBN = "9789994305353"
18 	lb.Faqe = 616
19 	fmt.Println(lb)
20 }

https://play.golang.org/p/ien1oao99Sr

Rezultati:

1 &{Origjina Dan Brown 9789994305353 616}

Vërejmë se është krijuar pointer i struktit.

Eksportimi i strukteve dhe fushave

Struktet që fillojnë me shkronjë të madhe do të eksportohen, dmth do të jenë të qasshëm edhe nga pakot tjera.

Po ashtu edhe fushat e struktit që fillojnë me shkronjë të madhe do të eksportohen.

Atyre që emri iu fillon me shkronjë të vogël, do të jenë të qasshëm vetëm brenda pakos ku janë deklaruar.

Struktet janë tipe me vlerë, jo me referencë.

 1 package main
 2 
 3 import (
 4 	"fmt"
 5 )
 6 
 7 type Libri struct {
 8 	Titulli, Autori, ISBN string
 9 	Faqe                  int
10 }
11 
12 func main() {
13 	var lb = Libri{Autori: "Dan Brown",
14 		Titulli: "Origjina",
15 		Faqe:    616,
16 		ISBN:    "9789994305353"}
17 
18 	lb2 := lb
19 	lb2.Titulli = "Zanafilla"
20 	fmt.Println(lb2)
21 }

https://play.golang.org/p/8-5Ukll6Pk1

Rezultati:

1 {Zanafilla Dan Brown 9789994305353 616}

Shohim se lb2 përmban kopjen e vlerave të lb dhe jo referencë.

Krahasimi i strukteve

Dy strukte janë të barabartë nëse të gjitha fushat janë të barabarta ndërmjet vete.

 1 package main
 2 
 3 import "fmt"
 4 
 5 type Katerkendeshi struct {
 6 	gjeresia int
 7 	gjatesia int
 8 }
 9 
10 func main() {
11 	k1 := Katerkendeshi{7, 3}
12 	k2 := Katerkendeshi{7, 3}
13 
14 	if k1 == k2 {
15 		fmt.Println("Të barabartë")
16 	} else {
17 		fmt.Println("Jo të barabartë")
18 	}
19 }

https://play.golang.org/p/m9M-gGI-rLY

Rezultati:

1  barabartë

Pointerët

 1 package main
 2 
 3 import (
 4 	"fmt"
 5 )
 6 
 7 func main() {
 8 	a := 65
 9 	fmt.Println(a)
10 
11 	b := &a
12 
13 	*b = 78
14 	fmt.Println(a, *b)
15 
16 	var c int = 6
17 	var d *int = &c
18 	*d = 88
19 	fmt.Println(c, *d)
20 }

https://play.golang.org/p/4kXvmZoaH72

Rezultati:

1 65
2 78 78
3 88 88

Interfejsat

Interfejsi është një set metodash. Një interfejs përshkruan sjelljen (behaviour) e një tipi.

Nëse interfejsi nuk e definon asnjë metodë, atëherë ai është interfejs i zbrazët. Të gjitha tipet e implementojnë interfejsin e zbrazët.

Në Go nuk kemi deklarim eksplicit të interfejsit me implements, siç e kemi në gjuhët tjera. Cilido tip i të dhënave (p.sh. strukt), që është pranues (receiver) i metodave të definuara në një interfejs, në mënyrë implicite e implementon atë interfejs.

 1 package main
 2 
 3 import (
 4 	"fmt"
 5 	"math"
 6 )
 7 
 8 type FormaGjeometrike interface {
 9 	Siperfaqja() float32
10 }
11 
12 type Katerkendeshi struct {
13 	Gjeresia float32
14 	Gjatesia float32
15 }
16 
17 func (k Katerkendeshi) Siperfaqja() float32 {
18 	return k.Gjeresia * k.Gjatesia
19 }
20 
21 func (r Rrethi) Siperfaqja() float32 {
22 	return math.Pi * r.Rrezja * r.Rrezja
23 }
24 
25 func Matja(fgj FormaGjeometrike) float32 {
26 	return fgj.Siperfaqja()
27 }
28 
29 type Rrethi struct {
30 	Rrezja float32
31 }
32 
33 func main() {
34 	r := Rrethi{5}
35 	fmt.Println("Siperfaqja e rrethit: ")
36 	fmt.Println(Matja(r))
37 
38 	k := Katerkendeshi{5, 2}
39 	fmt.Println("Siperfaqja e katerkendeshit: ")
40 	fmt.Println(Matja(k))
41 }

https://play.golang.org/p/OMrS3VIfpHc

Rezultati:

1 Siperfaqja e rrethit: 
2 78.53982
3 Siperfaqja e katerkendeshit: 
4 10
 1 package main
 2 
 3 import (
 4 	"fmt"
 5 )
 6 
 7 type Person interface {
 8 	Pershendetje() string
 9 }
10 
11 type Student struct {
12 	Emri         string
13 	Mbiemri      string
14 	Mosha        int
15 	NotaMesatare float32
16 }
17 
18 type Punetor struct {
19 	Emri    string
20 	Mbiemri string
21 	Mosha   int
22 	Paga    float32
23 }
24 
25 func (st Student) Pershendetje() string {
26 	return "Mirëmëngjes, " + st.Emri
27 }
28 
29 func (pn Punetor) Pershendetje() string {
30 	return "Punë të mbarë, " + pn.Emri
31 }
32 
33 // Pranon tipet Student dhe Punetor,
34 // sepse te dyja e implementojne interfejsin Person
35 func TeDhenat(p Person) string {
36 	rez := ""
37 	switch p.(type) {
38 	case Student:
39 		rez = p.(Student).Emri + " " + p.(Student).Mbiemri + ", student, nota mesatare " +\
40  fmt.Sprintf("%.2f", p.(Student).NotaMesatare)
41 	case Punetor:
42 		rez = p.(Punetor).Emri + " " + p.(Punetor).Mbiemri + ", punëtor, paga " + fmt.Spri\
43 ntf("%.2f", p.(Punetor).Paga) + " EURO"
44 	}
45 	return rez
46 }
47 
48 func main() {
49 	// STUDENTI
50 	var a = Student{
51 		Emri:         "Taulant",
52 		Mbiemri:      "Berisha",
53 		Mosha:        19,
54 		NotaMesatare: 8.9,
55 	}
56 
57 	// PUNËTORI
58 	fmt.Println(a.Pershendetje())
59 	fmt.Println(TeDhenat(a))
60 
61 	var b = Punetor{
62 		Emri:    "Durim",
63 		Mbiemri: "Krasniqi",
64 		Mosha:   28,
65 		Paga:    1000,
66 	}
67 
68 	fmt.Println(b.Pershendetje())
69 	fmt.Println(TeDhenat(b))
70 }

https://play.golang.org/p/XOdPtP4pYU4

Rezultati:

1 Mirëmëngjes, Taulant
2 Taulant Berisha, student, nota mesatare 8.90
3 Punë  mbarë, Durim
4 Durim Krasniqi, punëtor, paga 1000.00 EURO

Variablat a dhe b mund t’i deklarojmë të tipit Person, me çka a bëhet variabël e tipit Student që implementon interfejsin Person, ndërsa b bëhet variabël e tipit Punetor që implementon po atë interfejs. Mirëpo, ky nuk është implementim eksplicit i interfejsit, sepse interfejsi konsiderohet i implementuar nëse një tip (në këtë rast struct) posedon metodat që janë listuar në interfejs.

Nëse tipi, në këtë rast strukti, nuk i posedon TË GJITHA metodat e cekura në interfejs dhe me po të njëjtat nënshkrime të funksioneve (function signature), pra numrin dhe tipin e argumenteve dhe të rezultateve kthyese, atëherë nuk është duke e implementuar atë interfejs.

 1 // STUDENTI
 2 	var a Person = Student{
 3 		Emri:         "Taulant",
 4 		Mbiemri:      "Berisha",
 5 		Mosha:        19,
 6 		NotaMesatare: 8.9,
 7 	}
 8 
 9 	// PUNËTORI
10 	var b Person = Punetor{
11 		Emri:    "Durim",
12 		Mbiemri: "Krasniqi",
13 		Mosha:   28,
14 		Paga:    1000,
15 	}

Funksionet

  • Funksionet janë njësi të ripërdorshme të kodit.
  • Një funksion definohet me fjalën kyçe func.
  • Funksioni deklarohet një herë dhe mund të përdoret shumë herë.
  • Funksionet në Go mund të kthejnë më tepër se një vlerë
  • Funksionet në Go mund të lidhen me një tip të caktuar, me ç’rast ai tip quhet receiver (pranues)
  • Parametrat barten me vlerë (pass-by-value), që do të thotë se vlerat e parametrave kopjohen.

Go kërkon kthime eksplicite (explicit returns), pra funksioni nuk do ta kthejë me return vlerën e shprehjes së fundit në funksion. Megjithatë, nëse emri i një variabli ceket si vlerë kthyese në rreshtin e deklarimit të funksionit, funksioni do ta kthejë atë vlerë pa e cekur emrin e variablit. Në shembullin vijues, te nënshkrimi i funksionit (function signature) variabli c i tipit int është deklaruar si variabël vlerën e të cilit do ta kthejë urdhëri return.

 1 package main
 2 
 3 import (
 4 	"fmt"
 5 )
 6 
 7 func main() {
 8 	fmt.Println(Shumezo(3, 5))
 9 }
10 
11 func Shumezo(a, b int) (c int) {
12 	c = a * b
13 	return
14 }

https://play.golang.org/p/EO5SJZftjMm

Rezultati:

1 15

Closures

Recursion

Funksionet variadike

 1 package main
 2 
 3 import "fmt"
 4 
 5 func main() {
 6 	fmt.Println(mbledh(7, 3))
 7 	fmt.Println(mbledh(9, 2, 1))
 8 }
 9 
10 func mbledh(arg ...int) int {
11 	shuma := 0
12 	for _, v := range arg {
13 		shuma += v
14 	}
15 	return shuma 
16 }

https://play.golang.org/p/W5uTt_yTsRa

Funksion/metodë me pranues

Funksioneve që janë deklaruar nga një interfejs iu referohemi si metoda dhe ato mund të implementohen nga tipet e kustomizuar.

Implementimi i një metode duket ekzaktësisht si i funksionit, me dallimin që para emrit të funksionit shënohet tipi që e implementon atë, p.sh. (r Katerkendeshi) në shembullin vijues.

Kjo sintaksë mundëson që metoda e njëjtë t’iu përcaktohet tipeve të ndryshme.

Një tip dhe pointeri i tij e ndajnë namespace-n e njëjtë, prandaj një metodë mund të implementohet vetëm për njërin prej tyre. Në rast se metodën e përdorimin edhe për tipin edhe për pointerin, do të lajmërohet gabim gjatë kompajlimit sepse konsiderohet si rideklarim i metodës.

Metodat nuk mund të definohen për interfejsat, por vetëm për tipet konkrete. Megjithatë, interfejsët mund të përdoren në tipet kompozite, përfshirë këtu edhe parametrat e funksionit dhe vlerat kthyese.

Në Go, gjithçka bartet me vlerë (pass by value), kështu që kur thirret një funksion apo metodë, krijohet kopja e variablit në stack. Kjo nënkupton që ndryshimet që bëhen ndaj vlerë nuk reflektohen jashtë funksionit të thirrur. Edhe segmentet, mapat dhe tipet e tjerë referues barten me vlerë, por meqë struktura e brendshme e tyre përmban pointerë, ato veprojnë sikurse të ishin bartur me referencës. Nëse metoda është e definuar për një tip, nuk mund të definohet për pointerin e tij dhe anasjelltas.

 1 package main
 2 
 3 import "fmt"
 4 
 5 type Katerkendeshi struct {
 6 	a, b int
 7 }
 8 
 9 func (r Katerkendeshi) Siperfaqja() int {
10 	return r.a* r.b
11 }
12 
13 func main() {
14 	x := Katerkendeshi{a: 5, b: 3}
15 	fmt.Println("Siperfaqja: ", x.Siperfaqja())
16 }

https://play.golang.org/p/s85X-9RHTMF

Rezultati:

1 Siperfaqja:  15

Për ta ndryshuar variablin origjinal, argumenti duhet të jetë pointer i vetë variablit. Pointeri do të kopjohet, por ai do të jetë referencë e adresës së njëjtë memorike, duke mundësuar kështu ndryshimin e vlerës.

 1 package main
 2 
 3 import (
 4 	"fmt"
 5 )
 6 
 7 func main() {
 8 	a := 5
 9 	fmt.Println(a)
10 	test(&a)
11 	fmt.Println(a)
12 
13 }
14 
15 func test(b *int) bool {
16 	*b = 555
17 	return true
18 }

https://play.golang.org/p/ef75lkFpp3n

Rezultati:

1 5
2 555

Kur pranuesi i metodës është tip e jo pointer, nëse vlera i ndryshohet brenda funksionit, nuk do të propagohet jashtë funksionit, pra do të trajtohet si variabël lokale.

 1 package main
 2 
 3 import (
 4 	"fmt"
 5 )
 6 
 7 type Anetari struct {
 8 	Emri  string
 9 	Mosha int
10 }
11 
12 func (a Anetari) TregoMoshen() {
13 	a.Mosha++
14 	fmt.Println(a.Emri, "i ka", a.Mosha, "vjet")
15 }
16 
17 func main() {
18 	a := Anetari{Emri: "Petriti", Mosha: 23}
19 	fmt.Println(a.Emri, "i ka", a.Mosha, "vjet")
20 	a.TregoMoshen()
21 	fmt.Println(a.Emri, "i ka", a.Mosha, "vjet")
22 }

https://play.golang.org/p/7w5u7-1WluI

Rezultati:

1 Petriti i ka 23 vjet
2 Petriti i ka 24 vjet
3 Petriti i ka 23 vjet

Nëse pranuesi i metodës është pointer, vlera që ndryshohet do të jetë e dukshme edhe jashtë funksionit.

 1 package main
 2 
 3 import (
 4 	"fmt"
 5 )
 6 
 7 type Anetari struct {
 8 	Emri  string
 9 	Mosha int
10 }
11 
12 func (a *Anetari) TregoMoshen() {
13 	a.Mosha++
14 	fmt.Println(a.Emri, "i ka", a.Mosha, "vjet")
15 }
16 
17 func main() {
18 	a := Anetari{Emri: "Petriti", Mosha: 23}
19 	fmt.Println(a.Emri, "i ka", a.Mosha, "vjet")
20 	a.TregoMoshen()
21 	fmt.Println(a.Emri, "i ka", a.Mosha, "vjet")
22 }

https://play.golang.org/p/90AZHL23Yrz

Rezultati:

1 Petriti i ka 23 vjet
2 Petriti i ka 24 vjet
3 Petriti i ka 24 vjet

Built-in functions

Një numër i vogël i funksioneve është i paradefinuar, për të cilët nuk ka nevojë të importohet asnjë pako.

close

Përdoret në komunikim të kanaleve, ku shërben për mbylljen e kanalit.

delete

Përdoret për fshirjen e të dhënave në maps.

len dhe cap

Funksioni len tregon gjatësinë e një stringu si dhe numrin e anëtarëve të segmenteve (slices) dhe vargjeve (arrays).

new

Përdoret për rezervimin e memorjes për tipet e të dhënave e definuara nga përdoruesi (user defined data types).

make

Përdoret për rezervimin e memorjes për tipet e integruara ( built-in types), siç janë: hartat (maps), segmentet (slices) dhe kanalet (channels).

copy

Përdoret për kopjimin e segmenteve.

append

Përdoret për bashkangjitjen e segmenteve.

panic dhe recover

Përdoret në mekanizmin e raportimit të gabimeve.

print dhe println

Funksione të nivelit të ultë që mund të përdoren pa përdorimin e pakos fmt. Kryesisht përdoren për debugging.

complex, real and imag

Përdoren për punë me numrat kompleksë.

Funksionet anonime

Shembull #1:

1 package main
2 
3 import "fmt"
4 
5 func main() {
6 	func(emri string) {
7 		fmt.Println("Përshëndetje, " + emri)
8 	}("Arben")
9 }

https://play.golang.org/p/6V7fnluaye_s

Rezultati:

1 Përshëndetje, Arben

Shembull #2:

 1 package main
 2 
 3 import "fmt"
 4 
 5 func main() {
 6 	a := func(emri string) string {
 7 		return "Përshëndetje, " + emri
 8 	}
 9 	
10 	fmt.Println(a("Arben"))
11 }

https://play.golang.org/p/SHzy03Bokqs

Rezultati:

1 Përshëndetje, Arben

Defer

Defer

Urdhëri defer mundëson që një funksion 2 i thirrur nga një funksion 1 të ekzekutohet menjëherë para udhërit return të funksionit 1. Pra, thirrjen e funksionit 2 mund ta vendosim kudo brenda bllokut të funksionit 1, por ai do të ekzekutohet fare ne fund të funksionit.

 1 package main
 2 
 3 import (
 4 	"fmt"
 5 )
 6 
 7 func main() {
 8 	defer funksioni1()
 9 	funksioni2()
10 }
11 
12 func funksioni1() {
13 	fmt.Println("Funksioni 1")
14 }
15 
16 func funksioni2() {
17 	fmt.Println("Funksioni 2")
18 }

https://play.golang.org/p/URvBJ0CPxw3

Rezultati:

1 Funksioni 2
2 Funksioni 1

Kjo është e dobishme në situatat kur dëshirojmë të sigurohemi që një funksion i dytë do të ekzekutohet në fund të një funksioni, siç është rasti i mbylljes së koneksionit me databazë.

Radha e ekzekutimit të defer të shumëfishtë

Nëse kemi më tepër se një thirrje të funksioneve me defer, do të zbatohet ekzekutim revers, gjegjësisht renditja LIFO (last in, first out), d.m.th. ai funksion që bëhet defer në fund, ekzekutohet i pari.

 1 package main
 2 
 3 import (
 4 	"fmt"
 5 )
 6 
 7 func main() {
 8 	defer funksioni1()
 9 	funksioni2()
10 	defer funksioni3()
11 }
12 
13 func funksioni1() {
14 	fmt.Println("Funksioni 1")
15 }
16 
17 func funksioni2() {
18 	fmt.Println("Funksioni 2")
19 }
20 
21 func funksioni3() {
22 	fmt.Println("Funksioni 3")
23 }

https://play.golang.org/p/eaBVdkBaoD-

Rezultati:

1 Funksioni 2
2 Funksioni 3
3 Funksioni 1

Urdhërat/funksionet me defer ekzekutohen edhe kur ka panic, d.m.th. edhe nëse gjatë ekzekutimit të programit është lajmëruar gabim i nivelit panic, gjë që mund të jetë e dobishme në situata të caktuara ku nevojitet të ndërmirret diçka para se të shfaqet raporti i gabimit.

Error, panic & recover

Error

Në Go, gabimet dërgohen si vlerë e veçantë kthyese e një funksioni. Pra, në Go nuk kemi të bëjmë me Exceptions me struktura try/catch sikurse në Java, PHP, etj.

Programet në Go i përdorin vlerat e tipit error për të dhënë indikacion mbi një problem, gabim, gjendje abnormale.

Kur brenda një funksioni ka ardhur deri te një gabim, formohet raporti tekstual i tipit Error dhe i njëjti kthehet si një prej vlerave kthyese të funksionit, që më pas atë vlerë ta lexojmë nga variabla korrresponduese brenda funksionit thirrës, dhe nëse vlera nuk është nil (që e verifikojmë me strukturën if), vendosim se çfarë të ndodh më tej me rrjedhën e ekzekutimit të programit.

Pakoja errors implementon funksione për manipulimin me raportet e gabimeve.

Funksioni New krijon gabimet përmbajtj e të cilave është mesazh tekstual.

 1 package main
 2 
 3 import (
 4 	"errors"
 5 	"fmt"
 6 )
 7 
 8 func pjesto(a, b float32) (float32, error) {
 9 	if b == 0 {
10 		return 0.0, errors.New("Nuk mund të pjestoj me zero")
11 	}
12 	return a / b, nil
13 }
14 
15 func main() {
16 
17 	c, e := pjesto(9, 2)
18 	if e != nil {
19 		fmt.Println(e)
20 	} else {
21 		fmt.Println(c)
22 	}
23 }

https://play.golang.org/p/gb2YT3xhYi3

Rezultati:

1 4.5

Nëse funksionit i japim këto argumente: ` c, e := pjesto(9, 2)`, atëherë rezultati do të jetë:

1 Nuk mund  pjestoj me zero

Ndonëse jemi të lirë ta vendosim vlerën e gabimit kudo në vlerat kthyese të funksionit, me konvencion rekomandohet që të jetë vlera e fundit.

Kur raportit duam t’ia bashkangjisim edhe informata shtesë, në vend të string do të përdorim një struct. Kësisoj, raporti mund të ketë detaje sqaruese mbi parametra të ndryshëm që kanë sjellur deri te gabimi.

 1 package main
 2 
 3 import (
 4 	"fmt"
 5 )
 6 
 7 type raportGabimi struct {
 8 	arg1   float32
 9 	arg2   float32
10 	gabimi string
11 }
12 
13 func (r *raportGabimi) Error() string {
14 	return fmt.Sprintf("%v %v - %s", r.arg1, r.arg2, r.gabimi)
15 }
16 
17 func pjesto(a, b float32) (float32, error) {
18 	if b == 0 {
19 		return 0.00, &raportGabimi{a, b, "Nuk mund të pjestoj me zero"}
20 	}
21 	return a / b, nil
22 }
23 func main() {
24 	rezultati, e := pjesto(9, 2)
25 
26 	if rg, ok := e.(*raportGabimi); ok {
27 		fmt.Println("Argumenti 1: ", rg.arg1)
28 		fmt.Println("Argumenti 2: ", rg.arg2)
29 		fmt.Println("Gabimi: ", rg.gabimi)
30 		fmt.Println("Vlera: ", rezultati)
31 	} else {
32 		fmt.Println(rezultati)
33 	}
34 }

https://play.golang.org/p/hCu-E48E4bj

Rezultati:

1 4.5

Nëse funksionit i japim këto argumente: ` c, e := pjesto(9, 2)`, atëherë rezultati do të jetë:

1 Argumenti 1:  9
2 Argumenti 2:  0
3 Gabimi:  Nuk mund  pjestoj me zero
4 Vlera:  0

Raportin në formë stringu mund ta fitojmë me:

1 fmt.Println("Gabimi: ", rg.Error())

Te funksioni pjesto(), parametri i dytë duhet të jetë i tipit error. Kur si vlerë kthyese duhet të kthehet se nuk ka ndodhur gabim, si vlerë të dytë kthyese e kthejmë vlerën nil.

1 return a / b, nil

Çdo funksion që kthen si vlerë kthyese e ka të deklaruar tipin error, duhet të kthejë vlera të tipit error. Për shembull, funksioni Open nga pakoja os, si vlerë të dytë kthyese e ka variablin err të tipit error.

1 func Open(name string) (file *File, err error)

Prandaj, për të verifikuar nëse ka pasur gabim gjatë hapjes së fajllit me funksionin os.Open(), rezultatin e funksionit e vendosim në dy variabla, ku variabli i parë do ta përmbajë përmbajtjen e fajllit, ndërsa i dyti do ta përmbajë raportin e gabimit nëse ka pasur gabim. Nëse nuk ka pasur, vlera e variablit të dytë do të jetë nil.

 1 package main
 2 
 3 import (
 4 	"fmt"
 5 	"os"
 6 )
 7 
 8 func main() {
 9 	f, err := os.Open("file.txt")
10 	if err != nil {
11 		fmt.Println(err)
12 	} else {
13 		fmt.Println(f)
14 	}
15 }

https://play.golang.org/p/790oGr4BJuO

Rezultati:

Nëse nuk gjendet fajlli i specifikuar:

1 open file.txt: No such file or directory

Nëse gjendet, atëherë do të shfaqet përmbajtja e fajllit.

Tipi error i përket tipit intefejs. Një variabël i gabimit mund të përmbajë çfarëdo vlere që mund të prezantohet si string.

1 type error interface {
2     Error() string
3 }

Tipi error është tip i paradeklaruar dhe është i definuar në universe block, d.m.th. në nivelin e kodit të tërësishëm dhe jo vetëm brenda një pakoje apo funksioni.

Implementimi më i shpeshtë i pakos errors haset në përdorimin e tipit errorString.

 1 type error interface {
 2     Error() string
 3 }
 4 
 5 type errorString struct {
 6     s string
 7 }
 8 
 9 func (e *errorString) Error() string {
10     return e.s
11 }

Konstruktimi i vlerave të tipit error bëhet me funksionin errors.New, të cilit ia bartim vlerën e tipit string, ndërsa ky funksion kthen vlerë të tipit error.

1 func New(text string) error {
2     return &errorString{text}
3 }

Shembull si mund të implementohet:

1 func Sqrt(f float64) (float64, error) {
2     if f < 0 {
3         return 0, errors.New("Rrënjë katrore e numrit negativ!")
4     }
5     // kodi që llogarit rrënjën katrore
6 }

Kështu, kur e thërrasim funksionin Sqrt(), i përdorim dy variabla, një për rezultatin, tjetrin për gabimin:

1 f, err := Sqrt(-1)
2 if err != nil {
3     fmt.Println(err)
4 }

Në këtë rast, variabli err, nëse kemi dhënë si argument funksionit Sqrt() një vlerë negative, do të jetë: Rrënjë katrore e numrit negativ!.

Pakoja fmt e formaton vlerën e tipit error duke e thirrur metodën Error() që kthen tipin string.

Është detyrë e implementimit se si do të formulohet raporti i gabimit; raporti mund të jetë vetëm tekst, por edhe të përmbajë vlera të tjera për sqarim më të mirë të kontekstit të ndodhjes së gabimit.

Për shembull, tek rasti me rrënjën katrore, raporti mund të formulohet asisoj që ta tregojë edhe vlerën e argumentit për shkak të të cilit nuk mund të kryhet llogaritja:

1 if f < 0 {
2     return 0, fmt.Errorf("Rrënjë katrore e numrit negativ!", f)
3 }

fmt.Errorf() merr si argumente tipa të ndryshëm, bën bashkangjitjen e tyre, ndërsa si rezultat kthen vlerë të tipit error.

Interfejsi error kërkon të implementohet vetëm metoda Error(). Megjithatë, nëpër implementimi të ndryshme të tipit error mund të kërkohen edhe metoda shtesë. Për shembull, pakoja net kthen gabim të tipit error, por në disa implementime të caktuara mund të kërkohen metoda shtesë të definuara në interfejsin net.Error.

1 package net
2 
3 type Error interface {
4     error
5     Timeout() bool   // Is the error a timeout?
6     Temporary() bool // Is the error temporary?
7 }

Në bazë të vlerës së Timeout() do ta dijmë se ky gabim a ka të bëjë me kalimin e kohës së caktuar për arritjen e përgjigjes, ndërsa me Temporary() kuptojmë nëse gabimi ka karakter të përkohshëm.

Panic

Janë disa operacione që mund të shkaktojnë panic, siç janë:

  • Pjestimi i një integeri me 0
  • Qasje në një anëtar të vargut me indeks inekzistent
  • Dërgimi në një kanal të mbyllur
  • Dereferencimi i një nil pointeri
  • Përdorimi i thirrjes rekurzive të një funksioni që e mbush stack-un

Panic duhet të përdoret për gabimet që ndodhin papritmas dhe që normalisht nuk mund të kenë rikuperim. Rikuperimi nga një panic duhet të jetë vetëm përpjekje për të ndërmarrë diçka në lidhje me atë gabim para se të dilet nga aplikacioni. Nëse shfaqet në problem i papritur, kjo ndodh nëse gabimi nuk është menaxhuar si duhet apo mungojnë disa verifikime.

Përdorimi tipit i panic është për ndërprerjen e programit kur një funksion kthen një vlerë të tipit error të cilin nuk dijmë si ta menaxhojmë, apo nëse vazhdimi i ekzekutimit të programit nuk ka kuptim në rast se është shfaqur ai gabim.

 1 package main
 2 
 3 import (
 4 	"fmt"
 5 	"os"
 6 )
 7 
 8 func main() {
 9 	_, err := os.Create("fajlli.txt")
10 	if err != nil {
11 		panic(err)
12 	} else {
13 		fmt.Println("U krijua fajlli.txt")
14 	}
15 }

https://play.golang.org/p/U3xn0TFPLvA

Rezultati:

Në rast suksesi:

1 U krijua fajlli.txt

Në rast dështimi:

1 panic: open fajlli.txt: No such file or directory
2 goroutine 1 [running]:
3 main.main()
4 	/tmp/sandbox490522843/prog.go:11 +0xe0

Funksioni panic() është funksion i Go që do ta ndërpresë rrjedhën normale të ekzekutimit të programit dhe do të lajmërojë gabim. Kur një funksion F() e thirr funksionin panic(), çdo funksion i shtyrë (deferred functions) brenda funksionit F() do të ekzekutohet normalisht, pastaj funksioni F() i kthen rezultat thirrësit.

Ndaj thirrësit, funksioni F() sillet si thirrje për panic. Procesi vazhdon derisa të thirren të gjitha gorutinat dhe në fund programi e ndërpret ekzekutimin. Paniku mund të inicohen dhe e thirrur drejtpërsëdrejti funksionin panic(). Por, një panic mund të shkaktohet edhe për shkak të gabimeve gjatë ekzekutimit (runtime errors), siç është për shembull rasti kur e kërkojmë një anëtar të vargut me indeks inekzistent.

Recover

Funksioni recover() është funksion që rimerr kontrollin pas një gorutine që ka shkaktuar panik.
Ky funksion është i dobishëm vetëm brenda funksioneve të shtyra (deferred functions). Gjatë ekzekutimit normal, një thirrje për rikuperim (recover) do të kthejë vlerën nil dhe nuk do të ketë efekt tjetër. Nëse gorutina aktuale është duke shkaktuar panik, një thirrje e recover() do ta kapë vlerën e dhënë funksionit panic(), ndërsa ekzekutimi i programit do të vazhdojë normalisht.

Shembull nga https://blog.golang.org/defer-panic-and-recover

 1 package main
 2 
 3 import "fmt"
 4 
 5 func main() {
 6 	f()
 7 	fmt.Println("Returned normally from f.")
 8 }
 9 
10 func f() {
11 	defer func() {
12 		if r := recover(); r != nil {
13 			fmt.Println("Recovered in f", r)
14 		}
15 	}()
16 	fmt.Println("Calling g.")
17 	g(0)
18 	fmt.Println("Returned normally from g.")
19 }
20 
21 func g(i int) {
22 	if i > 3 {
23 		fmt.Println("Panicking!")
24 		panic(fmt.Sprintf("%v", i))
25 	}
26 	defer fmt.Println("Defer in g", i)
27 	fmt.Println("Printing in g", i)
28 	g(i + 1)
29 }

https://play.golang.org/p/Ujf1dRatTMb

Rezultati:

 1 Calling g.
 2 Printing in g 0
 3 Printing in g 1
 4 Printing in g 2
 5 Printing in g 3
 6 Panicking!
 7 Defer in g 3
 8 Defer in g 2
 9 Defer in g 1
10 Defer in g 0
11 Recovered in f 4
12 Returned normally from f.

Tash do ta largojmë funksionin anonim të shtyrë që bënte recover(), dhe kur vlera e i bëhet 4, do të thirret panic(), nga i cili nuk ka rikuperim, prandaj të gjitha funksionet e shtyra do të ekzekutohen në renditje të mbrapshtë, ndërsa funksionit main() thirrja e funksionit f() i cili nga funksioni g() ka pranuar panic do të kthehet po ashtu panic me çka main() do ta ndërpresë ekzekutimin prandaj edhe rreshti fmt.Println("Returned normally from f.") nuk do të ekzekutohet fare.

 1 package main
 2 
 3 import "fmt"
 4 
 5 func main() {
 6 	f()
 7 	fmt.Println("Returned normally from f.")
 8 }
 9 
10 func f() {
11 	fmt.Println("Calling g.")
12 	g(0)
13 	fmt.Println("Returned normally from g.")
14 }
15 
16 func g(i int) {
17 	if i > 3 {
18 		fmt.Println("Panicking!")
19 		panic(fmt.Sprintf("%v", i))
20 	}
21 	defer fmt.Println("Defer in g", i)
22 	fmt.Println("Printing in g", i)
23 	g(i + 1)
24 }

https://play.golang.org/p/hLQoozxSYOC

Rezultati:

 1 Calling g.
 2 Printing in g 0
 3 Printing in g 1
 4 Printing in g 2
 5 Printing in g 3
 6 Panicking!
 7 Defer in g 3
 8 Defer in g 2
 9 Defer in g 1
10 Defer in g 0
11 panic: 4
12 
13 goroutine 1 [running]:
14 main.g(0x4, 0x40c110)
15 	/tmp/sandbox693479664/prog.go:19 +0x340
16 main.g(0x3, 0x40c110)
17 	/tmp/sandbox693479664/prog.go:23 +0x180
18 main.g(0x2, 0x40c110)
19 	/tmp/sandbox693479664/prog.go:23 +0x180
20 main.g(0x1, 0x40c110)
21 	/tmp/sandbox693479664/prog.go:23 +0x180
22 main.g(0x0, 0x40c110)
23 	/tmp/sandbox693479664/prog.go:23 +0x180
24 main.f()
25 	/tmp/sandbox693479664/prog.go:12 +0xa0
26 main.main()
27 	/tmp/sandbox693479664/prog.go:6 +0x20

Gorutinat, kanalet, sinkronizimi

Konkurrenca

Ndër karakteristikat më të rëndësishme të Go është se ka përkrahje pët konkurrencë (concurrency). Me konkurrencë nënkuptojmë ekzekutimin e njëkohshëm të disa funksioneve, që në Go quhen gorutina (goroutines). Konkurrenca nuk nënkupton paralelizëm, por procese të ndara ku secili proces ka një detyrë të caktuar dhe mund të komunikojnë ndërmjet vete nëpërmes kanaleve.

Nëse procesori posedon vetëm një bërthamë, atëherë programi do të ekzekutohet në mënyrë sekuencionale, ku një gorutinë ekzekutohet pas gorutinës tjetër.

Suporti për konkurrencë në Go nëse rastet kur procesori ka më shumë bërthama (cores) mundëson shfrytëzimin e të gjitha bërthamave të procesorit për ekzekutimin sa më efiçent të një numri të madh të gorutinave.

Gorutinat

Gorutinat janë funksione apo metoda që ekzekutohen në mënyrë konkurrent me funksionet apo metodat tjera. Gorutinat mund të imagjinohen si threads me peshë më të vogël ( light weight threads). Kostoja e krijimit të një gorutine (në kuptim të resurseve procesorike) është më e ultë se e një thread. Prandaj, tipike për aplikacionet në Go është ekzekutimi konkurrent i mijëra gorutinave.

Gorutina, pra, është një detyrë e cila ekzekutohet në mënyrë të pavarur. Go mundëson edhe koordinimin ndërmjet gorutinave të ndryshme.

Gorutinat kundrejt threads

Gorutinat janë shumë më pak të kushtueshme në krahasim me threads. Ato zënë vetëm disa kilobajtë në stack dhe stack-u mund të rritet e zvogëlohet në varësi prej nevojave të aplikacionit, gjersa kur kemi të bëjmë me threads, madhësia e stack-ut duhet të specifikohet dhe të jetë fikse.

Gorutinat shfrytëzojnë threads, ku një thread-i i korrespondojnë shumë gorutina, në raste edhe me mijëra. Nëse për shembull një thread është i bllokuar në pritje të inputit nga ana e përdoruesit, atëherë ajo gorutinë bartet në një thread tjetër i cili krijohet aty për aty, prej nga vazhdon ekzekutimi i gorutinës.

Për gjithë procesin e menaxhimit të gorutinave dhe threads përkujdeset runtime, prandaj në kodin e aplikacionit nuk kemi nevojë të implementojmë kurrfarë logjike në lidhje me gorutinat.

Gorutinat komunikojnë duke përdorur kanalet. Kanalet mundësojnë parandalimin e ndodhjes së race conditions në rastet kur bëhet qasje në memorjen e ndarë (shared memory) nga gorutinat.

Kur startohet një gorutinë, thirrja e atij funksioni kthen (return) menjëherë. Për dallim prej funksioneve, kontrollli nuk do të presë që gorutina ta përfundojë ekzekutimin. Kontrolli kalon menjëherë në rreshtin vijues të kodit pas thirrjes së gorutinës dhe çfarëdo vlere kthyese nga gorutina do të injorohet.

Edhe funksioni main()\është gorutinë, dhe ai duhet të jetë duke u ekzekutuar për t’i mundësuar ekzekutimin gorutinave të tjera. Nëse main ndërpret ekzekutimin, atëherë programi do ta ndërpresë ekzekutimin dhe asnjë gorutinë tjetër nuk do të ekzekutohet.

Ta marrim shembullin e thirrjes së një funksioni nga një cikël, pra brenda një iteracioni:

 1 package main
 2 
 3 import "fmt"
 4 
 5 func main() {
 6 	for x := 1; x <= 10; x++ {
 7 		printo(x)
 8 	}
 9 }
10 
11 func printo(i int) {
12 	fmt.Println(i)
13 }

https://play.golang.org/p/EIAeMTefQIc

Rezultati:

 1 1
 2 2
 3 3
 4 4
 5 5
 6 6
 7 7
 8 8
 9 9
10 10

Në këtë program, 10 herë përsëritet thirrja e funksionit printo(), i cili e shfaq në ekran vlerën e inkrementit. Të gjitha thirrjet e funksionit bëhen në mënyrë sekuencionale, d.m.th. pa e kryer ekzekutimin i funksionit brenda një cikli nuk bëhet thirrje e sërishme e atij funksioni.

Tash ta bëjmë të njëjtën por duke përdorur gorutinat:

 1 package main
 2 
 3 import (
 4 	"fmt"
 5 	"time"
 6 )
 7 
 8 func main() {
 9 	for x := 1; x <= 10; x++ {
10 		go printo(x)
11 	}
12 
13 	time.Sleep(100 * time.Millisecond)
14 }
15 
16 func printo(i int) {
17 	fmt.Println(i)
18 }

https://play.golang.org/p/VNAXEkGGg3N

Rezultati:

 1 1
 2 2
 3 5
 4 8
 5 3
 6 10
 7 7
 8 9
 9 4
10 6

Për ta ekzekutuar një funksion në formë gorutine, duhet që para thirrjes së funksionit të shënohet go, pra: go printo(x). Me këtë do të krijohen 10 gorutina, ku secila ekzekutohet si thread në vete, ku ndonjë gorutinë ekzekutohet e pavarur nga një gorutinë tjetër, e ndonjë gorutinë ekzekutohet në formë sekuencionale, krejt në varësi prej numrit të bërthamave të procesorit. I tërë ky proces menaxhohet nga runtime i Go.

Vërejmë se renditja e rezultateve nuk është në sekuencë të njëjtë rritëse nga 1 deri 10, siç ishte rasti me shembullin e mëparshëm. Kjo ndodh për shkak se secila gorutinë është thread në vete, ku ndonjë gorutinë ekzekutohet më heret, ndonjë më vonë, dhe rezultatet shfaqen pikërisht sipas renditjes së kryerjes së ekzekutimit të gorutinave.

Pauza në fund: time.Sleep(100 * time.Millisecond) është e domosdoshme, në mënyrë që funksioni main() të arrijë t’i pranojë rezultatet e gorutinave. Nëse e fshijmë atë rresht dhe e startojmë programin, do të shohim se nuk do të shfaqet asnjë rezultat sepse funksioni main() do të jetë i ekzekutuar ende pa arritur të kthehen rezultatet nga gorutinat.

Në secilin ekzekutim vijues të programit, renditja e rezultateve mund të ndryshojë. Këtë shembull duhet ta kompajlojmë dhe ekzekutojmë lokalisht, sepse në Go Playground mund të ekzekutohet vetëm një thread prandaj gjithmonë do të gjenerohet sekuenca e njëjtë prej 1 deri 10, sepse të gjitha gorutinat ekzekutohen në mënyrë sekuencionale njëra pas tjetrës.

Pauzën në fund të funksionit main() mund ta realizojmë edhe në mënyra të tjera, për shembull duke e përdorur funksionin fmt.Scanln() që do të presë derisa përdoruesi ta shtyp tastin Enter.

Goroutinat mund t’i thërrasim edhe nga një closure.

Fillimisht, marrim shembullin e një closure që thirret nga një for iteracion, i cili e thirr një funksion por jo si gorutinë.

 1 package main
 2 
 3 import (
 4 	"fmt"
 5 )
 6 
 7 func main() {
 8 	numrat := []int{10, 25, 40}
 9 	for _, i := range numrat {
10 		func() {
11 			numero(i)
12 		}()
13 	}
14 }
15 
16 func numero(from int) {
17 	for i := from; i <= from+10; i += 5 {
18 		fmt.Println(i)
19 	}
20 }

https://play.golang.org/p/H1lp-LSCVHW

Rezultati:

1 10
2 15
3 20
4 25
5 30
6 35
7 40
8 45
9 50

Shohim se është formuar një seri sekuencionale e numrave nga 10 deri në 50, me hap 5.

Tash e provojmë të njëjtën, tash duke e thirrur closure me go.

 1 package main
 2 
 3 import (
 4 	"fmt"
 5 )
 6 
 7 func main() {
 8 	numrat := []int{10, 25, 40}
 9 	for _, i := range numrat {
10 		func() {
11 			go numero(i)
12 		}()
13 	}
14 	fmt.Scanln()
15 }
16 
17 func numero(from int) {
18 	for i := from; i <= from + 10; i += 5 {
19 		fmt.Println(i)
20 	}
21 }

https://play.golang.org/p/q2kmPapJeqI

Rezultati:

1 10
2 15
3 20
4 40
5 25
6 45
7 50
8 30
9 35

Tash vërejmë se vlerat nuk janë sekuencionale dhe kjo ndodh për shkak të vetë natyrës së gorutinave ku secila gorutinë është proces në vete dhe nuk e kemi të garantuar me çfarë radhitje do të kthehen rezultatet e secilës gorutinë veç e veç.

Në situata të këtilla, kur një closure thirret nga një cikël, mund të ndodhë që në momentin kur closure e lexo vlerën e iteratorit, ajo vlerë mos të jetë vlera e radhës, kështu që kurrë me saktësi nuk mund ta dijmë se cilën vlerë të iteratorit do ta marrë closure. Pra, mund të ndodhë që për ndonjë vlerë mos të startohet një gorutinë.

Këtë problem e zgjidhim duke e bartur vlerën e iteratorit në closure duke e dhënë si parametër:

 1 package main
 2 
 3 import (
 4 	"fmt"
 5 )
 6 
 7 func main() {
 8 	numrat := []int{10, 25, 40}
 9 	for _, i := range numrat {
10 		func(x int) {
11 			go numero(x)
12 		}(i)
13 	}
14 	fmt.Scanln()
15 }
16 
17 func numero(from int) {
18 	for i := from; i <= from+10; i += 5 {
19 		fmt.Println(i)
20 	}
21 }

https://play.golang.org/p/ygHdYAPS8CH

Rezultati:

1 25
2 30
3 35
4 40
5 45
6 50
7 10
8 15
9 20

Shembull nga https://golangbot.com/goroutines/

 1 package main
 2 
 3 import (
 4 	"fmt"
 5 	"time"
 6 )
 7 
 8 func numbers() {
 9 	for i := 1; i <= 5; i++ {
10 		time.Sleep(250 * time.Millisecond)
11 		fmt.Printf("%d ", i)
12 	}
13 }
14 func alphabets() {
15 	for i := 'a'; i <= 'e'; i++ {
16 		time.Sleep(400 * time.Millisecond)
17 		fmt.Printf("%c ", i)
18 	}
19 }
20 func main() {
21 	go numbers()
22 	go alphabets()
23 	time.Sleep(3000 * time.Millisecond)
24 	fmt.Println("main terminated")
25 }

Rezultati:

1 1 a 2 3 b 4 c 5 d e main terminated

Sinkronizimi

Go posedon me një pako të quajtur sync që mundëson thjeshtimin e procesit të sinkronizimit të goritunave.

Në situata të thjeshta, gorutinat mund të sinkronizohen nëpërmes kanaleve.

Pritja e një gorutine të vetme

Channels

Pritja e një gorutine të vetme mund të implementohet nëpërmes përdorimit të një kanali. Kur e përfundon ekzekutimin, gorutina dërgon mesazh gorutinës kryesore e cila është në pritje.

Shembull me funksion anonim:

 1 package main
 2 
 3 import "fmt"
 4 
 5 func main() {
 6 	msg := make(chan string)
 7 	fmt.Println("main(): Thirret gorutina")
 8 	go func() {
 9 		fmt.Println("Gorutina u thirr")
10 		msg <- "Gorutina perfundoi"
11 	}()
12 
13 	m := <-msg
14 	fmt.Println(m)
15 }

https://play.golang.org/p/BaDRWB7Lqcv

Rezultati:

1 main(): Thirret gorutina
2 Gorutina u thirr
3 Gorutina perfundoi

Shembull me funksion:

 1 package main
 2 
 3 import (
 4 	"fmt"
 5 	"time"
 6 )
 7 
 8 func worker(fund chan bool) {
 9 	fmt.Println("Gorutina filloi")
10 	time.Sleep(time.Second)
11 	fmt.Println("Gorutina mbaroi")
12 	fund <- true
13 }
14 
15 func main() {
16 	fund := make(chan bool)
17 
18 	fmt.Println("main(): gorutina do të startohet")
19 	go worker(fund)
20 
21 	fmt.Println("main(): duke pritur gorutinën të përfundojë")
22 	<-fund
23 	fmt.Println("main(): U krye")
24 }

https://play.golang.org/p/Z6Dlva11tLm

Rezultati:

1 main(): gorutina do  startohet
2 main(): duke pritur gorutinën  përfundojë
3 Gorutina filloi
4 Gorutina mbaroi
5 main(): U krye

Channels buffering

Në mënyrën standarde, kanalet janë unbuffered, që do të thotë se një kanal mund të pranojë dërgesë (chan <-) vetëm nëse ekziston pranuesi (<- chan) për ta pranuar vlerën e dërguar.

Kanalet që janë buffered mund të pranojnë një numër të specifikuar të vlerave pa pranues korrespondues për këto vlera. Në shembullin vijues krijohet buffer me 2 vlera.

 1 package main
 2 
 3 import "fmt"
 4 
 5 func main() {
 6 	m := make(chan string, 2)
 7 
 8 	m <- "mesazhi 1"
 9 	m <- "mesazhi 2"
10 
11 	fmt.Println(<-m)
12 	fmt.Println(<-m)
13 }

https://play.golang.org/p/7xjLQxVGQsz

Rezultati:

1 mesazhi 1
2 mesazhi 2

Drejtimi i kanalit

Kur përdoren kanalet si parametra të funksionit, mund të specifikojmë nëse kanali duhet vetëm të dergojë apo vetëm të pranojë vlera.

Nëse një kanal e deklarojmë vetëm si pranues, kompajleri do të raportojë gabim gjatë kompajlimit nëse brenda kodit e përdorim si dërgues.

 1 package main
 2 
 3 import "fmt"
 4 
 5 func main() {
 6 	dergimi := make(chan string, 1)
 7 	pranimi := make(chan string, 1)
 8 	dergo(dergimi, "Mesazhi u përcoll")
 9 	prano(dergimi, pranimi)
10 	fmt.Println(<-pranimi)
11 }
12 
13 func dergo(dergimi chan<- string, mesazhi string) {
14 	dergimi <- mesazhi
15 }
16 
17 func prano(dergimi <-chan string, pranimi chan<- string) {
18 	mesazhi := <-dergimi
19 	pranimi <- mesazhi
20 }

https://play.golang.org/p/rOvR1KQeIa6

Rezultati:

1 Mesazhi u përcoll

Select

Go’s select lets you wait on multiple channel operations. Combining goroutines and channels with select is a powerful feature of Go.

Select mundëson pritjen e operacioneve të shumëfishta të kanalit. Kombinimi i gorutinave dhe kanaleve me select është karakteristikë e fuqishme e Go.

 1 package main
 2 
 3 import (
 4 	"fmt"
 5 	"time"
 6 )
 7 
 8 func main() {
 9 	k1 := make(chan string)
10 	k2 := make(chan string)
11 
12 	go gorutina1(k1)
13 	go gorutina2(k2)
14 
15 	for i := 0; i < 2; i++ {
16 		select {
17 		case mesazhi1 := <-k1:
18 			fmt.Println("U pranua: ", mesazhi1)
19 		case mesazhi2 := <-k2:
20 			fmt.Println("U pranua: ", mesazhi2)
21 		}
22 	}
23 }
24 
25 func gorutina1(k1 chan<- string) {
26 	time.Sleep(1 * time.Second)
27 	k1 <- "i pari"
28 }
29 func gorutina2(k2 chan<- string) {
30 	time.Sleep(2 * time.Second)
31 	k2 <- "i dyti"
32 }

https://play.golang.org/p/agbUdEqqzZk

Rezultati:

1 U pranua:  i pari
2 U pranua:  i dyti

Timeouts

Timeouts janë të rëndësishme për programet që konektohen në resurset eksterne, ku nuk e kanë të garantuar se resursi ekstern do të jetë i qasshëm brenda një intervali të paracaktuar.

 1 package main
 2 
 3 import (
 4 	"fmt"
 5 	"time"
 6 )
 7 
 8 func main() {
 9 	k1 := make(chan string, 1)
10 	go func() {
11 		time.Sleep(2 * time.Second)
12 		k1 <- "Unë jam gorutina 1"
13 	}()
14 
15 	select {
16 	case rezultati := <-k1:
17 		fmt.Println(rezultati)
18 	case <-time.After(1 * time.Second):
19 		fmt.Println("Koha kaloi për gorutinën 1")
20 	}
21 
22 	k2 := make(chan string, 1)
23 	go func() {
24 		time.Sleep(2 * time.Second)
25 		k2 <- "Unë jam gorutina 2"
26 	}()
27 
28 	select {
29 	case res := <-k2:
30 		fmt.Println(res)
31 	case <-time.After(3 * time.Second):
32 		fmt.Println("Koha kaloi për gorutinën 2")
33 	}
34 }

https://play.golang.org/p/7y-rXJYSnZk

Rezultati:

1 Koha kaloi për gorutinën 1
2 Unë jam gorutina 2

Non-Blocking Channel Operations

Mbyllja e kanaleve

Range over Channels

Timers

Tickers

Worker Pools

WaitGroups

Rate Limiting

Atomic Counters

Mutexes

Stateful Goroutines

Sinkronizimi i kanaleve

Në shembullin vijues do të demonstrohet sinkrinozimi i gorutinës nëpërmes kanalit. Në këtë rast do të përdoret kanali me emrin perfundoi nëpërmes të cilit gorutina do t’ia përcjellë funksionit main() informatën se një proces ka përfunduar. Me rreshtin perfundoi <- true shkaktohet pritje në funksionin main() derisa të pranohet mesazhi nga gorutina.

 1 package main
 2 
 3 import (
 4 	"fmt"
 5 	"time"
 6 )
 7 
 8 func main() {
 9 
10 	perfundoi := make(chan bool, 1)
11 	go funksioni(perfundoi)
12 
13 	<-perfundoi
14 }
15 
16 func funksioni(perfundoi chan bool) {
17 	fmt.Print("Duke punuar...")
18 	time.Sleep(time.Second)
19 	fmt.Println("U krye")
20 
21 	perfundoi <- true
22 }

https://play.golang.org/p/MKSUA5NVmbq

Rezultati:

1 Duke punuar...U krye

Pritja e më shumë gorutinave

sync

Nëse nevojitet të presim përfundimin e gorutinave të shumëfishta, do ta përdorim pakon sync.WaitGroup. Gorutina main e thërret metodën Add për të specifikuar numrin e gorutinave që duhen pritur. Më pastaj, çdo gorutinë ekzekutohet dhe thërret metodën Done kur ta përfundojnë ekzekutimin. Wait mund të përdoret për bllokim gjersa të përfundojnë të gjitha gorutinat.

 1 package main
 2 
 3 import (
 4 	"fmt"
 5 	"sync"
 6 	"time"
 7 )
 8 
 9 func worker(wg *sync.WaitGroup, id int) {
10 	defer wg.Done()
11 
12 	fmt.Printf("Gorutina %v: filloi\n", id)
13 	time.Sleep(time.Second)
14 	fmt.Printf("Gorutina %v: përfundoi\n", id)
15 }
16 
17 func main() {
18 	var wg sync.WaitGroup
19 
20 	for i := 1; i <= 3; i++ {
21 		fmt.Println("main(): Duke e startuar gorutinën", i)
22 		wg.Add(1)
23 		go worker(&wg, i)
24 	}
25 
26 	fmt.Println("main(): Duke pritur gorutinat të përfundojnë")
27 	wg.Wait()
28 	fmt.Println("main(): Përfundoi")
29 }

https://play.golang.org/p/p7StS5oK6nX

Rezultati:

 1 main(): Duke e startuar gorutinën 1
 2 main(): Duke e startuar gorutinën 2
 3 main(): Duke e startuar gorutinën 3
 4 main(): Duke pritur gorutinat  përfundojnë
 5 Gorutina 3: filloi
 6 Gorutina 1: filloi
 7 Gorutina 2: filloi
 8 Gorutina 1: përfundoi
 9 Gorutina 3: përfundoi
10 Gorutina 2: përfundoi
11 main(): Përfundoi

HTTP Webserver

Go ofron suport për protokolin HTTP, me ç’rast nuk nevojitet kurrfarë serveri i jashtëm, për shembull siç është rasti me PHP.

 1 package main
 2 
 3 import (
 4 	"fmt"
 5 	"net/http"
 6 )
 7 
 8 func main() {
 9 	fmt.Println("Duke e startuar serverin")
10 	http.HandleFunc("/", homeHandler)
11 	err := http.ListenAndServe(":8080", nil)
12 	if err != nil {
13 		panic(err)
14 	}
15 }
16 func homeHandler(w http.ResponseWriter, r *http.Request) {
17 	fmt.Println("Pergjigja nga Web aplikacioni")
18 }

https://play.golang.org/p/n8CKMxzIp9z

Rezultati:

Programi i mësipërm kthen përgjigje në konzolë, por në fakt ne do ta modifikojmë asisoj që përgjigjen ta kthejë si HTPP Response, pra t’ia dërgojë tekstin browserit në vend të konzolës.

Ashtu si është tani, programi startohet, shfaq tekstin “Duke e startuar serverin” në konzolë.

E hapim browserin dhe atje shënojmë si adresë:

http://localhost:8080

Sa herë bëjmë refresh në browser, në konzolë do të shfaqet teksti “Pergjigja nga Web aplikacioni”.

Funksioni HandleFunc nga pakoja http, shërben si një ruter bazik.

Si parametër të parë e shënojmë rutën e që në rastin konkret është “/” që d.m.th. home page, respektivisht faqja kryesore që hapet kur e shënojmë vetëm domainin, në rastin konkret http://localhost:8080.

Si parametër të dytë e shënojmë emrin e funksionit i cili thirret, dhe i cili do ta bëjë kthimin e përgjigjes (Response), në rastin konkret homeHandler.

Funksioni homeHandler (sikurse edhe funksionet tjera që do t’i definojmë te rutat tjera), i ka dy parametra; w të tipit http.ResponseWriter dhe r të tipit pointer i http.Request :

  • w http.ResponseWriter
  • r *http.Request

Nëse ruta ka parametra, ato lexohen nga http.Request, ndërsa në http.ResponseWriter dërgohet përmbajtja e dëshiruar në browser, zakonisht të dhëna në formatin HTML ose JSON.

Nga http.Request lexohen edhe të dhënat e fushave të formularit, nëse ajo rutë është thirrur si “action” i një formulari.

Funksioni ListenAndServe() e starton serverin në portin e cekur. Prej momentit kur fillon ekzekutimi i këtij funksioni, ne mund t’i qasemi serverit nëpërmes browserit. Vetë programi do të ngelet duke u ekzekutuar deri sa ta ndërprejmë me Ctrl-C. Asgjë nuk do të ekzekutohet pas këtij rreshti (rreshti 11), përveç nëse paraqitet ndonjë gabim, me ç’rast ai gabim raportohet në konzolë (rreshtat 12-14).

Për këtë shkak, të gjitha definicionet e rutave duhet të bëhet para thirrjes së ListenAndServe().

Meqë në këtë program konkret ende nuk kemi shënuar funksione që dërgojnë përgjigje në http.Response(), kur e hapim adresën në browser, browseri do të shfaqë faqe të zbrazët.

r.Method

Me r.Method mund ta determinojmë HTPP metodën e përdorur.

Konstantat e paradefinuara për secilën prej metodave me të cilat mund të krahasojmë:

* MethodGet = “GET”
* MethodPost = “POST”
* MethodPut = “PUT”
* MethodPatch = “PATCH” // RFC 5789
* MethodDelete = “DELETE”
* MethodOptions = “OPTIONS”
* MethodHead = “HEAD”
* MethodConnect = “CONNECT”
* MethodTrace = “TRACE”

Dërgimi i kërkesës me metodën POST

resp, err := http.Post("http://example.com/upload", "image/jpeg", &buf)

Dërgimi i kërkesës me metodën GET

resp, err := http.Get("http://example.com/")

Dërgimi i kërkesës me PostForm

resp, err := http.PostForm("http://example.com/form", url.Values{"key": {"Value"}, "id": {"123"}})

r.URL

Me r.URL e lexojmë URL-në e shënuar në address bar të browserit bashkë me query string.

r.URL.Path

Me r.URL.Path e lexojmë vetëm rutën, pa query string.

Zbërthimi i një URL

p := strings.Split(r.URL.Path, "/")

Definicioni i rutës duhet të përfundojë me /:

http.HandleFunc("/test/", homeHandler)

Parametrit në segmentin e dytë i qasemi me p[2].

Nëse e duam si integer, e konvertojmë:

id, _ := strconv.Atoi(p[2])

Pas kësaj mund ta përdorim për ta përcjellur si id në SQL query.

Numri i segmenteve

Numri e segmenteve e determinojmë me len(p).

HTML escape

html.EscapeString(r.URL.Path)

Shembull i ngjashëm

 1 package main
 2 
 3 import (
 4 	"fmt"
 5 	"net/http"
 6 )
 7 
 8 func main() {
 9 	http.HandleFunc("/profile", profilePage)
10 	http.ListenAndServe(":8080", nil)
11 }
12 
13 func profilePage(w http.ResponseWriter, r *http.Request) {
14 	name := r.FormValue("name")
15 	fmt.Fprint(w, "This is your profile page, "+name)
16 }

https://play.golang.org/p/nBi8gB-k683

Aplikacioni i mësipërm përmban vetëm një rutë, së cilës rutë i korrespondon një funksion.

http.HandleFunc("/profile", profilePage)

Thirret në këtë formë: http://localhost:8080/profile?name=Teuta

Në funksionin http.HandleFunc që i përket pakos net/http, si parametër të parë e shënojmë emërtimin e rutës, ndërsa si parametër të dytë e shënojmë emrin e funksionit i cili duhet të ekzekutohet kur thirret kjo rutë.

Të gjitha funksionet të cilat do të përdoren për procesimin e Web kërkesave (Web requests) për të kthyer më pastaj përgjigje (Web response), do t’i kenë dy parametra:

  • http.ResponseWriter
  • *http.Request

http.ResponseWriter do të përdoret për dërgimin e response, ndërsa *http.Request për ta lexuar kërkesën e cila ka mundur të vijë me ndonjërën nga HTTP metodat.

Ndaj variablit r në këtë shembull ku është vendosur HTTP kërkesa, respektivisht rezultati i kthyer nga strukti *http.Request, mund të zbatojmë metoda/funksione të ndryshme specifike për Web kërkesën. Në rastin konkret, thirret metoda FormValue() me anë të së cilës do të jemi në gjendje të lexojmë vlerat e query string të dërguar me ndonjërën nga HTTP metodat e cekura më sipër.

Dërgimin e përgjigjes (response) e bëjmë me fmt.Fprint(), e cila metodë kërkon dy parametra:

  • Variablin i cili i korrespondon struktit të response (w)
  • Përmbajtjen e cila dërgohet, që në shembullin konkret është string i rëndomtë

http.ListenAndServe(":8080", nil) e starton Web serverin në portin 8080, në mënyrë që ne të mund t’i qasemi Web aplikacionit nga browseri duke shënuar:

`localhost:8080/profile’

Në shembullin konkret, do të shtojmë edhe ?name=Golang në fund të URL-së për të demonstruar leximin equery string nga URL-ja. Pra, në browser shënojmë:

`localhost:8080/profile?name=Golang’

Pas kësaj, në browser do të shfaqet teksti:

This is your profile page, Golang

Ndërtimi i rutave të tjera

Tash do t’i shtojmë pak më shumë funksionalitet aplikacionit tonë: regjistrimin e një anëtari, në këtë rast duke mos përdorur fare databazën, por vetëm duke i ruajtur të dhënat në memorje.

E formojmë një strukt për anëtarin:

1 type anetari struct {
2 	Emri   string
3 	Emaili string
4 }

E formojmë një map për anëtarët:

1 	anetarMap = make(map[string]anetari)

Si çelës i mapës do të përdoret emri i anëtarit, ndërsa si vlerë do të jetë strukti, i cili i përmban të dhënat e tjera të anëtarit (në këtë shembull, vetëm emrin dhe emailin).

E ndërtojmë funksionin për regjistrimin e anëtarit, duke lexuar të dhënat e kërkuara nga query string nga browseri, pra me metodën GET.

1 func regjistroAnetar(w http.ResponseWriter, r *http.Request) {
2 	Emri := r.URL.Query().Get("emri")
3 	Emaili := r.URL.Query().Get("emaili")
4 	anetarMap[Emri] = anetari{Emri, Emaili}
5 	w.Write([]byte("U regjistrua:" + Emri))
6 }

Vlerat hyrëse nga query string (apo nga formulari), lexohen me:

1 r.URL.Query().Get("emri")
2 r.URL.Query().Get("emaili")

Brenda Get() shënohet emri i fushës së formularit, ashtu siç është definuar me atributin name, sepse atributi name paraqitet si variabël në query string.

Funksioni i referohet ResponseWriter dhe Request nga pakoja http, prandaj nevojitet që kjo pako të importohet:

1 import (
2 	"fmt"
3 	"net/http"
4 )

Tash e shkruajmë funksonin për listimin e anëtarëve, i cili do ta formojë një string me emrat dhe emailat e anëtarëve, duke bërë iteracion nëpër mapën anetarMap me strukturën for.

 1 func listaAnetareve(w http.ResponseWriter, r *http.Request) {
 2 	lista := ""
 3 	for _, ant := range anetarMap {
 4 		if len(ant.Emri) > 0 {
 5 			lista += ant.Emri + ":" + ant.Emaili + "\n"
 6 
 7 		}
 8 	}
 9 	w.Write([]byte("Lista e anëtarëve:\n" + lista))
10 }

Në funksionin main() definojmë rutat për këto dy funksione:

1 	http.HandleFunc("/regjistro", regjistroAnetar)
2 	http.HandleFunc("/lista", listaAnetareve)

Programi komplet:

 1 package main
 2 
 3 import (
 4 	"fmt"
 5 	"net/http"
 6 )
 7 
 8 type anetari struct {
 9 	Emri   string
10 	Emaili string
11 }
12 
13 var anetarMap = make(map[string]anetari)
14 
15 func main() {
16 	fmt.Println("Duke e startuar serverin")
17 	http.HandleFunc("/regjistro", regjistroAnetar)
18 	http.HandleFunc("/lista", listaAnetareve)
19 	err := http.ListenAndServe(":8080", nil)
20 	if err != nil {
21 		panic(err)
22 	}
23 	fmt.Println("test")
24 }
25 
26 func regjistroAnetar(w http.ResponseWriter, r *http.Request) {
27 	Emri := r.URL.Query().Get("emri")
28 	Emaili := r.URL.Query().Get("emaili")
29 	anetarMap[Emri] = anetari{Emri, Emaili}
30 	w.Write([]byte("U regjistrua:" + Emri))
31 }
32 
33 func listaAnetareve(w http.ResponseWriter, r *http.Request) {
34 	lista := ""
35 	for _, ant := range anetarMap {
36 		if len(ant.Emri) > 0 {
37 			lista += ant.Emri + ":" + ant.Emaili + "\n"
38 
39 		}
40 	}
41 	w.Write([]byte("Lista e anëtarëve:\n" + lista))
42 }

https://play.golang.org/p/nKMyE2jgUGI

Pasi të jetë startuar programi, e hapim browserin dhe e hapim URL-në si vijon:

http://localhost:8080/regjistro?emri=Granit&emaili=granit@gmail.com

Në query string, pas variablit emri e shënojmë një emër, ndërsa pas variablit emaili e shënojmë një email, pastaj shtypim Enter. Këtë e përsërisim aq herë sa anëtarë dëshirojmë të regjistrojmë.

Të njëjtën mund ta bëjmë edhe nëpërmes një formulari, tek i cili shënojmë si vlerë të atributit “action” URL-në e regjistrimit.

 1 <!DOCTYPE html>
 2 <html lang="en">
 3 <head>
 4     <meta charset="UTF-8">
 5     <title>Title</title>
 6 </head>
 7 <body>
 8 <form method="get" action="http://localhost:8080/regjistro">
 9 <p>Emri: <input type="text" name="emri"></p>
10 <p>Emaili: <input type="email" name="emaili"></p>
11     <p><input type="submit" value="Ruaje"></p>
12 </form>
13 </body>
14 </html>

Ky formular mund të hapet si fajll statik drejtpërsëdrejti në browser.

Metoda e tretë është nëpërmes ndonjë programi për testimin e API-ve, siç janë Postman apo Insomnia.

Postman

Rezultati që kthehet është:

U regjistrua: Alban

Lista e anëtarëve shfletohet duke e thirrur URL-në lista.

http://localhost:8080/lista

Rezultati:


Lista e anëtarëve:
Tahir:tahir.hoxha@gmail.com
Arta:arta@gmail.com
Granit:granit@gmail.com
Alban:alban@gmail.com

Meqë rezultati në formë tekstuale nuk është i strukturuar, ne mundemi mapën e njëjtë ta konvertojmë në JSON, duke bërë ndryshime në funksion:

1 func listaAnetareve(w http.ResponseWriter, r *http.Request) {
2 	jAnetarMap, _ := json.Marshal(anetarMap)
3 	w.Header().Set("Content-Type", "application/json")
4 	w.Write([]byte(jAnetarMap))
5 }

Tash lista do të duket kështu:

1 {"Alban":{"Emri":"Alban","Emaili":"alban@gmail.com"},"Arta":{"Emri":"Arta","Emaili":\
2 "arta@gmail.com"},"Granit":{"Emri":"Granit","Emaili":"granit@gmail.com"}}

Ky rezultat mund të procesohet më tej me cilëndo gjuhë: Go, Java, C#, PHP, JavaScript, etj.

Package net

Në thelb të komunikimit rrjetor në Go është pakoja e quajtur net. Kjo pako ofron implementimet për HTTP klient dhe server.

Pakoja net përmban nënpako jo vetëm për HTTP operacionet relevante, por po ashtu edhe për serverët TCP/UDP, DNS dhe IP vegla.

hello.go

 1 package main
 2 
 3 import (
 4 	"fmt"
 5 	"log"
 6 	"net/http"
 7 	"time"
 8 )
 9 
10 const (
11 	Port = ":8080"
12 )
13 
14 func main() {
15 	http.HandleFunc("/statike", faqeStatike)
16 	http.HandleFunc("/dinamike", faqeDinamike)
17 	log.Fatal(http.ListenAndServe(Port, nil))
18 }
19 
20 func faqeStatike(w http.ResponseWriter, r *http.Request) {
21 	http.ServeFile(w, r, "dokumenti.html")
22 }
23 
24 func faqeDinamike(w http.ResponseWriter, r *http.Request) {
25 	response := "Ora është " + time.Now().String()
26 	fmt.Fprintln(w, response)
27 }

https://play.golang.org/p/dSSZS2VPxYh

Rezultati shfaqet në browser, duke e thirrur me rutat /dinamike për përmbajtjen dinamike që do të jetë shfaqja e kohës aktuale dhe /statike për përmbajtjen statike që në rastin konkret do të jetë një HTML dokument. Kodi duhet të kompajlohet: go run hello.go.

dokumenti.html

 1 <!DOCTYPE html>
 2 <html>
 3 <head>
 4     <title>Your Title Here</title>
 5 </head>
 6 <body>
 7 <H1>Ky eshte nje titull</H1>
 8 <p>Ky eshte nje paragraf.</p>
 9 </body>
10 </html>

Si port për Web server mund ta përdorim cilindo port, ndërsa në shembuj do ta përdorim portin 8080.

http.HandleFunc

Me http.HandleFunc i definojmë rutat e dëshiruara, duke e cekur si parametër të parë rutën, ndërsa si parametër të dytë funksionin i cili i korrespondon asaj rute.

1 http.HandleFunc("/statike", faqeStatike)

http.ServeFile

Me http.ServeFile mund të dërgojmë një përmbajtje statike si reagim ndaj kërkesës.

Në rreshtin http.HandleFunc("/statike", faqeStatike) përcaktojmë që kur të kërkohet ruta /statike të thirret funksioni faqeStatike, i cili në këtë shembull ka për detyrë vetëm ta shfaqë një dokument statik me emrin dokumenti.html.

1 func faqeStatike(w http.ResponseWriter, r *http.Request) {
2 	http.ServeFile(w, r, "dokumenti.html")
3 }

Nëpërmes variablit w do të mund të shkruajmë HTTP reagimin (response), ndërsa nëpërmes variablit r do ta pranojmë HTTP kërkesën (request).

fmt.Fprintln(w, response)

Me fmt.Fprintln dërgojmë përmbajtjen e dëshiruar si HTTP response, pra ajo përmbajtje do të jetë e qasshme në browser nëpërmes rutës së definuar, në rastin konkret /dinamike.

1 func faqeDinamike(w http.ResponseWriter, r *http.Request) {
2 	response := "Ora është " + time.Now().String()
3 	fmt.Fprintln(w, response)
4 }

Radhitja e procesit të zhvillimit të Web aplikacionit

  • E shkruajmë kodin në Go
  • E krijojmë HTML dokumentin statik
  • E kompajlojmë kodin dhe e ekzekutojmë
  • Nëse lajmërohet anti-virusi, e lejojmë që ta skanojë .exe programin e sapokrijuar
  • Nëse firewall kërkon leje për lejimin e portit, ia japim lejen
  • Kalojmë në browserin e dëshiruar dhe në address bar shënojmë /statike ose /dinamike për ta parë përmbajtjen e këtyre dy rutave
  • Nëse bëjmë ndryshime, atëherë e ndërprejmë në terminal ekzekutimin e programit me Ctrl-C, e më pas e kompajlojmë dhe e ekzekutojmë sërish

Leximi i përmbajtjes së një Web faqeje

Me programin vijues do ta lexojmë përmbajtjen e një faqeje në adresë të caktuar dhe përmbajtjen e saj do ta shfaqim në terminal.

 1 package main
 2 
 3 import (
 4 	"fmt"
 5 	"io/ioutil"
 6 	"net/http"
 7 )
 8 
 9 func main() {
10 	faqja, err := http.Get("http://example.com/")
11 	if err != nil {
12 		fmt.Println("Nuk munda ta hap faqen example.com")
13 	}
14 	defer faqja.Body.Close()
15 	body, err := ioutil.ReadAll(faqja.Body)
16 	fmt.Println(string(body))
17 }

https://play.golang.org/p/sr8tQd6jBs3

http.Get

Mundëson qasjen në përmbajtjen e një resursi në URL-në e specifikuar. Përmbajtja e kthyer do të jetë e tipit *http.Response.

ioutil.ReadAll

func ReadAll(r io.Reader) ([]byte, error)

CRUD operacionet

Në shembullin vijues, do të krijohet një REST API i thjeshtë me 4 operacionet bazike ndaj databazës (CRUD), me koneksionin e databazës si variabël globale, në mënyrë që të mos krijohet koneksioni me databazën brenda çdo funksioni.

 1 package main
 2 
 3 import (
 4 	"database/sql"
 5 	"fmt"
 6 	_ "github.com/go-sql-driver/mysql"
 7 	"log"
 8 	"net/http"
 9 	"strings"
10 )
11 
12 const (
13 	DBHost  = "127.0.0.1"
14 	DBPort  = ":3306"
15 	DBUser  = "root"
16 	DBPass  = ""
17 	DBDbase = "golang"
18 )
19 
20 var database *sql.DB
21 
22 func main() {
23 	dbConn := fmt.Sprintf("%s:%s@tcp(%s)/%s", DBUser, DBPass, DBHost, DBDbase)
24 	db, err := sql.Open("mysql", dbConn)
25 	if err != nil {
26 		log.Println("Couldn't connect!")
27 		log.Println(err.Error)
28 	}
29 
30 	database = db
31 
32 	fmt.Println("Duke e startuar serverin")
33 	http.HandleFunc("/book/", RHandler)
34 	http.HandleFunc("/save/", CHandler)
35 	http.HandleFunc("/delete/", DHandler)
36 	http.HandleFunc("/update/", UHandler)
37 
38 	err = http.ListenAndServe(":8082", nil)
39 	if err != nil {
40 		panic(err)
41 	}
42 }
43 func RHandler(w http.ResponseWriter, r *http.Request) {
44 	url := strings.Split(r.URL.Path, "/")
45 	sql := "SELECT id, book_title FROM books WHERE id='" + url[2] + "'"
46 
47 	var id int
48 	var book_title string
49 	var tabela string
50 
51 	rows, _ := database.Query(sql)
52 
53 	for rows.Next() {
54 		rows.Scan(&id, &book_title)
55 		tabela += book_title
56 	}
57 	fmt.Fprint(w, tabela)
58 }
59 
60 func CHandler(w http.ResponseWriter, r *http.Request) {
61 	s := r.URL.Query().Get("book_title")
62 	sql := "INSERT INTO books SET book_title='" + s + "'"
63 	database.Query(sql)
64 }
65 
66 func DHandler(w http.ResponseWriter, r *http.Request) {
67 	s := r.URL.Query().Get("id")
68 	sql := "DELETE FROM books WHERE id='" + s + "'"
69 	database.Query(sql)
70 }
71 
72 func UHandler(w http.ResponseWriter, r *http.Request) {
73 	url := strings.Split(r.URL.Path, "/")
74 	id := url[2]
75 	book_title := r.URL.Query().Get("book_title")
76 	sql := "UPDATE books SET book_title= '" + book_title + "' WHERE id='" + id + "'"
77 	database.Query(sql)
78 }

https://play.golang.org/p/srKlazK-9_S

Leximi i librit me ID 4:

http://localhost:8082/book/4

Insertimi i një libri të ri:

http://localhost:8082/save/?book_title=Java+per+fillestare

Përditësimi i një libri ekzistues

http://localhost:8082/update/1/?book_title=Python+per+fillestare

Fshirja e një libri

http://localhost:8082/delete/?id=1

Sqarime:

Definojmë konstantat me vlerat për hostin, portin, përdoruesin, fjalëkalimin dhe emrin e databazës.

1 const (
2 	DBHost  = "127.0.0.1"
3 	DBPort  = ":3306"
4 	DBUser  = "root"
5 	DBPass  = ""
6 	DBDbase = "golang"
7 )

Më pas, e definojmë një variabël globale me emrin database të tipit *sql.DB.

Në funksionin main(), e formatojmë stringun e koneksionit dhe e ruajmë në variablin dbConn.

1 	dbConn := fmt.Sprintf("%s:%s@tcp(%s)/%s", DBUser, DBPass, DBHost, DBDbase)

Bëjmë konektimin me MySQL server dhe verifikojmë nëse është shfaqur ndonj[ gabim:

1 	db, err := sql.Open("mysql", dbConn)
2 	if err != nil {
3 		log.Println("Couldn't connect!")
4 		log.Println(err.Error)
5 	}

Vlerën e variablit db e bartim në variablin database të cilin më parë e kemi deklaruar si variabël globale. Tash e tutje, çfarëdo operacion ndaj databazës e kryejmë duke iu referuar variablit database nëpër funksione:

1 database.Query(sql)

ose

1 rows, _ := database.Query(sql)

Struct / database query

Në shembullin vijues do të përdorim një strukt (Lajmi), të përbërë nga 4 fusha:

  • Id,
  • Titulli,
  • Teksti, dhe
  • Data.

Tabelën mund ta krijojmë duke e përdorur SQL kodin e mëposhtëm.

lajmet.sql

 1 SET SQL_MODE = "NO_AUTO_VALUE_ON_ZERO";
 2 SET AUTOCOMMIT = 0;
 3 START TRANSACTION;
 4 SET time_zone = "+00:00";
 5 
 6 CREATE TABLE `lajmet` (
 7   `id` int(11) NOT NULL,
 8   `titulli` varchar(255) COLLATE utf8mb4_unicode_ci NOT NULL,
 9   `teksti` text COLLATE utf8mb4_unicode_ci NOT NULL,
10   `data` timestamp NOT NULL
11 ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
12 
13 
14 ALTER TABLE `lajmet`
15   ADD PRIMARY KEY (`id`),
16   ADD KEY `titulli` (`titulli`),
17   ADD KEY `data` (`data`);
18 
19 ALTER TABLE `lajmet`
20   MODIFY `id` int(11) NOT NULL AUTO_INCREMENT;
21 COMMIT;

Programi

Për rutim më të avancuar, kësaj radhe do të përdoret pakoja github.com/gorilla/mux.

routes.HandleFunc("/page/{id:[0-9]+}", ServePage)

Me këtë kemi definuar se ruta do të jetë /page/, që do ta pranojë një parametër të cilit brenda funksionit do t’i referohemi si id. Id-ja do të jetë numër prej 0 deri 9 me një apo më tepër shifra. Kështu që një lajm do të mund të hapet me /page/1.

Nëse parametri i shënuar pas /page/ nuk është numerik, pra kur nuk i përgjigjet pattern-it të definuar {id:[0-9]+}, do të lajmërohet gabimi:

404 page not found

Funksioni ServePage do ta pranojë id-në nga r *http.Request me:

1 vars := mux.Vars(r)
2 	pageID := vars["id"]

mux.Vars(r) përmban të gjitha GET variablat nga request në formë të tipit map, prej nga me vars["id"] e ekstraktojmë vetëm fushën e id, të cilin pastaj e përdorim gjatë ndërtimit të SQL query`.

SQL query thirret me database.QueryRow() ku shënojmë SELECT id, titulli, teksti, data FROM lajmet WHERE id=?, ndërsa id-në e faqes e japim si parametër të dytë. Me këtë parandalojmë SQL injection. Rezultati me Scan bartet në fushat korresponduese të struktit Lajmi.

w http.ResponseWriter me fmt.Fprint() e dërgojmë struktin si response. Ky nuk është format i përshtatshëm për response, sepse do të duhet ta dërgojmë si JSON ose si HTML dokument.

Kodi i programit

 1 package main
 2 
 3 import (
 4 	"database/sql"
 5 	"fmt"
 6 	_ "github.com/go-sql-driver/mysql"
 7 	"github.com/gorilla/mux"
 8 	"log"
 9 	"net/http"
10 )
11 
12 const (
13 	DBHost  = "127.0.0.1"
14 	DBPort  = ":3306"
15 	DBUser  = "root"
16 	DBPass  = ""
17 	DBDbase = "golang"
18 )
19 
20 var database *sql.DB
21 
22 type Lajmi struct {
23 	Id int
24 	Titulli string
25 	Teksti  string
26 	Data    string
27 }
28 
29 func main() {
30 	dbConn := fmt.Sprintf("%s:%s@tcp(%s)/%s", DBUser, DBPass, DBHost, DBDbase)
31 	db, err := sql.Open("mysql", dbConn)
32 	if err != nil {
33 		log.Println("Couldn't connect!")
34 		log.Println(err.Error)
35 	}
36 
37 	database = db
38 
39 	routes := mux.NewRouter()
40 	routes.HandleFunc("/page/{id:[0-9]+}", ServePage)
41 	http.Handle("/", routes)
42 	http.ListenAndServe(":8080", nil)
43 }
44 
45 func ServePage(w http.ResponseWriter, r *http.Request) {
46 	vars := mux.Vars(r)
47 	pageID := vars["id"]
48 	Lajm := Lajmi{}
49 	database.QueryRow("SELECT id, titulli, teksti, data FROM lajmet WHERE id=?", pageID\
50 ).Scan(&Lajm.Id, &Lajm.Titulli, &Lajm.Teksti, &Lajm.Data)
51 	fmt.Fprint(w, Lajm)
52 }

https://play.golang.org/p/RkvQdXGqT0e

Leximi i një lajmi:

1 http://localhost:8080/page/1

Në versionin ekzistues, nëse nuk gjendet një lajm me id të caktuar në tabelën e lajmeve, krejt çka raporton programi është një strukt i zbrazët.

Për të fituar një raport që tregon se lajmi nuk u gjet, e modifikojmë funksionin:

 1 func ServePage(w http.ResponseWriter, r *http.Request) {
 2 	vars := mux.Vars(r)
 3 	pageID := vars["id"]
 4 	Lajm := Lajmi{}
 5 	err := database.QueryRow("SELECT id, titulli, teksti, data FROM lajmet WHERE id=?",\
 6  pageID).Scan(&Lajm.Id, &Lajm.Titulli, &Lajm.Teksti, &Lajm.Data)
 7 	if err != nil {
 8 		http.Error(w, http.StatusText(404), http.StatusNotFound)
 9 		log.Println("Couldn't get page!")
10 	} else {
11 		fmt.Fprint(w, Lajm)
12 	}
13 }

Nëse thirrja e QueryRow kthen gabim, atëherë në w kthejmë statusin 404, edhe si tekst, edhe si status kod numerik:

1 http.Error(w, http.StatusText(404), http.StatusNotFound)

Siç u cek më sipër, kthimi i rezultatit nga databaza u dërgua në ResponseWriter nuk ishte i formatuar në formë të përshtatshme. Tash do ta formatojmë si JSON.

Së pari e importojmë pakon encoding/json.

Konvertimi nga strukt në JSON kërkon që në strukt t’i definojmë JSON tags:

1 type Lajmi struct {
2 	Id      int    `json:"id"`
3 	Titulli string `json:"titulli"`
4 	Teksti  string `json:"teksti"`
5 	Data    string `json:"data"`
6 }

Konvertimi i struktit në JSON bëhet me metodën json.Marshal():

1 b, err := json.Marshal(Lajm)

Tash b përmban slice of bytes, të cilin me funksionin string() e kthejmë në tekst të lexueshëm dhe e dërgojmë në http.ResponseWriter nëpërmes variablit w:

1 fmt.Fprint(w, string(b))

Kodi komplet:

 1 package main
 2 
 3 import (
 4 	"database/sql"
 5 	"encoding/json"
 6 	"fmt"
 7 	_ "github.com/go-sql-driver/mysql"
 8 	"github.com/gorilla/mux"
 9 	"log"
10 	"net/http"
11 )
12 
13 const (
14 	DBHost  = "127.0.0.1"
15 	DBPort  = ":3306"
16 	DBUser  = "root"
17 	DBPass  = ""
18 	DBDbase = "golang"
19 )
20 
21 var database *sql.DB
22 
23 type Lajmi struct {
24 	Id      int    `json:"id"`
25 	Titulli string `json:"titulli"`
26 	Teksti  string `json:"teksti"`
27 	Data    string `json:"data"`
28 }
29 
30 func main() {
31 	dbConn := fmt.Sprintf("%s:%s@tcp(%s)/%s", DBUser, DBPass, DBHost, DBDbase)
32 	db, err := sql.Open("mysql", dbConn)
33 	if err != nil {
34 		log.Println("Couldn't connect!")
35 		log.Println(err.Error)
36 	}
37 
38 	database = db
39 
40 	routes := mux.NewRouter()
41 	routes.HandleFunc("/page/{id:[0-9]+}", ServePage)
42 	http.Handle("/", routes)
43 	http.ListenAndServe(":8080", nil)
44 }
45 
46 func ServePage(w http.ResponseWriter, r *http.Request) {
47 	vars := mux.Vars(r)
48 	pageID := vars["id"]
49 	Lajm := Lajmi{}
50 	err := database.QueryRow("SELECT id, titulli, teksti, data FROM lajmet WHERE id=?",\
51  pageID).Scan(&Lajm.Id, &Lajm.Titulli, &Lajm.Teksti, &Lajm.Data)
52 	if err != nil {
53 		http.Error(w, http.StatusText(404), http.StatusNotFound)
54 		log.Println("Couldn't get page!")
55 	} else {
56 		b, err := json.Marshal(Lajm)
57 		if err != nil {
58 			fmt.Println(err)
59 		} else {
60 			fmt.Fprint(w, string(b))
61 		}
62 	}
63 }

https://play.golang.org/p/pRlmmAfKEfL

Tash nëpërmes thirrjes së rutës http://localhost:8080/page/1

do të fitojmë një JSON response sikur kjo:

 1 {
 2 "id":1,
 3 "titulli":"Italia mposht Greqinë dhe siguron kualifikimin në Euro 2020\r\n",
 4 "teksti":"Kombëtarja e Italisë ka siguruar kualifikimin në Kampionatin Evropian pas \
 5 fitores 2-0 ndaj Greqisë në kuadër të ndeshjeve eliminatore të Grupit J.\r\n\r\nEkip\
 6 i i drejtuar nga Roberto Mancini ka vazhduar serinë e qind për qind me shtatë fitore\
 7  nga po kaq ndeshje, duke mposhtur në shtëpi Greqinë.\r\n\r\nPjesa e parë e ndeshjes\
 8  u karakterizua me dominim të Azurrëve, por edhe përkundër rasteve të krijuara nuk a\
 9 rritën të gjejnë rrjetën.\r\n\r\nNë pjesën e dytë, përsëri ishte Italia që dominoi n\
10 ë posedim dhe raste, ndërsa shënoi edhe dy gola për të siguruar fitoren.\r\n\r\nGoli\
11  i parë i ndeshjes erdhi në minutën e 63-të dhe ishte Jorginho, i cili u tregua i sa\
12 ktë nga pika e bardhë për 1-0.",
13 "data":"2019-10-12 00:00:00"
14 }

Databazat

Për të punuar me MySQL, së pari duhet të shkarkohet drajveri përkatës nga Github:

go get github.com/go-sql-driver/mysql

 1 package main
 2 
 3 import (
 4 	"database/sql"
 5 	"fmt"
 6 	_ "github.com/go-sql-driver/mysql"
 7 	"log"
 8 )
 9 
10 func main() {
11 	db, err := sql.Open("mysql", "root:@tcp(127.0.0.1:3306)/markdown")
12 	defer db.Close()
13 	if err != nil {
14 		log.Fatal(err)
15 	}
16 
17 	var id int
18 	var book_title string
19 
20 	rows, err := db.Query("SELECT id, book_title FROM books ORDER BY id")
21 	for rows.Next() {
22 		rows.Scan(&id, &book_title)
23 		fmt.Printf("ID: %v,  %v\n", id, book_title)
24 	}
25 }

https://play.golang.org/p/3l4xo1BS9y_s

Rezultati:

1 ID: 1,  Go për fillestarë
2 ID: 2,  PHP dhe MySQL për fillestarë
3 ID: 3,  Laravel për fillestarë

Leximi i radhëve të tabelës ne përdorimin e struct/tag

 1 package main
 2 
 3 import (
 4 	"database/sql"
 5 	"fmt"
 6 	_ "github.com/go-sql-driver/mysql"
 7 	"log"
 8 )
 9 
10 type Fushat struct {
11 	ID      int    `json:"id"` // Emertimi i fushes ne tabele
12 	Titulli string `json:"book_title"` // Emertimi i fushes ne tabele
13 }
14 
15 func main() {
16 	db, err := sql.Open("mysql", "root:@tcp(127.0.0.1:3306)/markdown")
17 	defer db.Close()
18 	if err != nil {
19 		log.Fatal(err)
20 	}
21 
22 	results, err := db.Query("SELECT id, book_title FROM books ORDER by id")
23 	if err != nil {
24 		panic(err.Error())
25 	}
26 
27 	for results.Next() {
28 		var fushat Fushat
29 		err = results.Scan(&fushat.ID, &fushat.Titulli)
30 		if err != nil {
31 			panic(err.Error())
32 		}
33 		fmt.Printf("%v %v\n", fushat.ID, fushat.Titulli)
34 	}
35 }

https://play.golang.org/p/1FMj5NKuGxn

Rezultati:

1 1 Go për fillestarë
2 2 PHP dhe MySQL për fillestarë
3 3 Laravel për fillestarë

Drivers:

https://github.com/golang/go/wiki/SQLDrivers

I/O and File Systems

Shkrimi dhe leximi i një fajlli

 1 package main
 2 
 3 import (
 4 	"fmt"
 5 	"io/ioutil"
 6 )
 7 
 8 func main() {
 9 	writeFile("test")
10 	readFile()
11 }
12 
13 func writeFile(message string) {
14 	bytes := []byte(message)
15 	ioutil.WriteFile("e:/temp/testgo.txt", bytes, 0644)
16 	fmt.Println("created a file")
17 }
18 
19 func readFile() {
20 	data, _ := ioutil.ReadFile("e:/temp/testgo.txt")
21 	fmt.Println("file content:")
22 	fmt.Println(string(data))
23 }

https://play.golang.org/p/GwOnhuvuzo5

Leximi i një fajlli me buffering

 1 package main
 2 
 3 import (
 4 	"fmt"
 5 	"io"
 6 	"os"
 7 )
 8 
 9 func main() {
10 
11 	f, err := os.Open("poema.txt")
12 	if err != nil {
13 		fmt.Println("Error:", err)
14 		return
15 	}
16 	defer f.Close()
17 
18 	var (
19 		b= make([]byte, 16)
20 	)
21 	for n := 0; err == nil; {
22 		n, err = f.Read(b)
23 		if err == nil {
24 			fmt.Print(string(b[:n]))
25 		}
26 	}
27 	if err != nil && err != io.EOF {
28 		fmt.Println("\n\nError:", err)
29 	}
30 }

poema.txt

1 O malet' e Shqipërisë e ju o lisat' e gjatë!
2 Fushat e gjëra me lule, q'u kam ndër mënt dit' e natë!
3 Ju bregore bukuroshe e ju lumenjt' e kulluar!
4 Çuka, kodra, brinja, gërxhe dhe pylle  gjelbëruar!
5 Do  këndonj bagëtinë  mbani ju e ushqeni,
6 O vendëthit e bekuar, ju mëndjen ma dëfreni.
7 Ti Shqipëri,  jep nderë,  jep emrin shqipëtar,
8 Zëmrën ti ma gatove plot me dëshirë dhe me zjarr.
9 ...

https://play.golang.org/p/hDSWObsvV1f

Rezultati:

1 O malet' e Shqipërisë e ju o lisat' e gjatë!
2 Fushat e gjëra me lule, q'u kam ndër mënt dit' e natë!
3 Ju bregore bukuroshe e ju lumenjt' e kulluar!
4 Çuka, kodra, brinja, gërxhe dhe pylle  gjelbëruar!
5 Do  këndonj bagëtinë  mbani ju e ushqeni,
6 O vendëthit e bekuar, ju mëndjen ma dëfreni.
7 Ti Shqipëri,  jep nderë,  jep emrin shqipëtar,
8 Zëmrën ti ma gatove plot me dëshirë dhe me zjarr.
9 ...
 1 package main
 2 
 3 import (
 4 	"fmt"
 5 	"io"
 6 	"os"
 7 	"strings"
 8 )
 9 
10 func main() {
11 	d := strings.NewReader("Teksti i cili do të ruhet në fajll.")
12 	f, err := os.Create("./tedhenat.txt")
13 	if err != nil {
14 		fmt.Println("Nuk e krijova fajllin:", err)
15 		os.Exit(1)
16 	}
17 	io.Copy(f, d)
18 }

https://play.golang.org/p/M35QuSLefEc

Rezultati:

Në fajll sistem krijohet fajlli tedhenat.txt me përmbajtjen Teksti i cili do të ruhet në fajll..

Package fmt

Marrë nga https://golang.org/pkg/fmt/

Të përgjithshme:

  • %v - vlera në formatin standard. Gjatë printimit të strukteve, shenja plus para v (%+v) i paraqet edhe emrat e fushave
  • %#v - Vlera.
  • %T - Tipi i vlerës.
  • %% - Shenja e përqindjes, nuk konsumon vlerë.

Boolean:

  • %t - Fjala true ose false.

Numrat e plotë:

  • %b - Baza 2, numra binarë (0 deri 1).
  • %c - Karakteri sipas Unicode kod pointit korrespondues. fmt.Printf(“%c”, 1234) shfaq Ӓ.
  • %d - Baza 10, numrat decimalë (0 deri 9).
  • %o - Baza 8, numrat oktalë (0 deri 7).
  • %q - Unicode karakteri i futur brenda apostrofave. fmt.Printf(“%q”, 1234) shfaq ‘Ӓ’.
  • %x - Baza 16, numra heksadecimalë (0-F). Shkronja të vogla (a-f).
  • %X - Baza 16, numra heksadecimalë (0-F). Shkronja të mëdha (A-F).
  • %U - Formati i Unicode. fmt.Printf(“%U”, 1234) shfaq U+04D2.

Numrat me presje dhjetore dhe kompleksë:

  • %b - Notacion shkencor pa decimale me eksponent të 2 në fuqi, p.sh. -123456p-78.
  • %e - Notacion shkencor, p.sh. -1.234456e+78
  • %E - Notacion shkencor, p.sh. -1.234456E+78
  • %f - Numër me presje dhjetore, por pa eksponent, p.sh. 123.456
  • %F - Sinonim i %f
  • %g - %e për eksponentë të mëdhenj, %f përndryshe.
  • %G - %E për eksponentë të mëdhenj, %F përndryshe.

Stringjet dhe bajt segmentet (slice):

  • %s - Bajtë të painterpretuar të një stringu apo segmenti.
  • %q - String u futur brenda thonjëzave.
  • %x - Baza 16, shkronja të vogla, dy karaktere për bajt.
  • %X - Baza 16, shkronja të mëdha, dy karaktere për bajt.

Segmentet:

  • %p - Adresa e elementit me indeks 0 në notacionin me bazën 16, me 0x paraprijëse.

Pointer:

  • %p - Notacion me bazën 16, me 0x paraprijëse.
  • %b, %d, %o, %x dhe %X punojnë edhe me pointerë, duke e formatuar vlerën njëjtë sikur të ishte numër i plotë.

Formati standard për %v është:

  • bool: %t
  • int, int8 etc.: %d
  • uint, uint8 etj.:** %d**, %#x nëse printohet me %#v
  • float32, complex64, etj.: %g
  • string: %s
  • chan: %p
  • pointer: %p

Në kuadër të pakos fmt është i definuar interfejsi Stringer i cili kërkon nga implementuesi që ta ketë funksionin String(), me çka na mundësohet që një tip kompozit si strukti të mund të paraqitet si string gjatë printimit.

1 type Stringer interface {
2     String() string
3 }

E krijojmë funksionin String() ku si pranues e caktojmë një strukt (Anetarj): func (p Anetari) String() string, ku si vlerë kthyese e funksionit do të jetë string. Brenda funksionit definojmë se si të kombinohen vlerat e fushave të ndryshme të struktit Anetari për ta formuar një string, pra që përmbajtja e struktit të prezantohet në formë të një rreshti tekstual.

 1 package main
 2 
 3 import "fmt"
 4 
 5 type Anetari struct {
 6 	Emri   string
 7 	Gjinia string
 8 	Mosha  int
 9 }
10 
11 func (p Anetari) String() string {
12 	return fmt.Sprintf("%v, %v", p.Emri, p.Mosha)
13 }
14 
15 func main() {
16 	a := Anetari{"Arben", "m", 32}
17 	b := Anetari{"Fjolla", "f", 24}
18 	fmt.Println(a)
19 	fmt.Println(b)
20 }

https://play.golang.org/p/8EFAyafWgCy

Rezultati:

1 Arben, 32
2 Fjolla, 24

Package strings

Join

 1 package main
 2 
 3 import (
 4 	"fmt"
 5 	"strings"
 6 )
 7 
 8 func main() {
 9 	ditet := []string{"E hëne", "E martë", "E mërkurë", "E enjte", "E premte", "E shtun\
10 ë", "E dielë"}
11 	fmt.Println(strings.Join(ditet, ", "))
12 }

https://play.golang.org/p/m26wcBgIwJg

Rezultati:

1 E hëne, E martë, E mërkurë, E enjte, E premte, E shtunë, E dielë

Regular Expressions

Kërkojmë një substring brenda një stringu:

 1 package main
 2 
 3 import (
 4 	"fmt"
 5 	"regexp"
 6 )
 7 
 8 func main() {
 9 	matched, error := regexp.MatchString("lisa", "O malet' e Shqipërisë e ju o lisat' e\
10  gjatë!")
11 	fmt.Println(matched)
12 	if error != nil {
13 		fmt.Println(error)
14 	}
15 
16 	matched, error = regexp.MatchString("fusha", "O malet' e Shqipërisë e ju o lisat' e\
17  gjatë!")
18 	fmt.Println(matched)
19 	if error != nil {
20 		fmt.Println(error)
21 	}
22 }

https://play.golang.org/p/7Oc6DQZQPhR

Rezultati:

1 true
2 false

Substringu “lisa” u gjet (true), ndërsa substringu “fusha” nuk u gjet (false).

Kërkojmë një substring që i ka 3 shkronja, e specifikojmë shkronjën e parë dhe të tretë, ndërsa shkronja e dytë le të jetë cilado shkronjë:

1 matched, error := regexp.MatchString("l.s", "O malet' e Shqipërisë e ju o lisat' e g\
2 jatë!")
3 }

Kërkojmë një substring që fillon me m dhe mbaron me t, nuk ka rëndësi sa shkronja janë ndërmjet.

1 matched, error := regexp.MatchString("m*t", "O malet' e Shqipërisë e ju o lisat' e g\
2 jatë!")

Kërkojmë substringun “fu” nëse është në fillim të stringut.

1 matched, error := regexp.MatchString("^fu", "Fushat e gjëra me lule, q'u kam ndër më\
2 nt dit' e natë!")

Kjo jep false sepse kërkimi është case-sensitive.

1 matched, error = regexp.MatchString("^Fu", "Fushat e gjëra me lule, q'u kam ndër mën\
2 t dit' e natë!")

Kjo rezulton në true.

Kërkojmë substringun “luar” në fund të stringut.

1 matched, error := regexp.MatchString("luar$", "Ju bregore bukuroshe e ju lumenjt' e \
2 kulluar")

A fillon stringu me “O”:

1 matched, error := regexp.MatchString("^O*", "O malet' e Shqipërisë e ju o lisat' e g\
2 jatë!")

REST API: Konceptet bazike

Web servisi është program që ka për qëllim realizimin e komunikimit ndërmjet kompjuterëve nëpërmes rrjetit, më konkretisht nëpërmes World Wide Web.

Informatat që shkëmbehen dërgohen dhe pranohen nëpërmes protokolit HTTP, sikurse në rastin e Web aplikacioneve, por në rastin e REST API theksi është tek të dhënat dhe jo te prezantimi në formë të HTML dokumentit.

REST API është pra backend servisi i cili e furnizon me të dhëna:

  • Front-end aplikacionet, siç janë aplikacionet e punuara me Angular, React, Vue, etj.
  • Aplikacionet mobile
  • Web serviset tjera

Tipet e Web serviseve

Web serviset kanë pësuar evolucion përgjatë historisë së Web-it.

Në të kaluarën më së shumti është përdorur SOAP protokoli (Simple Object Access Protocol), i cili për shkëmbimin e të dhënave ka përdorur formatin XML (eXtensible Markup Language). Edhe sot mund të hasen Web servise të bazuara në SOAP, mirëpo është evidente se dominon REST dhe formati JSON për të dhënat.

REST API

REST (Representational state transfer) është Web servis më i thjeshtë në krahasim me SOAP, dhe po ashtu edhe JSON formati është më i thjeshtë se formati XML.

REST API mundëson komunikimin ndërmjet sistemeve të ndryshme dhe dërgimin dhe pranimin e të dhënave në një formë më të thjeshtuar.

Çdo thirrje ndaj REST API realizohet nëpërmes HTTP metodave dhe URL-ve të caktuara. Rëndomë, një REST API përdoret për të dërguar të dhëna e më shpesh për të pranuar të dhëna nga një sistem për menaxhimin e databazave.

Çdo HTTP metodë i korrespondon një operacioni të caktuar në databazë, dhe çdo operacioni në databazë i korrespondon një URL.

Operacionet bazike ndaj databazës janë:

  • Create. Insertimi i të dhënave të reja në databazë.
  • Retrieve. Leximi i të dhënave nga databaza.
  • Update. Përditësimi i të dhënave në databazë.
  • Delete. Fshirja e të dhënave nga databaza.
REST SQL HTTP
Create INSERT POST
Retrieve SELECT GET
Update UPDATE PUT
Delete DELETE DELETE

Ka edhe dy HTTP metoda të tjera: PATCH dhe OPTIONS, përdorimin e të cilave do ta diskutojmë më vonë.

REST API: Karakteristikat

Karakteristikat e REST API

Stateless

REST API mund të jetë stateless, që e ka kuptimin se nuk e ruan gjendjen. P.sh. për çdo kërkesë (request) të dërguar, serveri kthen përgjigje (response) dhe çdo kërkesë vijuese që i shkon serverit konsiderohet si kërkesë që nuk ka kurrfarë lidhje me kërkesën paraprake. Pra, ka mundur të vijë nga përdoruesi i njëjtë apo një përdorues tjetër.

Cache

Në një aplikacion që pranon shumë kërkesa dhe kthen shumë përgjigje, mund të vërehet degradim i performansave, pra ngadalësim të punës. Kjo ndodh për shkak se prej momentit të dërgimit të një kërkese në serveri te aplikacioni ynë, aplikacioni duhet të kryejë një sërë operacionesh për të ardhur deri te kompletimi i përgjigjes që do ta kthejë, ku këtu përfshihen thirrje të shumëfishta në databazë, qasjen në fajlla, veprime të ndryshme të kalkulimit të vlerave për të përfunduar në gjenerimin e HTML apo JSON fajllit i cili do të jetë “produkt final” dhe që do të kthehet si përgjigje. I tërë ky proces mund të marrë shumë kohë për t’u kompletuar, ndërkohë që rezultatet e kthyera jo gjithmonë do të jenë të ndryshme nga ato që janë gjeneruar më parë.

Prandaj, një zgjidhje shumë e mirë që mundëson ngritjen e performancës së aplikacionit është nëpërmes përdorimit të cache, ku rezultatet e gjeneruara më parë ruhen në formën e vet finale, të gatshme për t’iu kthyer përdoruesit, pa pasur nevojë të kalohet nëpër tërë procesin e gjenerimit. Për shembull, kur kemi të bëjmë me të dhëna të cilat nuk ndryshojnë brenda një periudhe të caktuar, është shumë më e përshtatshme që të ruhen në formën finale për t’u servuar të gatshme, dhe jo që në çdo kërkesë të kalohet nëpër çdo hap të ekzekutimit të programit,

Kjo është e realizueshme për faktin se përgjigja e serverit gjithmonë është fajll, qoftë ai fajll në formatin tekstual, HTML, JSON, fajll binar, etj. dhe fajllat e gjeneruar mund të ruhen thjesht në formën statike për një periudhë të caktuar.

Sistem shumështresor

Një REST API mund të servohet nga shumë server të ndryshëm, ku serverët mund ta ndajnë ngarkesën ndërmjet vete, apo që një server të merret me pranimin e kërkesave, tjetri me gjenerimin e rezultateve, e një tjetër me kthimin e përgjigjesh drejt klientit i cili e ka dërguar kërkesën. Në këtë mënyrë mund ta zhvillojmë një Web servis i cili mund të veprojë i decentralizuar në kuptimin që funksionalitete të ndryshme mund të ofrohen nga servise të veçanta, të cilat ekzekutohen veçmas por megjithatë e formojnë një tërësi logjike dhe funksionale.

Kjo do të thotë që një REST API mund të komunikojë me një tjetër REST API, qoftë në sistemin e njëjtë, qoftë duke e përdorur një REST API ekstern, por që në instancë të fundit - të gjitha bashkë e formojnë një sistem.

Platformë agnostike

REST API ofron një ndërfaqe uniforme për komunikim, ku aspak nuk është e rëndësishme në çfarë platforme apo në cilën gjuhë programore është zhvilluar REST aplikacioni. Mund ta kemi një mikroservis të ndërtuar në Java i cili lehtësisht shkëmben të dhëna me një mikroservis tjetër të ndërtuar në Golang, Python, PHP apo ndonjë gjuhë tjetër.

Në vend se të zhvillohet si një Web aplikacion monolitik, një Web servis pra mund të zbërthehet në tërësi më të vogla të cilat mund të kenë dedikime të ndryshme por edhe të zhvillohen nga ekipe të ndryshme duke përdorur teknologji të ndryshme të cilat janë më të përshtatshme për punën konkrete, e prapë ai Web servis të veprojë si një tërësi kompakte përballë klientit të cilit në radhë të parë i interesojnë të dhënat dhe jo detajet e implementimit të aplikacionit.

REST API: HTTP metodat dhe status kodet

REST API përdor HTTP metodat e caktuara për të kryer veprime të caktuar ndaj resurseve apo grupi të resurseve.

Kur bëhet një kërkesë nga ana e klientit, ajo kërkesë duhet të përmbajë informatat vijuese:

  • REST verb (Metoda: GET, POST, PUT, DELETE, PATCH, OPTIONS)
  • Header (Meta informata)
  • Body (Informata)

Me HTTP metodën caktojmë çfarë veprimi do të kryhet ndaj një resursi specifik, p.sh. POST për insertim, GET për lexim, etj.

GET

E lexon një radhë (record) me të dhëna, apo një set radhësh nga serveri.

Në rast të suksesit, kthen status kodin 200.

Në rast të dështimit, kthen status kodin 404.

OPTIONS

Shfaq të gjitha REST operacionet që janë në dispozicion.

Në rast të suksesit, kthen status kodin 200.

POST

Krijon një resurs të ri apo set resursesh.

Në rast të suksesit, kthen status kodin 201.

Në rast të dështimit, kthen status kodet 404 ose 409.

PUT

Bën përditësimin apo zëvendësimin e radhës së zgjedhur.

Në rast të suksesit, kthen status kodin 202 ose 204.

Në rast të dështimit, kthen status kodin 404.

PATCH

Bën përditësimin/ndryshimin e radhës së zgjedhur.

Në rast të suksesit, kthen status kodin 202 ose 204.

Në rast të dështimit, kthen status kodin 404.

DELETE

E fshin resursin e zgjedhur.

Në rast të suksesit, kthen status kodin 200.

Në rast të dështimit, kthen status kodin 404.

Kodet për sukses dhe dështim janë HTTP kode, të cilat kthehen bashkë me përgjigjen. Këto kode janë informatë për klientin i cili e ka dërguar kërkesën për të ditur nëse kërkesa ka pasur sukses apo jo.

REST API: Metoda GET

Metoda GET bën leximin e një resursi të caktuar nga serveri. Për ta specifikuar resursit, metoda GET përdor disa tipe të URL kërkesave.

  • Parametrat nga ruta
  • Parametrat nga query string

Metoda GET përdoret sa herë e shënojmë një URL në browser, apo kur klikojmë në ndonjë link. Edhe dërgimi i të dhënave nga një formular mund të bëhet me metodën GET, por për shkak të disa kufizimeve që i imponon metoda GET, tek formularët rëndomë përdoret metoda POST.

Në rast të ekzekutimi të suksesshëm të kërkesës së bërë me metodën GET, serveri kthen status kodin 200.

Parametrat nga ruta janë parametrat që janë të shënuar si segmente të URL-së, p.sh.:

/post/102

Me këtë është specifikuar së kërkohet postimi me ID 102.

Kur aplikacioni e zbërthen këtë URL, do të kërkojë cili funksion duhet të thirret për ta shfaqur një postim, që më pas atij funksioni t’ia përcjellë ID-në e specifikuar, me çka aplikacioni do ta marrë informatën se çfarë informate të kërkojë specifikisht në databazë. Në rast se ekziston postimi me ID 102, databaza i kthen aplikacionit fushat e rekordit të caktuar nga tabela e databazës, të cilat të dhëna pastaj aplikacioni i “konverton” në të dhëna të formatit JSON apo si HTML, por nuk përjashtohen edhe formatet tjera.

Përveç parametrave të rutës, aplikacionit mund t’i dërgohen të dhëna edhe nëpërmes query string, gjegjësisht variablat e bashkangjitur në URL në formatin:

?variabli1=vlera1&variabli2=vlera2&variabli3=vlera3

Një URL mund t’i përmbajë edhe parametrat e rutës, edhe query string. Për shembull, mund të specifikojmë se na nevojiten postimet e kategorisë së caktuar nëpërmes parametrit të rutës, dhe cilën faqe të rezultateve e dëshirojmë - me anë të query string.

http://domaini.com/kategoria/5?faqja=3

Në këtë rast, aplikacioni e lexon segmentin e dytë të URL-së si ID të kategorisë, dhe nëse ajo kategori ka shumë postime, mund ta shfaqe faqe për faqe, ku variabli faqja do t’i shërbejë për ta shfaqur faqen e caktuar të rezultateve.

REST API: Metodat POST, PUT, PATCH, DELETE dhe OPTIONS

POST

Metoda POST përdoret për krijimin e një resursi të ri në server, p.sh. një postim apo një produkt të ri. Me krijim të resursit të ri rëndomë nënkuptojmë krijimin e një rekordi të ri në një tabelë të databazës.

Me POST nuk jemi të kufizuar në krijimin e vetëm një resursi, sepse në raste mund të kemi nevojë që nëpërmes metodës POST të dërgojmë një varg të dhënash të cilat në databazë do të ruhen si rekorde të shumta.

Gjatë insertimit të të dhënave në databazë, për çdo rekord do të krijohet një ID unike, e cila ID pastaj mund të përdoret për leximin e atyre resurseve. Nga aspekti i punës në databazë, një INSERT do të krijojë një apo më tepër rekorde, ku secili rekord do të ketë ID unike, rëndomë si inkrement. Më pas, me SELECT mund të zgjedhim një apo më tepër rekorde për lexim nga ana e aplikacionit, të cilat aplikacioni më pas do t’i kthejë si të dhëna p.sh. të formatit JSON.

PUT

Nëse dëshirojmë që ta përditësojmë një resurs, gjegjësisht ta ndryshojmë vlerën e një apo më tepër fushave të një rekordi, për dërgimin e kërkesës do ta përdorim metodën PUT, e cila metodë është e ngjashme me POST, me atë se do ta përdorim vetëm në rastet kur në databazë duhet të ndodhë një UPDATE. Rëndomë, UPDATE kryhet ndaj një resursi, gjegjësisht ndaj një rekordi, por kemi edhe raste kur veprojmë ndaj një seti të rekordeve.

Të dhënat me POST/PUT/PATCH mund të dërgohen edhe thjesht në formatin gjenerik tekstual, por në rastin e REST API, këto të dhëna do të jenë zakonisht në formatin JSON.

PATCH

Dallimi ndërmjet PUT dhe PATCH qëndron në faktin se me PUT bëjmë zëvendësimin e vlerave të tërë një rekordi me të dhëna të reja, ndërkaq me PATCH do të bëjmë përditësimin e fushave të caktuara, duke ua ruajtur të tjerave përmbajtjen e mëparshme. Në praktikë, është zgjedhja e programuesit se si konkretisht do t’i përdorë këto dy metoda.

PUT dhe PATCH kthejnë status kodin 200 në rast të suksesit, 404 kur nuk gjendet resursi i kërkuar.

DELETE

Metoda DELETE përdoret për fshirjen e një resursi, gjegjësisht fshirjen e një rekordi nga databaza. Është i ngjashëm me PUT por nuk përmban asnjë informatë përveç identifikatorit unik të resursit i cili duhet të fshihet. Pasi të bëhet fshirja, kërkesat ndaj atij resursi me metodën GET duhet ta kthejnë statusin 404. Kërkesat me metodën GET nuk ruhen në cache.

OPTIONS

Metoda OPTIONS përdoret rrallë, ndërsa përdoret për të treguar se cilat metoda janë në dispozicion për të vepruar ndaj një resursi. Kështu aplikacioni mund të dijë cilat metoda janë në dispozicion ndaj një resursi dhe kështu fillimisht e verifikon nëse një metodë e caktuar ekziston, që më pas të bëjë kërkesën për ekzekutim.

Thënë ndryshme, para se aplikacioni të veprojë ta zëmë me metodën PATCH, duke e thirrur metodën OPTIONS mund të shikojë nëse metoda PATCH është në listën e metodave me të cilat mund të veprohet ndaj atij resursi.

REST API: Status kodet

Në përpunim e sipër

HTTP Request/Headers/Response

Në përpunim e sipër

Web app: Web Programming Basics

Në përpunim e sipër

Web app: Web aplikacioni bazik

Në përpunim e sipër

Web app: Dizajnimi i aplikacionit

Në përpunim e sipër

Web app: Databases

Në përpunim e sipër

Web app: Forms

Në përpunim e sipër

Web app: Upload

Në përpunim e sipër

Web app: Templates

Në përpunim e sipër

Web app: Autentikimi

Në përpunim e sipër

Web app: Files

Në përpunim e sipër

Web app: Routing

Në përpunim e sipër

Web app: Middleware

Në përpunim e sipër

REST API (JSON and XML)

Në përpunim e sipër

Web app: Unit testing

Në përpunim e sipër

Gorilla

Në përpunim e sipër

Referencë koncize e funksioneve

ioutil

ReadDir(dirname string) ([]os.FileInfo, error)

https://golang.org/pkg/io/ioutil/#ReadDir

Tregon listën e nëndirektoriumeve të direktoriumit të specifikuar.

WriteFile(filename string, data []byte, perm os.FileMode) error

https://golang.org/pkg/io/ioutil/#WriteFile

Funksion për shkrimin e përmbajtjes në fajll.

os

Chdir(dir string) error

https://golang.org/pkg/os/#Chdir

E bën aktual direktoriumin e cekur në parametër.

Chmod(name string, mode FileMode) error

https://golang.org/pkg/os/#Chmod

E ndryshon modin e fajllit të cekur në parametrin e parë në modin e cekur në parametrin e dytë.

Chown(name string, uid, gid int) error

https://golang.org/pkg/os/#Chown

E ndryshon uid dhe gid të fajllit të cekur në parametrin e parë.

Chtimes(name string, atime time.Time, mtime time.Time) error

https://golang.org/pkg/os/#Chtimes

Ndryshon kohët e aksesit dhe modifikimit të fajllit të cekur.

Clearenv()

I fshin të gjithë variablat e ambientit.

Environ() []string

https://golang.org/pkg/os/#Environ

Kthen kopjen e stringut që reprezenton ambientin, në formën “çelësi=vlera”.

Executable() (string, error)

https://golang.org/pkg/os/#Executable

Kthen shtegun e fajllit ekzekutues që ka filluar procesin aktual.

Exit(code int)

https://golang.org/pkg/os/#Exit

Bën ndërprerjen e menjëhershme të ekzekutimit të programit.

Expand(s string, mapping func(string) string) string

https://golang.org/pkg/os/#Expand

ExpandEnv(s string) string

https://golang.org/pkg/os/#ExpandEnv

Getegid() int

https://golang.org/pkg/os/#Getegid

Getenv(key string) string

https://golang.org/pkg/os/#Getenv

Lexon vlerën e variablit të ambientit sipas çelësit të cekur.

Geteuid() int

https://golang.org/pkg/os/#Geteuid

Getgid() int

https://golang.org/pkg/os/#Getgid

Getgroups() ([]int, error)

https://golang.org/pkg/os/#Getgroups

Getpagesize() int

https://golang.org/pkg/os/#Getpagesize

Getpid() int

https://golang.org/pkg/os/#Getpid

Getppid() int

https://golang.org/pkg/os/#Getppid

Getuid() int

https://golang.org/pkg/os/#Getuid

Getwd() (dir string, err error)

https://golang.org/pkg/os/#Getwd

E kthen shtegun e plotë të direktoriumit aktual.

Hostname() (name string, err error)

https://golang.org/pkg/os/#Hostname

Kthen emrin e hostit të raportuar nga kerneli.

IsExist(err error) bool

https://golang.org/pkg/os/#IsExist

IsNotExist(err error) bool

https://golang.org/pkg/os/#IsNotExist

IsPathSeparator(c uint8) bool

https://golang.org/pkg/os/#IsPathSeparator

IsPermission(err error) bool

https://golang.org/pkg/os/#IsPermission

IsTimeout(err error) bool

https://golang.org/pkg/os/#IsTimeout

Lchown(name string, uid, gid int) error

https://golang.org/pkg/os/#Lchown

Link(oldname, newname string) error

https://golang.org/pkg/os/#Link

LookupEnv(key string) (string, bool)

https://golang.org/pkg/os/#LookupEnv

Mkdir(name string, perm FileMode) error

https://golang.org/pkg/os/#Mkdir

MkdirAll(path string, perm FileMode) error

https://golang.org/pkg/os/#MkdirAll

NewSyscallError(syscall string, err error) error

https://golang.org/pkg/os/#NewSyscallError

Pipe() (r *File, w *File, err error)

https://golang.org/pkg/os/#Pipe

Readlink(name string) (string, error)

https://golang.org/pkg/os/#Readlink

Remove(name string) error

https://golang.org/pkg/os/#Remove


OpenFile(name string, flag int, perm FileMode) (*File, error)

https://golang.org/pkg/os/#OpenFile

Hapja e një fajlli për I/O operacione.

Pyetje

Variablat

  1. Tipi rune është alias për tipin: int8, int16, int32 apo int64?
  2. Çfarë paraqet vlera e funksionit len() kur e përdorim me një string?
  3. Deklarimi i variablit në formën a := 5 bëhet: jashtë apo brenda një funksioni?
  4. Variablit të deklaruar brenda strukturës for, a mund t’i qasemi jashtë strukturës?

Strukturat

  1. Ku përdoret urdhëri fallthrough?
  2. Në cilat raste e përdorim urdhërin goto?

Përgjigjet

Shembuj

Shembuj të programeve në Golang.

Shembull: Krahasimi i dy vlerave të përafërta float

 1 package main
 2 
 3 import (
 4 	"fmt"
 5 	"math"
 6 )
 7 
 8 func main() {
 9 	a := 5.00000001
10 	b := 5.0000001
11 
12 	fmt.Println(a == b)
13 
14 	fmt.Println(equal(a, b, 1e-4))
15 }
16 
17 func equal(a, b, e float64) bool {
18 	return math.Abs(b-a) < e
19 }

https://play.golang.org/p/u_1gGij1C3J

Rezultati:

1 false
2 true

Shembull: Argumentet nga konzola

 1 package main
 2 
 3 import (
 4 	"fmt"
 5 	"os"
 6 )
 7 
 8 func main() {
 9 	for i := 1; i < len(os.Args); i++ {
10 		fmt.Println(os.Args[i])
11 	}
12 }

https://play.golang.org/p/SRY8024ZgKC

Programi kompajlohet, startohet duke i dhënë disa argumente të ndara me space.

args 12 54 7 23 9

Rezultati:

1 12
2 54
3 7
4 23
5 9

Shembull. Leximi i vlerës nga konzola

 1 package main
 2 
 3 import (
 4 	"bufio"
 5 	"fmt"
 6 	"os"
 7 )
 8 
 9 func main() {
10 	fmt.Println("Shënoje emrin:")
11 	b := bufio.NewReader(os.Stdin)
12 	line, _, err := b.ReadLine()
13 	if err != nil {
14 		fmt.Println(err)
15 	} else {
16 		fmt.Println("Ti ke shkruar:" + string(line))
17 	}
18 }

https://play.golang.org/p/oFrQnPdFP-Y

Rezultati:

1 Shënoje emrin:
2 Golang
3 Ti ke shkruar:Golang

Shembull: Shuma e numrave të lexuara nga konzola

 1 package main
 2 
 3 import (
 4 	"bufio"
 5 	"fmt"
 6 	"os"
 7 	"strconv"
 8 	"strings"
 9 )
10 
11 func main() {
12 	fmt.Println("Shëno disa numra të ndara me space:")
13 	teksti, err := bufio.NewReader(os.Stdin).ReadString('\n')
14 	if err == nil {
15 		var shuma float64
16 		for _, v := range strings.Fields(teksti) {
17 			i, err := strconv.ParseFloat(v, 64)
18 			if err != nil {
19 				fmt.Println(err)
20 			} else {
21 				shuma += i
22 			}
23 		}
24 		fmt.Println("Shuma e numrave:", shuma)
25 	}
26 }

https://play.golang.org/p/195fJpQ-x1V

Rezultati:

1 Rezultati: Shëno disa numra  ndara me space: 4 5 6 7
2 Shuma e numrave: 22

Shembull: Strukti me metodë

 1 package main
 2 
 3 import (
 4 	"fmt"
 5 )
 6 
 7 type Personi struct {
 8 	emri    string
 9 	mbiemri string
10 	mosha   int
11 }
12 
13 func (p Personi) teDhenat() {
14 	fmt.Printf("%s %s, mosha: %d\n", p.emri, p.mbiemri, p.mosha)
15 }
16 
17 func main() {
18 	anetar := Personi{
19 		emri:    "Arta",
20 		mbiemri: "Berisha",
21 		mosha:   30,
22 	}
23 
24 	anetar.teDhenat()
25 }

https://play.golang.org/p/mSC5yid0ea3

Rezultati:

1 Arta Berisha, mosha: 30

Nëse rezultatin e një funksioni nuk dëshirojmë ta lidhim me një mjet periferik dalës të caktuar, siç është rasti këtu me dërgimin e tekstit në konzolë, ne mund të bëjmë që një funksion të kthejë rezultat, e më pas thirrësi i atij funksioni (në këtë rast funksioni main()) të caktojë ku do të dërgohet: në konzolë, në databazë, si HTTP response, në fajll, etj.

 1 package main
 2 
 3 import (
 4 	"fmt"
 5 )
 6 
 7 type Personi struct {
 8 	emri    string
 9 	mbiemri string
10 	mosha   int
11 }
12 
13 func (p Personi) teDhenat() string {
14 	return fmt.Sprintf("%s %s, mosha: %d\n", p.emri, p.mbiemri, p.mosha)
15 }
16 
17 func main() {
18 	anetar := Personi{
19 		emri:    "Arta",
20 		mbiemri: "Berisha",
21 		mosha:   30,
22 	}
23 
24 	fmt.Println(anetar.teDhenat())
25 }

https://play.golang.org/p/FOw4vowVtmk

Rezultati:

1 Arta Berisha, mosha: 30

Shembull: Thirrja e metodës së struktit nga një funksion

 1 package main
 2 
 3 import (
 4 	"fmt"
 5 )
 6 
 7 type Anetari struct {
 8 	Emri   string
 9 	Emaili string
10 }
11 
12 func (a *Anetari) Mesazhi() string {
13 	return a.Emri + "<" + a.Emaili + ">"
14 }
15 
16 func dergoMesazhin(a Anetari) string {
17 	return a.Mesazhi()
18 }
19 
20 func main() {
21 	a := Anetari{
22 		Emri:   "Arben",
23 		Emaili: "arben@gmail.com",
24 	}
25 
26 	// nga metoda
27 	fmt.Println(a.Mesazhi())
28 
29 	// nga funksioni, funksioni prej metodës
30 	fmt.Println(dergoMesazhin(a))
31 }

https://play.golang.org/p/R83F_FaMDtE

Rezultati:

1 Arben<arben@gmail.com>
2 Arben<arben@gmail.com>

Shembull: Thirrja e një metode të struktit të brendshëm

 1 package main
 2 
 3 import (
 4 	"fmt"
 5 )
 6 
 7 type Anetari struct {
 8 	Emri   string
 9 	Emaili string
10 }
11 
12 func (a *Anetari) Dergo() string {
13 	return a.Emri + "<" + a.Emaili + ">"
14 }
15 
16 type Moderatori struct {
17 	Anetari
18 	Niveli int
19 }
20 
21 func main() {
22 	a := Moderatori{
23 		Anetari: Anetari{
24 			Emri:   "Arben",
25 			Emaili: "arben@gmail.com",
26 		},
27 		Niveli: 3,
28 	}
29 
30 	fmt.Println(a.Anetari.Dergo())
31 	// ose
32 	fmt.Println(a.Dergo())
33 }

https://play.golang.org/p/DNnFHzimbOj

Rezultati:

1 Arben<arben@gmail.com>
2 Arben<arben@gmail.com>

Shembull: Konvertimi i struktit në JSON

 1 package main
 2 
 3 import (
 4 	"encoding/json"
 5 	"fmt"
 6 )
 7 
 8 type libri struct {
 9 	Libri    int      `json:"libri"`
10 	Kapitujt []string `json:"kapitujt"`
11 }
12 
13 func main() {
14 	rezultati := &libri{
15 		Libri:    1,
16 		Kapitujt: []string{"Hyrje", "Funksionet", "Shembuj"}}
17 	jsonRezultati, _ := json.Marshal(rezultati)
18 	fmt.Println(string(jsonRezultati))
19 
20 }

https://play.golang.org/p/azPg-eIDB7P

Rezultati:

1 {"libri":1,"kapitujt":["Hyrje","Funksionet","Shembuj"]}

Shembull: Strukt në JSON, JSON në strukt

 1 package main
 2 
 3 import (
 4 	"encoding/json"
 5 	"fmt"
 6 )
 7 
 8 type Anetari struct {
 9 	Emri   string `json:"user_name"`
10 	Emaili string `json:"user_email"`
11 	Admin  bool   `json:"is_admin"`
12 }
13 
14 func main() {
15 	a := Anetari{
16 		Emri:   "Arben",
17 		Emaili: "arben@gmail.com",
18 		Admin:  true,
19 	}
20 
21 	// Nga struct ne JSON
22 
23 	b, _ := json.Marshal(a)
24 	fmt.Println(string(b))
25 
26 	// Nga JSON ne struct
27 
28 	var r Anetari
29 	json.Unmarshal(b, &r)
30 	fmt.Println("Emri: ", r.Emri)
31 	fmt.Println("Emaili: ", r.Emaili)
32 	fmt.Println("Admin: ", r.Admin)
33 }

https://play.golang.org/p/6J19r3TT7KP

Rezultati:

1 {"user_name":"Arben","user_email":"arben@gmail.com","is_admin":true}
2 Emri:  Arben
3 Emaili:  arben@gmail.com
4 Admin:  true

Shembull: Leximi i JSON fajllit me parsim me strukt

 1 package main
 2 
 3 import (
 4 	"encoding/json"
 5 	"fmt"
 6 	"io/ioutil"
 7 	"os"
 8 	"strconv"
 9 )
10 
11 type Anetaret struct {
12 	Anetaret []Anetari `json:"anetaret"`
13 }
14 
15 type Anetari struct {
16 	Emri     string   `json:"emri"`
17 	Mosha    int      `json:"mosha"`
18 	Gjinia   string   `json:"gjinia"`
19 	Kontakti Kontakti `json:"kontakti"`
20 }
21 
22 type Kontakti struct {
23 	Emaili   string `json:"emaili"`
24 	Telefoni string `json:"telefoni"`
25 }
26 
27 func main() {
28 	fajlli, err := os.Open("anetaret.json")
29 	if err != nil {
30 		fmt.Println(err)
31 	}
32 	defer fajlli.Close()
33 
34 	teDhenat, _ := ioutil.ReadAll(fajlli)
35 	var anetaret Anetaret
36 
37 	json.Unmarshal(teDhenat, &anetaret)
38 	for i := 0; i < len(anetaret.Anetaret); i++ {
39 		fmt.Println("Emri: " + anetaret.Anetaret[i].Emri)
40 		fmt.Println("Mosha: " + strconv.Itoa(anetaret.Anetaret[i].Mosha))
41 		fmt.Println("Gjinia: " + anetaret.Anetaret[i].Gjinia)
42 		fmt.Println("Emaili: " + anetaret.Anetaret[i].Kontakti.Emaili)
43 		fmt.Println("Telefoni: " + anetaret.Anetaret[i].Kontakti.Telefoni)
44 	}
45 }

anetaret.json

 1 {
 2   "anetaret": [
 3     {
 4       "emri": "Alban",
 5       "mosha": 23,
 6       "gjinia": "m",
 7       "kontakti": {
 8         "emaili": "alban@gmail.com",
 9         "telefoni": "049111222"
10       }
11     },
12     {
13       "emri": "Teuta",
14       "mosha": 17,
15       "gjinia": "f",
16       "kontakti": {
17         "emaili": "teuta@gmail.com",
18         "telefoni": "044222333"
19       }
20     }
21   ]
22 }

https://play.golang.org/p/a5H7kzLvC_4

Rezultati:

 1 Emri: Alban
 2 Mosha: 23
 3 Gjinia: m
 4 Emaili: alban@gmail.com
 5 Telefoni: 049111222
 6 Emri: Teuta
 7 Mosha: 17
 8 Gjinia: f
 9 Emaili: teuta@gmail.com
10 Telefoni: 044222333

Shembull: Maps - Qytetet dhe numri postar

 1 package main
 2 
 3 import (
 4 	"fmt"
 5 )
 6 
 7 var qytetet = []string{"Prishtinë", "Prizren", "Ferizaj", "Gjakovë", "Gjilan"}
 8 var kodet = []int{10000, 20000, 70000, 50000, 60000}
 9 var lista = map[int]string{}
10 
11 func main() {
12 	for i := 0; i < 5; i++ {
13 		fmt.Println(kodet[i], "\t", qytetet[i])
14 		lista[kodet[i]] = qytetet[i]
15 	}
16 	fmt.Println(lista)
17 }

https://play.golang.org/p/yBRomHWw_6L

Rezultati:

1 10000 	 Prishtinë
2 20000 	 Prizren
3 70000 	 Ferizaj
4 50000 	 Gjakovë
5 60000 	 Gjilan
6 map[10000:Prishtinë 20000:Prizren 50000:Gjakovë 60000:Gjilan 70000:Ferizaj]

Shembull: Map me listë shtetesh dhe numër të banorëve

 1 package main
 2 
 3 import (
 4 	"fmt"
 5 	"strconv"
 6 )
 7 
 8 func main() {
 9 	shtetet := map[string]int{
10 		"China":         1433783686,
11 		"India":         1366417754,
12 		"United States": 329064917,
13 		"Indonesia":     270625568,
14 		"Pakistan":      216565318,
15 		"Brazil":        211049527,
16 		"Nigeria":       200963599,
17 		"Bangladesh":    163046161,
18 		"Russia":        145872256,
19 		"Mexico":        127575529,
20 	}
21 
22 	fmt.Println(shtetet)
23 
24 	for shteti, banore := range shtetet {
25 		fmt.Println(shteti + ":" + strconv.Itoa(banore))
26 	}
27 
28 	fmt.Println("SHBA ka " + strconv.Itoa(shtetet["United States"]) + " banorë")
29 
30 	// Rezultati është 0 sepse Kosova nuk është në listë
31 	fmt.Println("Kosova ka " + strconv.Itoa(shtetet["Kosova"]) + " banorë")
32 
33 	// Por duhet ta dallojmë a nuk është në listë apo ka 0 banorë
34 	// ok kthen true nëse Kosova është në listë, false nëse nuk është
35 	n, ok := shtetet["Kosova"]
36 
37 	if !ok {
38 		// Nëse Kosova nuk është në listë
39 		fmt.Println("Kosova nuk është në listë")
40 	} else {
41 		// Nëse Kosova është në listë
42 		fmt.Println("Kosova ka " + strconv.Itoa(n) + " banorë")
43 	}
44 
45 	// Shtimi i një anëtari të ri
46 	shtetet["Kosova"] = 2000000
47 	fmt.Println("Kosova ka " + strconv.Itoa(shtetet["Kosova"]) + " banorë")
48 }

https://play.golang.org/p/F9nR6m6gSdy

Rezultati:

 1 map[Bangladesh:163046161 Brazil:211049527 India:1366417754 Indonesia:270625568 China\
 2 :1433783686 Mexico:127575529 Nigeria:200963599 Pakistan:216565318 Russia:145872256 U\
 3 nited States:329064917]
 4 Indonesia:270625568
 5 Nigeria:200963599
 6 Russia:145872256
 7 India:1366417754
 8 United States:329064917
 9 Brazil:211049527
10 Bangladesh:163046161
11 Mexico:127575529
12 Kina:1433783686
13 Pakistan:216565318
14 SHBA ka 329064917 banorë
15 Kosova ka 0 banorë
16 Kosova nuk është  listë
17 Kosova ka 2000000 banorë

Mapën e njëjtë mund ta konvertojmë në JSON.

 1 package main
 2 
 3 import (
 4 	"encoding/json"
 5 	"fmt"
 6 	"net/http"
 7 )
 8 
 9 var shtetet = map[string]int{
10 	"China":          1433783686,
11 	"India":         1366417754,
12 	"United States": 329064917,
13 	"Indonesia":     270625568,
14 	"Pakistan":      216565318,
15 	"Brazil":        211049527,
16 	"Nigeria":       200963599,
17 	"Bangladesh":    163046161,
18 	"Russia":        145872256,
19 	"Mexico":        127575529,
20 }
21 
22 func main() {
23 	fmt.Println("Duke e startuar serverin")
24 	http.HandleFunc("/lista", listaShteteve)
25 	err := http.ListenAndServe(":8080", nil)
26 	if err != nil {
27 		panic(err)
28 	}
29 	fmt.Println("test")
30 }
31 
32 func listaShteteve(w http.ResponseWriter, r *http.Request) {
33 	jShtetet, _ := json.Marshal(shtetet)
34 	w.Header().Set("Content-Type", "application/json")
35 	w.Write([]byte(jShtetet))
36 }

https://play.golang.org/p/wSf9iD-Xu2D

Aplikacioni thirret nga http://localhost:8080/lista

Rezultati:

1 {"Bangladesh":163046161,"Brazil":211049527,"India":1366417754,"Indonesia":270625568,\
2 "China":1433783686,"Mexico":127575529,"Nigeria":200963599,"Pakistan":216565318,"Russ\
3 ia":145872256,"United States":329064917}

Shembull: Bubble Sort

 1 package main
 2 
 3 import (
 4 	"fmt"
 5 )
 6 
 7 var list = []int{1, 900, 2, 4, 8, 6, 3, 2, 4, 8, 6, 7, 3, 0, -300}
 8 
 9 func bubbleSort(input []int) []int {
10 	n := len(input)
11 	swapped := true
12 	for swapped {
13 		swapped = false
14 		for i := 1; i < n; i++ {
15 			if input[i-1] > input[i] {
16 				input[i], input[i-1] = input[i-1], input[i]
17 				swapped = true
18 			}
19 		}
20 
21 		n--
22 	}
23 
24 	return input
25 }
26 
27 func main() {
28 	fmt.Println("Lista origjinale:")
29 	fmt.Println(list)
30 	fmt.Println("Lista e sortuar:")
31 	fmt.Println(bubbleSort(list))
32 }

https://play.golang.org/p/T_igbGHGwL4

Rezultati:

1 Lista origjinale:
2 [1 900 2 4 8 6 3 2 4 8 6 7 3 0 -300]
3 Lista e sortuar:
4 [-300 0 1 2 2 3 3 4 4 6 6 7 8 8 900]

Shembull: Fizzbuzz

 1 package main
 2 
 3 import (
 4 	"fmt"
 5 )
 6 
 7 func main() {
 8 	for i := 1; i < 100; i++ {
 9 		switch {
10 		case i%15 == 0:
11 			fmt.Println("fizzbuzz")
12 		case i%3 == 0:
13 			fmt.Println("fizz")
14 		case i%5 == 0:
15 			fmt.Println("buzz")
16 		default:
17 			fmt.Println(i)
18 		}
19 	}
20 }

https://play.golang.org/p/2u78tjXvVYP

Rezultati:

 1 1
 2 2
 3 fizz
 4 4
 5 buzz
 6 fizz
 7 7
 8 8
 9 fizz
10 buzz
11 11
12 fizz
13 13
14 14
15 fizzbuzz
16 ...

Shembull: Gjenerimi i sha256 hash

 1 package main
 2 
 3 import (
 4 	"crypto/sha256"
 5 	"fmt"
 6 )
 7 
 8 func main() {
 9 	teksti := "Lorem Ipsum dolor sit Amet"
10 	sha_256 := sha256.New()
11 	sha_256.Write([]byte(teksti))
12 
13 	fmt.Printf("Teksti:\t\t%v\n", teksti)
14 	fmt.Printf("sha256:\t\t%x\n", sha_256.Sum(nil))
15 
16 }

https://play.golang.org/p/sGbSxbLLTVS

Rezultati:

1 Teksti:		Lorem Ipsum dolor sit Amet
2 sha256:		eb7a03c377c28da97ae97884582e6bd07fa44724af99798b42593355e39f82cb

Shembull: Gjeneratori i stringut të rastësishëm

Të dy shembujt të testohen lokalisht sepse në Playground do të fitohen gjithmonë rezultate të njëjta.

 1 package main
 2 
 3 import (
 4 	"fmt"
 5 	"math/rand"
 6 	"time"
 7 )
 8 
 9 func main() {
10 	// Definohet gjatesia e deshiruar e stringut
11 	length := 10
12 
13 	// Caktohet stringu me te gjitha karakteret e lejuara
14 	// ne stringun e rastesishem
15 	const set = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789"
16 
17 	// Lexohet koha aktuale e sistemit ne forme te nanosekondave
18 	var nanosec int64 = time.Now().UnixNano()
19 
20 	// Gjeneratorit te numrit pseudo te rastesishem i jepet ai numer si seed
21 	var src = rand.NewSource(nanosec)
22 
23 	// Definohet nje byte slice per karakteret qe do te zgjedhen nga karakter set,
24 	// aq elemente sa eshte gjatesia e deshiruar e stringut
25 	b := make([]byte, length)
26 
27 	var c int64
28 	// Cikli perseritet aq here sa eshte gjatesia e deshiruar e stringut
29 	for i := range b {
30 		// Numri i rastesishem c eshte integer i madh (64 bits)
31 		c = src.Int63()
32 		fmt.Printf("Numri i rastesishem: %v \n", c)
33 
34 		// Per te mos qene jashte vlerave 0-61, sepse gjatesia e karakter setit eshte 62
35 		// behet modulus me 62, pra numrin e karaktereve te karakter setit
36 		d := c % int64(len(set))
37 		fmt.Printf("Modulusi me 62: %v \n", d)
38 
39 		// Nga karakter seti merret karakteri nga pozita qe tregon d dhe vendoset
40 		// si vlere e anetarit vijues te byte slice
41 		fmt.Printf("Merre karakterin nga pozita %v qe eshte: %v \n", d, string(set[d]))
42 		b[i] = set[d]
43 	}
44 
45 	fmt.Printf("Stringu i rastesishem: %v", string(b))
46 }

https://play.golang.org/p/IAjuawbb-Cf

Rezultati:

 1 Numri i rastesishem: 4601851300195147788
 2 Modulusi me 62: 0
 3 Merre karakterin nga pozita 0 qe eshte: a
 4 Numri i rastesishem: 4647152587264684499
 5 Modulusi me 62: 3
 6 Merre karakterin nga pozita 35 qe eshte: J
 7 . . .
 8 Numri i rastesishem: 2086826187330921635
 9 Modulusi me 62: 57
10 Merre karakterin nga pozita 57 qe eshte: 5
11 Stringu i rastesishem: aJFLa7XPH5

Detyrë:

Të rishkruhet si funksion.

 1 package main
 2 
 3 import (
 4 	"fmt"
 5 	"math/rand"
 6 	"time"
 7 )
 8 
 9 func main() {
10 	fmt.Println(randString(15))
11 }
12 
13 func randString(length int) string {
14 	const set = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789"
15 	var nanosec int64 = time.Now().UnixNano()
16 	var src = rand.NewSource(nanosec)
17 	b := make([]byte, length)
18 	var c int64
19 	for i := range b {
20 		c = src.Int63()
21 		d := c % int64(len(set))
22 		b[i] = set[d]
23 	}
24 
25 	return string(b)
26 }

https://play.golang.org/p/5FsV-l1_dyJ

Rezultati:

1 aJFLa7XPH59kWSX

Shembull: Fibonacci sekuenca

 1 package main
 2 
 3 import "fmt"
 4 
 5 func main() {
 6 	var fib = []int{0, 1}
 7 	var s int
 8 	for i := 1; i <= 18; i++ {
 9 		s = fib[i-1] + fib[i]
10 		fib = append(fib, s)
11 	}
12 
13 	fmt.Println(fib)
14 }

https://play.golang.org/p/mSt9Ezn5E7G

Rezultati:

1 [0 1 1 2 3 5 8 13 21 34 55 89 144 233 377 610 987 1597 2584 4181]

Shembull: Numri prim

Ky program tregon numrin prim të caktuar, në rastin konkret numrin e 20-të, i cili është 71. Duke e ndryshuar vlerën e argumentit të funksionit Prime, mund ta zgjedhim cilindo numër tjetër prim sipas renditjes.

 1 package main
 2 
 3 import (
 4 	"fmt"
 5 	"math"
 6 )
 7 
 8 func Prime(n int) int {
 9 	var lista = []int{2}
10 	prime := true
11 	numri := 3
12 	rrKatrore := 0
13 	for len(lista) < n {
14 		rrKatrore = int(math.Sqrt(float64(numri)))
15 		for i := 0; i < len(lista); i++ {
16 			if numri%lista[i] == 0 {
17 				prime = false
18 			}
19 			if lista[i] > rrKatrore {
20 				i = len(lista)
21 			}
22 		}
23 		if prime == true {
24 			lista = append(lista, numri)
25 		} else {
26 			prime = true
27 		}
28 		numri = numri + 2
29 	}
30 	return lista[n-1]
31 }
32 
33 func main() {
34 	fmt.Printf("%v\n", Prime(20))
35 }

https://play.golang.org/p/sGsuM-Ujijs

Rezultati:

1 71

Shembull: Shkrimi dhe leximi nga fajlli i formatit CSV

 1 package main
 2 
 3 import (
 4 	"encoding/csv"
 5 	"fmt"
 6 	"os"
 7 	"strconv"
 8 )
 9 
10 type Postimi struct {
11 	Autori  string
12 	Id      int
13 	Titulli string
14 }
15 
16 var csvFajlli string = "postimet.csv"
17 
18 func main() {
19 	postimet := []Postimi{
20 		{Id: 1, Autori: "Gabriel Garcia Marquez", Titulli: "Për dashurinë dhe demonë të tj\
21 erë"},
22 		{Id: 2, Autori: "Isabel Allende", Titulli: "Shuma e ditëve"},
23 		{Id: 3, Autori: "Dan Brown", Titulli: "Origjina"},
24 	}
25 
26 	//  Shkrimi
27 	CsvWrite(postimet)
28 
29 	// Leximi
30 	CsvRead()
31 }
32 
33 func CsvWrite(Postimet []Postimi) {
34 	csvFile, err := os.Create(csvFajlli)
35 	if err != nil {
36 		panic(err)
37 	}
38 	defer csvFile.Close()
39 
40 	writer := csv.NewWriter(csvFile)
41 	for _, postim := range Postimet {
42 		rreshti := []string{strconv.Itoa(postim.Id), postim.Autori, postim.Titulli}
43 		err := writer.Write(rreshti)
44 		if err != nil {
45 			panic(err)
46 		}
47 	}
48 	writer.Flush()
49 }
50 
51 func CsvRead() {
52 	fajlli, err := os.Open(csvFajlli)
53 	if err != nil {
54 		panic(err)
55 	}
56 	defer fajlli.Close()
57 
58 	reader := csv.NewReader(fajlli)
59 	reader.FieldsPerRecord = -1
60 
61 	rreshtat, err := reader.ReadAll()
62 	if err != nil {
63 		panic(err)
64 	}
65 
66 	var postimet []Postimi
67 
68 	for _, vlera := range rreshtat {
69 		id, _ := strconv.Atoi(vlera[0])
70 		rreshti := Postimi{Id: id, Autori: vlera[1], Titulli: vlera[2]}
71 
72 		postimet = append(postimet, rreshti)
73 	}
74 
75 	fmt.Println(postimet)
76 	// Segmenti i strukteve mund të procesohet më tej...
77 }

https://play.golang.org/p/VUqfU1UwK73

Rezultati:

1 [{Gabriel Garcia Marquez 1  Për dashurinë dhe demonë  tjerë} {Isabel Allende 2 Shu\
2 ma e ditëve} {Dan Brown 3 Origjina}]

Shembull: Enkodimi/dekodimi i map në JSON

 1 package main
 2 
 3 import (
 4 	"encoding/json"
 5 	"fmt"
 6 )
 7 
 8 func main() {
 9 
10 	// enkodimi
11 	harta := map[string]int{"Hyrje": 3, "Funksionet": 11, "Shembuj": 77}
12 	fmt.Printf("%T\n", harta)
13 	fmt.Println(harta)
14 	fmt.Println("--------")
15 
16 	jHarta, _ := json.Marshal(harta)
17 	fmt.Printf("%T\n", jHarta)
18 	fmt.Println(string(jHarta))
19 	fmt.Println("--------")
20 
21 	// dekodimi
22 	b := []byte(jHarta)
23 	var d map[string]interface{}
24 	if err := json.Unmarshal(b, &d); err != nil {
25 		panic(err)
26 	}
27 	fmt.Printf("%T\n", d)
28 	fmt.Println(d)
29 
30 }

https://play.golang.org/p/8SF21flSep5

Rezultati:

1 map[string]int
2 map[Funksionet:11 Hyrje:3 Shembuj:77]
3 \--------
4 []uint8
5 {"Funksionet":11,"Hyrje":3,"Shembuj":77}
6 \--------
7 map[string]interface {}
8 map[Funksionet:11 Hyrje:3 Shembuj:77]

Shembull: Nga strukt në XML

 1 package main
 2 
 3 import (
 4 	"encoding/xml"
 5 	"fmt"
 6 	"net/http"
 7 )
 8 
 9 type Personi struct {
10 	Emri      string
11 	Telefonat []string `xml:"Telefonat>Telefoni"`
12 }
13 
14 func main() {
15 	http.HandleFunc("/xml", shfaqXML)
16 	err := http.ListenAndServe(":8000", nil)
17 	if err != nil {
18 		fmt.Println(err.Error())
19 		return
20 	}
21 }
22 
23 func shfaqXML(w http.ResponseWriter, r *http.Request) {
24 	personi := Personi{"Alban", []string{"044123456", "049234567"}}
25 
26 	x, err := xml.MarshalIndent(personi, "", "  ")
27 	if err != nil {
28 		http.Error(w, err.Error(), http.StatusInternalServerError)
29 		return
30 	}
31 
32 	w.Header().Set("Content-Type", "application/xml")
33 	w.Write(x)
34 }

https://play.golang.org/p/dWlReYOqbhE

Rezultati:

1 <Personi>
2   <Emri>Alban</Emri>
3   <Telefonat>
4     <Telefoni>044123456</Telefoni>
5     <Telefoni>049234567</Telefoni>
6   </Telefonat>
7 </Personi>

Shembull: Enkodimi/dekodimi me base64

 1 package main
 2 
 3 import (
 4 	"encoding/base64"
 5 	"fmt"
 6 )
 7 
 8 func main() {
 9 	teksti := "O malet' e Shqipërisë e ju o lisat' e gjatë!"
10 	fmt.Printf("Teksti: '%s'\n", teksti)
11 
12 	// Enkodimi
13 	enkoduar := base64.StdEncoding.EncodeToString([]byte(teksti))
14 	fmt.Println("I enkoduar: ", enkoduar)
15 
16 	// Dekodimi
17 	dekoduar, _ := base64.StdEncoding.DecodeString(enkoduar)
18 	fmt.Printf("I dekoduar: '%s'  \n", string(dekoduar))
19 }

https://play.golang.org/p/HsbNHCEHuO0

Rezultati:

1 Teksti: 'O malet' e Shqipërisë e ju o lisat' e gjatë!'
2 I enkoduar:  TyBtYWxldCcgZSBTaHFpcMOrcmlzw6sgZSBqdSBvIGxpc2F0JyBlIGdqYXTDqyE=
3 I dekoduar: 'O malet' e Shqipërisë e ju o lisat' e gjatë!'`

Shembull: Enkriptim/dekriptim me XOR

 1 package main
 2 
 3 import (
 4 	"fmt"
 5 )
 6 
 7 func main() {
 8 	text := "Google"
 9 	key := "123"
10 
11 	e := Xor(text, key)
12 	fmt.Println(text + " : " + e)
13 
14 	d := Xor(e, key)
15 	fmt.Println(e + " : " + d)
16 }
17 
18 func Xor(input, key string) (output string) {
19 	for i := 0; i < len(input); i++ {
20 		output += string(input[i] ^ key[i%len(key)])
21 	}
22 	return
23 }

https://play.golang.org/p/jvpwzyzjsCl

Rezultati:

1 Google : v]\V^V
2 v]\V^V : Google

Shembull: Bitwise shift operator me enumerated constants

Në programin vijues, fillimisht formohet një listë konstantash nëpërmes vlerës speciale iota, e cila by default gjeneron serinë e vlerave: 0, 1, 2, 3,… Mirëpo, në këtë rast, ne do ta manipulojmë vlerën e iota me bitwise shift operators. Me këtë do ta fitojmë një seri prej: 0, 1, 2, 4, pra 0, 20, 21 dhe 22.

Qëllimi është që të fitohen vlera të kombinuara të 0, 1, 2 dhe 4, njëjtë sikurse e bën chmod në Linux. Kombinimin e dy vlerave në nivel të bitave do ta bëjmë me operatorin OR (|), me çka në fakt do të fitojmë numër.

Për shembull, operacioni OR ndaj Read (binar 100) dhe Write (binar 010), është 110, apo 6 në sistemin decimal. Kësisoj do të jemi në gjendje të ndërtojmë struktura degëzuese me if ose switch, ku do të mund t’i përdorim emrat e konstantave dhe operatorin OR për të verifikuar nëse përdoruesit i takon e drejta ekzekutimit, shkrimit apo leximit.

 1 package main
 2 
 3 import (
 4 	"fmt"
 5 )
 6 
 7 const (
 8 	isGuest = 1 << iota >> 1
 9 	canExecute
10 	canWrite
11 	canRead
12 )
13 
14 func main() {
15 	fmt.Printf("%03b = %v - No priviledges\n", isGuest, isGuest)
16 	fmt.Printf("%03b = %v - Execute\n", canExecute, canExecute)
17 	fmt.Printf("%03b = %v - Write\n", canWrite, canWrite)
18 	fmt.Printf("%03b = %v - Read\n", canRead, canRead)
19 	fmt.Printf("%03b = %v - Execute & Write\n", canExecute|canWrite, canExecute|canWrit\
20 e)
21 	fmt.Printf("%03b = %v - Execute & Read\n", canExecute|canRead, canExecute|canRead)
22 	fmt.Printf("%03b = %v - Write & Read\n", canWrite|canRead, canWrite|canRead)
23 	fmt.Printf("%03b = %v - Execute & Write & Read\n", canExecute|canWrite|canRead, can\
24 Execute|canWrite|canRead)
25 
26 	level := canRead | canWrite
27 
28 	switch level {
29 	case isGuest:
30 		fmt.Println("You have no priviledges")
31 	case canRead:
32 		fmt.Println("You can Read")
33 	case canWrite:
34 		fmt.Println("You can Write")
35 	case canExecute:
36 		fmt.Println("You can Execute")
37 	case canWrite | canRead:
38 		fmt.Println("You can Read & Write")
39 	case canExecute | canRead:
40 		fmt.Println("You can Read & Execute")
41 	case canExecute | canWrite:
42 		fmt.Println("You can Write & Execute")
43 	case canExecute | canWrite | canRead:
44 		fmt.Println("You can Read, Write, Execute")
45 	}
46 }

https://play.golang.org/p/KtQ-XWX4mBg

Rezultati:

1 000 = 0 - No priviledges
2 001 = 1 - Execute
3 010 = 2 - Write
4 100 = 4 - Read
5 011 = 3 - Execute & Write
6 101 = 5 - Execute & Read
7 110 = 6 - Write & Read
8 111 = 7 - Execute & Write & Read
9 You can Read & Write

Bitwise left shift për 1 pozitë e ngrit iota-n në eksponentin për një më të lartë të numrit 2. Pra, nëse vlera binare është 10, bitwise left shift e bën 100. Bitwise right shift e bën të kundërtën - p.sh. numrin binar 100 e kthen në 10.

Meqenëse iota me bitwise left shift fiton vlerë fillestare 1, ndërsa ne dëshirojmë të fillojë nga zeroja (p.sh. për përdoruesit që s’kanë asnjë privilegj), atëherë rezultatit të bitwise left shift i bëjmë bitwise right shift, gjë që do të mundësojë që vlera e parë e iota të jetë zero, por vlerat në vijim të jenë eksponente të numrit 2. Pra, kjo është arsyeja e përdorimit të shprehjes:

1 	isGuest =1 << iota >> 1

e cila në vend të serisë: 0, 1, 2, 3,…, që do të fitohej me:

1 	isGuest = iota

apo serisë: 1, 2, 4, 8,…, që do të fitohej me:

1 	isGuest =1 << iota

na e gjeneron serinë: 0, 1, 2, 4,…, për çdo konstantë vijuese në listë.

Duke e përdorur serinë 0, 1, 2, 4, jemi të siguruar se nuk do të ketë dy kombinime me rezultat të njëjtë të dy apo tri vlerave të ndryshme, sepse në çfarëdo mënyre që t’i mbledhim këta numra, gjithmonë kemi rezultat unik për secilin kombinim.

1 variabli = canExecute|canRead

ndërsa Go, operacionin canExecute|canRead në mënyrë interne do ta shohë si numër 6, por kjo për neve nuk ka rëndësi, sepse edhe krahasimin do ta bëjmë në mënyrë të njëjtë, pa iu referuar numrit konkret:

1 if variabli == canExecute|canRead {
2 ...
3 }

Shembull: ACL sistem me enumerated constants

 1 package main
 2 
 3 import "fmt"
 4 
 5 const (
 6 	rView = 1 << iota
 7 	rCreate
 8 	rRetrieve
 9 	rUpdate
10 	rDelete
11 	rStats
12 	rLogs
13 )
14 
15 func main() {
16 	Acl := rView | rDelete | rLogs | rStats
17 
18 	fmt.Println("User's privileges")
19 	fmt.Printf("View: %v\n", Acl&rView != 0)
20 	fmt.Printf("Create: %v\n", Acl&rCreate != 0)
21 	fmt.Printf("Retrieve: %v\n", Acl&rRetrieve != 0)
22 	fmt.Printf("Update: %v\n", Acl&rUpdate != 0)
23 	fmt.Printf("Delete: %v\n", Acl&rDelete != 0)
24 	fmt.Printf("Stats: %v\n", Acl&rStats != 0)
25 	fmt.Printf("Logs: %v\n", Acl&rLogs != 0)
26 
27 	if Acl&rDelete != 0 {
28 		fmt.Println("You can delete the post")
29 	}
30 }

https://play.golang.org/p/ukaaV5ggRdQ

Rezultati:

1 User's privileges
2 View: true
3 Create: false
4 Retrieve: false
5 Update: false
6 Delete: true
7 Stats: true
8 Logs: true
9 You can delete the post

Me këtë program, do t’i shfrytëzojmë bitat e konstantave të enumeruara si flag për privilegje të caktuara:

1     View: 00000001 ( 1)
2   Create: 00000010 ( 2)
3 Retrieve: 00000100 ( 4)
4   Update: 00001000 ( 8)
5   Delete: 00010000 (16)
6    Stats: 00100000 (32)
7     Logs: 01000000 (64)

Në rreshtin 16, variablit acl i japim vlerë asisoj që i cekim privilegjet e dëshiruara duke i ndarë me operatorin OR (|), me çka në fakt formohet një numër me bita të vlerës 1 në pozitat që i korrrespondojnë një privilegji të caktuar.

Kështu, për t’i caktuar një përdoruesi privilegjet rView | rDelete | rLogs | rStats formohet numri binar 01110001 apo 113 decimal.

Kur diku në aplikacion na duhet të verifikojmë nëse përdoruesi e ka NDONJËRIN prej privilegjeve, atëherë variablit që përmban privilegjet e tij (variabli Acl në këtë rast) duhet në njëfarë mënyre ta krahasojmë me privilegjin individual që na intereson, p.sh. a ka të drejtë ai përdorues të fshijë postime.

Për ta realizuar këtë, ndaj variablit Acl veprojmë me operatorin & dhe konstantën e privilegjit që na intereson, pastaj shikojmë nëse nuk është zero:

1 if (Acl&rDelete != 0) {
2 	// Fshije postimin
3 }

Çka në fakt ndodh këtu? Nëse variabli Acl përmban bit me vlerë 1 për privilegjin e fshirjes, edhe konstanta për privilegjin e fshirjes (rDelete) përmban bit me vlerë 1 në po atë pozitë.

Për Acl := rView | rDelete | rLogs | rStats, vlera binare është 01110001, ndërsa për rDelete është 00010000. Shohim se biti i pestë nga e djathta ka vlerën 1 edhe te variabli Acl edhe te konstanta rDelete.

1 01110001
2 AND
3 00010000
4 =
5 00010000

Meqenëse variabli Acl dhe konstanta rDelete në bitin e njëjtë kanë 1, rezultati nuk mund të jetë baras me zero; prandaj dhe verifikojmë nëse rezultati i shprehjes Acl&rDelete nuk është zero:

1 	if Acl&rDelete != 0 {
2 		fmt.Println("You can update the post")
3 	}

Pra, me këtë shprehje konstatojmë nëse privilegji për fshirje është i përfshirë në privilegjet e caktuara për këtë përdorues.

Shprehjen e krahasimit e bartim në një funksion në vete, variablin Acl e deklarojmë jashtë blloqeve të funksioneve ashtu që të jetë i qasshëm për funksionet, dhe kështu e fitojmë një kod më elegant:

 1 package main
 2 
 3 import "fmt"
 4 
 5 const (
 6 	rView = 1 << iota
 7 	rCreate
 8 	rRetrieve
 9 	rUpdate
10 	rDelete
11 	rStats
12 	rLogs
13 )
14 
15 var Acl int
16 
17 func main() {
18 	Acl = rView | rDelete | rLogs | rStats
19 
20 	if has(rDelete) {
21 		fmt.Println("You can delete the post")
22 	}
23 }
24 
25 func has(privilege int) bool {
26 	return Acl&privilege != 0
27 }

https://play.golang.org/p/_SFceaBThf2

Rezultati:

1 You can delete the post

Shembull: Faqe dinamike, të dhënat nga databaza në template

 1 package main
 2 
 3 import (
 4 	"database/sql"
 5 	_ "github.com/go-sql-driver/mysql"
 6 	"html/template"
 7 	"log"
 8 	"net/http"
 9 )
10 
11 type Libri struct {
12 	ID      int    `json:"id"`         // Emertimi i fushes ne tabele
13 	Titulli string `json:"book_title"` // Emertimi i fushes ne tabele
14 }
15 
16 func main() {
17 	http.HandleFunc("/books/", viewH)
18 	log.Fatal(http.ListenAndServe(":8080", nil))
19 }
20 
21 func viewH(w http.ResponseWriter, r *http.Request) {
22 	db, err := sql.Open("mysql", "root:@tcp(127.0.0.1:3306)/markdown")
23 	defer db.Close()
24 	if err != nil {
25 		log.Fatal(err)
26 	}
27 
28 	results, err := db.Query("SELECT id, book_title FROM books ORDER by id")
29 	if err != nil {
30 		panic(err.Error())
31 	}
32 
33 	var lista []Libri
34 	var libri Libri
35 
36 	for results.Next() {
37 		err = results.Scan(&libri.ID, &libri.Titulli)
38 		if err != nil {
39 			panic(err.Error())
40 		}
41 		lista = append(lista, Libri{ID: libri.ID, Titulli: libri.Titulli})
42 
43 	}
44 
45 	var templates = template.Must(template.ParseFiles("page.html"))
46 
47 	err = templates.ExecuteTemplate(w, "page.html", lista)
48 	if err != nil {
49 		http.Error(w, err.Error(), http.StatusInternalServerError)
50 	}
51 }

https://play.golang.org/p/AVJ8l65uLpm

page.html

 1 <!DOCTYPE html>
 2 <head>
 3 
 4 </head>
 5 
 6 <body>
 7 <table>
 8     {{range .}}
 9         <tr>
10             <td>{{ .ID}}</td>
11             <td>{{.Titulli}}</td>
12         </tr>
13     {{end}}
14 </table>
15 </body>
16 </html>

Rezultati (në browser):

1 1	Go për fillestarë
2 2	PHP dhe MySQL për fillestarë
3 3	Laravel për fillestarë
4 4	Bazat e Ueb dizajnit

Sqarim:

root:@tcp(127.0.0.1:3306)/markdown

Username është root, nuk ka password (pas dypikëshit), TCP protokol, hosti lokal 127.0.0.1, porti i MySQL 3306 emri i databazës është “markdown”

Shembull: Servimi dinamik i një aseti statik

 1 package main
 2 
 3 import (
 4 	"net/http"
 5 	"path"
 6 )
 7 
 8 func main() {
 9 	http.HandleFunc("/", fotografia)
10 	http.ListenAndServe(":8080", nil)
11 }
12 
13 func fotografia(w http.ResponseWriter, r *http.Request) {
14 	fp := path.Join("images", "foto.jpg")
15 	http.ServeFile(w, r, fp)
16 }

Duke e hapur adresën:

http://localhost:8080/

do të shfaqet fotografia e cekur.

Shembull: Zbërthimi i një URL

 1 package main
 2 
 3 import (
 4 	"fmt"
 5 	"log"
 6 	"net/url"
 7 )
 8 
 9 func main() {
10 	url, err := url.Parse("http://www.example.com/anetari/?emri=Golang&emaili=golang@go\
11 ogle.com")
12 
13 	if err != nil {
14 		log.Fatal(err)
15 		return
16 	}
17 
18 	fmt.Println(url.Scheme)
19 	fmt.Println(url.Host)
20 	fmt.Println(url.Path)
21 	queryString := url.Query()
22 	fmt.Println("emri: " + queryString["emri"][0])
23 	fmt.Println("emaili: " + queryString["emaili"][0])
24 }

https://play.golang.org/p/vftkwqxsS-L

Rezultati:

1 http
2 www.example.com
3 /anetari/
4 emri: Golang
5 emaili: golang@google.com

Shembull: Thirrja e funksionit anonim nëpërmes një variabli

 1 package main
 2 
 3 import (
 4 	"fmt"
 5 )
 6 
 7 func main() {
 8 	var a = func(item string) bool {
 9 		if item == "water" {
10 			return true
11 		}
12 		return false
13 	}
14 
15 	fmt.Println(a("water"))
16 	fmt.Println(a("air"))
17 }

https://play.golang.org/p/RYaTuySZ184

Rezultati:

1 true
2 false

Literatura

  • The Go programming language phrasebook / David Chisnall. Copyright © 2012 Pearson Education, Inc
  • Head First Go / Jay McGavren. Copyright © 2019 Jay McGavren.
  • Go Programming by Example / Agus Kurniawan. Copyright © 2015 Agus Kurniawan
  • Hands-On System Programming with Go / Alex Guerrieri. Copyright © 2019 Packt Publishing
  • An Introduction to Programming in Go / Caleb Doxsey. Copyright © 2012 by Caleb Doxsey
  • Go in Action / William Kennedy with Brian Ketelsen & Erik St. Martin. ©2016 by Manning Publications Co.
  • Building Microservices with Go / Nic Jackson. Copyright © 2017 Packt Publishing
  • Building RESTful Web Services with Go / Naren Yellavula. Copyright © 2017 Packt Publishing
  • Build Web Application with Golang / ???
  • Cloud Native Go / Kevin Hoffman & Dan Nemeth. Copyright © 2017 Pearson Education, Inc
  • Concurrency in Go, Tools and Techniques for Developers / Katherine Cox-Buday. Copyright © 2017 Katherine Cox-Buday.
  • Data Structures & Algorithms In Go / Hemant Jain. Copyright © Hemant Jain 2017
  • Distributed Computing with Go / V.N. Nikhil Anurag. Copyright © 2018 Packt Publishing
  • Go Programming Blueprints (Second Edition) / Mat Ryer. Copyright © 2016 Packt Publishing
  • The Little Go Book / Karl Seguin.
  • Programming in Go - Creating Applications for the 21st Century / Mark Summerfield. Copyright © 2012 Qtrac Ltd.
  • Go 101 / Tapir Liu.
  • Go Bootcamp / Matt Aimonetti.
  • Go: Building Web Applications / Nathan Kozyra & Mat Ryer. Copyright © 2016 Packt Publishing
  • Go Cookbook - Build modular, readable, and testable applications in Go / Aaron Torres. Copyright © 2017 Packt Publishing
  • Go Design Patterns / Mario Castro Contreras. Copyright © 2017 Packt Publishing
  • Go: Design Patterns for Real-World Projects. Copyright © 2017 Packt Publishing
  • Go in Practice / Matt Butcher & Matt Farina Copyright © 2017 Packt Publishing
  • Go Programming / John P. Baugh. © 2010 John P. Baugh
  • Go Recipes: A Problem-Solution Approach / Shiju Varghese. Copyright © 2016 by Shiju Varghese
  • Go Systems Programming - Master Linux and Unix system level programming with Go / Mihalis Tsoukalos. Copyright © 2017 Packt Publishing
  • Go Recipes - A Problem-Solution Approach / Shiju Varghese. © Shiju Varghese 2016
  • Go Web Programming / Sau Sheong Chang. ©2016 by Manning Publications Co.
  • Introducing Go - Build Reliable, Scalable Programs / Caleb Doxsey. Copyright © 2016 Caleb Doxsey
  • The Way to Go - A Thorough Introduction to the Go Programming Language / Ivo Balbaert . Copyright © 2012 by Ivo Balbaert
  • Network Programming with Go - Essential Skills for Using and Securing Networks / Jan Newmarch. Copyright © 2017 by Jan Newmarch
  • Learning Functional Programming in Go / Lex Sheehan. Copyright © 2017 Packt Publishing
  • Learning Go / Miek Gieben. Copyright ©2010, 2011 Miek Gieben
  • Learning Go Programming - An insightful guide to learning the Go programming language / Vladimir Vivien. Copyright © 2016 Packt Publishing
  • Learning Go Web Development / Nathan Kozyra. Copyright © 2016 Packt Publishing
  • Machine Learning With Go / Daniel Whitenack. Copyright © 2017 Packt Publishing
  • Mastering Concurrency in Go / Nathan Kozyra. Copyright © 2014 Packt Publishing
  • Mastering Go Web Services / Nathan Kozyra. Copyright © 2015 Packt Publishing
  • Mastering Go - Create Golang production applications using network libraries, concurrency, and advanced Go data structures / Mihalis Tsoukalos. Copyright © 2018 Packt Publishing
  • The Go Programming Language / Alan A. A. Donovan & Brian W. Kernighan. Copyright © 2016 Alan A. A. Donovan & Brian W. Kernighan
  • Web Development with Go Learn to Create Real World Web Applications using Go / Jonathan Calhoun. Copyright © 2016 by Jon Calhoun
  • Webapps in Go the anti textbook / Suraj Patil. © 2016 - 2019 Suraj Patil
  • Go Standard Library Cookbook / Radomír Sohlich. Copyright © 2018 Packt Publishing

Resurse online

Dokumentacion

https://golang.org/
https://golang.org/doc/effective_go.html

Blog

https://blog.golang.org/

Tutoriale

https://go101.org
https://gophercises.com/

Libra gratis

http://www.golangbootcamp.com/
https://www.openmymind.net/The-Little-Go-Book/
http://www.golang-book.com/
https://www.miek.nl/go/
https://github.com/pazams/go-for-javascript-developers
https://gobyexample.com
http://www.pazams.com/Go-for-Javascript-Developers
https://legacy.gitbook.com/download/pdf/book/codegangsta/building-web-apps-with-go
https://www.programming-books.io/essential/go

Forume

https://forum.golangbridge.org/
https://www.reddit.com/r/golang/