快捷搜索:  汽车  科技

golang可以跨平台吗(Golang闭包)

golang可以跨平台吗(Golang闭包)package main import "fmt" func main() { var functor func() int functor = func() int { return 10 } fmt.Println(functor()) functor = func() int { return 15 } fmt.Println(functor()) functor = func() int { return 20 } fmt.Println(functor()) }运行结果:➜ closure git:(mas

预计阅读时间:25分钟

golang可以跨平台吗(Golang闭包)(1)

南京夫子庙

闭包是高级语言通常都会包含的一种语法,Python和Go中都支持这个语法。

那么,究竟什么是闭包呢?

首先我们从匿名函数开始讲起:

匿名函数

同样Python和Go都支持匿名函数,以Go为例,我们看一个简单的例子吧:

package main import "fmt" func main() { lambda := func() { fmt.Println("I'm an anonymous function") } lambda() }

运行结果:

➜ closure git:(master) ✗ go run lambda.go I'm an anonymous function

函数作为一等公民(First class citizen),可以把它赋给一个变量,这样就可以做到实时更新:

package main import "fmt" func main() { var functor func() int functor = func() int { return 10 } fmt.Println(functor()) functor = func() int { return 15 } fmt.Println(functor()) functor = func() int { return 20 } fmt.Println(functor()) }

运行结果:

➜ closure git:(master) ✗ go run functor_realtime_update.go 10 15 20闭包

如果一个内部嵌套函数中,引用了外部定义的变量,那么我们就称这个函数为“闭包”,同时我们称被引用的变量为“引用变量”或“自由变量”。

下面我们看一个引用外部变量的嵌套函数:

package main import "fmt" func main() { var nestedFunctorInMain func() counter := 0 nestedFunctorInMain = func() { counter = 1 fmt.Println(counter) } nestedFunctorInMain() nestedFunctorInMain() nestedFunctorInMain() }

运行结果:

➜ closure git:(master) ✗ go run nested_function.go 1 2 3

这里的“nestedFunctorInMain”就是一个闭包,它嵌套在入口函数“main”中,并且引用了外部变量“counter”。

那闭包有什么特殊的用处呢?最简单的,利用闭包特性,我们可以达到数据隔离的目的:

package main import "fmt" func main() { counter := newCounter() fmt.Println(counter()) fmt.Println(counter()) } func newCounter() func() int { n := 0 return func() int { n = 1 return n } }

运行结果:

➜ closure git:(master) ✗ go run data_isolation.go 1 2

之前的嵌套函数的例子里面,我们的闭包使用了main函数中的外部变量,但是这个变量使用范围过大,其他函数也可以更新它,在本例中,我们设置在新函数中设置变量“n” 而嵌套匿名函数引用它,即使“newCounter”函数执行完毕,变量依然存在闭包中,且外部无法访问,这样就很方便地达到了数据隔离的目的。

闭包中遇到的常见问题
  • defer和go使用函数调用作为参数

假设我们有个setup和teardown的功能需要实现,即在函数执行前利用setup做一些初始化操作,然后在执行结束后,做环境清理工作:

package main import "fmt" func main() { defer func() { fmt.Prinltn("teardown") } }

运行结果:

➜ closure git:(master) ✗ go run defer_need_func_call.go # command-line-arguments ./defer_need_func_call.go:3:8: imported and not used: "fmt" ./defer_need_func_call.go:8:4: expression in defer must be function call

因为“defer”和“go”关键字的参数是函数调用,而不是函数,所以按照本例的写法,编译代码时会报错:“expression in defer must be function call”。

我们改进一下:

package main import "fmt" func main() { defer setup() } func setup() func() { fmt.Println("pretend to set things up") return func() { fmt.Println("pretend to tear things down") } }

运行结果:

➜ closure git:(master) ✗ go run defer_need_func_call.go pretend to set things up

“teardown”并未得到执行!为什么?

仔细看本例中代码,“setup”函数返回匿名函数,即“teardown”,但是“teardown”并未有机会得到显式执行,所以代码未运行到。

再改进一下:

package main import "fmt" func main() { defer setup()() } func setup() func() { fmt.Println("pretend to set things up") return func() { fmt.Println("pretend to tear things down") } }

运行结果:

➜ closure git:(master) ✗ go run defer_need_func_call.go pretend to set things up pretend to tear things down

显式执行返回的匿名函数,“teardown”部分得到执行。

  • “for”循环中的变量是引用传递而非值传递

这个问题很容易让人迷惑,我们先看个例子:

package main import "fmt" func main() { var functions []func() for i := 0; i < 10; i { functions = append(functions func() { fmt.Println(i) }) } for _ f := range functions { f() } }

运行结果:

➜ closure git:(master) ✗ go run for_loop_refer_assign.go 10 10 10 10 10 10 10 10 10 10

按照我们在理解,不是应该按顺序输出“0”到“9”的数字吗?这是因为,当我们在“for”循环做迭代的时候,“for”内部的变量传递不是我们通常理解的值传递,而是引用传递,导致最后函数切片中所有的函数中使用到的变量“i”都是循环之后变量“i”的值,从而最后输出的都是“10”。

那如果我们想按照初衷一次打印每个迭代中的数字,如何做呢?

有一种办法可以做到,因为函数参数是值传递,可以通过一个辅助函数达到目的:

package main import "fmt" func main() { var functions []func() for i := 0; i < 10; i { functions = append(functions build(i)) } for _ f := range functions { f() } } func build(val int) func() { return func() { fmt.Println(val) } }

运行结果:

➜ closure git:(master) ✗ go run for_loop_refer_assign.go 0 1 2 3 4 5 6 7 8 9

猜您喜欢: