go語言學習(11)--閉包與函數式編程

來源:互聯網
上載者:User

閉包

通過一個累加器來看閉包的概念

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 Function<Integer, Integer> adder() {        final Holder<Integer> 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() intfunc 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("%d\n", 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 在國內是被牆的

  1. go get -v github.com/gpmgo/gopm,github 在國內沒被牆,先下載 gopm 這個工具
  2. 配置$ GOPATH:bin
  3. gopm get -v -g -u golang.org/x/tools/cmd/goimportsgopm下載Google的工具包
  4. go install golang.org/x/tools/cmd/goimportsgoimports安裝到$ GOPATH 下

總結

  • 函數是一等公民,可以當做參數傳給另一個函數
  • 本來一個函數只能列印,但是改寫一下之後,讓他能傳一個函數進來,就可以幹任何事了,擴充性非常強(根據傳進來的函數來工作)
  • 推薦一本函數式編程的書 scip

上述代碼均已上傳至 github, 歡迎 star
https://github.com/yejunyu/golearn

image
相關文章

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.