A Tour of Go で Go に入門した

Go言語、ウォッチだけしててあまりまじめにやってなかったので昨日からチュートリアルみたいなのやってた。

A Tour of Go

Go の基本的な文法の軽い解説がありつつ、ブラウザでコード実行しつつ学べるみたいな感じ。 途中でいくつか練習問題みたいなのがあった。 サンドボックスが Google Frontend で動いててちゃんとしてる。

Go のドキュメントとして

とかが参考になった。

感想

  • ベター C としてみるといいことしかない
  • ところどころ Python っぽい
    • range とか 組み込みの len でいろんな型の長さっぽいのが取得できる とか シンタックスシュガーがあまりない とか
  • goroutine と channel のおかげでジョブキュー みたいな非同期処理を要求されるミドルウェアが簡単に書けそうだった
  • オブジェクト指向をサポートしてる言語からみると、言語機能が少なくて学習コストが小さそう
  • おしゃれに抽象化して書けるみたいな要素は少なそう

Rubyのライブラリとか読んでて、綺麗に抽象化されててコード量は少ないけど、その分コードが素直に読めなくなるみたいな世界観はあまり好きではないので、その点 Go は素直なコードに落ち着きそうな感じがしてわりと好き。

以下、練習問題の回答。

#25

http://tour.golang.org/#25

package main

import (
    "fmt"
    "math"
)

func Sqrt(x float64) float64 {
    z, prev := 2., 0.
    const threshhold float64 = 1e-10
    for i := 0; math.Abs(z - prev) > threshhold; i++ {
        prev = z
        z = z - (math.Pow(z, 2) - x) / (2 * z)
        fmt.Printf("loop %d\n", i + 1)
    }
    return z
}

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

#38

http://tour.golang.org/#38

package main

import "code.google.com/p/go-tour/pic"

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

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

#43

http://tour.golang.org/#43

package main

import (
    "code.google.com/p/go-tour/wc"
    "strings"
)

func WordCount(s string) map[string]int {
    countByWord := make(map[string]int)
    for _, word := range strings.Fields(s) {
        countByWord[word] += 1
    }
    return countByWord
}

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

#46

http://tour.golang.org/#46

package main

import "fmt"

// fx+2 = fx + fx+1
func fibonacci() func() int {
    x, y := 0, 1
    return func() int {
        x, y = y, x + y
        return x
    }
}

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

#50

http://tour.golang.org/#50

package main

import (
    "fmt"
    "math/cmplx"
)

func Cbrt(x complex128) complex128 {
    z, prev := complex128(2), complex128(0)
    threshhold := 1e-10
    for ; cmplx.Abs(z - prev) > threshhold; {
        prev = z
        z = z - (cmplx.Pow(z, 3) - x) / (3 * (z*z))
    }
    return z
}

func main() {
    fmt.Println(Cbrt(2))
    fmt.Println(cmplx.Pow(3, 1.0/3.0))
}

#58

http://tour.golang.org/#58

package main

import (
    "fmt"
    "math"
)

type ErrNegativeSqrt float64

func (e ErrNegativeSqrt) Error() string {
    return fmt.Sprintf("at %f", e)
}

func Sqrt(f float64) (float64, error) {
    if f < 0.0 {
        return 0., ErrNegativeSqrt(f)
    }

    z, prev := 2., 0.
    const threshhold float64 = 1e-10
    for i := 0; math.Abs(z - prev) > threshhold; i++ {
        prev = z
        z = z - (math.Pow(z, 2) - f) / (2 * z)
        fmt.Printf("loop %d\n", i + 1)
    }
    return z, nil
}

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

#60

http://tour.golang.org/#60

package main

import (
    "fmt"
    "net/http"
)

type String string

func (s String) ServeHTTP(
    w http.ResponseWriter,
    r *http.Request) {
    fmt.Fprintf(w, "%s, %q", s, r.URL.Path)
}

type Struct struct {
    Greeting string
    Punct    string
    Who      string
}

func (s Struct) ServeHTTP(
    w http.ResponseWriter,
    r *http.Request) {
    fmt.Fprintf(w, "%s, %q", s, r.URL.Path)
}

func main() {
    http.Handle("/string", String("I'm a frayed knot."))
    http.Handle("/struct", &Struct{"Hello", ":", "Gophers!"})

    http.ListenAndServe("localhost:4000", nil)
}

#62

http://tour.golang.org/#62

package main

import (
    "code.google.com/p/go-tour/pic"
    "image"
    "image/color"
)

type Image struct {
    Width  int
    Height int
}

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

func (img *Image) Bounds() image.Rectangle {
    return image.Rect(0, 0, img.Width, img.Height)
}

func (img *Image) At(x, y int) color.Color {
    return color.RGBA{uint8(x * y), uint8(y - x), 255, 255}
}

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

#63

http://tour.golang.org/#63

package main

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

type rot13Reader struct {
    r io.Reader
}

func (r *rot13Reader) Read(p []byte) (n int, err error) {
    n, err = r.r.Read(p)
    for i := range p {
        if ('A' <= p[i] && p[i] <= 'M') || ('a' <= p[i] && p[i] <= 'm') {
            p[i] += 13
        } else if ('N' <= p[i] && p[i] <= 'Z') || ('n' <= p[i] && p[i] <= 'z') {
            p[i] -= 13
        }
    }
    return
}

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

#71

package main

import (
    "code.google.com/p/go-tour/tree"
    "fmt"
)

// 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 {
    c1, c2 := make(chan int), make(chan int)
    go func() {
        Walk(t1, c1)
        close(c1)
    }()
    go func() {
        Walk(t1, c2)
        close(c2)
    }()
    for {
        x, ok1 := <-c1
        y, ok2 := <-c2
        if !ok1 || !ok2 {
            return ok1 == ok2
        }
        if x != y {
            return false
        }
    }
    return false
}

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

    if Same(tree.New(1), tree.New(1)) {
        fmt.Println("Same!")
    }
    if Same(tree.New(1), tree.New(2)) {
        fmt.Println("Not Same!")
    }
}

#72

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) {
    isFetchedByUrl := make(map[string]bool)
    crawl(url, depth, fetcher, isFetchedByUrl)
    return
}

func crawl(url string, depth int, fetcher Fetcher, isFetchedByUrl map[string]bool) {
    if depth <= 0 {
        return
    }
    if v, ok := isFetchedByUrl[url]; ok && v {
        return
    }

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

    var wg sync.WaitGroup
    for _, u := range urls {
        wg.Add(1)
        go func(u string) {
            defer wg.Done()
            crawl(u, depth-1, fetcher, isFetchedByUrl)
        }(u)
    }
    wg.Wait()
    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/",
        },
    },
}