Go - from Google



Funzioni anonime   

Un altro argomento sempre più di attualità è presentato in questo paragrafo. Anche Go supporta infatti le funzioni anonime ( o funzioni lambda) che tanta importanza hanno per la possibilità di poter parlare di chiusure, avvicinandoci così al mondo delle programmazione funzionale.
Una funzione anonima è una funzione (ovviamente...) che non è definita tramite un nome che la identifica e nemmeno è richiamata tramite un nome. Da questa definizione ben risulta il concetto di "anonimato". Nel nostro linguaggio una funzione anonima non può avere vita propria ma deve essere attribuita ad una variabile o comunque devono essere attributi i parametri iniziali, anche se il loro numero fossero 0. Quindi la funzione:

func (int x, int y) int {
return (x + y)
}

che non ha un nome se definita così originerebbe un errore mentre va bene come segue:

x := func (x int, y int) int { return (x + y) }

richiamando poi

x(2,3)

ad esempio, oppure passando direttamente i parametri:

x := func (x int, y int) int { return (x + y) }(2, 3)

per un uso diretto. Nel caso in cui i parametri da passare fossero zero la coppia di parentesi tonde finali sarebbero semplicemente aperte e chiuse
(), anzichè, come nell'esempio contenere i valori 2 e 3. Questo appunto per dire che ci deve essere una qualche inizializzazione.
Formalmente, come si intuisce, la definizione di una funzione anonima è:

func (eventuali parametri) (eventuali valori di ritorno) {  }

Quindi. riassumendo, le funzioni lambda possono essere assegnate a variabili e trattate come valori. Va detto che, parlando in termini assoluti, quindi non legati direttamente a Go, le funzioni lambda non sono un dogma, ovvero si può fare a meno di esse, in alcuni linguaggi non sono previste. Tuttavia vengono molto comode in tante situazioni per cui non è una cattiva idea imparare ad usarle.
Vediamo ora un esempio che riassume un po' quanto detto:

  esempio 6.1
1
2
3
4
5
6
7
8
package main
import "fmt"

func main() {
x := func (a int) int { return (a * a) }
fmt.Println(x(5))
fmt.Println(x(-4))
}

la funzione anonima definita alla riga 5 ed assegnata ad x vuole un parametro intero e restituisce un altro intero.

Attraverso le funzioni anonime e dato che in Go le funzioni sono entità di prima classe, è possibile anche esplicitare concettualmente le famose chiusure. Le cose (funzioni anonime e chiusure) ovviamente coincidono ma spesso si sentono citare come entità separate da qui sento la necessità di chiarire un po' la questione. La spiegazione approfondita del concetto esula dal nostro semplice excursus sul linguaggio, in rete troverete tutto quanto serve per la sua comprensione e anche di più. In breve possiamo dire che una chiusura effettua una sorta di "cattura" di un valore in una funzione. Vediamo un esempio di base,  veramente il più semplice e chiaro che ho trovato (non ho saputo fare di meglio insomma :-)) e che potete trovare in originale qui. Si tratta solo di un qualcosa di puramente didattico e l'importanza delle chiusure risulterà più chiaro in altri ambiti e in altre situazioni che, come detto, potrete approfondire altrove.

  esempio 6.2
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
package main
import "fmt"

var a func()
var i int = 10
var j int = 20

func main() {
func() {
var i int = 100
a = func() {
fmt.Printf("Hello World: %d\n", i + j);
}
}()
i *= 2
j *= 2
a()
}

Come per le funzioni normali anche le lambda possono essere soggette a differimento tramite la solita keyword defer. Anche qui diamo un esempio:

  esempio 6.3
1
2
3
4
5
6
7
8
9
10
11
12
13
14
package main
import "fmt"

func mult (x int) (y int) {
defer func() {
y++
}()
y = x
return (y * 2)
}

func main() {
fmt.Println(mult(33))
}

Le funzioni anonime possono anche essere utilizzate come valore di ritorno di altre funzioni. Quindi una funzione restituisce a sua volta un funzione. Vediamo come:

  esempio 6.4
1
2
3
4
5
6
7
8
9
10
11
12
13
14
ppackage main
import "fmt"

func xx(a int) (func() int) {
return func () int {
return a * 4
}
}

func main() {
x := xx(6)
y := x()
fmt.Println(y)
}

La riga 4 definisce la funzione xx che accetta in input un parametro e restituisce una funzione anonima priva di parametri in ingresso mentre restituisce a sua volta un intero. La riga 11 richiama la funzione xx passandogli il parametro richiesto (nel caso il numero 6). La x definita alla 6 stessa può essere vista essa stessa come una funzione inizializzata durante la chiamata stessa; Successivamente, alla 7, il tutto viene mandato in esecuzione. Studiando l'esempio credo che tutto diventerà più chiaro.