go学习总结(九)函数

函数定义

什么是函数呢?
函数就是将一堆代码进行重用的一种机制。函数就是一段代码,一个函数就像一个专门做这件事的人,我们调用它来做一些事情,它可能需要我们提供一些数据给它,它执行完成后可能会有一些执行结果给我们。要求的数据就叫参数,返回的执行结果就是返回值。

函数基本语法
func 函数名(){
函数体
}

通过func关键字来定义函数,函数名后面必须加括号。
在这里插入图片描述
注意:函数要执行必须调用,调用的方式通过函数名进行调用,但是千万不能忘记括号。以上代码的执行流程是:先执行main()函数,前面我们讲过,main函数是整个程序的入口,所以我们一般将需要调用的函数名称写在main函数中。当执行到StartGame()时,就会去执行StartGame()函数体中的代码,该函数体代码执行完毕后,又回到main()函数,继续往下执行,这时执行到EndGame(),就去执行EndGame()函数体中的代码,执行完后又回到main()函数,以此类推,指导将main()函数中的所有代码执行完毕为止。

普通参数列表

所谓的普通参数列表指的是,我们给函数传递的参数的个数都是确定好。基本语法如下:
在这里插入图片描述
首先我们定义了一个Test()函数,该函数有两个参数,a,和b .并且这两个参数的类型都是整型的(这两个参数我们称之为形参),在调用Test()函数时,我们将3传递给参数a,将5传递给参数b(在调用时输入的3和5这个参数我们称之为实参)。我们把这个过程称为参数的传递,并且在Test()函数中输出两个变量的值。
什么时候传递参数呢?其实就是根据我们的需求,例如:定义一个函数,专门实现两个数的和。
在这里插入图片描述
根据上面的案例我们总结出,参数的个数和类型可以根据需要去确定。
但是一定要注意:在定义函数时,形参与实参的个数与类型都要保持一致。
SumAdd函数需要的两个参数的类型都是整型的,所以该函数的参数也可以写成如下的形式:
在这里插入图片描述
这时候参数a的类型是整型。但是,不建议这样定义,因为不够清晰。
请看如下方法的定义:
func MyFunc05(a, b string, c float64, d, e int) {
}
该方法的参数类型分别什么?
如果我们将上面的方法定义成如下形式,大家看一下是不是非常清晰。
func MyFunc06(a string, b string, c float64, d int, e int) {
}

不定参数列表

根据前面的讲解,我们都知道了,在定义函数的时候根据需求指定参数的个数和类型,但是有时候如果无法确定参数的个数呢?
举例说明:上一小节我们写过一个求两个整数之和的函数,但是在实际的开发中,也会经常遇到这样的情况,就是项目经理(对整个项目的进度进行把控,对程序员进行管理的人员,称为项目经理),要求你写一个函数,实现整数的和。在这个要求中,项目经理并没有说清楚到底是有几个整数,那么我们应该怎样确定该函数的参数呢?就用接下来给大家讲解的“不定参数列表”来解决这个问题
那么我们可以通过如下的方式来定义函数:
在这里插入图片描述
Test()函数的参数名字叫args(参数的名字可以随便起),类型是整型的。但是,大家一定要注意,在args后面跟了三个点,就是表示该参数可以接收0或多个整数值。所以,args这个参数我们可以想象成是一个集合(类似数学中集合),可以存放多个值。
所以,在Test()函数内,我们通过以前学习的一个函数叫len(),来计算出args这个集合中存储了多少个数(如果args这个集合中存储了5个数,那么len()函数的值就是5),通过for循环将该集合中的数全部输出,在输出时我们通过下标的方式将args集合中的值输出的。所谓的下标,我们可以理解成就是一个编号,对存储在args这个集合中每个数字都加上了编号。在这里要注意的是:下标是从0开始计算的。如下图所示:
在这里插入图片描述
args集合中存储了5,6,7三个数,对应的下标(编号),分别是0,1,2. 如果该集合中存储了4个数,那么第4个数的编号就是3.现在取出第一个数就是args[0]值为5,第二个数args[1]值为6,以此类推。

