いろいろ備忘録日記

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

Goメモ-85 (チャネルからのデータをフィルタリングする関数, Filter)

概要

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

パイプラインを使って処理する場合、やっぱり C# の Where とか Java の filter みたいなのが欲しいところですね。

てことで、サンプルつくってみました。

サンプル

package chans

// Filter -- 入力データチャネル in から取得したデータを predicate に渡して 真 となったデータを返すチャネルを生成します。
func Filter(done <-chan struct{}, in <-chan interface{}, predicate func(interface{}) bool) <-chan interface{} {
    out := make(chan interface{})

    go func() {
        defer close(out)

    ChLoop:
        for {
            select {
            case <-done:
                break ChLoop
            case v, ok := <-in:
                if !ok {
                    break ChLoop
                }

                if predicate(v) {
                    select {
                    case out <- v:
                    case <-done:
                    }
                }
            }
        }
    }()

    return out
}

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

以下テストコードです。

package chans

import (
    "reflect"
    "strings"
    "testing"
)

func TestFilter(t *testing.T) {
    type (
        testin struct {
            input     []interface{}
            predicate func(interface{}) bool
        }
        testout struct {
            result []interface{}
        }
        testcase struct {
            in  testin
            out testout
        }
    )

    cases := []testcase{
        {
            in: testin{
                input: []interface{}{"hello", "world"},
                predicate: func(v interface{}) bool {
                    if s, ok := v.(string); ok {
                        if strings.HasPrefix(s, "w") {
                            return true
                        }

                        return false
                    }

                    return false
                },
            },
            out: testout{result: []interface{}{"world"}},
        },
        {
            in: testin{
                input: []interface{}{1, 2, 3, 4, 5},
                predicate: func(v interface{}) bool {
                    if i, ok := v.(int); ok {
                        if i <= 3 {
                            return true
                        }

                        return false
                    }

                    return false
                },
            },
            out: testout{result: []interface{}{1, 2, 3}},
        },
    }

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

            inCh := make(chan interface{})
            go func() {
                defer close(inCh)
                for _, v := range c.in.input {
                    inCh <- v
                }
            }()

            results := make([]interface{}, 0, 0)
            for v := range Filter(done, inCh, c.in.predicate) {
                t.Logf("[test-%02d] %v", caseIndex, v)
                results = append(results, v)
            }

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

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

$ go test -v github.com/devlights/gomy/chans -run ^TestFilter$
=== RUN   TestFilter
    TestFilter: filter_test.go:76: [test-00] world
    TestFilter: filter_test.go:76: [test-01] 1
    TestFilter: filter_test.go:76: [test-01] 2
    TestFilter: filter_test.go:76: [test-01] 3
--- PASS: TestFilter (0.00s)
PASS
ok      github.com/devlights/gomy/chans 0.082s

参考

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.hatenablog.com


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

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

devlights.github.io

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

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

github.com

github.com

github.com