Edited at

A Tour of Go の練習問題

More than 5 years have passed since last update.


Exercise: Loops and Functions

http://go-tour-jp.appspot.com/#24

package main

import (
"fmt"
"math"
)

const eps = 1e-9

func Sqrt(x float64) float64 {
z := 1.0
p := z

for {
z = z - (z * z - x) / (2 * z)
if math.Abs(z - p) < eps {
break
}
p = z
}

return z
}

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


Exercise: Slices

http://go-tour-jp.appspot.com/#36

package main

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

func Pic(dx, dy int) [][]uint8 {
image := make([][]uint8, dy)
for y := range image {
image[y] = make([]uint8, dx)
}

for y := 0; y < dy; y++ {
for x :=0; x < dx; x++ {
image[y][x] = uint8((x + y) / 2)
}
}

return image

}

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


Exercise: Maps

http://go-tour-jp.appspot.com/#41

package main

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

func WordCount(s string) map[string]int {
countMap := map[string]int{}

for _, word := range strings.Fields(s) {
countMap[word]++
}

return countMap
}

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


Exercise: Fibonacci closure

http://go-tour-jp.appspot.com/#44

package main

import "fmt"

func fibonacci() func() int {
a, b := 0, 1

return func() int {
a, b = b, a + b
return a
}
}

func main() {
f := fibonacci()

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


Advanced Exercise: Complex cube roots

http://go-tour-jp.appspot.com/#48

package main

import (
"fmt"
"math/cmplx"
)

const eps = 1e-9

func Cbrt(x complex128) complex128 {
z := 1 + 0i
p := z

for {
z = z - (z * z * z - x) / (3 * z * z)
if cmplx.Abs(z - p) < eps {
break
}
p = z
}

return z
}

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


Exercise: Errors

http://go-tour-jp.appspot.com/#56

package main

import (
"fmt"
"math"
)

type ErrNegativeSqrt float64

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

const eps = 1e-9

func Sqrt(x float64) (float64, error) {
if x < 0 {
return 0, ErrNegativeSqrt(x)
}
z := 1.0
p := z

for {
z = z - (z * z - x) / (2 * z)
if math.Abs(z - p) < eps {
break
}
p = z
}

return z, nil
}

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


Exercise: HTTP Handlers

http://go-tour-jp.appspot.com/#58

package main

import (
"fmt"
"net/http"
)

type String string

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

type Struct struct {
Greeting string
Punct string
Who string
}

func (s *Struct) ServeHTTP(w http.ResponseWriter, r *http.Request){
fmt.Fprintf(w, "%s", s.Who + s.Punct + s.Greeting)
}

func main() {
http.Handle("/string", String("I like takoyaki"))
http.Handle("/struct", &Struct{"Hello", ":", "Sam"})
http.ListenAndServe(":4000", nil)
}


Exercise: Images

http://go-tour-jp.appspot.com/#60

package main

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

type Image struct{}

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

func (im *Image) Bounds() image.Rectangle {
return image.Rectangle{
image.Point{0, 0},
image.Point{200, 200},
}
}

func (im *Image) At(x, y int) color.Color {
return color.RGBA{uint8(x % 256), uint8(y % 256), uint8((x * y) % 256 ), 255}
}

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


Exercise: Rot13 Reader

http://go-tour-jp.appspot.com/#61

package main

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

type rot13Reader struct {
r io.Reader
}

func rot13(b byte) byte {
if 'A' <= b && b <= 'Z' {
k := b - 'A'
return 'A' + (k - 13 + 26) % 26
}
if 'a' <= b && b <= 'z' {
k := b - 'a'
return 'a' + (k - 13 + 26) % 26
}
return b
}

func (rot *rot13Reader) Read(p []byte) (int, error){
nr, ne := rot.r.Read(p)
if ne != nil {
return nr, ne
}

for i:=0; i<nr; i++ {
p[i] = rot13(p[i])
}

return nr, nil
}

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


Exercise: Equivalent Binary Trees

http://go-tour-jp.appspot.com/#70

http://stackoverflow.com/questions/12224042/go-tour-exercise-equivalent-binary-trees

package main

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

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

ch <- t.Value

if t.Right != nil {
walker(t.Right)
}
}

