Verwendung variabler Funktionen in Go

Einführung

Avariadic function ist eine Funktion, die null, einen oder mehrere Werte als einzelnes Argument akzeptiert. Variadische Funktionen sind zwar nicht üblich, können jedoch verwendet werden, um den Code übersichtlicher und lesbarer zu gestalten.

Variadische Funktionen sind häufiger als es scheint. Die häufigste ist diePrintln-Funktion aus demfmt-Paket.

func Println(a ...interface{}) (n int, err error)

Einfunction mit einem Parameter, dem eine Reihe von Ellipsen (...) vorangestellt ist, wird als variadische Funktion betrachtet. Die Auslassungspunkte bedeuten, dass der bereitgestellte Parameter Null, Eins oder mehrere Werte sein kann. Für das Paketfmt.Println wird angegeben, dass der Parametera variadisch ist.

Erstellen wir ein Programm, das die Funktionfmt.Printlnverwendet und null, einen oder mehrere Werte übergibt:

print.go

package main

import "fmt"

func main() {
    fmt.Println()
    fmt.Println("one")
    fmt.Println("one", "two")
    fmt.Println("one", "two", "three")
}

Wenn wir zum ersten Malfmt.Println aufrufen, übergeben wir keine Argumente. Wenn wirfmt.Println zum zweiten Mal aufrufen, übergeben wir nur ein einziges Argument mit dem Wertone. Dann übergeben wirone undtwo und schließlichone,two undthree.

Führen Sie das Programm mit dem folgenden Befehl aus:

go run print.go

Wir werden die folgende Ausgabe sehen:

Output
one
one two
one two three

Die erste Zeile der Ausgabe ist leer. Dies liegt daran, dass wir beim ersten Aufruf vonfmt.Println keine Argumente übergeben haben. Beim zweiten Mal wurde der Wert vonone gedruckt. Dannone undtwo und schließlichone,two undthree.

Nachdem wir gesehen haben, wie man eine Variadic-Funktion aufruft, wollen wir uns ansehen, wie wir unsere eigene Variadic-Funktion definieren können.

Definieren einer variablen Funktion

Wir können eine variadische Funktion definieren, indem wir eine Ellipse (...) vor dem Argument verwenden. Erstellen wir ein Programm, das Personen begrüßt, wenn ihre Namen an die Funktion gesendet werden:

hello.go

package main

import "fmt"

func main() {
    sayHello()
    sayHello("Sammy")
    sayHello("Sammy", "Jessica", "Drew", "Jamie")
}

func sayHello(names ...string) {
    for _, n := range names {
        fmt.Printf("Hello %s\n", n)
    }
}

Wir haben einesayHello-Funktion erstellt, die nur einen einzigen Parameter namensnames akzeptiert. Der Parameter ist variadisch, da vor dem Datentyp eine Ellipse (...) steht:...string. Dies teilt Go mit, dass die Funktion null, ein oder viele Argumente akzeptieren kann.

Die FunktionsayHello empfängt den Parameternames alsslice. Da der Datentypstring ist, kann der Parameternames wie ein Stück Zeichenfolgen ([]string) innerhalb des Funktionskörpers behandelt werden. Wir können eine Schleife mit dem Operatorrangeerstellen und durch das String-Slice iterieren.

Wenn wir das Programm ausführen, erhalten wir die folgende Ausgabe:

OutputHello Sammy
Hello Sammy
Hello Jessica
Hello Drew
Hello Jamie

Beachten Sie, dass beim ersten Aufruf vonsayHello nichts gedruckt wurde. Dies liegt daran, dass der variadische Parameter ein leererslice vonstring war. Da wir das Slice durchlaufen, gibt es nichts zu durchlaufen, undfmt.Printf wird niemals aufgerufen.

Ändern Sie das Programm, um festzustellen, dass keine Werte gesendet wurden:

hello.go

package main

import "fmt"

