1、函数介绍

函数是基本的代码块,每一个程序都包含很多的函数。
编写多个函数的主要目的是将一个需要很多行代码的复杂问题分解为一系列简单的任务(函数)来解决。而且,同一个任务(函数)可以被调用多次,有助于代码重用。
当函数执行到代码块最后一行(} 之前)或者 return 语句的时候会退出,其中 return 语句可以带有零个或多个参数;这些参数将作为返回值供调用者使用。
Go 里面有三种类型的函数:

  1. - 普通函数
  2. - 匿名函数
  3. - 方法(Methods

2、普通函数

函数能够接收参数供自己使用,也可以返回零个或多个值。
在函数块里面,return 之后的语句都不会执行。如果一个函数需要返回值,那么这个函数里面的每一个代码分支都要有 return 语句。

  1. func Demo1(a int) bool{
  2. if a>0{
  3. return true
  4. }
  5. return false
  6. }
  7. func Demo2(a int) bool, string{
  8. if a>0{
  9. return true, 'a>0'
  10. }
  11. return false, 'a<=0'
  12. }
  13. func main() {
  14. res1 := Demo1(1)
  15. fmt.Println("res1 = ", res1)
  16. res2, msg2 := Demo2(-1)
  17. fmt.Println("res2 = ", res2)
  18. fmt.Println("msg2 = ", msg2)
  19. // 空白符"_"用来匹配一些不需要的值,然后丢弃掉
  20. res3, _ := Demo2(0)
  21. fmt.Println("res3 = ", res3)
  22. }

2.1、变参函数

如果函数的最后一个参数是采用 ...type 的形式,那么这个函数就可以处理一个变长的参数,这个长度可以为 0,这样的函数称为变参函数。

  1. func Greeting(prefix string, whos ...string) {
  2. if len(a)>0 {
  3. for _, who := range whos {
  4. fmt.Println(prefix, who)
  5. }
  6. }
  7. }
  8. func main() {
  9. Greeting("hello:", "Joe", "Anna", "Eileen")
  10. }

2.2、defer关键字

关键字 defer 允许我们推迟到当前函数返回之前(或任意位置执行 return 语句之后)一刻才执行某个语句或函数。用法类似于面向对象编程语言 Java 和 C# 的 finally 语句块,它一般用于释放某些已分配的资源。
当有多个 defer 行为被注册时,它们会以逆序执行(类似栈,即后进先出):

  1. func trace(s string) { fmt.Println("entering:", s) }
  2. func untrace(s string) { fmt.Println("leaving:", s) }
  3. func a() {
  4. trace("a")
  5. defer untrace("a")
  6. fmt.Println("in a")
  7. }
  8. func b() {
  9. trace("b")
  10. defer untrace("b")
  11. fmt.Println("in b")
  12. a()
  13. }
  14. func main() {
  15. b()
  16. }
  17. //输出
  18. entering: b
  19. in b
  20. entering: a
  21. in a
  22. leaving: a
  23. leaving: b

2.3、Go内置函数

Go 语言拥有一些不需要进行导入操作就可以使用的内置函数。

名称 说明
close 用于管道通信
len、cap len 用于返回某个类型的长度或数量(字符串、数组、切片、map 和管道);cap 是容量的意思,用于返回某个类型的最大容量(只能用于切片和 map)
new、make new 和 make 均是用于分配内存:new 用于值类型和用户定义的类型,如自定义结构,make 用于内置引用类型(切片、map 和管道)。它们的用法就像是函数,但是将类型作为参数:new(type)、make(type)。new(T) 分配类型 T 的零值并返回其地址,也就是指向类型 T 的指针。它也可以被用于基本类型:v := new(int)。make(T) 返回类型 T 的初始化之后的值,因此它比 new 进行更多的工作,new() 是一个函数,不要忘记它的括号
copy、append 用于复制和连接切片
panic、recover 两者均用于错误处理机制
print、println 底层打印函数,在部署环境中建议使用 fmt 包
complex、real imag 用于创建和操作复数

2.4、递归函数

当一个函数在其函数体内调用自身,则称之为递归。

  1. func main() {
  2. result := 0
  3. for i := 0; i <= 10; i++ {
  4. result = fibonacci(i)
  5. fmt.Printf("fibonacci(%d) is: %d\n", i, result)
  6. }
  7. }
  8. func fibonacci(n int) (res int) {
  9. if n <= 1 {
  10. res = 1
  11. } else {
  12. res = fibonacci(n-1) + fibonacci(n-2)
  13. }
  14. return
  15. }

2.5、回调函数

函数可以作为其它函数的参数进行传递,然后在其它函数内调用执行,一般称之为回调。

  1. func main() {
  2. callback(1, Add)
  3. }
  4. func Add(a, b int) {
  5. fmt.Printf("The sum of %d and %d is: %d\n", a, b, a+b)
  6. }
  7. func callback(y int, f func(int, int)) {
  8. f(y, 2) // this becomes Add(1, 2)
  9. }

3、匿名函数

当我们不希望给函数起名字的时候,可以使用匿名函数,例如:func(x, y int) int { return x + y }
这样的一个函数不能够独立存在(编译器会返回错误:non-declaration statement outside function body),但可以被赋值于某个变量,即保存函数的地址到变量中:fplus := func(x, y int) int { return x + y },然后通过变量名对函数进行调用:fplus(3,4)
当然,您也可以直接对匿名函数进行调用:func(x, y int) int { return x + y } (3, 4)

  1. func() {
  2. sum := 0
  3. for i := 1; i <= 1e6; i++ {
  4. sum += i
  5. }
  6. }()
  7. func f() {
  8. for i := 0; i < 4; i++ {
  9. g := func(i int) { fmt.Printf("%d ", i) } //此例子中只是为了演示匿名函数可分配不同的内存地址,在现实开发中,不应该把该部分信息放置到循环中。
  10. g(i)
  11. fmt.Printf(" - g is of type %T and has value %v\n", g, g)
  12. }
  13. }

3.1、闭包函数

  1. func main() {
  2. // make an Add2 function, give it a name p2, and call it:
  3. p2 := Add2()
  4. fmt.Printf("Call Add2 for 3 gives: %v\n", p2(3))
  5. // make a special Adder function, a gets value 3:
  6. TwoAdder := Adder(2)
  7. fmt.Printf("The result is: %v\n", TwoAdder(3))
  8. }
  9. func Add2() func(b int) int {
  10. return func(b int) int {
  11. return b + 2
  12. }
  13. }
  14. func Adder(a int) func(b int) int {
  15. return func(b int) int {
  16. return a + b
  17. }
  18. }