<em>Mac</em>Book项目 2009年学校开始实施<em>Mac</em>Book项目,所有师生配备一本<em>Mac</em>Book,并同步更新了校园无线网络。学校每周进行电脑技术更新,每月发送技术支持资料,极大改变了教学及学习方式。因此2011
2021-06-01 09:32:01
當函數具備以下兩種特性的時候,就可以稱之為高階函數(high order functions):
一般來說,附帶的還具備一個特性:函數可以作為一個值賦值給變數。
f := func(){...} f()
由於Go中函數不能巢狀命名函數,所以函數返回函數的時候,只能返回匿名函數。
先簡單介紹下高階函數,然後介紹閉包。
例如,將函數作為另一個函數的引數:
package main import "fmt" func added(msg string, a func(a, b int) int) { fmt.Println(msg, ":", a(33, 44)) } func main() { // 函數內部不能巢狀命名函數 // 所以main()中只能定義匿名函數 f := func(a, b int) int { return a + b } added("a+b", f) }
以下範例是函數返回另一個函數:
package main import "fmt" func added() func(a, b int) int { f := func(a, b int) int { return a + b } return f } func main() { m := added() fmt.Println(m(33, 44)) }
將函數B作為另一個函數A的引數,可以使得函數A的通用性更強,可以隨意定義函數B,只要滿足規則,函數A都可以去處理,這比較適合於回撥函數。
在Go的sort包中有一個很強大的Slice排序工具SliceStable(),它就是將排序函數作為引數的:
func SliceStable(slice interface{}, less func(i, j int) bool)
這個函數是什麼意思呢?給定一個名為slice的Slice結構,使用名為less的函數去對這個slice排序。這個less函數的結構為less func(i, j int) bool
,其中i和j指定排序依據。Go中已經內建好了排序的演演算法,我們無需自己去定義排序演演算法,Go會自動從Slice中每次取兩個i和j索引對應的元素,然後去回撥排序函數less。所以我們只需要傳遞升序還是降序、根據什麼排序就可以。
例如:
package main import ( "fmt" "sort" ) func main() { s1 := []int{112, 22, 52, 32, 12} // 定義排序函數 less := func(i, j int) bool { // 降序排序 return s1[i] > s1[j] // 升序排序:s1[i] < s1[j] } // sort.SliceStable(s1, less) fmt.Println(s1) }
這裡的排序函數就是回撥函數。每取一次i和j對應的元素,就呼叫一次less函數。
可以將排序函數直接寫在SliceStable()的引數位置:
sort.SliceStable(s1, func(i, j int) bool { return s1[i] > s1[j] })
還可以更強大更靈活。例如,按照字元大小順序來比較,而不是按照數值大小比較:
package main import ( "fmt" "sort" "strconv" ) func main() { s1 := []int{112, 220, 52, 32, 42} sort.SliceStable(s1, func(i, j int) bool { // 將i和j對應的元素值轉換成字串 bi := strconv.FormatInt(int64(s1[i]), 10) bj := strconv.FormatInt(int64(s1[j]), 10) // 按字元順序降序排序 return bi > bj }) fmt.Println(s1) }
按照字串長度來比較:
package main import ( "fmt" "sort" ) func main() { s1 := []string{"hello","malong","gaoxiao"} sort.SliceStable(s1, func(i, j int) bool { // 按位元組大小順序降序排序 return len(s1[i]) > len(s1[j]) }) fmt.Println(s1) }
更嚴格地說是按位元組數比較,因為len()操作字串時獲取的是位元組數而非字元數。如果要按照字元數比較,則使用如下程式碼:
package main import ( "fmt" "sort" ) func main() { s1 := []string{"hello","世界","gaoxiao"} sort.SliceStable(s1, func(i, j int) bool { // 按位元組大小順序降序排序 return len([]rune(s1[i])) > len([]rune(s1[j])) }) fmt.Println(s1) }
函數A返回函數B,最典型的用法就是閉包(closure)。
簡單地說,閉包就是"一個函數+一個作用域環境"組成的特殊函數。這個函數可以存取不是它自己內部的變數,也就是這個變數在其它作用域內,且這個變數是未賦值的,而是等待我們去賦值的。
例如:
package main import "fmt" func f(x int) func(int) int{ g := func(y int) int{ return x+y } // 返回閉包 return g } func main() { // 將函數的返回結果"閉包"賦值給變數a a := f(3) // 呼叫儲存在變數中的閉包函數 res := a(5) fmt.Println(res) // 可以直接呼叫閉包 // 因為閉包沒有賦值給變數,所以它稱為匿名閉包 fmt.Println(f(3)(5)) }
上面的f()返回的g之所以稱為閉包函數,是因為它是一個函數,且參照了不在它自己範圍內的變數x,這個變數x是g所在作用域環境內的變數,因為x是未知、未賦值的自由變數。
如果x在傳遞給g之前是已經賦值的,那麼閉包函數就不應該稱為閉包,因為這樣的閉包已經失去意義了。
下面這個g也是閉包函數,但這個閉包函數是自定義的,而不是通過函數返回函數得到的。
package main import "fmt" func main() { // 自由變數x var x int // 閉包函數g g := func(i int) int { return x + i } x = 5 // 呼叫閉包函數 fmt.Println(g(5)) x = 10 // 呼叫閉包函數 fmt.Println(g(3)) }
之所以這裡的g也是閉包函數,是因為g中存取了不屬於自己的變數x,而這個變數在閉包函數定義時是未繫結值的,也就是自由變數。
閉包的作用很明顯,在第一個閉包例子中,f(3)退出後,它返回的閉包函數g()仍然記住了原本屬於f()中的x=3
。這樣就可以讓很多閉包函數共用同一個自由變數x的值。
例如,下面的a(3)
、a(5)
、a(8)
都共用來自f()的x=3
。
a := f(3) a(3) a(5) a(8)
再往外層函數看,f(3)可以將自由變數x繫結為x=3
,自然也可以繫結為x=5
、x=8
等等。
所以,什麼時候使用閉包?一般來說,可以將過程分成兩部分或更多部分都進行工廠化的時候,就適合閉包(實際上,有些地方直接將閉包稱呼為工廠函數)。第一個部分是可以給自由變數批次繫結不同的值,第二部分是多個閉包函數可以共用第一步繫結後的自由變數。
更多關於Go回撥函數說明,Go閉包說明請檢視下面的相關連結
相關文章
<em>Mac</em>Book项目 2009年学校开始实施<em>Mac</em>Book项目,所有师生配备一本<em>Mac</em>Book,并同步更新了校园无线网络。学校每周进行电脑技术更新,每月发送技术支持资料,极大改变了教学及学习方式。因此2011
2021-06-01 09:32:01
综合看Anker超能充系列的性价比很高,并且与不仅和iPhone12/苹果<em>Mac</em>Book很配,而且适合多设备充电需求的日常使用或差旅场景,不管是安卓还是Switch同样也能用得上它,希望这次分享能给准备购入充电器的小伙伴们有所
2021-06-01 09:31:42
除了L4WUDU与吴亦凡已经多次共事,成为了明面上的厂牌成员,吴亦凡还曾带领20XXCLUB全队参加2020年的一场音乐节,这也是20XXCLUB首次全员合照,王嗣尧Turbo、陈彦希Regi、<em>Mac</em> Ova Seas、林渝植等人全部出场。然而让
2021-06-01 09:31:34
目前应用IPFS的机构:1 谷歌<em>浏览器</em>支持IPFS分布式协议 2 万维网 (历史档案博物馆)数据库 3 火狐<em>浏览器</em>支持 IPFS分布式协议 4 EOS 等数字货币数据存储 5 美国国会图书馆,历史资料永久保存在 IPFS 6 加
2021-06-01 09:31:24
开拓者的车机是兼容苹果和<em>安卓</em>,虽然我不怎么用,但确实兼顾了我家人的很多需求:副驾的门板还配有解锁开关,有的时候老婆开车,下车的时候偶尔会忘记解锁,我在副驾驶可以自己开门:第二排设计很好,不仅配置了一个很大的
2021-06-01 09:30:48
不仅是<em>安卓</em>手机,苹果手机的降价力度也是前所未有了,iPhone12也“跳水价”了,发布价是6799元,如今已经跌至5308元,降价幅度超过1400元,最新定价确认了。iPhone12是苹果首款5G手机,同时也是全球首款5nm芯片的智能机,它
2021-06-01 09:30:45