Table of Contents
- Parathënie
- Hyrje
- Udhëzime
- Variablat e ambientit
- Rregullat e përgjithshme
- Emërtimet e fajllave dhe identifikatorëve
- Variablat
- Konstantat
- Tipet e të dhënave
- Numrat
- Stringjet
- Degëzimi (if)
- Degëzimi (switch)
- Ciklet (for)
- Tipet kompozite: Vargjet
- Tipet kompozite: Segmentet
- Tipet kompozite: Mapat
- Tipet kompozite: Struktet
- Pointerët
- Interfejsat
- Funksionet
- Funksionet anonime
- Defer
- Error, panic & recover
- Gorutinat, kanalet, sinkronizimi
- HTTP Webserver
- Package net
- Databazat
- I/O and File Systems
- Package fmt
- Package strings
- Regular Expressions
- REST API: Konceptet bazike
- REST API: Karakteristikat
- REST API: HTTP metodat dhe status kodet
- REST API: Metoda GET
- REST API: Metodat POST, PUT, PATCH, DELETE dhe OPTIONS
- REST API: Status kodet
- HTTP Request/Headers/Response
- Web app: Web Programming Basics
- Web app: Web aplikacioni bazik
- Web app: Dizajnimi i aplikacionit
- Web app: Databases
- Web app: Forms
- Web app: Upload
- Web app: Templates
- Web app: Autentikimi
- Web app: Files
- Web app: Routing
- Web app: Middleware
- REST API (JSON and XML)
- Web app: Unit testing
- Gorilla
- Referencë koncize e funksioneve
- Pyetje
- Përgjigjet
- Shembuj
- Shembull: Krahasimi i dy vlerave të përafërta float
- Shembull: Argumentet nga konzola
- Shembull. Leximi i vlerës nga konzola
- Shembull: Shuma e numrave të lexuara nga konzola
- Shembull: Strukti me metodë
- Shembull: Thirrja e metodës së struktit nga një funksion
- Shembull: Thirrja e një metode të struktit të brendshëm
- Shembull: Konvertimi i struktit në JSON
- Shembull: Strukt në JSON, JSON në strukt
- Shembull: Leximi i JSON fajllit me parsim me strukt
- Shembull: Maps - Qytetet dhe numri postar
- Shembull: Map me listë shtetesh dhe numër të banorëve
- Shembull: Bubble Sort
- Shembull: Fizzbuzz
- Shembull: Gjenerimi i sha256 hash
- Shembull: Gjeneratori i stringut të rastësishëm
- Shembull: Fibonacci sekuenca
- Shembull: Numri prim
- Shembull: Shkrimi dhe leximi nga fajlli i formatit CSV
- Shembull: Enkodimi/dekodimi i map në JSON
- Shembull: Nga strukt në XML
- Shembull: Enkodimi/dekodimi me base64
- Shembull: Enkriptim/dekriptim me XOR
- Shembull: Bitwise shift operator me enumerated constants
- Shembull: ACL sistem me enumerated constants
- Shembull: Faqe dinamike, të dhënat nga databaza në template
- Shembull: Servimi dinamik i një aseti statik
- Shembull: Zbërthimi i një URL
- Shembull: Thirrja e funksionit anonim nëpërmes një variabli
- Literatura
- Resurse online
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:
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ë.

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.

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
- 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 int8 në int, 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
më
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
më
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ë case
në case
-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ë
të
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
Të
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ë
të
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
të
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
të
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
të
startohet
2
main
():
duke
pritur
gorutinën
të
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
të
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ë:
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.

Rezultati që kthehet është:
U regjistrua: Alban
Lista e anëtarëve shfletohet duke e thirrur URL-në 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.
Në 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
të
gjelbëruar
!
5
Do
të
këndonj
bagëtinë
që
mbani
ju
e
ushqeni
,
6
O
vendëthit
e
bekuar
,
ju
mëndjen
ma
dëfreni
.
7
Ti
Shqipëri
,
më
jep
nderë
,
më
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
të
gjelbëruar
!
5
Do
të
këndonj
bagëtinë
që
mbani
ju
e
ushqeni
,
6
O
vendëthit
e
bekuar
,
ju
mëndjen
ma
dëfreni
.
7
Ti
Shqipëri
,
më
jep
nderë
,
më
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
- Tipi
rune
është alias për tipin: int8, int16, int32 apo int64? - Çfarë paraqet vlera e funksionit
len()
kur e përdorim me një string? - Deklarimi i variablit në formën
a := 5
bëhet: jashtë apo brenda një funksioni? - Variablit të deklaruar brenda strukturës
for
, a mund t’i qasemi jashtë strukturës?
Strukturat
- Ku përdoret urdhëri
fallthrough
? - 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
të
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
:
04
9111222
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ë
në
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ë
të
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
>
04
9234567
<
/
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:
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
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/