函数
同一段代码要在好几个地方用,就把它包成一个函数,起个名字,用的时候叫它的名字。main 就是一个函数;下面再写一个:
1package main2 3import "fmt"4 5func greet(name string) {6 fmt.Println("你好," + name)7}8 9func main() {10 greet("小林")11 greet("小陈")12}
你好,小林 你好,小陈
func greet(name string) 定义了一个叫 greet 的函数。name 是参数,类型写在名字后面:name string。greet("小林") 是调用它,"小林" 是实参,进了函数就是 name。函数写在 main 前面后面都行。
返回值
函数可以算出一个值交回去:在括号后面写上返回值的类型,用 return 把值交回给调用它的地方:
1package main2 3import "fmt"4 5func total(price int, count int) int {6 return price * count7}8 9func main() {10 fmt.Println(total(25, 4))11 sum := total(12, 3) + total(8, 5)12 fmt.Println(sum)13}
100 76
total(25, 4) 这个调用本身就是一个值,可以显示,可以参加计算,也可以放进变量。几个参数类型一样时可以合着写:func total(price, count int) int。
少了 return
说了要返回 string,就每条路都要有 return。Go 编译器会检查:
1package main2 3import "fmt"4 5func grade(score int) string {6 if score >= 60 {7 return "及格"8 }9}10 11func main() {12 fmt.Println(grade(72))13}
# command-line-arguments
./prog.go:9:1: missing return发生了什么: 函数说好要返回值,可是有一条路走到最后也没有 return。
./prog.go:9:1: missing return 指着第 9 行,grade 结尾的那个 }:分数不到 60 的时候,函数走到这里还什么都没返回。在 } 前面加一行 return "补考" 就好了。
多个返回值
Go 的函数可以一次返回好几个值。返回值的类型用括号括起来,return 后面用逗号隔开,接的时候也用逗号:
1package main2 3import "fmt"4 5func divmod(a, b int) (int, int) {6 return a / b, a % b7}8 9func main() {10 q, r := divmod(7, 3)11 fmt.Println(q, r)12 hours, minutes := divmod(135, 60)13 fmt.Println(hours, "小时", minutes, "分钟")14}
2 1 2 小时 15 分钟
divmod 同时给出商和余数:135 分钟是 2 小时 15 分钟。
用 _ 丢掉不要的
有时候只想要其中一个值。可是两个都接住、只用一个,就碰上了老问题:
1package main2 3import "fmt"4 5func divmod(a, b int) (int, int) {6 return a / b, a % b7}8 9func main() {10 hours, minutes := divmod(135, 60)11 fmt.Println(minutes, "分钟")12}
# command-line-arguments
./prog.go:10:2: declared and not used: hours发生了什么: 变量 hours 声明了却没有用到。Go 不允许这样:用上它,或者删掉它(不想要的值可以给 _)。
hours 声明了没用,编译不过。不要的那个写成 _,它是个「扔掉」的位置,不算变量:
1package main2 3import "fmt"4 5func split(total, people int) (each, left int) {6 each = total / people7 left = total % people8 return9}10 11func main() {12 each, left := split(100, 3)13 fmt.Println("每人", each, "剩", left)14 _, left = split(50, 4)15 fmt.Println("剩", left)16}
每人 33 剩 1 剩 2
split 还用了命名返回值:(each, left int) 给两个返回值起了名字,它们一开始是零值,在函数里像普通变量一样赋值,最后一个光秃秃的 return 就把它们交回去。短小的函数里偶尔这样写,平时还是 return 值 更清楚。
函数拿到的是一份复制
这段程序显示什么?
1package main2 3import "fmt"4 5func double(n int) {6 n = n * 27}8 9func main() {10 price := 3011 double(price)12 fmt.Println(price)13}
调用 double(price) 时,n 拿到的是 price 的值复制出来的一份。函数里改的是 n 这份复制,main 里的 price 还是 30。
Go 调用函数时把实参的值复制一份给参数,这叫传值。点单步,看右边:进了 addBonus,变量面板上多出一层,有它自己的 score;两个 score 名字一样,却是两个格子。
1package main2 3import "fmt"4 5func addBonus(score int) {6 score = score + 107 fmt.Println("函数里", score)8}9 10func main() {11 score := 8012 addBonus(score)13 fmt.Println("main 里", score)14}
函数里 90 main 里 80
函数回来以后,它那一层就没有了。想让 main 拿到新的值,就让函数把它 return 回来,再赋值:score = addBonus(score)(这样 addBonus 就要写成返回 int:func addBonus(score int) int,最后 return score)。(还有一种办法是指针,第 11 课讲。)
可变参数
fmt.Println 可以收一个、两个、很多个值。自己的函数也可以:最后一个参数的类型前面写 ...,调用时给几个都行,一个不给也行:
1package main2 3import "fmt"4 5func sum(nums ...int) int {6 total := 07 for _, n := range nums {8 total += n9 }10 return total11}12 13func main() {14 fmt.Println(sum(12, 30, 8))15 fmt.Println(sum(99))16 fmt.Println(sum())17}
50 99 0
在函数里,nums 是装着这些值的一个切片(第 6 课细讲)。for _, n := range nums 把它们一个一个拿出来放进 n,前面的 _ 丢掉的是位置编号,第 6 课也会讲到。
练一练
写好代码,点检查。提示随便看,实在想不出来再看答案。
打折以后多少钱
✓写好 discount:原价 price 打掉 percent%,返回打折以后的价(整数)。要显示 170 和 49。
170 49
1package main2 3import "fmt"4 5func discount(price, percent int) int {6 // 在这里算7 return 0 // 先占个位,换成你算出来的价8}9 10func main() {11 fmt.Println(discount(200, 15))12 fmt.Println(discount(99, 50))13}
三个温度里最低和最高
✓写好 minMax,同时返回三个数里最小的和最大的。要显示 最低 -3 最高 8 和 最低 15 最高 27。
最低 -3 最高 8 最低 15 最高 27
1package main2 3import "fmt"4 5func minMax(a, b, c int) (int, int) {6 return 0, 07}8 9func main() {10 low, high := minMax(5, -3, 8)11 fmt.Println("最低", low, "最高", high)12 low, high = minMax(27, 15, 22)13 fmt.Println("最低", low, "最高", high)14}
修好它:加了税,价格没变
✓addTax 想把价格加上 10% 的税,可是 main 里的 price 一直没变。改好它,显示 含税 220 和 含税 60。
含税 220 含税 60
1package main2 3import "fmt"4 5func addTax(price int) {6 price = price * 110 / 1007}8 9func main() {10 price := 20011 addTax(price)12 fmt.Println("含税", price)13 price = 5514 addTax(price)15 fmt.Println("含税", price)16}
这一课学会了
func 名字(参数 类型) 返回类型 { … }:类型写在名字后面;类型一样的参数可以合着写a, b int。return把值交回去;说了有返回值,每条路都要return,不然是missing return。- 多个返回值:
func divmod(a, b int) (int, int),接的时候q, r := divmod(7, 3);不要的写_。 - 命名返回值
(each, left int)先是零值,光写return就交回它们。 - 传值:参数是实参的一份复制,函数里改它不影响调用的地方;要新值就
return回来。 - 可变参数
nums ...int能收任意多个值,在函数里是一个切片。