ゴルーチンとチャネルでジェネレータ

ゴルーチンとチャネルを使ってジェネレータを作ることもできる。

package main

import (
    "fmt"
)

type Item interface {
    Eq(Item) bool
    Less(Item) bool
}

type Node struct {
    item Item
    left, right *Node
}

func newNode(x Item) *Node {
    p := new(Node)
    p.item = x
    return p
}

func insertNode(node *Node, x Item) *Node {
    switch {
        case node == nil: return newNode(x)
        case x.Eq(node.item): return node
        case x.Less(node.item): node.left = insertNode(node.left, x)
        default: node.right = insertNode(node.right, x)
    }
    return node
}

func foreachNode(f func(Item), node *Node) {
    if node != nil {
        foreachNode(f, node.left)
        f(node.item)
        foreachNode(f, node.right)
    }
}

type Tree struct {
    root *Node
}

func newTree() *Tree {
    return new(Tree)
}

func (t *Tree) insertTree(x Item) {
    t.root = insertNode(t.root, x)
}

func (t *Tree) foreachTree(f func(Item)) {
    foreachNode(f, t.root)
}

func (t *Tree) makeGen() func() Item {
    ch := make(chan Item)
    go func() {
        t.foreachTree(func(x Item) {
            ch <- x }
        )
        close(ch)
    }()
    return func() Item {
        return <- ch
    }
}

type Int int

func (n Int) Eq(m Item) bool {
    return n == m.(Int)
}

func (n Int) Less(m Item) bool {
    return n < m.(Int)
}

func main() {
    a := newTree() b := []int { 5,6,4,7,3,8,2,9,1,0 }
    for _, x := range b {
        a.insertTree(Int(x))
    }
    resume := a.makeGen()
    for i := 0; i < 11; i++ {
        fmt.Println(resume())
    }
}

makeGen 関数は無名関数を返す。この無名関数はクロージャになっていて、makeGen 内のゴルーチンからチャネルを使って受け取った値を返す。ゴルーチンは二分木の値を昇順に返すので、結果として無名関数も呼び出しごとに同じ順で値を返すことになる。
実行してみよう。

^o^ > go run go_gen.go
0
1
2
3
4
5
6
7
8
9
<nil>

最後の <nil> は何だろう?

カテゴリー: Go パーマリンク

コメントを残す

メールアドレスが公開されることはありません。 * が付いている欄は必須項目です

このサイトはスパムを低減するために Akismet を使っています。コメントデータの処理方法の詳細はこちらをご覧ください