Go 語言的世界 – Go的語言特性 – 優點



Go 語言的世界 – Go的語言特性 – 優點

0 0


go-intro


On Github roth1002 / go-intro

Go

Introduce the Go Programming Language

Presented by Roth / @彭煜釗

Go 語言的世界

宏觀, 安裝, 配置, 工程命令, 標準命令, 語法

開放設計Logo

Go的語言特性

開源

意味著版本與開發速度上的快速, 回顧看看C/C++ JAVA的歷史, 他們起初就不是開源

靜態 編譯型語言

降低直譯式語言所需要的型別檢查成本

跨平台?

其實叫做跨平台支援......

Garbage Collection

自動, 甚至允許手動

併發 Concurrency

可以先理解成比C語言的pthread輕量幾十倍的執行緒

goroutine, channel(後面會提)

物件導向?函數編程?

同時具備 物件導向 函數編程

物件導向

無繼承層次的輕量級物件導向

非侵入式接口(利於測試)

函數編程

函數類型叫做第一等類性(後面會說明)

惰性求值

Unication of Coding Style

優點

Compare with C/C++?

依賴管理明確

gc的能力使你不用過度干涉記憶體管理 大幅降低原始碼長度 開發效率提高

Compare with Java?

還要我說嗎?(去用Scala吧)

Compare with php?

我看$不爽可以吧XD

Compare with python/ruby?

效能

缺點

Compare with C/C++?

運行速度還不及

第三方函式庫的數量較少

不支援自已定義的泛型類型

併發其實有潛規則要遵守

不然等著創造坑自己踩進去吧

垃圾回收的改進

Hello World

package main

import "fmt"

func main() {
    fmt.Printf("Hello World!\n")
}

變數宣告

var x string = "Hello World"

var y string
y = "Hello World"

z := "Hello World"

