完成《A Tour of Go》的学习

我花了大约一个月的时间完成了Go语言的教程《A Tour of Go》。我使用的执行环境只是该网站内的playground工具。

借鉴的内容

    • How to Write Go Code

 

    • WEB+DB PRESS vol.82 はじめてのGo

 

    WEB+DB PRESS vol.95 Goによる並行処理

以下是各个问题所写的代码和笔记。

2016年11月13日开始

值得纪念的开始学习Go的一天。包。

2016年11月23日的課題是關於「For循環」和「牛頓法」。

处理牛顿法的问题。看到这个方程式突然就觉得糟糕。尽力获得正确的输出结果,但是却不使用for循环,而强行使用递归处理和全局变量等,无法理解出题者的意图…

package main

import (
     "fmt"
     "math"
)

var z = 1.0
var cnt = 0

func Sqrt(x float64) float64 {
     zbuf := z
     z = z - (z*z-x)/(2*z)
     fmt.Println(cnt, z)
     if math.Abs(zbuf-z) < 0.000001 {
          return z
     }
     cnt++
     return Sqrt(x)
}

func main() {
     fmt.Println(Sqrt(3))
     fmt.Println(math.Sqrt(3))
}

输出结果

0 2
1 1.75
2 1.7321428571428572
3 1.7320508100147276
4 1.7320508075688772
1.7320508075688772
1.7320508075688772

2016年11月26日的作业:切片

练习:切片
这个也很难,但是若有所思之后就做出来了。当出现漂亮的图像时,感到非常激动。

package main

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

func Pic(dx, dy int) [][]uint8 {
     pic := make([][]uint8, dy)
     for y := 0; y < dy; y++ {
          pic[y] = make([]uint8, dx)
          for x := 0; x < dx; x++ {
               pic[y][x] = uint8((x+y)/2)
          }

     }
     return pic
}

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

2016年11月26日的問題:地圖

练习:地图
地图的任务。在查看API参考文档的同时编写了代码。总算做完了。但是当我回头看代码时却搞不清楚它在做什么。

package main

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

func WordCount(s string) map[string]int {
    sf := strings.Fields(s)
    fmt.Println(sf)

    smap := make(map[string]int)
    for _, elm := range sf {
        fmt.Println(elm)
        smap[elm] = smap[elm] + 1
    }
    return smap
}

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

输出结果

[I am learning Go!]
I
am
learning
Go!
PASS
 f("I am learning Go!") = 
  map[string]int{"I":1, "am":1, "learning":1, "Go!":1}
[The quick brown fox jumped over the lazy dog.]
The
quick
brown
fox
jumped
over
the
lazy
dog.
PASS
 f("The quick brown fox jumped over the lazy dog.") = 
  map[string]int{"jumped":1, "the":1, "The":1, "quick":1, "brown":1, "fox":1, "over":1, "lazy":1, "dog.":1}
[I ate a donut. Then I ate another donut.]
I
ate
a
donut.
Then
I
ate
another
donut.
PASS
 f("I ate a donut. Then I ate another donut.") = 
  map[string]int{"another":1, "I":2, "ate":2, "a":1, "donut.":2, "Then":1}
[A man a plan a canal panama.]
A
man
a
plan
a
canal
panama.
PASS
 f("A man a plan a canal panama.") = 
  map[string]int{"man":1, "a":2, "plan":1, "canal":1, "panama.":1, "A":1}

2016/11/27的课题:闭包/斐波那契数列。

练习:斐波那契数列
记得当初我曾费很大劲,但最终成功了。如今回过头再看这段代码,仍然有些搞不明白(汗)

package main

import "fmt"

// fibonacci is a function that returns
// a function that returns an int.
func fibonacci() func() int {
     var f []int
     return func() int {
          switch len(f) {
          case 0:
               f = append(f, 0)
          case 1:
               f = append(f, 1)
          default:
               f = append(f, (f[len(f)-2] + f[len(f)-1]))
          }
          return f[len(f)-1]
     }
}

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

输出结果 (Chū lì jié guǒ)

0
1
1
2
3
5
8
13
21
34

2016年12月11日的课题是:错误。

练习:错误
我记不太清楚了。大致上,我认为做得不错。

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 ErrNegativeSqrt
    if x < 0 {
        err = ErrNegativeSqrt(x)
        return 0, err
    }

    last_z, z := x, 1.0
    for math.Abs(z-last_z) >= 1.0e-6 {
        last_z, z = z, z-(z*z-x)/(2*z)
    }
    return z, nil
}

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

输出结果 (Chū lì jié guǒ)

1.4142135623730951 <nil>
0 cannot Sqrt negative number: -2

2016年12月11日的课题是《读者》。

练习:读者
请实现一个输出无限流ASCII字符 ‘A’ 的Reader类型。这个任务有点模糊不清,我不太记得了,不过好像做出来了。

package main

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

type MyReader struct{}

// TODO: Add a Read([]byte) (int, error) method to MyReader.
func (r MyReader) Read(b []byte) (int, error) {
    for {
        copy(b,"A")
        return 1, nil
    }
}

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

输出结果

OK!

2016/12/11问题:将Reader进行包裹/rot13Reader

这是放弃了。无法完成。练习:rot13Reader。

2016年12月14日的課題是與Image介面有關。

练习:图像
虽然记不太清楚了,但是我通过查看API参考文档来模仿写出来的,结果成功了。

