MENU

假装学会 Go 语言

2020 年 02 月 08 日 • 阅读: 3255 • 后端

花两天时间刷了 Go 语言指南,语法比 C、C++ 和 Java 等简洁,但 切片协程 等涉及底层的内容可能会有坑,先贴一下练习题答案,有机会写个项目练练手。本文写于 2020.2.8,随着时间推移,题目可能发生变更,以下内容仅供参考。

循环与函数

牛顿迭代法 求平方根,公式在题目描述里已经给出,直接 循环 即可。

/*
 * Copyright (c) 2020, LOGI. All rights reserved.
 * This file is created by LOGI at 6:31, 8/2/2020,
 * answer for https://tour.go-zh.org/flowcontrol/8
 */

package main

import (
    "fmt"
    "math"
)

func Sqrt(x float64) float64 {
    y := 1e-6
    z := x / 2
    for i := 1; ; i++ {
        j := z
        z -= (z*z - x) / (2 * z)
        if math.Abs(j-z) <= y {
            fmt.Printf("Loop counts: %v\n", i)
            break
        }
    }
    return z
}

func main() {
    fmt.Println("Result of customized Sqrt(2): ", Sqrt(2))
    fmt.Println("Result of builtin Sqrt(2)   : ", math.Sqrt(2))
}

切片

操作 二维切片 为图片像素点赋值,类似其他语言的 二维数组

/*
 * Copyright (c) 2020, LOGI. All rights reserved.
 * This file is created by LOGI at 6:55, 8/2/2020,
 * answer for https://tour.go-zh.org/moretypes/18
 */

package main

import "golang.org/x/tour/pic"

func Pic(dx, dy int) [][]uint8 {
    p := make([][]uint8, dy)
    for i := range p {
        p[i] = make([]uint8, dx)
        for j := range p[i] {
            p[i][j] = uint8(i * j)
        }
    }
    return p
}

func main() {
    pic.Show(Pic)
}

映射

统计一句话中每个单词出现的次数,提示使用 strings.Fields() 将句子分割成单词数组,Java 有类似工具类。

/*
 * Copyright (c) 2020, LOGI. All rights reserved.
 * This file is created by LOGI at 7:6, 8/2/2020,
 * answer for https://tour.go-zh.org/moretypes/23
 */

package main

import (
    "golang.org/x/tour/wc"
    "strings"
)

func WordCount(s string) map[string]int {
    ret := make(map[string]int)
    for _, w := range strings.Fields(s) {
        if ret[w] == 0 {
            ret[w] = 1
            continue
        }
        ret[w] += 1
    }
    return ret
}

func main() {
    wc.Test(WordCount)
}

斐波纳契闭包

闭包 下的 斐波纳契 数列实现,写过 JavaScript 应该很容易接受。

/*
 * Copyright (c) 2020, LOGI. All rights reserved.
 * This file is created by LOGI at 7:21, 8/2/2020,
 * answer for https://tour.go-zh.org/moretypes/26
 */

package main

import "fmt"

func fibonacci() func() int {
    ret := 0
    prev := -1
    next := 1
    return func() int {
        ret = prev + next
        prev = next
        next = ret
        return ret
    }
}

func main() {
    f := fibonacci()
    for i := 0; i < 10; i++ {
        fmt.Print(f()," ")
    }
}

Stringer

为类型实现 Stringer 接口,fmt.Print 系列方法会自动调用 String()Java 有类似特性。

/*
 * Copyright (c) 2020, LOGI. All rights reserved.
 * This file is created by LOGI at 7:27, 8/2/2020,
 * answer for https://tour.go-zh.org/methods/18
 */

package main

import (
    "fmt"
)

type IPAddr [4]byte

func (addr IPAddr) String() string {
    return fmt.Sprintf("%d.%d.%d.%d", addr[0], addr[1], addr[2], addr[3])
}

func main() {
    hosts := map[string]IPAddr{
        "loopback":  {127, 0, 0, 1},
        "googleDNS": {8, 8, 8, 8,},
    }
    for name, ip := range hosts {
        fmt.Printf("%v: %v\n", name, ip)
    }
}

错误

