文章摘要: return func(v int) int {閉包是將函式內部和函式外部連線起來的橋樑 go 閉包 func adder() func(int) int {
閉包
通過一個累加器來看閉包的概念
python 閉包
def fun1(): sum = 0 def fun2(v): nonlocal sum sum += v return sum return fun2 a = fun1() for i in range(10): print(a(i))
fun1返回的不是一個值,而是一個函式 fun2,a = fun2,所以 a(i)會列印 sum 的值,為什麼 sum 一直在加呢,函式裡的值為什麼可以帶到函式體外呢,這就是閉包的神奇之處,閉包是離散數學的一個概念,可以多看看網上的講解加深印象
其實可以把閉包看做一個類, sum 就是類裡的屬性, fun2就是類的方法
所以 fun2可以使用 sum(自由變數)
java 閉包
static Functionadder() { final Holder sum = new Holder<>(0); return (Integer value) -> { sum.value += value; return sum.value; }; } public static void main(String[] args) { Function a = adder(); for (int i = 0; i < 10; i++) { System.out.println(a.apply(i)); } }
java 裡函式不能像變數一樣傳遞,但也能模擬閉包這裏的 adder 其實是一個 Function 物件
上面 python
程式碼裡 sum
前有個 nonlocal
修飾,表明 sum
不是一個區域性變數,這裏直接用了 final
修飾
閉包就是能夠讀取其他函式內部變數的函式。例如在javascript中,只有函式內部的子函式才能讀取區域性變數,所以閉包可以理解成「定義在一個函式內部的函式「。在本質上,閉包是將函式內部和函式外部連線起來的橋樑
go 閉包
func adder() func(int) int { sum := 0 return func(v int) int { sum += v return sum } }
但是正統的函數語言程式設計不是這樣,函式其實是一個系統,我們只關心,入參(x)和返回值(y)是什麼,其實裏面是怎麼實現的我們並不關心,現代的很多業務程式碼,其實在函式體內做了很多事情,創造了很多變數和物件,這其實被稱為函式的'副作用'
還是看累加器
// 正統的函數語言程式設計 // 只有常量和函式 type iAdder func(int) (int, iAdder) func adder2(base int) iAdder { return func(i int) (int, iAdder) { return base + i, adder2(base + i) } } func main(){ a := adder2(0) for i := 0; i < 10; i++ { var s int s, a = a(i) fmt.Println(s) } }
函數語言程式設計入門
斐波那契數列
func fib() func() int { a, b := 1, 1 return func() int { a, b = b, a+b return a } } f := fib() for i := 0; i < 10; i++ { fmt.Println(f()) } // 1 2 3 ... 55 89
這是用 print 列印的 fib 數,之前說道了 read 和 write 這兩個基本介面.
現在讓 fib這個函式實現一個 read 介面,然後任何能接收 reader 的方法都能輸出這個 fib 數了
// 定義一個函式的結構體,用函式實現介面,函式和普通變數一樣 type intGen func() int func fib1() intGen { a, b := 1, 1 return func() int { a, b = b, a+b return a } }
把滑鼠放到intGen 上,然後右鍵
image
image
image
func (g intGen) Read(p []byte) (n int, err error) { // 下一個 fib 數 next := g() //fib 數讀不完,需要有一個結束條件 if next > 1000 { return 0, io.EOF } // 底層找一個已經實現的 s := fmt.Sprintf("%dn", next) return strings.NewReader(s).Read(p) }
註釋已經寫得很清楚了,讓 intGen 這個函式結構體實現 reader 介面,等會就可以寫一個 接收 reader 引數的print 函式,把intGen函式當做引數傳進去了
/** 列印的方法 讓 fib 實現 Reader 介面,就可以用 print 方法列印了 */ func printFileContents(reader io.Reader) { scanner := bufio.NewScanner(reader) for scanner.Scan() { fmt.Println(scanner.Text()) } } f1 := fib1() printFileContents(f1) // 1 2 3 5 8 13 21 34 ... 610 987
goimports
一個好用的工具
image
能夠自動整理imports
把沒用到的去除,用到的,但系統沒有的,自動 go get
但是正常是下不下來的,因為需要下載
golang.org/x/tools/cmd/goimports
,而
golang.org
在國內是被牆的
-
go get -v github.com/gpmgo/gopm
,github 在國內沒被牆,先下載gopm
這個工具 - 配置
$ GOPATH:bin
-
gopm get -v -g -u golang.org/x/tools/cmd/goimports
用gopm
下載谷歌的工具包 -
go install golang.org/x/tools/cmd/goimports
把goimports
安裝到$ GOPATH 下
總結
scip
上述程式碼均已上傳至 github, 歡迎 star
https://github.com/yejunyu/golearn
image