1 Concetti di base delle funzioni

In programmazione, una funzione è un insieme di codice che esegue un'attività specifica e può avere parametri di input e valori restituiti. Nel linguaggio Go, le funzioni sono ampiamente utilizzate per organizzare e riutilizzare il codice. Utilizzando le funzioni in modo efficace, è possibile rendere il codice più conciso, altamente leggibile e facile da mantenere.

Le funzioni sono un componente fondamentale del linguaggio Go e capire come dichiarare e definire le funzioni è cruciale per scrivere codice efficiente e leggibile.

2 Definizione delle funzioni

2.1 Dichiarazione della funzione

Nel linguaggio Go, la forma generale della dichiarazione di una funzione è la seguente:

func nomeFunzione(parametri) tipoRitorno {
    // Corpo della funzione
}

Analizziamo questi componenti:

  1. La parola chiave func è utilizzata per dichiarare una funzione.
  2. nomeFunzione è il nome della funzione, seguendo le convenzioni di denominazione del linguaggio Go. Una funzione con lettera iniziale maiuscola è esportabile, il che significa che è visibile al di fuori del pacchetto; una funzione con lettera iniziale minuscola non è esportabile e può essere utilizzata solo all'interno dello stesso pacchetto.
  3. parametri è l'elenco dei parametri che la funzione riceve, separati da virgole. È necessario specificare il tipo di ciascun parametro e, se più parametri hanno lo stesso tipo, il tipo può essere specificato solo una volta.
  4. tipoRitorno è il tipo del valore restituito dalla funzione. Se la funzione non restituisce un valore, questa parte può essere omessa. Se la funzione restituisce più valori, devono essere racchiusi tra parentesi.

Ad esempio, possiamo dichiarare una semplice funzione per calcolare la somma di due numeri interi:

func Somma(a int, b int) int {
    return a + b
}

In questo esempio, il nome della funzione è Somma, essa prende due parametri di tipo int (a e b) e restituisce la loro somma con tipo di ritorno int.

2.2 Elenco dei parametri

L'elenco dei parametri definisce quali parametri accetta la funzione e il tipo di ciascun parametro. I parametri sono un tipo speciale di variabile utilizzato per passare dati a una funzione.

func Saluta(nome string, eta int) {
    fmt.Printf("Ciao, %s! Hai %d anni.\n", nome, eta)
}

In questa funzione Saluta, nome e eta sono parametri. Il tipo di nome è string, e il tipo di eta è int. Quando si chiama questa funzione, è necessario fornire valori effettivi per questi parametri.

2.3 Tipi di ritorno

Le funzioni possono restituire risultati elaborati e il tipo di ritorno definisce il tipo di dato del valore restituito dalla funzione. Le funzioni possono non avere un valore restituito, oppure possono avere uno o più valori restituiti.

Se una funzione ha più valori restituiti, i loro tipi devono essere racchiusi tra parentesi durante la dichiarazione:

func Dividi(dividendo, divisore float64) (float64, error) {
    if divisore == 0 {
        return 0, errors.New("impossibile dividere per zero")
    }
    return dividendo / divisore, nil
}

La funzione Dividi restituisce qui due valori: il quoziente e un messaggio di errore. Se il divisore è zero, viene restituito un errore.

2.4 Parametri variadici

In Golang, quando non si è sicuri di quante volte il chiamante passerà dei parametri durante la definizione di una funzione, è possibile utilizzare parametri variadici. I parametri variadici sono indicati da un ellissi ..., il che significa che la funzione può accettare un numero qualsiasi di parametri. Questo è molto utile quando si tratta di un quantitativo di dati incerto o nell'implementazione di determinati tipi di funzioni come formattazione, riepilogo o funzionalità di iterazione.

Sce...

Utilizzo dei parametri variabili

Ecco un semplice esempio di utilizzo dei parametri variabili:

// Definire una funzione che prende un numero variabile di parametri interi e restituisce la loro somma
func Somma(nums ...int) int {
    totale := 0
    for _, num := range nums {
        totale += num
    }
    return totale
}

func main() {
    // È possibile passare qualsiasi numero di parametri
    fmt.Println(Somma(1, 2))          // Output: 3
    fmt.Println(Somma(1, 2, 3, 4))    // Output: 10
    
    // È anche possibile passare una slice e utilizzare i tre punti dopo la slice
    numeri := []int{1, 2, 3, 4, 5}
    fmt.Println(Somma(numeri...))    // Output: 15
}

Nella funzione Somma, nums è una slice di interi che contiene tutti i parametri passati alla funzione Somma. Possiamo utilizzare un ciclo range per iterare attraverso questa slice e calcolare la somma.

È importante notare che i parametri variabili all'interno della funzione sono effettivamente una slice, quindi tutte le operazioni relative alle slice possono essere utilizzate. Quando è necessario passare una slice come parametro variabile a una funzione, basta aggiungere i tre punti ... dopo la slice.

L'uso dei parametri variabili può rendere le chiamate alle funzioni più flessibili e concise, ma avrà anche un leggero impatto sulle prestazioni, poiché l'uso dei parametri variabili comporta la creazione di slice e l'allocazione di memoria. Pertanto, è necessario fare attenzione quando sono in atto rigorosi requisiti di prestazioni.

Suggerimento: Una spiegazione dettagliata sulle slice verrà fornita nei capitoli successivi. Se hai esperienza con altri linguaggi di programmazione, puoi temporaneamente pensare a esse come array.

3 Chiamata di funzioni

3.1 Chiamate di funzioni di base

Chiamare una funzione significa eseguire il codice della funzione. In Go, chiamare una funzione definita è molto semplice, basta usare il nome della funzione e passare i parametri appropriati. Ad esempio:

risultato := aggiungi(3, 4)
fmt.Println(risultato)  // Output: 7

In questo esempio, la funzione aggiungi viene chiamata e vengono passati due interi come parametri, quindi il risultato restituito viene assegnato alla variabile risultato.

3.2 Passaggio di parametri

Quando si passano parametri a una funzione, Go usa per default il passaggio per valore, il che significa che viene passata una copia del valore del parametro e i dati originali non verranno modificati. Tuttavia, se si desidera che la funzione modifichi variabili esterne o per motivi di prestazioni, è possibile utilizzare il passaggio per riferimento, ad esempio utilizzando puntatori o passando strutture complesse.

Ecco esempi di passaggio per valore e passaggio per riferimento:

// Esempio di passaggio per valore
func doppio(val int) {
    val *= 2
}

// Esempio di passaggio per riferimento
func doppioPtr(val *int) {
    *val *= 2
}

func main() {
    valore := 3
    doppio(valore)
    fmt.Println(valore)  // Output 3, il valore rimane invariato

    doppioPtr(&valore)
    fmt.Println(valore)  // Output 6, valore raddoppiato
}

Nell'esempio sopra, la funzione doppio cerca di raddoppiare il valore val passato, ma raddoppia solo la sua copia, lasciando invariata la variabile originale valore. Tuttavia, la funzione doppioPtr cambia il valore della variabile originale ricevendo un puntatore a una variabile intera come parametro.