A Go programozási nyelv

Utasítások, vezérlési szerkezetek



Deklarációk segítségével azonosítókat vezetünk be, melyekre később kényelmesen hivatkozhatunk. Nevet köthetünk konstanshoz, változóhoz, függvényhez és típushoz.
A csomag neve azonosító, de a package csomagnév nem deklaráció.

A Go programban minden azonosítót, melyet használunk, deklarálni kell. Ugyanazt az azonosítót nem deklarálhatjuk egy blokkon belül kétszer, valamint nem deklarálhatjuk ugyanazt az azonosítót a fájl- és a csomagblokkban is.

Deklarációk hatásköre

A deklaráció hatásköre a forráskód azon része, ahol az azonosító a deklaráció által megjelölt entitásra hivatkozik. A láthatósági szabályok az alábbiak:

Ha egy azonosítót, melyet egy külső blokkban deklaráltunk, újra deklarálunk a blokk egy belső blokkjában, akkor a belső deklaráció hatásköre elfedi a külső deklarációt.

Változódeklaráció

Változót a var kulcsszóval vezethetünk be var név típus formában.

Kezdeti érték adható a változónak egyenlőségjel után (var x int = 42). Kezdeti érték híján a változó értéke az alapérték lesz. Kezdeti érték megadása esetén a típus el is hagyható, ekkor a fordító kikövetkezteti a típust a jobboldali kifejezésből. Ennek szabályait ld. az Értékadás szakaszban.

Lehetőségünk van több változót deklarálni a var egyszeri kiírásával, így:

var egyedül complex128 var ( i int64 x, y rune // egyszerre több azonos típusú változó u, v, s = 2.0, 4, "bar" // rövidíthetünk a szimultán értékadáshoz hasonlóan is (ekkor a típusok kikövetkeztetése automatikus) )

Konstans-deklaráció

Konstansokat a változókhoz hasonlóan, de a const kulcsszó után deklarálhatunk, és mindig meg kell adnunk az értéküket. Ha egy konstans deklarációból elhagyjuk a típust és az értéke nemtípusos konstanst jelöl, nemtípusos konstanst kapunk (ld. Konstansok és Konstans kifejezés).

Speciális lehetőségként egy zárójeles const szakaszon belül az első kivételével elhagyhatjuk az értékeket: ennek hatására az összes konstanst az első értékét kapja. Ezt együtt használva az iota konstanssal hatékonyan tudunk egymást követő értékeket generálni.

const ( Hétfő = iota // 0 Kedd // 1 Szerda // ... Csütörtök Péntek Szombat Vasárnap napokSzáma // nem exportált konstans )

Függvénydeklaráció

A Go nyelvben a függvénydeklaráció a func kulcsszó segítségével történik. A deklaráció során a func kulcsszó után megadjuk a függvény nevét, majd zárójelben a paramétereket nevük és típusuk segítségével, végül zárójelben a visszatérési értékek típusait. A függvényblokk elejét és végét kapcsos zárójelek jelzik a C típusú nyelvekhez hasonlóan.

func függvényNév(változó1 típus1, változó2 típus2) (visszatérésiTípus1, visszatérésiTípus2){ //kód }

Az azonos típusú paraméterek rövidíthetőek. Ha minden paraméter típusa azonos, akkor a típust csak egyszer kell kiírnunk, a változóneveket, pedig vesszővel választjuk el. Ezek alapján

func add(x int, y int){} megegyezik func add(x,y int){} függvénnyel

Amint láthattuk több visszatérési értéke is lehet a függvénynek. A paraméterekhez hasonlóan a visszatérési értékek is elnevezhetőek és rövidíthetőek. Ezek a nevek felhasználhatóak a függvényblokkon belül.

func foo(sum int) (x,y int) { x= sum * 4 / 9 y = sum – x return }

A függvénydeklaráció végén jelölhetünk változó számú paramétereket is a ... jelöléssel. Ez lehet nulla vagy több paraméter is, amely a megadott típusú változókból álló slice-ként adódik át.

func sum(nums ...int) { total := 0 for _, num := range nums { total += num } fmt.Println(total) }

Értékadás

Van szimultán értékadás, ekkor a bal és jobboldalon álló elemek számának (és típusának) meg kell egyeznie. Ha nem akarjuk felhasználni valamelyik értéket a jobb oldalról, akkor a bal oldalon a megfelelő helyre írhatunk _ üres azonosítót, így az ehhez tartozó érték elveszik.

A szimultán értékadás segítségével nyerhetjük ki többszörös visszatérési értékű függvények visszatérési értékét, pl.:

func f () (int, int) { /* megvalósítás */ } var x, y int = f()

Szimultán értékadással akár két változó értékét is felcserélhetjük: x, y = y, x utasítás legális és az elvárásainknak megfelelően működik.

Inicializáló értékadás

Függvény törzsén belül a var kulcsszó nélkül, rövidebb formában tudunk bevezetni változót a := operátorral. Ekkor a típus kikövetkeztetésre kerül a jobb oldali értékből.

név := kezdőérték

Ebben az esetben is használhatjuk a szimultán értékadás lehetőségét. Ha legalább egy új azonosító van a bal oldalon, akkor használhatunk :=-t. Pl.:

a := 42 b, c := 3, 5 var x, y int64 x, y, z := (a * b) / c Helytelen, mert nincs új változó a bal oldalon! x, y := 0, 0

Értékadás kombinálása operátorral

Ha op egy operátor, akkor x op= y megegyezik x = x op y-nal (de x csak egyszer értékelődik ki). Ebben az esetben mindkét oldalon pontosan egy kifejezés állhat.

Növelés és csökkentés

A C++-szal ellentétben a Go nyelvben a megszokott ++ és -- operátorok nem kifejezések, hanem utasítások, ezért csak önállóan állhatnak.

A készítőknek választaniuk kellett a prefix és a postfix forma (++x ill. x++) közül. Választásuk az utóbbira esett, hiszen a C++-nak is a nevében ez van, az előbbi változat csak a boost könyvtár megjelenése óta terjedt el, hatékonysági okokból.

Elágazás

Az if kulcsszóval; nincs többágú elágazás, csak egymásba ágyazott if kifejezések.

if kezdőutasítás;feltétel { ág1 } else { ág2 }

A kezdőutasítás egy egyszerű utasítás lehet, ez elhagyható. A { } zárójelek kötelezőek, a feltételnek logikai kifejezésnek kell lennie.

Switch

Kétféle switch utasítás létezik a nyelvben. Az első egy kifejezés értéke alapján hajtja végre valamelyik ágat, a második egy érték típusa alapján. A vezérlés nem megy tovább a következő ágra, mint C++-ban, kivéve, ha az ág utolsó utasítása a fallthrough (ez az utasítás nem használható típus alapú switchben).

Érték alapú switch

switch kezdőutasítás; logikai kifejezés { case érték1, érték2, ...: utasítások case érték3: utasítások ... default: utasítások }

A kezdő utasítás szabadon elhagyható. Ha a logikai kifejezést elhagyjuk, az értéke alapértelmezett módon true lesz, így a következő elágazás is helyes:

switch { case x < y: f1() case x < z: f2() case x == 4: f3() }

A default ág nem kötelező, illetve nem szükséges, hogy a switch végén legyen. Egyes ágakhoz több értéket is írhatunk, az első illeszkedő értékhez tartozó ág fut le.

Az értékek tetszőlegesek lehetnek, kiértékelésük balról jobbra és fentről lefelé történik az első egyezésig - ezután a megfelelő ág lefut és a többi értéket a program nem értékeli ki (a 2. példában például ha x < y, akkor sem f2(), sem f3() nem kerül kiértékelésre).

Típus alapú switch

switch kezdőutasítás; azonosító := kifejezés.(type) { case nil: utasítások case típus1, típus2, ...: utasítások ... default: utasítások }

Ezt akkor használhatjuk, ha a kifejezés valamilyen interface típusú, és a dinamikus típusától függően akarunk más-más utasításokat végrehajtani.

A kezdőutasítás itt is elhagyható. Az azonosító és a := szintén opcionális: ha van ilyen, akkor a név minden case-ágban használható és az ágnak megfelelő típusú lesz.

Megengedett továbbá a nil ág is: ez akkor fut le, ha a vizsgált kifejezés egy nil interface érték.

Ciklusok

A Go-ban egyetlen kulcsszó van a különböző ciklusokra, a for.

Az első fajta ciklus a szokásos while-nak felel meg. Ez egy egyszerű elől tesztelő ciklus, ami addig fut, amíg a ciklusfeltétel teljesül.

for feltétel { ciklusmag }

A második fajta ciklus a C++-os for ciklus. Itt (ahogy a C++-ban is) bármelyik rész lehet üres utasítás is.

for kezdő utasítás; feltétel; léptető utasítás { ciklusmag }

A harmadik egy foreach-jellegű ciklus a range kulcsszóval. Ezzel tömbökön, szeleteken, map-eken, stringeken és csatornákon iterálhatunk végig.

for index := range elemek { ciklusmag } for index, value := range elemek { ciklusmag }

A csatornák kivételével két értéket ad vissza a range minden iterációnál: az aktuális elem indexét és az aktuális elem értékét. Utóbbit (fent value) elhagyhatjuk (az index eldobására a _ üres azonosítót használhatjuk). Csatornák esetében egy értéket kapunk, ekkor addig fogad elemeket a csatornán, amíg az nincs bezárva és nem üres.

Negyedik lehetőségünk a végtelen ciklus:

for { /* végtelen ciklus magja */ }

Break és continue

A break utasítás a bennfoglaló legbelső for, switch vagy select utasítás megszakítására szolgál.

A continue utasítás a legbelső bennfoglaló ciklust (for utasítást) új iteráció megkezdésére kényszeríti.

Címkék

A ciklusok, illetve switch és select utasítások elláthatók címkével, pl.:

Címke1: select { ... }

Ekkor az ilyen ciklusban, switch-ben vagy select-ben hívható a break, ill. continue utasítás címkézett változata, ami ekkor az adott címkével ellátott vezérlési szerkezetre vonatkozik. Így például egymásba ágyazott ciklusok vagy elágazás utasítások esetén nem csak a legbelsőt tudjuk vezérelni. Pl.:

Címke2: for x == 0 { for i := 1; i < y; i++ { ... continue Címke2 // a külső ciklust indítja újra ... } }

Valójában tetszőleges utasítás ellátható címkével, és a goto címke utasítással ugorhatunk a címkével megjelölt utasításra. Nem ugorhatunk azonban át (vagy be) a goto utasítást tartalmazó blokktól különböző blokkba, illetve az ugrás hatására nem kerülhetünk olyan változó hatáskörébe, amelynek a hatásköre nem foglalja magában az ugró utasítást. Példák:

Helyes ugrás: func f() bool { ... if x < 0 { goto Error } Error: return false } Hibás (belép változó hatáskörébe): goto L v := 3 L: Hibás (beugrik egy blokkba): if n%2 == 1 { goto L1 } for n > 0 { f() n-- L1: f() n-- }

A go utasítás

A go utasítás a megadott függvény- vagy metódushívást független, konkurens végrehajtási szálon (de azonos memória-címtérben) indítja el és azonnal visszatér. A go utasítással elindított szubrutinokat gorutinoknak nevezzük. Pl.:

go Start()

A gorutinban indított függvények (metódusok) esetleges visszatérési értéke a futás végén eldobásra kerül. A kommunikációt csatornákkal (chan) biztosíthatjuk a különböző gorutinok, ill. a főprogram között.

Select

A select utasítás olyan esetekben használható, amikor többféle, csatornán történő kommunikáció közül kell választani. Szintaxisa hasonló a switch-éhez.

Amikor a vezérlés a select utasításhoz ér, fentről lefele kiértékeli az összes ágat. Ezután a következők történhetnek:

Példák:

var c, c1, c2 chan int var i1, i2 int select { case i1 = <-c1: print("received ", i1, " from c1\n") case c2 <- i2: print("sent ", i2, " to c2\n") default: print("no communication\n") } for { // véletlen 0-1 sorozat küldése a c-re select { case c <- 0: case c <- 1: } } select { } // örökre blokkolni fog

Blokkok

A blokk deklarációk és utasítások sorozata, kapcsos zárójelek között. A nyelv az azonosítók hatókörét a blokkok alapján határozza meg. A blokkok egymásba ágyazhatók.

Mivel a fordításkor a fordító pótolja a ki nem írt pontosvesszőket a sorok végén, a blokkok kezdő kapcsos zárójelét egy sorba kell írni azzal a kulcsszóval, amihez tartozik. Például ez helyes:

func foo() { //... }

Ez viszont helytelen:

func foo() //ide a fordító pontosvesszőt tenne { //ennek a blokknak semmi köze nem lenne a függvényhez //... }

Az explicit blokkok mellett automatikusan létrejönnek implicit blokkok is:

Blokkok végtelen mélységig egymásba ágyazhatók. Alapvetően befolyásolják a változók hatáskörét.