共计 3246 个字符,预计需要花费 9 分钟才能阅读完成。
大家好,明天将梳理出的 Go 语言函数用法内容,分享给大家。请多多指教,谢谢。
本次《Go 语言函数应用》内容共分为三个章节,本文为第二章节。
- Golang 根底之函数应用 (一)
- Golang 根底之函数应用 (二)
- Golang 根底之函数应用 (三)
本章节内容
- 匿名函数
- 闭包
- 递归函数
- 提早调用 (defer)
匿名函数
介绍
在 Go 语言中,函数能够像一般变量一样被传递或应用,反对随时在代码里定义匿名函数。
匿名函数由一个不带函数名的函数申明和函数体组成。匿名函数的优越性在于能够间接应用函数内的变量,不用申明。
匿名函数有动态创建的个性,该个性使得匿名函数不必通过参数传递的形式,就能够间接援用内部的变量。
应用
第一种用法:将匿名函数赋给变量,在通过变量调用匿名函数
sum := func(a, b int) int {return a + b}
fmt.Println(sum(1, 2)) // 输入 3
第二种用法:在定义匿名函数的时候间接应用,这种形式只能应用一次传参
sum := func(a, b int) int {return a + b}(1,2) // 传入参数
fmt.Println(sum) // 输入 3
第三种用法:将匿名函数赋给一个全局变量,匿名函数在以后程序中都能够应用
package main
import "fmt"
var (
// 全局变量必须首字母大写
Sum = func(a, b int) int {return a + b}
)
func main() {sum := Sum(1, 2)
fmt.Println("sum=", sum) // 输入 sum= 3
}
闭包
介绍
所谓“闭包”,指的是一个领有许多变量和绑定了这些变量的环境的表达式(通常是一个函数),因此这些变量也是该表达式的一部分。
闭包(Closure),是援用了自在变量的函数。这个被援用的自在变量将和这个函数一起存在,即便曾经来到了发明它的环境也不例外。所以,有另一种说法认为闭包是由函数和与其相干的援用环境组合而成的实体。闭包在运行时能够有多个实例,不同的援用环境和雷同的函数组合能够产生不同的实例。
能够了解为:闭包是匿名函数与匿名函数所援用环境的组合,相似惯例函数援用全局变量处于一个包的环境。
闭包的长处
- 变量能够常驻内存
- 变量不净化全局
闭包里作用域返回的局部变量不会被立即销毁回收,可能会占用更多内存适度应用闭包会导致性能降落。
应用
package main
import "fmt"
func main() {
n := 0
count := func() int { // 这就是一个闭包
n += 1
return n
}
fmt.Println(count()) // 输入 1
fmt.Println(count()) // 输入 2
}
惯例函数、匿名函数 + 全局变量 + 包就等同于闭包,count
不仅存储了函数的返回值,还存储了闭包的状态。
闭包被返回赋予一个同类型的变量时,同时赋值的是整个闭包的状态,该状态会始终存在内部被赋值的变量
count
中,直到count
被销毁,整个闭包生命周期完结。
也能够写成下列模式
package main
import "fmt"
func Count() func() int { // 返回函数
n := 0
return func() int {
n++
return n
}
}
func main() {count := Count()
fmt.Println(count()) // 输入 1
fmt.Println(count()) // 输入 2
}
高级闭包个性,比方并发中的闭包。将放到前面章节为大家介绍。
递归函数
介绍
递归,就是在运行的过程中调用本人。
一个函数调用本人,就叫做递归函数。
形成递归具备的条件:
- 子问题须要与原始问题为同样的事,且更为简略。
- 不能无限度地调用自身,须有个进口,化简为非递归情况解决。
应用
举例:数字阶乘
一个正整数的阶乘是所有小于及等于该数的正整数的积,并且 0 的阶乘为 1。
package main
import "fmt"
func factorial(i int) int { // 解读为 5*4*3*2*1=120
if i <= 1 {return 1}
return i * factorial(i-1)
}
func main() {
var i int = 5
fmt.Printf("%d\n", factorial((i))) // 120
}
1808 年,基斯顿·卡曼引进这个表示法。
举例:裴波那契数列(Fibonacci)
这个数列从第 3 项开始,每一项都等于前两项之和。
package main
import "fmt"
func fibonaci(i int) int {
if i == 0 {return 0}
if i == 1 {return 1}
return fibonaci(i-1) + fibonaci(i-2)
}
func main() {
var i int
for i = 0; i < 10; i++ {fmt.Printf("%d", fibonaci(i)) // 0 1 1 2 3 5 8 13 21 34
}
}
提早调用 (defer)
介绍
在根底语法中曾经介绍了 defer 提早调用的应用,明天深刻理解应用一下 defer 机制。
defer 个性
- 关键字 defer 用于注册提早调用。
- 这些调用直到 return 前才被执。因而,能够用来做资源清理。
- 多个 defer 语句,按 先进后出 的形式执行。
- defer 语句中的变量,在 defer 申明时就决定了。
- 某个提早调用产生谬误,这些调用依旧会被执行。
defer 前面的语句在执行的时候,函数调用的参数会被保存起来,然而不执行。也就是复制了一份。
defer 用处
- 敞开文件句柄
- 锁资源开释
- 数据库连贯开释
应用
多个 defer 注册,按 FILO 秩序执行 (先进后出) 准则。
package main
func main() {defer println("1") // 先进来, 最初进来
defer println("2")
defer println("3") // 最初进来, 先进来
}
输入
3
2
1
提早调用参数在注册时求值或复制,能够用指针或闭包“提早”读取。
package main
import "fmt"
func main() {
x, y := 10, 100
defer func(i int) {fmt.Printf("defer x = %v, y = %v\n", i, y) // y 闭包援用
}(x) // x 被复制
x += 10
y += 20
println("x =", x, "y =", y)
}
输入
x = 20 y = 120
defer x = 10, y = 120
defer 和 return 两者执行程序
- 有名返回值 (函数返回值为曾经命名的返回值)
package main
import "fmt"
func foo() (i int) { // 3.return i 值
i = 0
defer func() {fmt.Println(i) // 2. 读取长期变量地址(返回值)
}()
return 2 // 1. 返回值赋值, 写入长期变量
}
func main() {foo()
}
输入
2
在 foo()
返回值的函数中 (这里返回值为 i),执行 return 2
的时候实际上曾经将 i 的值从新赋值为 2。所以 defer closure 输入后果为 2。
解析:
return 的机制:1. 首先将返回值放到一个长期变量中(为返回值赋值) 2. 而后将返回值返回到被调用处。
而 defer 函数恰在 return 的两个操作之间执行。
执行程序是: 先为返回值赋值,行将返回值放到一个长期变量中,而后执行 defer,而后 return 到函数被调用处。
- 无名返回值 (即函数返回值为没有命名的函数返回值)
package main
import "fmt"
func foo() int {
var i int
defer func() {
i++ // 这个中央 i,不是长期变量
fmt.Println("defer =", i) // 输入 defer = 1
}()
return i // 返回值赋值, 写入长期变量
}
func main() {fmt.Println("return =", foo()) // 输入 return = 0
}
解析:
return 先把返回值放到一个长期变量中,defer 函数无奈获取到这个长期变量地址 (没有函数返回值),所以无论 defer 函数做任何操作,都不会对最终返回值造成任何变动。
技术文章继续更新,请大家多多关注呀~~
搜寻微信公众号【帽儿山的枪手】,关注我