截至2017年4月18日的A Tour of Go练习

机会

因为无缘无故地翻阅了一下A Tour of Go的练习题目,所以决定顺便留下来做一做。
但事先声明一下,在学习sync包之前已经使用过它了。

练习:循环和函数

    • exercise-loops-and-functions.go

https://play.golang.org/p/tS2F5Fsf_2

本当はEPS(計算機イプシロン)でやるべきなのかも?
https://play.golang.org/p/XOXwIdNfsa

math.Sqrt()を使わないだけでmathパッケージは使っちゃってる(本質とは関係ないから良いかなって)

package main

import (
    "fmt"
    "math"
)

// DELTA ...
const DELTA = 1e-10

// Sqrt is Square root using Newton's law
func Sqrt(x float64) float64 {
    zn0, zn1 := float64(0), float64(1)
    for math.Abs(zn1-zn0) > DELTA {
        zn0, zn1 = zn1, zn1-((math.Pow(zn1, 2)-x)/(2*zn1))
    }
    return zn1
}

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

运动:切片

    • exercise-slices.go

 

    https://play.golang.org/p/oR5GyfbMrJ
package main

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

// Pic https://go-tour-jp.appspot.com/moretypes/18
func Pic(dx, dy int) [][]uint8 {
    data := make([][]uint8, dy)
    for y := range data {
        data[y] = make([]uint8, dx)
        for x := range data[y] {
            data[y][x] = uint8((x + y) / 2)
        }
    }
    return data
}

func main() {
    pic.Show(Pic)
}
    • ちなみに画像は以下のようにすれば

    • に変換できた

base64エンコードされてるだけ
参照: http://d.hatena.ne.jp/moogme/20090814/p3

$ go run exercise-slices.go | sed -e 's/IMAGE:\(.*\)/<img src="data:image\/png;base64,\1">/g'
<img src="data:image/png;base64,iVBORw0KGgo.....ggg==">

运动:地图

    • exercise-maps.go

https://play.golang.org/p/6mCsDYvIra

strings.Fields() はまさに単語区切りにしてくれる関数

package main

import (
    "strings"

    "golang.org/x/tour/wc"
)

// WordCount https://go-tour-jp.appspot.com/moretypes/23
func WordCount(s string) map[string]int {
    result := map[string]int{}
    for _, word := range strings.Fields(s) {
        result[word]++
    }
    return result
}

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

练习:斐波那契闭包

    • exercise-fibonacci-closure.go

https://play.golang.org/p/8VDvbbq17G

これで実現できるけど、i0 := -1とか逆に繰り返してみただけで、どこから出てきたの?って感じだから良いのかな…

if文でn=0とn=1は実現した方が正しいかもしれない

package main

import "fmt"

// fibonacci is a function that returns
// a function that returns an int.
func fibonacci() func() int {
    i0, i1 := -1, 1
    return func() int {
        i0, i1 = i1, (i0 + i1)
        return i1
    }
}

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

锻炼:绳索

    • exercise-stringer.go

https://play.golang.org/p/8PrLYNy3Jn

問題の難しさが1ランク下がった感あってとっても不安だった(こんなんで良いの?みたいな)

package main

import (
    "fmt"
)

// IPAddr https://go-tour-jp.appspot.com/methods/18
type IPAddr [4]byte

// TODO: Add a "String() string" method to IPAddr.
func (ip IPAddr) String() string {
    return fmt.Sprintf("%d.%d.%d.%d", ip[0], ip[1], ip[2], ip[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)
    }
}

运动:错误

    • exercise-errors.go

https://play.golang.org/p/mMSJpKfY5K

fmt.Sprint() は確かに無限ループなんだけど、別にfmt.Sprintf() にしちゃえば良いのではと思った

“%v”を第一引数に指定したら同じことが起きるしバグの元だから駄目ですかね

package main

import (
    "fmt"
    "math"
)

// ErrNegativeSqrt https://go-tour-jp.appspot.com/methods/20
type ErrNegativeSqrt float64

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

// Sqrt is Square root using Newton's law
func Sqrt(x float64) (float64, error) {
    if x < 0 {
        return 0, ErrNegativeSqrt(x)
    }

    zn0 := float64(0)
    zn1 := float64(1)
    for math.Abs(zn1-zn0) > 1e-10 {
        zn0, zn1 = zn1, zn1-((math.Pow(zn1, 2)-x)/(2*zn1))
    }
    return zn1, nil
}

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

锻炼:读者们

    • exercise-reader.go

https://play.golang.org/p/5c4aTf2P3Z

byte(‘A’)ってやらなくても’A’でOK!もらえちゃうんだよね

package main

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

// MyReader https://go-tour-jp.appspot.com/methods/22
type MyReader struct{}

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

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

练习:rot13阅读器

    • exercise-rot-reader.go

https://play.golang.org/p/_Z4KDxjZU1

数値(13とか26とか)のマジックナンバーを登場させず、ある程度読めるように書いたらこうなった

package main

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

type rot13Reader struct {
    r io.Reader
}

