ゼロから学ぶGo言語プログラミング(15) A Tour of Go 67~70

f:id:belbo:20140616112930j:plain

A Tour of Goの続き。

67.Selcet

selectは、switchのようで違う、goroutine用の制御構造。

select は、それの case の条件で、いずれかを実行できるようになるまでブロックし、

こうあるので、selectのcaseにはchannelが必須ということでしょうか。

複数のcaseが成立した場合、全てでも先頭でも末尾でもなく、ランダムに実行されるcaseが決まるというのは、使い所があるんでしょうか。

68.Default Selection

これもswitchと同じで、caseとdefaultで制御。

通信のレスポンスとか待機時間やステータスが色々な場合に、selectで待ち構えておいて、defaultで待機処理したりに使えるのかな。

構文の性格上、fallthrough は不要だから無さそう。

69.Exercise: Equivalent Binary Trees

70.Exercise: Equivalent Binary Trees

goroutineのまとめっぽい課題。

"Binary Trees"で、バイナリデータを持ったツリー?と勘違いしてしまったが、二分木のことだった。 ツアーの解説の中では二分木という表現の方が良いと思う。

課題の内容は、"code.google.com/p/go-tour/tree"パッケージによって、二分木を生成するtree.New()が提供され、この二分木を比較して同一かを判定する、というもの。

二分木の比較をするためには、ツリーを辿って探査していかないといけないのか。 Walk関数でそれを実装しろとなっている。 与えられる二分木はこういう構造体。

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

Treeは自身の持つ値を返す Value()を持ち、LeftやRightは子のTreeへのポインタ。 ということは、ルートから順に値を取り、LeftやRightの存在を確認して、存在すれば次のノードへ、という方法で走査できそう。

Walk()にchannelを渡すのだから、go Walk()という風にgoroutineで動かすのだろうと試すと、"runtime error: invalid memory address or nil pointer dereference"のpanicが。 tにツリーが渡される前提になっていたのが原因だったので、t == nil でWalk()を終了することに。 というようなのを繰り返し、なんとか動く形にはなった。

chaanelでの受信を条件にしたループの書き方が分からなかったけど、rangeでいけるらしい。

他に、Walk()ひとつを呼ぶだけでは、close()のタイミングが持ちようがなく、結局Walk()とwalkNode()という2つの関数を設置した。

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) {
    walkNode(t, ch)
    close(ch)
}

func walkNode(t *tree.Tree, ch chan int) {
    if t == nil {
        return
    }
    if t.Left != nil {
        walkNode(t.Left, ch)
    }
    ch <- t.Value
    if t.Right != nil {
        walkNode(t.Right, 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 := range c1 {
        n2 := <-c2
        if n1 != n2 {
            return false
        }
    }
    return true
}

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

相変わらずこれがGoらしいコードなのか、そして根本的な使い方など誤っていないのかは分からない。 でもとりあえず、goroutineに少しだけ慣れられた気がする。