walker(t)
close(ch)
}

// Same determines whether the trees
// t1 and t2 contain the same values.
func Same(t1, t2 *tree.Tree) bool {
c1 := make(chan int)
c2 := make(chan int)

go Walk(t1, c1)
go Walk(t2, c2)

for {
n1, ok1 := <-c1
n2, ok2 := <-c2

if n1 != n2 || ok1 != ok2 {
return false
}

if !ok1 {
break
}
}

return true
}

func main() {
fmt.Println(Same(tree.New(1), tree.New(1)))
fmt.Println(Same(tree.New(5), tree.New(5)))
fmt.Println(Same(tree.New(3), tree.New(2)))
}


Exercise: Web Crawler

http://go-tour-jp.appspot.com/#71

http://c4se.hatenablog.com/entry/2013/01/24/175114

次のコードはdepth <= 0の時の処理が間違っています

別バージョンの方で修正しました

package main

import (
"fmt"
"time"
)

type Fetcher interface {
Fetch(url string) (body string, urls []string, err error)
}

func Crawl(url string, depth int, fetcher Fetcher) {
type URL struct {
url string
depth int
}

msg := make(chan string)
req := make(chan URL)
quit := make(chan int)

crawler := func (url string, depth int) {
defer func() { quit <- 0 }()

if depth <= 0 {
return
}

body, urls, err := fetcher.Fetch(url)

if err != nil {
msg <- fmt.Sprintf("%s\n", err)
return
}

msg <- fmt.Sprintf("found: %s %q\n", url, body)

for _, u := range urls {
req <- URL{ u, depth - 1 }
}
}

works := 1

memo := make(map[string]bool)
memo[url] = true

go crawler(url, depth)

for works > 0 {
select {
case s := <-msg:
fmt.Print(s)
case u := <-req:
if !memo[u.url] {
memo[u.url] = true
works++

go crawler(u.url, u.depth)
}
case <- quit:
works--
}
}
}

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

type fakeFetcher map[string]*fakeResult

type fakeResult struct {
body string
urls []string
}

func (f fakeFetcher) Fetch(url string) (string, []string, error) {
time.Sleep(time.Second * 5)
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/",
},
},
}


Exercise: Web Crawler 別バージョン

上のはブロッキングするのでブロッキングしないバージョンも書いてみた

あとdepth <= 0のところの処理が間違っていたのでそこも修正

こっちのがすっきりしてる感ある

package main

import (
"fmt"
"time"
)

type Fetcher interface {
Fetch(url string) (body string, urls []string, err error)
}

func Crawl(url string, depth int, fetcher Fetcher) {
type Msg struct {
message string
}
type URL struct {
url string
depth int
}
type Quit struct{}

ch := make(chan interface{}, 20)
crawler := func (url string, depth int) {
defer func() { ch <- Quit{} }()

body, urls, err := fetcher.Fetch(url)

if err != nil {
ch <- Msg{ fmt.Sprintf("%s\n", err) }
return
}

ch <- Msg{ fmt.Sprintf("found: %s %q\n", url, body) }

for _, u := range urls {
ch <- URL{ u, depth - 1 }
}
}

works := 0
memo := make(map[string]bool)

ch <- URL{ url, depth }

for {
req := <- ch

switch req := req.(type) {
case Msg:
fmt.Print(req.message)
case URL:
if req.depth > 0 && !memo[req.url] {
memo[req.url] = true
works++

go crawler(req.url, req.depth)
}
case Quit:
works--
}

if works <= 0 {
break
}
}
}

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

type fakeFetcher map[string]*fakeResult

type fakeResult struct {
body string
urls []string
}

func (f fakeFetcher) Fetch(url string) (string, []string, error) {
time.Sleep(time.Second * 5)
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/",
},
},
}