// Read https://go-tour-jp.appspot.com/methods/23
func (r3r rot13Reader) Read(buf []byte) (int, error) {
    size, err := r3r.r.Read(buf)
    if err == io.EOF {
        return size, err
    }

    for i, char := range buf {
        switch {
        case 'A' <= char && char <= 'M':
            buf[i] = 'N' + (char - 'A')
        case 'N' <= char && char <= 'Z':
            buf[i] = 'A' + (char - 'N')
        case 'a' <= char && char <= 'm':
            buf[i] = 'n' + (char - 'a')
        case 'n' <= char && char <= 'z':
            buf[i] = 'a' + (char - 'n')
        default:
            continue
        }
    }

    return size, nil
}

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

锻炼:图片

    • exercise-images.go

 

    https://play.golang.org/p/TDrFIJifFF
package main

import (
    "image"
    "image/color"

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

// Image https://go-tour-jp.appspot.com/methods/25
type Image struct{}

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

// Bounds ...
func (img Image) Bounds() image.Rectangle {
    return image.Rect(0, 0, 256, 256)
}

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

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

练习:等价二叉树

    • exercise-equivalent-binary-trees.go

https://play.golang.org/p/7GI8qq1S5b

自由度高かったからsyncパッケージとか勝手に使った
ネットで見たらWalk()は他の例の中でもシンプルに書けてると思う

Same()は少し長いけど受信と比較の処理を分離させた

というか他の例が受信側が直列ばっかりなんだけどそんなので本当に良いんだろうか?
と思ってselect文で複数channelsを並べてみた

package main

import (
    "fmt"
    "sync"

    "golang.org/x/tour/tree"
)

// Walk walks the tree t sending all values
// from the tree to the channel ch.
func Walk(t *tree.Tree, ch chan int) {
    if t == nil {
        return
    }

    Walk(t.Left, ch)
    ch <- t.Value
    Walk(t.Right, ch)
}

// Same determines whether the trees
// t1 and t2 contain the same values.
func Same(t1, t2 *tree.Tree) bool {
    done := make(chan bool, 1)
    ch1, ch2 := make(chan int, 1), make(chan int, 1)
    buf1, buf2 := make([]int, 0), make([]int, 0)

    // sender side logic
    wg := new(sync.WaitGroup)
    wg.Add(2)
    go func() {
        Walk(t1, ch1)
        wg.Done()
    }()
    go func() {
        Walk(t2, ch2)
        wg.Done()
    }()
    go func() {
        wg.Wait()
        close(done)
    }()

    // reciever side logic
loop:
    for {
        select {
        case num := <-ch1:
            buf1 = append(buf1, num)

        case num := <-ch2:
            buf2 = append(buf2, num)

        case <-done:
            break loop
        }
    }

    // check values
    if len(buf1) != len(buf2) {
        return false
    }
    for i := range buf1 {
        if buf1[i] != buf2[i] {
            return false
        }
    }
    return true

}

func main() {
    fmt.Println("Walk")
    ch := make(chan int, 1)
    go func() {
        defer close(ch)
        Walk(tree.New(1), ch)
    }()
    for num := range ch {
        fmt.Println(num)
    }

    fmt.Println("Check 1")
    actual1 := Same(tree.New(1), tree.New(1))
    if actual1 {
        fmt.Println("same")
    }

    fmt.Println("Check 2")
    actual2 := Same(tree.New(1), tree.New(2))
    if !actual2 {
        fmt.Println("not same")
    }
}

运动:网络爬虫

    • exercise-web-crawler.go

https://play.golang.org/p/MjytQIQbuU

実行結果これで合ってるよね??

sync.Mutex{}.Lock()しないといけないのは内部で保持するURLの履歴だけで合ってるよね?
クロージャの中で自身を呼ぶ処理はエラーになるようなので先に変数定義だけしてる

Crawl()で無理にやってしまったけど、Fetcherインターフェースを持つ側の問題なので間違ったなと後々思った

本来はfakeFetcherが持つmapは初期は空のはずなので簡単なんだけど
今回は初めからfetch結果を持ってしまっているので別に履歴管理をしないといけない
それが微妙なのでCrawl()のままにした

package main

import (
    "fmt"
    "sync"
)

// Fetcher https://go-tour-jp.appspot.com/concurrency/9
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) {
    wg := new(sync.WaitGroup)
    defer wg.Wait()

    mux := new(sync.Mutex)
    history := map[string]int{}

    var checkOrUpdateFetchedURL func(url string) bool
    checkOrUpdateFetchedURL = func(url string) bool {
        mux.Lock()
        defer mux.Unlock()
        if _, ok := history[url]; ok {
            return true
        }
        history[url]++
        return false
    }

    var crawler func(url string, depth int)
    crawler = func(url string, depth int) {
        defer wg.Done()
        if depth <= 0 {
            return
        }

        // Don't fetch the same URL twice.
        if checkOrUpdateFetchedURL(url) {
            return
        }

        body, urls, err := fetcher.Fetch(url)
        if err != nil {
            fmt.Println(err)
            return
        }
        fmt.Printf("found: %s %q\n", url, body)

        // Fetch URLs in parallel.
        for _, u := range urls {
            wg.Add(1)
            go crawler(u, depth-1)
        }

        return
    }

    wg.Add(1)
    go crawler(url, depth)
    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/",
        },
    },
}
广告
将在 10 秒后关闭
bannerAds