File: doc.go

package info (click to toggle)
golang-github-jkeiser-iter 0.0~git20200628.c8aa0ae-2
  • links: PTS, VCS
  • area: main
  • in suites: bookworm, experimental, forky, sid
  • size: 72 kB
  • sloc: makefile: 2
file content (125 lines) | stat: -rw-r--r-- 3,895 bytes parent folder | download
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
/*
Generic forward-only iterator that is safe and leak-free.

This package is intended to support forward-only iteration in a variety of use
cases while avoiding the normal errors and leaks that can happen with iterators
in Go. It provides mechanisms for map/select filtering, background iteration
through a goroutine, and error handling throughout.

The type of the iterator is interface{}, so it can store anything, at the cost
that you have to cast it back out when you use it. This package can be used as
is, or used as an example for creating your own forward-only iterators of more
specific types.

  sum := 0
  iterator.Each(func(item interface{}) {
    sum = sum + item.(int)
  })

Motivation

With the lack of generics and a builtin iterator pattern, iterators have been
the topic of much discussion in Go. Here are the discussions that inspired this:

http://ewencp.org/blog/golang-iterators/: Ewan Cheslack-Postava's
discussion of the major iteration patterns. Herein we have chosen the closure
pattern for iterator implementation, and given the choice of callback and
channel patterns for iteration callers.

http://blog.golang.org/pipelines: A March 2014 discussion of pipelines
on the go blog presents some of the pitfalls of channel iteration, suggesting
the "done" channel implementation to compensate.

Creating Iterators

Simple error- and cleanup-free iterators can be easily created:

  // Create a simple iterator from a function
  val := 1
  iterator := iter.NewSimple(func() interface{} {
    val = val * 2;
    return val > 2000 ? val : nil // nil terminates iteration
  })

Typically you will create iterators in packages ("please iterate over this
complicated thing").  You will often handle errors and have cleanup to do.
iter supports both of these. You can create a fully-functional iterator thusly:

  // Create a normal iterator parsing a file, close when done
  func ParseStream(reader io.ReadCloser) iter.Iterator {
    return iter.Iterator{
      Next: func() (iterator{}, error) {
        item, err := Parse()
        if item == nil && err == nil {
          return nil, iter.FINISHED
        }
        return item, err
      },
      Close: func() {
        reader.Close()
      }
    }
  }

Iterating

Callback iteration looks like this and handles any bookkeeping automatically:

  // Iterate over all values
  err := iterator.Each(func(item interface{}) {
    fmt.Println(item)
  })

Sometimes you need to handle errors:

  // Iterate over all values, terminating if processing has a problem
  var files []*File
  err := iterator.EachWithError(func(item interface{}) error {
    file, err = os.Open(item.(string))
    if err == nil {
      files = append(files, file)
    }
    return err
  })

Raw iteration looks like this:

  defer iterator.Close() // allow the iterator to clean itself up
  item, err := iterator.Next()
  for err == nil {
    ... // do stuff with value
    item, err = iterator.Next()
  }
  if err != iter.FINISHED {
    ... // handle error
  }

Background goroutine iteration (using channels) deserves special mention:

  // Produce the values in a goroutine, cleaning up safely afterwards.
  // This allows background iteration to continue at its own pace while we
  // perform blocking operations in the foreground.
  var responses []http.Response
  err := iterator.BackgroundEach(1000, func(item interface{}) error {
    response, err := http.Get(item.(string))
    if err == nil {
      responses = append(list, response)
    }
    return err
  })

Utilities

There are several useful functions provided to work with iterators:

  // Square the ints
  squaredIterator, err := iterator.Map(func(item interface{}) interface{} { item.int() * 2 })

  // Select non-nil values
  nonNilIterator, err := iterator.Select(func(item interface{}) bool) { item != nil })

  // Produce a list
  list, err := iterator.ToList()

*/
package iter