Go 学习笔记(16)— 函数(02)[函数签名、有名函数、匿名函数、调用匿名函数、匿名函数赋值给变量、匿名函数做回调函数]
1. 函數簽名
函數類型也叫做函數簽名,可以使用 fmt.Printf("%T") 格式化參數打印函數類型。
package mainimport "fmt"func sumTwo(a []int) (ret int) {for _, v := range a {ret += v}return
}
func main() {n := []int{1, 2, 3, 4, 5}result := sumTwo(n) fmt.Println("result is ", result) // result is 15fmt.Printf("sumTwo type is %T\n", sumTwo) // sumTwo type is func([]int) int}
兩個函數類型相同的條件是:擁有相同的形參列表和返回值列表,其中列表元素的次序、個數和類型都相同,形參名稱可以不相同。
在 Go 語言中,函數可是一等的(first-class)公民,函數類型也是一等的數據類型。Go 語言在語言層面支持了函數式編程。這是什么意思呢?
簡單來說,這意味著函數不但可以用于封裝代碼、分割功能、解耦邏輯,還可以化身為普通的值,在其他函數間傳遞、賦予變量、做類型判斷和轉換等等,就像切片和字典的值那樣。
而更深層次的含義就是:函數值可以由此成為能夠被隨意傳播的獨立邏輯組件(或者說功能模塊)。
書寫函數簽名的方式與函數聲明的是一致的。只是緊挨在參數列表左邊的不是函數名稱,而是關鍵字 func 。這里函數名稱和 func 互換了一下位置而已。
函數的簽名其實就是函數的參數列表和結果列表的統稱,它定義了可用來鑒別不同函數的那些特征,同時也定義了我們與函數交互的方式。
注意,各個參數和結果的名稱不能算作函數簽名的一部分,甚至對于結果聲明來說,沒有名稱都可以。
只要兩個函數的參數列表和結果列表中的元素順序及其類型是一致的,我們就可以說它們是一樣的函數,或者說是實現了同一個函數類型的函數。
嚴格來說,函數的名稱也不能算作函數簽名的一部分,它只是我們在調用函數時,需要給定的標識符而已。
package mainimport "fmt"type Printer func(contents string) (n int, err error)/*
函數printToStd的簽名與Printer的是一致的,因此前者是后者的一個實現,即使它們的名稱以及有的結果名稱是不同的。
*/
func printToStd(contents string) (bytesNum int, err error) {return fmt.Println(contents)
}func main() {var p Printerp = printToStd // 將函數賦值給一個變量p("something")
}
2. 有名函數
有名函數是指函數有具體的名稱,有名函數的函數名可以看作函數類型的常量,可以直接使用函數名調用函數,也可以直接賦值給函數類型變量,可以通過該變量來調用該函數。
package mainimport "fmt"func sum(a, b int) int {fmt.Println("I am in sum function")return a + b
}func main() {sum(3, 10) // 直接調用result := sum(3, 4)fmt.Println("result is ", result)f := sum // 有名函數可以直接賦值給變量ret := f(3, 10) // 通過該變量來調用該函數fmt.Println("ret is ", ret)
}
在 Go 語言中函數也是類型,可以作為參數傳遞給別的函數。
package main//定義一個函數類型,兩個 int 參數,一個 int 返回值
type math func(int, int) int//定義一個函數 add,這個函數兩個 int 參數一個 int 返回值,與 math 類型相符
func add(i int, j int) int {return i + j
}//再定義一個 multiply,這個函數同樣符合 math 類型
func multiply(i, j int) int {return i * j
}//foo 函數,需要一個 math 類型的參數,用 math 類型的函數計算第 2 和第 3 個參數數字,并返回計算結果
//稍后在 main 中我們將 add 函數和 multiply 分別作為參數傳遞給它
func foo(m math, n1, n2 int) int {return m(1, 2)
}func main() {//傳遞 add 函數和兩個數字,計算相加結果n := foo(add, 1, 2)println(n)//傳遞 multply 和兩個數字,計算相乘結果n = foo(multiply, 1, 2)println(n)
}
輸出結果:
3
2
3. 匿名函數
匿名函數是指不需要定義函數名的一種函數實現方式,由一個不帶函數名的函數聲明和函數體組成。
匿名函數顧名思義就是函數沒有名稱,匿名函數可以直接賦值給變量,可以當作實參,也可以作為返回值,還可以直接被調用。
匿名函數的定義格式如下:
func(參數列表)(返回參數列表){函數體
}
匿名函數的定義就是沒有名字的普通函數定義。
3.1 在定義時調用匿名函數
匿名函數可以在聲明后調用,例如:
func main() {func(data int) {fmt.Println("hello", data)}(100) // }后的(100),表示對匿名函數進行調用,傳遞參數為 100。
}
或者
func main() {func() {sum := 0for i := 1; i <= 100; i++ {sum += i}fmt.Println("sum is ", sum)}()
}
3.2 將匿名函數賦值給變量
匿名函數可以被賦值,例如:
// 將匿名函數體保存到f()中
f := func(data int) {fmt.Println("hello", data)
}// 使用f()調用
f(100)
3.3 匿名函數用作回調函數
下面的代碼實現對切片的遍歷操作,遍歷中訪問每個元素的操作使用匿名函數來實現,用戶傳入不同的匿名函數體可以實現對元素不同的遍歷操作,代碼如下:
package mainimport ("fmt"
)// 遍歷切片的每個元素, 通過給定函數進行元素訪問
func visit(list []int, f func(int)) {for _, v := range list {f(v)}
}func main() {// 使用匿名函數打印切片內容,// 匿名函數作為實參visit([]int{1, 2, 3, 4}, func(v int) {fmt.Println(v)})
}
3.4 匿名函數實現操作封裝
下面這段代碼將匿名函數作為 map 的鍵值,通過命令行參數動態調用匿名函數,代碼如下:
package mainimport ("flag""fmt"
)
//定義命令行參數 skill,從命令行輸入 --skill 可以將 = 后的字符串傳入 skillParam 指針變量。
var skillParam = flag.String("skill", "", "skill to perform")func main() {// 解析命令行參數,解析完成后,skillParam 指針變量將指向命令行傳入的值。flag.Parse()// 定義一個從字符串映射到 func() 的 map,然后填充這個 mapvar skill = map[string]func(){"fire": func() { // 初始化 map 的鍵值對,值為匿名函數。fmt.Println("chicken fire")},"run": func() {fmt.Println("soldier run")},"fly": func() {fmt.Println("angel fly")},}
/* skillParam 是一個 *string 類型的指針變量,
使用 *skillParam 獲取到命令行傳過來的值,并在 map 中查找對應命令行參數指定的字符串的函數。
*/if f, ok := skill[*skillParam]; ok {f()} else {fmt.Println("skill not found")}}
運行結果如下:
PS D:\code> go run main.go --skill=fly
angel fly
PS D:\code> go run main.go --skill=run
soldier run
3.5 匿名函數其它示例
package mainimport "fmt"// 匿名函數直接賦值給變量
var sum = func(a, b int) int {return a + b
}// 匿名函數作為返回值
func swap(str string) func(int, int) int {if str == "add" {return func(a, b int) int {return a + b}}if str == "sub" {return func(a, b int) int {return a - b}}return nil}func input(f func(int, int) int, a, b int) int {return f(a, b)
}
func main() {// 匿名函數作為實參z := input(func(x, y int) int {return x + y}, 10, 20)fmt.Println("z is ", z)fmt.Println("sum(1, 3) is ", sum(1, 3))fmt.Println("swap(1, 3) is ", swap("sub")(1, 10))}
輸出:
z is 30
sum(1, 3) is 4
swap(1, 3) is -9
匿名函數可賦值給變量,做為結構字段,或者在 channel 里傳送。
package mainfunc main() {// --- function variable ---fn := func() { println("Hello, World!") }fn()// --- function collection ---fns := [](func(x int) int){func(x int) int { return x + 1 },func(x int) int { return x + 2 },}println(fns[0](100))// --- function as field ---d := struct {fn func() string}{fn: func() string { return "Hello, World!" },}println(d.fn())// --- channel of function ---fc := make(chan func() string, 2)fc <- func() string { return "Hello, World!" }println((<-fc)())
}
輸出:
Hello, World!
101
Hello, World!
Hello, World!
總結
以上是生活随笔為你收集整理的Go 学习笔记(16)— 函数(02)[函数签名、有名函数、匿名函数、调用匿名函数、匿名函数赋值给变量、匿名函数做回调函数]的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 1960的一元纸币人民币值多少钱?
- 下一篇: 包真皮座椅多少钱啊?