Erstellen von Funktionen

Abgeschlossen

In Go können Sie mithilfe von Funktionen Anweisungen gruppieren, die Sie über andere Teile Ihrer Anwendung aufrufen können. Anstatt ein Programm mit vielen Anweisungen zu erstellen, können Sie Funktionen verwenden, um den Code zu organisieren und besser lesbar zu machen. Wenn Ihr Code besser lesbar ist, kann er auch besser verwaltet werden.

Bisher haben wird die Funktion fmt.Println() aufgerufen und Code in die Funktion main() geschrieben. In diesem Abschnitt erfahren Sie, wie Sie benutzerdefinierte Funktionen erstellen können. Außerdem erhalten Sie Informationen zu einigen anderen Techniken für Funktionen in Go.

main-Funktion

Die Funktion, mit der Sie bisher interagiert haben, ist die Funktion main(). Alle ausführbaren Programme in Go weisen diese Funktion auf, weil es sich hierbei um den Startpunkt des Programms handelt. Ihr Programm darf nur eine main()-Funktion enthalten. Wenn Sie ein Go-Paket erstellen, müssen Sie keine main()-Funktion schreiben. In einem späteren Modul erfahren Sie, wie Sie Pakete erstellen können.

Bevor wir uns mit den Grundlagen der Erstellung von benutzerdefinierten Funktionen in Go befassen, möchten wir auf einen wichtigen Aspekt der main()-Funktion eingehen. Wie Sie vielleicht bemerkt haben, hat die Funktion main() keine Parameter und gibt keine Ergebnisse zurück. Das bedeutet jedoch nicht, dass sie keine Werte vom Benutzer lesen kann, z. B. Befehlszeilenargumente. Wenn Sie in Go auf Befehlszeilenargumente zugreifen müssen, können Sie hierfür das Paket os und die Variable os.Args verwenden, die alle Argumente enthält, die an das Programm übergeben werden.

Der folgende Code liest zwei Zahlen über die Befehlszeile und fasst sie zusammen:

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)
}

Die Variable os.Args enthält alle Befehlszeilenargumente, die an das Programm übergeben werden. Da diese Werte vom Typ string sind, müssen Sie sie in int konvertieren, um sie zu addieren.

Verwenden Sie den folgenden Befehl, um das Programm auszuführen:

go run main.go 3 5

So sieht die Ausgabe aus:

Sum: 8

Im folgenden Abschnitt erfahren Sie, wie Sie den obenstehenden Code umgestalten und Ihre erste benutzerdefinierte Funktion erstellen können.

Benutzerdefinierte Funktionen

Die Syntax zum Erstellen einer Funktion lautet wie folgt:

func name(parameters) (results) {
    body-content
}

Verwenden Sie das Schlüsselwort func, um eine Funktion zu definieren und ihr einen Namen zuzuweisen. Geben Sie nach dem Namen die Liste der Parameter für die Funktion an. Diese kann 0 (null) oder mehr Parameter umfassen. Ebenfalls können Sie die Rückgabetypen der Funktion definieren. Davon kann es auch 0 oder mehr geben. Im nächsten Abschnitt erhalten Sie Informationen zur Rückgabe mehrerer Werte. Schreiben Sie den Textinhalt der Funktion, nachdem Sie alle diese Werte definiert haben.

Gestalten Sie zur Übung den Code aus dem letzten Abschnitt um, um die Zahlen in einer benutzerdefinierten Funktion zu addieren. Verwenden Sie diesen Code:

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
}

Mit diesem Code wird eine Funktion namens sum erstellt, die zwei string Argumente übernimmt, in int umwandelt und dann das Ergebnis der Summierung zurückgibt. Wenn Sie einen Rückgabetyp definieren, muss die Funktion einen Wert dieses Typs zurückgeben.

In Go können Sie auch wie bei Variablen einen Namen für den Rückgabewert einer Funktion festlegen. Beispielsweise könnten Sie die Funktion sum wie folgt umgestalten:

func sum(number1 string, number2 string) (result int) {
    int1, _ := strconv.Atoi(number1)
    int2, _ := strconv.Atoi(number2)
    result = int1 + int2
    return
}

Hier müssen Sie den Ergebniswert der Funktion in Klammern setzen. Sie können auch die Variable innerhalb der Funktion verwenden und einfach eine return-Zeile am Ende hinzufügen. Go gibt die aktuellen Werte dieser Rückgabevariablen zurück. Es erscheint verlockend einfach, das Schlüsselwort return ans Ende der Funktion zu setzen (insbesondere bei mehreren Rückgabewerten). Dieser Ansatz wird nicht empfohlen. Es kann unklar sein, was die Funktion zurückgibt.

Zurückgeben mehrerer Werte

