In many open source projects, we often see the use of closures. In Go language, function types are a special type. Function types can be declared, assigned to variables, and passed as parameters like other types. Then there are anonymous functions and closures. This article will briefly record the concept of closures and some commonly used scenarios.
What is a closure
The Go function can be a closure. A closure is a function value that references a variable from outside the function body. Functions can access and assign values to referenced variables; in this sense, functions are "bound" to variables. -- a Tour of Go
One simplest example: 1~10 addition
package main import "fmt" func adder() func(int) int { sum := 0 return func(x int) int { sum += x return sum } } func main() { pos := adder() for i := 0; i < 10; i++ { ( pos(i), ) } } //OUTPUT //0 //1 //3 //6 //10 //15 //21 //28 //36 //45
In the above example, the add function returns a closure. Inside the closure, it references the external variablesum
。
The difference between closure and anonymous functions
Anonymous functions refer to functions that are not explicitly named in the code. A closure is a function value that can access variables captured in its lexical environment. Anonymous functions can be closures or not closures. Anonymous functions refer to variables in the external scope, and they become closures.
func main() { x := 10 // Anonymous function, not closure anonymousFunc := func() { ("Anonymous Function:", x) } anonymousFunc() // Closure closureFunc := func() { ("Closing:", x) } closureFunc() }
Common application scenarios
Closures have wide applications in actual programming. The following are several common application scenarios:
- Save state: By capturing external variables, closures can preserve state information between function calls, such as iterators
- Function factory: Dynamically create functions according to different configuration parameters.
- Callback function: Pass a function as a parameter to another function, capture some context information through a closure and execute the function
- Concurrent programming: It is safe to share and modify variables in multiple goroutines, a simple way
Save the status -- iterator
package main import "fmt" func Iterator(values []int) func() (int, bool) { index := 0 return func() (int, bool) { if index >= len(values) { return 0, false } value := values[index] index++ return value, true } } func main() { numbers := []int{1, 2, 3, 4, 5} iterate := Iterator(numbers) for { value, ok := iterate() if !ok { break } (value) } } //OUTPUT: //1 //2 //3 //4 //5
In the above example, index is the shared state of the closure. Iterator creates a closure function that is used to iterate over elements in slices. Where, each time the closure is executed, the index will be incremented until the end of the loop. For example, in many open source framework middleware, it is implemented using the iterator mode.
Function Factory
package main import "fmt" func FunctionFactory(operation string) func(int, int) int { switch operation { case "add": return func(a, b int) int { return a + b } case "subtract": return func(a, b int) int { return a - b } case "multiply": return func(a, b int) int { return a * b } case "divide": return func(a, b int) int { if b != 0 { return a / b } return 0 } default: return nil } } func main() { addFunc := FunctionFactory("add") subtractFunc := FunctionFactory("subtract") multiplyFunc := FunctionFactory("multiply") divideFunc := FunctionFactory("divide") (addFunc(5, 3)) (subtractFunc(10, 2)) (multiplyFunc(4, 5)) (divideFunc(10, 2)) (divideFunc(10, 0)) } //OUTPUT: 8 8 20 5 0
In the above example, FunctionFactory provides closure functions for addition, subtraction, multiplication and division respectively, and encapsulates them into a function factory, making the creation of functions more flexible and customizable.
Callback function
package main import ( "fmt" "time" ) func PerformOperationAsync(input int, callback func(int)) { go func() { (2 * ) result := input * 2 callback(result) }() } func main() { callback := func(result int) { ("Operation result:", result) } PerformOperationAsync(5, callback) (3 * ) } //OUTPUT //Operation result: 10
In the above example, an anonymous function was created.callback
, that is, the callback function. When performing an asynchronous operation, it will calculate the resultresult
Passed to the callback function. For example, we need to wait for a long operation or a scene after an event is triggered.
Concurrent programming
package main import ( "fmt" "sync" "time" ) func ConcurrentTask(tasks []func() int) []int { results := make([]int, len(tasks)) wg := {} for i, task := range tasks { (1) go func(i int, task func() int) { defer () results[i] = task() }(i, task) } () return results } func main() { tasks := []func() int{ func() int { (2 * ) return 1 }, func() int { (1 * ) return 2 }, func() int { (3 * ) return 3 }, } results := ConcurrentTask(tasks) (results) // Output: [1 2 3]} //OUTPUT //[1 2 3]
In the above example, the for loop creates an anonymous function for each task. These anonymous functions use closures to capture loop variablesi
and task functionstask
. Within each anonymous function, we call the task function and store the result in the corresponding location.
Summarize
This article mainly learns to record a powerful and expressive feature in the Go language: closure. Including the basic concept of closures, the difference between anonymous functions, as well as some common programming scenarios. Because of its flexibility and strong plasticity, closures are widely used in open source libraries, which is of great help to improve the maintainability and expansion of code.
The above is the detailed explanation of closures and common scenarios in Golang. For more information about Go closures, please follow my other related articles!