一、闭包

Go 语言支持匿名函数,可作为闭包。匿名函数是一个”内联”语句或表达式。匿名函数的优越性在于可以直接使用函数内的变量,不必申明。

下面一个示例,很清楚地展现了以函数为参数值的用法:

  1. var x int
  2. inc := func() int {
  3. x++
  4. return x
  5. }
  6. fmt.Println(func() (a, b int) {
  7. return inc(), inc()
  8. }())

输出值显而易见,是 1 2,如果基础不扎实,还是不容易搞清楚的。

二、可变长度的参数

使用 ... 指定参数的长度是可变的。

var Test = func(values...int)  {
    for _, v := range values {
        fmt.Println(v)
    }
}

Test(1,2,3)

三、立即执行的函数

跟JS类似,go中也可以让函数立即执行,并且不需要像JS一样在函数之前添加 ;

    func(values...int)  {
        for _, v := range values {
            fmt.Println(v)
        }
    }(1,2,3)

四、返回多个值的函数

函数可以包含多个返回值:

func swap(x, y string) (string, string) {
    return y, x
}

func main() {
    a, b := swap("A", "B")
    fmt.Println(a, b) // B A
}

五、值传递与引用传递

先看一个比较烂大街的交换数值的例子:

func swap(x int, y int) {
    var temp int

    temp = x
    x = y
    y = temp
}

func main() {
    /* 定义局部变量 */
    var a int = 100
    var b int = 200
    fmt.Printf("交换前 a 的值为 : %d\n", a ) // 100
    fmt.Printf("交换前 b 的值为 : %d\n", b ) // 200

    /* 通过调用函数来交换值 */
    swap(a, b)
    fmt.Printf("交换后 a 的值 : %d\n", a ) // 100
    fmt.Printf("交换后 b 的值 : %d\n", b ) // 200
}

程序中使用的是值传递,所以两个值并没有实现交换。

使用引用的示例:

func swap(x *int, y *int) {
    var temp int

    temp = *x
    *x = *y
    *y = temp
}

func main() {
    /* 定义局部变量 */
    var a int = 100
    var b int = 200
    fmt.Printf("交换前 a 的值为 : %d\n", a ) // 100
    fmt.Printf("交换前 b 的值为 : %d\n", b ) // 200

    /* 通过调用函数来交换值 */
    swap(&a, &b)
    fmt.Printf("交换后 a 的值 : %d\n", a ) // 200
    fmt.Printf("交换后 b 的值 : %d\n", b ) // 100
}

由于传递的是引用,指向内存地址,所有值成功交换了。

六、延迟语句

使用 defer 关键字可以使函数中的某一条语句延迟执行,也就是在函数结束的时候才执行。
比如在读写文件时,可以将关闭文件写在前面,以防止遗忘关闭文件的操作:

func readFile(path string) ([]byte, error) {
    file, err := os.Open(path)
    if err != nil {
        return nil, err
    }
    defer file.Close()
    return ioutil.ReadAll(file)
}

defer匿名函数

当一个函数中存在多个defer语句时,可以将其放置于匿名函数中,它们携带的表达式语句的执行顺序一定是它们的出现顺序的倒序。

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

循环之中的defer

当defer放于循环之中,其输出值将反向输出。

func main() {
    f := func(i int) int {
        fmt.Printf("%d ",i)
        return i * 10
    }
    for i := 1; i < 5; i++ {
        defer fmt.Printf("%d ", f(i))
    }
}

输出:

1 2 3 4 40 30 20 10

循环中defer匿名函数

当在循环中defer的是一个匿名函数,那么其输出值将会是最终循环值。

for i := 1; i < 5; i++ {
    defer func() {
        fmt.Print(i)
    }()
}

输出:

5555

原因是defer语句携带的表达式语句中的那个匿名函数包含了对外部(确切地说,是该defer语句之外)的变量的使用。注意,等到这个匿名函数要被执行(且会被执行4次)的时候,包含该defer语句的那条for语句已经执行完毕了。此时的变量i的值已经变为了5。因此该匿名函数中的打印函数只会打印出5。

正确的用法是:把要使用的外部变量作为参数传入到匿名函数中。(玩过JS的一定都懂)

for i := 1; i < 5; i++ {
    defer func(n int) {
        fmt.Print(n)
    }(i)
}

七、递归函数

阶乘

func Factorial(n uint64)(result uint64) {
    if n > 0 {
        result = n * Factorial(n-1)
        return result
    }
    return 1
}

func main() {
    var i int = 5
    fmt.Printf("%d 的阶乘是 %d\n", i, Factorial(uint64(i)))
}

斐波那契数列

func fibonacci(n int) int {
    if n < 2 {
        return n
    }
    return fibonacci(n-2) + fibonacci(n-1)
}

func main() {
    var i int
    for i = 0; i < 10; i++ {
        fmt.Printf("%d\t", fibonacci(i))
    }
}