Sommario
La semplice funzione per generare la serie di Fibonacci può essere scritta in Go sfruttando le closure.
Fibonacci
Ne abbiamo parlato nel post precedente, ma la sequenza di Fibonacci ha ancora qualcosa da farci scoprire, almeno dal punto di vista del linguaggio di programmazione Go.
Avevano scritto la funzione che calcola l’ennesimo numero della serie a partire dalla definizione della serie: il numero successivo è la somma dei due numeri precedenti. Il codice è quindi il seguente:
package main
// ennesimo numero della serie di Fibonacci
func fibonacci(n int) int {
var a, b int = 0, 1
for i := 0; i < n-1; i++ {
a, b = b, a+b
}
return a
}
func main() {
for i := 1; i < 10; i++ {
print(fibonacci(i), " ")
}
println()
}
Funzioni di prima classe
Quando le funzioni sono tipi di prima classe possono essere trattate come valori, dunque possono essere assegnate a variabili e restituite da una funzione.
Questa prerogativa dei linguaggi funzionali è presente in Lua ed anche in Go (quello che diremo vale indifferentemente per i due linguaggi). Per fare un esempio in Go, assegnamo una funzione ad una variabile per il calcolo della somma degli argomenti interi (ricordo che per provare il codice possiamo utilizzare il comodo servizio web Playground:
package main
func main() {
add := func(a, b int) int {
return a + b
}
println(add(4,5))
}
Assegnare una funzione ad una variabile significa creare una funzione anonima (senza nome) ma, rispetto alla definizione diretta, cambia solamente la semantica/sintassi del linguaggio ma non il risultato che è esattamente equivalente.
Un esempio con una funzione factory è il seguente dove rispetto ad un simbolo passato come argomento, una funzione restituisce la funzione dell’operazione corrispondente:
package main
func operation(op string) func(int, int) int {
switch op {
case "+":
return func(a, b int) int {
return a + b
}
case "-":
return func(a, b int) int {
return a - b
}
case "*":
return func(a, b int) int {
return a * b
}
case "/":
return func(a, b int) int {
return a / b
}
default:
return nil
}
}
func main() {
add := operation("+")
println(add(4, 5))
molt := operation("*")
println(molt(4, 5))
}
Closure
Quando una funzione di prima classe ha accesso alle variabili locali, le variabili che appartengono allo stesso scopo della funzione, viene chiamata closure.
Tornando a Fibonacci, poiché sono necessari due valori iniziali di innesco della serie, possiamo esprimerli con variabili locali di una closure:
package main
// fibonacci is a function that returns
// a function that returns an int
func fibonacci() func() int {
a, b := 1, 0
return func() int {
a, b = b, a + b
return a
}
}
func main() {
f := fibonacci()
for i := 0; i < 10; i++ {
println(f())
}
}
Le variabili a e b interne alla funzione fibonacci() sono variabili che possono essere lette e scritte dalla funzione anonima, verificando il concetto di funzione closure.
A questo punto possiamo anche creare funzioni di Fibonacci a piacere definendo di volta in volta i primi due numeri della sequenza:
package main
// fibonacci is a function that returns
// a function that returns an int
func fibonacci(n1, n2 int) func() int {
return func() int {
n1, n2 = n2, n1 + n2
return n1
}
}
func main() {
f1 := fibonacci(1, 0)
for i := 0; i < 10; i++ {
println(f1())
}
println()
f2 := fibonacci(50, 17)
for i := 0; i < 10; i++ {
println(f2())
}
}
Ed in Lua?
-- Lua version : - )
-- fibonacci is a function that returns
-- a function that returns an int
function fibonacci(n1, n2)
return function()
n1, n2 = n2, n1 + n2
return n1
end
end
f1 = fibonacci(1, 0)
for i=1,10 do
print(f1())
end
print()
f2 = fibonacci(50, 17)
for i=1,10 do
print(f2())
end
Insomma possiamo dire che i progettisti del Go hanno studiato in modo approfondito anche Lua e Python. O no? 🙂
Alla prossima…
Un saluto.
R.