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 6 lidí.