-
Notifications
You must be signed in to change notification settings - Fork 32
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #289 from vaskoz/day139
Day139
- Loading branch information
Showing
3 changed files
with
139 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,49 @@ | ||
package day139 | ||
|
||
// Iterator is a simple iterator interface. | ||
type Iterator interface { | ||
Next() interface{} | ||
HasNext() bool | ||
} | ||
|
||
// PeekableIterator wraps an iterator and provides the ability to peek. | ||
type PeekableIterator struct { | ||
peek interface{} | ||
peeked bool | ||
Iter Iterator | ||
} | ||
|
||
// Peek returns the next element without moving the iterator. | ||
func (p *PeekableIterator) Peek() interface{} { | ||
has := p.Iter.HasNext() | ||
if !p.peeked && !has { | ||
return nil | ||
} else if !p.peeked { | ||
p.peeked = true | ||
p.peek = p.Iter.Next() | ||
} | ||
return p.peek | ||
} | ||
|
||
// Next returns the next element and advances the iterator. | ||
func (p *PeekableIterator) Next() interface{} { | ||
has := p.Iter.HasNext() | ||
var result interface{} | ||
if p.peeked { | ||
result = p.peek | ||
} else if has { | ||
result = p.Iter.Next() | ||
} | ||
p.peeked = false | ||
return result | ||
} | ||
|
||
// HasNext returns true if there is more to read from the iterator | ||
// and false otherwise. | ||
func (p *PeekableIterator) HasNext() bool { | ||
has := p.Iter.HasNext() | ||
if has || p.peeked { | ||
return true | ||
} | ||
return false | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,89 @@ | ||
package day139 | ||
|
||
import "testing" | ||
|
||
var testcases = []struct { | ||
data []interface{} | ||
}{ | ||
{[]interface{}{3, 5, 6, 7, 9}}, | ||
{[]interface{}{"foo", "bar", "baz"}}, | ||
} | ||
|
||
type SliceIterator struct { | ||
Slice []interface{} | ||
pos int | ||
} | ||
|
||
func (si *SliceIterator) Next() interface{} { | ||
if si.pos >= len(si.Slice) { | ||
return nil | ||
} | ||
result := si.Slice[si.pos] | ||
si.pos++ | ||
return result | ||
} | ||
|
||
func (si *SliceIterator) HasNext() bool { | ||
return si.pos < len(si.Slice) | ||
} | ||
|
||
func TestPeekableIterator(t *testing.T) { | ||
t.Parallel() | ||
for _, tc := range testcases { | ||
p := PeekableIterator{Iter: &SliceIterator{Slice: tc.data}} | ||
for i := range tc.data { | ||
if result := p.Peek(); result != tc.data[i] { | ||
t.Errorf("Peek should return %v got %v", tc.data[i], result) | ||
} | ||
if more := p.HasNext(); !more { | ||
t.Errorf("There should be more data") | ||
} | ||
if result := p.Next(); result != tc.data[i] { | ||
t.Errorf("Next should return %v got %v", tc.data[i], result) | ||
} | ||
} | ||
if result := p.Peek(); result != nil { | ||
t.Errorf("Peek should return %v got %v", nil, result) | ||
} | ||
if more := p.HasNext(); more { | ||
t.Errorf("There should be NO more data") | ||
} | ||
if result := p.Next(); result != nil { | ||
t.Errorf("Next should return %v got %v", nil, result) | ||
} | ||
} | ||
} | ||
|
||
func TestPeekableIteratorNoPeek(t *testing.T) { | ||
t.Parallel() | ||
for _, tc := range testcases { | ||
p := PeekableIterator{Iter: &SliceIterator{Slice: tc.data}} | ||
for i := range tc.data { | ||
if more := p.HasNext(); !more { | ||
t.Errorf("There should be more data") | ||
} | ||
if result := p.Next(); result != tc.data[i] { | ||
t.Errorf("Next should return %v got %v", tc.data[i], result) | ||
} | ||
} | ||
if more := p.HasNext(); more { | ||
t.Errorf("There should be NO more data") | ||
} | ||
if result := p.Next(); result != nil { | ||
t.Errorf("Next should return %v got %v", nil, result) | ||
} | ||
} | ||
} | ||
|
||
func BenchmarkPeekableIterator(b *testing.B) { | ||
for i := 0; i < b.N; i++ { | ||
for _, tc := range testcases { | ||
p := PeekableIterator{Iter: &SliceIterator{Slice: tc.data}} | ||
for range tc.data { | ||
p.Peek() | ||
p.HasNext() | ||
p.Next() | ||
} | ||
} | ||
} | ||
} |