一文带你熟悉Go语言中函数的使用
作者:陈明勇 发布时间:2023-07-13 08:22:23
函数
函数的英文单词是 Function
,这个单词还有着功能的意思。在 Go 语言中,函数是实现某一特定功能的代码块。函数代表着某个功能,可以在同一个地方多次使用,也可以在不同地方使用。因此使用函数,可以提高代码的复用性,减少代码的冗余。
函数的声明
通过案例了解函数的声明有哪几部分:
定义一个函数,实现两个数相加的功能,并将相加之后的结果返回。
func Add(num1 int, num2 int) int {
var sum int
sum += num1
sum += num2
return sum
}
通过案例可以发现,函数的声明有5部分:
1、关键字
函数的关键字是 func
,声明函数必须以 func
关键字开始。
2、函数名
Go
推荐使用驼峰命名的方式,和变量的命名规则一样,首字母大写的函数名可以在包外访问,小写的只能在包内访问。
3、参数列表
参数列表中声明了在函数体里所使用到的变量。参数列表位于函数名后面,用括号包裹着,多个参数使用逗号分隔开。
4、返回值列表
返回值为函数执行后的一个结果,上述代码只有一个返回值,如果有多个返回值,需要用括号包裹着,返回值之间用逗号分隔开。
少数情况下,我们会声明返回值的的名称如 func Add(num1 int, num2 int) sum int {}
,多数情况下是不用声明返回值的名称的。
5、函数体
大括号内就是函数体,存放着函数的具体实现。 函数的第 3
第 4
部分可有可无,也就是说一个函数可以没有参数和返回值。
Go 函数支持变长参数
在上面的案例中,实现的功能是对两数求和,如果我们需要对多个数求和,但又不知道具体的数量,函数的参数该怎么声明呢?这时可以声明变长参数去接收多个参数。
func main() {
sum := Add(1, 2, 3, 4)
println(sum) // 10
}
func Add(nums ...int) int {
var sum int
for _, num := range nums {
sum += num
}
return sum
}
变长参数作为形式参数可以接收不定的实际参数,声明变长参数需要在类型面前加上 ...
。可变参数实际上是一个切片,可以通过 for-range
去操作。
匿名函数
通常情况下,如果一个函数只使用一次,我们就可以定义成匿名函数。
func main() {
// 定义匿名函数,直接调用
result := func(num1, num2 int) int {
return num1 + num2
}(1, 2)
println(result)
// 2、将匿名函数赋值给一个变量,由变量调用
resultFunc := func(num1, num2 int) int {
return num1 + num2
}
println(resultFunc(1, 2))
}
声明函数时,不指定函数名的函数,叫做匿名函数。匿名函数可以直接调用,也可以通过赋值给变量,由变量调用。
闭包
闭包就是一个函数和其相关引用环境组合的一个整体。
import "fmt"
// 返回值为一个匿名函数
func getSum() func(int) int {
var sum int = 0
// 匿名函数
result := func(num int) int {
sum += num
return sum
}
return result
}
func main() {
f := getSum()
fmt.Println(f(1)) // 1
fmt.Println(f(2)) // 3
}
闭包的本质就是一个匿名函数,匿名函数里使用了定义它的函数(
getSum
)里面的变量sum
,就组成了闭包。由上述代码可知,匿名函数中所引用的变量
sum
会一直保存在内存中。
init 函数
每个 go
文件都可以包含一个 init
函数,它是一个初始化函数,用于进行初始化的操作。
var print = getNum()
func getNum() int {
println("初始化变量")
return 1
}
func main() {
println("main...")
}
func init() {
println("init...")
}
执行结果:
初始化变量
init...
main...
根据执行结果可知,它们的执行顺序为:全局变量 →
init
函数 →main
函数。多个
go
文件都有init
函数时,执行顺序为:先执行所导入的模块的init
函数,再执行本文件的init
函数。
函数参数详解
形式参数与实际参数
在函数声明的地方,参数列表中的参数被称为形式参数,简称形参;而在函数调用的时候所传递的参数被称为实际参数,简称实参。举例说明:
func main() {
sum := Add(1, 2)
println(sum) // 3
}
func Add(num1 int, num2 int) int {
var sum int
sum += num1
sum += num2
return sum
}
Add
后面的参数被称为形参,而在 main
方法里,1
和 2
被称为实参。
值传递
基本数据类型和数组作为实参时,默认是按 值传递,即进行值拷贝,在函数内修改它们的值,原来的值不会改变。举例说明:
func main() {
num1, num2 := 1, 2
Swap(1, 2)
fmt.Printf("main 函数体内打印:num1: %d, num2: %d", num1, num2)
}
func Swap(num1 int, num2 int) {
num2, num1 = num1, num2
fmt.Printf("Swap 函数体内打印:num1: %d, num2: %d\n", num1, num2)
}
执行结果:
Swap 函数体内打印:num1: 2, num2: 1
main 函数体内打印:num1: 1, num2: 2
在 Swap
函数内,num1
和 num2
的值已经相互交换,但是在 main
函数里,num1
和 num2
的值没有发生变化。
func main() {
nums := [3]int{0, 1, 2}
Swap(nums)
fmt.Println("main 函数体内打印:nums: ", nums)
}
func Swap(nums [3]int) {
nums[0] = 1
fmt.Println("Swap 函数体内打印:nums: ", nums)
}
同样传递数组,在函数内修改数组的值,原数组的值并不会改变。 前面的文章有对指针进行了介绍,指出了通过 *
操作符,可以对指针所指向的变量的值进行修改,因此如果我们想要在函数内改变外部传过来的参数的值,函数声明时,形参类型指定为指针类型。
func main() {
num1, num2 := 1, 2
Swap(&num1, &num2)
fmt.Printf("main 函数体内打印:num1: %d, num2: %d", num1, num2)
}
func Swap(num1 *int, num2 *int) {
*num2, *num1 = *num1, *num2
fmt.Printf("Swap 函数体内打印:num1: %d, num2: %d\n", *num1, *num2)
}
执行结果:
Swap 函数体内打印:num1: 2, num2: 1
main 函数体内打印:num1: 2, num2: 1
通过结果可知,使用指针变量作为形参,在函数里是可以改变外部的参数的值的。
函数是一种数据类型
在 Go
里面,函数是一种数据类型,因此函数还可以有很多用法,如创建一个函数变量、将函数作为函数的形参,将函数作为函数的返回值等。
创建函数变量
func main() {
// 创建函数变量
hl := Hello
fmt.Printf("%T\n", hl) // func(string)
hl("cmy")
}
func Hello(name string) {
fmt.Printf("Hello %s", name)
}
创建一个函数变量 hl
,打印 hl
的类型 → func(string)
,通过 hl
变量调用函数。
作为函数的形参
import "fmt"
func main() {
Print("cmy", Hello)
}
func Hello(name string) {
fmt.Printf("Hello %s", name)
}
func Print(name string, f func(string)) {
f(name)
}
定义函数 Print
,声明两个参数,一个参数是 name
,为 string
类型的,另一个参数是 f
,为函数类型。传入 cmy
和 Hello
函数,由 Print
函数去执行 Hello
函数。
作为函数的返回值
import "fmt"
func main() {
f := Print()
f()
}
func Print() func() {
return func() {
fmt.Println("Hello,World!")
}
}
通过Print
函数返回一个匿名函数函数,这个匿名函数的功能是输出 Hello,World!
,使用 f
变量接收这个函数并调用。
基于函数自定义类型
type AddHandleFunc func(num1, num2 int) int
func main() {
sum := GetSum(1, 2, Add)
println(sum)
}
func Add(num1, num2 int) int {
return num1 + num2
}
func GetSum(num1, num2 int, handleFunc AddHandleFunc) int {
return handleFunc(num1, num2)
}
基于函数 func(num1, num2 int) int
自定义一个类型 AddHandleFunc
,将它声明为 GetSum
的形参,然后调用 GetSum
时,因为 Add
函数和 AddHandleFunc
是等价的,因此可以将 Add
当做实参传进去。
小结
本文对函数的声明进行介绍,根据一个案例了解了其组成的5部分。然后介绍了其一些特点如支持变长参数、传递参数时,实参按值传递等,最后根据函数在Go中是一种数据类型的特点,说明了其一些特别用法。
来源:https://juejin.cn/post/7171068063021400100
猜你喜欢
- 发现很多朋友对 CSS 的优先权不甚了解,规则很简单。需要说明的一点,如果你的样式管理需要深层判断 CSS 的优先权,更应反思自己的 CSS
- 用户登录验证脚本,Chkpwd.asp<% '=======用户登录验证脚本======= '
- froglt 的站点:http://www.go2here.net 欢迎转载,请注明出处,未经作者允许,禁止一切商业应用。这是即
- 异步操作数据的方式有两种常见的方式:XMLHttpRequest 和 iframe. 孰优孰劣在此我们不争论,只是想举一个例子说明在获取网片
- 上次在blueidea上看到一个元素圆角的实现方法,但是那个太复杂了。于是就自己写了一个函数,可以将元素自动圆角,如div层,表格等。共有四
- 【基本介绍】【格式】:pivot(聚合函数 for 需要转为列的字段名 in(需要转为列的字段值))【说明】:实现将指定字段的字段值转换为列
- 需要分件html源代码 此例中的被抓取的html源代码如下 <p align=left>2004年8月24日星期二;白天:晴有时
- Web 前端优化最佳实践第三部分面向 Cookie 。目前只有 2 条实践规则。1. 缩小 Cookie (Reduce Cook
- 说实话,对于移除这个旧有功能对于我来说,我是欢心鼓舞的。因为我在开发和应用当中一向不用expression来处理,虽然它确实是非常方便,比如
- <% '#######以下是一个类文件,下面的注解是调用类的方法####################
- flash_url : "../swfupload/swfupload_f8.swf" upload_url: &quo
- 工作中,网页设计师经常会遇见这些状况:时间这么短又要出彩、又是要大气要有气氛、风格不明确很难把握、栏目这么多页面又这么长……突然觉得束手无策
- 如何剔除HTML标识?这里没有使用正则表达式来去除,当然大家用正则"<.+?>"也可以实现这个功能只留下干净
- 引言今年互联网的就业环境真的好糟糕啊,好多朋友被优化。我们平常在工作中除了撸好代码,跑通项目之外,还要注意内外兼修。内功和招式都得练👌,才能
- 本来在网上有不少关于这方面的文章,可是我找了好久也没看到把(可能我的搜索水平有线把)不过倒是聊天室的很多。如何统计会员再线状态,希望对刚开始
- 在PyCharm2017中同目录下import其他模块,会出现No model named ...的报错,但实际可以运行这是因为PyChar
- 1、灵活运用样式熟悉网页设计的网友就知道,调用Style的方法很多,我们可以单击鼠标右键选择Custo
- 又一个js加密工具:js混淆,完整源代码如下,有点长呵呵:<HTML><HEAD><TITLE>Cunf
- 有效地加载数据有时我们需大量地把数据加载到数据表,采用批量加载的方式比一个一个记录加载效率高,因为MySQL不用每加载一条记录就刷新一次索引
- 从MySQL 5.0.2开始,通过mysql_stmt_attr_set() C API函数实现了服务器端光标。服务器端光标允许在服务器端生