代碼:
package main
import "fmt"
func add() func(int) int {
sum:=0
innerFunc := func(x int) int{
sum += x
return sum
}
return innerFunc
}
func main() {
pos, neg := add(), add()
for i:=0;i<5;i++{
fmt.Println(pos(i),neg(-1*i))
}
}
預期結果:
0 0
1 -1
2 -2
3 -3
4 -4
實際輸出:
0 0
1 -1
3 -3
6 -6
10 -10
問:爲啥局部變量sum值會有變化呢?
答:該問題其實牽涉到一種編程模式——函數式編程,在golang中主要體現在閉包應用上面。
何爲函數式編程呢?我們中的大多數人可能都學習過面向過程編程和麪向對象編程,但對函數式編程並太不瞭解。函數式編程其實是需要一定的條件的,那就是函數被認定爲“一等公民”和語言支持匿名函數。
何爲“一等公民”?“一等公民”意味着函數可以像普通的類型(整型、字符串等)一樣進行賦值、作爲函數的參數傳遞、作爲函數的返回值等。支持函數爲“一等公民的”常見編程語言有:Golang、Python、Scala、JavaScript、Ruby等。
關於函數式編程思想,有一本書叫《Learning Functional Programming in Go》,作者Lex Sheehan,有興趣可以下載。百度雲鏈接:https://pan.baidu.com/s/1VKTyKX_Vyhb2sVC9PFzqzQ 密碼:ylc3
其實我們通常談論到的函數式編程是關於閉包的。網上已經有很多關於閉包的講解,附上一個我覺得不錯的地址https://www.ibm.com/developerworks/cn/linux/l-cn-closure/index.html
那麼本例中,閉包環境爲
func add() func(int) int {
sum:=0
innerFunc := func(x int) int{
sum += x
return sum
}
return innerFunc
}
目前我的理解:
匿名函數和它引用的變量以及環境,類似常規函數引用全局變量處於一個包的環境。
可以將innerFunc代表的匿名函數看作常規函數,而sum就看作全局變量,它們都存在於add(),innerFunc不僅僅是存儲了一個函數的返回值,它同時存儲了一個閉包的狀態。在匿名函數中操作sum,那麼就相當於在常規函數中操作全局變量。因此,當本例中的pos和neg分別代表add()的實例化對象時,它們的每次執行,其函數空間中的sum值都會跟隨執行被改變。
函數式編程的一些應用舉例代碼:
一、業務函數的計數器
代碼:
package main
import "fmt"
func countFun(f func()) func() int{
count :=0
a := func () int{
f()
count++
fmt.Printf("work() had do %d times work\n",count)
return count
}
return a
}
func work() {
println("working...")
}
func main() {
cf := countFun(work)
for i:=0;i<5;i++{
cf()
}
}
輸出:
working...
work() had do 1 times work
working...
work() had do 2 times work
working...
work() had do 3 times work
working...
work() had do 4 times work
working...
work() had do 5 times work
二、實現裝飾器(裝飾器模式簡要來講就是保證已有方法完整性的前提下,提供額外的功能)
代碼:
package main
import "fmt"
func wrapper(f func() string) {
fmt.Println("ready...")
fmt.Println(f())
fmt.Println("end")
}
func workerOne() string {
return "worker one work!"
}
func workerTwo() string {
return "worker two work!"
}
func main() {
wrapper(workerOne)
wrapper(workerTwo)
}
輸出:
ready...
worker one work!
end
ready...
worker two work!
end
三、實現斐波那契數列(在數學上,斐波那契數列以如下被以遞歸的方法定義:F0=0,F1=1,Fn=Fn-1+Fn-2(n>=2,n∈N*))
代碼:
package main
import "fmt"
func makeFibGen() func() int {
a := 0
b := 1
return func() int {
b, a = a+b, b
return a
}
}
func main() {
gen := makeFibGen()
for i := 0; i < 10; i++ {
fmt.Println(gen())
}
}
輸出:
1
1
2
3
5
8
13
21
34
55