go

2018-02-24 15:18 更新

X分鐘速成Y

其中 Y=Go

源代碼下載:?learngo-cn.go

發(fā)明Go語言是出于更好地完成工作的需要。Go不是計(jì)算機(jī)科學(xué)的最新發(fā)展潮流,但它卻提供了解決現(xiàn)實(shí)問題的最新最快的方法。

Go擁有命令式語言的靜態(tài)類型,編譯很快,執(zhí)行也很快,同時(shí)加入了對(duì)于目前多核CPU的并發(fā)計(jì)算支持,也有相應(yīng)的特性來實(shí)現(xiàn)大規(guī)模編程。

Go語言有非常棒的標(biāo)準(zhǔn)庫(kù),還有一個(gè)充滿熱情的社區(qū)。

// 單行注釋
/* 多行
    注釋 */

// 導(dǎo)入包的子句在每個(gè)源文件的開頭。
// Main比較特殊,它用來聲明可執(zhí)行文件,而不是一個(gè)庫(kù)。
package main

// Import語句聲明了當(dāng)前文件引用的包。
import (
    "fmt"       // Go語言標(biāo)準(zhǔn)庫(kù)中的包
    "net/http"  // 一個(gè)web服務(wù)器包
    "strconv"   // 字符串轉(zhuǎn)換
)

// 函數(shù)聲明:Main是程序執(zhí)行的入口。
// 不管你喜歡還是不喜歡,反正Go就用了花括號(hào)來包住函數(shù)體。
func main() {
    // 往標(biāo)準(zhǔn)輸出打印一行。
    // 用包名fmt限制打印函數(shù)。
    fmt.Println("Hello world!")

    // 調(diào)用當(dāng)前包的另一個(gè)函數(shù)。
    beyondHello()
}

// 函數(shù)可以在括號(hào)里加參數(shù)。
// 如果沒有參數(shù)的話,也需要一個(gè)空括號(hào)。
func beyondHello() {
    var x int   // 變量聲明,變量必須在使用之前聲明。
    x = 3       // 變量賦值。
    // 可以用:=來偷懶,它自動(dòng)把變量類型、聲明和賦值都搞定了。
    y := 4
    sum, prod := learnMultiple(x, y)        // 返回多個(gè)變量的函數(shù)
    fmt.Println("sum:", sum, "prod:", prod) // 簡(jiǎn)單輸出
    learnTypes()                            // 少于y分鐘,學(xué)的更多!
}

// 多變量和多返回值的函數(shù)
func learnMultiple(x, y int) (sum, prod int) {
    return x + y, x * y // 返回兩個(gè)值
}

// 內(nèi)置變量類型和關(guān)鍵詞
func learnTypes() {
    // 短聲明給你所想。
    s := "Learn Go!" // String類型

    s2 := `A "raw" string literal
can include line breaks.` // 同樣是String類型

    // 非ascii字符。Go使用UTF-8編碼。
    g := 'Σ' // rune類型,int32的別名,使用UTF-8編碼

    f := 3.14195 // float64類型,IEEE-754 64位浮點(diǎn)數(shù)
    c := 3 + 4i  // complex128類型,內(nèi)部使用兩個(gè)float64表示

    // Var變量可以直接初始化。
    var u uint = 7  // unsigned 無符號(hào)變量,但是實(shí)現(xiàn)依賴int型變量的長(zhǎng)度
    var pi float32 = 22. / 7

    // 字符轉(zhuǎn)換
    n := byte('\n') // byte是uint8的別名

    // 數(shù)組類型編譯的時(shí)候大小固定。
    var a4 [4] int              // 有4個(gè)int變量的數(shù)組,初始為0
    a3 := [...]int{3, 1, 5}     // 有3個(gè)int變量的數(shù)組,同時(shí)進(jìn)行了初始化

    // Slice 可以動(dòng)態(tài)的增刪。Array和Slice各有千秋,但是使用slice的地方更多些。
    s3 := []int{4, 5, 9}        // 和a3相比,這里沒有省略號(hào)
    s4 := make([]int, 4)        // 分配一個(gè)有4個(gè)int型變量的slice,全部被初始化為0

    var d2 [][]float64          // 聲明而已,什么都沒有分配
    bs := []byte("a slice")     // 類型轉(zhuǎn)換的語法

    p, q := learnMemory()       // 聲明p,q為int型變量的指針
    fmt.Println(*p, *q)         // * 取值

    // Map是動(dòng)態(tài)可增長(zhǎng)關(guān)聯(lián)數(shù)組,和其他語言中的hash或者字典相似。
    m := map[string]int{"three": 3, "four": 4}
    m["one"] = 1

    // 在Go語言中未使用的變量在編譯的時(shí)候會(huì)報(bào)錯(cuò),而不是warning。
    // 下劃線 _ 可以使你“使用”一個(gè)變量,但是丟棄它的值。
    _,_,_,_,_,_,_,_,_ = s2, g, f, u, pi, n, a3, s4, bs
    // 輸出變量
    fmt.Println(s, c, a4, s3, d2, m)

    learnFlowControl() // 回到流程控制 
}

