A Go programozási nyelv

Alprogramok, modulok

Szintaxis

A függvényeket a func szó vezeti be. Ezt követi a függvény neve, majd zárójelek között a formális paraméterek (az egyes változók neve, illetve típusa ebben a sorrendben), majd végül a visszatérési érték típusa. Ha nincs visszatérési érték, akkor semmit nem kell írnunk.

func Proc() { // ennek a függvénynek nincs visszatérési értéke } func Konkat(param1 string, param2 string) string { return param1 + param2 }

Egyszerre több visszatérési érték is lehet, ekkor ezek típusát zárójelek között kell vesszővel elválasztva felsorolni, pl.:

func StringbolInt(forrás string) (int, bool) { ... }

A visszatérési érték(ek)nek adhatunk nevet is. Ekkor a függvény törzsében az adott nevet lokális változóként használhatjuk, és a futás végén (a return utasítás végrehajtásakor) meglévő értéke lesz a visszatérési érték.

func MaxKer(adatok []float64, keresett float64) (index int, érték int) { ... index = i érték = adatok[i] ... return }

Mind a paraméterek, mind a visszatérési értékek listájából elhagyhatjuk a változóneveket, de mindkét listára igaz, hogy vagy az összes nevet elhagyjuk, vagy mindet ki kell írnunk. Ha egyes paramétereket nem szeretnénk használni, azok számára használhatjuk az üres azonosítót (_).

A return utasítás használata

A return utasítás befejezi egy függvény futását, és esetleg megadhatja a visszatérési értékeket is.

Visszatéréi értékkel nem rendelkező függvényben szereplő return-nek tilos visszatérési érték(ek)et megadni. Ilyen függvényekben nem kötelező szerepeltetni a kulcsszót, ekkor a blokkja végén a függvény véget ér.

Kötelező viszont szerepeltetni a return-t minden olyan függvény és metódus végén, amely rendelkezik visszatérési értékkel. Ez azt is jelenti, hogy ha egy egyszerű elágazás mindkét ágában szerepel, az nem helyes kód (mivel pl. goto utasítással akár ki is ugorhatunk az elágazásból).

A visszatéréi értékek megadására három lehetőség van:

A visszatérési változók mindig az alapértékükre inicializálódnak a függvénybe való belépéskor.

Fogadó (receiver) paraméter

A func szó és a függvény neve közé zárójelek közt egy újabb típus (illetve változó) neve is beékelődhet. Ekkor nevezzük a függvényt metódusnak. Az adott függvény a megfelelő típusú változó után pontot írva hívható meg, hasonlóan ahhoz, ahogy az objektumorientált nyelvekben szokás.

Példa:

func (s string) Teszt(param string) string { return s + param } Ekkor meghívhatjuk így: s := "alma" // ekkor s string lesz z := s.Teszt() // meghívjuk a string metódusát

A fogadó típus T vagy *T alakú kell, hogy legyen, ahol T nem pointer és nem interface típusnév, és a metódussal azonos csomagban deklarálták (vagy előre definiált).

Ha a fogadó típusa *T, akkor a paraméter egy pointer, és a referált értéket a metódus ennek megfelelően megváltoztathatja. ha a fogadó paraméter nem pointer típus, akkor a fogadó paraméter értékének változtatása nem hat a függvény törzsén kívül.

Egy típusra definiált metódusok neve mind különböző kell, hogy legyen, továbbá, ha a fogadó típus rekord (struct), akkor a mezők nevével sem lehet ütközés.

A metódus típusa a hasonló szignatúrájú függvény típusa, de első paramétere a fogadó paraméter, ezt követi a többi paraméter. Pl.:

func(p *Point, factor float64)

Azonban egy ilyen módon definiált függvény nem lesz a Point típus metódusa.

Interface-ek megvalósítása

Interface-eket metódusok segítségével valósíthatunk meg: ha egy típus megvalósítja egy interface összes metódusát, akkor megvalósítja az interface-t. Pl.:

type IntStream interface { Read (int) bool Write (int) bool } type MyIntStream struct { // ez a rekord meg fogja valósítani az IntStream interface-t } func (s *MyIntStream) Read (int r) bool { // megvalósítás } func (s *MyIntStream) Write (int w) bool { // megvalósítás }

Paraméterátadás

A paraméterek alapértelmezetten érték szerint adódnak át, kivéve a szelet, map és csatorna típusú paramétereket (ezek referencia szerint kerülnek átadásra). Out és inout paraméterátadási mód megvalósítására ezen túl mutatókat használhatunk.

Speciális paramétertípus a ...T. Ez azt jelöli, hogy az adott helyre változó számú, T típusú paraméter kerülhet. Ez muszáj, hogy a paraméterlista utolsó eleme legyen. Például a klasszikus printf deklarációja Go-ban:

func Printf(format string, args ...interface{}) Printf("%s %d", "hello", 23)

A ...T paraméter átadása []T valójában típusként történik (pl. az előbbi Printf törzsében a paramétereket az args[i] indexeléssel érhetnénk el).

Megfelelő típusú szeletet is átadhatunk a ...T paraméter számára, ekkor a szeletet eredményező kifejezés végére híváskor ...-ot kell írni, pl.:

var szelet []inteface{} = []interface{} {"hello", 23} Printf("%s %d", szelet...)

Túlterhelés

Túlterhelésre a Go nyelvben nincs lehetőség. Viszont hasonló hatást érhetünk el a fent említett ... alkalmazásával.

Másik lehetőségünk a túlterhelés szimulálására az interface{} típus használata. Mivel az üres interface-t minden típus megvalósítja, ezért ilyen típusú paraméterként majdnem bármilyen érték átadható, majd ez a típusalapú switch konstrukció segítségével megfelelő dinamikus típusra alakítható.

Rekurzió

Alprogramok rekurzív hívása a megszokottak szerint működik.

Csomagok

Minden Go fájl a package kulcsszóval kezdődik, majd ezt egy csomagnév követi, mely egy azonosító. Ez mondja meg, hogy melyik csomaghoz tartozik. Egy csomag több fájlból is állhat, ám azon fájloknak egy könyvtárban kell lenniük.

A package rész után az import rész jön, mely azt mondja meg, hogy mi mely csomagokat szeretnénk felhasználni.

Végül típusok és függvények sokasága jön. Azt, hogy melyik látszik kívülről, a név kezdőbetűje dönti el: a nagybetűvel kezdődőek látszanak kívülről, a többiek nem. (Nagybetű alatt a Unicode nagybetű osztályát értjük, tehát pl. az Űrhajó függvény látszik kívülről. Ezen a ponton viszont a távol-keleti nyelvek hátrányban vannak, mert ott nincs kis- ill. nagybetű.)

A csomagok tartalmazhatnak egy vagy több init() függvényt. Ezek a csomag importálásakor futnak le, miután a csomag szintű változók értéket kaptak. Ha a csomag további csomagokat importál, ezek előbb ezek inicializálódnak. Ha több init() függvény van, mindegyik lefut, de a sorrendjükre nincs megkötés.

Minden programban kell lennie egy main csomagnak, benne egy main() függvénnyel. A program belépési pontja ez a main.main() függvény lesz.