Continue(s)

Twitter:@dn0t_ GitHub:@ogrew

A Tour of Go完走。Exerciseの解答まとめ。

皆様にだいぶ遅れを取る形となってしまいましたがこちら無事完走しました。 稚拙ではありますが一応正解している(はずの)コードを置いておきます。 ツッコミポイントはありそう。

いつかこれを見直して笑ってやる。

Exercise: Loops and Functions

関数とループを使った簡単な練習として、平方根の計算を実装してみましょう: 数値 x が与えられたときに z² が最も x に近い数値 z を求めたいと思います。 (以下略)

package main

import (
    "fmt"
    . "math"
)

func Sqrt(x float64) float64 {
    z := x / 2.0
    for i := 0; i < 10; i++ {
        diff := (z*z - x) / (2.0 * z)
        z = z - diff
        if Abs(diff) < 1e-6 {
            break
        }
    }
    return z
}

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

// Output: 1.7320508075688772

Exercise: Slices

Pic 関数を実装してみましょう。 このプログラムを実行すると、生成した画像が下に表示されるはずです。 この関数は、長さ dy のsliceに、各要素が8bitのunsigned int型で長さ dx のsliceを割り当てたものを返すように実装する必要があります。 画像は、整数値をグレースケール(実際はブルースケール)として解釈したものです。 (以下略)

package main

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

func Pic(dx, dy int) [][]uint8 {

    // 準備
    img := make([][]uint8, dy)
    for y := range img {
        img[y] = make([]uint8, dx)
    }

    // 関数
    for y := 0; y < dy; y++ {
        for x := 0; x < dx; x++ {
            img[y][x] = uint8(x ^ y - y ^ x)
        }
    }

    return img
}

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

// Output: 省略

Exercise: Maps

WordCount 関数を実装してみましょう。string s で渡される文章の、各単語の出現回数のmapを返す必要があります。

package main

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

func WordCount(s string) map[string]int {
    arr := strings.Fields(s)

    // Mapの用意
    m := make(map[string]int)

    // WordCount
    for _, w := range arr {
        m[w]++
    }

    return m
}

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

// Output: 省略

Exercise: Fibonacci closure

fibonacci (フィボナッチ)関数を実装しましょう。 この関数は、連続するフィボナッチ数(0, 1, 1, 2, 3, 5, ...)を返す関数(クロージャ)を返します。

package main

import "fmt"

// fibonacci = int型を返す関数を返す関数
func fibonacci() func() int {
    p := 1
    pp := 0

    return func() int {
        fib := pp + p
        pp = p
        p = fib
        return fib
    }
}

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

// Output: 1,2,3,5,8,13,21,34,55,89,

Exercise: Stringers

IPAddr 型を実装してみましょう IPアドレスをドットで4つに区切った( dotted quad )表現で出力するため、 fmt.Stringer インタフェースを実装してください。

package main

import "fmt"

type IPAddr [4]byte

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

// Output:
// loopback: 127.0.0.1
// googleDNS: 8.8.8.8

Exercise: Errors

Sqrt 関数を 以前の演習 からコピーし、 error の値を返すように修正してみてください。 Sqrt は、複素数をサポートしていないので、負の値が与えられたとき、nil以外のエラー値を返す必要があります。

package main

import (
    "fmt"
    . "math"
)

type ErrNegativeSqrt float64

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

func Sqrt(x float64) (float64, error) {
    // 負の値が与えられたときnil以外のエラー値を返す
    if x < 0.0 {
        return 0, ErrNegativeSqrt(x)
    }

    // 初期値
    z := x / 2.0

    for i := 0; i < 10; i++ {
        diff := (z*z - x) / (2.0 * z)
        z = z - diff
        if Abs(diff) < 1e-9 {
            break
        }
    }
    return z, nil
}

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

// Output:
// 1.4142135623730951 <nil>
// 0 cannot Sqrt negative number: -2.000000

Exercise: Readers

ASCII文字 'A' の無限ストリームを出力する Reader 型を実装してください。

package main

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

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

// Output: 略

Exercise: rot13Reader

io.Reader を実装し、 io.Reader でROT13 換字式暗号( substitution cipher )をすべてのアルファベットの文字に適用して読み出すように rot13Reader を実装してみてください。

package main

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

type rot13Reader struct {
    r io.Reader
}

// io.Readerインターフェイスを見てみる
// type Reader interface {
//         Read(p []byte) (n int, err error)
// }
// byteの並んだsliceを受け取って、intとerrorを返す

func (r13 *rot13Reader) Read(b []byte) (int, error) {
    n, e := r13.r.Read(b)
    for i := 0; i < n; i++ {
        if b[i] > 'A' && b[i] < 'z' {
            if b[i] > 'z'-13 {
                b[i] = b[i] - 13
            } else {
                b[i] = b[i] + 13
            }
        }
    }
    return n, e
}

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

// Output: You cracked the code!

Exercise: Images

自分の Image 型を定義し、 インタフェースを満たすのに必要なメソッド を実装し、 pic.ShowImage を呼び出してみてください。 Bounds は、 image.Rect(0, 0, w, h) のようにして image.Rectangle を返すようにします。 ColorModel は、 color.RGBAModel を返すようにします。 At は、ひとつの色を返します。 生成する画像の色の値 v を color.RGBA{v, v, 255, 255} を利用して返すようにします。

package main

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

type Image struct {
    w int
    h int
}

// 以下のinterfaceを満たす
//    type Image interface {
//        ColorModel() color.Model
//        Bounds() Rectangle
//        At(x, y int) color.Color
//    }

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)/2)
    return color.RGBA{v, v, 255, 255}
}

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

// Output: 略

Exercise: Equivalent Binary Trees

長いので略。 https://go-tour-jp.appspot.com/concurrency/7 https://go-tour-jp.appspot.com/concurrency/8

package main

import (
    "fmt"
    "golang.org/x/tour/tree"
)

//    type Tree struct {
//        Left  *Tree
//        Value int
//        Right *Tree
//    }

// Walk walks the tree t sending all values
// from the tree to the channel ch.
func Walk(t *tree.Tree, ch chan int) {
    // 終わったらchannelをcloseさせたいので更に関数に切り出す
    walk(t, ch)
    close(ch)
}

func walk(t *tree.Tree, ch chan int) {
    if t == nil {
        return
    }

    if t.Left != nil {
        walk(t.Left, ch)
    }

    ch <- t.Value

    if t.Right != nil {
        walk(t.Right, ch)
    }
}

//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 v1 := range ch1 {
        v2 := <-ch2
        if v1 != v2 {
            return false
        }
    }
    return true
}

func main() {
    ch := make(chan int)
    go Walk(tree.New(1), ch)

    for v := range ch {
        fmt.Print(v,":")
    }
    fmt.Println()
    fmt.Println(Same(tree.New(1), tree.New(1)))
    fmt.Println(Same(tree.New(2), tree.New(3)))
}

// Output: 
// 1:2:3:4:5:6:7:8:9:10:
// true
// false

ではまた。