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表達式,但是有匿名函數