Go入門した

最近、Go界隈が熱い気がして(主にDockerのせい?)、一介のプログラマならば一般教養として基本的な文法ぐらいは抑えておいた方がいいだろうと思いGoの勉強を始めた。

色々調べたところ、A Tour of Goが Go入門には良いらしく、僕もそれに従った。
A Tour of Goはブラウザ上でインタラクティブに試せるので環境構築とかせずにGoで遊ぶことができる。
オリジナルは英語だが、日本語版もある。
しかし、英語に苦労しないなら、英語版にすることをオススメする。
日本語版は所々、訳が微妙だった。(僕は日本語版をやった。)

Goの印象

オブジェクト指向に慣れてる人間からすると、lenとかrangeとかmakeとか組み込みキーワードっぽいのがでてくるのが嫌だった。
あとは、シンタックス的な好き嫌いがあるがこれは慣れの問題もあるだろう。(例えば、パッケージ中の関数名が大文字で始まるものが多いとか、mapの宣言の仕方が気持ち悪いとか。)
多重代入や多重返り値をサボートしているは良かった。(現代的な言語ではほぼ当たり前かな?)
一番の特徴はゴルーチンと呼ばれる、並行性を意識した機能が言語ネイティブでサポートされていることかな。

A Tour of Go のエクササイズ

A Tour of Go には手頃なエクササイズが付いており、それによってGoにより親しむことができる。
以下、僕のエクササイズの解答を載せておく。(合ってる保証はない)
ライセンスはMIT。

24

package main

import (
    "fmt"
    "math"
)

func Sqrt(x float64) float64 {
    z      := 0.0
    next_z := x / 2.0
    for ; math.Abs(z - next_z) > 1e-10; {
        z = next_z
        next_z = z - (z * z - x) / (2 * z)
    }
    return next_z
}

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

36

package main

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

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

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

41

package main

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

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

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

44

package main

import "fmt"

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

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

48

package main

import "fmt"
import "math/cmplx"

func Cbrt(x complex128) complex128 {
    var z complex128 = x / 2.0
    for i := 0; i < 10; i++ {
        z = z - (z * z * z - x) / (3.0 * z * z)
    }
    return z
}

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

56

package main

import (
    "fmt"
    "math"
)


type ErrNegativeSqrt float64

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

    z      := 0.0
    next_z := f / 2.0
    for ; math.Abs(z - next_z) > 1e-10; {
        z = next_z
        next_z = z - (z * z - f) / (2 * z)
    }
    return next_z, nil
}

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

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

61

package main

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



type outOfAlphabetRangeError byte

func (e outOfAlphabetRangeError) Error() string {
    return fmt.Sprintf("%d is out of alphabet range in ascii code", e)
}
   

type rot13Reader struct {
    r io.Reader
}


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

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

70

package main

import "fmt"
import "code.google.com/p/go-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.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, ch2 := make(chan int), make(chan int)
    go Walk(t1, ch1)
    go Walk(t2, ch2)
    for i := 0; i < 10; i++ {
        v1, v2 := <-ch1, <-ch2
        if v1 != v2 {
            return false
        }
    }
    return true
}

func main() {
    ch := make(chan int)
    t := tree.New(1)
    go Walk(t, ch)
    for i := 0; i < 10; i++{
        v := <-ch
        fmt.Println(v)
    }
    fmt.Println(Same(tree.New(1), tree.New(1)))
    fmt.Println(Same(tree.New(1), tree.New(2)))
}