GO語言筆記--函數

1.函數function

1)Go函數不支持嵌套、重載和默認參數

2)但支持以下特性:無需聲明原型、不定長參數、多返回值、命名返回值參數、匿名函數、閉包

3)定義函數只有關鍵字func,且左大括號不能另起一行

4)函數也可以作爲一種類型使用

5)函數可以返回多個值

2.defer

1)執行方式類似其他語言中的析構函數,在函數執行結束後安裝調用順序的相反順序逐個執行

2)即使函數發生嚴重錯誤也會執行

3)支持匿名函數的調用

4)常用於資源清理、文件關閉、解鎖以及記錄時間等操作

5)如果函數體內某個變量作爲defer時匿名函數的參數,則在定義defer時及已經獲得了拷貝,否則是引用某個變量的地址

6)Go沒有異常機制但有painc/recover模式來處理錯誤

7)panic可以在任何地方引發,但recover只有在defer調用的函數中有效

 

實例:

package main

import (
    "fmt"
)

func main() {
    var fs = [4]func(){}
    for i := 0; i < 4; i++ {
        defer fmt.Println("defer i = ", i)
        defer func() { fmt.Println("defer_closure i = ", i) }()
        fs[i] = func() { fmt.Println("closure i = ", i) }
    }

    for _, f := range fs {
        f()
    }
}
輸出:
API server listening at: 127.0.0.1:12291
closure i = 4
closure i = 4
closure i = 4
closure i = 4
defer_closure i = 4
defer i = 3
defer_closure i = 4
defer i = 2
defer_closure i = 4
defer i = 1
defer_closure i = 4
defer i = 0

實例二:

package main

import (
    "fmt"
)

func eval(a, b int, op string) (int, error) {
    switch op {
    case "+":
        return a + b, nil
    case "-":
        return a - b, nil
    case "*":
        return a * b, nil
    case "/":
        q, _ := div(a, b)
        return q, nil
    default:
        return 0, fmt.Errorf("unsupported operation:%s", op)
    }
}

func div(a, b int) (q, r int) {
    return a / b, a % b
}

func main() {
    if result, err := eval(3, 4, "X"); err != nil {
        fmt.Println("Error:", err)
    } else {
        fmt.Println(result)
    }
    q, r := div(13, 3)
    fmt.Println(q, r)
}
輸出:
API server listening at: 127.0.0.1:6205
Error: unsupported operation:X
4 1

可變參數實例

package main

import (
    "fmt"
)

//可變參數就是傳入的參數個數是不確定的
func sum(num ...int) int {
    s := 0
    //由於參數個數不確定因此使用for進行range遍歷
    for i := range num {
        s += num[i]
    }
    return s
}

func main() {
    fmt.Println(sum(1, 2, 3, 4, 5, 6, 7, 8, 9))
    fmt.Println(sum(1))
    fmt.Println(sum(1, 2, 3))
}
輸出:
API server listening at: 127.0.0.1:3830
45
1
6

3.函數式編程

說明:

1)總之參數、變量、返回值都可以是函數

2)在”正統“的函數式編程中具有不可變性,也就是不能有狀態,只有常量和函數;函數只能有一個參數,GO語言函數式編程忽略了這些所謂的”正統“思想

如下實例展示了函數式編程,其中涉及到的知識點有反射、閉包、匿名函數等內容

package main

import (
    "fmt"
    "math"
    "reflect"
    "runtime"
)

//函數式編程,此處op是傳入的函數方法也就是func op(a,b int) int {}這樣一個形式
func apply(op func(int, int) int, a, b int) int {
    //通過反射獲取到函數名
    p := reflect.ValueOf(op).Pointer()
    opName := runtime.FuncForPC(p).Name()
    fmt.Println("Calling function %s with argws"+"(%d, %d)", opName, a, b)
    //返回調用函數
    return op(a, b)
}

func mytest(a, b int) int {
    return a + b
}

func main() {
    //利用閉包匿名函數,重寫math.Pow然後傳入到apply
    a := apply(func(a int, b int) int {
        return int(math.Pow(float64(a), float64(b)))
    }, 3, 4)
    fmt.Println(a)
    //傳入自定義的函數
    b := apply(mytest, 3, 4)
    fmt.Println(b)
}
執行結果:
API server listening at: 127.0.0.1:3333
Calling function %s with argws(%d, %d) main.main.func1 3 4
81
Calling function %s with argws(%d, %d) main.mytest 3 4
7