In Go kann eine Funktion mehr als einen Wert zurückgeben. Sie können diese Werte ähnlich wie die Parameter der Funktion definieren. Anders ausgedrückt: Sie geben einen Typ und einen Namen an, aber der Name ist optional.

Angenommen, Sie möchten eine Funktion erstellen, die zwei Zahlen nicht nur addiert, sondern auch multipliziert. Der Funktionscode würde dann wie folgt aussehen:

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
}

Sie benötigen hier zwei Variablen, um die Ergebnisse der Funktion zu speichern. Andernfalls erfolgt keine Kompilierung. Das Fenster sieht so aus:

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)
}

Ein weiteres interessantes Feature in Go ist, dass Sie nicht benötigte Rückgabewerte aus einer Funktion verwerfen können, indem Sie sie der Variablen _ zuweisen. Die Variable _ ist die idiomatische Methode für Go, um Rückgabewerte zu ignorieren. Mit ihr kann das Programm kompiliert werden. Wenn Sie also nur den sum-Wert verwenden möchten, können Sie diesen Code verwenden:

package main

import (
    "fmt"
    "os"
    "strconv"
)

func main() {
    sum, _ := calc(os.Args[1], os.Args[2])
    fmt.Println("Sum:", sum)
}

In einem der nächsten Module, in dem es um die Fehlerbehandlung geht, erfahren Sie mehr über das Ignorieren von Rückgabewerten.

Ändern von Funktionsparameterwerten (Zeiger)

Wenn Sie einen Wert an eine Funktion übergeben, haben Änderungen in dieser Funktion keine Auswirkungen auf die aufrufende Funktion. Go ist eine Pass-by-Value-Programmiersprache. Wenn Sie einen Wert an eine Funktion übergeben, akzeptiert Go diesen Wert und erstellt eine lokale Kopie (eine neue Variable im Arbeitsspeicher). Änderungen, die Sie in der Funktion an dieser Variablen vornehmen, haben keine Auswirkung auf die Variable, die Sie an die Funktion gesendet haben.

Nehmen wir beispielsweise an, Sie erstellen eine Funktion, um den Namen einer Person zu aktualisieren. Testen Sie, was geschieht, wenn Sie diesen Code ausführen:

package main

import "fmt"

func main() {
    firstName := "John"
    updateName(firstName)
    fmt.Println(firstName)
}

func updateName(name string) {
    name = "David"
}

Obwohl Sie den Namen in der Funktion in „David“ geändert haben, lautet die Ausgabe weiterhin „John“. Die Ausgabe hat sich nicht geändert, da durch Ihre Änderung der updateName-Funktion nur die lokale Kopie geändert wird. Go hat den Wert der Variablen und nicht die Variable selbst übergeben.

Wenn Sie möchten, dass die Änderungen, die Sie in der Funktion updateName vornehmen, auch die Variable firstName in der Funktion main betreffen, müssen Sie einen Zeiger verwenden. Ein Zeiger ist eine Variable, die die Speicheradresse einer anderen Variablen enthält. Wenn Sie einen Zeiger an eine Funktion senden, übergeben Sie keinen Wert, sondern eine Speicheradresse. Deshalb wirkt sich jede Änderung an dieser Variablen auf den Aufrufer aus.

In Go gibt es zwei Operatoren für die Arbeit mit Zeigern:

  • Der Operator & übernimmt die Adresse des ihm nachfolgenden Objekts.
  • Der Operator * dereferenziert einen Zeiger. Sie erhalten Zugriff auf das Objekt an der Adresse, die im Zeiger enthalten ist.

Zur Verdeutlichung der Funktionsweise von Zeigern ändern wir das obige Beispiel wie folgt:

package main

import "fmt"

func main() {
    firstName := "John"
    updateName(&firstName)
    fmt.Println(firstName)
}

func updateName(name *string) {
    *name = "David"
}

Führen Sie den obigen Code aus. Die Ausgabe zeigt nun David anstelle von John an.

Als Erstes müssen Sie die Signatur der Funktion ändern, um anzugeben, dass Sie einen Zeiger erhalten möchten. Ändern Sie zu diesem Zweck den Parametertyp von string in *string. Letzterer ist immer noch eine Zeichenfolge, aber es handelt sich jetzt um einen Zeiger auf eine Zeichenfolge. Wenn Sie dieser Variablen einen neuen Wert zuweisen, müssen Sie das Sternchen (*) vor die Variable setzen, um den Wert dieser Variablen zu erhalten. Wenn Sie die Funktion updateName aufrufen, senden Sie nicht den Wert, sondern die Speicheradresse der Variablen. Das &-Symbol auf der linken Seite der Variablen gibt die Adresse der Variablen an.