返回顶部
首页 > 资讯 > 后端开发 > GO >Golang中的闭包怎么实现
  • 162
分享到

Golang中的闭包怎么实现

2023-07-04 14:07:58 162人浏览 八月长安
摘要

这篇文章主要介绍了golang中的闭包怎么实现的相关知识,内容详细易懂,操作简单快捷,具有一定借鉴价值,相信大家阅读完这篇Golang中的闭包怎么实现文章都会有所收获,下面我们一起来看看吧。1、什么是闭包?在真正讲述闭包之前,我们先铺垫一点

这篇文章主要介绍了golang中的闭包怎么实现的相关知识,内容详细易懂,操作简单快捷,具有一定借鉴价值,相信大家阅读完这篇Golang中的闭包怎么实现文章都会有所收获,下面我们一起来看看吧。

1、什么是闭包?

在真正讲述闭包之前,我们先铺垫一点知识点:

  • 函数式编程

  • 函数作用域

  • 作用域的继承关系

1.1 前提知识铺垫

1.2.1 函数式编程

函数式编程是一种编程范式,看待问题的一种方式,每一个函数都是为了用小函数组织成为更大的函数,函数的参数也是函数,函数返回的也是函数。我们常见的编程范式有:

  • 命令式编程:

    • 主要思想为:关注计算机执行的步骤,也就是一步一步告诉计算机先做什么再做什么。

    • 先把解决问题步骤规范化,抽象为某种算法,然后编写具体的算法去实现,一般只要支持过程化编程范式的语言,我们都可以称为过程化编程语言,比如 BASIC,C 等。

  • 声明式编程:

    • 主要思想为:告诉计算机应该做什么,但是不指定具体要怎么做,比如 sql,网页编程的 htmlCSS

  • 函数式编程:

    • 只关注做什么而不关注怎么做,有一丝丝声明式编程的影子,但是更加侧重于”函数是第一位“的原则,也就是函数可以出现在任何地方,参数、变量、返回值等等。

函数式编程可以认为是面向对象编程的对立面,一般只有一些编程语言会强调一种特定的编程方式,大多数的语言都是多范式语言,可以支持多种不同的编程方式,比如 javascript ,Go 等。

函数式编程是一种思维方式,将电脑运算视为函数的计算,是一种写代码的方法论,其实我应该聊函数式编程,然后再聊到闭包,因为闭包本身就是函数式编程里面的一个特点之一。

在函数式编程中,函数是头等对象,意思是说一个函数,既可以作为其它函数的输入参数值,也可以从函数中返回值,被修改或者被分配给一个变量。(维基百科)

一般纯函数编程语言是不允许直接使用程序状态以及可变对象的,函数式编程本身就是要避免使用 共享状态可变状态,尽可能避免产生 副作用

函数式编程一般具有以下特点:

  • 函数是第一等公民:函数的地位放在第一位,可以作为参数,可以赋值,可以传递,可以当做返回值。

  • 没有副作用:函数要保持纯粹独立,不能修改外部变量的值,不修改外部状态。

  • 引用透明:函数运行不依赖外部变量或者状态,相同的输入参数,任何情况,所得到的返回值都应该是一样的。

1.2.2 函数作用域

作用域(scope),程序设计概念,通常来说,一段程序代码中所用到的名字并不总是有效/可用的,而限定这个名字的可用性的代码范围就是这个名字的作用域

通俗易懂的说,函数作用域是指函数可以起作用的范围。函数有点像盒子,一层套一层,作用域我们可以理解为是个封闭的盒子,也就是函数的局部变量,只能在盒子内部使用,成为独立作用域。

Golang中的闭包怎么实现

函数内的局部变量,出了函数就跳出了作用域,找不到该变量。(里层函数可以使用外层函数的局部变量,因为外层函数的作用域包括了里层函数),比如下面的 innerTmep 出了函数作用域就找不到该变量,但是 outerTemp 在内层函数里面还是可以使用。

Golang中的闭包怎么实现

不管是任何语言,基本存在一定的内存回收机制,也就是回收用不到的内存空间,回收的机制一般和上面说的函数的作用域是相关的,局部变量出了其作用域,就有可能被回收,如果还被引用着,那么就不会被回收。

1.2.3 作用域的继承关系

