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