func main() {
    sayHello()
    sayHello("Sammy")
    sayHello("Sammy", "Jessica", "Drew", "Jamie")
}

func sayHello(names ...string) {
    if len(names) == 0 {
        fmt.Println("nobody to greet")
        return
    }
    for _, n := range names {
        fmt.Printf("Hello %s\n", n)
    }
}

Wenn Sie nunif statement verwenden und keine Werte übergeben werden, beträgt die Länge vonnames0, und wir druckennobody to greet aus:

Outputnobody to greet
Hello Sammy
Hello Sammy
Hello Jessica
Hello Drew
Hello Jamie

Die Verwendung eines variablen Parameters kann den Code lesbarer machen. Erstellen wir eine Funktion, die Wörter mit einem bestimmten Trennzeichen verbindet. Wir werden dieses Programm zunächst ohne eine Variadic-Funktion erstellen, um zu zeigen, wie es lauten würde:

join.go

package main

import "fmt"

func main() {
    var line string

    line = join(",", []string{"Sammy", "Jessica", "Drew", "Jamie"})
    fmt.Println(line)

    line = join(",", []string{"Sammy", "Jessica"})
    fmt.Println(line)

    line = join(",", []string{"Sammy"})
    fmt.Println(line)
}

func join(del string, values []string) string {
    var line string
    for i, v := range values {
        line = line + v
        if i != len(values)-1 {
            line = line + del
        }
    }
    return line
}

In diesem Programm übergeben wir ein Komma (,) als Trennzeichen an die Funktionjoin. Dann übergeben wir eine Werteschicht, um sie zu verbinden. Hier ist die Ausgabe:

OutputSammy,Jessica,Drew,Jamie
Sammy,Jessica
Sammy

Da die Funktion einen String-String alsvalues-Parameter verwendet, mussten wir alle unsere Wörter in einen Slice einschließen, als wir diejoin-Funktion aufriefen. Dies kann das Lesen des Codes erschweren.

Schreiben wir nun die gleiche Funktion, verwenden jedoch eine andere Funktion:

join.go

package main

import "fmt"

func main() {
    var line string

    line = join(",", "Sammy", "Jessica", "Drew", "Jamie")
    fmt.Println(line)

    line = join(",", "Sammy", "Jessica")
    fmt.Println(line)

    line = join(",", "Sammy")
    fmt.Println(line)
}

func join(del string, values ...string) string {
    var line string
    for i, v := range values {
        line = line + v
        if i != len(values)-1 {
            line = line + del
        }
    }
    return line
}

Wenn wir das Programm ausführen, können wir sehen, dass wir die gleiche Ausgabe wie das vorherige Programm erhalten:

OutputSammy,Jessica,Drew,Jamie
Sammy,Jessica
Sammy

Während beide Versionen derjoin-Funktion programmgesteuert genau dasselbe tun, ist die variable Version der Funktion beim Aufrufen viel einfacher zu lesen.

Variadic Argument Order

Sie können nur einen variablen Parameter in einer Funktion haben, und es muss der letzte in der Funktion definierte Parameter sein. Das Definieren von Parametern in einer variablen Funktion in einer anderen Reihenfolge als dem letzten Parameter führt zu einem Kompilierungsfehler:

join.go

package main

import "fmt"

func main() {
    var line string

    line = join(",", "Sammy", "Jessica", "Drew", "Jamie")
    fmt.Println(line)

    line = join(",", "Sammy", "Jessica")
    fmt.Println(line)

    line = join(",", "Sammy")
    fmt.Println(line)
}

func join(values ...string, del string) string {
    var line string
    for i, v := range values {
        line = line + v
        if i != len(values)-1 {
            line = line + del
        }
    }
    return line
}

Dieses Mal setzen wir den Parametervalues zuerst in die Funktionjoin. Das wird den folgenden Kompilierungsfehler verursachen:

Output./join_error.go:18:11: syntax error: cannot use ... with non-final parameter values