所谓作用域继承,就是前面说的小盒子可以继承外层大盒子的作用域,在小盒子可以直接取出大盒子的东西,但是大盒子不能取出小盒子的东西,除非发生了逃逸(逃逸可以理解为小盒子的东西给出了引用,大盒子拿到就可以使用)。一般而言,变量的作用域有以下两种:

  • 全局作用域:作用于任何地方

  • 局部作用域:一般是代码块,函数、包内,函数内部声明/定义的变量叫局部变量作用域仅限于函数内部

1.2 闭包的定义

“多数情况下我们并不是先理解后定义,而是先定义后理解“,先下定义,读不懂没关系

闭包(closure)是一个函数以及其捆绑的周边环境状态(lexical environment,词法环境)的引用的组合。 换而言之,闭包让开发者可以从内部函数访问外部函数的作用域。 闭包会随着函数的创建而被同时创建。

一句话表述:

闭包=函数+引用环境闭包 = 函数 + 引用环境

以上定义找不到 Go语言 这几个字眼,聪明的同学肯定知道,闭包是和语言无关的,不是 JavaScript 特有的,也不是 Go 特有的,而是函数式编程语言的特有的,是的,你没有看错,任何支持函数式编程的语言都支持闭包,Go 和 JavaScript 就是其中之二, 目前 Java 目前版本也是支持闭包的,但是有些人可能认为不是完美的闭包,详细情况文中讨论。

1.3 闭包的写法

1.3.1 初看闭包

下面是一段闭包的代码:

import "fmt"func main() {sumFunc := lazySum([]int{1, 2, 3, 4, 5})fmt.Println("等待一会")fmt.Println("结果:", sumFunc())}func lazySum(arr []int) func() int {fmt.Println("先获取函数,不求结果")var sum = func() int {fmt.Println("求结果...")result := 0for _, v := range arr {result = result + v}return result}return sum}

输出的结果:

先获取函数,不求结果等待一会求结果...结果: 15

可以看出,里面的 sum() 方法可以引用外部函数 lazySum() 的参数以及局部变量,在lazySum()返回函数 sum() 的时候,相关的参数和变量都保存在返回的函数中,可以之后再进行调用。

上面的函数或许还可以更进一步,体现出捆绑函数和其周围的状态,我们加上一个次数 count

import "fmt"func main() {sumFunc := lazySum([]int{1, 2, 3, 4, 5})fmt.Println("等待一会")fmt.Println("结果:", sumFunc())fmt.Println("结果:", sumFunc())fmt.Println("结果:", sumFunc())}func lazySum(arr []int) func() int {fmt.Println("先获取函数,不求结果")count := 0var sum = func() int {count++fmt.Println("第", count, "次求结果...")result := 0for _, v := range arr {result = result + v}return result}return sum}

上面代码输出什么呢?次数 count 会不会发生变化,count明显是外层函数的局部变量,但是在内存函数引用(捆绑),内层函数被暴露出去了,执行结果如下:

先获取函数,不求结果等待一会第 1 次求结果...结果: 15第 2 次求结果...结果: 15第 3 次求结果...结果: 15

结果是 count 其实每次都会变化,这种情况总结一下:

  • 函数体内嵌套了另外一个函数,并且返回值是一个函数。

  • 内层函数被暴露出去,被外层函数以外的地方引用着,形成了闭包。

此时有人可能有疑问了,前面是lazySum()被创建了 1 次,执行了 3 次,但是如果是 3 次执行都是不同的创建,会是怎么样呢?实验一下:

import "fmt"func main() {sumFunc := lazySum([]int{1, 2, 3, 4, 5})fmt.Println("等待一会")fmt.Println("结果:", sumFunc())sumFunc1 := lazySum([]int{1, 2, 3, 4, 5})fmt.Println("等待一会")fmt.Println("结果:", sumFunc1())sumFunc2 := lazySum([]int{1, 2, 3, 4, 5})fmt.Println("等待一会")fmt.Println("结果:", sumFunc2())}func lazySum(arr []int) func() int {fmt.Println("先获取函数,不求结果")count := 0var sum = func() int {count++fmt.Println("第", count, "次求结果...")result := 0for _, v := range arr {result = result + v}return result}return sum}

执行的结果如下,每次执行都是第 1 次:

