Go

A Tour of Goのexercises

要約

"Go"lden weekなのでGo入門としてA tour of Go
やった。
exerciseで書いたやつ。

やった環境

% go version
go version go1.10.1 darwin/amd64

Flow control statements: for, if, else, switch and defer

sqrt

exercise-loops-and-functions.go
package main

import (
    "fmt"
    "math"
)

func Sqrt(x float64) (z float64) {
    z = 1
    for {
        if y := z - (z*z-x)/(2*z); math.Abs(z-y) < 1e-14 {
            break
        } else {
            z = y
        }
    }
    return
}

func main() {
    for i := 1; i < 1000; i++ {
        fmt.Printf("i: %v\n", i)
        fmt.Println(Sqrt(float64(i)))
        fmt.Println(math.Sqrt(float64(i)))
    }
}

More types: structs, slices, and maps.

slice

exercise-slices.go
package main

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

func Pic(dx, dy int) [][]uint8 {
    ret := make([][]uint8, dy)
    for i := range ret{
        ret[i] = make([]uint8, dx)
        for j := range ret[i]{
//          ret[i][j] = uint8((i+j/2))
//          ret[i][j] = uint8((i*j))
            ret[i][j] = uint8((i^j))
        }
    }
    return ret
}

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

map

exercise-maps.go
package main

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

func WordCount(s string) map[string]int {
    splitted := strings.Split(s, " ")
    x := make(map[string]int)
    for _,v := range splitted {
        x[v] += 1
    }
    return x
}

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

fibonacci

exercise-fibonacci-closure.go
package main

import "fmt"

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

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

Methods and interfaces

stringer

exercise-stringer.go
package main

import "fmt"

type IPAddr [4]byte

// TODO: Add a "String() string" method to IPAddr.

func (ip IPAddr) String() string {
    return fmt.Sprintf("%v.%v.%v.%v", 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)
    }
}

errorを返す

exercise-errors.go
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) {
    if x < 0 {
        return 0, ErrNegativeSqrt(x)
    }
    z := float64(1)
    for {
        if y := z - (z*z-x)/(2*z); math.Abs(z-y) < 1e-14 {
            break
        } else {
            z = y
        }
    }
    return z, nil
}
func main() {
    fmt.Println(Sqrt(2))
    fmt.Println(Sqrt(-2))
}

'A'を延々と返すRead

exercise-reader.go
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(x []byte)(int, error){
    for i:= 0; i<len(x); i++{
        x[i] = byte('A')
    }
    return len(x), nil
}

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

rot13のreaderをかくやつ

exercise-rot-reader.go
package main

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

type rot13Reader struct {
    r io.Reader
}


func (r rot13Reader)decode(x byte)byte{
    ranges := [][]byte{
        []byte{'A', 'Z'},
        []byte{'a', 'z'},
    }
    for _, lr := range ranges{
        if lr[0] <= x && x <= lr[1]{
            if y := x - 13; y < lr[0]{
                return y + 26
            }else{
                return y
            }
        }
    }
    return x
}

func (rot rot13Reader) Read(x []byte) (int, error) {
    n, err := rot.r.Read(x)
    if err != nil{
        return 0, err
    }
    for i := 0; i < n; i++ {
        x[i] = rot.decode(x[i])
    }
    return n, nil
}

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

imageかくやつ

exercise-images.go
package main

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

type Image struct{
    w,h int
}

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

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

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

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

Concurrency

2分木が同じ列を保持してるかどうか

exercise-equivalent-binary-trees.go
package main

import (
    "fmt"

    "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)
}

func TestWalk() string {
    for elm := 0; elm < 10; elm++ {
        ch := make(chan int)
        go Walk(tree.New(elm), ch)

        for i := 1; i < 11; i++ {
            if elm*i != <-ch{
                return "ng"
            }
        }
    }
    return "ok"
}

// Same determines whether the trees
// t1 and t2 contain the same values.
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 TestSame() string {
    for i := 0; i < 10; i++ {
        for j := 0; j < 10; j++ {
            if Same(tree.New(i), tree.New(j)) != (i == j) {
                return "ng"
            }
        }
    }
    return "ok"
}

func main() {
    fmt.Println(TestWalk())
    fmt.Println(TestSame())
}

fetchを並列にする+同じアドレスをfetchしないみたいなやつ

exercise-web-crawler.go
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)
}

type Cache struct {
    urls map[string]bool
    mux  sync.Mutex
}

func (c *Cache) exist(url string) bool {
    c.mux.Lock()
    //fmt.Println("aleady?", url, "\"", c.urls[url], "\"")
    defer c.mux.Unlock()
    defer func() { c.urls[url] = true }()
    return c.urls[url]
}

func Crawler(url string, depth int, fetcher Fetcher, cache *Cache, c chan bool) {
    defer func() { c <- true }()
    if depth <= 0 {
        return
    }
    if cache.exist(url) {
        return
    }
    body, urls, err := fetcher.Fetch(url)

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

    fmt.Printf("found: %s %q\n", url, body)
    ch := make(chan bool, len(urls))
    for _, u := range urls {
        go Crawler(u, depth-1, fetcher, cache, ch)
    }
    for i := 0; i < len(urls); i++ {
        _ = <-ch
    }
    return
}

// Crawl uses fetcher to recursively crawl
// pages starting with url, to a maximum of depth.
func Crawl(url string, depth int, fetcher Fetcher) {
    // TODO: Fetch URLs in parallel.
    // TODO: Don't fetch the same URL twice.
    // This implementation doesn't do either:
    cache := Cache{urls: make(map[string]bool)}
    c := make(chan bool, 1)
    Crawler(url, depth, fetcher, &cache, c)
    _ = <-c
}

func main() {

    Crawl("https://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{
    "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/",
        },
    },
}

その他

想定解とかはBSDライセンスで公開されてる本家のgithubのリポジトリのと比べればよいたぶん。 https://github.com/golang/tour