golang匿名函数与闭包理解

匿名函数:所谓匿名函数,就是没有名字的函数
定义匿名函数

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

匿名函数作为返回值

func getPrintMessage() func(string) {
    // returns an anonymous function
    return func(message string) {
        fmt.Println(message)
    }
}

匿名函数赋给变量

f := func() int {
    ...
}

返回多个匿名函数

func calc(x, y int) (func(int), func()) {
    f1 := func(z int) int {
        return (x + y) * z / 2
    }

    f2 := func() int {
        return 2 * (x + y)
    }
    return f1, f2
}

完整代码:

package main

import "fmt"

func showMessage(message string) {
    fmt.Println(message)
}

func getPrintMessage() func(string) {
    // returns an anonymous function
    return func(message string) {
        fmt.Println(message)
    }
}

func main() {
    // named function
    showMessage("Hello function!")

    // anonymous function declared and called
    func(message string) {
        fmt.Println(message)
    }("Hello anonymous function!")

    // gets anonymous function and calls it
    printfunc := getPrintMessage()
    printfunc("Hello anonymous function using caller!")

}

输出: 
Hello function! 
Hello anonymous function! 
Hello anonymous function using caller!

闭包的概念:闭包就是能够读取其他函数内部变量的函数。 只有函数内部的子函数才能读取局部变量,因此可以把闭包简单理解成”定义在一个函数内部的函数”。

golang中使用闭包

package main

import "fmt"

func outer(name string) {
    // variable in outer function
    text := "Modified " + name

    // foo is a inner function and has access to text variable, is a closure
    // closures have access to variables even after exiting this block
    foo := func() {
        fmt.Println(text)
    }

    // calling the closure
    foo()
}

func main() {
    outer("hello")
}

返回闭包

package main

import "fmt"

func outer(name string) func() {
    // variable
    text := "Modified " + name

    // closure. function has access to text even after exiting this block
    foo := func() {
        fmt.Println(text)
    }

    // return the closure
    return foo
}

func main() {
    // foo is a closure
    foo := outer("hello")

    // calling a closure
    foo()
}

Closures and state

package main

import "fmt"

func counter(start int) (func() int, func()) {
    // if the value gets mutated, the same is reflected in closure
    ctr := func() int {
        return start
    }

    incr := func() {
        start++
    }

    // both ctr and incr have same reference to start
    // closures are created, but are not called
    return ctr, incr
}

func main() {
    // ctr, incr and ctr1, incr1 are different
    ctr, incr := counter(100)
    ctr1, incr1 := counter(100)
    fmt.Println("counter - ", ctr())
    fmt.Println("counter1 - ", ctr1())
    // incr by 1
    incr()
    fmt.Println("counter - ", ctr())
    fmt.Println("counter1- ", ctr1())
    // incr1 by 2
    incr1()
    incr1()
    fmt.Println("counter - ", ctr())
    fmt.Println("counter1- ", ctr1())
}

陷阱

package main

import "fmt"

func functions() []func() {
    // pitfall of using loop variables
    arr := []int{1, 2, 3, 4}
    result := make([]func(), 0)

    for i := range arr {
        result = append(result, func() { fmt.Printf("index - %d, value - %d\n", i, arr[i]) })
    }

    return result
}

func main() {
    fns := functions()
    for f := range fns {
        fns[f]()
    }
}

输出: 
index - 3, value - 4 
index - 3, value - 4 
index - 3, value - 4 
index - 3, value - 4

传址会修改原数据

func main() {

    x := [3]int{1,2,3}

    func(arr *[3]int) {

        (*arr)[0] = 7   

        fmt.Println(arr)    // &[7 2 3]

    }(&x)

    fmt.Println(x)    // [7 2 3]

}
 

斐波拉切数列

package main

import "fmt"

// fibonacci is a function that returns
// a function that returns an int.
func fibonacci() func() int {
    x, y := 0, 1
    return func() int {
        x, y = y, x+y
        return x
    }
}

func main() {
    f := fibonacci()
    for i := 0; i < 10; i++ {
        fmt.Println(f())
    }
}

for 语句中的迭代变量与闭包函数

for 语句中的迭代变量在每次迭代中都会重用,即 for 中创建的闭包函数接收到的参数始终是同一个变量,在 goroutine 开始执行时都会得到同一个迭代值:

func main() {

    data := []string{"one", "two", "three"}

    for _, v := range data {

        go func() {

            fmt.Println(v)

        }()

    }

    time.Sleep(3 * time.Second)

    // 输出 three three three

}

修改一:最简单的解决方法:无需修改 goroutine 函数,在 for 内部使用局部变量保存迭代值,再传参:

func main() {

    data := []string{"one", "two", "three"}

    for _, v := range data {

        vCopy := v

        go func() {

            fmt.Println(vCopy)

        }()

    }

    time.Sleep(3 * time.Second)

    // 输出 one two three

}

另一个解决方法:直接将当前的迭代值以参数形式传递给匿名函数:

func main() {

    data := []string{"one", "two", "three"}

    for _, v := range data {

        go func(in string) {

            fmt.Println(in)

        }(v)

    }

    time.Sleep(3 * time.Second)

    // 输出 one two three

}

 

猜你喜欢

转载自blog.csdn.net/wade3015/article/details/88247947