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
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 212,185评论 6 493
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 90,445评论 3 385
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 157,684评论 0 348
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 56,564评论 1 284
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 65,681评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 49,874评论 1 290
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,025评论 3 408
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,761评论 0 268
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,217评论 1 303
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,545评论 2 327
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,694评论 1 341
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,351评论 4 332
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,988评论 3 315
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,778评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,007评论 1 266
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,427评论 2 360
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,580评论 2 349

推荐阅读更多精彩内容