Sdílej stránku

Nauč se X za Y minut

Kde X=Go

Jazyk Go byl vytvořen, jelikož bylo potřeba dokončit práci. Není to poslední trend ve světě počítačové vědy, ale je to nejrychlejší a nejnovější způsob, jak řešit realné problémy.

Go používá známé koncepty imperativních jazyků se statickým typováním. Rychle se kompiluje a také rychle běží. Přidává snadno pochopitelnou podporu konkurenčnosti, což umožňuje využít výhody multi-core procesorů a jazyk také obsahuje utility, které pomáhají se škálovatelným programováním.

Go má již v základu vynikající knihovnu a je s ním spojená nadšená komunita.

// Jednořádkový komentář
/* Několika
 řádkový komentář */

// Každý zdroják začíná deklarací balíčku (package)
// main je vyhrazené jméno, které označuje spustitelný soubor,
// narozdíl od knihovny
package main

// Importní deklarace říkají, které knihovny budou použity v tomto souboru.
import (
    "fmt"       // Obsahuje formátovací funkce a tisk na konzolu
    "io/ioutil" // Vstupně/výstupní funkce
    m "math"    // Odkaz na knihovnu math (matematické funkce) pod zkratkou m
    "net/http"  // Podpora http protokolu, klient i server.
    "strconv"   // Konverze řetězců, např. na čísla a zpět.
)

// Definice funkce. Funkce main je zvláštní, je to vstupní bod do programu.
// Ať se vám to líbí, nebo ne, Go používá složené závorky
func main() {
    // Println vypisuje na stdout.
    // Musí být kvalifikováno jménem svého balíčko, ftm.
    fmt.Println("Hello world!")

    // Zavoláme další funkci
    svetPoHello()
}

// Funkce mají své parametry v závorkách
// Pokud funkce nemá parametry, tak musíme stejně závorky uvést.
func svetPoHello() {
    var x int // Deklarace proměnné. Proměnné musí být před použitím deklarované
    x = 3     // Přiřazení hodnoty do proměnné
    // Existuje "krátká" deklarace := kde se typ proměnné odvodí, 
    // proměnná vytvoří a přiřadí se jí hodnota
    y := 4
    sum, prod := naucSeNasobit(x, y)        // Funkce mohou vracet více hodnot
    fmt.Println("sum:", sum, "prod:", prod) // Jednoduchý výstup
    naucSeTypy()                            // < y minut je za námi, je čas učit se víc!
}

/* <- začátek mnohořádkového komentáře
Funkce mohou mít parametry a (několik) návratových hodnot.
V tomto případě jsou `x`, `y` parametry a `sum`, `prod` jsou návratové hodnoty.
Všiměte si, že `x` a `sum` jsou typu `int`.
*/
func naucSeNasobit(x, y int) (sum, prod int) {
    return x + y, x * y // Vracíme dvě hodnoty
}

// zabudované typy a literáty.
func naucSeTypy() {
    // Krátká deklarace většinou funguje
    str := "Learn Go!" // typ řetězec.

    s2 := `"surový" literát řetězce
může obsahovat nové řádky` // Opět typ řetězec.

    // Můžeme použít ne ASCII znaky, Go používá UTF-8.
    g := 'Σ' // type runa, což je alias na int32 a ukládá se do něj znak UTF-8

    f := 3.14159 // float64, je IEEE-754 64-bit číslem s plovoucí čárkou.
    c := 3 + 4i  // complex128, interně uložené jako dva float64.

    // takhle vypadá var s inicializací
    var u uint = 7 // Číslo bez znaménka, jehož velikost záleží na implementaci,
                   // stejně jako int
    var pi float32 = 22. / 7

    // takto se převádí typy za pomoci krátké syntaxe
    n := byte('\n') // byte je jiné jméno pro uint8.

    // Pole mají fixní délku, které se určuje v době kompilace.
    var a4 [4]int           // Pole 4 intů, všechny nastaveny na 0.
    a3 := [...]int{3, 1, 5} // Pole nastaveno na tři hodnoty
    // elementy mají hodntu 3, 1 a 5

    // Slicy mají dynamickou velikost. Pole i slacy mají své výhody,
    // ale většinou se používají slicy.
    s3 := []int{4, 5, 9}    // Podobně jako a3, ale není tu výpustka.
    s4 := make([]int, 4)    // Alokuj slice 4 intů, všechny nastaveny na 0.
    var d2 [][]float64      // Deklarace slicu, nic se nealokuje.
    bs := []byte("a slice") // Přetypování na slice

    // Protože jsou dynamické, můžeme ke slicům přidávat za běhu
    // Přidat ke slicu můžeme pomocí zabudované funkce append().
    // Prvním parametrem je slice, návratová hodnota je aktualizovaný slice.
    s := []int{1, 2, 3}     // Výsledkem je slice se 3 elementy.
    s = append(s, 4, 5, 6)  // Přidány další 3 elementy. Slice má teď velikost 6.
    fmt.Println(s) // Slice má hodnoty [1 2 3 4 5 6]

    // Pokud chceme k poli přičíst jiné pole, můžeme předat referenci na slice,
    // nebo jeho literát a přidat výpustku, čímž se slicu "rozbalí" a přidá se k
    // původnímu slicu.
    s = append(s, []int{7, 8, 9}...) // druhým parametrem je literát slicu.
    fmt.Println(s)  // slice má teď hodnoty [1 2 3 4 5 6 7 8 9]

    p, q := naucSePraciSPameti() // Deklarujeme p a q jako typ pointer na int.
    fmt.Println(*p, *q)   // * dereferencuje pointer. Tím se vypíší dva inty.

    // Mapy jsou dynamické rostoucí asociativní pole, jako hashmapa, nebo slovník
    // (dictionary) v jiných jazycích
    m := map[string]int{"tri": 3, "ctyri": 4}
    m["jedna"] = 1

    // Napoužité proměnné jsou v Go chybou.
    // Použijte podtržítko, abychom proměnno "použili".
    _, _, _, _, _, _, _, _, _, _ = str, s2, g, f, u, pi, n, a3, s4, bs
    // Výpis promenné se počítá jako použití.
    fmt.Println(s, c, a4, s3, d2, m)

    naucSeVetveníProgramu() // Zpátky do běhu.
}