在Test()函数中,我们除了使用len()函数,计算出集合中存储的数据的个数,然后再输出以外,还有另外一种输出的方式就是使用range关键字。如下所示:
在这里插入图片描述
range会从集合中返回两个数,第一个是对应的坐标,赋值给了变量i,第二个就是对应的值,赋值了变量data
所以以上两种输出集合的方式,大家在以后的开发过程中都可以使用。

当然在使用不定参数时,要注意几个问题:
第一:一定(只能)放在形参中的最后一个参数。例如:
在这里插入图片描述
上面我们定义了一个Test()函数,该函数第一个参数是一个普通的整型类型,第二个参数是不定参数。那么不定参数args,必须放在后面,整型类型的参数a必须放在前面.如果两者的位置进行互换,如下所示:
在这里插入图片描述
在编译的时候就会出错,出现的错误信息如下:
在这里插入图片描述
最后我们来实现一下,一开始提出的“写一个函数,实现整数的和”这个问题,这个函数现在实现起来就非常简单了。
在这里插入图片描述
在以上的案例中,我们用到了一个下划线( _ ),该下划线表示匿名变量,丢弃数据不进行处理,也就是任何赋予它的值都会被丢弃。在前面讲解的时候,我们只是说过匿名变量的语法,没有讲解其应用的场景,那么大家可以通过该案例体会出匿名变量的应用场景。

函数嵌套调用

函数也可以像我们在前面学习if选择结构,for循环结构一样进行嵌套使用。所谓函数的嵌套使用,其实就是在一个函数中调用另外的函数。
在这里插入图片描述
函数嵌套执行的过程:
(1) 先执行main()函数,在main()函数中调用Test()函数,同时将参数分别传递给Test()函数的a,b
(2) Test( )函数中调用Test1( )函数,进行参数的传递。
(3) 执行Test1( )函数中的代码,打印两个数的和。
(4) Test1( )函数中所有的代码执行完成后,会回到Test( )函数,执行Tes( )函数剩余的代码。
(5) 当Test( )函数中所有的代码执行完成后,会回到main( )函数,执行main( )函数后面剩余的代码。

不定参数函数调用
不定参数的函数在调用的时候,要注意一些细节问题。
我们通过案例给大家演示一下:
在这里插入图片描述
Test1(args…)表示将参数全部传递,所以Test1( )函数最终的输出结果是:1,2,3
如果我们只想传递一部分数据,而不是传递所有的数据,应该怎样进行传递呢?
在这里插入图片描述
以上案例的输出结果 :3
另外一种写法:
在这里插入图片描述
以上程序的输出结果是1,2

函数返回值

前面我们学习过一个GO自带的函数,len( )函数。该函数的作用是获取集合中数据的个数,也就是说该函数有返回值。
我们拿到该返回值后,就可以做进一步的处理,例如:可以用来作为循环条件。
我们自己定义的函数怎样返回值呢?
基本语法如下:
在这里插入图片描述
(1) 在定义函数Test( )时,后面加了int,表示该函数最终返回的是一个整型的数据
(2) 在Test( )函数中要返回数据,必须要返回的数据放在return关键字之后(通过return关键字返回数据)。
(3) 在main( )中调用Test( )函数,这时会执行Test( )函数中的代码,当执行完 return sum时,会将sum变量中保存的值返回。
(4) Test( )函数返回的值会赋值给main( )函数中的result变量。

以上是定义一个具有返回值函数的基本语法,当然,GO语言也提供了另外一种语法定义具有返回值的函数,如下所示:
在这里插入图片描述
第三种写法:
在这里插入图片描述
以上几种写法,都可以大家可以根据自己的习惯进行选择。
以上案例中,没有给Test( )函数传递参数,如果需要对Test( )函数进行参数传递,可以按照前面讲解的参数传递的内容,对函数进行参数进行传递。
案例演示如下:
在这里插入图片描述
返回多个值:
上面案例中,我们定义的函数都是返回一个指,那么一个函数是否可以返回多个值呢?可以,具体语法如下:
在这里插入图片描述
第二种写法:
在这里插入图片描述