累加器實例

package main

import "fmt"

func adder() func(int) int {
    sum := 0
//這裏使用到了一個閉包
    return func(v int) int {
        sum += v
        return sum
    }
}

func main() {
    a := adder()
    for i := 0; i < 10; i++ {
        fmt.Printf("0 + 1 + ... + %d = %d\n", i, a(i))
    }
}
輸出:
API server listening at: 127.0.0.1:21876
0 + 1 + ... + 0 = 0
0 + 1 + ... + 1 = 1
0 + 1 + ... + 2 = 3
0 + 1 + ... + 3 = 6
0 + 1 + ... + 4 = 10
0 + 1 + ... + 5 = 15
0 + 1 + ... + 6 = 21
0 + 1 + ... + 7 = 28
0 + 1 + ... + 8 = 36
0 + 1 + ... + 9 = 45

上述實例中使用到了閉包

在函數體內閉包由兩部分構成:

1)局部變量

2)自由變量,上述sum就是一個自由變量

實例,菲波那切數列生成器

package main

import "fmt"

func fibonacci() func() int {
    a, b := 0, 1
    return func() int {
        a, b = b, a+b
        return a
    }
}

func main() {
    f := fibonacci()
    fmt.Println(f())
    fmt.Println(f())
    fmt.Println(f())
    fmt.Println(f())
    fmt.Println(f())
    fmt.Println(f())
    fmt.Println(f())
    fmt.Println(f())
}
輸出:
API server listening at: 127.0.0.1:13243
1
1
2
3
5
8
13
21

實例,爲函數實現接口

package main

import (
    "bufio"
    "fmt"
    "io"
    "strings"
)

type intGen func() int

func fibonacci() intGen {
    a, b := 0, 1
    return func() int {
        a, b = b, a+b
        return a
    }
}

func (g intGen) Read(p []byte) (n int, err error) {
    next := g()
    //菲波那切數列不會停止因此這裏設置一個數,超過50就停止
    if next > 50 {
        return 0, io.EOF
    }
    s := fmt.Sprintf("%d\n", next)
    return strings.NewReader(s).Read(p)
}

func testIoReader(reader io.Reader) {
    scanner := bufio.NewScanner(reader)
    for scanner.Scan() {
        fmt.Println(scanner.Text())
    }
}

func main() {
    f := fibonacci()
    //這樣菲波那切數列生成器就可以當作一個文件來讀取
    testIoReader(f)
}
輸出:
API server listening at: 127.0.0.1:27326
1
1
2
3
5
8
13
21
34

實例,使用函數遍歷二叉樹

tree/tree.go

package tree

import "fmt"

type Node struct {
    Value       int
    Left, Right *Node
}


func (node Node) Print() {
    fmt.Printf("%d ", node.Value)
}

func CreateNode(value int) *Node {
    return &Node{Value: value}
}

func (node *Node) Traverse() {
    node.TraverseFunc(func(n *Node) {
        n.Print()
    })
    fmt.Println()
}

func (node *Node) TraverseFunc(f func(*Node)) {
    if node == nil {
        return
    }

    node.Left.TraverseFunc(f)
    f(node)
    node.Right.TraverseFunc(f)
}

main.go

package main


import (
    "fmt"
    "my-code/test-code/tree/tree"
)


func main() {
    var root tree.Node
    root = tree.Node{Value: 3}
    root.Left = &tree.Node{}
    root.Right = &tree.Node{5, nil, nil}
    root.Right.Left = new(tree.Node)
    root.Left.Right = tree.CreateNode(2)


    root.Traverse()
    //統計節點數
    nodeCount := 0
    root.TraverseFunc(func(node *tree.Node) {
        nodeCount++
    })
    fmt.Println("node count:", nodeCount)
}
執行輸出:
API server listening at: 127.0.0.1:31917
0 2 3 0 5
node count: 5

GO語言閉包小結:

1)更爲自然,不需要修飾如何訪問自由變量

2)沒有Lambda表達式,但是有匿名函數

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章