先获取函数,不求结果等待一会第 1 次求结果...结果: 15先获取函数,不求结果等待一会第 1 次求结果...结果: 15先获取函数,不求结果等待一会第 1 次求结果...结果: 15

从以上的执行结果可以看出:

闭包被创建的时候,引用的外部变量count就已经被创建了 1 份,也就是各自调用是没有关系的

继续抛出一个问题,**如果一个函数返回了两个函数,这是一个闭包还是两个闭包呢?**下面我们实践一下:

一次返回两个函数,一个用于计算加和的结果,一个计算乘积:

import "fmt"func main() {sumFunc, productSFunc := lazyCalculate([]int{1, 2, 3, 4, 5})fmt.Println("等待一会")fmt.Println("结果:", sumFunc())fmt.Println("结果:", productSFunc())}func lazyCalculate(arr []int) (func() int, func() int) {fmt.Println("先获取函数,不求结果")count := 0var sum = func() int {count++fmt.Println("第", count, "次求加和...")result := 0for _, v := range arr {result = result + v}return result}var product = func() int {count++fmt.Println("第", count, "次求乘积...")result := 0for _, v := range arr {result = result * v}return result}return sum, product}

运行结果如下:

先获取函数,不求结果等待一会第 1 次求加和...结果: 15第 2 次求乘积...结果: 0

从上面结果可以看出,闭包是函数返回函数的时候,不管多少个返回值(函数),都是一次闭包,如果返回的函数有使用外部函数变量,则会绑定到一起,相互影响:

Golang中的闭包怎么实现

闭包绑定了周围的状态,我理解此时的函数就拥有了状态,让函数具有了对象所有的能力,函数具有了状态。

1.3.2 闭包中的指针和值

上面的例子,我们闭包中用到的都是数值,如果我们传递指针,会是怎么样的呢?

import "fmt"func main() {i := 0testFunc := test(&i)testFunc()fmt.Printf("outer i = %d\n", i)}func test(i *int) func() {*i = *i + 1fmt.Printf("test inner i = %d\n", *i)return func() {*i = *i + 1fmt.Printf("func inner i = %d\n", *i)}}

运行结果如下:

test inner i = 1func inner i = 2outer i = 2

可以看出如果是指针的话,闭包里面修改了指针对应的地址的值,也会影响闭包外面的值。这个其实很容易理解,Go 里面没有引用传递,只有值传递,那我们传递指针的时候,也是值传递,这里的值是指针的数值(可以理解为地址值)。

当我们函数的参数是指针的时候,参数会拷贝一份这个指针地址,当做参数进行传递,因为本质还是地址,所以内部修改的时候,仍然可以对外部产生影响。

闭包里面的数据其实地址也是一样的,下面的实验可以证明:

func main() {i := 0testFunc := test(&i)testFunc()fmt.Printf("outer i address %v\n", &i)}func test(i *int) func() {*i = *i + 1fmt.Printf("test inner i address %v\n", i)return func() {*i = *i + 1fmt.Printf("func inner i address %v\n", i)}}

输出如下, 因此可以推断出,闭包如果引用外部环境的指针数据,只是会拷贝一份指针地址数据,而不是拷贝一份真正的数据(==先留个问题:拷贝的时机是什么时候呢==):

test inner i address 0xc0003fab98func inner i address 0xc0003fab98outer i address 0xc0003fab98

1.3.2 闭包延迟化

上面的例子仿佛都在告诉我们,闭包创建的时候,数据就已经拷贝了,但是真的是这样么?

下面是继续前面的实验:

func main() {i := 0testFunc := test(&i)i = i + 100fmt.Printf("outer i before testFunc  %d\n", i)testFunc()fmt.Printf("outer i after testFunc %d\n", i)}func test(i *int) func() {*i = *i + 1fmt.Printf("test inner i = %d\n", *i)return func() {*i = *i + 1fmt.Printf("func inner i = %d\n", *i)}}

我们在创建闭包之后,把数据改了,之后执行闭包,答案肯定是真实影响闭包的执行,因为它们都是指针,都是指向同一份数据:

test inner i = 1outer i before testFunc  101func inner i = 102outer i after testFunc 102

假设我们换个写法,让闭包外部环境中的变量在声明闭包函数的之后,进行修改:

import "fmt"func main() {sumFunc := lazySum([]int{1, 2, 3, 4, 5})fmt.Println("等待一会")fmt.Println("结果:", sumFunc())}func lazySum(arr []int) func() int {fmt.Println("先获取函数,不求结果")count := 0var sum = func() int {fmt.Println("第", count, "次求结果...")result := 0for _, v := range arr {result = result + v}return result}count = count + 100return sum}

实际执行结果,count 会是修改后的值:

等待一会第 100 次求结果...结果: 15

这也证明了,实际上闭包并不会在声明var sum = func() int {...}这句话之后,就将外部环境的 count绑定到闭包中,而是在函数返回闭包函数的时候,才绑定的,这就是延迟绑定

如果还没看明白没关系,我们再来一个例子:

func main() {funcs := testFunc(100)for _, v := range funcs {v()}}func testFunc(x int) []func() {var funcs []func()values := []int{1, 2, 3}for _, val := range values {funcs = append(funcs, func() {fmt.Printf("testFunc val = %d\n", x+val)})}return funcs}

上面的例子,我们闭包返回的是函数数组,本意我们想入每一个 val 都不一样,但是实际上 val都是一个值,==也就是执行到return funcs 的时候(或者真正执行闭包函数的时候)才绑定的 val值==(关于这一点,后面还有个Demo可以证明),此时 val的值是最后一个 3,最终输出结果都是 103:

testFunc val = 103testFunc val = 103testFunc val = 103

以上两个例子,都是闭包延迟绑定的问题导致,这也可以说是 feature,到这里可能不少同学还是对闭包绑定外部变量的时机有疑惑,到底是返回闭包函数的时候绑定的呢?还是真正执行闭包函数的时候才绑定的呢?

下面的例子可以有效的解答:

import ("fmt""time")func main() {sumFunc := lazySum([]int{1, 2, 3, 4, 5})fmt.Println("等待一会")fmt.Println("结果:", sumFunc())time.Sleep(time.Duration(3) * time.Second)fmt.Println("结果:", sumFunc())}func lazySum(arr []int) func() int {fmt.Println("先获取函数,不求结果")count := 0var sum = func() int {count++fmt.Println("第", count, "次求结果...")result := 0for _, v := range arr {result = result + v}return result}go func() {time.Sleep(time.Duration(1) * time.Second)count = count + 100fmt.Println("go func 修改后的变量 count:", count)}()return sum}

输出结果如下:

先获取函数,不求结果等待一会第 1 次求结果...结果: 15go func 修改后的变量 count: 101第 102 次求结果...结果: 15

第二次执行闭包函数的时候,明显 count被里面的 go func()修改了,也就是调用的时候,才真正的获取最新的外部环境,但是在声明的时候,就会把环境预留保存下来。

其实本质上,Go Routine的匿名函数的延迟绑定就是闭包的延迟绑定,上面的例子中,go func(){}获取到的就是最新的值,而不是原始值0

总结一下上面的验证点:

  • 闭包每次返回都是一个新的实例,每个实例都有一份自己的环境。

  • 同一个实例多次执行,会使用相同的环境。

  • 闭包如果逃逸的是指针,会相互影响,因为绑定的是指针,相同指针的内容修改会相互影响。

  • 闭包并不是在声明时绑定的值,声明后只是预留了外部环境(逃逸分析),真正执行闭包函数时,会获取最新的外部环境的值(也称为延迟绑定)。

  • Go Routine的匿名函数的延迟绑定本质上就是闭包的延迟绑定。

2、闭包的好处与坏处?

2.1 好处

纯函数没有状态,而闭包则是让函数轻松拥有了状态。但是凡事都有两面性,一旦拥有状态,多次调用,可能会出现不一样的结果,就像是前面测试的 case 中一样。那么问题来了:

Q:如果不支持闭包的话,我们想要函数拥有状态,需要怎么做呢?

A: 需要使用全局变量,让所有函数共享同一份变量。

但是我们都知道全局变量有以下的一些特点(在不同的场景,优点会变成缺点):

  • 常驻于内存之中,只要程序不停会一直在内存中。

  • 污染全局,大家都可以访问,共享的同时不知道谁会改这个变量。