// Go全面支持垃圾回收。Go有指針,但是不支持指針運(yùn)算。
// 你會(huì)因?yàn)榭罩羔樁稿e(cuò),但是不會(huì)因?yàn)樵黾又羔樁稿e(cuò)。
func learnMemory() (p, q *int) {
    // 返回int型變量指針p和q
    p = new(int)    // 內(nèi)置函數(shù)new分配內(nèi)存
    // 自動(dòng)將分配的int賦值0,p不再是空的了。
    s := make([]int, 20)    // 給20個(gè)int變量分配一塊內(nèi)存
    s[3] = 7                // 賦值
    r := -2                 // 聲明另一個(gè)局部變量
    return &s[3], &r        // & 取地址
}

func expensiveComputation() int {
    return 1e6
}

func learnFlowControl() {
    // If需要花括號(hào),括號(hào)就免了
    if true {
        fmt.Println("told ya")
    }
    // 用go fmt 命令可以幫你格式化代碼,所以不用怕被人吐槽代碼風(fēng)格了,
    // 也不用容忍被人的代碼風(fēng)格。
    if false {
        // pout
    } else {
        // gloat
    }
    // 如果太多嵌套的if語句,推薦使用switch
    x := 1
    switch x {
    case 0:
    case 1:
        // 隱式調(diào)用break語句,匹配上一個(gè)即停止
    case 2:
        // 不會(huì)運(yùn)行
    }
    // 和if一樣,for也不用括號(hào)
    for x := 0; x < 3; x++ { // ++ 自增
        fmt.Println("iteration", x)
    }
    // x在這里還是1。為什么?

    // for 是go里唯一的循環(huán)關(guān)鍵字,不過它有很多變種
    for { // 死循環(huán)
        break    // 騙你的 
        continue // 不會(huì)運(yùn)行的
    }
    // 和for一樣,if中的:=先給y賦值,然后再和x作比較。
    if y := expensiveComputation(); y > x {
        x = y
    }
    // 閉包函數(shù)
    xBig := func() bool {
        return x > 100 // x是上面聲明的變量引用
    }
    fmt.Println("xBig:", xBig()) // true (上面把y賦給x了) 
    x /= 1e5                     // x變成10
    fmt.Println("xBig:", xBig()) // 現(xiàn)在是false

    // 當(dāng)你需要goto的時(shí)候,你會(huì)愛死它的!
    goto love
love:

    learnInterfaces() // 好東西來了!
}

// 定義Stringer為一個(gè)接口類型,有一個(gè)方法String
type Stringer interface {
    String() string
}

// 定義pair為一個(gè)結(jié)構(gòu)體,有x和y兩個(gè)int型變量。
type pair struct {
    x, y int
}

// 定義pair類型的方法,實(shí)現(xiàn)Stringer接口。
func (p pair) String() string { // p被叫做“接收器”
    // Sprintf是fmt包中的另一個(gè)公有函數(shù)。
    // 用 . 調(diào)用p中的元素。
    return fmt.Sprintf("(%d, %d)", p.x, p.y)
}

func learnInterfaces() {
    // 花括號(hào)用來定義結(jié)構(gòu)體變量,:=在這里將一個(gè)結(jié)構(gòu)體變量賦值給p。
    p := pair{3, 4}
    fmt.Println(p.String()) // 調(diào)用pair類型p的String方法 
    var i Stringer          // 聲明i為Stringer接口類型 
    i = p                   // 有效!因?yàn)閜實(shí)現(xiàn)了Stringer接口(類似java中的塑型) 
    // 調(diào)用i的String方法,輸出和上面一樣
    fmt.Println(i.String())

    // fmt包中的Println函數(shù)向?qū)ο笠鼈兊膕tring輸出,實(shí)現(xiàn)了String方法就可以這樣使用了。
    // (類似java中的序列化)
    fmt.Println(p) // 輸出和上面一樣,自動(dòng)調(diào)用String函數(shù)。
    fmt.Println(i) // 輸出和上面一樣。

    learnErrorHandling()
}

