上面的章节把Go语言的基础知识学的差不多了,这次再学习一个重要的知识点——函数

简介

函数通俗的讲是组织好的、可重复使用的,用于执行指定任务的代码块。

定义

Go语言中函数定义使用func关键字,语法格式如下:

func 函数名(参数)(返回值){
    函数体
}

说明:

  • 函数名 - 由字母、数字、下划线组成,但第一个字母不能是数字,而且同一个包内,函数名也不能重复。
  • 参数 - 由参数变量及变量类型组成,多个参数之间使用,分隔。
  • 返回值 - 由返回值变量和变量类型组成,也可以只有返回值类型,多个返回值是必须用()包裹,并用,分隔。
  • 函数体 - 实现函数具体功能的代码块。

来个小示例吧,求两数之和:

func intSum(x int, y int) int {
    return x + y
}

参数

常规写法

参数常规的写法,就像上面的小示例一样,每个参数都有变量名、变量类型组成。

类型简写

如果参数中相邻变量的类型一样,则可以省略类型,比如上面的小示例可以简化为:

func intSum(x, y int) int {
    return x + y
}

注:即把变量x的类型int给省略了

可变参数

可变参数指的是函数的参数数量不是固定的,此时可以通过在参数的后面加...来标识,示例如下:

func intSum(x ...int) int {
    sum := 0
    for _, val := range x {
        sum += val
    }
    return sum
}

func main() {
    fmt.Println(intSum(1, 2))    // 3
    fmt.Println(intSum(1, 2, 3)) // 6
}

说明:

  • 可变参数的变量是一个切片类型
  • 可变参数与固定的参数搭配使用时,可变参数要放在固定参数的最后

返回值

Go语言中的返回值和其它语言一样,使用的return关键字,形式分为以下几种。

单返回值

单返回值顾名思义,即指有一个返回值,比如上面的几个例子,就只有一个返回值。

多返回值

多返回值顾名思义,即指有多个返回值,此时返回值必须用()包裹起来,示例如下:

func calc(x, y int) (int, int) {
    sum := x + y
    sub := x - y
    return sum, sub
}

返回值命名

返回值命名是指在返回值中把变量名定义好,这样子在函数体里就可以直接使用了(PS:调用方也不用为取返回值变量名发愁了),最后通过return关键字返回,示例如下:

func calc(x, y int) (sum, sub int) {
    sum := x + y
    sub := x - y
    return
}

调用

上面说了这么多,还有没有说怎么调用呢,其实调用也比较简单,分为两种,接下来我们介绍一下。

函数名调用

直接通过函数名()的方式调用,这也是开发中最常用的方式,示例如下:

func main() {
    fmt.Println(calc(4, 3)) // 7 1
}

函数类型变量调用

即把函数赋值给一个变量,这种不常用,咱也介绍一下吧,来个小示例:

func main() {
    aa := calc                          // 将函数赋值给变量aa
    fmt.Printf("type of aa : %T\n", aa) // type of aa : func(int, int) (int, int)
    fmt.Println(aa(4, 5))               // 9 -1
}

进阶

高阶函数

函数做为参数

函数可以做为另一个函数的参数,示例如下:

func intSum(x ...int) int {
    sum := 0
    for _, val := range x {
        sum += val
    }
    return sum
}

func add(x, y int, op func(...int) int) int {
    return op(x, y)
}

func main() {
    fmt.Println(add(10, 20, intSum)) // 30
}

函数做为返回值

函数也可以做为另一个函数的返回值,示例如下:

func intSum(x ...int) int {
    sum := 0
    for _, val := range x {
        sum += val
    }
    return sum
}

func do() func(...int) int {
    return intSum // 返回一个函数
}

func main() {
    cc := do()               // 将返回的函数赋值给一个变量
    fmt.Println(cc(1, 2, 3)) // 6
}

匿名函数

匿名函数即没有函数名的函数,语法格式如下:

func (参数) (返回值) {
    函数体
}

因为匿名函数是没有函数名的,因此就不能通过调用函数名的方式执行,执行它有两个方式:

赋值给变量后执行

即先将匿名函数赋值给一个变量,也相当于给它个名字,然后再执行,如下:

func main() {
    aa := func(x, y int) {
        fmt.Println(x + y)
    }
    aa(2, 3) // 5
}

定义好后直接执行

即匿名函数定义好之后,就直接在后面加()传参即可直接执行,如下:

func main() {
    func(x, y int) {
        fmt.Println(x + y)
    }(4, 5) // 9
}

注:匿名函数在开发中一般用于回调函数和闭包中。

闭包

闭包指的是一个函数与其它相关引用环境组合而成的一个实体。简单来说就是闭包=函数+引用环境,示例如下:

func main() {
    jpgFunc := makeSuffixFunc(".jpg")
    fmt.Println(jpgFunc("test")) // test.jpg
}

func makeSuffixFunc(suffix string) func(string) string {
    return func(name string) string {
        if !strings.HasSuffix(name, suffix) {
            return name + suffix
        }
        return name
    }
}

defer语句

Go语言中的defer语句会将后面跟随的语句进行延迟处理。当defer所在的函数返回时,延迟处理的语句会逆序执行,也就是先定义的最后执行,最后定义的最先执行。来个小示例感受一下吧~

func main() {
    fmt.Println("start")
    defer fmt.Println(1)
    defer fmt.Println(2)
    defer fmt.Println(3)
    fmt.Println("end")
}

输出:

start
end
3
2
1

再来个经典案例:

func f1() int {
    x := 5
    defer func() {
        x++
    }()
    return x
}

func f2() (x int) {
    defer func() {
        x++
    }()
    return 5
}

func f3() (y int) {
    x := 5
    defer func() {
        x++
    }()
    return x
}

func f4() (x int) {
    defer func(x int) {
        x++
    }(x)
    return 5
}

func main() {
    fmt.Println(f1()) // 5
    fmt.Println(f2()) // 6
    fmt.Println(f3()) // 5
    fmt.Println(f4()) // 5
}

再来一个面试题:

func calc(index string, a, b int) int {
    ret := a + b
    fmt.Println(index, a, b, ret)
    return ret
}

func main() {
    x := 1
    y := 2
    defer calc("AA", x, calc("A", x, y))
    x = 10
    defer calc("BB", x, calc("B", x, y))
    y = 20
}

输出:

A 1 2 3
B 10 2 12
BB 10 12 22
AA 1 3 4

内建函数

Go语言中还有一些常用的内建函数,之前的文章中也用到过一些,今天小小总结一下。

内建函数使用说明
close关闭channel管道
len求长度,比如:string、array、slice、map、channel
new用来分配内存,主要用来分配值类型的,比如:int、string、struct等,返回的是指针类型
make用来分配内在,主要用来分配引用类型,比如:map、slice、chan,返回的是它本身
panic/recover用来做错误的处理
函数相关的大概就这些东西了,886