闭包可以一定程度优化这个问题:

  • 不需要使用全局变量,外部函数局部变量在闭包的时候会创建一份,生命周期与函数生命周期一致,闭包函数不再被引用的时候,就可以回收了。

  • 闭包暴露的局部变量,外界无法直接访问,只能通过函数操作,可以避免滥用。

除了以上的好处,像在 JavaScript 中,没有原生支持私有方法,可以靠闭包来模拟私有方法,因为闭包都有自己的词法环境。

2.2 坏处

函数拥有状态,如果处理不当,会导致闭包中的变量被误改,但这是编码者应该考虑的问题,是预期中的场景。

闭包中如果随意创建,引用被持有,则无法销毁,同时闭包内的局部变量也无法销毁,过度使用闭包会占有更多的内存,导致性能下降。一般而言,能共享一份闭包(共享闭包局部变量数据),不需要多次创建闭包函数,是比较优雅的方式。

3、闭包怎么实现的?

从上面的实验中,我们可以知道,闭包实际上就是外部环境的逃逸,跟随着闭包函数一起暴露出去。

我们用以下的程序进行分析:

import "fmt"func testFunc(i int) func() int {i = i * 2testFunc := func() int {i++return i}i = i * 2return testFunc}func main() {test := testFunc(1)fmt.Println(test())}

执行结果如下:

5

先看看逃逸分析,用下面的命令行可以查看:

 go build --GCflags=-m main.go

Golang中的闭包怎么实现

可以看到 变量 i被移到堆中,也就是本来是局部变量,但是发生逃逸之后,从栈里面放到堆里面,同样的 test()函数由于是闭包函数,也逃逸到堆上。

下面我们用命令行来看看汇编代码:

go tool compile -N -l -S main.go

生成代码比较长,我截取一部分:

