いろいろ備忘録日記

主に .NET とか Go とか Flutter とか Python絡みのメモを公開しています。

Goメモ-84 (指定した分チャネルからのデータをスキップする関数, Skip,SkipWhile)

概要

引き続き、小ネタチャネル関数の続き。( #関連記事 参照)

以前の記事で、指定した個数または条件に合致する間、チャネルから要素を取得する Take 関数について書きました。

devlights.hatenablog.com

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

参考

Go言語による並行処理

Go言語による並行処理

関連記事

devlights.hatenablog.com

devlights.hatenablog.com

devlights.hatenablog.com

devlights.hatenablog.com

devlights.hatenablog.com

devlights.hatenablog.com

devlights.hatenablog.com

devlights.hatenablog.com

devlights.hatenablog.com

devlights.hatenablog.com

devlights.hatenablog.com

devlights.hatenablog.com

devlights.hatenablog.com


過去の記事については、以下のページからご参照下さい。

  • いろいろ備忘録日記まとめ

devlights.github.io

サンプルコードは、以下の場所で公開しています。

  • いろいろ備忘録日記サンプルソース置き場

github.com

github.com

github.com