函数类型

在讲解函数类型之前,我们先简单的回顾一下,前面我们是怎样定义一个函数,以及怎样调用一个函数的。
我们通过如下的案例简单复习一下:
在这里插入图片描述
通过上面的案例,我们将函数的定义,参数传递,返回值都复习了一下。
在GO语言中还有另外一种定义使用函数的方式,就是函数类型,所谓的函数类型就是将函数作为一种类型可以用来定义变量,这种用法类似于前面我们讲过的int ,float64,string等类型,这些类型都是可以用来定义变量。基本语法如下:
在这里插入图片描述
说明如下:type关键字后面跟着类型的名字(FunType),FunType就是一个类型.那么FunType是一个什么类型呢?
是一个函数类型,因为FunType后面跟着func(用来定义函数的),但是这里注意的是没有函数名字。那么FunType是怎样的一个函数类型呢?是一个需要传递两个整型参数,有一个整型返回值的函数类型。

既然函数类型类似于我们前面学习过的 int ,string 等类型,那么函数类型可以用来定义变量。
var result FuncType //表示定义了一个变量叫result,该变量的 类型是FuncType类型,而该类型是一个函数类型。
下面我们可以使用result这个函数类型的变量来调用函数了。
result=Test //将要调用的函数的名字赋值给result变量(也可以理解成将result变量指向了要调用的函数),这里要注意的是:第一:Test后面不能加括号。第二:函数类型变量result要和将要调用的函数Test保持一致,所谓一致就是我们定义的函数类型FuncType的变量result,只能调用参数是两个整型的,并且有一个返回值,而且也是整型的函数。那么Test函数完全满足要求。

现在已经完成了函数类型变量result指向了函数Test,那么我们可以使用函数类型的变量result调用函数:
result(3,6)//完成函数的调用。

这是我们在GO语言中使用函数类型的方式完成函数的调用,可能有同学感觉这种方式比较麻烦,不如我们前面讲解的方式使用起来简单。那么在这里,要求大家先理解这种方式,能记住语法就可以了。关于该方式的优势,等我们讲解完面向对象编程这个知识点后,大家就有深刻的理解了。

函数作用域

局部变量

前面我们定义的函数中,都经常使用变量。那么我们看一下如下程序的输出结果:
在这里插入图片描述
最终的输出结果是9,为什么呢?在执行fmt.Println(a)语句之前,我们已经调用了函数Test(),并在该函数中我们已经重新给变量a赋值了。但是为什么结果没有发生变化呢?这就是变量的作用范围(作用域)的问题。在Test( )函数中定义的变量a,它的作用范围只在改函数中有效,当Test( )函数执行完成后,在该函数中定义的变量也就无效了。也就是说,当Test( )函数执行完以后,定义在改函数中所有的变量,所占有的内存空间都会被回收。
所以,我们把定义在函数内部的变量称为局部变量。
局部变量的作用,为了临时保存数据需要在函数中定义变量来进行存储,这就是它的作用。
并且,通过上面的案例我们发现:不同的函数,可以定义相同的名字的局部变量,但是各用个的不会产生影响。例如:我们在main( )函数中定义变量a,在Test( )函数中也定义了变量a,但是两者之间互不影响,就是因为它们属于不同的函数,作用范围不一样,在内存中是两个存储区域。

全局变量

