Skapa funktioner

Slutförd

Med funktionerna i Go kan du gruppera en uppsättning instruktioner som du kan anropa från andra delar av programmet. I stället för att skapa ett program med många instruktioner kan du använda funktioner för att organisera koden och göra den mer läsbar. Mer läsbar kod är också mer underhållsbar.

Fram tills nu har vi anropat fmt.Println() funktionen och vi har skrivit kod i main() funktionen. I det här avsnittet ska vi utforska hur du kan skapa anpassade funktioner. Vi ska också titta på några andra tekniker som du kan använda med funktioner i Go.

Huvudfunktion

Funktionen som du har interagerat med är funktionen main() . Alla körbara program i Go har den här funktionen eftersom det är programmets startpunkt. Du kan bara ha en main() funktion i programmet. Om du skapar ett Go-paket behöver du inte skriva en main() funktion. Vi ska titta på hur du skapar paket i en kommande modul.

Innan vi går in på grunderna för att skapa anpassade funktioner i Go ska vi titta på en viktig aspekt av main() funktionen. Som du kanske har märkt main() har funktionen inga parametrar och returnerar ingenting. Men det betyder inte att det inte kan läsa värden från användaren, till exempel kommandoradsargument. Om du behöver komma åt kommandoradsargument i Go kan du göra det med os-paketet och variabeln os.Args , som innehåller alla argument som skickas till programmet.

Följande kod läser två tal från kommandoraden och summerar dem:

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

Variabeln os.Args innehåller alla kommandoradsargument som skickas till programmet. Eftersom dessa värden är av typen stringmåste du konvertera dem till int för att summera dem.

Om du vill köra programmet använder du det här kommandot:

go run main.go 3 5

Så här ser utdata ut:

Sum: 8

Nu ska vi se hur vi kan omstrukturera koden ovan och skapa vår första anpassade funktion.

Anpassade funktioner

Här är syntaxen för att skapa en funktion:

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

Observera att du använder nyckelordet func för att definiera en funktion och sedan tilldela den ett namn. Efter namnet anger du listan med parametrar för funktionen. Det kan finnas noll eller fler parametrar. Du kan också definiera returtyperna för funktionen, där det också kan finnas noll eller mer. (Vi ska prata om att returnera flera värden i nästa avsnitt.) Och när du har definierat alla dessa värden skriver du brödtextinnehållet i funktionen.

För att öva på den här tekniken ska vi omstrukturera föregående avsnitts kod för att summera talen i en anpassad funktion. Vi använder den här koden:

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
}

Den här koden skapar en funktion med namnet sum som tar två string argument, omvandlar dem till intoch returnerar sedan resultatet av att summera dem. När du definierar en returtyp måste funktionen returnera ett värde av den typen.

I Go kan du också ange ett namn till returvärdet för en funktion, som om det vore en variabel. Du kan till exempel omstrukturera sum funktionen så här:

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

Observera att du nu måste omsluta resultatvärdet för funktionen inom parentes. Du kan också använda variabeln inuti funktionen och bara lägga till en return rad i slutet. Go returnerar de aktuella värdena för dessa returvariabler. Enkelheten att skriva nyckelordet return i slutet av funktionen är tilltalande (särskilt när det finns mer än ett returvärde). Vi rekommenderar inte den här metoden. Det kan vara oklart vad funktionen returnerar.

Returnera flera värden

I Go kan en funktion returnera mer än ett värde. Du kan definiera dessa värden på ett sätt som liknar hur du definierar funktionens parametrar. Med andra ord anger du en typ och ett namn, men namnet är valfritt.

Anta till exempel att du vill skapa en funktion som summerar två tal men också multiplicerar dem. Funktionskoden skulle se ut så här:

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
}

Nu behöver du två variabler för att lagra resultatet av funktionen. (Annars kompileras den inte.) Så här ser det ut:

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

En annan intressant funktion i Go är att om du inte behöver något av returvärdena från en funktion kan du ta bort det genom att tilldela det returnerade värdet till variabeln _ . Variabeln _ är det idiomatiska sättet för Go att ignorera returvärden. Det gör att programmet kan kompilera. Så om du bara vill ha summavärdet kan du använda den här koden:

package main

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

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

Vi tittar mer på att ignorera returvärden från funktioner när vi utforskar felhantering i en kommande modul.

Ändra funktionsparametervärden (pekare)

När du skickar ett värde till en funktion påverkas inte anroparen av varje ändring i funktionen. Go är ett programmeringsspråk för "pass by value". När du skickar ett värde till en funktion tar Go det värdet och skapar en lokal kopia (en ny variabel i minnet). Ändringar som du gör i variabeln i funktionen påverkar inte den som du skickade till funktionen.

Anta till exempel att du skapar en funktion för att uppdatera en persons namn. Observera vad som händer när du kör den här koden:

package main

import "fmt"

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

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

Även om du har ändrat namnet till "David" i funktionen är utdata fortfarande "John". Utdata har inte ändrats eftersom ändringen i updateName funktionen endast ändrar den lokala kopian. Go skickade värdet för variabeln, inte själva variabeln.

Om du vill att den ändring du gör i updateName funktionen ska påverka variabeln firstName main i funktionen måste du använda en pekare. En pekare är en variabel som innehåller minnesadressen för en annan variabel. När du skickar en pekare till en funktion skickar du inte ett värde, utan skickar en minnesadress. Så varje ändring du gör i den variabeln påverkar anroparen.

I Go finns det två operatorer för att arbeta med pekare:

  • Operatorn & tar adressen till det objekt som följer det.
  • Operatorn * avrefererar en pekare. Det ger dig åtkomst till objektet på den adress som finns i pekaren.

Nu ska vi ändra vårt tidigare exempel för att klargöra hur pekare fungerar:

package main

import "fmt"

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

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

Kör föregående kod. Observera att utdata nu visas David i stället för John.

Det första du måste göra är att ändra funktionens signatur för att indikera att du vill ta emot en pekare. Det gör du genom att ändra parametertypen från string till *string. (Det senare är fortfarande en sträng, men nu är det en pekare till en sträng.) När du sedan tilldelar variabeln ett nytt värde måste du lägga till stjärnan (*) till vänster om variabeln för att ge variabelns värde. När du anropar updateName funktionen skickar du inte värdet utan minnesadressen för variabeln. Symbolen & till vänster om variabeln anger variabelns adress.