"".testFunc STEXT size=218 args=0x8 locals=0x38 funcid=0x0 align=0x0        0x0000 00000 (main.go:5)        TEXT    "".testFunc(SB), ABIInternal, $56-8        0x0000 00000 (main.go:5)        CMPQ    SP, 16(R14)        0x0004 00004 (main.go:5)        PCDATA  $0, $-2        0x0004 00004 (main.go:5)        JLS     198        0x000a 00010 (main.go:5)        PCDATA  $0, $-1        0x000a 00010 (main.go:5)        SUBQ    $56, SP        0x000e 00014 (main.go:5)        MOVQ    BP, 48(SP)        0x0013 00019 (main.go:5)        LEAQ    48(SP), BP        0x0018 00024 (main.go:5)        FUNCDATA        $0, gclocals·69c1753bd5f81501d95132d08af04464(SB)        0x0018 00024 (main.go:5)        FUNCDATA        $1, gclocals·d571c0f6cf0af59df28f76498f639cf2(SB)        0x0018 00024 (main.go:5)        FUNCDATA        $5, "".testFunc.arginfo1(SB)        0x0018 00024 (main.go:5)        MOVQ    AX, "".i+64(SP)        0x001d 00029 (main.go:5)        MOVQ    $0, "".~r0+16(SP)        0x0026 00038 (main.go:5)        LEAQ    type.int(SB), AX        0x002d 00045 (main.go:5)        PCDATA  $1, $0        0x002d 00045 (main.go:5)        CALL    runtime.newobject(SB)        0x0032 00050 (main.go:5)        MOVQ    AX, "".&i+40(SP)        0x0037 00055 (main.go:5)        MOVQ    "".i+64(SP), CX        0x003c 00060 (main.go:5)        MOVQ    CX, (AX)        0x003f 00063 (main.go:6)        MOVQ    "".&i+40(SP), CX        0x0044 00068 (main.go:6)        MOVQ    "".&i+40(SP), DX        0x0049 00073 (main.go:6)        MOVQ    (DX), DX        0x004c 00076 (main.go:6)        SHLQ    $1, DX        0x004f 00079 (main.go:6)        MOVQ    DX, (CX)        0x0052 00082 (main.go:7)        LEAQ    type.noalg.struct { F uintptr; "".i *int }(SB), AX        0x0059 00089 (main.go:7)        PCDATA  $1, $1        0x0059 00089 (main.go:7)        CALL    runtime.newobject(SB)        0x005e 00094 (main.go:7)        MOVQ    AX, ""..autotmp_3+32(SP)        0x0063 00099 (main.go:7)        LEAQ    "".testFunc.func1(SB), CX        0x006a 00106 (main.go:7)        MOVQ    CX, (AX)        0x006d 00109 (main.go:7)        MOVQ    ""..autotmp_3+32(SP), CX        0x0072 00114 (main.go:7)        TESTB   AL, (CX)        0x0074 00116 (main.go:7)        MOVQ    "".&i+40(SP), DX        0x0079 00121 (main.go:7)        LEAQ    8(CX), DI        0x007d 00125 (main.go:7)        PCDATA  $0, $-2        0x007d 00125 (main.go:7)        CMPL    runtime.writeBarrier(SB), $0        0x0084 00132 (main.go:7)        JEQ     136        0x0086 00134 (main.go:7)        JMP     142        0x0088 00136 (main.go:7)        MOVQ    DX, 8(CX)        0x008c 00140 (main.go:7)        JMP     149        0x008e 00142 (main.go:7)        CALL    runtime.gcWriteBarrierDX(SB)        0x0093 00147 (main.go:7)        JMP     149        0x0095 00149 (main.go:7)        PCDATA  $0, $-1        0x0095 00149 (main.go:7)        MOVQ    ""..autotmp_3+32(SP), CX        0x009a 00154 (main.go:7)        MOVQ    CX, "".testFunc+24(SP)        0x009f 00159 (main.go:11)       MOVQ    "".&i+40(SP), CX        0x00a4 00164 (main.go:11)       MOVQ    "".&i+40(SP), DX        0x00a9 00169 (main.go:11)       MOVQ    (DX), DX        0x00ac 00172 (main.go:11)       SHLQ    $1, DX        0x00af 00175 (main.go:11)       MOVQ    DX, (CX)        0x00b2 00178 (main.go:12)       MOVQ    "".testFunc+24(SP), AX        0x00b7 00183 (main.go:12)       MOVQ    AX, "".~r0+16(SP)        0x00bc 00188 (main.go:12)       MOVQ    48(SP), BP        0x00c1 00193 (main.go:12)       ADDQ    $56, SP        0x00c5 00197 (main.go:12)       RET        0x00c6 00198 (main.go:12)       NOP        0x00c6 00198 (main.go:5)        PCDATA  $1, $-1        0x00c6 00198 (main.go:5)        PCDATA  $0, $-2        0x00c6 00198 (main.go:5)        MOVQ    AX, 8(SP)        0x00cb 00203 (main.go:5)        CALL    runtime.morestack_noctxt(SB)        0x00d0 00208 (main.go:5)        MOVQ    8(SP), AX        0x00d5 00213 (main.go:5)        PCDATA  $0, $-1        0x00d5 00213 (main.go:5)        JMP     0

可以看到闭包函数实际上底层也是用结构体new创建出来的:

Golang中的闭包怎么实现

使用的就是堆上面的 i

Golang中的闭包怎么实现

也就是返回函数的时候,实际上返回结构体,结构体里面记录了函数的引用环境。

4、浅聊一下

4.1 Java 支不支持闭包?

网上有很多种看法,实际上 Java 虽然暂时不支持返回函数作为返参,但是Java 本质上还是实现了闭包的概念的,所使用的的方式是内部类的形式,因为是内部类,所以相当于自带了一个引用环境,算是一种不完整的闭包。

目前有一定限制,比如是 final 声明的,或者是明确定义的值,才可以进行传递:

Golang中的闭包怎么实现

4.2 函数式编程的前景怎么样?

下面是Wiki的内容:

函数式编程长期以来在学术界流行,但几乎没有工业应用。造成这种局面的主因是函数式编程常被认为严重耗费CPU和存储器资源[18] ,这是由于在早期实现函数式编程语言时并没有考虑过效率问题,而且面向函数式编程特性,如保证参照透明性等,要求独特的数据结构和算法。[19]

