golang语言 函数

形参(或结果)列表中,名称要么都存在,要么都不存在

指定了返回形参的名字,在进入函数时,返回值会被初始化为自身类型的零值

若所有的返回值都有名称,return语句可以省略操作数,这被称为bare return

若只有一个没有名称的结果,结果列表可以不加括号

未初始化的函数类型变量的值为nil,调用nil值的函数会引发panic

函数不能比较,可以和nil比较

实参在函数调用前求值

 

形参都是值传递的,不存在传引用,切片传的也是值

 

函数的类型被称为函数的标识符。如果两个函数形式参数列表和返回值列表中的变量类型一一对应,那么这两个函数被认为有相同的类型和标识符。

 

若函数g的返回值和f的形参符合,那么可以 f( g() ),

g的返回值不只1个,那么g()不能和其他参数一起作为f的实参,f( 3, g() )是错误的

g只有1个返回值,g()可以和其他参数一起作为f的实参,f(3,4,g(),5,6)是可以的

 

return &File{fd: fd, name: name} // 可返回地址

 

func f (x int) (y int){

  x := 1 // 报错

  y := 1 // 报错

  return 1 // x y 都没使用 ok

}

 

函数字面量

f := func(x, y int) int { return x + y } //赋值给变量 func(ch chan int) { ch <- ACK }(replyChan) //字面量调用

 

函数值

func square(n int) int { return n * n }

func product(m, n int) int { return m * n }

f := square

fmt.Println(f(3)) // "9"

f = product // compile error: can't assign func(int, int) int to func(int) int

 

匿名函数

拥有函数名的函数只能在包级语法块中被声明,通过函数字面量(function literal) ,我们可绕过这一限制,在任何表达式中表示一个函数值。

func squares() func() int {

    var x int

    return func() int {

        x++

        return x * x

    }

func main() {

    f := squares()

    fmt.Println(f()) // "1"

    fmt.Println(f()) // "4"

    fmt.Println(f()) // "9"

    fmt.Println(f()) // "16"

}

squares中定义的匿名内部函数可以访问和更新squares中的局部变量,这意味着匿名函数和squares中,存在变量引用。这就是函数值属于引用类型和函数值不可比较的原因。

 

递归调用匿名函数,需要先声明变量,再将匿名函数赋值给变量,必须分成两步,否则报错。

var  f  func(i  int) int

f = func(i  int) int {

   if i == 0{ return 0 }

   return i + f(i-1)

}

f(3) // 3+2+1+0==6

 

可变参数

最后一个形参的类型前可能带有...前缀,表示接受零个或多个实参,

在函数体中,可变参数被看作slice

slice后加上...,可将slice作为实参传给可变参数

可变参数和切片参数是不同的

func f(...int) {}  // func(...int)

func g([]int) {}  // func([]int)

 

panic

函数 F 中的 panic 调用会终止 F 的执行。 任何在 F 中 defer 的函数都会在 F 返回给其调用者前执行,且不执行函数F剩余的部分(panic后面的defer也不会被执行)。其调用者 G 对 F 的调用如同对 panic 的调用,即终止G的执行, 并以相同的方式运行被推迟的函数。这会持续到该Go程中所有函数都按相反顺序停止执行之后。 到那时,该程序将被终止,而该错误情况会被报告,包括引发 panic 的实参值。 此终止序列称为恐慌过程。

 

    defer fmt.Println("in main")

    defer func() {

        defer func() {

            panic("panic again and again")

        }()

        panic("panic again")

    }()

    panic("panic once")

 

 

打印:

in main

panic: panic once

  panic: panic again

  panic: panic again and again

 

  recover

recover 函数允许程序管理恐慌过程Go程的行为。在defer的函数中,执行 recover() 调用会通过恢复正常执行, 并取回传递至 panic 调用的错误值来停止恐慌过程序列。导致panic的函数不会继续运行,但能正常返回。panic后面的任何代码,包括后面的defer和recover等,都不会执行。

 recover 在已推迟函数之外被调用,它将不会停止恐慌过程序列。在此情况下,或当Go程不在恐慌过程中时, 或提供给 panic 的实参为 nil 时,recover 就会返回 nil,多次调用recover,除第一次执行的recover以外,其他recover都返回nil

    func protect(g func()) {
        defer func() {
            if x := recover(); x != nil {
                log.Printf("run time panic: %v", x)
            }
        }()
        g()
    }


    defer recover()            // defer 直接调用 recover(),无效


    defer func() {
        func() { recover() }()    // defer 嵌套调用 recover(),无效
    }()

 

 

 

defer

defer的函数在外层函数执行到末尾,或者执行了return语句,或者panic后执行

defer语句执行时,defer的函数值与形参会被求值并保存

defer的函数按被defer的相反顺序执行

defer的函数 f() 如果是nil值的函数,那么在defer f()时不会panic,在执行 f 时会panic

    for i := 0; i <= 3; i++ {
        defer fmt.Print(i)            // 3 2 1 0
    }

    for i := 0; i <= 3; i++ {
        defer  func(){                 // 4 4 4 4
            fmt.Println(i)
        }()
    }

    for i := 0; i <= 3; i++ {
        defer  func( i int ){             // 3 2 1 0
            fmt.Println(i)
        } ( i )
    }

    func f() (result int) {        // f 返回 1
        defer func() {
            result++
        }()
        return 0
    }

    func(){                        // 打印 a
        f := func(){ fmt.Println("a") }
        defer f()                    // 此时 f 已经求值
        f := func(){ fmt.Println("b") }
    }


    func NeverExit(name string, f func()) {        // panic时,重启goroutine
        defer func() {
            if v := recover(); v != nil { go NeverExit(name, f)  }
        }()
        f()
    }

    --------------------------------------------------------------------------------

    for _, v := range arr {
        go func() {
            process( v ) // for所在的go程和多个新go程共享和修改v
        }()
    }

    for _, v := range arr {
        go func(v int) {
            process( v )
        }(v)        //复制了v的副本
    }

    for _, v := range arr {
        v := v    //复制了v的副本,局部变量屏蔽循环变量,常见写法
        go func() {
            process( v )
        }()
    }

    --------------------------------------------------------------------------------

    var rmdirs []func()
    for _, dir := range tempDirs() {
        rmdirs = append(rmdirs, func() {
            os.RemoveAll(dir)     // 错误
        })
    }

    dirs := tempDirs()
    for i := 0; i < len(dirs); i++ {
        rmdirs = append(rmdirs, func() {
            os.RemoveAll(dirs[i]) // 错误
        })
    }

 

posted @ 2020-05-27 20:59  是的哟  阅读(225)  评论(0编辑  收藏  举报