概要
チャネル関数の続き。
今回は、指定された値を延々と繰り返してくれるチャネルを返す関数です。C# の Enumerable.Repeat
チックな感じ。
サンプル
package chans // Repeat -- 指定した値を永遠と繰り返すチャネルを返します。 func Repeat(done <-chan struct{}, values ...interface{}) <-chan interface{} { out := make(chan interface{}) go func() { defer close(out) for { for _, v := range values { select { case <-done: return case out <- v: } } } }() return out } // RepeatFn -- 指定した関数を永遠と繰り返し、その戻り値を返すチャネルを返します。 func RepeatFn(done <-chan struct{}, fn func() interface{}) <-chan interface{} { out := make(chan interface{}) go func() { defer close(out) for { select { case <-done: return case out <- fn(): } } }() return out }
gomy/repeat.go at master · devlights/gomy · GitHub
以下、テストコードです。
package chans import ( "testing" ) func TestRepeat(t *testing.T) { type ( testin struct { data []interface{} } testout struct { value []interface{} } testcase struct { in testin out testout } ) cases := []testcase{ { in: testin{ data: []interface{}{1}, }, out: testout{ value: []interface{}{1, 1, 1}, }, }, { in: testin{ data: []interface{}{1, 2, 3}, }, out: testout{ value: []interface{}{1, 2, 3}, }, }, } for index, c := range cases { func() { done := make(chan struct{}) defer close(done) repeatCh := Repeat(done, c.in.data...) result1 := <-repeatCh result2 := <-repeatCh result3 := <-repeatCh if result1 != c.out.value[0] { t.Errorf("[testcase-%02d][result1] want: %v\tgot: %v", index+1, c.out.value[0], result1) } if result2 != c.out.value[1] { t.Errorf("[testcase-%02d][result2] want: %v\tgot: %v", index+1, c.out.value[1], result1) } if result3 != c.out.value[2] { t.Errorf("[testcase-%02d][result3] want: %v\tgot: %v", index+1, c.out.value[2], result1) } }() } } func TestRepeatFn(t *testing.T) { type ( testin struct { fn func() interface{} } testout struct { value []interface{} } testcase struct { in testin out testout } ) cases := []testcase{ { in: testin{ fn: func() interface{} { return "helloworld" }, }, out: testout{ value: []interface{}{"helloworld", "helloworld", "helloworld"}, }, }, } for index, c := range cases { func() { done := make(chan struct{}) defer close(done) repeatCh := RepeatFn(done, c.in.fn) result1 := <-repeatCh result2 := <-repeatCh result3 := <-repeatCh if result1 != c.out.value[0] { t.Errorf("[testcase-%02d][result1] want: %v\tgot: %v", index+1, c.out.value[0], result1) } if result2 != c.out.value[1] { t.Errorf("[testcase-%02d][result2] want: %v\tgot: %v", index+1, c.out.value[1], result1) } if result3 != c.out.value[2] { t.Errorf("[testcase-%02d][result3] want: %v\tgot: %v", index+1, c.out.value[2], result1) } }() } }
実行すると以下のようになります。
$ go test -v github.com/devlights/gomy/chans -run ^TestRepeat.*$ === RUN TestRepeat --- PASS: TestRepeat (0.00s) === RUN TestRepeatFn --- PASS: TestRepeatFn (0.00s) PASS ok github.com/devlights/gomy/chans 0.003s
RepeatFn() の方は、乱数などを返す関数を設定したりすると毎回異なる乱数を延々と繰り返すように出来ていいかもしれませんね。
例えば、こんな感じです。
package async import ( "context" "fmt" "math/rand" "github.com/devlights/gomy/chans" ) // TakeFirst10Items -- 最初の10個のみを取得するサンプルです func TakeFirst10Items() error { var ( rootCtx = context.Background() mainCtx, cancel = context.WithCancel(rootCtx) ) defer cancel() // 乱数を返す関数 randomInt := func() interface{} { return rand.Int() } // 乱数を延々と返すチャネル生成 repeatCh := chans.RepeatFn(mainCtx.Done(), randomInt) // 最初の10件のみ取得するチャネル生成 takeFirst10ItemCh := chans.Take(mainCtx.Done(), repeatCh, 10) // 出力 for v := range takeFirst10ItemCh { fmt.Println(v) } return nil }
実行すると以下のようになります。
$ make run ENTER EXAMPLE NAME: async_take_first_10items [Name] "async_take_first_10items" 711512294376717777 6597590559487096282 7546803073246591619 2730330101447707632 8913116274994349004 1574111430622025378 963317749379199556 5718127538066206519 3934619028980328702 7294862805965956347 [Elapsed] 2.340763ms
参考

- 作者:Katherine Cox-Buday
- 発売日: 2018/10/26
- メディア: 単行本(ソフトカバー)
関連記事
過去の記事については、以下のページからご参照下さい。
- いろいろ備忘録日記まとめ
サンプルコードは、以下の場所で公開しています。
- いろいろ備忘録日記サンプルソース置き場