函数也是一种类型,定义变量用var,定义常量用const,定义函数用func。
匿名函数
匿名函数,没有名字,可以在定义的时候就直接运行了。
什么场景下会使用匿名函数呢?也就是闭包。一个最常用的场景就是程序运行出现错误的时候,我们要去恢复,一般是通过关键字defer,我们要有一个程序逻辑,在某些特定场景下运行的,但是我没有必要为整个逻辑定义函数,通常这样就可以使用闭包来做。
闭包
闭包(Closure)是引用了自由变量的函数,自由变量将和函数一同存在,即使已经离开了创造它的环境,闭包复制的是原对象的指针。
通常情况下一个函数的内部变量,在函数退出之后,这个函数的局部变量都会标志以废弃,都会被垃圾回收机制给回收,但是闭包就不一样。
package main
import "fmt"
//闭包(Closure)是引用了自由变量的函数。自由变量将和函数一同存在,即使已经离开了创造它的环境。
func sub() func() {
i := 10
fmt.Printf("%p\n", &i)
b := func() {
fmt.Printf("i addr %p\n", &i) //闭包复制的是原对象的指针
i-- //b函数内部引用了变量i
fmt.Println(i)
}
return b //返回了b函数,变量i和b函数将一起存在,即使已经离开函数sub()
}
// 外部引用函数参数局部变量
func add(base int) func(int) int {
return func(i int) int {
fmt.Printf("base addr %p\n", &base)
base += i
return base
}
}
func main() {
b := sub()
b()
b()
fmt.Println()
tmp1 := add(10)
fmt.Println(tmp1(1), tmp1(2)) //11,13
// 此时tmp1和tmp2不是一个实体了
tmp2 := add(100)
fmt.Println(tmp2(1), tmp2(2)) //101,103
}
函数也是一种数据类型
//f参数是一种函数类型
func function_arg1(f func(a, b int) int, b int) int {
a := 2 * b
return f(a, b)
}
type foo func(a, b int) int //foo是一种函数类型
func function_arg2(f foo, b int) int { //参数类型看上去简洁多了
a := 2 * b
return f(a, b)
}
type User struct {
Name string
bye foo //bye的类型是foo,而foo代表一种函数类型
hello func(name string) string //使用匿名函数来声明struct字段的类型
}
ch := make(chan func(string) string, 10)
ch <- func(name string) string { //使用匿名函数
return "hello " + name
}
函数类型赋值给变量
函数也可以赋值给变量,存储在数组、切片、映射中,也可作为参数传递给函数或作为函数返回值进行返回。
func add(a,b int) int{
return a + b
}
func main() {
c := add
fmt.Printf("%v",reflect.TypeOf(c))
}
这个c到底是什么东西?可以打印一下c的类型。
func(int, int) int 通过这个也可以看出来函数也是一种类型即函数类型,函数类型由参数类型和数量以及返回值数量和类型组成的。
如果我想要定义一个函数类型的变量怎么定义呢?
var f func(int,int) int
这样就定义了一个函数类型,变量后面就是函数类型。
var f func(int,int) int
f = add
fmt.Println(f(2,3))
func add(a,b int) int{
return a + b
}
func mul(a,b int) int{
return a - b
}
func main() {
c := add
fmt.Printf("%v",reflect.TypeOf(c))
fs := []func(int,int) int{c,mul}
fmt.Println(fs)
for _,v := range fs{
fmt.Println(v(2,1))
}
}
可以看到函数也是一种类型,函数类型是一个可以将函数类型赋值给一个变量。
通过函数类型也可以定义集合类型,如切片,map。在 Go 语言中,函数类型、map 类型自身,以及切片只支持与 nil 的比较,而不支持同类型两个变量的比较。
s1 := make([]int, 1)
s2 := make([]int, 2)
f1 := func() {}
f2 := func() {}
m1 := make(map[int]string)
m2 := make(map[int]string)
println(s1 == s2) // 错误:invalid operation: s1 == s2 (slice can only be compared to nil)
println(f1 == f2) // 错误:invalid operation: f1 == f2 (func can only be compared to nil)
println(m1 == m2) // 错误:invalid operation: m1 == m2 (map can only be compared to nil)
(1)函数可以赋值给变量
(2)函数是引用类型,只能和nil做比较
回调函数(Callback)
函数作为参数传入其他函数,并且在其他函数内部调用执行
声明&调用参数类型为函数的函数
func print1(fmt1 func(string)string,args ...string){
for i,v := range args{
fmt.Println(i,fmt1(v))
}
}
func format(params string)string{
return "*" + params + "*"
}
func table(params string)string{
return "|" + params + "|"
}
func main() {
names := []string{"jack","lucas"}
print1(format,names...)
print1(table,names...)
}
定义的函数format可以作为参数传入到print1函数里面去。print1函数可以接收任意函数类型为func(string)string的函数。上面就是通过函数类将一个函数传入到另外一个函数里面去了,并且在另外一个函数里面调用这个函数。
函数类型在定义的时候,一定要定义出它的参数和返回值。
函数返回值为函数类型
func()是一个是没有参数,没有返回值的一个函数
func sayHello() {
fmt.Println("hello")
}
func sayHi() {
fmt.Println("hi")
}
func genFunc() func(){
rand.Seed(time.Now().Unix())
if rand.Int() % 2 == 0 {
return sayHello
}else {
return sayHi
}
}
func main() {
a := genFunc()
a()
}
所以函数可以放到切片里面,map里面,可以赋值给一个变量,可以作为参数传递,也可以作为函数值进行返回。
这里FiledsFunc(s string,f func(rune) bool)[]string,这里第一个参数是string类型的,第二个参数是一个函数,一个传递rune类型返回值是bool类型的函数。
FiledsFunc(s string,f func(rune) bool)[]string
func aFileds(split rune) bool {
return split == 'a'
}
c := strings.FieldsFunc("abcd",aFileds)
fmt.Println(c)