1. 程式人生 > 其它 >學習筆記-go入門2.0

學習筆記-go入門2.0

go編譯型語言,so函式編寫的順序無關緊要。
goroutine 協程
Go語言裡面擁三種類型的函式:
-普通的帶有名字的函式
-匿名函式或者 lambda 函式
-方法

同一種類型返回值和帶有變數名的返回值
func name()(int,int){return a,b}
func name()(a , b int){return}
不可以:
func name()(a,b int,int){return}

定義函式變數:
var fname func()
匿名函式篇:
① 函式內部定義函式:閉包
好處:內聯在函式中,不需要宣告,可直接使用函式的變數
Go語言中閉包是引用了自由變數的函式,被引用的自由變數和函式一同存在,即使已經離開了自由變數的環境也不會被釋放或者刪除,在閉包中可以繼續使用這個自由變數
函式 + 引用環境 = 閉包
一個函式型別就像結構體一樣,可以被例項化,函式本身不儲存任何資訊,只有與引用環境結合後形成的閉包才具有“記憶性”,函式是編譯期靜態的概念,而閉包是執行期動態的概念。
閉包(Closure)在某些程式語言中也被稱為 Lambda 表示式。
閉包對環境中變數的引用過程也可以被稱為“捕獲”,在 C++11 標準中,捕獲有兩種型別,分別是引用和複製,可以改變引用的原值叫做“引用捕獲”,捕獲的過程值被複制到閉包中使用叫做“複製捕獲”。
// C++ 與 C# 中為閉包建立了一個類,而被捕獲的變數在編譯時放到類中的成員中,閉包在訪問被捕獲的變數時,實際上訪問的是閉包隱藏類的成員。
在閉包內部修改引用的變數
閉包對它作用域上部的變數可以進行修改,修改引用的變數會對變數進行實際修改,通過下面的例子來理解:
例子:
// 準備一個字串
str := "hello world"
// 建立一個匿名函式
foo := func() { 
    // 匿名函式中訪問str
    str = "hello dude"
}
// 呼叫匿名函式
foo()
被捕獲到閉包中的變數讓閉包本身擁有了記憶效應,閉包中的邏輯可以修改閉包捕獲的變數,變數會跟隨閉包生命期一直存在,閉包本身就如同變數一樣擁有了記憶效應。

② 函式內部呼叫其他函式:函式呼叫

③ 函式內部呼叫引數傳過來的函式:回撥函式
將一個函式的指標作為引數傳遞給另一個函式,在外部再定義這個函式的實現。
回撥函式例子:
func visit(list []int, f func(int)) {
    for _, v := range list {
        f(v)
    }
}
func main() {
    // 使用匿名函式列印切片內容,可改變傳入函式的實現
    visit([]int{1, 2, 3, 4}, func(v int) {
        fmt.Println(v)
    })
}

④ 函式內部呼叫自己這個函式:遞迴
go介面篇:
定義介面:
type Invoker interface {
    // 需要實現一個Call方法
    Call(interface{})  //這個傳參??
}

1.結構體實現介面
type Struct struct {
}
// 實現Invoker的Call
func (s *Struct) Call(p interface{}) {
    fmt.Println("from struct", p)
}
main函式中:
// 宣告介面變數
var invoker Invoker
// 例項化結構體
s := new(Struct)
// 將例項化的結構體賦值到介面
invoker = s
// 使用介面呼叫例項化結構體的方法Struct.Call
invoker.Call("hello") 

2.函式體實現介面
函式的宣告不能直接實現介面,需要將函式定義為型別後,使用型別實現結構體,當型別方法被呼叫時,還需要呼叫函式本體。
// 函式定義為型別
type FuncCaller func(interface{})
// 實現Invoker的Call
func (f FuncCaller) Call(p interface{}) {
    // 呼叫f()函式本體
    f(p)
}
main函式中:
// 宣告介面變數
var invoker Invoker
// 將匿名函式轉為FuncCaller型別, 再賦值給介面
invoker = FuncCaller(func(v interface{}) {
    fmt.Println("from function", v)
})
// 使用介面呼叫FuncCaller.Call, 內部會呼叫函式本體
invoker.Call("hello")
TODO:標記部分程式碼以供將來參考:優化和改進的領域、可能的更改、要討論的問題等。

Go語言goroutine和channel使用
goroutine是Go語言中的輕量級執行緒實現,由Go語言執行時(runtime)管理。使用的時候在函式前面加“go”這個單詞作為關鍵詞,也是與普通函式的區別了。在函式前面加go關鍵字就可以建立一個新的goroutine進行併發執行。
channel是Go語言提供的goroutine間的通訊方式,我們可以使用channel在兩個或多個goroutine之家傳遞訊息。channel使用的關鍵字是用“chan”.
go方法:
go語言中函式的概念和c語言中的函式類似,函式名其實是一個指標,而go語言的方法是擁有接收者的函式,其實是c++中類的方法的概念。函式是獨立存在的,而方法必須有接收者,即必須依附於某個物件。go語言使用struct來抽象物件。因此方法的接收者可以是struct例項或struct的指標。
type user struct {
        name  string,
        email string,
}
//這是函式的定義
func notify(email string) {
        fmt.Println("Email is %s", email)
}
//這是方法的定義
func (u user) notify(email string) {
        fmt.Println("Email is %d", email)
}
go error型別:
見go入門。
對方法的接收者傳值:不改變接收者。傳地址才改變。
defer:
Go語言的 defer 語句會將其後面跟隨的語句進行延遲處理,在 defer 歸屬的函式即將返回時,將延遲處理的語句按 defer 的逆序進行執行,也就是說,先被 defer 的語句最後被執行,最後被 defer 的語句,最先被執行。
當有多個 defer 行為被註冊時,它們會以逆序執行(類似棧,即後進先出)
defer func(){   }
go func(){   }
go中{}後的():