Golang 中的闭包


2022年1月15日, Learn eTutorial
1778

在本教程中,您将学习如何在Golang中使用闭包。在讨论闭包之前,我们需要理解变量作用域和生命周期的概念。您将涵盖闭包的需求及其独特环境。

Golang闭包中变量作用域和生命周期的概念

考虑一个函数,其中声明了一个在该函数内部可见的变量。作用域是静态的,这是我们编写的编程代码的一个特性,基于编译时;而生命周期则取决于程序执行(运行时)。

Golang中的闭包是什么?

闭包是一个函数内部的函数,“捕获”外部函数的一个或多个局部变量。Golang支持嵌套函数。嵌套函数是定义在另一个函数内部的函数。
考虑下面的示例代码  


func  fib()  func()  int {
a, b := 0,1
return  func()  int {
a, b = b, a+b
return b
}
}

闭包涉及一个函数使用来自另一个函数作用域的变量

GO : Closure
  •   显然,图中所示存在一个外部作用域F2。函数F1处于另一个函数F2的作用域内。
  •   这里F1和F2是嵌套函数。(一个函数内部的另一个函数)
  •   F1引用了F2的局部变量,这将涉及闭包。
  •   函数F1捕获了函数F2中的变量“b”。

让我们通过上面代码的实际解释来理解

  •   代码包含一个函数 fib (fib( ))。fib不返回一个值,而是返回一个名为 func (func( )) 的函数。
  •   fib( ) 是 F1,func( ) 是 F2。
  •   fib( ) 的返回类型是一个函数 func( ),它返回整数类型 int。
  •   因此,fib( ) 是一个返回另一个函数的函数。(外部函数)
  •   内部函数是一个匿名函数,它没有名称。它只是一个带有某些代码的函数字面量。它使用了变量 a 和 b,但没有声明 a 和 b。
  •   变量 a 和 b 在 fib (F1) 中声明。当内部函数返回时,即使函数 fib 消失,它也会继续引用 a 和 b。
  •   当 fib 运行时,它返回 a 和 b,它们将继续存在,因为返回的内部函数捕获了 a 和 b,所以它使 a 和 b 保持活动状态。
     

请看下图

GO : Closure

该图左侧显示函数,右侧显示闭包。假设有一个变量赋值 
f:= fib
f被声明并赋值为fib,这里fib使用时没有括号,只取fib这个名称,指的是函数,而不是函数调用,这里相当于一个指向代码的指针,即它将指向函数fib 
如果f作为带括号的函数被调用,即像fib()函数调用一样。f和fib是同一个东西的两个名称。这些由上图的左侧表示。 

接下来在右侧,调用函数fib()并赋值给变量f。
f := fib( )
f不是一个函数,它是一个闭包。它有两部分:env 和 fp。在上面讨论的代码中,有一个匿名函数(F2),它实际上是从fib中返回的第一部分。第二部分包含了关于在哪里找到a和b的信息。因为匿名内部函数改变了a和b的值,并返回了b。
所以闭包是程序后台运行时不可见的实践。  

注意:-f在右侧是闭包,因为它有一个指向匿名函数(fp)的指针和一个指向环境(env)的指针,而在左侧它只是一个指向函数(fp)的指针

 

闭包程序


// Anonymous functions are useful when you want to define
// a function inline without having to name it.

package main
import "fmt"

// This function `fib` returns another function, which
// we define anonymously in the body of `fib`. The
// returned function _closes over_ the variable `a & b` to
// form a closure.
func  fib()  func()  int {
a, b := 0,1
return  func() int {
a, b = b, a+b
return b
}
}

func main() {
f := fib()
for x:=f();x<100;x=f() {
fmt.Println(x);           // 1 2,3,4,8,13,21,34,55,89

}
}

输出


1
2
3
5
8
13
21
34
55
89

Golang中闭包的需求是什么?

Golang中闭包(即一个函数返回另一个函数)的需求是作用域。假设有一个函数,其中某些值被初始化给一个变量。函数只是一组要执行的指令。该函数内部的内部函数将使用外部函数初始化的值。

如何在Golang中创建闭包?

通过在已声明的外部函数内部声明一个匿名函数来创建闭包。该函数可以访问其周围状态并形成独特。
示例   

GO : Closure

下面的代码是一个包含i的函数f的闭包。闭包特性得到满足,匿名函数f可以直接访问i的值。


package main

import (
    "fmt"
)
 
func main() {
 
    i := 40
    f := func() {
                // has access to i
        j := i - 2
        fmt.Println(j)
    }
     
    f()      // 38
}

输出


38

Golang中闭包的独特环境?

Golang支持闭包函数中可用的数据隔离特性。每当创建闭包时,其状态都是唯一的。这使得它们各自拥有自己的状态。内部变量对于每个闭包来说都是唯一的。
让我们通过一个例子来理解


package main
import "fmt"


func  fib()  func()  int {
  a, b := 0, 1
       // return a closure over a & b
       return  func() int {
  a, b = b, a+b
  return b
  }

      }

func main(){

f , g := fib(), fib()  
// f & g have their own copies of a & b
fmt.Println(f(),f(),f())
fmt.Println(g(),g(),g())  //f() , g()   prints  identical lines 1,2,3

}

输出


1 2 3
1 2 3