函数
golang函数简介
函数是go语言中的一级公民,我们把所有的功能单元都定义在函数中,可以重复使用。函数包含函数的名称,参数列表和返回值类型,这些构成了函数的签名。
golang中函数的特性
- go语言中有3种函数:普通函数,匿名函数,方法(定义在struct上的函数)。
- go语言中不允许函数重载(overload),也就是说不允许函数同名。
- go语言中的函数不能嵌套函数,但可以嵌套匿名函数。
- 函数是一个值,可以将函数赋值给变量,使得这个变量也成为函数。
- 函数可以作为参数传递给另一个函数。
- 函数的返回值可以是一个函数。
- 函数调用的时候,如果有参数传递给函数,则先拷贝参数的副本,再将副本传递给函数。(值传递)
- 函数参数可以没有名称。
golang中函数的定义和调用
函数在使用之前必须先定义,可以调用函数来完成某个任务。函数可以重复使用,从而达到代码重用。
语法
func function_name([parameter list])(return_types){
//函数体
}
- func:声明函数
- function_name:函数名
- [parameter list]:参数列表,参数就像一个占位符,当函数被调用时,你可以将值传递给函数,这个值被称为实际参数。参数列表指定的是参数类型,顺序及参数个数。参数是可选的,也就是说函数也可以不包含参数。
- return_types:函数返回值的类型。
- 函数体:函数的逻辑代码部分
//定义一个两数之和
func sum(a int,b int)(res int){
res=a+b
return res
}
golang函数的返回值
函数可以有0个或多个返回值,返回值需要指定数据类型,返回值通过return关键字来指定。
return可以有参数,也可以没有参数,这些返回值可以有名称,也可以没有名称。go中的函数可以有多个返回值。
- return关键字中指定了参数时,返回值可以不用名称。如果return省略参数,则返回值部分必须带名称。
- 当返回值有名称时,必须使用括号包围,逗号分隔,即使只有一个返回值。
- 但即使返回值命名了,return中也可以强制指定其他返回值名称,也就是说return的优先级更高。
- 命名的返回值是预先声明好的,在函数内部可以直接使用,无需再次声明。命名返回值的名称不能和函数参数名称相同,否则报错提示变量重复声明。
- return中可以有表达式,但不能出现赋值表达式,这其他语言可能有所不同。例如可以:return a+b,但是不能:return a=b+c
package main
import "fmt"
//没有返回值
func foo1() {
print("没有参数,也没有返回值!\n")
}
//有参数,没有返回值
func foo2(name string) {
print("有一个参数:")
fmt.Printf("name: %v\n", name)
}
//有参数,有返回值 且给返回值命名了
func foo3(name string, age int) (name2 string, age2 int) {
name2 = name
age2 = age
return name2, age2
// return 如果return不指定返回值,则默认返回上面定义的返回值
}
// 有参数,有返回值,但没有给返回值命名 这个时候就必须需要return来指定返回值
func foo4(name string, age int) (string, int) {
return name, age
}
func main() {
foo1()
foo2("Tom")
name2, age2 := foo3("Tom", 18)
fmt.Printf("name2: %v\n", name2)
fmt.Printf("age2: %v\n", age2)
name, _ := foo4("Tom", 18) //丢弃age
fmt.Printf("name: %v\n", name)
}
运行结果
没有参数,也没有返回值!
有一个参数:name: Tom
name2: Tom
age2: 18
name: Tom
tips:
- go中经常会使用其中一个返回值作为函数是否执行成功,是否有错误信息的判断条件。例如return value,exists;return value,ok(map中就是这样使用的);return value,err等
- 当函数的返回值过多时,例如有4个以上的返回值,应该将这些返回值收集到容器中,然后以返回容器的方式去返回。
- 当函数有多个返回值时,如果其中某个或几个返回值不想使用,可以通过下划线_来丢弃这些返回值。
golang函数的参数
go语言函数可以有0个或多个参数,参数需要指定数据类型。
声明函数时的参数列表叫做形参,调用时传递的参数叫做实参。
go语言是通过传值的方式传参的,意味着传递给函数的是拷贝后的副本,所以函数内部访问,修改的也是这个副本。(但是map,slice,interface,channel这些数据类型本身就是指针类型,所以就算是拷贝传值也是拷贝的指针,拷贝后的参数任然指向底层数据结构,所以修改它们可能会影响外部数据结构的值。)
go语言可以使用变长参数,有时候不能确定参数的个数时,可以使用变长参数,可以在函数定义语句的参数部分使用args…type的方式。这时会将…代表的参数全部保存到一个名为args的slice中,并且这些参数的数据类型都是type。
package main
import "fmt"
func test(a int) {
a = 200
fmt.Printf("里面的a: %v\n", a)
}
//两数之和
func sum(a int, b int) (c int) {
c = a + b
return
}
//...接收无数个参数
func foo(name string, age int, args ...string) {
fmt.Printf("name: %v\n", name)
fmt.Printf("age: %v\n", age)
for _, v := range args {
fmt.Printf("v: %v\n", v)
}
}
func main() {
a := 100
test(a)
fmt.Printf("外面的a=%v\n", a)
res := sum(1, 2)
fmt.Printf("res: %v\n", res)
foo("Tom", 18, "Hello", "My", "name", "is", "Tom")
}
运行结果
里面的a: 200
外面的a=100
res: 3
name: Tom
age: 18
v: Hello
v: My
v: name
v: is
v: Tom
golang函数类型与函数变量
可以使用type关键字来定义一个函数类型,语法如下:
type fun func(par_type1,par_type2...) res_type1...
- fun:自己定义的函数类型名
- par_type1,par_type2…:表示各个参数的类型
- res_type1…:表示各个返回值的类型
package main
import "fmt"
func sum(a int, b int) (c int) {
c = a + b
return
}
func main() {
//自己声明函数的类型,然后再将函数赋值给一个变量
type my_func func(int, int) int
var my_sum my_func
my_sum = sum
res := my_sum(1, 2)
fmt.Printf("res: %v\n", res)
//直接通过短变量的形式将一个函数赋值给一个变量
c := sum
res2 := c(1, 2)
fmt.Printf("res2: %v\n", res2)
}
运行结果
res: 3
res2: 3
高阶函数
go语言的函数,可以作为函数的参数,传递给另外一个函数,同时也可以作为函数的返回值返回。
函数作为参数和返回值
package main
import "fmt"
func sum(a int, b int) (c int) {
c = a + b
return
}
func sub(a int, b int) (c int) {
c = a - b
return
}
func test(a int, b int, f func(int, int) int) {
res := f(a, b)
fmt.Printf("%v+%v=res: %v\n", a, b, res)
}
func cal(flag string) func(int, int) int {
switch flag {
case "+":
return sum
case "-":
return sub
default:
return sum
}
}
func main() {
//函数作为参数传给函数
test(10, 20, sum)
//函数作为返回值
f_sum := cal("+")
res := f_sum(1, 2)
fmt.Printf("res: %v\n", res)
f_sub := cal("-")
res2 := f_sub(1, 2)
fmt.Printf("res2: %v\n", res2)
}
运行结果
10+20=res: 30
res: 3
res2: -1
匿名函数
go语言函数不能嵌套,但是在函数内部可以定义匿名函数,实现一些简单的功能。所谓匿名函数,就是没有名称的函数。
语法
func ([参数列表])([返回值列表]){
//函数体
}
package main
import "fmt"
func main() {
//将匿名函数赋值给一个变量
say_hello := func(name string, age int) {
fmt.Printf("My name is %v,And i',m %v old.\n", name, age)
}
say_hello("Tom", 18)
//直接调用匿名函数
func(words string) {
fmt.Printf("words: %v\n", words)
}("Hi,我是一个匿名函数!")
}
运行结果
My name is Tom,And i',m 18 old.
words: Hi,我是一个匿名函数!
闭包
闭包就是延伸了函数作用于的函数,使得函数可以访问到函数体外的非全局变量。
package main
import "fmt"
//求平均值的闭包
func make_avger() func(int) float64 {
s := make([]int, 0)
//对于下面的匿名函数来说,切片s就相当于函数体外的非全局变量
//并且调用make_avager方法后,在全局中是访问不到切片s的
return func(num int) float64 {
s = append(s, num)
sum := 0
for _, v := range s {
sum += v
}
return float64(sum / len(s))
}
}
func main() {
avg := make_avger()
for i := 0; i < 10; i++ {
fmt.Printf("%.2f\n", avg(i))
}
}
运行结果
0.00
0.00
1.00
1.00
2.00
2.00
3.00
3.00
4.00
4.00
递归
函数内部调用函数自己的函数称为递归函数(同闭包一样,递归不是go语言特有的,而是go语言具备实现递归的条件)。
package main
//使用递归实现斐波那契数列
func fib(n int) int {
if n == 1 || n == 2 {
return 1
} else {
return fib(n-1) + fib(n-2)
}
}
func main() {
res := fib(10)
print(res)
}
运行结果
102334155
同步更新于个人博客系统:golang学习笔记系列之函数