概要
引き続き、小ネタチャネル関数の続き。( #関連記事 参照)
以前の記事で、指定した個数または条件に合致する間、チャネルから要素を取得する Take 関数について書きました。
C#のLinqには、これの逆をするSkip, SkipWhileというのがあります。なので、同じように作ってみました。
サンプル
package chans // Skip -- 指定した個数分、入力用チャネルから値をスキップするチャネルを返します。 func Skip(done <-chan struct{}, in <-chan interface{}, count int) <-chan interface{} { out := make(chan interface{}) go func() { defer close(out) skipCount := 0 for v := range OrDone(done, in) { if skipCount < count { skipCount++ continue } select { case out <- v: case <-done: } } }() return out } // SkipWhile -- 入力用チャネルから取得した値が指定した値と同一である間、値をスキップし続けるチャネルを返します。 func SkipWhile(done <-chan struct{}, in <-chan interface{}, value interface{}) <-chan interface{} { return SkipWhileFn(done, in, func() interface{} { return value }) } // SkipWhileFn -- 入力用チャネルから取得した値が指定した関数の戻り値と同一である間、値をスキップし続けるチャネルを返します。 func SkipWhileFn(done <-chan struct{}, in <-chan interface{}, fn func() interface{}) <-chan interface{} { out := make(chan interface{}) go func(fn func() interface{}) { defer close(out) var ( fnResult = fn() skipEnd = false ) for v := range OrDone(done, in) { if !skipEnd && v == fnResult { continue } skipEnd = true select { case out <- v: case <-done: } } }(fn) return out }
gomy/skip.go at master · devlights/gomy · GitHub
以下テストコードです。
package chans import ( "testing" ) func TestSkip(t *testing.T) { type ( testin struct { total int count int } testout struct { count int } testcase struct { in testin out testout } ) cases := []testcase{ { in: testin{ total: 10, count: 0, }, out: testout{count: 10}, }, { in: testin{ total: 10, count: 1, }, out: testout{count: 9}, }, { in: testin{ total: 10, count: 5, }, out: testout{count: 5}, }, { in: testin{ total: 10, count: 10, }, out: testout{count: 0}, }, } for caseCount, c := range cases { func() { done := make(chan struct{}) defer close(done) inCh := make(chan interface{}, c.in.total) func() { defer close(inCh) for i := 0; i < c.in.total; i++ { inCh <- i } }() skipCh := Skip(done, inCh, c.in.count) recvCount := 0 for v := range skipCh { t.Logf("[test-%02d][skip] %v\n", caseCount+1, v) recvCount++ } if c.out.count != recvCount { t.Errorf("want: %v\tgot: %v", c.out.count, recvCount) } }() } } func TestSkipWhile(t *testing.T) { type ( testin struct { value int data []int } testout struct { count int } testcase struct { in testin out testout } ) cases := []testcase{ { in: testin{ data: []int{1}, value: 1, }, out: testout{count: 0}, }, { in: testin{ data: []int{1, 1, 1, 1, 1, 2, 2}, value: 1, }, out: testout{count: 2}, }, { in: testin{ data: []int{1, 1, 2, 1, 1, 2, 2}, value: 1, }, out: testout{count: 5}, }, } for caseCount, c := range cases { func() { done := make(chan struct{}) defer close(done) inCh := make(chan interface{}, len(c.in.data)) func() { defer close(inCh) for _, v := range c.in.data { inCh <- v } }() skipCh := SkipWhile(done, inCh, c.in.value) recvCount := 0 for v := range skipCh { t.Logf("[test-%02d][skip] %v\n", caseCount+1, v) recvCount++ } if c.out.count != recvCount { t.Errorf("want: %v\tgot: %v", c.out.count, recvCount) } }() } } func TestSkipWhileFn(t *testing.T) { type ( testin struct { fn func() interface{} data []int } testout struct { count int } testcase struct { in testin out testout } ) cases := []testcase{ { in: testin{ data: []int{1}, fn: func() interface{} { return 1 }, }, out: testout{count: 0}, }, { in: testin{ data: []int{1, 1, 1, 1, 1, 2, 2}, fn: func() interface{} { return 1 }, }, out: testout{count: 2}, }, { in: testin{ data: []int{1, 1, 2, 1, 1, 2, 2}, fn: func() interface{} { return 1 }, }, out: testout{count: 5}, }, } for caseCount, c := range cases { func() { done := make(chan struct{}) defer close(done) inCh := make(chan interface{}, len(c.in.data)) func() { defer close(inCh) for _, v := range c.in.data { inCh <- v } }() skipCh := SkipWhileFn(done, inCh, c.in.fn) recvCount := 0 for v := range skipCh { t.Logf("[test-%02d][skip] %v\n", caseCount+1, v) recvCount++ } if c.out.count != recvCount { t.Errorf("want: %v\tgot: %v", c.out.count, recvCount) } }() } }
実行すると以下のようになります。
$ go test -v github.com/devlights/gomy/chans -run ^TestSkip.*$ === RUN TestSkip TestSkip: skip_test.go:71: [test-01][skip] 0 TestSkip: skip_test.go:71: [test-01][skip] 1 TestSkip: skip_test.go:71: [test-01][skip] 2 TestSkip: skip_test.go:71: [test-01][skip] 3 TestSkip: skip_test.go:71: [test-01][skip] 4 TestSkip: skip_test.go:71: [test-01][skip] 5 TestSkip: skip_test.go:71: [test-01][skip] 6 TestSkip: skip_test.go:71: [test-01][skip] 7 TestSkip: skip_test.go:71: [test-01][skip] 8 TestSkip: skip_test.go:71: [test-01][skip] 9 TestSkip: skip_test.go:71: [test-02][skip] 1 TestSkip: skip_test.go:71: [test-02][skip] 2 TestSkip: skip_test.go:71: [test-02][skip] 3 TestSkip: skip_test.go:71: [test-02][skip] 4 TestSkip: skip_test.go:71: [test-02][skip] 5 TestSkip: skip_test.go:71: [test-02][skip] 6 TestSkip: skip_test.go:71: [test-02][skip] 7 TestSkip: skip_test.go:71: [test-02][skip] 8 TestSkip: skip_test.go:71: [test-02][skip] 9 TestSkip: skip_test.go:71: [test-03][skip] 5 TestSkip: skip_test.go:71: [test-03][skip] 6 TestSkip: skip_test.go:71: [test-03][skip] 7 TestSkip: skip_test.go:71: [test-03][skip] 8 TestSkip: skip_test.go:71: [test-03][skip] 9 --- PASS: TestSkip (0.00s) === RUN TestSkipWhile TestSkipWhile: skip_test.go:139: [test-02][skip] 2 TestSkipWhile: skip_test.go:139: [test-02][skip] 2 TestSkipWhile: skip_test.go:139: [test-03][skip] 2 TestSkipWhile: skip_test.go:139: [test-03][skip] 1 TestSkipWhile: skip_test.go:139: [test-03][skip] 1 TestSkipWhile: skip_test.go:139: [test-03][skip] 2 TestSkipWhile: skip_test.go:139: [test-03][skip] 2 --- PASS: TestSkipWhile (0.00s) === RUN TestSkipWhileFn TestSkipWhileFn: skip_test.go:207: [test-02][skip] 2 TestSkipWhileFn: skip_test.go:207: [test-02][skip] 2 TestSkipWhileFn: skip_test.go:207: [test-03][skip] 2 TestSkipWhileFn: skip_test.go:207: [test-03][skip] 1 TestSkipWhileFn: skip_test.go:207: [test-03][skip] 1 TestSkipWhileFn: skip_test.go:207: [test-03][skip] 2 TestSkipWhileFn: skip_test.go:207: [test-03][skip] 2 --- PASS: TestSkipWhileFn (0.00s) PASS ok github.com/devlights/gomy/chans 0.003s
参考
- 作者:Katherine Cox-Buday
- 発売日: 2018/10/26
- メディア: 単行本(ソフトカバー)
関連記事
過去の記事については、以下のページからご参照下さい。
- いろいろ備忘録日記まとめ
サンプルコードは、以下の場所で公開しています。
- いろいろ備忘録日記サンプルソース置き場