go闭包

匿名函数同样被称之为闭包(函数式语言的术语):它们被允许调用定义在其它环境下的变量。闭包可使得某个函数捕捉到一些外部状态,例如:函数被创建时的状态。

另一种表示方式为:一个闭包继承了 函数所声明时的作用域。这种状态(作用域内的变量)都被共享到闭包的环境中,因此这些变量可以在 闭包中被操作,直到被销毁。闭包经常被用作包装函数:它们会预先定义好 1 个或多个参数以用于包 装。另一个不错的应用就是使用闭包来完成更加简洁的错误检查。

仅仅从形式上将闭包简单理解为匿名函数是不够的,还需要理解闭包实质上的含义。

实质上看,闭包是由函数及其相关引用环境组合而成的实体(即:闭包=函数+引用环境)。闭包在运行时 可以有多个实例,不同的引用环境和相同的函数组合可以产生不同的实例。由闭包的实质含义,我们可 以推论:闭包获取捕获变量相当于引用传递,而非值传递;对于闭包函数捕获的常量和变量,无论闭包 何时何处被调用,闭包都可以使用这些常量和变量,而不用关心它们表面上的作用域。

引用环境验证

package main

import "fmt"

func addNumber(x int) func(int) {
    fmt.Printf("x :%d addr of x:%p\n", x, &x)
    return func(y int) {
        k := x + y
        x = k
        y = k
        fmt.Printf("x :%d addr of x:%p\n", x, &x)
        fmt.Printf("y :%d addr of y:%p\n", y, &y)
    }
}

func main() {
    addNum := addNumber(5)
    addNum(1)
    addNum(1)
    addNum(1)
    fmt.Println("-------------------------------")
    addNum2 := addNumber(5)
    addNum2(1)
    addNum2(1)
    addNum2(2)
}

output:

x :5 addr of x:0xc00000a0c8
x :6 addr of x:0xc00000a0c8
y :6 addr of y:0xc00000a110
x :7 addr of x:0xc00000a0c8
y :7 addr of y:0xc00000a128
x :8 addr of x:0xc00000a0c8
y :8 addr of y:0xc00000a140
-------------------------------
x :5 addr of x:0xc00000a158
x :6 addr of x:0xc00000a158
y :6 addr of y:0xc00000a168
x :7 addr of x:0xc00000a158
y :7 addr of y:0xc00000a180
x :9 addr of x:0xc00000a158
y :9 addr of y:0xc00000a198

根据上面代码结果,发现X在匿名函数声明时传入,是引用该参数。后续调用同一个匿名函数x地址不变,但值会改变。而y值作为匿名函数参数每次调用都传值,地址在改变。

斐波那契数


import "fmt"

func test(a, b int) func() int {
    return func() int {
        a, b = b, a+b
        return a
    }
}

func main() {
    var a, b int = 1, 1
    c := test(a, b)
    for i := 0; i < 10; i++ {
        fmt.Println(c())
    }
}

output:

1
2
3
5
8
13
21
34
55
89

使用闭包调试:

方法1:
当您在分析和调试复杂的程序时,无数个函数在不同的代码文件中相互调用,如果这时候能够准确地知 道哪个文件中的具体哪个函数正在执行,对于调试是十分有帮助的。您可以使用 runtime 或 log 包 中的特殊函数来实现这样的功能。包 runtime 中的函数 Caller() 提供了相应的信息,因此可以在 需要的时候实现一个 where() 闭包函数来打印函数执行的位置:

package main

import (
    "fmt"
    "runtime"
)

func main() {
    where := func() {
        _, file, line, _ := runtime.Caller(1)
        fmt.Printf("%s:%d\n", file, line)
    }
    where()
    fmt.Println("---next---")
    where()
}

output:

H:/awesomeProject/foo/go42.go:13
---next---
H:/awesomeProject/foo/go42.go:15

方法2:
使用log包的打印函数

package main

import (
  "fmt"
  "log"
)

func main() {
  log.SetFlags(log.Llongfile | log.LstdFlags)
  where := log.Print
  where()
  fmt.Println("---next---")
  where()
}

output:

2020/10/12 21:46:44 H:/awesomeProject/foo/go42.go:11: 
---next---
2020/10/12 21:46:44 H:/awesomeProject/foo/go42.go:13: 
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。