函数类型+匿名函数+闭包
函数也可以赋值给变量,存储在数组、切片、映射中,也可作为参数传递给函数或作为函数返回值进行返回
通过函数类型,我们也可以定义一些集合类型,比如切片的映射的
函数类型的意义:
函数类型可以把函数作为参数传递到其他的函数中
1 函数类型
函数类型:由参数的数量和他的类型,还有返回值的数量和类型组成
package main
import "fmt"
func add(a, b int) int {
return a + b
}
func main() {
c := add
fmt.Printf("%T\n", c)
}
通过输出我们可以看到,函数本身也是一个类型
[17:04:03 root@go day1]#go run main.go
func(int, int) int
1.1 定义函数类型
package main
import "fmt"
func main() {
// 定义函数类型
var f func(int, int) int
fmt.Printf("%T\n", f)
}
[15:01:56 root@go day1]#go run main.go
func(int,int) int
这个函数类型的 f 有什么用呢,我们就可以将 add 函数赋值给 f,f 就可以调用 add 函数中的形参和返回值,前提是 f 这个函数类型的形参和返回值要对应 add 函数的类型
package main
import "fmt"
func add(a, b int) int {
return a + b
}
func main() {
// 定义函数类型
var f func(int, int) int
// 将 add 函数赋值给 f
f = add
fmt.Printf("type = %T\nvalue = %v\n", f, f(1, 2))
}
[15:28:20 root@go day1]#go run main.go
type = func(int, int) int
value = 3
也可以这样赋值
package main
import "fmt"
func add(a, b int) int {
return a + b
}
func main() {
// 定义函数类型
var f func(int, int) int = add
fmt.Printf("type = %T\nvalue = %v\n", f, f(1, 2))
}
[15:28:20 root@go day1]#go run main.go
type = func(int, int) int
value = 3
1.1.1 定义函数切片
package main
import "fmt"
func add(a, b int) int {
return a + b
}
func mul(a, b int) int {
return a * b
}
func main() {
// 定义函数切片
var fs []func(int, int) int
// append 追加 add 和 mul 函数
fs = append(fs, add, mul)
// for-range 变量 fs ,由于 fs 是一个切片,所以 fs 的元素就是对应的每个函数
// 将 fs 的元素赋值给 f
for _, f := range fs {
// 给 f 这个函数传递 2,3
// 第一次调用 add 函数
// 第二次调用 mul 函数
fmt.Println(f(2, 3))
}
}
[15:29:06 root@go day1]#go run main.go
5
6
1.1.2 声明&调用参数类型为函数的函数
函数类型
func(参数类型列表) 返回值类型列表
传递一个函数到另一个函数中的时候,形参函数的类型、返回值与传递函数的类型、返回值要相同
函数形参的定义不需要定义名字,只需要定义函数的类型
package main
import "fmt"
// print 函数形参为 fmts 函数类型为 func(string) string
// args 可变参数的 string 类型
func print(fmts func(string) string, args ...string) {
// 遍历 args 取出对应的值
for i, v := range args {
// 再将 args 的值取出来传递给 fmts 函数
fmt.Println(i, fmts(v))
}
}
// 定义 star 函数类型维 func(string) string
func star(txt string) string {
return "*" + txt + "*"
}
func main() {
// 定义 names 切片
names := []string{"zz", "kk", "17"}
// 调用 print 函数,传入 start 函数和 names 切片解包后的值
// star 传给 fmts 函数
// names... 传递给 args
print(star, names...)
}
[15:31:38 root@go day1]#go run main.go
0 *zz*
1 *kk*
2 *17*
1.1.3 函数返回值是一个函数类型
作为返回值的时候,需要返回的函数类型和返回值相等
package main
import (
"fmt"
"math/rand"
"time"
)
func sayHi() {
fmt.Println("Hi")
}
func sayHello() {
fmt.Println("hello")
}
// 返回值为 func() 函数
func genFunc() func() {
// 随机数取模 2 == 0 时候 return sayHi ,否则 return sayHello
if rand.Int()%2 == 0 {
return sayHi
} else {
return sayHello
}
}
func main() {
// 种子
rand.Seed(time.Now().UnixNano())
f := genFunc()
f()
}
[16:12:38 root@go day1]#go run main.go
Hi
范例二:
package main
import "fmt"
// 定义返回值为 func(int, int) int
func test(srt string) func(int, int) int {
fmt.Print(srt)
return intr
}
func intr(a, b int) int {
return a + b
}
func main() {
// f 为 func(int, int) int ,接收 test 函数返回值
f := test("值等于:")
fmt.Println(f(1, 2))
}
[16:16:40 root@go day1]#go run main.go
值等于
3
范例三:
package main
import "fmt"
func print(fmts func(string) string, args ...string) func(int, int) int {
for i, v := range args {
fmt.Println(i, fmts(v))
}
return intr
}
func star(txt string) string {
return "♥" + txt + "♥"
}
func intr(a, b int) int {
fmt.Println("intr 函数结果:")
return a + b
}
func main() {
names := []string{"77", "拦截", "zz"}
// 返回值函数赋值给 res = func(int, int) int
// 并且 print 函数里面的代码也会执行
res := print(star, names...)
// 输出的时候给 res 赋值 1,2
fmt.Println(res(1, 2))
}
[16:29:44 root@go day1]#go run main.go
0 ♥77♥
1 ♥拦截♥
2 ♥zz♥
intr 函数结果:
3
通过调用函数,实现字符分割
package main
import (
"fmt"
"strings"
)
// 定义 函数 aFields 类型为 func(rune) bool
func aFields(split rune) bool {
if split == 'a' {
return true
} else {
return false
}
}
func main() {
// 输出 strings.FieldsFunc 函数,第一个参数为 string ,第二个参数必须为 func(rune) bool
fmt.Printf("%q\n", strings.FieldsFunc("abcdef", aFields))
}
把 a 进行分割
[16:43:40 root@go day1]#go run main.go
["bcdef"]
当然这个代码还能够优化
很多时候代码会写得很长,但是我们只需要一句话就可以将代码写完
package main
import (
"fmt"
"strings"
)
func aFields(split rune) bool {
// 由于返回值为 bool 类型,所以这里直接 split == a 默认就为 真,否则为 false
return split == 'a'
}
func main() {
fmt.Printf("%q\n", strings.FieldsFunc("abcdef", aFields))
}
[16:43:40 root@go day1]#go run main.go
["bcdef"]
也可以简写成匿名函数
package main
import (
"fmt"
"strings"
)
func main() {
fmt.Printf("%q\n", strings.FieldsFunc("abcdef",
func(split rune) bool {
return split == 'a'
}))
}
[16:43:40 root@go day1]#go run main.go
["bcdef"]
1.2 匿名函数
不需要定义名字的函数叫做匿名函数,常用帮助函数在局部代码块中使用或作为其他函数的参数
匿名函数是没有名字,一般用来赋值给变量的,只能使用在函数内部
1.2.1 匿名函数定义
很多时候我们定义完了函数只需要使用一次,不具有代码的复用性,当我们临时使用的时候就可以使用匿名函数
package main
import "fmt"
func main() {
// 定义一个 c 类型为 func()
c := func() {
}
fmt.Printf("%T\n", c)
}
通过输出我们会发现 c 是一个 func()
类型
[16:51:51 root@go day1]#go run main.go
func()
匿名函数赋值和使用
package main
import "fmt"
func main() {
// 定义函数,变量为 c
c := func() {
fmt.Println("我是匿名函数")
}
// 使用 c 函数
c()
fmt.Printf("%T\n", c)
}
[17:01:18 root@go day1]#go run main.go
我是匿名函数
func()
使用范例二
package main
import "fmt"
func print(fmts func(string) string, args ...string) {
for i, v := range args {
fmt.Println(i, fmts(v))
}
}
func main() {
names := []string{"77", "kk", "zz"}
// 定义 star 匿名函数,类型为 func(string) string
// star 标识符只能在 main 函数内部使用
star := func(txt string) string {
return "*" + txt + "*"
}
// 传递给 print 函数
print(star, names...)
}
[17:02:18 root@go day1]#go run main.go
0 *77*
1 *kk*
2 *zz*
简写直接函数传递
package main
import "fmt"
func print(fmts func(string) string, args ...string) {
for i, v := range args {
fmt.Println(i, fmts(v))
}
}
func main() {
names := []string{"77", "kk", "zz"}
// 传递给 print 函数
// 直接传递 函数 func(s string) string { return "*" + s + "*" }
print(func(s string) string {
return "*" + s + "*"
}, names...)
}
[17:02:18 root@go day1]#go run main.go
0 *77*
1 *kk*
2 *zz*
1.2.3 函数直接调用
匿名函数直接使用
package main
import "fmt"
func main() {
// 由于这个变量没有使用变量来接收,所以需要使用 () 自我调用
func() {
fmt.Println("我是匿名函数")
}() // 后面加上 () 表示直接调用
}
[17:43:55 root@go day1]#go run main.go
我是匿名函数
1.3 闭包
匿名函数又叫闭包, 是指在函数内定义的匿名函数引用外部函数的变量,只要匿名函数继续使用则外部函数赋值的变量不被自动销毁
如果使用不严谨的话会导致内存泄漏
addBase
: 函数执行完以后,这个 addBase
函数中的 base
应该释放掉,函数内部引用函数外部的变量,使函数外部的变量生命周期发生了变化这就是闭包
变量生命周期:指的是变量生成到死亡的时间
package main
import "fmt"
// 生成一个函数
// 定义 addBase(int) func(int) int
// 返回值函数 num + base
func addBase(base int) func(int) int {
// 在函数内部引用函数外部的变量
// 当我们内部的函数返回的时候,其实外部的这个 base 变量不能及时销毁
// 所以我们在返回函数里面就要添加 base 变量
return func(num int) int {
return base + num
}
}
func main() {
// add2 = func(int) int
add2 := addBase(2)
fmt.Printf("%T\n", add2)
fmt.Println(add2(10))
}
[17:46:26 root@go day1]#go run main.go
12