A Tour of GoでGoに入門した

Goの勉強にはまずA Tour of Goを読むと良いっぽかったので読みました。 途中途中に練習問題があって、それを解きながら進めると適度に手も動かせて良かったです。

以下は自分用のまとめです。 最後に練習問題の解答をまとめて載せています。 (合ってるかは保証できません)

Packages, variables, and functions

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

// while文
for ; sum < 1000; {
    sum += sum
}

// 無限ループ
for {
    // do something
}

More types: structs, slices, and maps.

func main() {
	i, j := 42, 2701

	p := &i         // point to i
	fmt.Println(*p) // read i through the pointer
	*p = 21         // set i through the pointer
	fmt.Println(i)  // see the new value of i

	p = &j         // point to j
	*p = *p / 37   // divide j through the pointer
	fmt.Println(j) // see the new value of j
}
m := make(map[string]int)

m["Answer"] = 42 // 初期化
fmt.Println("The value:", m["Answer"])

m["Answer"] = 48 // 更新
fmt.Println("The value:", m["Answer"])

delete(m, "Answer") // 削除
fmt.Println("The value:", m["Answer"])

v, ok := m["Answer"] // 取得
fmt.Println("The value:", v, "Present?", ok)

Methods and interfaces

type Vertex struct {
	X, Y float64
}

func (v Vertex) Abs() float64 {
	return math.Sqrt(v.X*v.X + v.Y*v.Y)
}

func main() {
	v := Vertex{3, 4}
	fmt.Println(v.Abs())
}
type I interface {
	M()
}

func main() {
	var i I
	describe(i)
	i.M() // ランタイムエラー
}
var i interface{} = "hello"

s, ok := i.(string)
fmt.Println(s, ok)

Concurrency

type SafeCounter struct {
	v   map[string]int
	mux sync.Mutex
}

func (c *SafeCounter) Inc(key string) {
	c.mux.Lock()
	c.v[key]++
	c.mux.Unlock()
}

Execises

package main

import (
	"fmt"
	"math"

)

func Sqrt(x float64) float64 {
	z := 1.0
	for i := 0; ; i++ {
		diff := ( z * z - x ) / ( 2 * z )
		if math.Abs(diff) < 1e-10 {
			fmt.Printf("loop count is %d\n", i)
			return z
		} 
		z -= diff
	}
}

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

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

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

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

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

func WordCount(s string) map[string]int {
	m := make(map[string]int)
	for i := range strings.Fields(s) {
		m[strings.Fields(s)[i]] += 1
	}
	return m
}

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

import "fmt"

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

func main() {
	f := fibonacci()
	for i := 0; i < 10; i++ {
		fmt.Println(f())
	}
}
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)
	}
}
package main

import (
	"fmt"
)

type ErrNegativeSqrt float64

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

func Sqrt(x float64) (float64, error) {
	if x < 0.0 {
		return 0.0, ErrNegativeSqrt(x)
	}
	z := 1.0
	for i := 0; ; i++ {
		if z2 := z - (z*z-x)/(2*z); z2 - z < 1e-10 {
			return z, nil
		} else {
			z = z2
		}
	}
}

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

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

type MyReader struct{}

func (r MyReader) Read(b []byte) (int, error) {
	b[0] = 'A'
	return 1, nil
}


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

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

type rot13Reader struct {
	r io.Reader
}

func (reader *rot13Reader) Read(b []byte) (n int, err error) {
    n, err = reader.r.Read(b) // bに文字を読み込む
	// 以下で文字を変換する
    for i := range b {
        if ('A' <= b[i] && b[i] <= 'M') || ('a' <= b[i] && b[i] <= 'm') {
            b[i] += 13
        } else if ('N' <= b[i] && b[i] <= 'Z') || ('n' <= b[i] && b[i] <= 'z') {
            b[i] -= 13
        }
    }
    return n, err
}

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

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

type Image struct{
	w, h int
}

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

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

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

func main() {
	m := Image{100, 100}
	pic.ShowImage(m)
}
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)
}

// 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(t2, c2)
        close(c2)
    }()
	for {
        x, ok1 := <-c1
        y, ok2 := <-c2
		if !ok1 || !ok2 {
			return true
		}
        if x != y {
            return false
        }
    }
    return true
}

func main() {
	ch := make(chan int)
	t := tree.New(1)
	go func() {
		Walk(t, ch)
		close(ch)
	}()
	for v := range ch {
		fmt.Println(v)
	}
	
	if Same(tree.New(1), tree.New(1)) {
		fmt.Println("Same")
	}
	if !Same(tree.New(1), tree.New(2)) {
		fmt.Println("Not Same")
	}
}
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)
}

func Crawl(url string, depth int, fetcher Fetcher) {
	m := make(map[string]bool)
	crawl(url, depth, fetcher, m)
}

// Crawl uses fetcher to recursively crawl
// pages starting with url, to a maximum of depth.
func crawl(url string, depth int, fetcher Fetcher, m map[string]bool) {
	if depth <= 0 {
		return
	}
	mux := &sync.Mutex{}
	mux.Lock()
	if v, ok := m[url]; v && ok {
		return
	}
	m[url] = true
	mux.Unlock()
	
	body, urls, err := fetcher.Fetch(url)
	if err != nil {
		fmt.Println(err)
		return
	}
	fmt.Printf("found: %s %q\n", url, body)
	
	wg := &sync.WaitGroup{}
	for _, u := range urls {
		wg.Add(1)
		go func (u string) {
			crawl(u, depth-1, fetcher, m)
			wg.Done()
		}(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/",
		},
	},
}