Golang 函数数据类型、函数作为返回值

函数也是一种类型,定义变量用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)
  • 2
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值