您现在的位置是:首页 >其他 >go破冰之旅·8·go函数基本实践及各种玩法网站首页其他

go破冰之旅·8·go函数基本实践及各种玩法

ProblemTerminator 2023-06-27 00:00:04
简介go破冰之旅·8·go函数基本实践及各种玩法

一次5-10分钟即可搞定,实用+效率!

以干货+效率的学习方式带你更直观的玩转各种玩法!

目录

什么是函数?有哪些元素?

函数参数、返回值

小程序:函数与函数调用,各种玩法举一反三

函数参数与值传递说明


什么是函数?有哪些元素?

函数是一个基本的代码块,可以让每个函数代表一个基本职能,或者说定义要做的功能或任务;

一个完整的go程序中至少有个main函数,单个函数可声明函数名称、参数列表、返回值列表,定义函数需要用func关键字。

同时,go中的函数也是一个变量,可以在函数内继续定义函数。

其中init和main这两个内置函数的作用功能在前面讲解包时已充分说明,这里不再赘述哦!

函数参数、返回值

下面我们快速看一下函数的组成和表示:

// 示例1
func funcName(a int, b string) (string, int) {
    // 函数体
    return "",0
}

【说明】
函数名称:funcName
参数列表:两个参数,分别是a、b,类型分别为int、string
返回值列表:两个返回值,类型分别为string、int,且没有定义返回值名称


// 示例2
func funcName(a,b int) (sum int) {
    // 函数体
    return
}

【说明】
函数名称:funcName
参数列表:两个参数,分别是a、b,类型都为int类型
返回值列表:单个返回值,定义为sum变量,为int类型


函数的参数用来做输入条件,返回值用来返回结果。

以一个常见的过程说明该模型:你要计算a+b的和,假设a+b=c,那我们这个函数要做的就是计算两数之和,返回的结果就是计算所得的和,而函数的参数就是a和b,也就是输入的条件,得告诉函数要以什么为条件让它去执行。当然。参数列表和返回值列表也都可以不设置。

小程序:函数与函数调用,各种玩法举一反三

下面写一个简单程序来认识一下函数到底怎么定义和使用。

程序完成求和、求积两个职能,下面我们根据实际含义定义两个函数:

// 两数之和
func add(a, b int) int {
	return a + b
}

// 两数之积
func multiply(a, b int) int {
	return a * b
}

在main中调用并打印结果:

import "fmt"

func main() {

	var a, b = 1, 2

	// 求a+b的和
	r1 := add(a, b)
	fmt.Printf("a+b= %v
", r1)

	// 求a*b的积
	r2 := multiply(a, b)
	fmt.Printf("a*b= %v
", r2)
}

这样就完成了函数调用和返回值的接收,结果:

a+b= 3
a*b= 2

另外,如果add函数我不想定义在外面,只想在本程序中唯一使用,也可以这么玩:

func main() {

	var a, b = 1, 2
	add0 := func(a, b int) int {
		return a + b
	}

	// 求a+b的和
	r1 := add0(a, b)
	fmt.Printf("a+b= %v
", r1)

	// 求a*b的积
	r2 := multiply(a, b)
	fmt.Printf("a*b= %v
", r2)
}

// 两数之积
func multiply(a, b int) int {
	return a * b
}

可以看到在要求和的上方直接定义了一个函数add0,并且在接下来完成了调用,结果效果和上面完全一致。

当然,你觉得函数只有在这一个地方用到,为什么还要写函数名,让我直接调用岂不更简单?当然可以,咱们这么玩:

	var a, b = 1, 2

	// 求a+b的和
	r1 := func(a, b int) int {
		return a + b
	}(a, b)
	fmt.Printf("a+b= %v
", r1)

r1就是对a、b求和的结果,效果也是完全一样,注意这个示例中func后的a、b也是函数参数,实际输入的1和2这两个参数是通过函数后的小括号传进去的,这个就是实际参数,而func后的a、b是形式参数,也叫形参。

同时在这个案例中,该func就是一个匿名函数,即没有名称的函数。

函数参数与值传递说明

值得注意的是,go中主要是值传递,并没有引用传递,所谓值传递就是形式参数是对应值的一个副本,传的并不是值本身。

假设我们有这样一个需求,函数updateNum修改输入参数n的值,将值恒定改为1,分为两种,返回修改的结果、不返回修改的结果:


func updateNum (n int) int{
	n = 1
    return n  // 实际这里直接return 1就行了,但为了看起来更明白因此多此一举加了n = 1
}

func updateNum (n int){
	n = 1
}

要说明的是,返回结果这种情况只要调用方去接收函数的返回值,则接收到的必定是1,因此没有悬念,这种严格说算不上修改返回值,而是直接返回了1,下面重点看看第二种:

	n := 10
	fmt.Printf("n=%d
", n)
	updateNum(n)
	fmt.Printf("n=%d
", n)

上面提到,go中默认是值传递,因此传入updateNum的参数为n的副本,函数只是传入了10而已,n本身还是n,函数内部所做的n=1实际修改的是函数的形参的值,外界的n不受影响,因此修改前后打印的n结果都是10。

那怎样才能让这个函数成功的修改了n呢?答案就是指针,给函数参数传递n的地址即可:

func main() {
	n := 10
	fmt.Printf("n=%d
", n)
	updateNum(&n)
	fmt.Printf("n=%d
", n)
}

func updateNum(n *int) {
	*n = 1
}

这样修改为什么能成功,原因就是传入了n的地址,通过地址修改了变量内存地址对应的值,修改后外界会一同修改,因此在调用结束函数后再次打印,n就变成了1。这实际就是其它语言中听到的引用传递。

在后续熟悉了其它引用类型后,这会变得更加有趣!

到这里,相信你对函数的使用和玩法也有了一个清晰的认识啦。

在后续熟悉了引用类型及指针结合并逐渐加深后也可以阅读这篇(不包含在本文范畴内,目前阶段暂不必须):

关于go语言指针/指针指向的全面分析

来进一步加强这方面的全面理解。

风语者!平时喜欢研究各种技术,目前在从事后端开发工作,热爱生活、热爱工作。