// narozdíl od jiných jazyků, v Go je možné mít pojmenované návratové hodnoty.
// Tak můžeme vracet hodnoty z mnoha míst funkce, aniž bychom uváděli hodnoty v
// return.
func naucSePojmenovaneNavraty(x, y int) (z int) {
    z = x * y
    return // z je zde implicitní, jelikož bylo pojmenováno.
}

// Go má garbage collector. Používá pointery, ale neumožňuje jejich aritmetiku.
// Můžete tedy udělat chybu použitím nil odkazu, ale ne jeho posunutím.
func naucSePraciSPameti() (p, q *int) {
    // Pojmenované parametry p a q mají typ odkaz na int.
    p = new(int) // Zabudované funkce new alokuje paměť.
    // Alokované místo pro int má hodnotu 0 a p už není nil.
    s := make([]int, 20) // Alokujeme paměť pro 20 intů.
    s[3] = 7             // Jednu z nich nastavíme.
    r := -2              // Deklarujeme další lokální proměnnou.
    return &s[3], &r     // a vezmeme si jejich odkaz pomocí &.
}

func narocnyVypocet() float64 {
    return m.Exp(10)
}

func naucSeVetveníProgramu() {
    // Výraz if vyžaduje složené závorky, ale podmínka nemusí být v závorkách.
    if true {
        fmt.Println("říkal jsme ti to")
    }
    // Formátování je standardizované pomocí utility "go fmt".
    if false {
        // posměšek.
    } else {
        // úšklebek.
    }
    // Použij switch, když chceš zřetězit if.
    x := 42.0
    switch x {
    case 0:
    case 1:
    case 42:
        // jednotlivé case nepropadávají. není potřeba "break"
    case 43:
        // nedosažitelné, jelikož už bylo ošetřeno.
    default:
        // implicitní větev je nepovinná.
    }
    // Stejně jako if, for (smyčka) nepoužívá závorky.
    // Proměnné definované ve for jsou lokální vůči smyčce.
    for x := 0; x < 3; x++ { // ++ je výrazem.
        fmt.Println("iterace", x)
    }
    // zde je x == 42.

    // For je jediná smyčka v Go, ale má několik tvarů.
    for { // Nekonečná smyčka
        break    // Dělám si legraci
        continue // Sem se nedostaneme
    }

    // Můžete použít klíčové slovo range pro iteraci nad mapami, poli, slicy,
    // řetězci a kanály.
    // range vrací jednu (kanál) nebo dvě hodnoty (pole, slice, řetězec a mapa).
    for key, value := range map[string]int{"jedna": 1, "dva": 2, "tri": 3} {
        // pro každý pár (klíč a hodnota) je vypiš
        fmt.Printf("klíč=%s, hodnota=%d\n", key, value)
    }

    // stejně jako for, := v podmínce if přiřazuje hodnotu
    // nejříve nastavíme y a pak otestujeme, jestli je y větší než x.
    if y := narocnyVypocet(); y > x {
        x = y
    }
    // Funkční literáty jsou tzv. uzávěry (closure)
    xBig := func() bool {
        return x > 10000 // odkazuje na x deklarované ve příkladu použití switch
    }
    x = 99999
    fmt.Println("xBig:", xBig()) // true
    x = 1.3e3                    // To udělá z x == 1300
    fmt.Println("xBig:", xBig()) // teď už false.

    // Dále je možné funkční literáty definovat a volat na místě jako parametr
    // funkce, dokavaď:
    // a) funkční literát je okamžitě volán pomocí (),
    // b) výsledek se shoduje s očekávaným typem.
    fmt.Println("Sečte + vynásobí dvě čísla: ",
        func(a, b int) int {
            return (a + b) * 2
        }(10, 2)) // Voláno s parametry 10 a 2
    // => Sečti a vynásob dvě čísla. 24

    // Když to potřebujete, tak to milujete
    goto miluji
miluji:

    naučteSeFunkčníFactory() // funkce vracející funkce je zábava(3)(3)
    naučteSeDefer()      // malá zajížďka k důležitému klíčovému slovu.
    naučteSeInterfacy() // Přichází dobré věci!
}

