Go

Installation

$ go get golang.org/dl/go1.17.3
$ ~/go/bin/go1.17.3 download
$ ln -s ~/go/bin/go1.17.3 ~/go/bin/go
$ go env

Slice

index

slice 允许 index 到 len(s) 而不报错:

func ExampleIndex() {
    a := []int{}
    b := []int{0}
    var c []int
    fmt.Println(a == nil)
    fmt.Println(c == nil)
    fmt.Println(a[0:])
    fmt.Println(b[1:])
    fmt.Println(c[0:])
    fmt.Println(a[1:]) // panic: runtime error: slice bounds out of range
    // Output:
    // false
    // true
    // []
    // []
    // []
}

make

make() 如果 len 参数不为 0,返回的 slice 是有零值填充的。

func ExampleMake() {
    a := make([]int, 4)
    b := make([]int, 0, 4)
    a = append(a, 1)
    b = append(b, 1)
    fmt.Println(a)
    fmt.Println(b)
    // Output:
    // [0 0 0 0 1]
    // [1]
}

append

append() 总是会返回一个新的 slice,length 和原来的不同。

func ExampleAppend() {
    a := make([]int, 0, 10)
    b := a
    a = append(a, 1)
    fmt.Println(a)
    fmt.Println(b)
    // Wrong Output:
    // [1]
    // [1] ✗

    // Output:
    // [1]
    // []

    // Javascript array:
    // a = [1, 2, 3]
    // b = a
    // a.push(4)
    // console.log(a)
    // console.log(b)

    // Javascript Output:
    // [1, 2, 3, 4]
    // [1, 2, 3, 4]
}

modify

修改 slice 元素会修改 underlying array,而且不会返回一个新的 slice。

func ExampleModify() {
    a := []int{1, 2, 3}
    b := a
    fmt.Println(a)
    fmt.Println(b)
    a[0] = 0
    fmt.Println(a)
    fmt.Println(b)
    // Output:
    // [1 2 3]
    // [1 2 3]
    // [0 2 3]
    // [0 2 3]
}

reverse

func reverse(s []int) {
    for i, j := 0, len(s)-1; i < j; i, j = i+1, j-1 {
        s[i], s[j] = s[j], s[i]
    }
}

func ExampleReverse() {
    s := []int{0, 1, 2, 3, 4}
    reverse(s)
    fmt.Println(s)
    // Output:
    // [4 3 2 1 0]
}

rotate

func ExampleRotate() {
    // Rotate s left by two positions.
    s := []int{0, 1, 2, 3, 4}
    reverse(s[:2])
    reverse(s[2:])
    reverse(s)
    fmt.Println(s)
    // Output:
    // [2 3 4 0 1]
}

Channels

One-to-one dead lock

func main() {
    chA := make(chan int)

    go func() { chA <- 0 }()

    for a := range chA {
        fmt.Println("A:", a)
        time.Sleep(time.Second * 1)
        chA <- a + 1
    }
}

One-to-one

func main() {
    chA := make(chan int)

    go func() { chA <- 0 }()

    for a := range chA { // consume one
        fmt.Println("A:", a)
        time.Sleep(time.Second * 1)
        go func(a int) {
            chA <- a + 1 // produce one
        }(a)
    }
}

One-to-many dead lock

func main() {
    chA := make(chan int)

    go func() { ChA <- 0 }()

    for a := range chA {
        fmt.Println("A:", a)
        for i := 0; i < 10; i++ {
            chA <- i
        }
    }
}

One-to-many

func main() {
    chA := make(chan int)

    go func() { ChA <- 0 }()

    for a := range chA { // consume one
        fmt.Println("A:", a)
        for i := 0; i < 10; i++ { // produce many
            go func(i int) {
                chA <- i // no sequence guaranteed
            }(i)
        }
    }
}

Two channels: One-to-one

func main() {
    chA := make(chan int)
    chB := make(chan int)

    go func() { chB <- 0 }()

    go func() {
        for a := range chA { // consume one
            time.Sleep(time.Second * 1)
            fmt.Println("A:", a)
            chB <- a + 1 // produce one
        }
    }()

    for b := range chB { // consume one
        fmt.Println("B:", b)
        chA <- b + 1 // produce one
    }
}

Two channels: One-to-many dead lock

func main() {
    chA := make(chan int)
    chB := make(chan int)

    go func() { chB <- 0 }()

    go func() {
        for a := range chA {
            time.Sleep(time.Second * 1)
            fmt.Println("A:", a)
            chB <- a
        }
    }()

    for b := range chB {
        fmt.Println("B:", b)
        for i := 0; i < 10; i++ {
            chA <- i
        }
    }
}

Two channels: One-to-many

func main() {
    chA := make(chan int)
    chB := make(chan int)

    go func() { chB <- 0 }()

    go func() { // worker goroutine
        for a := range chA {
            time.Sleep(time.Second * 1)
            fmt.Println("A:", a)
            go func(a int) { // goroutines accumulated
                chB <- a // no sequence guaranteed
            }(a)
        }
    }()

    for b := range chB {
        fmt.Println("B:", b)
        for i := 0; i < 10; i++ {
            chA <- i // sequence guaranteed
        }
    }
}

One-to-many with multiple worker goroutines

func main() {
    chA := make(chan int)
    chB := make(chan int)

    go func() { chB <- 0 }()

    for i := 0; i < 20; i++ {
        go func() {
            for a := range chA {
                time.Sleep(time.Second * 1) // simulate real work
                fmt.Println("A:", a)
                go func(a int) { // goroutine accumulation
                    chB <- a
                }(a)
            }
        }()
    }

    for b := range chB { // consume one
        fmt.Println("B:", b)
        for i := 0; i < 10; i++ { // produce many
            chA <- i
        }
    }
}

One-to-many with counting semaphore

func main() {
    worklist := make(chan []string)

    go func() { worklist <- os.Args[1:] }()

    seed := make(map[string]bool)
    for list := range worklist { // consume one
        for _, link := range list {
            if !seen[link] {
                seed[link] = true
                go func(link string) { // goroutine accumulation
                    worklist <- crawl(link) // produce many
                }(link)
            }
        }
    }
}

var tokens = make(chan struct{}, 20)

func crawl(url string) []string {
    tokens <- struct{}{} // acquire a token
    list, err := links.Extract(url)
    <-tokens // release the token
    if err != nil {
        log.Printf(err)
    }
    return list
}