Bei der Definition einer variablen Funktion kann nur der letzte Parameter variabel sein.

Explodierende Argumente

Bisher haben wir gesehen, dass wir einer variadischen Funktion keinen, einen oder mehrere Werte übergeben können. Es wird jedoch Gelegenheiten geben, in denen wir einen Wertebereich haben und diese an eine variable Funktion senden möchten.

Schauen wir uns unserejoin-Funktion aus dem letzten Abschnitt an, um zu sehen, was passiert:

join.go

package main

import "fmt"

func main() {
    var line string

    names := []string{"Sammy", "Jessica", "Drew", "Jamie"}

    line = join(",", names)
    fmt.Println(line)
}

func join(del string, values ...string) string {
    var line string
    for i, v := range values {
        line = line + v
        if i != len(values)-1 {
            line = line + del
        }
    }
    return line
}

Wenn wir dieses Programm ausführen, erhalten wir einen Kompilierungsfehler:

Output./join-error.go:10:14: cannot use names (type []string) as type string in argument to join

Obwohl die Variadic-Funktion den Parameter vonvalues ...string in ein Slice von Strings[]string konvertiert, können wir kein Slice von Strings als Argument übergeben. Dies liegt daran, dass der Compiler diskrete Argumente von Zeichenfolgen erwartet.

Um dies zu umgehen, können wirexplode ein Slice erstellen, indem wir es mit einer Reihe von Ellipsen (...) versehen und in diskrete Argumente umwandeln, die an eine variable Funktion übergeben werden:

join.go

package main

import "fmt"

func main() {
    var line string

    names := []string{"Sammy", "Jessica", "Drew", "Jamie"}

    line = join(",", names...)
    fmt.Println(line)
}

func join(del string, values ...string) string {
    var line string
    for i, v := range values {
        line = line + v
        if i != len(values)-1 {
            line = line + del
        }
    }
    return line
}

Dieses Mal, als wir diejoin-Funktion aufgerufen haben, haben wir dienames-Schicht durch Anhängen von Ellipsen (...) aufgelöst.

Dadurch kann das Programm nun wie erwartet ausgeführt werden:

OutputSammy,Jessica,Drew,Jamie

Es ist wichtig zu beachten, dass wir immer noch ein, ein oder mehrere Argumente sowie einen Teil, den wir explodieren, übergeben können. Hier ist der Code, der alle Variationen übergibt, die wir bisher gesehen haben:

join.go

package main

import "fmt"

func main() {
    var line string

    line = join(",", []string{"Sammy", "Jessica", "Drew", "Jamie"}...)
    fmt.Println(line)

    line = join(",", "Sammy", "Jessica", "Drew", "Jamie")
    fmt.Println(line)

    line = join(",", "Sammy", "Jessica")
    fmt.Println(line)

    line = join(",", "Sammy")
    fmt.Println(line)

}

func join(del string, values ...string) string {
    var line string
    for i, v := range values {
        line = line + v
        if i != len(values)-1 {
            line = line + del
        }
    }
    return line
}
OutputSammy,Jessica,Drew,Jamie
Sammy,Jessica,Drew,Jamie
Sammy,Jessica
Sammy

Wir wissen jetzt, wie wir null, ein oder mehrere Argumente sowie einen Teil, den wir auflösen, an eine variable Funktion übergeben können.

Fazit

In diesem Tutorial haben wir gesehen, wie verschiedene Funktionen Ihren Code sauberer machen können. Sie müssen sie zwar nicht immer verwenden, finden sie jedoch möglicherweise nützlich:

  • Wenn Sie feststellen, dass Sie ein temporäres Segment erstellen, um es an eine Funktion zu übergeben.

  • Wenn die Anzahl der Eingabeparameter unbekannt ist oder beim Aufrufen variiert.

  • Um Ihren Code lesbarer zu machen.

Um mehr über das Erstellen und Aufrufen von Funktionen zu erfahren, können SieHow to Define and Call Functions in Go lesen.