func naučteSeFunkčníFactory() {
    // Následující dvě varianty jsou stejné, ale ta druhá je praktičtější
    fmt.Println(větaFactory("létní")("Hezký", "den!"))

    d := větaFactory("letní")
    fmt.Println(d("Hezký", "den!"))
    fmt.Println(d("Líný", "odpoledne!"))
}

// Dekorátory jsou běžné v jiných jazycích. To samé můžete udělat v Go
// pomocí parameterizovatelných funkčních literátů.
func větaFactory(můjŘetězec string) func(před, po string) string {
    return func(před, po string) string {
        return fmt.Sprintf("%s %s %s", před, můjŘetězec, po) // nový řetězec
    }
}

func naučteSeDefer() (ok bool) {
    // Odloží (defer) příkazy na okamžik těsně před opuštěním funkce.
    // tedy poslední se provede první
    defer fmt.Println("odložené příkazy jsou zpravovaná v LIFO pořadí.")
    defer fmt.Println("\nProto je tato řádka vytištěna první")
    // Defer se běžně používá k zavírání souborů a tím se zajistí, že soubor
    // bude po ukončení funkce zavřen.
    return true
}

// definuje typ interfacu s jednou metodou String()
type Stringer interface {
    String() string
}

// Definuje pár jako strukturu se dvěma poli typu int x a y.
type pár struct {
    x, y int
}

// Definuje method pár. Pár tedy implementuje interface Stringer.
func (p pár) String() string { // p je tu nazýváno "Receiver" - přijímač
    // Sprintf je další veřejná funkce z balíčku fmt.
    // Pomocí tečky přistupujeme k polím proměnné p
    return fmt.Sprintf("(%d, %d)", p.x, p.y)
}

func naučteSeInterfacy() {
    // Složené závorky jsou "strukturální literáty. Vyhodnotí a inicializuje
    // strukturu. Syntaxe := deklaruje a inicializuje strukturu.
    p := pár{3, 4}
    fmt.Println(p.String()) // Volá metodu String na p typu pár.
    var i Stringer          // Deklaruje i jako proměnné typu Stringer.
    i = p                   // Toto je možné, jelikož oba implementují Stringer
    // zavolá metodu String(( typu Stringer a vytiskne to samé jako předchozí.
    fmt.Println(i.String())

    // Funkce ve balíčku fmt volají metodu String, když zjišťují, jak se má typ
    // vytisknout.
    fmt.Println(p) // Vytiskne to samé, jelikož Println volá String().
    fmt.Println(i) // Ten samý výstup.

    naučSeVariabilníParametry("super", "učit se", "tady!")
}

// Funcke mohou mít proměnlivé množství parametrů.
func naučSeVariabilníParametry(mojeŘetězce ...interface{}) {
    // Iterujeme přes všechny parametry
    // Potržítku tu slouží k ignorování indexu v poli.
    for _, param := range mojeŘetězce {
        fmt.Println("parameter:", param)
    }

    // Použít variadický parametr jako variadický parametr, nikoliv pole.
    fmt.Println("parametery:", fmt.Sprintln(mojeŘetězce...))

    naučSeOšetřovatChyby()
}

func naučSeOšetřovatChyby() {
    // ", ok" je metodou na zjištění, jestli něco fungovalo, nebo ne.
    m := map[int]string{3: "tri", 4: "ctyri"}
    if x, ok := m[1]; !ok { // ok bude false, jelikož 1 není v mapě.
        fmt.Println("není tu jedna")
    } else {
        fmt.Print(x) // x by bylo tou hodnotou, pokud by bylo v mapě.
    }
    // hodnota error není jen znamením OK, ale může říct více o chybě.
    if _, err := strconv.Atoi("ne-int"); err != nil { // _ hodnotu zahodíme
        // vytiskne 'strconv.ParseInt: parsing "non-int": invalid syntax'
        fmt.Println(err)
    }
    // Znovu si povíme o interfacech, zatím se podíváme na
    naučSeKonkurenčnost()
}