流程控制

    // for
    i := 1
    for i <= 10 {
        fmt.Println(i)
        i = i + 1
    }

    // also for
    for i := 1; i <= 10; i++ {
        fmt.Println(i)
    }

    // add if
    for i := 1; i <= 10; i++ {
        if i%2 == 0 {
            fmt.Println(i, "even")
        } else {
            fmt.Println(i, "odd")
        }
    }

    // switch
    switch i {
    case 0:
        fmt.Println("Zero")
    case 1:
        fmt.Println("One")

陣列

    // int array
    var x [5]int
    x[4] = 100
    fmt.Println(x) //auto fill 0 with non-assignment

    // go through array
    var y [5]float64
    y[0] = 98
    y[1] = 93
    y[2] = 77
    y[3] = 82
    y[4] = 83
    var total float64 = 0
    for i := 0; i < len(y); i++ {
        total += y[i]
    }
    fmt.Println("Total =", total)
    fmt.Println("Average =", total/float64(len(y))) //Mind len(y) is int

    // slice不固定大小的陣列( have append copy function....etc)
    z := make([]float64, 4, 10)
    fmt.Println(z)

    fmt.Println(y[1:4]) // same as python

    //append
    fmt.Println(append(z, 100, 200, 300, 400, 555, 666, 777, 888))

    // Map, need memory malloc by using make
    elements := make(map[string]string)
    elements["H"] = "Hydrogen"
    elements["He"] = "Helium"
    elements["Li"] = "Lithium"
    elements["Be"] = "Beryllium"
    elements["B"] = "Boron"
    elements["C"] = "Carbon"
    elements["N"] = "Nitrogen"
    elements["O"] = "Oxygen"
    elements["F"] = "Fluorine"
    elements["Ne"] = "Neon"

    fmt.Println(elements["Li"])
    fmt.Println(elements)

    // add if
    name, ok := elements["Li"]
    fmt.Println(name, ok)

    // Map in Map
    people := map[string]map[string]string{
        "H": map[string]string{
            "name":  "Hydrogen",
            "state": "gas",
        },
        "He": map[string]string{
            "name":  "Helium",
            "state": "gas",
        },
        "Li": map[string]string{
            "name":  "Lithium",
            "state": "solid",
        },
        "Be": map[string]string{
            "name":  "Beryllium",
            "state": "solid",
        },
        "B": map[string]string{
            "name":  "Boron",
            "state": "solid",
        },
        "C": map[string]string{
            "name":  "Carbon",
            "state": "solid",
        },
        "N": map[string]string{
            "name":  "Nitrogen",
            "state": "gas",
        },
        "O": map[string]string{
            "name":  "Oxygen",
            "state": "gas",
        },
        "F": map[string]string{
            "name":  "Fluorine",
            "state": "gas",
        },
        "Ne": map[string]string{
            "name":  "Neon",
            "state": "gas",
        },
    }

    if el, ok := people["Li"]; ok {
        fmt.Println(el["name"], el["state"])
        fmt.Println(el)

Function

Normal

func average(scores []float64) (float64) {
    total := 0.0
    for _, value := range scores {
        total += value
    }
    return total / float64(len(scores))
}

多個input參數

func add(arguments ...int) int {
    total := 0
    for _, value := range arguments {
        total += value
    }
    return total
}

Generator

func makeEvenGenerator() func() uint {
    i := uint(0)
    fmt.Println("this only be called once")
    return func() (ret uint) {
        ret = i
        i += 2
        return
    }
}

遞迴

func factorial(x uint) uint {
    if x == 0 {
        return 1
    }
    return x * factorial(x-1)
}

defer

func first() {
    fmt.Println("first")
}

func second() {
    fmt.Println("second")
}

func main() {
    defer first()
    second()
}

//output:
//second
//first

指標

傳參數

func zero(x int) {
    x = 0
}

func main() {
    x := 5
    zero(x)
    fmt.Println(x) // x is still 5
}

傳參考

func zeroByRef(xPtr *int) {
    *xPtr = 0
}

func main() {
    x := 5
    zeroByRef(&x)
    fmt.Println(x) // x change to 0
}

傳指標變數

func two(xPtr *int) {
    *xPtr = 2
}

func main() {
    xPtr := new(int)
    two(xPtr)
    fmt.Println(*xPtr) //xPtr is 2
}

交換變數的例子

func swap(x *int, y *int) {
    zPtr := new(int)
    *zPtr = *x
    *x = *y
    *y = *zPtr
}

func main() {
    var (
        a = 2
        b = 1
    )
    swap(&a, &b)
    fmt.Println("a =", a, "b =", b)
}

Go千萬不要這樣實作

Go的變數參考可以略過那個tmp

func main() {
    var (
        a = 2
        b = 1
    )
    a, b = b, a
    fmt.Println("a =", a, "b =", b)
}

Concurrency

Goroutine

func f(n int) {
    for i := 0; i < 10; i++ {
        fmt.Println(n, ":", i)
        amt := time.Duration(rand.Intn(250))
        time.Sleep(time.Millisecond * amt)
    }
}

func main() {
    for i := 0; i < 10; i++ {
        go f(i)
    }
    var input string
    fmt.Scanln(&input)
}

Goroutine IPC with channel

func tim(tim chan<- string) { // 傳入channal 然後一直將 "tim" send進去 tim這個channal
    for i := 1; ; i++ { // go's while ( true ) in C lang
        tim <- "tim"
    }
}

func randy(randy chan<- string) { //可以將傳入方向放在argument宣告  方便讀懂code  如果是雙向都要的時候就不指定方向
    for i := 0; ; i++ {
        randy <- "randy"
    }
}

func receiveChannalAndPrint(leo <-chan string) { // 傳入 channal 然後每秒接收channal的內容
    for { // another go's while ( true ) in C lang
        msg := <-leo
        fmt.Println(msg)
        time.Sleep(time.Second * 1)
    }
}

func main() {
    var c chan string = make(chan string)
    go tim(c) //相當於發起thread
    go randy(c)
    go receiveChannalAndPrint(c)
    var input string
    fmt.Scanln(&input) //故意讓主程式停住
}

Goroutine with Bufferd channel

    chan1 := make(chan string, 10)
    chan2 := make(chan string, 10)

    go func() {
        for {
            chan1 <- "from tim"
            time.Sleep(time.Second * 1)
        }
    }() //很像Javascript吧XD

    go func() {
        for {
            chan2 <- "from randy"
            time.Sleep(time.Second * 1)
        }
    }()

    go func() {
        for {
            select {
            case msg1 := <-chan1:
                fmt.Println("message 1", msg1)
            case msg2 := <-chan2:
                fmt.Println("message 2", msg2)
            case <-time.After(time.Second):
                fmt.Println("timeout")
            default:
                fmt.Println("nothing")
                time.Sleep(time.Second * 1)
            }
        }
    }()

Package

使用自定義/內建/第三方函式庫

使用自己的要注意目錄結構(除非你另外放在github上import)

package math

package math

// Finds the average of a series of numbers
func Average(xs []float64) float64 {
    total := float64(0)
    for _, x := range xs {
        total += x
    }
    return total / float64(len(xs))
}

package main

package main

import (
    "./math"
    "fmt"
)

func main() {
    xs := []float64{1, 2, 3, 4}
    avg := math.Average(xs)
    fmt.Println(avg)
}

測試

沒有繼承問題

沒有侵入式接口問題

沒有非同步無法測試的問題(Javascript有實做一些方式幫助你處理)

方式你可能覺得跟Javascript超像

沒錯!! 因為它帶了Javascript的優良部份過來

Google提供的內建測試包 testing

說實在我沒有很喜歡XD

因為不太直觀

average.go

package math

// Finds the average of a series of numbers
func Average(xs []float64) float64 {
    total := float64(0)
    for _, x := range xs {
        total += x
    }
    return total / float64(len(xs))
}

average_test.go

package math

import "testing"

type testpair struct {
    values  []float64
    average float64
}

var tests = []testpair{
    {[]float64{1, 2}, 1.5},
    {[]float64{1, 1, 1, 1, 1, 1}, 1},
    {[]float64{-1, 1}, 0},
}

func TestAverage(t *testing.T) {
    for _, pair := range tests {
        v := Average(pair.values)
        if v != pair.average {
            t.Error(
                "For", pair.values,
                "expected", pair.average,
                "got", v,
            )
        }
    }
}

Open Source的好處來了

跳坑玩家提供

https://github.com/stretchr/testify

add.go

package math

// Finds the sum of a series of numbers
func Add(xs []float64) float64 {
    total := float64(0)
    for _, x := range xs {
        total += x
    }
    return total
}

add_test.go

package math

import (
    "github.com/stretchr/testify/assert"
    "testing"
)

type addtestpair struct {
    values []float64
    sum    float64
}

var addtests = []addtestpair{
    {[]float64{1, 2}, 3},
    {[]float64{1, 1, 1, 1, 1, 1}, 6},
    {[]float64{-1, 1}, 0},
    {[]float64{1.1, 2.3}, 3.4},
}

func TestAdd(t *testing.T) {
    for _, pair := range addtests {
        v := Add(pair.values)
        assert.Equal(t, v, pair.sum, "they should be equal")
    }
}

上次Google的朋友來提到的字串相加

就照著Google提供的規範走吧

package main

import (
    "fmt"
    "strings"
)

func main() {
    fmt.Println(
        // true
        strings.Contains("test", "es"),

        // 2
        strings.Count("test", "t"),

        // true
        strings.HasPrefix("test", "te"),

        // true
        strings.HasSuffix("test", "st"),

        // 1
        strings.Index("test", "e"),

        // "a-b"
        strings.Join([]string{"a", "b"}, "-"),

        // == "aaaaa"
        strings.Repeat("a", 5),

        // "bbaa"
        strings.Replace("aaaa", "a", "b", 2),

        // []string{"a","b","c","d","e"}
        strings.Split("a-b-c-d-e", "-"),

        // "test"
        strings.ToLower("TEST"),

        // "TEST"
        strings.ToUpper("test"),
    )
}

End

Go Introduce the Go Programming Language Presented by Roth / @彭煜釗