函数就是盛放一组代码块的”容器”,把实现某个特定功能一组代码块组织到一个函数内,相当于制作了一个工具,可以重复调用
不同于Python,在go语言中,非声明语句只能在函数内,比如fmt.Println("啊p啊")写到函数外则会报语法错误。
| syntax error: non-declaration statement outside function body |
也就是说go强制我们把功能代码组织到函数内,而使用函数至少解决了以下3种问题
(1)代码的组织结构不清晰,可读性差
(2)遇到重复的功能只能重复编写实现代码,代码冗余
(3)功能需要扩展时,需要找出所有实现该功能的地方修改之,无法统一管理且维护难度极大
| #1、内置函数 |
| 为了方便我们的开发,针对一些简单的功能,go已经为我们定义好了的函数即内置函数。对于内置函数,我们可以拿来就用而无需事先定义,例如len、cap、append、make等 |
| |
| #2、自定义函数 |
| 很明显内置函数所能提供的功能是有限的,这就需要我们自己根据需求,事先定制好我们自己的函数来实现某种功能,以后,在遇到应用场景时,调用自定义的函数即可。 |
| 此外,我们早已知晓,go程序中必须自定义一个main函数,main函数是整个程序的入口 |
Go语言中还支持匿名函数和闭包,并且函数在Go语言中属于“一等公民”。
语法
| func 函数名(参数1 类型,参数2 类型,...)(返回参数1 类型,返回参数2 类型,...){ |
| 函数体 |
| } |
| |
| 解释: |
| |
| - 1、函数名:函数名要能反映其意义,命名规范同变量名,同一个包内不能重复 |
| - 2、参数与返回值都是可选的 |
| - 2.1 参数由参数名和类型组成,可以有多个,用逗号分隔开即可 |
| - 2.2 返回值由参数名和类型组成,可以只写返回值的类型,也可以有多个,但在定义函数时,如果有多个返回值,则必须用逗号分隔开并且用()包裹 |
| - 3、函数体:具体的功能实现 |
定义函数的三种形式
1、无参
| func test() { |
| fmt.Println("老妹真白") |
| } |
2、有参
| |
| func add(x int, y int) { |
| fmt.Println(x + y) |
| } |
| |
| |
| func add(x int,y int) int{ |
| return x + y |
| } |
3、空函数
| |
| func sayHi(name string) { |
| } |
注意:在查看源码时,我们偶尔会遇到不仅没有函数体、甚至连花括号都没有的函数声明,这表示该函数不是以Go实现的,如下
| package math |
| |
| func Sin(x float64) float // 采用汇编语实现 |
调用函数语法如下
因为在go中非声明语句必须在函数内,所以函数调用这种非声明语句必须在某一函数内,具体来说,调用函数也有三种形式
1、语句形式:test()
| |
| func test() { |
| fmt.Println("老妹真白") |
| } |
| |
| |
| func main() { |
| test() |
| } |
2、表达式形式:3*add(1,2),本质是在利用函数的返回值
| |
| func add(x int, y int) int { |
| return x + y |
| } |
| |
| |
| func main() { |
| res := 3 * add(1, 2) |
| fmt.Println(res) |
| } |
3、当做参数传给另外一个函数,本质也是在利用函数的返回值
| |
| func add(x int, y int) int { |
| return x + y |
| } |
| |
| |
| func main() { |
| res := 3 * add(1, add(2,3)) |
| fmt.Println(res) |
| } |
强调:在调用函数时,小括号换行需在末尾加逗号
与大括号{}一样,调用函数的小括弧()也可以另起一行缩进,但必须在末尾显式加逗号,这是Go编译器的一个特性,用于防止编译器在行尾自动插入分号而导致的编译错误
| package main |
| |
| import "fmt" |
| |
| func main() { |
| var x, y = 1, 2 |
| |
| |
| fmt.Println(x, |
| y |
| ) |
| |
| fmt.Println(x, |
| y, |
| ) |
| } |
参数分为形参与实参,实参指的是调用函数时传入的值(相当于变量值),形参指的是定义函数是指定参数(相当于变量名),调用函数时,会将实参值赋值给形参
强调:
1、每一次函数调用都必须按照声明顺序为所有形参数提供实参(参数值)。
2、在函数调用时,Go语言没有默认参数值,也没有任何方法可以通过参数名指定形参,因此形参和返回值的变量名对于函数调用者而言没有意义。
形参中若相邻变量的类型相同,则可以省略类型
| func add(x,y int) int{ |
| return x + y |
| } |
可变长指的就是调用函数时,传入的实参值个数不固定,我们可以在形参名or其类型前加…,该形参就可以用来接收溢出的实参值,并把它们存成一个切片,称之为可变长参数,本质上,函数的可变参数是通过切片来实现的。
例
| func add(x ...int) int { |
| fmt.Println(x) |
| sum := 0 |
| for _, v := range x { |
| sum += v |
| } |
| return sum |
| } |
调用
| func main() { |
| res1 := add(1, 2, 3) |
| res2 := add(1, 2, 3, 4, 5) |
| res3 := add(1, 2, 3, 4, 5, 6) |
| fmt.Println(res1, res2, res3) |
| |
| } |
注意:固定参数搭配可变参数使用时,可变参数要放在固定参数的后面
| func add(x int,y ...int) int { |
| fmt.Println(x,y) |
| sum := 0 |
| for _, v := range y { |
| sum += v |
| } |
| return sum |
| } |
返回值是函数的运行结果,函数体一旦执行到return就会立即结束并且返回值
强调:
如果一个函数在声明时,包含返回值列表,该函数必须以 return语句结尾,除非函数明显无法运行到结尾处。例如函数在结尾时调用了panic异常或函数中存在无限循环。
Go语言中函数支持多返回值,函数如果需要有多个返回值,我们在定义函数的返回值时,必须用()
将所有返回值包裹起来。
调用多返回值函数时,返回给调用者的是一组值,调用者必须显式的将这些值分配给变量:
| func divmod(x, y int) (int, int, int) { |
| res1 := x / y |
| res2 := x % y |
| res3 := x + y |
| return res1, res2, res3 |
| } |
| |
| func main() { |
| m, n, _ := divmod(1, 2) |
| fmt.Println(m, n) |
| } |
返回值也可以像形参一样被命名,有名返回值与函数的形参会被当作函数最外层的局部变量,
| func add(x int, y int) (z int) { |
| |
| |
| |
| |
| } |
例1
| |
| func divmod(x, y int) (res1,res2 int) { |
| fmt.Println(res1,res2) |
| return |
| } |
| func main() { |
| divmod(1,2) |
| } |
例2:
| func divmod(x, y int) (res1,res2 int) { |
| res1 = x / y |
| res2 = x % y |
| return |
| } |
| |
| func main() { |
| fmt.Println(divmod(10,3)) |
| } |
若函数返回值类型为slice,想要返回空切片,直接返回nil即可,没必要显示返回一个长度为0的切片,也就是说nil可以看做是一个有效的slice。
| func someFunc(x string) []int { |
| if x == "" { |
| return nil |
| } |
| ... |
| } |
了解:
许多标准库中的函数返回2个值,一个是期望得到的函数处理结果,另一个是函数出错时的错误信息
| func findLinks() ([]int, error) { |
| return 正确结果,nil |
| |
| } |
- 分金币
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| var ( |
| coins = 50 |
| users = []string{ |
| "Matthew", "Sarah", "Augustus", "Heidi", "Emilie", "Peter", "Giana", "Adriano", "Aaron", "Elizabeth", |
| } |
| distribution = make(map[string]int, len(users)) |
| ) |
| |
| func main() { |
| left := dispatchCoin() |
| fmt.Println("剩下:", left) |
| } |
答案
| package main |
| |
| import "fmt" |
| |
| var ( |
| coins = 50 |
| users = []string{ |
| "Matthew", "Sarah", "Augustus", "Heidi", "Emilie", "Peter", "Giana", "Adriano", "Aaron", "Elizabeth", |
| } |
| distribution = make(map[string]int, len(users)) |
| ) |
| |
| func dispatchCoin() int { |
| for _, name := range users { |
| for _, c := range name { |
| switch c { |
| case 'e', 'E': |
| distribution[name]++ |
| coins-- |
| case 'i', 'I': |
| distribution[name] += 2 |
| coins -= 2 |
| case 'o', 'O': |
| distribution[name] += 3 |
| coins -= 3 |
| case 'u', 'U': |
| distribution[name] += 4 |
| coins -= 4 |
| } |
| } |
| } |
| return coins |
| } |
| func main() { |
| left := dispatchCoin() |
| fmt.Println("剩下:", left) |
| fmt.Println(distribution) |
| |
| } |