土拨鼠-->函数

函数

  在go语言中函数是重中之重,函数是程序实现具体功能的基本代码块,本章节主要介绍函数定义、函数进阶、函数高阶、匿名函数、闭包、defer、内置函数等。

函数定义

  格式:

  (1)函数名:由字母、数字、下划线组成,但函数名的第一个字母不能是数字,在同一个包中函数名不能重名;

  (2)参数:由参数变量和参数类型组成,多个参数间使用英文逗号隔开;

  (3)返回值:由返回值变量和其变量类型组成,也可只写返回值类型,多个返回值必须用()包裹,返回值间并用英文逗号隔开

  (4)函数体:实现指定功能的代码块

  函数的参数:

  (1)参数类型简写

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

  (2)可变参数

func sum(x int, y ...int) int {
    fmt.Println(y) //参数y是切片
    sum := x
    for _, v := range y {
        sum += v
    }
    return sum
}

  注意:可变参数必须作为函数最后的一个参数,可变参数是通过切片来实现的

  函数返回值

  (1)通过return关键字向外输出返回值

  (2)多返回值

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

  (3)给返回值命名,可在函数中直接使用这些返回值变量,最后通过return返回

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

  函数进阶

  (1)全局变量和局部变量

//全局变量定义在函数外部的变量
var num int64 = 10

func globalVar() {
    fmt.Printf("num=%d", num)
}
//局部变量
//1.函数内定义的变量无法在该函数外使用
//2.如果局部变量和全局变量重名,优先使用局部变量
//3.if条件判断、for循环、switch上使用定义变量的方式
func testLocalVar(x, y int) {
    fmt.Println(x, y)
    if x > 0 {
        z := 1000
        fmt.Println(z)
    }
    //fmt.Println(z)
    for i := 0; i < 10; i++ {
        fmt.Println(i)
    }
    //fmt.Println(i)
}

  (2)函数类型和变量

//函数类型
//定义一个函数类型
type calculation func(int, int) int

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

func sub(x, y int) int {
    return x - y
}

//凡是满足包括参数类型、参数个数,返回值类型、个数都一致,那么就可以说这个函数是这
//种函数的类型,就可以把该函数赋值给这种函数类型定义的变量

var c calculation  //函数类型变量
c = add       //函数赋值
c = sub
c(2,3) //函数调用        

  高阶函数

  (1)函数作为参数

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

func sub(x, y int) int {
    return x - y
}

func calc(x, y int, op func(int, int) int) int {
    return op(x, y)
}

func main() {
    ret := calc(10, 20, add)
    fmt.Println(ret) //30
}

  (2)函数作为返回值

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

func sub(x, y int) int {
    return x - y
}

func doSomething(s string) (func(int, int) int, error) {
    switch s {
    case "+":
        return add, nil
    case "-":
        return sub, nil
    default:
        err := errors.New("无法识别的操作符")
        return nil, err
    }
}

func main() {
    f,_:=doSomething("+")
        res:=f(1,2)
    fmt.Println(ret) //3
}

匿名函数

   匿名函数就是没有名称的函数,其无法像普通函数一样调用,需要保存到某个变量或者立即执行函数,这种函数多用于回调函数和闭包。

func main() {
    //将匿名函数保存到变量
    s := func(x, y int) {
        fmt.Println(x + y)
    }
    s(1, 2) //通过变量调用匿名函数

    //自执行函数,匿名函数定义完加()直接执行
    func(x, y int) {
        fmt.Println(x - y)
    }(10, 2)
}

闭包

   闭包指的是函数和与其相关的引用环境组合而成的整体(实体);闭包本质是返回一个函数(匿名函数),但是返回函数会用到该函数外部的一些变量,它们共同组成一个整体,所以要分析清楚返回的匿名函数使用到了那些外部变量;总体说:闭包=函数+引用环境。

func adder1() func(int) int {
    var x int
    return func(y int) int {
        x += y
        return x
    }
    //闭包包含了 外部变量x和func这个函数
}

func main() {变量
    //闭包
    var f = adder1() //函数f引用了外部作用域的x,此时f就是一个闭包,在f的生命周期内变量x也一直有效
    fmt.Println(f(10)) //10  
    fmt.Println(f(20))//30
    fmt.Println(f(30))//60

    f1 := adder2(20) //20
    fmt.Println(f1(40)) //60
    fmt.Println(f1(50)) //110
}

func adder2(x int) func(int) int {
    return func(y int) int {
        x += y
        return x
    }
}

func calc(base int) (func(int) int, func(int) int) {
    add := func(i int) int {
        base += i
        return base
    }

    sub := func(i int) int {
        base -= i
        return base
    }
    return add, sub
}

   闭包简单实现斐波那契数列:

func fibonacci() func() int {
    a, b := 0, 1
    return func() int {
        a, b = b, a+b
        return a
    }
}
func main() {
    f := fibonacci()
    for i := 0; i < 20; i++ { //计算前20位的斐波那契额数列
        fmt.Printf("%d  ", f())
    }
}

 

defer语句

   defer语义是延迟的意思,在go语言中使用defer,会将后面跟随的语句进行延迟处理,它的执行时机是defer语句归属的函数即将返回时,执行defer跟随的语句,如果一个函数中使用了多个defer,那么先被defer的语句后执行,最后defer的语句先执行。

func testDefer() {
    fmt.Println("start")
    defer fmt.Println(1)
    defer fmt.Println(2)
    defer fmt.Println(3)
    fmt.Println("end")
}
//输出
/*
    start
    end
    3
    2
    1
*/

  defer底层机制是当执行到defer时,暂不执行,只是将defer后面的语句压入到独立的栈中,当函数执行完毕后,再从该独立栈按中照先入后出的方式出栈,并执行对应的语句。

内置函数

内置函数 介绍
close 主要用来关闭channel
len 用来求长度,比如string、array、slice、map、channel
new 用来分配内存,主要用来分配值类型,比如int、struct,返回的是指针
make 用来分配内存,主要用来分配引用类型,比如chan、map、slice
append 用来追加元素到数组、slice中
panic 用来做错误处理,可以在任何地方引发/使用
recover 用来做错误处理,recover只有在defer调用的函数中有效
func funcA() {
    fmt.Println("func A")
}

func funcB() {
    defer func() {
        err := recover()
        //如果程序出出现了panic错误,可以通过recover恢复过来
        if err != nil {
            fmt.Println("recover in B")
        }
    }()
    panic("panic in B")
}

func funcC() {
    fmt.Println("func C")
}

func main() {
//程序运行期间funcB中引发了panic导致程序崩溃,异常退出了。这个时候我们就可以通过recover将程序恢复回来,继续往后执行
    funcA()
    funcB()
    funcC()
}

注意:

  (1)recover必须在defer中使用;

  (2)defer一定要在可能引发panic的语句之前定义;

  (3)发生panic时,一直向上返回,并执行每一层的defer。

 

posted @ 2020-03-01 19:30  MrJuJu  阅读(109)  评论(0编辑  收藏  举报