Vytvoření funkcí
Funkce v go umožňují seskupit sadu příkazů, které můžete volat z jiných částí aplikace. Místo vytváření programu s mnoha příkazy můžete pomocí funkcí uspořádat kód a lépe ho číst. Čitelnější kód je také lépe udržovatelný.
Až do této chvíle voláme fmt.Println()
funkci a do funkce píšeme kód main()
. V této části se podíváme, jak můžete vytvářet vlastní funkce. Podíváme se také na některé další techniky, které můžete použít s funkcemi v Go.
Hlavní funkce
Funkce, se kterou jste pracovali, je funkce main()
. Všechny spustitelné programy v go mají tuto funkci, protože je to výchozí bod programu. V programu můžete mít jenom jednu main()
funkci. Pokud vytváříte balíček Go, nemusíte psát main()
funkci. Podíváme se, jak vytvořit balíčky v nadcházejícím modulu.
Než se pustíme do základů vytváření vlastních funkcí v Go, podíváme se na jeden zásadní aspekt main()
funkce. Jak jste si možná všimli, main()
funkce nemá žádné parametry a nevrací nic. To ale neznamená, že nemůže číst hodnoty od uživatele, jako jsou argumenty příkazového řádku. Pokud potřebujete získat přístup k argumentům příkazového řádku v Go, můžete to udělat pomocí balíčku operačního os.Args
systému a proměnné, která obsahuje všechny argumenty předané programu.
Následující kód načte dvě čísla z příkazového řádku a sečte je:
package main
import (
"fmt"
"os"
"strconv"
)
func main() {
number1, _ := strconv.Atoi(os.Args[1])
number2, _ := strconv.Atoi(os.Args[2])
fmt.Println("Sum:", number1+number2)
}
Proměnná os.Args
obsahuje každý argument příkazového řádku předaný programu. Vzhledem k tomu, že tyto hodnoty jsou typu string
, je třeba je převést na int
sečtení.
Pokud chcete program spustit, použijte tento příkaz:
go run main.go 3 5
Tady je výstup:
Sum: 8
Pojďme se podívat, jak můžeme refaktorovat výše uvedený kód a vytvořit první vlastní funkci.
Vlastní funkce
Tady je syntaxe pro vytvoření funkce:
func name(parameters) (results) {
body-content
}
Všimněte si, že pomocí klíčového func
slova definujete funkci a pak k ní přiřadíte název. Za názvem zadáte seznam parametrů funkce. Může existovat nula nebo více parametrů. Můžete také definovat návratové typy funkce, z nichž může být také nula nebo více. (Budeme mluvit o vrácení více hodnot v další části.) A po definování všech těchto hodnot napíšete základní obsah funkce.
Abychom si tuto techniku procvičily, pojďme refaktorovat kód předchozí části tak, aby sečetl čísla ve vlastní funkci. Použijeme tento kód:
package main
import (
"fmt"
"os"
"strconv"
)
func main() {
sum := sum(os.Args[1], os.Args[2])
fmt.Println("Sum:", sum)
}
func sum(number1 string, number2 string) int {
int1, _ := strconv.Atoi(number1)
int2, _ := strconv.Atoi(number2)
return int1 + int2
}
Tento kód vytvoří funkci, sum
která přebírá dva string
argumenty, přetypuje je na int
a vrátí výsledek jejich sečtení. Když definujete návratový typ, musí funkce vrátit hodnotu tohoto typu.
V go můžete také nastavit název na návratovou hodnotu funkce, jako by to byla proměnná. Můžete například refaktorovat sum
funkci takto:
func sum(number1 string, number2 string) (result int) {
int1, _ := strconv.Atoi(number1)
int2, _ := strconv.Atoi(number2)
result = int1 + int2
return
}
Všimněte si, že teď potřebujete uzavřít výslednou hodnotu funkce do závorek. Můžete také použít proměnnou uvnitř funkce a přidat řádek return
na konec. Go vrátí aktuální hodnoty těchto vrácených proměnných. Jednoduchost psaní return
klíčového slova na konci funkce je atraktivní (zejména pokud existuje více než jedna návratová hodnota). Tento přístup nedoporučujeme. Může být nejasné, co funkce vrací.
Vrácení více hodnot
Funkce v go může vrátit více než jednu hodnotu. Tyto hodnoty můžete definovat způsobem, který je podobný tomu, jak definujete parametry funkce. Jinými slovy, zadáte typ a název, ale název je nepovinný.
Řekněme například, že chcete vytvořit funkci, která sečte dvě čísla, ale také je vynásobí. Kód funkce by vypadal takto:
func calc(number1 string, number2 string) (sum int, mul int) {
int1, _ := strconv.Atoi(number1)
int2, _ := strconv.Atoi(number2)
sum = int1 + int2
mul = int1 * int2
return
}
K uložení výsledků funkce teď potřebujete dvě proměnné. (Jinak se nezkompiluje.) Vypadá takto:
package main
import (
"fmt"
"os"
"strconv"
)
func main() {
sum, mul := calc(os.Args[1], os.Args[2])
fmt.Println("Sum:", sum)
fmt.Println("Mul:", mul)
}
Další zajímavou funkcí v Go je, že pokud nepotřebujete některou z vrácených hodnot funkce, můžete ji zahodit přiřazením návratové hodnoty k _
proměnné. Proměnná _
je idiomatickou cestou, jak go ignorovat návratové hodnoty. Umožňuje, aby se program zkompiloval. Pokud tedy chcete jenom hodnotu součtu, můžete použít tento kód:
package main
import (
"fmt"
"os"
"strconv"
)
func main() {
sum, _ := calc(os.Args[1], os.Args[2])
fmt.Println("Sum:", sum)
}
Když prozkoumáme zpracování chyb v nadcházejícím modulu, podíváme se na ignorování návratových hodnot z funkcí.
Změna hodnot parametrů funkce (ukazatele)
Když funkci předáte hodnotu, nebude mít každá změna této funkce vliv na volajícího. Go je programovací jazyk "pass by value". Pokaždé, když funkci předáte hodnotu, go vezme tuto hodnotu a vytvoří místní kopii (novou proměnnou v paměti). Změny provedené v této proměnné ve funkci nemají vliv na ten, který jste do funkce odeslali.
Řekněme například, že vytvoříte funkci, která aktualizuje jméno osoby. Všimněte si, co se stane, když spustíte tento kód:
package main
import "fmt"
func main() {
firstName := "John"
updateName(firstName)
fmt.Println(firstName)
}
func updateName(name string) {
name = "David"
}
I když jste ve funkci změnili název na "David", výstup je stále "John". Výstup se nezměnil, protože změna funkce updateName
upraví pouze místní kopii. Přejděte předanou hodnotu proměnné, nikoli samotnou proměnnou.
Pokud chcete, aby změna, kterou ve updateName
funkci provedete, ovlivnila firstName
proměnnou ve main
funkci, musíte použít ukazatel. Ukazatel je proměnná, která obsahuje adresu paměti jiné proměnné. Když pošlete ukazatel na funkci, nepředáváte hodnotu, předáváte adresu paměti. Každá změna, kterou v této proměnné uděláte, má vliv na volajícího.
V Go existují dva operátory pro práci s ukazateli:
- Operátor
&
přebírá adresu objektu, který jej následuje. - Operátor
*
dereference ukazatel. Poskytuje přístup k objektu na adrese obsažené v ukazateli.
Pojďme upravit předchozí příklad, abychom si vysvětlili, jak fungují ukazatele:
package main
import "fmt"
func main() {
firstName := "John"
updateName(&firstName)
fmt.Println(firstName)
}
func updateName(name *string) {
*name = "David"
}
Spusťte předchozí kód. Všimněte si, že výstup se teď zobrazuje David
místo John
.
První věc, kterou musíte udělat, je upravit podpis funkce tak, aby indikovaly, že chcete přijmout ukazatel. Chcete-li to provést, změňte typ parametru z string
na *string
. (Druhý je stále řetězec, ale teď je to ukazatel na řetězec.) Když pak této proměnné přiřadíte novou hodnotu, musíte přidat hvězdičku (*
) na levé straně proměnné, abyste mohli hodnotu této proměnné získat. Při volání updateName
funkce neodesíláte hodnotu, ale adresu paměti proměnné. Symbol &
na levé straně proměnné označuje adresu proměnné.