func learnErrorHandling() {
    // ", ok"用來判斷有沒有正常工作 
    m := map[int]string{3: "three", 4: "four"}
    if x, ok := m[1]; !ok { // ok 為false,因?yàn)閙中沒有1
        fmt.Println("no one there")
    } else {
        fmt.Print(x) // 如果x在map中的話,x就是那個(gè)值嘍。
    }
    // 錯(cuò)誤可不只是ok,它還可以給出關(guān)于問題的更多細(xì)節(jié)。
    if _, err := strconv.Atoi("non-int"); err != nil { // _ discards value
        // 輸出"strconv.ParseInt: parsing "non-int": invalid syntax"
        fmt.Println(err)
    }
    // 待會(huì)再說接口吧。同時(shí),
    learnConcurrency()
}

// c是channel類型,一個(gè)并發(fā)安全的通信對(duì)象。
func inc(i int, c chan int) {
    c <- i + 1 // <-把右邊的發(fā)送到左邊的channel。
}

// 我們將用inc函數(shù)來并發(fā)地增加一些數(shù)字。
func learnConcurrency() {
    // 用make來聲明一個(gè)slice,make會(huì)分配和初始化slice,map和channel。
    c := make(chan int)
    // 用go關(guān)鍵字開始三個(gè)并發(fā)的goroutine,如果機(jī)器支持的話,還可能是并行執(zhí)行。
    // 三個(gè)都被發(fā)送到同一個(gè)channel。
    go inc(0, c) // go is a statement that starts a new goroutine.
    go inc(10, c)
    go inc(-805, c)
    // 從channel中獨(dú)處結(jié)果并打印。
    // 打印出什么東西是不可預(yù)知的。
    fmt.Println(<-c, <-c, <-c) // channel在右邊的時(shí)候,<-是讀操作。

    cs := make(chan string)       // 操作string的channel
    cc := make(chan chan string)  // 操作channel的channel
    go func() { c <- 84 }()       // 開始一個(gè)goroutine來發(fā)送一個(gè)新的數(shù)字 
    go func() { cs <- "wordy" }() // 發(fā)送給cs
    // Select類似于switch,但是每個(gè)case包括一個(gè)channel操作。
    // 它隨機(jī)選擇一個(gè)準(zhǔn)備好通訊的case。
    select {
    case i := <-c: // 從channel接收的值可以賦給其他變量
        fmt.Println("it's a", i)
    case <-cs: // 或者直接丟棄
        fmt.Println("it's a string")
    case <-cc: // 空的,還沒作好通訊的準(zhǔn)備 
        fmt.Println("didn't happen.")
    }
    // 上面c或者cs的值被取到,其中一個(gè)goroutine結(jié)束,另外一個(gè)一直阻塞。

    learnWebProgramming() // Go很適合web編程,我知道你也想學(xué)!
}

// http包中的一個(gè)簡(jiǎn)單的函數(shù)就可以開啟web服務(wù)器。
func learnWebProgramming() {
    // ListenAndServe第一個(gè)參數(shù)指定了監(jiān)聽端口,第二個(gè)參數(shù)是一個(gè)接口,特定是http.Handler。
    err := http.ListenAndServe(":8080", pair{})
    fmt.Println(err) // 不要無視錯(cuò)誤。
}

// 使pair實(shí)現(xiàn)http.Handler接口的ServeHTTP方法。
func (p pair) ServeHTTP(w http.ResponseWriter, r *http.Request) {
    // 使用http.ResponseWriter返回?cái)?shù)據(jù)
    w.Write([]byte("You learned Go in Y minutes!"))
}

更進(jìn)一步

Go的根源在Go官方網(wǎng)站。 在那里你可以學(xué)習(xí)入門教程,通過瀏覽器交互式地學(xué)習(xí),而且可以讀到很多東西。

強(qiáng)烈推薦閱讀語言定義部分,很簡(jiǎn)單而且很簡(jiǎn)潔!(as language definitions go these days.)

學(xué)習(xí)Go還要閱讀Go標(biāo)準(zhǔn)庫(kù)的源代碼,全部文檔化了,可讀性非常好,可以學(xué)到go,go style和go idioms。在文檔中點(diǎn)擊函數(shù)名,源代碼就出來了!

以上內(nèi)容是否對(duì)您有幫助:
在線筆記
App下載
App下載

掃描二維碼

下載編程獅App

公眾號(hào)
微信公眾號(hào)

編程獅公眾號(hào)