然而,最近几种函数式编程语言已经在商业或工业系统中使用[20],例如:

  • Erlang,它由瑞典公司爱立信在20世纪80年代后期开发,最初用于实现容错电信系统。此后,它已在Nortel、Facebook、Électricité de France和WhatsApp等公司作为流行语言创建一系列应用程序。[21][22]

  • Scheme,它被用作早期Apple Macintosh计算机上的几个应用程序的基础,并且最近已应用于诸如训练模拟软件和望远镜控制等方向。

  • OCaml,它于20世纪90年代中期推出,已经在金融分析,驱动程序验证,工业机器人编程和嵌入式软件静态分析等领域得到了商业应用。

  • Haskell,它虽然最初是作为一种研究语言,也已被一系列公司应用于航空航天系统,硬件设计和网络编程等领域。

其他在工业中使用的函数式编程语言包括多范型的Scala[23]、F#,还有Wolfram语言、Common Lisp、Standard ML和Clojure等。

关于“Golang中的闭包怎么实现”这篇文章的内容就介绍到这里,感谢各位的阅读!相信大家对“Golang中的闭包怎么实现”知识都有一定的了解,大家如果还想学习更多知识,欢迎关注编程网GO频道。

您可能感兴趣的文档:

--结束END--

本文标题: Golang中的闭包怎么实现

本文链接: https://lsjlt.com/news/346171.html(转载时请注明来源链接)

有问题或投稿请发送至: 邮箱/279061341@qq.com    QQ/279061341

