New upstream version 0.3.3+git20171004.e631537
Dr. Tobias Quathamer
6 years ago
0 | # Compiled Object files, Static and Dynamic libs (Shared Objects) | |
1 | *.o | |
2 | *.a | |
3 | *.so | |
4 | ||
5 | # Folders | |
6 | _obj | |
7 | _test | |
8 | ||
9 | # Architecture specific extensions/prefixes | |
10 | *.[568vq] | |
11 | [568vq].out | |
12 | ||
13 | *.cgo1.go | |
14 | *.cgo2.c | |
15 | _cgo_defun.c | |
16 | _cgo_gotypes.go | |
17 | _cgo_export.* | |
18 | ||
19 | _testmain.go | |
20 | ||
21 | *.exe | |
22 | *.test | |
23 | *.prof |
0 | language: go | |
1 | ||
2 | go: | |
3 | - 1.7 | |
4 | - 1.8 | |
5 | - 1.9 | |
6 | ||
7 | install: | |
8 | - go get -u github.com/golang/lint/golint | |
9 | ||
10 | script: | |
11 | - go test -v -cover | |
12 | - test -z "$(golint | tee /dev/stderr)" | |
13 | - test -z "$(go vet | tee /dev/stderr | grep error)" | |
14 | - test -z "$(gofmt -l *.go | tee /dev/stderr)"⏎ |
0 | MIT License | |
1 | ||
2 | Copyright (c) 2017 Martin Strobel | |
3 | ||
4 | Permission is hereby granted, free of charge, to any person obtaining a copy | |
5 | of this software and associated documentation files (the "Software"), to deal | |
6 | in the Software without restriction, including without limitation the rights | |
7 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell | |
8 | copies of the Software, and to permit persons to whom the Software is | |
9 | furnished to do so, subject to the following conditions: | |
10 | ||
11 | The above copyright notice and this permission notice shall be included in all | |
12 | copies or substantial portions of the Software. | |
13 | ||
14 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | |
15 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | |
16 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE | |
17 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER | |
18 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, | |
19 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE | |
20 | SOFTWARE. |
0 | # collection | |
1 | [![GoDoc](https://godoc.org/github.com/marstr/collection?status.svg)](https://godoc.org/github.com/marstr/collection) [![Build Status](https://travis-ci.org/marstr/collection.svg?branch=master)](https://travis-ci.org/marstr/collection) [![Go Report Card](https://goreportcard.com/badge/github.com/marstr/collection)](https://goreportcard.com/report/github.com/marstr/collection) | |
2 | ||
3 | # Usage | |
4 | ||
5 | ## Querying Collections | |
6 | Inspired by .NET's Linq, querying data structures used in this library is a snap! Build Go pipelines quickly and easily which will apply lambdas as they query your data structures. | |
7 | ||
8 | ### Slices | |
9 | Converting between slices and a queryable structure is as trivial as it should be. | |
10 | ``` Go | |
11 | original := []interface{}{"a", "b", "c"} | |
12 | subject := collection.AsEnumerable(original...) | |
13 | ||
14 | for entry := range subject.Enumerate(nil) { | |
15 | fmt.Println(entry) | |
16 | } | |
17 | // Output: | |
18 | // a | |
19 | // b | |
20 | // c | |
21 | ||
22 | ``` | |
23 | ||
24 | ### Where | |
25 | ``` Go | |
26 | subject := collection.AsEnumerable(1, 2, 3, 4, 5, 6) | |
27 | filtered := collection.Where(subject, func(num interface{}) bool{ | |
28 | return num.(int) > 3 | |
29 | }) | |
30 | for entry := range filtered.Enumerate(nil) { | |
31 | fmt.Println(entry) | |
32 | } | |
33 | // Output: | |
34 | // 4 | |
35 | // 5 | |
36 | // 6 | |
37 | ``` | |
38 | ### Select | |
39 | ``` Go | |
40 | subject := collection.AsEnumerable(1, 2, 3, 4, 5, 6) | |
41 | updated := collection.Select(subject, func(num interface{}) interface{}{ | |
42 | return num.(int) + 10 | |
43 | }) | |
44 | for entry := range updated.Enumerate(nil) { | |
45 | fmt.Println(entry) | |
46 | } | |
47 | // Output: | |
48 | // 11 | |
49 | // 12 | |
50 | // 13 | |
51 | // 14 | |
52 | // 15 | |
53 | // 16 | |
54 | ``` | |
55 | ||
56 | ## Queues | |
57 | ### Creating a Queue | |
58 | ||
59 | ``` Go | |
60 | done := make(chan struct{}) | |
61 | subject := collection.NewQueue(1, 2, 3, 5, 8, 13, 21) | |
62 | selected := subject.Enumerate(done).Skip(3).Take(3) | |
63 | for entry := range selected { | |
64 | fmt.Println(entry) | |
65 | } | |
66 | close(done) | |
67 | // Output: | |
68 | // 5 | |
69 | // 8 | |
70 | // 13 | |
71 | ``` | |
72 | ||
73 | ### Checking if a Queue is empty | |
74 | ``` Go | |
75 | populated := collection.NewQueue(1, 2, 3, 5, 8, 13) | |
76 | notPopulated := collection.NewQueue() | |
77 | fmt.Println(populated.IsEmpty()) | |
78 | fmt.Println(notPopulated.IsEmpty()) | |
79 | // Output: | |
80 | // false | |
81 | // true | |
82 | ``` | |
83 | ||
84 | # Versioning | |
85 | This library will conform to strict semantic versions as defined by [semver.org](http://semver.org/spec/v2.0.0.html)'s v2 specification. | |
86 | ||
87 | # Contributing | |
88 | I accept contributions! To ensure `glide` users and `go get` users retrieve the same commit, please submit PRs to the 'dev' branch. Remember to add tests! |
0 | package collection | |
1 | ||
2 | type fibonacciGenerator struct{} | |
3 | ||
4 | // Fibonacci is an Enumerable which will dynamically generate the fibonacci sequence. | |
5 | var Fibonacci Enumerable = fibonacciGenerator{} | |
6 | ||
7 | func (gen fibonacciGenerator) Enumerate(cancel <-chan struct{}) Enumerator { | |
8 | retval := make(chan interface{}) | |
9 | ||
10 | go func() { | |
11 | defer close(retval) | |
12 | a, b := 0, 1 | |
13 | ||
14 | for { | |
15 | select { | |
16 | case retval <- a: | |
17 | a, b = b, a+b | |
18 | case <-cancel: | |
19 | return | |
20 | } | |
21 | } | |
22 | }() | |
23 | ||
24 | return retval | |
25 | } |
0 | package collection | |
1 | ||
2 | import ( | |
3 | "errors" | |
4 | "os" | |
5 | "path/filepath" | |
6 | ) | |
7 | ||
8 | // DirectoryOptions is a means of configuring a `Directory` instance to including various children in its enumeration without | |
9 | // supplying a `Where` clause later. | |
10 | type DirectoryOptions uint | |
11 | ||
12 | // These constants define all of the supported options for configuring a `Directory` | |
13 | const ( | |
14 | DirectoryOptionsExcludeFiles = 1 << iota | |
15 | DirectoryOptionsExcludeDirectories | |
16 | DirectoryOptionsRecursive | |
17 | ) | |
18 | ||
19 | // Directory treats a filesystem path as a collection of filesystem entries, specifically a collection of directories and files. | |
20 | type Directory struct { | |
21 | Location string | |
22 | Options DirectoryOptions | |
23 | } | |
24 | ||
25 | func defaultEnumeratePredicate(loc string, info os.FileInfo) bool { | |
26 | return true | |
27 | } | |
28 | ||
29 | func (d Directory) applyOptions(loc string, info os.FileInfo) bool { | |
30 | if info.IsDir() && 0 != (d.Options&DirectoryOptionsExcludeDirectories) { | |
31 | return false | |
32 | } | |
33 | ||
34 | if !info.IsDir() && 0 != d.Options&DirectoryOptionsExcludeFiles { | |
35 | return false | |
36 | } | |
37 | ||
38 | return true | |
39 | } | |
40 | ||
41 | // Enumerate lists the items in a `Directory` | |
42 | func (d Directory) Enumerate(cancel <-chan struct{}) Enumerator { | |
43 | results := make(chan interface{}) | |
44 | ||
45 | go func() { | |
46 | defer close(results) | |
47 | ||
48 | filepath.Walk(d.Location, func(currentLocation string, info os.FileInfo, openErr error) (err error) { | |
49 | if openErr != nil { | |
50 | err = openErr | |
51 | return | |
52 | } | |
53 | ||
54 | if d.Location == currentLocation { | |
55 | return | |
56 | } | |
57 | ||
58 | if info.IsDir() && 0 == d.Options&DirectoryOptionsRecursive { | |
59 | err = filepath.SkipDir | |
60 | } | |
61 | ||
62 | if d.applyOptions(currentLocation, info) { | |
63 | select { | |
64 | case results <- currentLocation: | |
65 | // Intentionally Left Blank | |
66 | case <-cancel: | |
67 | err = errors.New("directory enumeration cancelled") | |
68 | } | |
69 | } | |
70 | ||
71 | return | |
72 | }) | |
73 | }() | |
74 | ||
75 | return results | |
76 | } |
0 | package collection | |
1 | ||
2 | import ( | |
3 | "fmt" | |
4 | "math" | |
5 | "path" | |
6 | "path/filepath" | |
7 | "testing" | |
8 | ) | |
9 | ||
10 | func TestEnumerateDirectoryOptions_UniqueBits(t *testing.T) { | |
11 | isPowerOfTwo := func(subject float64) bool { | |
12 | a := math.Abs(math.Log2(subject)) | |
13 | b := math.Floor(a) | |
14 | ||
15 | return a-b < .0000001 | |
16 | } | |
17 | ||
18 | if !isPowerOfTwo(64) { | |
19 | t.Log("isPowerOfTwo decided 64 is not a power of two.") | |
20 | t.FailNow() | |
21 | } | |
22 | ||
23 | if isPowerOfTwo(91) { | |
24 | t.Log("isPowerOfTwo decided 91 is a power of two.") | |
25 | t.FailNow() | |
26 | } | |
27 | ||
28 | seen := make(map[DirectoryOptions]struct{}) | |
29 | ||
30 | declared := []DirectoryOptions{ | |
31 | DirectoryOptionsExcludeFiles, | |
32 | DirectoryOptionsExcludeDirectories, | |
33 | DirectoryOptionsRecursive, | |
34 | } | |
35 | ||
36 | for _, option := range declared { | |
37 | if _, ok := seen[option]; ok { | |
38 | t.Logf("Option: %d has already been declared.", option) | |
39 | t.Fail() | |
40 | } | |
41 | seen[option] = struct{}{} | |
42 | ||
43 | if !isPowerOfTwo(float64(option)) { | |
44 | t.Logf("Option should have been a power of two, got %g instead.", float64(option)) | |
45 | t.Fail() | |
46 | } | |
47 | } | |
48 | } | |
49 | ||
50 | func ExampleDirectory_Enumerate() { | |
51 | traverser := Directory{ | |
52 | Location: ".", | |
53 | Options: DirectoryOptionsExcludeDirectories, | |
54 | } | |
55 | ||
56 | done := make(chan struct{}) | |
57 | ||
58 | filesOfInterest := traverser.Enumerate(done).Select(func(subject interface{}) (result interface{}) { | |
59 | cast, ok := subject.(string) | |
60 | if ok { | |
61 | result = path.Base(cast) | |
62 | } else { | |
63 | result = subject | |
64 | } | |
65 | return | |
66 | }).Where(func(subject interface{}) bool { | |
67 | cast, ok := subject.(string) | |
68 | if !ok { | |
69 | return false | |
70 | } | |
71 | return cast == "filesystem_test.go" | |
72 | }) | |
73 | ||
74 | for entry := range filesOfInterest { | |
75 | fmt.Println(entry.(string)) | |
76 | } | |
77 | close(done) | |
78 | ||
79 | // Output: filesystem_test.go | |
80 | } | |
81 | ||
82 | func TestDirectory_Enumerate(t *testing.T) { | |
83 | subject := Directory{ | |
84 | Location: filepath.Join(".", "testdata", "foo"), | |
85 | } | |
86 | ||
87 | testCases := []struct { | |
88 | options DirectoryOptions | |
89 | expected map[string]struct{} | |
90 | }{ | |
91 | { | |
92 | options: 0, | |
93 | expected: map[string]struct{}{ | |
94 | filepath.Join("testdata", "foo", "a.txt"): struct{}{}, | |
95 | filepath.Join("testdata", "foo", "c.txt"): struct{}{}, | |
96 | filepath.Join("testdata", "foo", "bar"): struct{}{}, | |
97 | }, | |
98 | }, | |
99 | { | |
100 | options: DirectoryOptionsExcludeFiles, | |
101 | expected: map[string]struct{}{ | |
102 | filepath.Join("testdata", "foo", "bar"): struct{}{}, | |
103 | }, | |
104 | }, | |
105 | { | |
106 | options: DirectoryOptionsExcludeDirectories, | |
107 | expected: map[string]struct{}{ | |
108 | filepath.Join("testdata", "foo", "a.txt"): struct{}{}, | |
109 | filepath.Join("testdata", "foo", "c.txt"): struct{}{}, | |
110 | }, | |
111 | }, | |
112 | { | |
113 | options: DirectoryOptionsRecursive, | |
114 | expected: map[string]struct{}{ | |
115 | filepath.Join("testdata", "foo", "bar"): struct{}{}, | |
116 | filepath.Join("testdata", "foo", "bar", "b.txt"): struct{}{}, | |
117 | filepath.Join("testdata", "foo", "a.txt"): struct{}{}, | |
118 | filepath.Join("testdata", "foo", "c.txt"): struct{}{}, | |
119 | }, | |
120 | }, | |
121 | { | |
122 | options: DirectoryOptionsExcludeFiles | DirectoryOptionsRecursive, | |
123 | expected: map[string]struct{}{ | |
124 | filepath.Join("testdata", "foo", "bar"): struct{}{}, | |
125 | }, | |
126 | }, | |
127 | { | |
128 | options: DirectoryOptionsRecursive | DirectoryOptionsExcludeDirectories, | |
129 | expected: map[string]struct{}{ | |
130 | filepath.Join("testdata", "foo", "a.txt"): struct{}{}, | |
131 | filepath.Join("testdata", "foo", "bar", "b.txt"): struct{}{}, | |
132 | filepath.Join("testdata", "foo", "c.txt"): struct{}{}, | |
133 | }, | |
134 | }, | |
135 | { | |
136 | options: DirectoryOptionsExcludeDirectories | DirectoryOptionsExcludeFiles, | |
137 | expected: map[string]struct{}{}, | |
138 | }, | |
139 | { | |
140 | options: DirectoryOptionsExcludeFiles | DirectoryOptionsRecursive | DirectoryOptionsExcludeDirectories, | |
141 | expected: map[string]struct{}{}, | |
142 | }, | |
143 | } | |
144 | ||
145 | for _, tc := range testCases { | |
146 | subject.Options = tc.options | |
147 | t.Run(fmt.Sprintf("%d", uint(tc.options)), func(t *testing.T) { | |
148 | for entry := range subject.Enumerate(nil) { | |
149 | cast := entry.(string) | |
150 | if _, ok := tc.expected[cast]; !ok { | |
151 | t.Logf("unexpected result: %q", cast) | |
152 | t.Fail() | |
153 | } | |
154 | delete(tc.expected, cast) | |
155 | } | |
156 | ||
157 | if len(tc.expected) != 0 { | |
158 | for unseenFile := range tc.expected { | |
159 | t.Logf("missing file: %q", unseenFile) | |
160 | } | |
161 | t.Fail() | |
162 | } | |
163 | }) | |
164 | } | |
165 | } |
0 | package collection | |
1 | ||
2 | import ( | |
3 | "bytes" | |
4 | "errors" | |
5 | "fmt" | |
6 | "strings" | |
7 | "sync" | |
8 | ) | |
9 | ||
10 | // LinkedList encapsulates a list where each entry is aware of only the next entry in the list. | |
11 | type LinkedList struct { | |
12 | first *llNode | |
13 | last *llNode | |
14 | length uint | |
15 | key sync.RWMutex | |
16 | } | |
17 | ||
18 | type llNode struct { | |
19 | payload interface{} | |
20 | next *llNode | |
21 | } | |
22 | ||
23 | // Comparator is a function which evaluates two values to determine their relation to one another. | |
24 | // - Zero is returned when `a` and `b` are equal. | |
25 | // - Positive numbers are returned when `a` is greater than `b`. | |
26 | // - Negative numbers are returned when `a` is less than `b`. | |
27 | type Comparator func(a, b interface{}) (int, error) | |
28 | ||
29 | // A collection of errors that may be thrown by functions in this file. | |
30 | var ( | |
31 | ErrUnexpectedType = errors.New("value was of an unexpected type") | |
32 | ) | |
33 | ||
34 | // NewLinkedList instantiates a new LinkedList with the entries provided. | |
35 | func NewLinkedList(entries ...interface{}) *LinkedList { | |
36 | list := &LinkedList{} | |
37 | ||
38 | for _, entry := range entries { | |
39 | list.AddBack(entry) | |
40 | } | |
41 | ||
42 | return list | |
43 | } | |
44 | ||
45 | // AddBack creates an entry in the LinkedList that is logically at the back of the list. | |
46 | func (list *LinkedList) AddBack(entry interface{}) { | |
47 | toAppend := &llNode{ | |
48 | payload: entry, | |
49 | } | |
50 | ||
51 | list.key.Lock() | |
52 | defer list.key.Unlock() | |
53 | ||
54 | list.length++ | |
55 | ||
56 | if list.first == nil { | |
57 | list.first = toAppend | |
58 | list.last = toAppend | |
59 | return | |
60 | } | |
61 | ||
62 | list.last.next = toAppend | |
63 | list.last = toAppend | |
64 | } | |
65 | ||
66 | // AddFront creates an entry in the LinkedList that is logically at the front of the list. | |
67 | func (list *LinkedList) AddFront(entry interface{}) { | |
68 | toAppend := &llNode{ | |
69 | payload: entry, | |
70 | } | |
71 | ||
72 | list.key.Lock() | |
73 | defer list.key.Unlock() | |
74 | ||
75 | list.length++ | |
76 | ||
77 | toAppend.next = list.first | |
78 | if list.first == nil { | |
79 | list.last = toAppend | |
80 | } | |
81 | ||
82 | list.first = toAppend | |
83 | } | |
84 | ||
85 | // Enumerate creates a new instance of Enumerable which can be executed on. | |
86 | func (list *LinkedList) Enumerate(cancel <-chan struct{}) Enumerator { | |
87 | retval := make(chan interface{}) | |
88 | ||
89 | go func() { | |
90 | list.key.RLock() | |
91 | defer list.key.RUnlock() | |
92 | defer close(retval) | |
93 | ||
94 | current := list.first | |
95 | for current != nil { | |
96 | select { | |
97 | case retval <- current.payload: | |
98 | break | |
99 | case <-cancel: | |
100 | return | |
101 | } | |
102 | current = current.next | |
103 | } | |
104 | }() | |
105 | ||
106 | return retval | |
107 | } | |
108 | ||
109 | // Get finds the value from the LinkedList. | |
110 | // pos is expressed as a zero-based index begining from the 'front' of the list. | |
111 | func (list *LinkedList) Get(pos uint) (interface{}, bool) { | |
112 | list.key.RLock() | |
113 | defer list.key.RUnlock() | |
114 | node, ok := get(list.first, pos) | |
115 | if ok { | |
116 | return node.payload, true | |
117 | } | |
118 | return nil, false | |
119 | } | |
120 | ||
121 | // IsEmpty tests the list to determine if it is populate or not. | |
122 | func (list *LinkedList) IsEmpty() bool { | |
123 | list.key.RLock() | |
124 | defer list.key.RUnlock() | |
125 | ||
126 | return list.first == nil | |
127 | } | |
128 | ||
129 | // Length returns the number of elements present in the LinkedList. | |
130 | func (list *LinkedList) Length() uint { | |
131 | list.key.RLock() | |
132 | defer list.key.RUnlock() | |
133 | ||
134 | return list.length | |
135 | } | |
136 | ||
137 | // PeekBack returns the entry logicall stored at the back of the list without removing it. | |
138 | func (list *LinkedList) PeekBack() (interface{}, bool) { | |
139 | list.key.RLock() | |
140 | defer list.key.RUnlock() | |
141 | ||
142 | if list.last == nil { | |
143 | return nil, false | |
144 | } | |
145 | return list.last.payload, true | |
146 | } | |
147 | ||
148 | // PeekFront returns the entry logically stored at the front of this list without removing it. | |
149 | func (list *LinkedList) PeekFront() (interface{}, bool) { | |
150 | list.key.RLock() | |
151 | defer list.key.RUnlock() | |
152 | ||
153 | if list.first == nil { | |
154 | return nil, false | |
155 | } | |
156 | return list.first.payload, true | |
157 | } | |
158 | ||
159 | // RemoveFront returns the entry logically stored at the front of this list and removes it. | |
160 | func (list *LinkedList) RemoveFront() (interface{}, bool) { | |
161 | list.key.Lock() | |
162 | defer list.key.Unlock() | |
163 | ||
164 | if list.first == nil { | |
165 | return nil, false | |
166 | } | |
167 | ||
168 | retval := list.first.payload | |
169 | ||
170 | list.first = list.first.next | |
171 | list.length-- | |
172 | ||
173 | if 0 == list.length { | |
174 | list.last = nil | |
175 | } | |
176 | ||
177 | return retval, true | |
178 | } | |
179 | ||
180 | // RemoveBack returns the entry logically stored at the back of this list and removes it. | |
181 | func (list *LinkedList) RemoveBack() (interface{}, bool) { | |
182 | list.key.Lock() | |
183 | defer list.key.Unlock() | |
184 | ||
185 | if list.last == nil { | |
186 | return nil, false | |
187 | } | |
188 | ||
189 | retval := list.last.payload | |
190 | list.length-- | |
191 | ||
192 | if list.length == 0 { | |
193 | list.first = nil | |
194 | } else { | |
195 | node, _ := get(list.first, list.length-1) | |
196 | node.next = nil | |
197 | } | |
198 | return retval, true | |
199 | } | |
200 | ||
201 | // Sort rearranges the positions of the entries in this list so that they are | |
202 | // ascending. | |
203 | func (list *LinkedList) Sort(comparator Comparator) error { | |
204 | list.key.Lock() | |
205 | defer list.key.Unlock() | |
206 | var err error | |
207 | list.first, err = mergeSort(list.first, comparator) | |
208 | if err != nil { | |
209 | return err | |
210 | } | |
211 | list.last = findLast(list.first) | |
212 | return err | |
213 | } | |
214 | ||
215 | // Sorta rearranges the position of string entries in this list so that they | |
216 | // are ascending. | |
217 | func (list *LinkedList) Sorta() error { | |
218 | list.key.Lock() | |
219 | defer list.key.Unlock() | |
220 | ||
221 | var err error | |
222 | list.first, err = mergeSort(list.first, func(a, b interface{}) (int, error) { | |
223 | castA, ok := a.(string) | |
224 | if !ok { | |
225 | return 0, ErrUnexpectedType | |
226 | } | |
227 | castB, ok := b.(string) | |
228 | if !ok { | |
229 | return 0, ErrUnexpectedType | |
230 | } | |
231 | ||
232 | return strings.Compare(castA, castB), nil | |
233 | }) | |
234 | list.last = findLast(list.first) | |
235 | return err | |
236 | } | |
237 | ||
238 | // Sorti rearranges the position of integer entries in this list so that they | |
239 | // are ascending. | |
240 | func (list *LinkedList) Sorti() (err error) { | |
241 | list.key.Lock() | |
242 | defer list.key.Unlock() | |
243 | ||
244 | list.first, err = mergeSort(list.first, func(a, b interface{}) (int, error) { | |
245 | castA, ok := a.(int) | |
246 | if !ok { | |
247 | return 0, ErrUnexpectedType | |
248 | } | |
249 | castB, ok := b.(int) | |
250 | if !ok { | |
251 | return 0, ErrUnexpectedType | |
252 | } | |
253 | ||
254 | return castA - castB, nil | |
255 | }) | |
256 | if err != nil { | |
257 | return | |
258 | } | |
259 | list.last = findLast(list.first) | |
260 | return | |
261 | } | |
262 | ||
263 | // String prints upto the first fifteen elements of the list in string format. | |
264 | func (list *LinkedList) String() string { | |
265 | list.key.RLock() | |
266 | defer list.key.RUnlock() | |
267 | ||
268 | builder := bytes.NewBufferString("[") | |
269 | current := list.first | |
270 | for i := 0; i < 15 && current != nil; i++ { | |
271 | builder.WriteString(fmt.Sprintf("%v ", current.payload)) | |
272 | current = current.next | |
273 | } | |
274 | if current == nil || current.next == nil { | |
275 | builder.Truncate(builder.Len() - 1) | |
276 | } else { | |
277 | builder.WriteString("...") | |
278 | } | |
279 | builder.WriteRune(']') | |
280 | return builder.String() | |
281 | } | |
282 | ||
283 | // Swap switches the positions in which two values are stored in this list. | |
284 | // x and y represent the indexes of the items that should be swapped. | |
285 | func (list *LinkedList) Swap(x, y uint) error { | |
286 | list.key.Lock() | |
287 | defer list.key.Unlock() | |
288 | ||
289 | var xNode, yNode *llNode | |
290 | if temp, ok := get(list.first, x); ok { | |
291 | xNode = temp | |
292 | } else { | |
293 | return fmt.Errorf("index out of bounds 'x', wanted less than %d got %d", list.length, x) | |
294 | } | |
295 | if temp, ok := get(list.first, y); ok { | |
296 | yNode = temp | |
297 | } else { | |
298 | return fmt.Errorf("index out of bounds 'y', wanted less than %d got %d", list.length, y) | |
299 | } | |
300 | ||
301 | temp := xNode.payload | |
302 | xNode.payload = yNode.payload | |
303 | yNode.payload = temp | |
304 | return nil | |
305 | } | |
306 | ||
307 | // ToSlice converts the contents of the LinkedList into a slice. | |
308 | func (list *LinkedList) ToSlice() []interface{} { | |
309 | return list.Enumerate(nil).ToSlice() | |
310 | } | |
311 | ||
312 | func findLast(head *llNode) *llNode { | |
313 | if head == nil { | |
314 | return nil | |
315 | } | |
316 | current := head | |
317 | for current.next != nil { | |
318 | current = current.next | |
319 | } | |
320 | return current | |
321 | } | |
322 | ||
323 | func get(head *llNode, pos uint) (*llNode, bool) { | |
324 | for i := uint(0); i < pos; i++ { | |
325 | if head == nil { | |
326 | return nil, false | |
327 | } | |
328 | head = head.next | |
329 | } | |
330 | return head, true | |
331 | } | |
332 | ||
333 | // merge takes two sorted lists and merges them into one sorted list. | |
334 | // Behavior is undefined when you pass a non-sorted list as `left` or `right` | |
335 | func merge(left, right *llNode, comparator Comparator) (first *llNode, err error) { | |
336 | curLeft := left | |
337 | curRight := right | |
338 | ||
339 | var last *llNode | |
340 | ||
341 | appendResults := func(updated *llNode) { | |
342 | if last == nil { | |
343 | last = updated | |
344 | } else { | |
345 | last.next = updated | |
346 | last = last.next | |
347 | } | |
348 | if first == nil { | |
349 | first = last | |
350 | } | |
351 | } | |
352 | ||
353 | for curLeft != nil && curRight != nil { | |
354 | var res int | |
355 | if res, err = comparator(curLeft.payload, curRight.payload); nil != err { | |
356 | break // Don't return, stitch the remaining elements back on. | |
357 | } else if res < 0 { | |
358 | appendResults(curLeft) | |
359 | curLeft = curLeft.next | |
360 | } else { | |
361 | appendResults(curRight) | |
362 | curRight = curRight.next | |
363 | } | |
364 | } | |
365 | ||
366 | if curLeft != nil { | |
367 | appendResults(curLeft) | |
368 | } | |
369 | if curRight != nil { | |
370 | appendResults(curRight) | |
371 | } | |
372 | return | |
373 | } | |
374 | ||
375 | func mergeSort(head *llNode, comparator Comparator) (*llNode, error) { | |
376 | if head == nil { | |
377 | return nil, nil | |
378 | } | |
379 | ||
380 | left, right := split(head) | |
381 | ||
382 | repair := func(left, right *llNode) *llNode { | |
383 | lastLeft := findLast(left) | |
384 | lastLeft.next = right | |
385 | return left | |
386 | } | |
387 | ||
388 | var err error | |
389 | if left != nil && left.next != nil { | |
390 | left, err = mergeSort(left, comparator) | |
391 | if err != nil { | |
392 | return repair(left, right), err | |
393 | } | |
394 | } | |
395 | if right != nil && right.next != nil { | |
396 | right, err = mergeSort(right, comparator) | |
397 | if err != nil { | |
398 | return repair(left, right), err | |
399 | } | |
400 | } | |
401 | ||
402 | return merge(left, right, comparator) | |
403 | } | |
404 | ||
405 | // split breaks a list in half. | |
406 | func split(head *llNode) (left, right *llNode) { | |
407 | left = head | |
408 | if head == nil || head.next == nil { | |
409 | return | |
410 | } | |
411 | right = head | |
412 | sprinter := head | |
413 | prev := head | |
414 | for sprinter != nil && sprinter.next != nil { | |
415 | prev = right | |
416 | right = right.next | |
417 | sprinter = sprinter.next.next | |
418 | } | |
419 | prev.next = nil | |
420 | return | |
421 | } |
0 | package collection | |
1 | ||
2 | import "fmt" | |
3 | import "testing" | |
4 | ||
5 | func ExampleLinkedList_AddFront() { | |
6 | subject := NewLinkedList(2, 3) | |
7 | subject.AddFront(1) | |
8 | result, _ := subject.PeekFront() | |
9 | fmt.Println(result) | |
10 | // Output: 1 | |
11 | } | |
12 | ||
13 | func ExampleLinkedList_AddBack() { | |
14 | subject := NewLinkedList(2, 3, 5) | |
15 | subject.AddBack(8) | |
16 | result, _ := subject.PeekBack() | |
17 | fmt.Println(result) | |
18 | fmt.Println(subject.Length()) | |
19 | // Output: | |
20 | // 8 | |
21 | // 4 | |
22 | } | |
23 | ||
24 | func ExampleLinkedList_Enumerate() { | |
25 | subject := NewLinkedList(2, 3, 5, 8) | |
26 | results := subject.Enumerate(nil).Select(func(a interface{}) interface{} { | |
27 | return -1 * a.(int) | |
28 | }) | |
29 | for entry := range results { | |
30 | fmt.Println(entry) | |
31 | } | |
32 | // Output: | |
33 | // -2 | |
34 | // -3 | |
35 | // -5 | |
36 | // -8 | |
37 | } | |
38 | ||
39 | func ExampleLinkedList_Get() { | |
40 | subject := NewLinkedList(2, 3, 5, 8) | |
41 | val, _ := subject.Get(2) | |
42 | fmt.Println(val) | |
43 | // Output: 5 | |
44 | } | |
45 | ||
46 | func TestLinkedList_Get_OutsideBounds(t *testing.T) { | |
47 | subject := NewLinkedList(2, 3, 5, 8, 13, 21) | |
48 | result, ok := subject.Get(10) | |
49 | if !(result == nil && ok == false) { | |
50 | t.Logf("got: %v %v\nwant: %v %v", result, ok, nil, false) | |
51 | t.Fail() | |
52 | } | |
53 | } | |
54 | ||
55 | func ExampleNewLinkedList() { | |
56 | subject1 := NewLinkedList('a', 'b', 'c', 'd', 'e') | |
57 | fmt.Println(subject1.Length()) | |
58 | ||
59 | slice := []interface{}{1, 2, 3, 4, 5, 6} | |
60 | subject2 := NewLinkedList(slice...) | |
61 | fmt.Println(subject2.Length()) | |
62 | // Output: | |
63 | // 5 | |
64 | // 6 | |
65 | } | |
66 | ||
67 | func TestLinkedList_findLast_empty(t *testing.T) { | |
68 | if result := findLast(nil); result != nil { | |
69 | t.Logf("got: %v\nwant: %v", result, nil) | |
70 | } | |
71 | } | |
72 | ||
73 | func TestLinkedList_merge(t *testing.T) { | |
74 | testCases := []struct { | |
75 | Left *LinkedList | |
76 | Right *LinkedList | |
77 | Expected []int | |
78 | Comp Comparator | |
79 | }{ | |
80 | { | |
81 | NewLinkedList(1, 3, 5), | |
82 | NewLinkedList(2, 4), | |
83 | []int{1, 2, 3, 4, 5}, | |
84 | UncheckedComparatori, | |
85 | }, | |
86 | { | |
87 | NewLinkedList(1, 2, 3), | |
88 | NewLinkedList(), | |
89 | []int{1, 2, 3}, | |
90 | UncheckedComparatori, | |
91 | }, | |
92 | { | |
93 | NewLinkedList(), | |
94 | NewLinkedList(1, 2, 3), | |
95 | []int{1, 2, 3}, | |
96 | UncheckedComparatori, | |
97 | }, | |
98 | { | |
99 | NewLinkedList(), | |
100 | NewLinkedList(), | |
101 | []int{}, | |
102 | UncheckedComparatori, | |
103 | }, | |
104 | { | |
105 | NewLinkedList(1), | |
106 | NewLinkedList(1), | |
107 | []int{1, 1}, | |
108 | UncheckedComparatori, | |
109 | }, | |
110 | { | |
111 | NewLinkedList(2), | |
112 | NewLinkedList(1), | |
113 | []int{1, 2}, | |
114 | UncheckedComparatori, | |
115 | }, | |
116 | { | |
117 | NewLinkedList(3), | |
118 | NewLinkedList(), | |
119 | []int{3}, | |
120 | UncheckedComparatori, | |
121 | }, | |
122 | { | |
123 | NewLinkedList(), | |
124 | NewLinkedList(10), | |
125 | []int{10}, | |
126 | UncheckedComparatori, | |
127 | }, | |
128 | } | |
129 | ||
130 | for _, tc := range testCases { | |
131 | t.Run("", func(t *testing.T) { | |
132 | result, err := merge(tc.Left.first, tc.Right.first, tc.Comp) | |
133 | if err != nil { | |
134 | t.Error(err) | |
135 | } | |
136 | ||
137 | i := 0 | |
138 | for cursor := result; cursor != nil; cursor, i = cursor.next, i+1 { | |
139 | if cursor.payload != tc.Expected[i] { | |
140 | t.Logf("got: %d want: %d", cursor.payload.(int), tc.Expected[i]) | |
141 | t.Fail() | |
142 | } | |
143 | } | |
144 | ||
145 | if expectedLength := len(tc.Expected); i != expectedLength { | |
146 | t.Logf("Unexpected length:\n\tgot: %d\n\twant: %d", i, expectedLength) | |
147 | t.Fail() | |
148 | } | |
149 | }) | |
150 | } | |
151 | } | |
152 | ||
153 | func TestLinkedList_mergeSort_repair(t *testing.T) { | |
154 | testCases := []*LinkedList{ | |
155 | NewLinkedList(1, 2, "str1", 4, 5, 6), | |
156 | NewLinkedList(1, 2, 3, "str1", 5, 6), | |
157 | NewLinkedList(1, 'a', 3, 4, 5, 6), | |
158 | NewLinkedList(1, 2, 3, 4, 5, uint(8)), | |
159 | NewLinkedList("alpha", 0), | |
160 | NewLinkedList(0, "kappa"), | |
161 | } | |
162 | ||
163 | for _, tc := range testCases { | |
164 | t.Run(tc.String(), func(t *testing.T) { | |
165 | originalLength := tc.Length() | |
166 | originalElements := tc.Enumerate(nil).ToSlice() | |
167 | originalContents := tc.String() | |
168 | ||
169 | if err := tc.Sorti(); err != ErrUnexpectedType { | |
170 | t.Log("`Sorti() should have thrown ErrUnexpectedType") | |
171 | t.Fail() | |
172 | } | |
173 | ||
174 | t.Logf("Contents:\n\tOriginal: \t%s\n\tPost Merge: \t%s", originalContents, tc.String()) | |
175 | ||
176 | if newLength := tc.Length(); newLength != originalLength { | |
177 | t.Logf("Length changed. got: %d want: %d", newLength, originalLength) | |
178 | t.Fail() | |
179 | } | |
180 | ||
181 | remaining := tc.Enumerate(nil).ToSlice() | |
182 | ||
183 | for _, desired := range originalElements { | |
184 | found := false | |
185 | for i, got := range remaining { | |
186 | if got == desired { | |
187 | remaining = append(remaining[:i], remaining[i+1:]...) | |
188 | found = true | |
189 | break | |
190 | } | |
191 | } | |
192 | ||
193 | if !found { | |
194 | t.Logf("couldn't find element: %v", desired) | |
195 | t.Fail() | |
196 | } | |
197 | } | |
198 | }) | |
199 | } | |
200 | } | |
201 | ||
202 | func ExampleLinkedList_Sort() { | |
203 | // Sorti sorts into ascending order, this example demonstrates sorting | |
204 | // into descending order. | |
205 | subject := NewLinkedList(2, 4, 3, 5, 7, 7) | |
206 | subject.Sort(func(a, b interface{}) (int, error) { | |
207 | castA, ok := a.(int) | |
208 | if !ok { | |
209 | return 0, ErrUnexpectedType | |
210 | } | |
211 | castB, ok := b.(int) | |
212 | if !ok { | |
213 | return 0, ErrUnexpectedType | |
214 | } | |
215 | ||
216 | return castB - castA, nil | |
217 | }) | |
218 | fmt.Println(subject) | |
219 | // Output: [7 7 5 4 3 2] | |
220 | } | |
221 | ||
222 | func ExampleLinkedList_Sorta() { | |
223 | subject := NewLinkedList("charlie", "alfa", "bravo", "delta") | |
224 | subject.Sorta() | |
225 | for _, entry := range subject.ToSlice() { | |
226 | fmt.Println(entry.(string)) | |
227 | } | |
228 | // Output: | |
229 | // alfa | |
230 | // bravo | |
231 | // charlie | |
232 | // delta | |
233 | } | |
234 | ||
235 | func ExampleLinkedList_Sorti() { | |
236 | subject := NewLinkedList(7, 3, 2, 2, 3, 6) | |
237 | subject.Sorti() | |
238 | fmt.Println(subject) | |
239 | // Output: [2 2 3 3 6 7] | |
240 | } | |
241 | ||
242 | func TestLinkedList_Sorti(t *testing.T) { | |
243 | testCases := []struct { | |
244 | *LinkedList | |
245 | Expected []int | |
246 | }{ | |
247 | { | |
248 | NewLinkedList(), | |
249 | []int{}, | |
250 | }, | |
251 | { | |
252 | NewLinkedList(1, 2, 3, 4), | |
253 | []int{1, 2, 3, 4}, | |
254 | }, | |
255 | { | |
256 | NewLinkedList(0, -1, 2, 8, 9), | |
257 | []int{-1, 0, 2, 8, 9}, | |
258 | }, | |
259 | } | |
260 | ||
261 | for _, tc := range testCases { | |
262 | t.Run(tc.String(), func(t *testing.T) { | |
263 | if err := tc.Sorti(); err != nil { | |
264 | t.Error(err) | |
265 | } | |
266 | ||
267 | sorted := tc.ToSlice() | |
268 | ||
269 | if countSorted, countExpected := len(sorted), len(tc.Expected); countSorted != countExpected { | |
270 | t.Logf("got: %d want: %d", countSorted, countExpected) | |
271 | t.FailNow() | |
272 | } | |
273 | ||
274 | for i, entry := range sorted { | |
275 | cast, ok := entry.(int) | |
276 | if !ok { | |
277 | t.Errorf("Element was not an int: %v", entry) | |
278 | } | |
279 | ||
280 | if cast != tc.Expected[i] { | |
281 | t.Logf("got: %d want: %d at: %d", cast, tc.Expected[i], i) | |
282 | t.Fail() | |
283 | } | |
284 | } | |
285 | }) | |
286 | } | |
287 | } | |
288 | ||
289 | func TestLinkedList_split_Even(t *testing.T) { | |
290 | subject := NewLinkedList(1, 2, 3, 4) | |
291 | ||
292 | left, right := split(subject.first) | |
293 | if left == nil { | |
294 | t.Logf("unexpected nil value for left") | |
295 | t.Fail() | |
296 | } else if left.payload != 1 { | |
297 | t.Logf("got: %d\nwant: %d", left.payload, 1) | |
298 | t.Fail() | |
299 | } | |
300 | ||
301 | if right == nil { | |
302 | t.Logf("unexpected nil for right") | |
303 | t.Fail() | |
304 | } else if right.payload != 3 { | |
305 | t.Logf("got: %d\nwant: %d", right.payload, 3) | |
306 | } | |
307 | } | |
308 | ||
309 | func TestLinkedList_split_Odd(t *testing.T) { | |
310 | subject := NewLinkedList(1, 2, 3, 4, 5) | |
311 | ||
312 | left, right := split(subject.first) | |
313 | ||
314 | if left == nil { | |
315 | t.Logf("unexpected nil value for left") | |
316 | t.Fail() | |
317 | } else if left.payload != 1 { | |
318 | t.Logf("got: %d\n want: %d", left.payload, 1) | |
319 | t.Fail() | |
320 | } else if last := findLast(left).payload; last != 2 { | |
321 | t.Logf("got:\n%d\nwant:\n%d", last, 2) | |
322 | t.Fail() | |
323 | } | |
324 | ||
325 | if right == nil { | |
326 | t.Logf("unexpected nil value for right") | |
327 | t.Fail() | |
328 | } else if right.payload != 3 { | |
329 | t.Logf("got:\n%d\nwant:\n%d", right.payload, 3) | |
330 | t.Fail() | |
331 | } else if last := findLast(right).payload; last != 5 { | |
332 | t.Logf("got:\n%d\nwant:%d", last, 5) | |
333 | } | |
334 | } | |
335 | ||
336 | func TestLinkedList_split_Empty(t *testing.T) { | |
337 | subject := NewLinkedList() | |
338 | ||
339 | left, right := split(subject.first) | |
340 | ||
341 | if left != nil { | |
342 | t.Logf("got: %v\nwant: %v", left, nil) | |
343 | t.Fail() | |
344 | } | |
345 | ||
346 | if right != nil { | |
347 | t.Logf("got: %v\nwant: %v", right, nil) | |
348 | t.Fail() | |
349 | } | |
350 | } | |
351 | ||
352 | func TestLinkedList_split_Single(t *testing.T) { | |
353 | subject := NewLinkedList(1) | |
354 | ||
355 | left, right := split(subject.first) | |
356 | ||
357 | if left == nil { | |
358 | t.Logf("unexpected nil value for left") | |
359 | t.Fail() | |
360 | } else if left.payload != 1 { | |
361 | t.Logf("got: %d\nwant: %d", left.payload, 1) | |
362 | t.Fail() | |
363 | } | |
364 | ||
365 | if right != nil { | |
366 | t.Logf("got: %v\nwant: %v", right, nil) | |
367 | t.Fail() | |
368 | } | |
369 | ||
370 | if last := findLast(left).payload; last != 1 { | |
371 | t.Logf("got:\n%d\nwant:\n%d", last, 1) | |
372 | t.Fail() | |
373 | } | |
374 | } | |
375 | ||
376 | func TestLinkedList_split_Double(t *testing.T) { | |
377 | subject := NewLinkedList(1, 2) | |
378 | left, right := split(subject.first) | |
379 | ||
380 | if left == nil { | |
381 | t.Logf("unexpected nil value for left") | |
382 | t.Fail() | |
383 | } else if left.payload != 1 { | |
384 | t.Logf("got: %d\nwant: %d", left.payload, 1) | |
385 | } | |
386 | ||
387 | if right == nil { | |
388 | t.Logf("unexpected nil value for right") | |
389 | t.Fail() | |
390 | } else if right.payload != 2 { | |
391 | t.Logf("got: %d\nwant: %d", right.payload, 2) | |
392 | } | |
393 | } | |
394 | ||
395 | func UncheckedComparatori(a, b interface{}) (int, error) { | |
396 | return a.(int) - b.(int), nil | |
397 | } | |
398 | ||
399 | func ExampleLinkedList_String() { | |
400 | subject1 := NewLinkedList() | |
401 | for i := 0; i < 20; i++ { | |
402 | subject1.AddBack(i) | |
403 | } | |
404 | fmt.Println(subject1) | |
405 | ||
406 | subject2 := NewLinkedList(1, 2, 3) | |
407 | fmt.Println(subject2) | |
408 | // Output: | |
409 | // [0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 ...] | |
410 | // [1 2 3] | |
411 | } | |
412 | ||
413 | func ExampleLinkedList_Swap() { | |
414 | subject := NewLinkedList(2, 3, 5, 8, 13) | |
415 | subject.Swap(1, 3) | |
416 | fmt.Println(subject) | |
417 | // Output: [2 8 5 3 13] | |
418 | } | |
419 | ||
420 | func TestLinkedList_Swap_OutOfBounds(t *testing.T) { | |
421 | subject := NewLinkedList(2, 3) | |
422 | if err := subject.Swap(0, 8); err == nil { | |
423 | t.Log("swap should have failed on y") | |
424 | t.Fail() | |
425 | } | |
426 | ||
427 | if err := subject.Swap(11, 1); err == nil { | |
428 | t.Logf("swap shoud have failed on x") | |
429 | t.Fail() | |
430 | } | |
431 | ||
432 | if count := subject.Length(); count != 2 { | |
433 | t.Logf("got: %d\nwant: %d", count, 2) | |
434 | t.Fail() | |
435 | } | |
436 | ||
437 | wantStr := "[2 3]" | |
438 | gotStr := subject.String() | |
439 | if wantStr != gotStr { | |
440 | t.Logf("got: %s\nwant: %s", gotStr, wantStr) | |
441 | t.Fail() | |
442 | } | |
443 | } |
0 | package collection | |
1 | ||
2 | import ( | |
3 | "bytes" | |
4 | "fmt" | |
5 | "sync" | |
6 | ) | |
7 | ||
8 | // List is a dynamically sized list akin to List in the .NET world, | |
9 | // ArrayList in the Java world, or vector in the C++ world. | |
10 | type List struct { | |
11 | underlyer []interface{} | |
12 | key sync.RWMutex | |
13 | } | |
14 | ||
15 | // NewList creates a new list which contains the elements provided. | |
16 | func NewList(entries ...interface{}) *List { | |
17 | return &List{ | |
18 | underlyer: entries, | |
19 | } | |
20 | } | |
21 | ||
22 | // Add appends an entry to the logical end of the List. | |
23 | func (l *List) Add(entries ...interface{}) { | |
24 | l.key.Lock() | |
25 | defer l.key.Unlock() | |
26 | l.underlyer = append(l.underlyer, entries...) | |
27 | } | |
28 | ||
29 | // AddAt injects values beginning at `pos`. If multiple values | |
30 | // are provided in `entries` they are placed in the same order | |
31 | // they are provided. | |
32 | func (l *List) AddAt(pos uint, entries ...interface{}) { | |
33 | l.key.Lock() | |
34 | defer l.key.Unlock() | |
35 | ||
36 | l.underlyer = append(l.underlyer[:pos], append(entries, l.underlyer[pos:]...)...) | |
37 | } | |
38 | ||
39 | // Enumerate lists each element present in the collection | |
40 | func (l *List) Enumerate(cancel <-chan struct{}) Enumerator { | |
41 | retval := make(chan interface{}) | |
42 | ||
43 | go func() { | |
44 | l.key.RLock() | |
45 | defer l.key.RUnlock() | |
46 | defer close(retval) | |
47 | ||
48 | for _, entry := range l.underlyer { | |
49 | select { | |
50 | case retval <- entry: | |
51 | break | |
52 | case <-cancel: | |
53 | return | |
54 | } | |
55 | } | |
56 | }() | |
57 | ||
58 | return retval | |
59 | } | |
60 | ||
61 | // Get retreives the value stored in a particular position of the list. | |
62 | // If no item exists at the given position, the second parameter will be | |
63 | // returned as false. | |
64 | func (l *List) Get(pos uint) (interface{}, bool) { | |
65 | l.key.RLock() | |
66 | defer l.key.RUnlock() | |
67 | ||
68 | if pos > uint(len(l.underlyer)) { | |
69 | return nil, false | |
70 | } | |
71 | return l.underlyer[pos], true | |
72 | } | |
73 | ||
74 | // IsEmpty tests to see if this List has any elements present. | |
75 | func (l *List) IsEmpty() bool { | |
76 | l.key.RLock() | |
77 | defer l.key.RUnlock() | |
78 | return 0 == len(l.underlyer) | |
79 | } | |
80 | ||
81 | // Length returns the number of elements in the List. | |
82 | func (l *List) Length() uint { | |
83 | l.key.RLock() | |
84 | defer l.key.RUnlock() | |
85 | return uint(len(l.underlyer)) | |
86 | } | |
87 | ||
88 | // Remove retreives a value from this List and shifts all other values. | |
89 | func (l *List) Remove(pos uint) (interface{}, bool) { | |
90 | l.key.Lock() | |
91 | defer l.key.Unlock() | |
92 | ||
93 | if pos > uint(len(l.underlyer)) { | |
94 | return nil, false | |
95 | } | |
96 | retval := l.underlyer[pos] | |
97 | l.underlyer = append(l.underlyer[:pos], l.underlyer[pos+1:]...) | |
98 | return retval, true | |
99 | } | |
100 | ||
101 | // Set updates the value stored at a given position in the List. | |
102 | func (l *List) Set(pos uint, val interface{}) bool { | |
103 | l.key.Lock() | |
104 | defer l.key.Unlock() | |
105 | var retval bool | |
106 | count := uint(len(l.underlyer)) | |
107 | if pos > count { | |
108 | retval = false | |
109 | } else { | |
110 | l.underlyer[pos] = val | |
111 | retval = true | |
112 | } | |
113 | return retval | |
114 | } | |
115 | ||
116 | // String generates a textual representation of the List for the sake of debugging. | |
117 | func (l *List) String() string { | |
118 | l.key.RLock() | |
119 | defer l.key.RUnlock() | |
120 | ||
121 | builder := bytes.NewBufferString("[") | |
122 | ||
123 | for i, entry := range l.underlyer { | |
124 | if i >= 15 { | |
125 | builder.WriteString("... ") | |
126 | break | |
127 | } | |
128 | builder.WriteString(fmt.Sprintf("%v ", entry)) | |
129 | } | |
130 | builder.Truncate(builder.Len() - 1) | |
131 | builder.WriteRune(']') | |
132 | return builder.String() | |
133 | } | |
134 | ||
135 | // Swap switches the values that are stored at positions `x` and `y` | |
136 | func (l *List) Swap(x, y uint) bool { | |
137 | l.key.Lock() | |
138 | defer l.key.Unlock() | |
139 | return l.swap(x, y) | |
140 | } | |
141 | ||
142 | func (l *List) swap(x, y uint) bool { | |
143 | count := uint(len(l.underlyer)) | |
144 | if x < count && y < count { | |
145 | temp := l.underlyer[x] | |
146 | l.underlyer[x] = l.underlyer[y] | |
147 | l.underlyer[y] = temp | |
148 | return true | |
149 | } | |
150 | return false | |
151 | } |
0 | package collection | |
1 | ||
2 | import "fmt" | |
3 | ||
4 | func ExampleList_AddAt() { | |
5 | subject := NewList(0, 1, 4, 5, 6) | |
6 | subject.AddAt(2, 2, 3) | |
7 | fmt.Println(subject) | |
8 | // Output: [0 1 2 3 4 5 6] | |
9 | } |
0 | package collection | |
1 | ||
2 | import ( | |
3 | "errors" | |
4 | "reflect" | |
5 | "runtime" | |
6 | "sync" | |
7 | ) | |
8 | ||
9 | // Enumerable offers a means of easily converting into a channel. It is most | |
10 | // useful for types where mutability is not in question. | |
11 | type Enumerable interface { | |
12 | Enumerate(cancel <-chan struct{}) Enumerator | |
13 | } | |
14 | ||
15 | // Enumerator exposes a new syntax for querying familiar data structures. | |
16 | type Enumerator <-chan interface{} | |
17 | ||
18 | // Predicate defines an interface for funcs that make some logical test. | |
19 | type Predicate func(interface{}) bool | |
20 | ||
21 | // Transform defines a function which takes a value, and returns some value based on the original. | |
22 | type Transform func(interface{}) interface{} | |
23 | ||
24 | // Unfolder defines a function which takes a single value, and exposes many of them as an Enumerator | |
25 | type Unfolder func(interface{}) Enumerator | |
26 | ||
27 | type emptyEnumerable struct{} | |
28 | ||
29 | var ( | |
30 | errNoElements = errors.New("Enumerator encountered no elements") | |
31 | errMultipleElements = errors.New("Enumerator encountered multiple elements") | |
32 | ) | |
33 | ||
34 | // IsErrorNoElements determines whethr or not the given error is the result of no values being | |
35 | // returned when one or more were expected. | |
36 | func IsErrorNoElements(err error) bool { | |
37 | return err == errNoElements | |
38 | } | |
39 | ||
40 | // IsErrorMultipleElements determines whether or not the given error is the result of multiple values | |
41 | // being returned when one or zero were expected. | |
42 | func IsErrorMultipleElements(err error) bool { | |
43 | return err == errMultipleElements | |
44 | } | |
45 | ||
46 | // Identity is a trivial Transform which applies no operation on the value. | |
47 | var Identity Transform = func(value interface{}) interface{} { | |
48 | return value | |
49 | } | |
50 | ||
51 | // Empty is an Enumerable that has no elements, and will never have any elements. | |
52 | var Empty Enumerable = &emptyEnumerable{} | |
53 | ||
54 | func (e emptyEnumerable) Enumerate(cancel <-chan struct{}) Enumerator { | |
55 | results := make(chan interface{}) | |
56 | close(results) | |
57 | return results | |
58 | } | |
59 | ||
60 | // All tests whether or not all items present in an Enumerable meet a criteria. | |
61 | func All(subject Enumerable, p Predicate) bool { | |
62 | done := make(chan struct{}) | |
63 | defer close(done) | |
64 | ||
65 | return subject.Enumerate(done).All(p) | |
66 | } | |
67 | ||
68 | // All tests whether or not all items present meet a criteria. | |
69 | func (iter Enumerator) All(p Predicate) bool { | |
70 | for entry := range iter { | |
71 | if !p(entry) { | |
72 | return false | |
73 | } | |
74 | } | |
75 | return true | |
76 | } | |
77 | ||
78 | // Any tests an Enumerable to see if there are any elements present. | |
79 | func Any(iterator Enumerable) bool { | |
80 | done := make(chan struct{}) | |
81 | defer close(done) | |
82 | ||
83 | for range iterator.Enumerate(done) { | |
84 | return true | |
85 | } | |
86 | return false | |
87 | } | |
88 | ||
89 | // Anyp tests an Enumerable to see if there are any elements present that meet a criteria. | |
90 | func Anyp(iterator Enumerable, p Predicate) bool { | |
91 | done := make(chan struct{}) | |
92 | defer close(done) | |
93 | ||
94 | for element := range iterator.Enumerate(done) { | |
95 | if p(element) { | |
96 | return true | |
97 | } | |
98 | } | |
99 | return false | |
100 | } | |
101 | ||
102 | type enumerableSlice []interface{} | |
103 | ||
104 | func (f enumerableSlice) Enumerate(cancel <-chan struct{}) Enumerator { | |
105 | results := make(chan interface{}) | |
106 | ||
107 | go func() { | |
108 | defer close(results) | |
109 | for _, entry := range f { | |
110 | select { | |
111 | case results <- entry: | |
112 | break | |
113 | case <-cancel: | |
114 | return | |
115 | } | |
116 | } | |
117 | }() | |
118 | ||
119 | return results | |
120 | } | |
121 | ||
122 | type enumerableValue struct { | |
123 | reflect.Value | |
124 | } | |
125 | ||
126 | func (v enumerableValue) Enumerate(cancel <-chan struct{}) Enumerator { | |
127 | results := make(chan interface{}) | |
128 | ||
129 | go func() { | |
130 | defer close(results) | |
131 | ||
132 | elements := v.Len() | |
133 | ||
134 | for i := 0; i < elements; i++ { | |
135 | select { | |
136 | case results <- v.Index(i).Interface(): | |
137 | break | |
138 | case <-cancel: | |
139 | return | |
140 | } | |
141 | } | |
142 | }() | |
143 | ||
144 | return results | |
145 | } | |
146 | ||
147 | // AsEnumerable allows for easy conversion of a slice to a re-usable Enumerable object. | |
148 | func AsEnumerable(entries ...interface{}) Enumerable { | |
149 | if len(entries) != 1 { | |
150 | return enumerableSlice(entries) | |
151 | } | |
152 | ||
153 | val := reflect.ValueOf(entries[0]) | |
154 | ||
155 | if kind := val.Kind(); kind == reflect.Slice || kind == reflect.Array { | |
156 | return enumerableValue{ | |
157 | Value: val, | |
158 | } | |
159 | } | |
160 | return enumerableSlice(entries) | |
161 | } | |
162 | ||
163 | // AsEnumerable stores the results of an Enumerator so the results can be enumerated over repeatedly. | |
164 | func (iter Enumerator) AsEnumerable() Enumerable { | |
165 | return enumerableSlice(iter.ToSlice()) | |
166 | } | |
167 | ||
168 | // Count iterates over a list and keeps a running tally of the number of elements | |
169 | // satisfy a predicate. | |
170 | func Count(iter Enumerable, p Predicate) int { | |
171 | return iter.Enumerate(nil).Count(p) | |
172 | } | |
173 | ||
174 | // Count iterates over a list and keeps a running tally of the number of elements | |
175 | // satisfy a predicate. | |
176 | func (iter Enumerator) Count(p Predicate) int { | |
177 | tally := 0 | |
178 | for entry := range iter { | |
179 | if p(entry) { | |
180 | tally++ | |
181 | } | |
182 | } | |
183 | return tally | |
184 | } | |
185 | ||
186 | // CountAll iterates over a list and keeps a running tally of how many it's seen. | |
187 | func CountAll(iter Enumerable) int { | |
188 | return iter.Enumerate(nil).CountAll() | |
189 | } | |
190 | ||
191 | // CountAll iterates over a list and keeps a running tally of how many it's seen. | |
192 | func (iter Enumerator) CountAll() int { | |
193 | tally := 0 | |
194 | for range iter { | |
195 | tally++ | |
196 | } | |
197 | return tally | |
198 | } | |
199 | ||
200 | // Discard reads an enumerator to the end but does nothing with it. | |
201 | // This method should be used in circumstances when it doesn't make sense to explicitly cancel the Enumeration. | |
202 | func (iter Enumerator) Discard() { | |
203 | for range iter { | |
204 | // Intentionally Left Blank | |
205 | } | |
206 | } | |
207 | ||
208 | // ElementAt retreives an item at a particular position in an Enumerator. | |
209 | func ElementAt(iter Enumerable, n uint) interface{} { | |
210 | done := make(chan struct{}) | |
211 | defer close(done) | |
212 | return iter.Enumerate(done).ElementAt(n) | |
213 | } | |
214 | ||
215 | // ElementAt retreives an item at a particular position in an Enumerator. | |
216 | func (iter Enumerator) ElementAt(n uint) interface{} { | |
217 | for i := uint(0); i < n; i++ { | |
218 | <-iter | |
219 | } | |
220 | return <-iter | |
221 | } | |
222 | ||
223 | // First retrieves just the first item in the list, or returns an error if there are no elements in the array. | |
224 | func First(subject Enumerable) (retval interface{}, err error) { | |
225 | done := make(chan struct{}) | |
226 | ||
227 | err = errNoElements | |
228 | ||
229 | var isOpen bool | |
230 | ||
231 | if retval, isOpen = <-subject.Enumerate(done); isOpen { | |
232 | err = nil | |
233 | } | |
234 | close(done) | |
235 | ||
236 | return | |
237 | } | |
238 | ||
239 | // Last retreives the item logically behind all other elements in the list. | |
240 | func Last(iter Enumerable) interface{} { | |
241 | return iter.Enumerate(nil).Last() | |
242 | } | |
243 | ||
244 | // Last retreives the item logically behind all other elements in the list. | |
245 | func (iter Enumerator) Last() (retval interface{}) { | |
246 | for retval = range iter { | |
247 | // Intentionally Left Blank | |
248 | } | |
249 | return | |
250 | } | |
251 | ||
252 | type merger struct { | |
253 | originals []Enumerable | |
254 | } | |
255 | ||
256 | func (m merger) Enumerate(cancel <-chan struct{}) Enumerator { | |
257 | retval := make(chan interface{}) | |
258 | ||
259 | var wg sync.WaitGroup | |
260 | wg.Add(len(m.originals)) | |
261 | for _, item := range m.originals { | |
262 | go func(input Enumerable) { | |
263 | defer wg.Done() | |
264 | for value := range input.Enumerate(cancel) { | |
265 | retval <- value | |
266 | } | |
267 | }(item) | |
268 | } | |
269 | ||
270 | go func() { | |
271 | wg.Wait() | |
272 | close(retval) | |
273 | }() | |
274 | return retval | |
275 | } | |
276 | ||
277 | // Merge takes the results as it receives them from several channels and directs | |
278 | // them into a single channel. | |
279 | func Merge(channels ...Enumerable) Enumerable { | |
280 | return merger{ | |
281 | originals: channels, | |
282 | } | |
283 | } | |
284 | ||
285 | // Merge takes the results of this Enumerator and others, and funnels them into | |
286 | // a single Enumerator. The order of in which they will be combined is non-deterministic. | |
287 | func (iter Enumerator) Merge(others ...Enumerator) Enumerator { | |
288 | retval := make(chan interface{}) | |
289 | ||
290 | var wg sync.WaitGroup | |
291 | wg.Add(len(others) + 1) | |
292 | ||
293 | funnel := func(prevResult Enumerator) { | |
294 | for entry := range prevResult { | |
295 | retval <- entry | |
296 | } | |
297 | wg.Done() | |
298 | } | |
299 | ||
300 | go funnel(iter) | |
301 | for _, item := range others { | |
302 | go funnel(item) | |
303 | } | |
304 | ||
305 | go func() { | |
306 | wg.Wait() | |
307 | close(retval) | |
308 | }() | |
309 | return retval | |
310 | } | |
311 | ||
312 | type parallelSelecter struct { | |
313 | original Enumerable | |
314 | operation Transform | |
315 | } | |
316 | ||
317 | func (ps parallelSelecter) Enumerate(cancel <-chan struct{}) Enumerator { | |
318 | return ps.original.Enumerate(cancel).ParallelSelect(ps.operation) | |
319 | } | |
320 | ||
321 | // ParallelSelect creates an Enumerable which will use all logically available CPUs to | |
322 | // execute a Transform. | |
323 | func ParallelSelect(original Enumerable, operation Transform) Enumerable { | |
324 | return parallelSelecter{ | |
325 | original: original, | |
326 | operation: operation, | |
327 | } | |
328 | } | |
329 | ||
330 | // ParallelSelect will execute a Transform across all logical CPUs available to the current process. | |
331 | func (iter Enumerator) ParallelSelect(operation Transform) Enumerator { | |
332 | if cpus := runtime.NumCPU(); cpus != 1 { | |
333 | intermediate := iter.splitN(operation, uint(cpus)) | |
334 | return intermediate[0].Merge(intermediate[1:]...) | |
335 | } | |
336 | return iter | |
337 | } | |
338 | ||
339 | type reverser struct { | |
340 | original Enumerable | |
341 | } | |
342 | ||
343 | // Reverse will enumerate all values of an enumerable, store them in a Stack, then replay them all. | |
344 | func Reverse(original Enumerable) Enumerable { | |
345 | return reverser{ | |
346 | original: original, | |
347 | } | |
348 | } | |
349 | ||
350 | func (r reverser) Enumerate(cancel <-chan struct{}) Enumerator { | |
351 | return r.original.Enumerate(cancel).Reverse() | |
352 | } | |
353 | ||
354 | // Reverse returns items in the opposite order it encountered them in. | |
355 | func (iter Enumerator) Reverse() Enumerator { | |
356 | cache := NewStack() | |
357 | for entry := range iter { | |
358 | cache.Push(entry) | |
359 | } | |
360 | ||
361 | retval := make(chan interface{}) | |
362 | ||
363 | go func() { | |
364 | for !cache.IsEmpty() { | |
365 | val, _ := cache.Pop() | |
366 | retval <- val | |
367 | } | |
368 | close(retval) | |
369 | }() | |
370 | return retval | |
371 | } | |
372 | ||
373 | type selecter struct { | |
374 | original Enumerable | |
375 | transform Transform | |
376 | } | |
377 | ||
378 | func (s selecter) Enumerate(cancel <-chan struct{}) Enumerator { | |
379 | return s.original.Enumerate(cancel).Select(s.transform) | |
380 | } | |
381 | ||
382 | // Select creates a reusable stream of transformed values. | |
383 | func Select(subject Enumerable, transform Transform) Enumerable { | |
384 | return selecter{ | |
385 | original: subject, | |
386 | transform: transform, | |
387 | } | |
388 | } | |
389 | ||
390 | // Select iterates over a list and returns a transformed item. | |
391 | func (iter Enumerator) Select(transform Transform) Enumerator { | |
392 | retval := make(chan interface{}) | |
393 | ||
394 | go func() { | |
395 | for item := range iter { | |
396 | retval <- transform(item) | |
397 | } | |
398 | close(retval) | |
399 | }() | |
400 | ||
401 | return retval | |
402 | } | |
403 | ||
404 | type selectManyer struct { | |
405 | original Enumerable | |
406 | toMany Unfolder | |
407 | } | |
408 | ||
409 | func (s selectManyer) Enumerate(cancel <-chan struct{}) Enumerator { | |
410 | return s.original.Enumerate(cancel).SelectMany(s.toMany) | |
411 | } | |
412 | ||
413 | // SelectMany allows for unfolding of values. | |
414 | func SelectMany(subject Enumerable, toMany Unfolder) Enumerable { | |
415 | return selectManyer{ | |
416 | original: subject, | |
417 | toMany: toMany, | |
418 | } | |
419 | } | |
420 | ||
421 | // SelectMany allows for flattening of data structures. | |
422 | func (iter Enumerator) SelectMany(lister Unfolder) Enumerator { | |
423 | retval := make(chan interface{}) | |
424 | ||
425 | go func() { | |
426 | for parent := range iter { | |
427 | for child := range lister(parent) { | |
428 | retval <- child | |
429 | } | |
430 | } | |
431 | close(retval) | |
432 | }() | |
433 | ||
434 | return retval | |
435 | } | |
436 | ||
437 | // Single retreives the only element from a list, or returns nil and an error. | |
438 | func Single(iter Enumerable) (retval interface{}, err error) { | |
439 | done := make(chan struct{}) | |
440 | defer close(done) | |
441 | ||
442 | err = errNoElements | |
443 | ||
444 | firstPass := true | |
445 | for entry := range iter.Enumerate(done) { | |
446 | if firstPass { | |
447 | retval = entry | |
448 | err = nil | |
449 | } else { | |
450 | retval = nil | |
451 | err = errMultipleElements | |
452 | break | |
453 | } | |
454 | firstPass = false | |
455 | } | |
456 | return | |
457 | } | |
458 | ||
459 | // Singlep retrieces the only element from a list that matches a criteria. If | |
460 | // no match is found, or two or more are found, `Singlep` returns nil and an | |
461 | // error. | |
462 | func Singlep(iter Enumerable, pred Predicate) (retval interface{}, err error) { | |
463 | iter = Where(iter, pred) | |
464 | return Single(iter) | |
465 | } | |
466 | ||
467 | type skipper struct { | |
468 | original Enumerable | |
469 | skipCount uint | |
470 | } | |
471 | ||
472 | func (s skipper) Enumerate(cancel <-chan struct{}) Enumerator { | |
473 | return s.original.Enumerate(cancel).Skip(s.skipCount) | |
474 | } | |
475 | ||
476 | // Skip creates a reusable stream which will skip the first `n` elements before iterating | |
477 | // over the rest of the elements in an Enumerable. | |
478 | func Skip(subject Enumerable, n uint) Enumerable { | |
479 | return skipper{ | |
480 | original: subject, | |
481 | skipCount: n, | |
482 | } | |
483 | } | |
484 | ||
485 | // Skip retreives all elements after the first 'n' elements. | |
486 | func (iter Enumerator) Skip(n uint) Enumerator { | |
487 | results := make(chan interface{}) | |
488 | ||
489 | go func() { | |
490 | defer close(results) | |
491 | ||
492 | i := uint(0) | |
493 | for entry := range iter { | |
494 | if i < n { | |
495 | i++ | |
496 | continue | |
497 | } | |
498 | results <- entry | |
499 | } | |
500 | }() | |
501 | ||
502 | return results | |
503 | } | |
504 | ||
505 | // splitN creates N Enumerators, each will be a subset of the original Enumerator and will have | |
506 | // distinct populations from one another. | |
507 | func (iter Enumerator) splitN(operation Transform, n uint) []Enumerator { | |
508 | results, cast := make([]chan interface{}, n, n), make([]Enumerator, n, n) | |
509 | ||
510 | for i := uint(0); i < n; i++ { | |
511 | results[i] = make(chan interface{}) | |
512 | cast[i] = results[i] | |
513 | } | |
514 | ||
515 | go func() { | |
516 | for i := uint(0); i < n; i++ { | |
517 | go func(addr uint) { | |
518 | defer close(results[addr]) | |
519 | for { | |
520 | read, ok := <-iter | |
521 | if !ok { | |
522 | return | |
523 | } | |
524 | results[addr] <- operation(read) | |
525 | } | |
526 | }(i) | |
527 | } | |
528 | }() | |
529 | ||
530 | return cast | |
531 | } | |
532 | ||
533 | type taker struct { | |
534 | original Enumerable | |
535 | n uint | |
536 | } | |
537 | ||
538 | func (t taker) Enumerate(cancel <-chan struct{}) Enumerator { | |
539 | return t.original.Enumerate(cancel).Take(t.n) | |
540 | } | |
541 | ||
542 | // Take retreives just the first `n` elements from an Enumerable. | |
543 | func Take(subject Enumerable, n uint) Enumerable { | |
544 | return taker{ | |
545 | original: subject, | |
546 | n: n, | |
547 | } | |
548 | } | |
549 | ||
550 | // Take retreives just the first 'n' elements from an Enumerator. | |
551 | func (iter Enumerator) Take(n uint) Enumerator { | |
552 | results := make(chan interface{}) | |
553 | ||
554 | go func() { | |
555 | defer close(results) | |
556 | i := uint(0) | |
557 | for entry := range iter { | |
558 | if i >= n { | |
559 | return | |
560 | } | |
561 | i++ | |
562 | results <- entry | |
563 | } | |
564 | }() | |
565 | ||
566 | return results | |
567 | } | |
568 | ||
569 | type takeWhiler struct { | |
570 | original Enumerable | |
571 | criteria func(interface{}, uint) bool | |
572 | } | |
573 | ||
574 | func (tw takeWhiler) Enumerate(cancel <-chan struct{}) Enumerator { | |
575 | return tw.original.Enumerate(cancel).TakeWhile(tw.criteria) | |
576 | } | |
577 | ||
578 | // TakeWhile creates a reusable stream which will halt once some criteria is no longer met. | |
579 | func TakeWhile(subject Enumerable, criteria func(interface{}, uint) bool) Enumerable { | |
580 | return takeWhiler{ | |
581 | original: subject, | |
582 | criteria: criteria, | |
583 | } | |
584 | } | |
585 | ||
586 | // TakeWhile continues returning items as long as 'criteria' holds true. | |
587 | func (iter Enumerator) TakeWhile(criteria func(interface{}, uint) bool) Enumerator { | |
588 | results := make(chan interface{}) | |
589 | ||
590 | go func() { | |
591 | defer close(results) | |
592 | i := uint(0) | |
593 | for entry := range iter { | |
594 | if !criteria(entry, i) { | |
595 | return | |
596 | } | |
597 | i++ | |
598 | results <- entry | |
599 | } | |
600 | }() | |
601 | ||
602 | return results | |
603 | } | |
604 | ||
605 | // Tee creates two Enumerators which will have identical contents as one another. | |
606 | func (iter Enumerator) Tee() (Enumerator, Enumerator) { | |
607 | left, right := make(chan interface{}), make(chan interface{}) | |
608 | ||
609 | go func() { | |
610 | for entry := range iter { | |
611 | left <- entry | |
612 | right <- entry | |
613 | } | |
614 | close(left) | |
615 | close(right) | |
616 | }() | |
617 | ||
618 | return left, right | |
619 | } | |
620 | ||
621 | // ToSlice places all iterated over values in a Slice for easy consumption. | |
622 | func ToSlice(iter Enumerable) []interface{} { | |
623 | return iter.Enumerate(nil).ToSlice() | |
624 | } | |
625 | ||
626 | // ToSlice places all iterated over values in a Slice for easy consumption. | |
627 | func (iter Enumerator) ToSlice() []interface{} { | |
628 | retval := make([]interface{}, 0) | |
629 | for entry := range iter { | |
630 | retval = append(retval, entry) | |
631 | } | |
632 | return retval | |
633 | } | |
634 | ||
635 | type wherer struct { | |
636 | original Enumerable | |
637 | filter Predicate | |
638 | } | |
639 | ||
640 | func (w wherer) Enumerate(cancel <-chan struct{}) Enumerator { | |
641 | retval := make(chan interface{}) | |
642 | ||
643 | go func() { | |
644 | defer close(retval) | |
645 | for entry := range w.original.Enumerate(cancel) { | |
646 | if w.filter(entry) { | |
647 | retval <- entry | |
648 | } | |
649 | } | |
650 | }() | |
651 | ||
652 | return retval | |
653 | } | |
654 | ||
655 | // Where creates a reusable means of filtering a stream. | |
656 | func Where(original Enumerable, p Predicate) Enumerable { | |
657 | return wherer{ | |
658 | original: original, | |
659 | filter: p, | |
660 | } | |
661 | } | |
662 | ||
663 | // Where iterates over a list and returns only the elements that satisfy a | |
664 | // predicate. | |
665 | func (iter Enumerator) Where(predicate Predicate) Enumerator { | |
666 | retval := make(chan interface{}) | |
667 | go func() { | |
668 | for item := range iter { | |
669 | if predicate(item) { | |
670 | retval <- item | |
671 | } | |
672 | } | |
673 | close(retval) | |
674 | }() | |
675 | ||
676 | return retval | |
677 | } | |
678 | ||
679 | // UCount iterates over a list and keeps a running tally of the number of elements | |
680 | // satisfy a predicate. | |
681 | func UCount(iter Enumerable, p Predicate) uint { | |
682 | return iter.Enumerate(nil).UCount(p) | |
683 | } | |
684 | ||
685 | // UCount iterates over a list and keeps a running tally of the number of elements | |
686 | // satisfy a predicate. | |
687 | func (iter Enumerator) UCount(p Predicate) uint { | |
688 | tally := uint(0) | |
689 | for entry := range iter { | |
690 | if p(entry) { | |
691 | tally++ | |
692 | } | |
693 | } | |
694 | return tally | |
695 | } | |
696 | ||
697 | // UCountAll iterates over a list and keeps a running tally of how many it's seen. | |
698 | func UCountAll(iter Enumerable) uint { | |
699 | return iter.Enumerate(nil).UCountAll() | |
700 | } | |
701 | ||
702 | // UCountAll iterates over a list and keeps a running tally of how many it's seen. | |
703 | func (iter Enumerator) UCountAll() uint { | |
704 | tally := uint(0) | |
705 | for range iter { | |
706 | tally++ | |
707 | } | |
708 | return tally | |
709 | } |
0 | package collection | |
1 | ||
2 | import ( | |
3 | "fmt" | |
4 | "sync" | |
5 | ) | |
6 | ||
7 | func ExampleAsEnumerable() { | |
8 | // When a single value is provided, and it is an array or slice, each value in the array or slice is treated as an enumerable value. | |
9 | original := []int{1, 2, 3, 4, 5} | |
10 | wrapped := AsEnumerable(original) | |
11 | ||
12 | for entry := range wrapped.Enumerate(nil) { | |
13 | fmt.Print(entry) | |
14 | } | |
15 | fmt.Println() | |
16 | ||
17 | // When multiple values are provided, regardless of their type, they are each treated as enumerable values. | |
18 | wrapped = AsEnumerable("red", "orange", "yellow", "green", "blue", "indigo", "violet") | |
19 | for entry := range wrapped.Enumerate(nil) { | |
20 | fmt.Println(entry) | |
21 | } | |
22 | // Output: | |
23 | // 12345 | |
24 | // red | |
25 | // orange | |
26 | // yellow | |
27 | // green | |
28 | // blue | |
29 | // indigo | |
30 | // violet | |
31 | } | |
32 | ||
33 | func ExampleEnumerator_Count() { | |
34 | subject := AsEnumerable("str1", "str1", "str2") | |
35 | count1 := subject.Enumerate(nil).Count(func(a interface{}) bool { | |
36 | return a == "str1" | |
37 | }) | |
38 | fmt.Println(count1) | |
39 | // Output: 2 | |
40 | } | |
41 | ||
42 | func ExampleEnumerator_CountAll() { | |
43 | subject := AsEnumerable('a', 'b', 'c', 'd', 'e') | |
44 | fmt.Println(subject.Enumerate(nil).CountAll()) | |
45 | // Ouput: 5 | |
46 | } | |
47 | ||
48 | func ExampleEnumerator_ElementAt() { | |
49 | done := make(chan struct{}) | |
50 | defer close(done) | |
51 | fmt.Print(Fibonacci.Enumerate(done).ElementAt(4)) | |
52 | // Output: 3 | |
53 | } | |
54 | ||
55 | func ExampleFirst() { | |
56 | empty := NewQueue() | |
57 | notEmpty := NewQueue(1, 2, 3, 4) | |
58 | ||
59 | fmt.Println(First(empty)) | |
60 | fmt.Println(First(notEmpty)) | |
61 | // Output: | |
62 | // <nil> Enumerator encountered no elements | |
63 | // 1 <nil> | |
64 | } | |
65 | ||
66 | func ExampleLast() { | |
67 | subject := NewList(1, 2, 3, 4) | |
68 | fmt.Println(Last(subject)) | |
69 | // Output: 4 | |
70 | } | |
71 | ||
72 | func ExampleEnumerator_Last() { | |
73 | subject := AsEnumerable(1, 2, 3) | |
74 | fmt.Print(subject.Enumerate(nil).Last()) | |
75 | //Output: 3 | |
76 | } | |
77 | ||
78 | func ExampleMerge() { | |
79 | a := AsEnumerable(1, 2, 4) | |
80 | b := AsEnumerable(8, 16, 32) | |
81 | c := Merge(a, b) | |
82 | sum := 0 | |
83 | for x := range c.Enumerate(nil) { | |
84 | sum += x.(int) | |
85 | } | |
86 | fmt.Println(sum) | |
87 | ||
88 | product := 1 | |
89 | for y := range a.Enumerate(nil) { | |
90 | product *= y.(int) | |
91 | } | |
92 | fmt.Println(product) | |
93 | // Output: | |
94 | // 63 | |
95 | // 8 | |
96 | } | |
97 | ||
98 | func ExampleEnumerator_Reverse() { | |
99 | a := AsEnumerable(1, 2, 3).Enumerate(nil) | |
100 | a = a.Reverse() | |
101 | fmt.Println(a.ToSlice()) | |
102 | // Output: [3 2 1] | |
103 | } | |
104 | ||
105 | func ExampleSelect() { | |
106 | const offset = 'a' - 1 | |
107 | ||
108 | subject := AsEnumerable('a', 'b', 'c') | |
109 | subject = Select(subject, func(a interface{}) interface{} { | |
110 | return a.(rune) - offset | |
111 | }) | |
112 | ||
113 | fmt.Println(ToSlice(subject)) | |
114 | // Output: [1 2 3] | |
115 | } | |
116 | ||
117 | func ExampleEnumerator_Select() { | |
118 | subject := AsEnumerable('a', 'b', 'c').Enumerate(nil) | |
119 | const offset = 'a' - 1 | |
120 | results := subject.Select(func(a interface{}) interface{} { | |
121 | return a.(rune) - offset | |
122 | }) | |
123 | ||
124 | fmt.Println(results.ToSlice()) | |
125 | // Output: [1 2 3] | |
126 | } | |
127 | ||
128 | func ExampleEnumerator_SelectMany() { | |
129 | ||
130 | type BrewHouse struct { | |
131 | Name string | |
132 | Beers Enumerable | |
133 | } | |
134 | ||
135 | breweries := AsEnumerable( | |
136 | BrewHouse{ | |
137 | "Mac & Jacks", | |
138 | AsEnumerable( | |
139 | "African Amber", | |
140 | "Ibis IPA", | |
141 | ), | |
142 | }, | |
143 | BrewHouse{ | |
144 | "Post Doc", | |
145 | AsEnumerable( | |
146 | "Prereq Pale", | |
147 | ), | |
148 | }, | |
149 | BrewHouse{ | |
150 | "Resonate", | |
151 | AsEnumerable( | |
152 | "Comfortably Numb IPA", | |
153 | "Lithium Altbier", | |
154 | ), | |
155 | }, | |
156 | BrewHouse{ | |
157 | "Triplehorn", | |
158 | AsEnumerable( | |
159 | "Samson", | |
160 | "Pepper Belly", | |
161 | ), | |
162 | }, | |
163 | ) | |
164 | ||
165 | beers := breweries.Enumerate(nil).SelectMany(func(brewer interface{}) Enumerator { | |
166 | return brewer.(BrewHouse).Beers.Enumerate(nil) | |
167 | }) | |
168 | ||
169 | for beer := range beers { | |
170 | fmt.Println(beer) | |
171 | } | |
172 | ||
173 | // Output: | |
174 | // African Amber | |
175 | // Ibis IPA | |
176 | // Prereq Pale | |
177 | // Comfortably Numb IPA | |
178 | // Lithium Altbier | |
179 | // Samson | |
180 | // Pepper Belly | |
181 | } | |
182 | ||
183 | func ExampleSkip() { | |
184 | done := make(chan struct{}) | |
185 | defer close(done) | |
186 | ||
187 | trimmed := Take(Skip(Fibonacci, 1), 3) | |
188 | for entry := range trimmed.Enumerate(done) { | |
189 | fmt.Println(entry) | |
190 | } | |
191 | // Output: | |
192 | // 1 | |
193 | // 1 | |
194 | // 2 | |
195 | } | |
196 | ||
197 | func ExampleEnumerator_Skip() { | |
198 | subject := AsEnumerable(1, 2, 3, 4, 5, 6, 7) | |
199 | skipped := subject.Enumerate(nil).Skip(5) | |
200 | for entry := range skipped { | |
201 | fmt.Println(entry) | |
202 | } | |
203 | // Output: | |
204 | // 6 | |
205 | // 7 | |
206 | } | |
207 | ||
208 | func ExampleTake() { | |
209 | done := make(chan struct{}) | |
210 | defer close(done) | |
211 | ||
212 | taken := Take(Fibonacci, 4) | |
213 | for entry := range taken.Enumerate(done) { | |
214 | fmt.Println(entry) | |
215 | } | |
216 | // Output: | |
217 | // 0 | |
218 | // 1 | |
219 | // 1 | |
220 | // 2 | |
221 | } | |
222 | ||
223 | func ExampleEnumerator_Take() { | |
224 | done := make(chan struct{}) | |
225 | defer close(done) | |
226 | ||
227 | taken := Fibonacci.Enumerate(done).Skip(4).Take(2) | |
228 | for entry := range taken { | |
229 | fmt.Println(entry) | |
230 | } | |
231 | // Output: | |
232 | // 3 | |
233 | // 5 | |
234 | } | |
235 | ||
236 | func ExampleTakeWhile() { | |
237 | taken := TakeWhile(Fibonacci, func(x interface{}, n uint) bool { | |
238 | return x.(int) < 10 | |
239 | }) | |
240 | for entry := range taken.Enumerate(nil) { | |
241 | fmt.Println(entry) | |
242 | } | |
243 | // Output: | |
244 | // 0 | |
245 | // 1 | |
246 | // 1 | |
247 | // 2 | |
248 | // 3 | |
249 | // 5 | |
250 | // 8 | |
251 | } | |
252 | ||
253 | func ExampleEnumerator_TakeWhile() { | |
254 | taken := Fibonacci.Enumerate(nil).TakeWhile(func(x interface{}, n uint) bool { | |
255 | return x.(int) < 6 | |
256 | }) | |
257 | for entry := range taken { | |
258 | fmt.Println(entry) | |
259 | } | |
260 | // Output: | |
261 | // 0 | |
262 | // 1 | |
263 | // 1 | |
264 | // 2 | |
265 | // 3 | |
266 | // 5 | |
267 | } | |
268 | ||
269 | func ExampleEnumerator_Tee() { | |
270 | base := AsEnumerable(1, 2, 4) | |
271 | left, right := base.Enumerate(nil).Tee() | |
272 | var wg sync.WaitGroup | |
273 | wg.Add(2) | |
274 | ||
275 | product := 1 | |
276 | go func() { | |
277 | for x := range left { | |
278 | product *= x.(int) | |
279 | } | |
280 | wg.Done() | |
281 | }() | |
282 | ||
283 | sum := 0 | |
284 | go func() { | |
285 | for x := range right { | |
286 | sum += x.(int) | |
287 | } | |
288 | wg.Done() | |
289 | }() | |
290 | ||
291 | wg.Wait() | |
292 | ||
293 | fmt.Printf("Sum: %d\n", sum) | |
294 | fmt.Printf("Product: %d\n", product) | |
295 | // Output: | |
296 | // Sum: 7 | |
297 | // Product: 8 | |
298 | } | |
299 | ||
300 | func ExampleUCount() { | |
301 | subject := NewStack(9, 'a', "str1") | |
302 | result := UCount(subject, func(a interface{}) bool { | |
303 | _, ok := a.(string) | |
304 | return ok | |
305 | }) | |
306 | fmt.Println(result) | |
307 | // Output: 1 | |
308 | } | |
309 | ||
310 | func ExampleEnumerator_UCount() { | |
311 | subject := AsEnumerable("str1", "str1", "str2") | |
312 | count1 := subject.Enumerate(nil).UCount(func(a interface{}) bool { | |
313 | return a == "str1" | |
314 | }) | |
315 | fmt.Println(count1) | |
316 | // Output: 2 | |
317 | } | |
318 | ||
319 | func ExampleUCountAll() { | |
320 | subject := NewStack(8, 9, 10, 11) | |
321 | fmt.Println(UCountAll(subject)) | |
322 | // Output: 4 | |
323 | } | |
324 | ||
325 | func ExampleEnumerator_UCountAll() { | |
326 | subject := AsEnumerable('a', 2, "str1") | |
327 | fmt.Println(subject.Enumerate(nil).UCountAll()) | |
328 | // Output: 3 | |
329 | } | |
330 | ||
331 | func ExampleEnumerator_Where() { | |
332 | done := make(chan struct{}) | |
333 | defer close(done) | |
334 | results := Fibonacci.Enumerate(done).Where(func(a interface{}) bool { | |
335 | return a.(int) > 8 | |
336 | }).Take(3) | |
337 | fmt.Println(results.ToSlice()) | |
338 | // Output: [13 21 34] | |
339 | } | |
340 | ||
341 | func ExampleWhere() { | |
342 | results := Where(AsEnumerable(1, 2, 3, 4, 5), func(a interface{}) bool { | |
343 | return a.(int) < 3 | |
344 | }) | |
345 | fmt.Println(ToSlice(results)) | |
346 | // Output: [1 2] | |
347 | } |
0 | package collection | |
1 | ||
2 | import ( | |
3 | "testing" | |
4 | "time" | |
5 | ) | |
6 | ||
7 | func Test_Empty(t *testing.T) { | |
8 | if Any(Empty) { | |
9 | t.Log("empty should not have any elements") | |
10 | t.Fail() | |
11 | } | |
12 | ||
13 | if CountAll(Empty) != 0 { | |
14 | t.Log("empty should have counted to zero elements") | |
15 | t.Fail() | |
16 | } | |
17 | ||
18 | alwaysTrue := func(x interface{}) bool { | |
19 | return true | |
20 | } | |
21 | ||
22 | if Count(Empty, alwaysTrue) != 0 { | |
23 | t.Log("empty should have counted to zero even when discriminating") | |
24 | t.Fail() | |
25 | } | |
26 | } | |
27 | ||
28 | func BenchmarkEnumerator_Sum(b *testing.B) { | |
29 | nums := AsEnumerable(getInitializedSequentialArray()...) | |
30 | ||
31 | b.ResetTimer() | |
32 | for i := 0; i < b.N; i++ { | |
33 | for range nums.Enumerate(nil).Select(sleepIdentity) { | |
34 | // Intentionally Left Blank | |
35 | } | |
36 | } | |
37 | } | |
38 | ||
39 | func sleepIdentity(num interface{}) interface{} { | |
40 | time.Sleep(2 * time.Millisecond) | |
41 | return Identity(num) | |
42 | } | |
43 | ||
44 | func getInitializedSequentialArray() []interface{} { | |
45 | ||
46 | rawNums := make([]interface{}, 1000, 1000) | |
47 | for i := range rawNums { | |
48 | rawNums[i] = i + 1 | |
49 | } | |
50 | return rawNums | |
51 | } |
0 | package collection | |
1 | ||
2 | import ( | |
3 | "sync" | |
4 | ) | |
5 | ||
6 | // Queue implements a basic FIFO structure. | |
7 | type Queue struct { | |
8 | underlyer *LinkedList | |
9 | key sync.RWMutex | |
10 | } | |
11 | ||
12 | // NewQueue instantiates a new FIFO structure. | |
13 | func NewQueue(entries ...interface{}) *Queue { | |
14 | retval := &Queue{ | |
15 | underlyer: NewLinkedList(entries...), | |
16 | } | |
17 | return retval | |
18 | } | |
19 | ||
20 | // Add places an item at the back of the Queue. | |
21 | func (q *Queue) Add(entry interface{}) { | |
22 | q.key.Lock() | |
23 | defer q.key.Unlock() | |
24 | if nil == q.underlyer { | |
25 | q.underlyer = NewLinkedList() | |
26 | } | |
27 | q.underlyer.AddBack(entry) | |
28 | } | |
29 | ||
30 | // Enumerate peeks at each element of this queue without mutating it. | |
31 | func (q *Queue) Enumerate(cancel <-chan struct{}) Enumerator { | |
32 | q.key.RLock() | |
33 | defer q.key.RUnlock() | |
34 | return q.underlyer.Enumerate(cancel) | |
35 | } | |
36 | ||
37 | // IsEmpty tests the Queue to determine if it is populate or not. | |
38 | func (q *Queue) IsEmpty() bool { | |
39 | q.key.RLock() | |
40 | defer q.key.RUnlock() | |
41 | return q.underlyer == nil || q.underlyer.IsEmpty() | |
42 | } | |
43 | ||
44 | // Length returns the number of items in the Queue. | |
45 | func (q *Queue) Length() uint { | |
46 | q.key.RLock() | |
47 | defer q.key.RUnlock() | |
48 | if nil == q.underlyer { | |
49 | return 0 | |
50 | } | |
51 | return q.underlyer.length | |
52 | } | |
53 | ||
54 | // Next removes and returns the next item in the Queue. | |
55 | func (q *Queue) Next() (interface{}, bool) { | |
56 | q.key.Lock() | |
57 | defer q.key.Unlock() | |
58 | if q.underlyer == nil { | |
59 | return nil, false | |
60 | } | |
61 | return q.underlyer.RemoveFront() | |
62 | } | |
63 | ||
64 | // Peek returns the next item in the Queue without removing it. | |
65 | func (q *Queue) Peek() (interface{}, bool) { | |
66 | q.key.RLock() | |
67 | defer q.key.RUnlock() | |
68 | if q.underlyer == nil { | |
69 | return nil, false | |
70 | } | |
71 | return q.underlyer.PeekFront() | |
72 | } | |
73 | ||
74 | // ToSlice converts a Queue into a slice. | |
75 | func (q *Queue) ToSlice() []interface{} { | |
76 | q.key.RLock() | |
77 | defer q.key.RUnlock() | |
78 | ||
79 | if q.underlyer == nil { | |
80 | return []interface{}{} | |
81 | } | |
82 | return q.underlyer.ToSlice() | |
83 | } |
0 | package collection | |
1 | ||
2 | import ( | |
3 | "fmt" | |
4 | "testing" | |
5 | ) | |
6 | ||
7 | func ExampleQueue_Add() { | |
8 | subject := &Queue{} | |
9 | subject.Add(1) | |
10 | subject.Add(2) | |
11 | res, _ := subject.Peek() | |
12 | fmt.Println(res) | |
13 | // Output: 1 | |
14 | } | |
15 | ||
16 | func ExampleNewQueue() { | |
17 | empty := NewQueue() | |
18 | fmt.Println(empty.Length()) | |
19 | ||
20 | populated := NewQueue(1, 2, 3, 5, 8, 13) | |
21 | fmt.Println(populated.Length()) | |
22 | // Output: | |
23 | // 0 | |
24 | // 6 | |
25 | } | |
26 | ||
27 | func ExampleQueue_IsEmpty() { | |
28 | empty := NewQueue() | |
29 | fmt.Println(empty.IsEmpty()) | |
30 | ||
31 | populated := NewQueue(1, 2, 3, 5, 8, 13) | |
32 | fmt.Println(populated.IsEmpty()) | |
33 | // Output: | |
34 | // true | |
35 | // false | |
36 | } | |
37 | ||
38 | func ExampleQueue_Next() { | |
39 | subject := NewQueue(1, 2, 3, 5, 8, 13) | |
40 | for !subject.IsEmpty() { | |
41 | val, _ := subject.Next() | |
42 | fmt.Println(val) | |
43 | } | |
44 | // Output: | |
45 | // 1 | |
46 | // 2 | |
47 | // 3 | |
48 | // 5 | |
49 | // 8 | |
50 | // 13 | |
51 | } | |
52 | ||
53 | func TestQueue_Length(t *testing.T) { | |
54 | empty := NewQueue() | |
55 | if count := empty.Length(); count != 0 { | |
56 | t.Logf("got: %d\nwant: %d", count, 0) | |
57 | t.Fail() | |
58 | } | |
59 | ||
60 | // Not the type magic number you're thinking of! | |
61 | // https://en.wikipedia.org/wiki/1729_(number) | |
62 | single := NewQueue(1729) | |
63 | if count := single.Length(); count != 1 { | |
64 | t.Logf("got: %d\nwant: %d", count, 1) | |
65 | t.Fail() | |
66 | } | |
67 | ||
68 | expectedMany := []interface{}{'a', 'b', 'c', 'd', 'e', 'e', 'f', 'g'} | |
69 | many := NewQueue(expectedMany...) | |
70 | if count := many.Length(); count != uint(len(expectedMany)) { | |
71 | t.Logf("got: %d\nwant: %d", count, len(expectedMany)) | |
72 | } | |
73 | } | |
74 | ||
75 | func TestQueue_Length_NonConstructed(t *testing.T) { | |
76 | subject := &Queue{} | |
77 | if got := subject.Length(); got != 0 { | |
78 | t.Logf("got: %d\nwant: %d", got, 0) | |
79 | t.Fail() | |
80 | } | |
81 | } | |
82 | ||
83 | func TestQueue_Next_NonConstructed(t *testing.T) { | |
84 | subject := &Queue{} | |
85 | if got, ok := subject.Next(); ok { | |
86 | t.Logf("Next should not have been ok") | |
87 | t.Fail() | |
88 | } else if got != nil { | |
89 | t.Logf("got: %v\nwant: %v", got, nil) | |
90 | t.Fail() | |
91 | } | |
92 | } | |
93 | ||
94 | func TestQueue_Peek_DoesntRemove(t *testing.T) { | |
95 | expected := []interface{}{1, 2, 3} | |
96 | subject := NewQueue(expected...) | |
97 | if result, ok := subject.Peek(); !ok { | |
98 | t.Logf("no item present") | |
99 | t.Fail() | |
100 | } else if result != expected[0] { | |
101 | t.Logf("got: %d\nwant: %d", result, 1) | |
102 | t.Fail() | |
103 | } else if count := subject.Length(); count != uint(len(expected)) { | |
104 | t.Logf("got: %d\nwant: %d", count, len(expected)) | |
105 | } | |
106 | } | |
107 | ||
108 | func TestQueue_Peek_NonConstructed(t *testing.T) { | |
109 | subject := &Queue{} | |
110 | if got, ok := subject.Peek(); ok { | |
111 | t.Logf("Peek should not have been ok") | |
112 | t.Fail() | |
113 | } else if got != nil { | |
114 | t.Logf("got: %v\nwant: %v", got, nil) | |
115 | t.Fail() | |
116 | } | |
117 | } | |
118 | ||
119 | func TestQueue_ToSlice(t *testing.T) { | |
120 | subject := NewQueue(0, 1, 1, 2, 3, 5) | |
121 | expectedSliceString := "[0 1 1 2 3 5]" | |
122 | if result := subject.ToSlice(); len(result) != 6 { | |
123 | t.Logf("got: %d\nwant: %d", len(result), 6) | |
124 | t.Fail() | |
125 | } else if fmt.Sprintf("%v", result) != expectedSliceString { | |
126 | t.Logf("got:\n%v\nwant:\n%s\n", result, expectedSliceString) | |
127 | t.Fail() | |
128 | } | |
129 | } | |
130 | ||
131 | func TestQueue_ToSlice_Empty(t *testing.T) { | |
132 | subject := NewQueue() | |
133 | result := subject.ToSlice() | |
134 | ||
135 | if len(result) != 0 { | |
136 | t.Logf("result should have been empty") | |
137 | t.Fail() | |
138 | } | |
139 | expectedStr := "[]" | |
140 | resultStr := fmt.Sprintf("%v", result) | |
141 | if resultStr != expectedStr { | |
142 | t.Logf("got:\n%s\nwant:\n%s", resultStr, expectedStr) | |
143 | t.Fail() | |
144 | } | |
145 | } | |
146 | ||
147 | func TestQueue_ToSlice_NotConstructed(t *testing.T) { | |
148 | subject := &Queue{} | |
149 | result := subject.ToSlice() | |
150 | ||
151 | if len(result) != 0 { | |
152 | t.Logf("result should have been empty") | |
153 | t.Fail() | |
154 | } | |
155 | expectedStr := "[]" | |
156 | resultStr := fmt.Sprintf("%v", result) | |
157 | if resultStr != expectedStr { | |
158 | t.Logf("got:\n%s\nwant:\n%s", resultStr, expectedStr) | |
159 | t.Fail() | |
160 | } | |
161 | } |
0 | package collection | |
1 | ||
2 | import ( | |
3 | "sync" | |
4 | ) | |
5 | ||
6 | // Stack implements a basic FILO structure. | |
7 | type Stack struct { | |
8 | underlyer *LinkedList | |
9 | key sync.RWMutex | |
10 | } | |
11 | ||
12 | // NewStack instantiates a new FILO structure. | |
13 | func NewStack(entries ...interface{}) *Stack { | |
14 | retval := &Stack{} | |
15 | retval.underlyer = NewLinkedList() | |
16 | ||
17 | for _, entry := range entries { | |
18 | retval.Push(entry) | |
19 | } | |
20 | return retval | |
21 | } | |
22 | ||
23 | // Enumerate peeks at each element in the stack without mutating it. | |
24 | func (stack *Stack) Enumerate(cancel <-chan struct{}) Enumerator { | |
25 | stack.key.RLock() | |
26 | defer stack.key.RUnlock() | |
27 | ||
28 | return stack.underlyer.Enumerate(cancel) | |
29 | } | |
30 | ||
31 | // IsEmpty tests the Stack to determine if it is populate or not. | |
32 | func (stack *Stack) IsEmpty() bool { | |
33 | stack.key.RLock() | |
34 | defer stack.key.RUnlock() | |
35 | return stack.underlyer == nil || stack.underlyer.IsEmpty() | |
36 | } | |
37 | ||
38 | // Push adds an entry to the top of the Stack. | |
39 | func (stack *Stack) Push(entry interface{}) { | |
40 | stack.key.Lock() | |
41 | defer stack.key.Unlock() | |
42 | ||
43 | if nil == stack.underlyer { | |
44 | stack.underlyer = NewLinkedList() | |
45 | } | |
46 | stack.underlyer.AddFront(entry) | |
47 | } | |
48 | ||
49 | // Pop returns the entry at the top of the Stack then removes it. | |
50 | func (stack *Stack) Pop() (interface{}, bool) { | |
51 | stack.key.Lock() | |
52 | defer stack.key.Unlock() | |
53 | ||
54 | if nil == stack.underlyer { | |
55 | return nil, false | |
56 | } | |
57 | return stack.underlyer.RemoveFront() | |
58 | } | |
59 | ||
60 | // Peek returns the entry at the top of the Stack without removing it. | |
61 | func (stack *Stack) Peek() (interface{}, bool) { | |
62 | stack.key.RLock() | |
63 | defer stack.key.RUnlock() | |
64 | return stack.underlyer.PeekFront() | |
65 | } | |
66 | ||
67 | // Size returns the number of entries populating the Stack. | |
68 | func (stack *Stack) Size() uint { | |
69 | stack.key.RLock() | |
70 | defer stack.key.RUnlock() | |
71 | if stack.underlyer == nil { | |
72 | return 0 | |
73 | } | |
74 | return stack.underlyer.Length() | |
75 | } |
0 | package collection | |
1 | ||
2 | import ( | |
3 | "fmt" | |
4 | "testing" | |
5 | ) | |
6 | ||
7 | func TestStack_NewStack_FromEmpty(t *testing.T) { | |
8 | subject := NewStack() | |
9 | subject.Push("alfa") | |
10 | subject.Push("bravo") | |
11 | subject.Push("charlie") | |
12 | ||
13 | if result, ok := subject.Pop(); result != "charlie" || ok != true { | |
14 | t.Logf("got: %s %v\nwant: %s %v", result, ok, "charlie", true) | |
15 | t.Fail() | |
16 | } | |
17 | if result, ok := subject.Pop(); result != "bravo" || ok != true { | |
18 | t.Logf("got: %s %v\nwant: %s %v", result, ok, "bravo", true) | |
19 | t.Fail() | |
20 | } | |
21 | if result, ok := subject.Pop(); result != "alfa" || ok != true { | |
22 | t.Logf("got: %s %v\nwant: %s %v", result, ok, "alfa", true) | |
23 | t.Fail() | |
24 | } | |
25 | if !subject.IsEmpty() { | |
26 | t.Log("subject should have been empty.") | |
27 | t.Fail() | |
28 | } | |
29 | } | |
30 | ||
31 | func ExampleNewStack() { | |
32 | subject := NewStack(1, 2, 3) | |
33 | for !subject.IsEmpty() { | |
34 | val, _ := subject.Pop() | |
35 | fmt.Println(val) | |
36 | } | |
37 | // Output: | |
38 | // 3 | |
39 | // 2 | |
40 | // 1 | |
41 | } | |
42 | ||
43 | func TestStack_Push_NonConstructor(t *testing.T) { | |
44 | subject := &Stack{} | |
45 | ||
46 | sizeAssertion := func(want uint) { | |
47 | if got := subject.Size(); got != want { | |
48 | t.Logf("got: %d\nwant:%d\n", got, want) | |
49 | t.Fail() | |
50 | } | |
51 | } | |
52 | ||
53 | sizeAssertion(0) | |
54 | subject.Push(1) | |
55 | sizeAssertion(1) | |
56 | subject.Push(2) | |
57 | sizeAssertion(2) | |
58 | ||
59 | if result, ok := subject.Pop(); !ok { | |
60 | t.Logf("Pop is not ok") | |
61 | t.Fail() | |
62 | } else if result != 2 { | |
63 | t.Logf("got: %d\nwant: %d", result, 2) | |
64 | t.Fail() | |
65 | } | |
66 | } | |
67 | ||
68 | func TestStack_Pop_NonConstructorEmpty(t *testing.T) { | |
69 | subject := &Stack{} | |
70 | ||
71 | if result, ok := subject.Pop(); ok { | |
72 | t.Logf("Pop should not have been okay") | |
73 | t.Fail() | |
74 | } else if result != nil { | |
75 | t.Logf("got: %v\nwant: %v", result, nil) | |
76 | } | |
77 | } |