Tworzenie funkcji
W języku Go funkcje umożliwiają grupowanie zestawu instrukcji, które można wywołać z innych części aplikacji. Zamiast tworzyć program z wieloma instrukcjami, można użyć funkcji do organizowania kodu i zwiększenia czytelności. Bardziej czytelny kod jest również bardziej czytelny.
Do tego momentu wywołaliśmy fmt.Println()
funkcję i pisaliśmy kod w main()
funkcji. W tej sekcji dowiesz się, jak tworzyć funkcje niestandardowe. Przyjrzymy się również innym technikom, których można używać z funkcjami w języku Go.
Funkcja Main
Funkcja, z którą korzystasz, jest funkcją main()
. Wszystkie programy wykonywalne w języku Go mają tę funkcję, ponieważ jest to punkt początkowy programu. W programie można mieć tylko jedną main()
funkcję. Jeśli tworzysz pakiet języka Go, nie musisz pisać main()
funkcji. Przyjrzymy się, jak tworzyć pakiety w nadchodzącym module.
Zanim przejdziemy do podstaw tworzenia funkcji niestandardowych w języku Go, przyjrzyjmy się jednemu kluczowemu aspektowi main()
funkcji. Jak można zauważyć, main()
funkcja nie ma żadnych parametrów i nie zwraca żadnych parametrów. Nie oznacza to jednak, że nie może odczytywać wartości od użytkownika, takich jak argumenty wiersza polecenia. Jeśli musisz uzyskać dostęp do argumentów wiersza polecenia w języku Go, możesz to zrobić za pomocą pakietu systemu operacyjnego i os.Args
zmiennej, która zawiera wszystkie argumenty przekazane do programu.
Poniższy kod odczytuje dwie liczby z wiersza polecenia i sumuje 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)
}
Zmienna os.Args
przechowuje każdy argument wiersza polecenia przekazany do programu. Ponieważ te wartości są typu string
, należy je przekonwertować, aby int
je podsumować.
Aby uruchomić program, użyj następującego polecenia:
go run main.go 3 5
Dane wyjściowe są następujące:
Sum: 8
Zobaczmy, jak możemy refaktoryzować powyższy kod i utworzyć naszą pierwszą funkcję niestandardową.
Funkcje niestandardowe
Oto składnia tworzenia funkcji:
func name(parameters) (results) {
body-content
}
Zwróć uwagę, że słowo kluczowe służy func
do definiowania funkcji, a następnie przypisywania do niej nazwy. Po nazwie należy określić listę parametrów funkcji. Może istnieć zero lub więcej parametrów. Można również zdefiniować zwracane typy funkcji, z których może być również zero lub więcej. (Omówimy zwracanie wielu wartości w następnej sekcji). Po zdefiniowaniu wszystkich tych wartości należy napisać zawartość treści funkcji.
Aby przećwiczyć tę technikę, refaktoryzujmy kod poprzedniej sekcji, aby zsumować liczby w funkcji niestandardowej. Użyjemy tego kodu:
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
}
Ten kod tworzy funkcję o nazwie sum
, która przyjmuje dwa string
argumenty, rzutuje je na int
, a następnie zwraca wynik sumowania ich. Podczas definiowania typu zwracanego funkcja musi zwrócić wartość tego typu.
W języku Go można również ustawić nazwę na wartość zwracaną funkcji, tak jakby była to zmienna. Można na przykład refaktoryzować sum
funkcję w następujący sposób:
func sum(number1 string, number2 string) (result int) {
int1, _ := strconv.Atoi(number1)
int2, _ := strconv.Atoi(number2)
result = int1 + int2
return
}
Zwróć uwagę, że teraz należy ująć wartość wynikową funkcji w nawiasy. Możesz również użyć zmiennej wewnątrz funkcji i po prostu dodać return
wiersz na końcu. Funkcja Go zwróci bieżące wartości tych zmiennych zwracanych. Prostota pisania słowa kluczowego return
na końcu funkcji jest atrakcyjna (zwłaszcza gdy istnieje więcej niż jedna wartość zwracana). Nie zalecamy tego podejścia. Nie jest jasne, co zwraca funkcja.
Zwracanie wielu wartości
W języku Go funkcja może zwrócić więcej niż jedną wartość. Te wartości można zdefiniować w sposób podobny do sposobu definiowania parametrów funkcji. Innymi słowy, należy określić typ i nazwę, ale nazwa jest opcjonalna.
Załóżmy na przykład, że chcesz utworzyć funkcję, która sumuje dwie liczby, ale także je mnoży. Kod funkcji będzie wyglądać następująco:
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
}
Teraz potrzebne są dwie zmienne do przechowywania wyników funkcji. (W przeciwnym razie nie zostanie skompilowany). Oto jak wygląda:
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)
}
Inną interesującą funkcją w języku Go jest to, że jeśli nie potrzebujesz jednej z zwracanych wartości z funkcji, możesz ją odrzucić, przypisując zwracaną wartość do zmiennej _
. Zmienna _
jest idiomatycznym sposobem ignorowania zwracanych wartości przez metodę Go. Umożliwia kompilowanie programu. Dlatego, jeśli chcesz tylko wartość sumy, możesz użyć tego kodu:
package main
import (
"fmt"
"os"
"strconv"
)
func main() {
sum, _ := calc(os.Args[1], os.Args[2])
fmt.Println("Sum:", sum)
}
Przyjrzymy się dokładniej ignorowaniu zwracanych wartości z funkcji podczas eksplorowania obsługi błędów w nadchodzącym module.
Zmienianie wartości parametrów funkcji (wskaźniki)
Po przekazaniu wartości do funkcji każda zmiana tej funkcji nie wpłynie na obiekt wywołujący. Go to język programowania "pass by value". Za każdym razem, gdy przekazujesz wartość do funkcji, go przyjmuje tę wartość i tworzy kopię lokalną (nową zmienną w pamięci). Zmiany wprowadzone w tej zmiennej w funkcji nie mają wpływu na tę zmienną, która została wysłana do funkcji.
Załóżmy na przykład, że tworzysz funkcję w celu zaktualizowania nazwiska osoby. Zwróć uwagę, co się stanie po uruchomieniu tego kodu:
package main
import "fmt"
func main() {
firstName := "John"
updateName(firstName)
fmt.Println(firstName)
}
func updateName(name string) {
name = "David"
}
Mimo że nazwa została zmieniona na "David" w funkcji, dane wyjściowe są nadal "John". Dane wyjściowe nie uległy zmianie, ponieważ zmiana funkcji updateName
modyfikuje tylko kopię lokalną. Funkcja Go przekazała wartość zmiennej, a nie samą zmienną.
Jeśli chcesz, aby zmiana w updateName
funkcji wpływała na firstName
zmienną w main
funkcji, musisz użyć wskaźnika. Wskaźnik to zmienna zawierająca adres pamięci innej zmiennej. Gdy wysyłasz wskaźnik do funkcji, nie przekazujesz wartości, przekazujesz adres pamięci. Dlatego każda zmiana wprowadzana w tej zmiennej ma wpływ na obiekt wywołujący.
W języku Go istnieją dwa operatory do pracy ze wskaźnikami:
- Operator
&
przyjmuje adres obiektu, który następuje po nim. - Operator
*
wyłusza wskaźnik. Zapewnia dostęp do obiektu pod adresem zawartym w wskaźniku.
Zmodyfikujmy nasz poprzedni przykład, aby wyjaśnić, jak działają wskaźniki:
package main
import "fmt"
func main() {
firstName := "John"
updateName(&firstName)
fmt.Println(firstName)
}
func updateName(name *string) {
*name = "David"
}
Uruchom poprzedni kod. Zwróć uwagę, że dane wyjściowe są teraz wyświetlane David
zamiast John
.
Najpierw należy zmodyfikować podpis funkcji, aby wskazać, że chcesz otrzymać wskaźnik. W tym celu zmień typ parametru z string
na *string
. (Ten ostatni jest nadal ciągiem, ale teraz jest to wskaźnik do ciągu). Następnie po przypisaniu nowej wartości do tej zmiennej należy dodać gwiazdkę (*
) po lewej stronie zmiennej, aby uzyskać wartość tej zmiennej. Podczas wywoływania updateName
funkcji nie wysyłasz wartości, ale adresu pamięci zmiennej. Symbol &
po lewej stronie zmiennej wskazuje adres zmiennej.