チャネルを使って、数列を生成することを考える。数列のような一連のデータの流れを、ストリームとよぶ。
まずは単純な例から。
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つ(n と m)を引数にとって 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