为类型实现 error 接口,fmt.Print 系列方法会自动调用 Error()

/*
 * Copyright (c) 2020, LOGI. All rights reserved.
 * This file is created by LOGI at 7:46, 8/2/2020,
 * answer for https://tour.go-zh.org/methods/20
 */

package main

import (
    "fmt"
    "math"
)

type ErrNegativeSqrt float64

func (e ErrNegativeSqrt) Error() string {
    return fmt.Sprintf("cannot Sqrt negative number: %v", float64(e))
}

func sqrt(x float64) (float64, error) {
    var err error = nil
    if x < 0 {
        err = ErrNegativeSqrt(x)
    }
    return math.Sqrt(x), err
}

func main() {
    fmt.Println(sqrt(2))
    fmt.Println(sqrt(-2))
}

Reader

为类型实现 Reader 接口,即添加 read 方法。

/*
 * Copyright (c) 2020, LOGI. All rights reserved.
 * This file is created by LOGI at 7:53, 8/2/2020,
 * answer for https://tour.go-zh.org/methods/22
 */

package main

import "golang.org/x/tour/reader"

type MyReader struct {
}

func (reader MyReader) Read(b []byte) (int, error) {
    for i := range b {
        b[i] = 'A'
    }
    return len(b), nil
}

func main() {
    reader.Validate(MyReader{})
}

rot13Reader

练习 io 流的包装,示例是实现 rot13 代换码Java 有类似设计。

/*
 * Copyright (c) 2020, LOGI. All rights reserved.
 * This file is created by LOGI at 8:2, 8/2/2020,
 * answer for https://tour.go-zh.org/methods/23
 */

package main

import (
    "io"
    "os"
    "strings"
)

type rot13Reader struct {
    r io.Reader
}

func (rot13 rot13Reader) Read(b []byte) (int, error) {
    c := make([]byte, len(b))
    n, err := rot13.r.Read(c)
    if err == io.EOF {
        return 0, io.EOF
    }
    for i := 0; i < n; i++ {
        if c[i] >= 'A' && c[i] <= 'Z' {
            b[i] = (c[i]-'A'+13)%26 + 'A'
        } else if c[i] >= 'a' && c[i] <= 'z' {
            b[i] = (c[i]-'a'+13)%26 + 'a'
        } else {
            b[i] = c[i]
        }
    }
    return n, nil
}

func main() {
    s := strings.NewReader("Lbh penpxrq gur pbqr!")
    r := rot13Reader{s}
    io.Copy(os.Stdout, &r)
}

图像

实现 Image 接口

type Image interface {
    // ColorModel returns the Image's color model.
    ColorModel() color.Model
    // Bounds returns the domain for which At can return non-zero color.
    // The bounds do not necessarily contain the point (0, 0).
    Bounds() Rectangle
    // At returns the color of the pixel at (x, y).
    // At(Bounds().Min.X, Bounds().Min.Y) returns the upper-left pixel of the grid.
    // At(Bounds().Max.X-1, Bounds().Max.Y-1) returns the lower-right one.
    At(x, y int) color.Color
}
/*
 * Copyright (c) 2020, LOGI. All rights reserved.
 * This file is created by LOGI at 8:3, 8/2/2020,
 * answer for https://tour.go-zh.org/methods/25
 */

package main

import (
    "golang.org/x/tour/pic"
    "image"
    "image/color"
)

type Image struct {
    w, h int
}

func (img Image) ColorModel() color.Model {
    return color.RGBAModel
}

func (img Image) Bounds() image.Rectangle {
    return image.Rect(0, 0, img.w, img.h)
}

func (img Image) At(x, y int) color.Color {
    v := uint8(x + y)
    return color.RGBA{v, v, 255, 255}
}

func main() {
    m := Image{100, 100}
    pic.ShowImage(m)
}

等价二叉查找树

遍历二叉树,并用 协程 检测两棵树是否相等。

/*
 * Copyright (c) 2020, LOGI. All rights reserved.
 * This file is created by LOGI at 8:4, 8/2/2020,
 * answer for https://tour.go-zh.org/concurrency/8
 */

package main

import (
    "fmt"
    "golang.org/x/tour/tree"
)

