Tworzenie funkcji

Ukończone

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.