いろいろ備忘録日記

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

Goメモ-76 (ループ用のチャネル, Loop, LoopInfinite)

概要

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

書籍には記載されていないですが、チャネルさんが触ってて面白いので、ネタ的な関数をこれから数回分続けようかなと。

まず、ループする用のチャネルつくる関数です。普通に回した方がいいと思いますが、勉強としてメモメモ。。

パイプラインにつなぐことができるので、これはこれで便利かもしれませんね。

サンプル

package chans

import (
    "math"

    "github.com/devlights/gomy/enumerable"
)

// Loop -- 指定された開始と終了の間、データを返し続けるチャネルを生成します。
func Loop(done <-chan struct{}, start, end int) <-chan int {
    out := make(chan int)

    go func(done <-chan struct{}, start, end int) {
        defer close(out)

        fn := func() func() interface{} {
            r := enumerable.NewRange(start, end)
            return func() interface{} {
                defer r.Next()
                return r.Current()
            }
        }()

        repeatCh := RepeatFn(done, fn)
        takeCh := Take(done, repeatCh, end-start)

        for v := range takeCh {
            if i, ok := v.(int); ok {
                out <- i
            }
        }
    }(done, start, end)

    return out
}

// LoopInfinite -- 無限にループして、データを返し続けるチャネルを生成します。
func LoopInfinite(done <-chan struct{}) <-chan int {
    return Loop(done, 0, math.MaxInt64)
}

gomy/loop.go at master · devlights/gomy · GitHub

以下テストコードです。

package chans

import (
    "context"
    "reflect"
    "testing"
    "time"
)

func TestLoop(t *testing.T) {
    type (
        testin struct {
            start, end int
        }
        testout struct {
            result []int
        }
        testcase struct {
            in  testin
            out testout
        }
    )

    cases := []testcase{
        {
            in: testin{
                start: 0,
                end:   1,
            },
            out: testout{result: []int{0}},
        },
        {
            in: testin{
                start: 0,
                end:   5,
            },
            out: testout{result: []int{0, 1, 2, 3, 4}},
        },
    }

    for i, c := range cases {
        func() {
            done := make(chan struct{})
            defer close(done)

            r := make([]int, 0, 0)
            for v := range Loop(done, c.in.start, c.in.end) {
                t.Logf("[test-%02d] %v", i, v)
                r = append(r, v)
            }

            if !reflect.DeepEqual(c.out.result, r) {
                t.Errorf("want: %v\tgot: %v", c.out.result, r)
            }
        }()
    }
}

func TestLoopInfinite(t *testing.T) {
    type (
        testin struct {
            timeLimit time.Duration
        }
        testout struct {
        }
        testcase struct {
            in  testin
            out testout
        }
    )

    cases := []testcase{
        {
            in:  testin{timeLimit: 1 * time.Second},
            out: testout{},
        },
    }

    for i, c := range cases {
        func() {
            mainCtx, cancel := context.WithTimeout(context.Background(), c.in.timeLimit)
            defer cancel()

            r := make([]int, 0, 0)
            for v := range LoopInfinite(mainCtx.Done()) {
                t.Logf("[test-%02d] %v", i, v)
                r = append(r, v)

                <-time.After(100 * time.Millisecond)
            }

            if len(r) == 0 {
                t.Error("want: non-zero list\tgot zero list")
            }
        }()
    }
}

実行すると以下のようになります。

$ go test -v github.com/devlights/gomy/chans -run ^TestLoop.*$
=== RUN   TestLoop
    TestLoop: loop_test.go:48: [test-00] 0
    TestLoop: loop_test.go:48: [test-01] 0
    TestLoop: loop_test.go:48: [test-01] 1
    TestLoop: loop_test.go:48: [test-01] 2
    TestLoop: loop_test.go:48: [test-01] 3
    TestLoop: loop_test.go:48: [test-01] 4
--- PASS: TestLoop (0.00s)
=== RUN   TestLoopInfinite
    TestLoopInfinite: loop_test.go:86: [test-00] 0
    TestLoopInfinite: loop_test.go:86: [test-00] 1
    TestLoopInfinite: loop_test.go:86: [test-00] 2
    TestLoopInfinite: loop_test.go:86: [test-00] 3
    TestLoopInfinite: loop_test.go:86: [test-00] 4
    TestLoopInfinite: loop_test.go:86: [test-00] 5
    TestLoopInfinite: loop_test.go:86: [test-00] 6
    TestLoopInfinite: loop_test.go:86: [test-00] 7
    TestLoopInfinite: loop_test.go:86: [test-00] 8
    TestLoopInfinite: loop_test.go:86: [test-00] 9
    TestLoopInfinite: loop_test.go:86: [test-00] 10
--- PASS: TestLoopInfinite (1.11s)
PASS
ok      github.com/devlights/gomy/chans 1.228s

参考

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.github.io

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

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

github.com

github.com

github.com