func Walk(t *tree.Tree, ch chan int) {
    if t == nil {
        return
    }
    Walk(t.Left, ch)
    ch <- t.Value
    Walk(t.Right, ch)
}

func Same(t1, t2 *tree.Tree) bool {
    ch1 := make(chan int)
    ch2 := make(chan int)
    go Walk(t1, ch1)
    go Walk(t2, ch2)
    for i := 0; i < 10; i++ {
        if <-ch1 != <-ch2 {
            return false
        }
    }
    return true
}

func main() {
    ch := make(chan int)
    go Walk(tree.New(1), ch)
    for i := 0; i < 10; i++ {
        fmt.Print(<-ch, " ")
    }
    fmt.Println()
    fmt.Println(Same(tree.New(1), tree.New(1)))
    fmt.Println(Same(tree.New(1), tree.New(2)))
}

Web 爬虫

模拟 多线程 爬取网页内容,要求重复链接只爬一次,提示用 map 保存,关键是让主线程 等待 协程结束。

/*
 * Copyright (c) 2020, LOGI. All rights reserved.
 * This file is created by LOGI at 8:4, 8/2/2020,
 * answer for https://tour.go-zh.org/concurrency/10
 */

package main

import (
    "fmt"
    "sync"
)

type Fetcher interface {
    Fetch(url string) (body string, urls []string, err error)
}

type FetchMask struct {
    result map[string]bool
    sync.Mutex
}

func (mask *FetchMask) GetMask(url string) bool {
    mask.Lock()
    defer mask.Unlock()
    return mask.result[url]
}

func (mask *FetchMask) SetMask(url string, result bool) {
    mask.Lock()
    defer mask.Unlock()
    mask.result[url] = result
}

var mask = &FetchMask{result: make(map[string]bool),}

func Crawl(url string, depth int, fetcher Fetcher, ch chan int) {
    if ch != nil {
        defer func() {
            ch <- 1
        }()
    }

    if depth <= 0 || mask.GetMask(url) {
        return
    }

    body, urls, err := fetcher.Fetch(url)
    mask.SetMask(url, true)

    if err != nil {
        fmt.Println(err)
        return
    }

    fmt.Printf("found: %s %q\n", url, body)

    ch1 := make(chan int)
    for _, u := range urls {
        go Crawl(u, depth-1, fetcher, ch1)
    }
    for range urls {
        <-ch1
    }
    return
}

func main() {
    Crawl("https://golang.org/", 4, fetcher, nil)
}

type fakeFetcher map[string]*fakeResult
type fakeResult struct {
    body string
    urls []string
}

func (f fakeFetcher) Fetch(url string) (string, []string, error) {
    if res, ok := f[url]; ok {
        return res.body, res.urls, nil
    }
    return "", nil, fmt.Errorf("not found: %s", url)
}

var fetcher = fakeFetcher{
    "https://golang.org/": &fakeResult{
        "The Go Programming Language",
        []string{
            "https://golang.org/pkg/",
            "https://golang.org/cmd/",
        },
    },
    "https://golang.org/pkg/": &fakeResult{
        "Packages",
        []string{
            "https://golang.org/",
            "https://golang.org/cmd/",
            "https://golang.org/pkg/fmt/",
            "https://golang.org/pkg/os/",
        },
    },
    "https://golang.org/pkg/fmt/": &fakeResult{
        "Package fmt",
        []string{
            "https://golang.org/",
            "https://golang.org/pkg/",
        },
    },
    "https://golang.org/pkg/os/": &fakeResult{
        "Package os",
        []string{
            "https://golang.org/",
            "https://golang.org/pkg/",
        },
    },
}
TG 大佬群 QQ 大佬群 QQ 追番群

最后编辑于: 2020 年 02 月 10 日
返回文章列表 文章二维码
本页链接的二维码
打赏二维码
添加新评论

已有 2 条评论
  1. Darren.Li Darren.Li     MacOS /    Google Chrome

    两天刷完怎么做到了?果真没有不可能@(大拇指)

    1. LOGI LOGI     Windows 10 /    Google Chrome

      @Darren.Li因为没往深看,只是几页 hello world @(你懂的)

0:00