package main

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

type Image struct{}

// ColorModel returns the Image's color model.
func (img Image) ColorModel() color.Model {
    return color.RGBAModel
}

// Bounds returns the domain for which At can return non-zero color.
// The bounds do not necessarily contain the point (0, 0).
func (img Image) Bounds() image.Rectangle {
    return image.Rect(0, 0, 100, 100)
}

// 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.
func (img Image) At(x, y int) color.Color {
    return color.RGBA{0, 0, 255, 255}
}
func main() {
    m := Image{}
    pic.ShowImage(m)
}
ImageInterface.png

2016年12月20日的课题是并行处理/二分树。

终于到了任务中最关键的并发处理问题。我花了大约三天时间来解决这个问题,最终还是放弃了。虽然我已经接近找到解决方法,但是非常沮丧,所以我在另一篇文章中详细写了这个情况。

2016年12月23日,最后的任务:并行处理/网络爬虫。

虽然不容易,但通过参考杂志文章和教程,我做到了。

package main

import (
    "fmt"
    "sync"
)

type Fetcher interface {
    // Fetch returns the body of URL and
    // a slice of URLs found on that page.
    Fetch(url string) (body string, urls []string, err error)
}

// Crawl uses fetcher to recursively crawl
// pages starting with url, to a maximum of depth.
func Crawl(url string, depth int, fetcher Fetcher) {
    c := SafeCounter{v: make(map[string]int)}
    ch := make(chan string)
    go InnerCrawl(url, depth, fetcher, ch , &c)
    fmt.Println("<-ch", <-ch)
    return
}

func InnerCrawl(url string, depth int, fetcher Fetcher, ch chan string, c *SafeCounter) {
    c.Inc(url)
    cnt := c.Value(url)
    if cnt > 1 {
        fmt.Printf("duplicate cnt:%v url:%s\n",cnt, url)
        ch <- url
        return
    }

    fmt.Println("depth:",depth)
    if depth <= 0 {
        ch <- url
        return
    }

    body, urls, err := fetcher.Fetch(url)
    if err != nil {
        fmt.Println(err)
        ch <- url
        return
    }

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

    fmt.Printf("len(urls):%v\n", len(urls))
    ich := make(chan string)
    for _, u := range urls {
        go InnerCrawl(u, depth-1, fetcher, ich, c)
    }

    for i := 0; i < len(urls); i++ {
        fmt.Println("<-ich", <-ich)
    }

    ch <- url
    return
}

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

// fakeFetcher is Fetcher that returns canned results.
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)
}

// fetcher is a populated fakeFetcher.
var fetcher = fakeFetcher{
    "http://golang.org/": &fakeResult{
        "The Go Programming Language",
        []string{
            "http://golang.org/pkg/",
            "http://golang.org/cmd/",
        },
    },
    "http://golang.org/pkg/": &fakeResult{
        "Packages",
        []string{
            "http://golang.org/",
            "http://golang.org/cmd/",
            "http://golang.org/pkg/fmt/",
            "http://golang.org/pkg/os/",
        },
    },
    "http://golang.org/pkg/fmt/": &fakeResult{
        "Package fmt",
        []string{
            "http://golang.org/",
            "http://golang.org/pkg/",
        },
    },
    "http://golang.org/pkg/os/": &fakeResult{
        "Package os",
        []string{
            "http://golang.org/",
            "http://golang.org/pkg/",
        },
    },
}

// SafeCounter is safe to use concurrently.
type SafeCounter struct {
    v   map[string]int
    mux sync.Mutex
}

// Inc increments the counter for the given key.
func (c *SafeCounter) Inc(key string) {
    c.mux.Lock()
    // Lock so only one goroutine at a time can access the map c.v.
    c.v[key]++
    c.mux.Unlock()
}

// Value returns the current value of the counter for the given key.
func (c *SafeCounter) Value(key string) int {
    c.mux.Lock()
    // Lock so only one goroutine at a time can access the map c.v.
    defer c.mux.Unlock()
    return c.v[key]
}

输出结果

found: http://golang.org/ "The Go Programming Language"
not found: http://golang.org/cmd/
<-ich http://golang.org/cmd/
found: http://golang.org/pkg/ "Packages"
found: http://golang.org/pkg/os/ "Package os"
duplicate cnt:2 url:http://golang.org/pkg/
<-ich http://golang.org/pkg/
duplicate cnt:2 url:http://golang.org/
<-ich http://golang.org/
duplicate cnt:2 url:http://golang.org/cmd/
<-ich http://golang.org/cmd/
found: http://golang.org/pkg/fmt/ "Package fmt"
duplicate cnt:3 url:http://golang.org/pkg/
<-ich http://golang.org/pkg/
duplicate cnt:3 url:http://golang.org/
<-ich http://golang.org/
<-ich http://golang.org/pkg/os/
duplicate cnt:4 url:http://golang.org/
<-ich http://golang.org/
<-ich http://golang.org/pkg/fmt/
<-ich http://golang.org/pkg/
<-ch http://golang.org/

总结

这就结束了。如果写了奇怪的代码,请指出来,谢谢你。

Playground保存了代码,非常方便。但是在写这篇文章的过程中,回顾过去的代码时发现完全没有记忆,所以总体而言

让我们留下评论。

以上、我在现场(所见)的回报。

bannerAds