猜你喜欢
  • Golang中的闭包怎么实现
    这篇文章主要介绍了Golang中的闭包怎么实现的相关知识,内容详细易懂,操作简单快捷,具有一定借鉴价值,相信大家阅读完这篇Golang中的闭包怎么实现文章都会有所收获,下面我们一起来看看吧。1、什么是闭包?在真正讲述闭包之前,我们先铺垫一点...
    99+
    2023-07-04
  • golang函数中的闭包是如何实现的?
    go中函数闭包通过嵌套函数实现,允许内部函数访问外部函数作用域的变量。具体步骤如下:定义外部函数,接收参数并返回闭包函数。定义闭包函数,内部访问外部函数变量。返回闭包函数,即使外部函数已...
    99+
    2024-05-24
    golang 闭包 作用域
  • Golang闭包函数如何实现?
    在 go 中,闭包函数是指可以访问外部变量的匿名函数。实现方法:创建闭包函数时,会形成对外部变量的引用。实战应用包括累积数字列表的和。优点:提高代码可维护性和可重用性,封装状态,以及创建...
    99+
    2024-04-11
    golang 闭包函数 作用域
  • Python闭包怎么实现
    本篇内容介绍了“Python闭包怎么实现”的有关知识,在实际案例的操作过程中,不少人都会遇到这样的困境,接下来就让小编带领大家学习一下如何处理这些情况吧!希望大家仔细阅读,能够学有所成!1、闭包的概念请大家跟我理解一下,如果在一个函数的内部...
    99+
    2023-06-29
  • C++项目中怎么实现闭包
    今天就跟大家聊聊有关C++项目中怎么实现闭包,可能很多人都不太了解,为了让大家更加了解,小编给大家总结了以下内容,希望大家根据这篇文章可以有所收获。示例#include <iostream>#include ...
    99+
    2023-06-19
  • golang函数闭包实现的详细指南
    函数闭包在 golang 中是可以访问和修改外部作用域变量的嵌套函数,在以下场景中很有用:状态管理:管理外部函数返回后仍存在的变量状态。对象建模:创建具有状态和行为的对象。上下文传递:将...
    99+
    2024-04-23
    go 函数闭包 golang 作用域
  • golang 关闭通道怎么实现
    Golang是一种流行的高效编程语言,它的并发特性是该语言最大的特点之一。在Golang中,通道(channel)是一种重要的并发原语,它是一种可以在两个或多个goroutines之间传递数据的对象。在使用通道时,关闭通道是一种常见的操作。...
    99+
    2023-05-14
    Golang
  • golang闭包函数怎么调用
    在Go语言中,可以直接调用闭包函数。闭包函数是指一个函数值包含了它外部作用域的变量。可以通过以下方式调用闭包函数: 定义一个闭包函...
    99+
    2024-02-29
    golang
  • 详解golang中的闭包与defer
    目录闭包与defer1.闭包2.defer闭包与defer 1.闭包 闭包 : 一个函数与其相关的引用环境组合的一个实体,其实可以理解为面向对象中类中的属性与方法。如代码块中,函数f...
    99+
    2024-04-02
  • 一文浅析Golang中的闭包
    1、什么是闭包?在真正讲述闭包之前,我们先铺垫一点知识点:函数式编程函数作用域作用域的继承关系【相关推荐:Go视频教程】1.1 前提知识铺垫1.2.1 函数式编程函数式编程是一种编程范式,看待问题的一种方式,每一个函数都是为了用小函数组织成...
    99+
    2023-05-14
    闭包 Go 后端
  • golang函数中的闭包机制
    闭包是一种仍然保留其作用域链但在函数执行完后仍然存在的函数。在 go 语言中,可以通过匿名函数和变量来实现闭包,它在函数内允许访问外部作用域中声明的变量,从而提供了封装数据和行为以及为不...
    99+
    2024-04-29
    golang 闭包 作用域
  • golang函数的闭包
    闭包是在嵌套函数中定义的函数,可以访问嵌套函数作用域中的变量,包括返回值已返回后的变量。它们用于创建灵活可重用的代码,例如生成斐波那契数列的项:定义一个生成斐波那契项的闭包函数。闭包函数...
    99+
    2024-04-19
    golang 闭包 作用域
  • JavaScript中闭包的实现原理是什么
    这篇文章将为大家详细讲解有关JavaScript中闭包的实现原理是什么,小编觉得挺实用的,因此分享给大家做个参考,希望大家阅读完这篇文章后可以有所收获。什么是闭包,为什么要用它?闭包是指有权访问另一个函数作...
    99+
    2024-04-02
  • golang闭包的概念是什么
    闭包是指一个函数值(函数的指针)与其引用的其环境变量(值)的组合,形成了一个整体。换句话说,闭包是一个包含自由变量(在函数中使用,但...
    99+
    2023-10-20
    golang
  • Javascript中如何实现闭包
    这篇文章主要介绍了Javascript中如何实现闭包,具有一定借鉴价值,感兴趣的朋友可以参考下,希望大家阅读完这篇文章之后大有收获,下面让小编带着大家一起了解一下。闭包(closure)是Javascrip...
    99+
    2024-04-02
  • 一文彻底理解Golang闭包实现原理
    目录前言函数一等公民作用域实现闭包闭包扫描闭包赋值闭包函数调用函数式编程总结前言 闭包对于一个长期写 Java 的开发者来说估计鲜有耳闻,我在写 Pyt...
    99+
    2024-04-02
  • golang函数闭包实现的原理和使用场景
    go 中闭包原理:函数内嵌函数返回时,内嵌函数可访问外层函数变量,形成封闭环境。使用场景:1. 保持状态:闭包可维护内嵌函数状态,即使外层函数已返回;2. 延迟执行:用于延迟执行代码;3...
    99+
    2024-04-23
    golang 闭包
  • Python中的闭包是如何实现的?
    Python中的闭包是如何实现的?闭包是一种函数内部定义的函数,并且在函数内部引用了外部函数的变量。这种特性使得内部函数可以访问外部函数的变量,并且在外部函数执行完毕后,闭包仍然可以访问和操作外部函数的变量。闭包在Python中通过以下几个...
    99+
    2023-10-22
    Python 闭包 实现
  • Python中的闭包怎么用
    这篇文章主要介绍Python中的闭包怎么用,文中介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们一定要看完!1、闭包概念闭包在函数中提出的概念,简单来说就是一个函数定义中引用了函数外定义的变量,并且该函数可以在其定义环境外被执行。这样的...
    99+
    2023-06-25
  • Golang函数的闭包应用实例分享
    Golang是一门编程语言,它以其简洁、高效、并发安全等特性,受到越来越多开发者的青睐。Golang的函数是值类型,具备高度的灵活性和扩展性,可以方便地实现闭包这一功能。闭包是函数中常用的技巧之一,它允许函数访问在外部作用域中定义的变量,并...
    99+
    2023-05-16
    Golang 闭包 应用实例
软考高级职称资格查询
编程网,编程工程师的家园,是目前国内优秀的开源技术社区之一,形成了由开源软件库、代码分享、资讯、协作翻译、讨论区和博客等几大频道内容,为IT开发者提供了一个发现、使用、并交流开源技术的平台。
  • 官方手机版

  • 微信公众号

  • 商务合作