-
Notifications
You must be signed in to change notification settings - Fork 1
/
Copy pathdoc.go
125 lines (98 loc) · 3.8 KB
/
doc.go
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