【go语言】函数和闭包

函数声明(定义)

Go 语言函数定义格式如下:


func function_name( parameter list ) return_types {
   函数体
}

func:函数由 func 开始声明
function_name:函数名称,参数列表和返回值类型构成了函数签名。
parameter list:参数列表,当函数被调用时,你可以将值传递给参数,这个值被称为实际参数。参数列表指定的是参数类型、顺序、及参数个数。参数是可选的,也就是说函数也可以不包含参数。
return_types:返回类型,函数返回一列值。return_types 是该列值的数据类型。函数可以不需要返回值,这种情况下 return_types 不是必须的。
函数体:实现具体功能的代码集合。

实例
这是一个叫add() 函数的代码,该函数传入两个整型参数 num1 和 num2,并返回这两个参数的和

func add(num1 int,num2 int)int{
    return num1+num2
}

函数调用

定义了函数之后,可以通过函数名(参数...)的方式调用函数。 调用上面定义的add()函数

func main(){
	a,b:=3,5
	ret:=add(3,5)
	fmt.Println(ret)
}

在这里插入图片描述

函数返回多个值

go语言的函数和其他语言的函数相比的一个特点是go语言的函数可以返回多个值,这些返回值都是要被接收的
看一下下面的例子

func swap(x, y string) (string, string) {
   return y, x
}

func main() {
   a, b := swap("C++", "golang")
   fmt.Println(a, b)
   //如果不想接收第二个返回值,可以这样写
    a, _ := swap("C++", "golang")
}

在这里插入图片描述

参数传递机制

Go 语言的参数传递可以分为按值传递按寻引用传递。 Go 语言默认按值传递,按值传递 传递的是参数的副本,函数接收参数副本后,使用变量的过程中可能对副本的值进行更改,但不会影响原来的变量。换句话说,调用函数时修改参数的值,不会影响原来的实参的值,因为数值变化只作用在副本上。
所以如果要让函数直接修改参数的值,而不是对参数的副本进行修改,就需要将参数的地址(变量名前面添加&符号,比如&var)传递给函数,这就是按“引用传递”。此时传递给函数的是一个指针
如果把指针传递给函数 , 指针的值(地址)就会被复制, 但指针的值指向的地址上的那个值不会被复制(被复制的是指针,但是两个指针指向同一个实际的值) 。 这样一来 , 修改这个指针的值,实际上意味着这个值所指向的地址上的值就被修改了

用文字描述可能有点绕,但是通过下面的例子就比较好理解了

func incr(a int)int{
	a++
	return a
 }
 func incrP(a *int)int{
	*a++
	return *a
 }
func main(){
	x:=3
	fmt.Println("x=",x,"&x=",&x)
	y:=incr(x)
	fmt.Println("x=",x,"y=",y)
	z:=incrP(&x)
	fmt.Println("x=",x,"z=",z)
}

incr()函数中 x 变量的值没有发生变化 , 而incrP()函数中 x 的值变了。当调用 incr()的时候, incr()接收的参数其实是 x 的副本,而不是 x 本身;而incrP()函数接收的参数是一个指针,指向的是 x 的本身,所以 x实际的值就改变了 。
在这里插入图片描述

函数作为类型

type addFunc func(a,b int)int

这段代码的意思是,长这样的函数func(a,b int)int属于addFunc类型,既然有了类型,那么就可以定义这样类型的变量。

type addNum func(int,int)int
func main(){
	var a addNum=add
    b:=add
	fmt.Println(a(1,3))
	fmt.Println(b(5,7))
}
func add(a,b int)int{
	return a+b
}

在这里插入图片描述

go语言中函数也是值(first-class value)可以赋值给变量
但是,如果现在还有一个函数f。

func f(s string,x,y int) string{
	fmt.Sprintf(s,"%d %d",x,y)
	return s
}

那么,显然f不能赋值给a(类型不一样),f也不能赋值给b,因为b的类型也确定了,和f不一样。

函数作为参数

既然函数可以作为类型,那么我们就可以把这个类型的函数当成值来进行传递。

type FormatFunc func(s string,x,y int) string
func main(){
	
	b:=format(func(s string,x,y int)string{
		return fmt.Sprintf(s,x,y)
	},"%d%d",1,2)
	fmt.Println(b)
}
func format(ff FormatFunc,s string,x,y int) string{
	return ff(s,x,y)
}

在这里插入图片描述

可变参数

如果一个函数的最后一个参数是…type的形式那么这里函数可以处理类型为type的变长参数。

func minAndmax(s string ,a...int)int{
	if len(a)==0{
		return -1
	}
	if s=="max"{
		max:=a[0]
		for _,v:=range a{
			if v>max{
				max=v
			}
		}
		return max
	}else if s=="min"{
		min:=a[0]
		for _,v:=range a{
			if v<min{
				min=v
			}
		}
		return min
	}else{
		return -1
	}
}

传递参数的方法有两种,一种是手动填写参数,一种是将这堆数据存在数组arr中,通过arr…传递。

//手动填写参数
age:=minAndmax("min",9,3,1,5,2)
fmt.Printf("最小的年龄为%d\n",age)
//数组作为参数
arr:=[]int{2,4,1,5,6,3}
age=minAndmax("max",arr...)
fmt.Printf("最大的年龄为%d\n",age)

在这里插入图片描述

匿名函数/闭包

匿名函数是在函数声明时没有名字的一种函数,不能独立存在但是可以被赋值给某一个变量

func(num int){
		sum:=0
		for i:=0;i<=num;i++{
			sum+=i
		}
		fmt.Println(sum)
}

声明之后需要立即被调用或赋值给一个变量,不然的话就会报错。
在这里插入图片描述

闭包函数赋值

a:=func(num int){
		sum:=0
		for i:=0;i<=num;i++{
			sum+=i
		}
		fmt.Println(sum)
}

闭包函数调用
在函数体后面加上括号,如果有参数就传入参数。

func(num int){
		sum:=0
		for i:=0;i<=num;i++{
			sum+=i
		}
		fmt.Println(sum)
}(100)

闭包函数继承了声明时的作用域

func main(){
	j:=5
	
	a:=func() func(){
		i:=	10
		return func() {
			fmt.Printf("i=%d j=%d\n",i,j)
		}
	}()
	a()
}

由闭包函数继承了声明时的作用域可知,这个闭包访问在声明前的变量j是合法的。

闭包函数可以保存并累积变量值

func main(){
	func1:=ins()
	fmt.Println(func1())
	fmt.Println(func1())
	fmt.Println(func1())
	func2:=ins()
	fmt.Println(func2())
	fmt.Println(func2())
}
func ins() func() int{
	i:=0
	return func() int{
		i++
		return i
	}
}

在这里插入图片描述

  • 5
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值