// c je kanál, způsob, jak bezpečně komunikovat v konkurenčním prostředí.
func zvyš(i int, c chan int) {
    c <- i + 1 // <- znamená "pošli" a posílá data do kanálu na levé straně.
}

// Použijeme funkci zvyš a konkurečně budeme zvyšovat čísla.
func naučSeKonkurenčnost() {
    // funkci make jsme již použili na slicy. make alokuje a inicializuje slidy,
    // mapy a kanály.
    c := make(chan int)
    // nastartuj tři konkurenční go-rutiny. Čísla se budou zvyšovat
    // pravděpodobně paralelně pokud je počítač takto nakonfigurován.
    // Všechny tři zapisují do toho samého kanálu.
    go zvyš(0, c) // go je výraz pro start nové go-rutiny.
    go zvyš(10, c)
    go zvyš(-805, c)
    // Přečteme si tři výsledky a vytiskeneme je..
    // Nemůžeme říct, v jakém pořadí výsledky přijdou!
    fmt.Println(<-c, <-c, <-c) // pokud je kanál na pravo, jedná se o "přijmi".

    cs := make(chan string)       // Další kanál, tentokrát pro řetězce.
    ccs := make(chan chan string) // Kanál kanálu řetězců.
    go func() { c <- 84 }()       // Start nové go-rutiny na posílání hodnot.
    go func() { cs <- "wordy" }() // To samé s cs.
    // Select má syntaxi jako switch, ale vztahuje se k operacím nad kanály.
    // Náhodně vybere jeden case, který je připraven na komunikaci.
    select {
        case i := <-c: // Přijatá hodnota může být přiřazena proměnné.
            fmt.Printf("je to typ %T", i)
        case <-cs: // nebo může být zahozena
            fmt.Println("je to řetězec")
        case <-ccs: // prázdný kanál, nepřipraven ke komunikaci.
            fmt.Println("to se nestane.")
    }
    // V tomto okamžiku máme hodnotu buď z kanálu c nabo cs. Jedna nebo druhá
    // nastartovaná go-rutina skončila a další zůstane blokovaná.

    naučSeProgramovatWeb() // Go to umí. A vy to chcete taky.
}

// jen jedna funkce z balíčku http spustí web server.
func naučSeProgramovatWeb() {

    // První parametr ListenAndServe je TCP adresa, kde poslouchat.
    // Druhý parametr je handler, implementující interace http.Handler.
    go func() {
        err := http.ListenAndServe(":8080", pár{})
        fmt.Println(err) // neignoruj chyby
    }()

    requestServer()
}

// Umožní typ pár stát se http tím, že implementuje její jedinou metodu
// ServeHTTP.
func (p pár) ServeHTTP(w http.ResponseWriter, r *http.Request) {
    // Servíruj data metodou http.ResponseWriter
    w.Write([]byte("Naučil ses Go za y minut!"))
}

func requestServer() {
    resp, err := http.Get("http://localhost:8080")
    fmt.Println(err)
    defer resp.Body.Close()
    body, err := ioutil.ReadAll(resp.Body)
    fmt.Printf("\nWebserver řekl: `%s`", string(body))
}

Kam dále

Vše hlavní o Go se nachází na oficiálních stránkách go. Tam najdete tutoriály, interaktivní konzolu a mnoho materiálu ke čtení. Kromě úvodu, dokumenty tam obsahují jak psát čistý kód v Go popis balíčků (package), dokumentaci příkazové řádky a historii releasů.

Také doporučujeme přečíst si definici jazyka. Je čtivá a překvapivě krátká. Tedy alespoň proti jiným současným jazyků.

Pokud si chcete pohrát s Go, tak navštivte hřiště Go. Můžete tam spouštět programy s prohlížeče. Také můžete https://go.dev/play/ použít jako REPL, kde si v rychlosti vyzkoušíte věci, bez instalace Go.

Na vašem knižním seznamu, by neměly chybět zdrojáky stadardní knihovny. Důkladně popisuje a dokumentuje Go, styl zápisu Go a Go idiomy. Pokud kliknete na dokumentaci tak se podíváte na dokumentaci.

Dalším dobrým zdrojem informací je Go v ukázkách.

Go mobile přidává podporu pro Android a iOS. Můžete s ním psát nativní mobilní aplikace nebo knihovny, které půjdou spustit přes Javu (pro Android), nebo Objective-C (pro iOS). Navštivte web Go Mobile pro více informací.


Máš připomínku, nebo si našel chybu? Otevři tiket v GitHub repozitáři, nebo pošli rovnou svojí úpravu!

Autor původní verze je Sonia Keys a na úpravách se podílelo 10 lidí.