こんにちは。KOUKIです。
今回は、Go言語でパイプライン処理を実装してみましょう!といった趣旨の記事です。
パイプラインとは
工場のラインを思い浮かべてください。
商品を作るのに様々な工程があって、一つ一つの工程が完了したら後続の工程に受け渡していく、パイプラインとはそんな処理です。
<例>
サンドイッチを作る
工程1:パンをきる -> 工程2: 切ったパンにハムをのせる -> 工程3: パンを包む -> 完成
この様に、各工程をパイプで繋ぐような処理をここではパイプラインと呼びます。
Go言語では、goroutineとchannelをうまく活用することで、このパイプラインを表現することができます。
作るもの
こんな感じのものを作ってみましょう。
工程1: 数値生成 -> 工程2: 数値を累乗する -> 結果を表示する
工程1と工程2をパイプで繋ぎます。
実装
事前準備
ワークスペースを作りましょう。
1 2 3 |
mkdir sample cd sample touch main.go |
1 2 3 4 5 6 7 |
// main.go package main func main() { } |
工程1: 数値生成
最初に数値を生成する関数(工程1)を定義します。ここでは、渡された数値をチャネルに格納するだけの処理です。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 |
// ...intで任意の数のパラメータを受け取れる // channelを返すところがポイント func generate(nums ...int) <-chan int { // 返却用のchannelを用意 // キャパシティは受け取ったパラメータの数 out := make(chan int, len(nums)) go func() { defer close(out) // 受け取ったパラメータをchannelに渡す // リストみたいに格納される for _, n := range nums { out <- n } }() return out } |
工程2: 数値を累乗する
次は工程1から受け取った結果を累乗するプログラムを書きます。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
// inは前工程の生成物を受け取るイメージ func square(in <-chan int) <-chan int { // 返却用のchannelを用意 // キャパシティは受け取ったchannelの大きさ out := make(chan int, len(in)) go func() { defer close(out) for n := range in { // 累乗する out <- n * n } }() return out } |
結果を出力する
最後にパイプラインで処理した結果をコンソールに出力してみましょう。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 |
package main import "fmt" // ...intで任意の数のパラメータを受け取れる // channelを返すところがポイント func generate(nums ...int) <-chan int { // 返却用のchannelを用意 // キャパシティは受け取ったパラメータの数 out := make(chan int, len(nums)) go func() { defer close(out) // 受け取ったパラメータをchannelに渡す // リストみたいに格納される for _, n := range nums { out <- n } }() return out } // inは前工程の生成物を受け取るイメージ func square(in <-chan int) <-chan int { // 返却用のchannelを用意 // キャパシティは受け取ったchannelの大きさ out := make(chan int, len(in)) go func() { defer close(out) for n := range in { // 累乗する out <- n * n } }() return out } func main() { ch := generate(2, 4) result := square(ch) for n := range result { fmt.Println(n) } } |
generate関数に2と4を渡したので、最終的に4,16となるはずです。
プログラムを実行します。
1 2 3 |
$ go run main.go 4 16 |
OKですね。
channelを受け取るので、こんなこともできます。
1 2 3 4 5 |
func main() { for n := range square(generate(2, 4)) { fmt.Println(n) } } |
さらにこんなこともできます。
1 2 3 4 5 |
func main() { for n := range square(square(generate(2, 4))) { fmt.Println(n) } } |
1 2 3 |
$ go run main.go 16 256 |
素晴らしいですね。部品(工程)を作ればいくらでも繋げていけるわけです。
まとめ
ある程度プログラミング経験を積んでしまうとスキルアップを感じなくなってしまいますが、goroutineとパイプラインの概念は私にとっては新鮮でした。
プログラマでありながら、並行処理をほとんど書いたことなかったので、ガンガンgoroutineを使いこなして行きたいと思います!
それではまた!
最近のコメント