有局部变量,那么就有全局变量。所谓的全局变量:既能在一个函数中使用,也能在其他的函数中使用,这样的变量就是全局变量.也就是定义在函数外部的变量就是全局变量。全局变量在任何的地方都可以使用。案例如下:
在这里插入图片描述
注意:在上面的案例中,我们在函数外面定义了变量a,那么该变量就是全局变量,并且Test( )函数和main( )函数都可以使用该变量。该程序的执行流程是:先执行main( )函数,给变量a赋值为9,紧接着调用Test( )函数,在改函数中完成对变量a的修改。
由于main( )函数与Test( )函数所使用的变量a是同一个,所以当Test( )函数执行完成后,变量的a已经变成了6. 回到main( )函数执行后面的代码,也就是 fmt.Println(a),输出的值就是6.
可能有同学已经发现该程序和我们前面写的程序还有一点不同的地方是:第一个程序我们是a:=9,但是第二个程序执行修改成了 a=9, 现在修改一下第二个程序如下:
在这里插入图片描述
该程序与上面的程序不同之处在于,该程序是a:=9,上面的程序是a=9.
现在大家思考一下该程序的结果是多少?
最终结果是9.
原因是:a:=9等价于
var a int
a=9
也就是定义一个整型变量a,并且赋值为9.
那么现在的问题是,我们定义了一个全局变量a,同时在main( )中又定义了一个变量也叫a,但是该变量是一个局部变量。
当全局变量与局部变量名称一致时,局部变量的优先级要高于全局变量。所以在main( )函数中执行fmt.Println(a)时输出的是局部变量a的值。但是Test( )函数中的变量a还是全局变量。
注意:大家以后在开发中,尽量不要让全局变量的名字与局部变量的名字一样。
所以大家,思考以下程序执行的结果:
在这里插入图片描述
总结:
(1)在函数外边定义的变量叫做全局变量。
(2)全局变量能够在所有的函数中进行访问
(3)如果全局变量的名字和局部变量的名字相同,那么使用的是局部变量的,小技巧强龙不压地头蛇

匿名函数

前面我们定义函数的时候,发现是不能在一个函数中,再次定义一个函数。如果我们想在一个函数中再定义一个函数,那么可以使用匿名函数,所谓匿名函数就是没有名字的函数。
如下所示:
在这里插入图片描述
在main( )函数中定义了一个匿名函数,定义的方式非常简单func(){ 函数体 }, 一定要注意的是在func的后面没有函数的名字,同时在这里定义的该匿名函数也没有参数。我们将定义好的匿名函数赋值给了变量f,那么变量f就是一个函数类型。要想执行该匿名函数,就可以通过f( )的方式去调用执行。
在这里,有一件非常有意思的事情,就是在匿名函数中可以直接访问main( )函数中定义的局部变量,并且在匿名函数中对变量的值进行了修改,最终会影响到整个main( )函数中定义的变量的值。所以上面两行输入都是10.
关于这一点,一定与上一节讲解的函数作用域进行区别。

匿名函数还有其它调用方式如下:
在这里插入图片描述
上面案例中,定义的匿名函数赋值给了变量f,那么f的类型就是函数类型,所以我们自己也可以定义一个函数类型的变量来调用匿名函数。但是上面的应用比较繁琐,实际用的比较少。

定义匿名函数时,直接调用
在这里插入图片描述
该方式,需要在匿名函数的末尾加上小括号,表示调用。同时也不需要将定义好的匿名函数赋值给某个变量。

下面看一下怎样给匿名函数传递参数:
在这里插入图片描述
或者如下方式:
在这里插入图片描述
匿名函数如果有返回值,怎样进行处理呢?
在这里插入图片描述
以上案例中定义了一个匿名函数,该匿名函数需要两个整型参数,同时指定了该函数返回值的名字是变量max与min
当执行到return时,让变量manx与min返回,赋值了变量x,y。x中存储的是max变量的值,y中存储的是min变量的值。
以上就是关于什么是匿名函数,以及匿名函数的使用。匿名函数最主要的功能就是实现了闭包。

递归函数

通过前面的学习知道一个函数可以调用其他函数。
如果一个函数在内部不调用其它的函数,而是自己本身的话,这个函数就是递归函数。
例如:
在这里插入图片描述
注意:递归函数的执行流程
递归函数的作用:
举个例子,我们来计算阶乘 n! = 1 * 2 * 3 * … * n

其它应用场景:
电商网站中的商品类别菜单的应用。
查找某个磁盘下的文件。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

vegetablesssss

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值