歡迎光臨
我們一直在努力

go語言學習(11)–閉包與函數語言程式設計

文章摘要: 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 Function adder() {
        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

在國內是被牆的

  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 下載谷歌的工具包
  4. go install golang.org/x/tools/cmd/goimportsgoimports 安裝到$ GOPATH 下

總結

scip

上述程式碼均已上傳至 github, 歡迎 star

https://github.com/yejunyu/golearn

image

未經允許不得轉載:頭條楓林網 » go語言學習(11)–閉包與函數語言程式設計