golang中匿名函数看这篇就够啦

匿名函数

匿名函数就是没有名称的函数,主要记住它可以用于各种类型(切片、结构体、map等)中就行。

1. 一等公民

golang中的函数是一等公民 —— 它能做类型、变量、参数等传递。

package main

import "fmt"

func main() {

    // 切片中充当元素
    d := []func(int) int{
        func(x int) int { return x + 2 },
        func(y int) int { return y + 2 },
    }

    fmt.Println(d[0](2))

    // 结构体中字段
    s := struct {
        fn func(int) int // 结构体这里没有逗号
    }{
        fn: func(x int) int { return x + 100 },
    }

    fmt.Println(s.fn(2))

    // map中充当value
    m := make(map[string]func(int) int) // map 可以不指定长度
    m["first"] = func(x int) int { return x + 200 }
    fmt.Println(m["first"](100))
}

// 4
// 102
// 300

2. 闭包

闭包 = 函数 + 环境变量

光看上面的话,我们也不知道具体什么意思;其实记住下面两点就行:

  1. 匿名函数能获取外部变量的值
package main

import "fmt"

func main() {
    x := 2
    func() {
        fmt.Println(x) // 能获取外部的x
    }()
}
  1. 外部变量的会以地址(引用)方式的方式绑定

例子1:通过地址值验证(打印出来是同一个地址)

package main

import "fmt"

func main() {
    x := 1
    f := func() {
        fmt.Printf("f中x地址: %p\n", &x)
        fmt.Println(x) // 能获取外部的x
    }

    fmt.Printf("x地址: %p\n", &x)
    f()
}

// x地址: 0xc00001a198
// f中x地址: 0xc00001a198
// 1

例子2: 在匿名函数中修改值

package main

import "fmt"

func main() {
    f := genFuc()

    // 同一个匿名函数 修改的是同一个x地址 所以x会变
    f() // 打印 1
    f() // 打印 2
    f() // 打印 3

    // 这个时候返回的是 新匿名函数 x地址不同 所以回到初识值了
    f1 := genFuc()
    f1() // 打印 1
}

// 返回一个匿名函数
func genFuc() func() {
    x := 0
    return func() {
        x++
        fmt.Println(x)
    }
}

// 1
// 2
// 3
// 1

3. 循环中变量引起的问题

循环指的是for/range,我们先来看产生的问题

package main

import "fmt"

func main() {
    ch := make(chan func(), 10) // 这个通道中装的是函数

    // 装入通道
    for i := 0; i < 10; i++ {
        ch <- func() {
            fmt.Println(i) // 绑定外部变量i
        }
    }

    close(ch)

    // 取出函数后执行
    for f := range ch {
        f()
    }
}

// 10
// 10
// 10
// 10
// 10
// 10
// 10
// 10
// 10
// 10

我们会发现输出的居然全是10;为什么会这样呢?

在循环语句中的变量,它只初始化了一次,它的地址是不变的;每次改动的只是将不同的值分配到这个地址上而已;
而匿名函数绑定的是变量的地址,在绑定变量后,函数没有立即执行,而等到执行时,地址i中的值早已变成10了。

这种情况,发生在延迟执行(绑定变量后,执行前变量值发生了更改)。

如何避免呢?
在每一轮循环下增加一行,初始化一个变量用于存储绑定值即可。

修改后代码如下:

package main

import "fmt"

func main() {
    ch := make(chan func(), 10) // 这个通道中装的是函数

    // 装入通道
    for i := 0; i < 10; i++ {
        ni := i
        ch <- func() {
            fmt.Println(ni) // 绑定外部变量i
        }
    }

    close(ch)

    // 取出函数后执行
    for f := range ch {
        f()
    }
}

// 0
// 1
// 2
// 3
// 4
// 5
// 6
// 7
// 8
// 9
©著作权归作者所有,转载或内容合作请联系作者
【社区内容提示】社区部分内容疑似由AI辅助生成,浏览时请结合常识与多方信息审慎甄别。
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

相关阅读更多精彩内容

友情链接更多精彩内容