ストリーム

チャネルを使って、数列を生成することを考える。数列のような一連のデータの流れを、ストリームとよぶ。
まずは単純な例から。

package main

import (
    "fmt"
)

type Stream chan int

func makeInt(n, m int) Stream {
    s := make(Stream)
    go func() {
        for i := n; i <= m; i++ {
            s <- i
        }
        close(s)
    }()
    return s
}

func main() {
    s := makeInt(1, 20)
    for x := range s {
        fmt.Print(x, " ")
    }
    fmt.Println("")
}

type を使って、整数をやり取りするチャネルに Stream という別名をつけている。
makeInt 関数は、整数2つ(nm)を引数にとって Stream を返す。中では、Stream を作っておき、ゴルーチンで匿名関数を実行する。このゴルーチンは for ループの中で n から m までを Stream に書き込み、最後に閉じる。
こうすることによって、数列が生成できるわけだ。
実行例:

^o^ > go run stream.go
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20

次に、Stream を高階関数に渡してみよう。マッピングを行う streamMap とフィルタリングを行う streamFilter を作ってみる。どちらも関数と Stream を受け取って、新しい Stream を返す。

package main

import (
    "fmt"
)

type Stream chan int

func makeInt(n, m int) Stream {
    s := make(Stream)
    go func() {
        for i := n; i <= m; i++ {
            s <- i
        }
        close(s)
    }()
    return s
}

func streamMap(f func(int) int, in Stream) Stream {
    s := make(Stream)
    go func() {
        for {
            x, ok := <- in
            if !ok {
                break
            }
            s <- f(x)
        }
        close(s)
    }()
    return s
}

func streamFilter(f func(int) bool, in Stream) Stream {
    s := make(Stream)
    go func() {
        for {
            x, ok := <- in
            if !ok {
                break
            }
            if f(x) {
                s <- x
            }
        }
        close(s)
    }()
    return s
}

func main() {
    s0 := makeInt(1, 20)
    for x := range s0 {
        fmt.Print(x, " ")
    }
    fmt.Println("")
    square := func(x int) int {
        return x * x
    }
    s1 := streamMap(square, makeInt(1, 20))
    for x := range s1 {
        fmt.Print(x, " ")
    }
    fmt.Println("")
    isOdd := func(x int) bool {
        return x % 2 != 0
    }
    s2 := streamFilter(isOdd, makeInt(1, 20))
    for x := range s2 {
        fmt.Print(x, " ")
    }
    fmt.Println("")
} 

実行例:

^o^ > go run stream2.go
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
1 4 9 16 25 36 49 64 81 100 121 144 169 196 225 256 289 324 361 400
1 3 5 7 9 11 13 15 17 19