In Go, a closure is a function value that “closes over” its surrounding scope. This means that a closure has access to the variables in its surrounding scope, even after the surrounding function has returned.
Here’s a simple example of a closure in Go:
package main
import "fmt"
func adder() func(int) int {
sum := 0
return func(x int) int {
sum += x
return sum
}
}
func main() {
pos, neg := adder(), adder()
for i := 0; i < 10; i++ {
fmt.Println(
pos(i),
neg(-2*i),
)
}
}
Output:
0 0
1 -2
3 -6
6 -12
10 -20
15 -30
21 -42
28 -56
36 -72
45 -90
In this example, the adder
function returns a closure. The closure is a function that adds an int
value to its surrounding sum
variable. When the adder
function is called twice, it returns two separate closures with their own separate sum
variables. This allows the two closures to maintain their own separate running totals, even though they are defined in the same scope.
Closures can be useful in a variety of situations where you need to maintain some state or context across multiple function calls. For example, you can use a closure to implement a simple counter:
package main
import "fmt"
func counter() func() int {
count := 0
return func() int {
count++
return count
}
}
func main() {
counterA := counter()
counterB := counter()
fmt.Println(counterA(), counterA(), counterA(), counterB(), counterB(), counterA(), counterB())
}
Output:
1 2 3 1 2 3 4
In this example, the counter
function returns a closure that increments and returns a count
variable. When the counter
function is called twice, it returns two separate closures with their own separate count
variables. This allows the two closures to maintain their own separate counts, even though they are defined in the same scope.
Closures are often used in Go to implement higher-order functions, which are functions that take one or more functions as arguments or return a function as a result. For example, you can use a closure to implement a simple function that returns a function that implements a simple filter:
package main
import "fmt"
func filter(f func(int) bool) func([]int) []int {
return func(s []int) []int {
var res []int
for _, v := range s {
if f(v) {
res = append(res, v)
}
}
return res
}
}
func main() {
even := filter(func(n int) bool {
return n%2 == 0
})
odd := filter(func(n int) bool {
return n%2 == 1
})
fmt.Println("even:", even([]int{1, 2, 3, 4, 5}))
fmt.Println("odd:", odd([]int{1, 2, 3, 4, 5}))
}
Output:
even: [2 4]
odd: [1 3 5]
In this example, the filter
function returns a closure that filters a []int
slice based on a provided function f
. The closure takes a []int
slice as an argument and returns a new []int
slice that contains only the elements that satisfy the f
function. When the filter
function is called twice with different f
functions, it returns two separate closures that implement different filters. This allows the two closures to maintain their own separate filters, even though they are defined in the same scope.
Closures can also be used to implement simple state machines, like the following example:
package main
import "fmt"
func stateMachine() func() string {
state := 0
return func() string {
state++
switch state {
case 1:
return "first"
case 2:
return "second"
default:
return "unknown"
}
}
}
func main() {
f := stateMachine()
fmt.Println(f(), f(), f(), f(), f())
}
Output:
first second unknown unknown unknown
In this example, the stateMachine
function returns a closure that implements a simple state machine. The closure increments a state
variable and returns a string value based on the value of state
. When the stateMachine
function is called once, it returns a closure that implements the state machine. This allows the closure to maintain its own state, even though it is defined in the same scope.
Leave a Reply