Goでも独自型をrangeしたい!chanを使う

Pythonでは__iter__メソッドを定義すれば((Rubyでは.eachメソッドを定義してEnumerableをincludeすれば、C#ではGetEnumerator()メソッドを定義すれば、 PHPではIteratorを実装すれば))、 晴れて独自のコレクション型でも、foreach文などでループできるようになるわけですが、でもGoにはそんな仕組みはありません。

まあ、妥協案としては、関数を引数にとる.Eachを作ることはできます。

sum := 0
myCollection.Each(func(x int) {
  sum += x
})

「でも、ぼくがつくったさいきょうの○○型をfor - range文で使いたいんだ!」

解法:channelを返す.Iter()

知ってる人は知っている、rangeにはchannelが渡せます。 A "for" statement with a "range" clause iterates through all entries of an array, slice, string or map, or values received on a channel.

he Go Programming Language Specification - The Go Programming Language

これを利用して、deckarep/golang-setSet型は、for文で使えるように.Iter()メソッドを定義してます。

package main

import (
    "fmt"
    mapset "github.com/deckarep/golang-set"
)

func main() {
    s := mapset.NewSet()

    s.Add(7)
    s.Add(11)
    s.Add(13)
    s.Add(11) // Setだから、重複は排除される

    c := s.Iter()
    fmt.Printf("%V\n", c) // %!V(<-chan interface {}=0xXXXXXXXXXX)

    for x := range s.Iter() { // range文で使える!
        fmt.Println(x)
    }
    // 7
    // 11
    // 13
}

やりましたね!

genericsが無いGolangで独自コレクションを作る機会は少ないでしょうけど、 channelがrangeできること自体は、知っていると得かもしれませんよ。