Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Reverse iterator #18

Merged
merged 6 commits into from
Jun 25, 2016
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
53 changes: 41 additions & 12 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,8 @@ Implementation of various data structures and algorithms in Go.
- [Iterator](#iterator)
- [IteratorWithIndex](#iteratorwithindex)
- [IteratorWithKey](#iteratorwithkey)
- [ReverseIteratorWithIndex](#reverseiteratorwithindex)
- [ReverseIteratorWithKey](#reverseiteratorwithkey)
- [Enumerable](#enumerable)
- [EnumerableWithIndex](#enumerablewithindex)
- [EnumerableWithKey](#enumerablewithkey)
Expand All @@ -53,17 +55,18 @@ Containers are either ordered or unordered. All ordered containers provide [stat

| Container | Ordered | [Iterator](#iterator) | [Enumerable](#enumerable) | Ordered by |
| :--- | :---: | :---: | :---: | :---: |
| [ArrayList](#arraylist) | yes | yes | yes | index |
| [ArrayList](#arraylist) | yes | yes* | yes | index |
| [SinglyLinkedList](#singlylinkedlist) | yes | yes | yes | index |
| [DoublyLinkedList](#doublylinkedlist) | yes | yes | yes | index |
| [DoublyLinkedList](#doublylinkedlist) | yes | yes* | yes | index |
| [HashSet](#hashset) | no | no | no | index |
| [TreeSet](#treeset) | yes | yes | yes | index |
| [TreeSet](#treeset) | yes | yes* | yes | index |
| [LinkedListStack](#linkedliststack) | yes | yes | no | index |
| [ArrayStack](#arraystack) | yes | yes | no | index |
| [ArrayStack](#arraystack) | yes | yes* | no | index |
| [HashMap](#hashmap) | no | no | no | key |
| [TreeMap](#treemap) | yes | yes | yes | key |
| [RedBlackTree](#redblacktree) | yes | yes | no | key |
| [BinaryHeap](#binaryheap) | yes | yes | no | index |
| [TreeMap](#treemap) | yes | yes* | yes | key |
| [RedBlackTree](#redblacktree) | yes | yes* | no | key |
| [BinaryHeap](#binaryheap) | yes | yes* | no | index |
| | | <sub><sup>*reversible</sup></sub> | | |

### Lists

Expand Down Expand Up @@ -572,12 +575,12 @@ Some data structures (e.g. TreeMap, TreeSet) require a comparator function to au

Comparator is defined as:

Return values:
Return values (int):

```go
-1, if a < b
0, if a == b
1, if a > b
negative , if a < b
zero , if a == b
positive , if a > b
```

Comparator signature:
Expand Down Expand Up @@ -642,7 +645,7 @@ func main() {

### Iterator

All ordered containers have stateful iterators. Typically an iterator is obtained by _Iterator()_ function of an ordered container. Once obtained, iterator's _Next()_ function moves the iterator to the next element and returns true if there was a next element. If there was an element, then element's can be obtained by iterator's _Value()_ function. Depending on the ordering type, it's position can be obtained by iterator's _Index()_ or _Key()_ functions.
All ordered containers have stateful iterators. Typically an iterator is obtained by _Iterator()_ function of an ordered container. Once obtained, iterator's _Next()_ function moves the iterator to the next element and returns true if there was a next element. If there was an element, then element's can be obtained by iterator's _Value()_ function. Depending on the ordering type, it's position can be obtained by iterator's _Index()_ or _Key()_ functions. Some containers even provide reversible iterators, essentially the same, but provide another extra _Prev()_ function that moves the iterator to the previous element and returns true if there was a previous element.

#### IteratorWithIndex

Expand All @@ -668,6 +671,32 @@ for it.Next() {
}
```

#### ReverseIteratorWithIndex

A [iterator](#iterator) whose elements are referenced by an index. Typical usage:

```go
it := list.Iterator()
for it.Next() { /* Move to end */ }
for it.Prev() {
index, value := it.Index(), it.Value()
...
}
```

#### ReverseIteratorWithKey

A [iterator](#iterator) whose elements are referenced by a key. Typical usage:

```go
it := map.Iterator()
for it.Next() { /* Move to end */ }
for it.Prev() {
key, value := it.Key(), it.Value()
...
}
```

### Enumerable

Enumerable functions for ordered containers that implement [EnumerableWithIndex](#enumerablewithindex) or [EnumerableWithKey](#enumerablewithkey) interfaces.
Expand Down
9 changes: 8 additions & 1 deletion containers/containers.go
Original file line number Diff line number Diff line change
Expand Up @@ -24,11 +24,18 @@ OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/

// Package containers provides core interfaces and functions for data structures.
//
// Container is the base interface for all data structures to implement.
//
// Iterators provide stateful iterators.
//
// Enumerable provides Ruby inspired (each, select, map, find, any?, etc.) container functions.
package containers

import "github.com/emirpasic/gods/utils"

// Container is base interface that all data structures implement
// Container is base interface that all data structures implement.
type Container interface {
Empty() bool
Size() int
Expand Down
3 changes: 0 additions & 3 deletions containers/enumerable.go
Original file line number Diff line number Diff line change
Expand Up @@ -24,9 +24,6 @@ OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/

// Enumerable functions for ordered containers.
// Ruby's enumerable inspired package.

package containers

// EnumerableWithIndex provides functions for ordered containers whose values can be fetched by an index.
Expand Down
32 changes: 30 additions & 2 deletions containers/iterator.go
Original file line number Diff line number Diff line change
Expand Up @@ -24,8 +24,6 @@ OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/

// Stateful iterator pattern for ordered containers.

package containers

// IteratorWithIndex is stateful iterator for ordered containers whose values can be fetched by an index.
Expand Down Expand Up @@ -55,3 +53,33 @@ type IteratorWithKey interface {
// Does not modify the state of the iterator.
Key() interface{}
}

// ReverseIteratorWithIndex is stateful iterator for ordered containers whose values can be fetched by an index.
//
// Essentially it is the same as IteratorWithIndex, but provides additional Prev() function to enable traversal in reverse.
type ReverseIteratorWithIndex interface {
// Prev moves the iterator to the previous element and returns true if there was a previous element in the container.
// If Prev() returns true, then previous element's index and value can be retrieved by Index() and Value().
// Modifies the state of the iterator.
Prev() bool

IteratorWithIndex
// Next() bool
// Value() interface{}
// Index() int
}

// ReverseIteratorWithKey is a stateful iterator for ordered containers whose elements are key value pairs.
//
// Essentially it is the same as IteratorWithKey, but provides additional Prev() function to enable traversal in reverse.
type ReverseIteratorWithKey interface {
// Prev moves the iterator to the previous element and returns true if there was a previous element in the container.
// If Prev() returns true, then previous element's index and value can be retrieved by Key() and Value().
// Modifies the state of the iterator.
Prev() bool

IteratorWithKey
// Next() bool
// Value() interface{}
// Key() interface{}
}
23 changes: 18 additions & 5 deletions lists/arraylist/arraylist.go
Original file line number Diff line number Diff line change
Expand Up @@ -24,10 +24,11 @@ OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/

// Implementation of list using a slice.
// Package arraylist implements the array list.
//
// Structure is not thread safe.
// References: http://en.wikipedia.org/wiki/List_%28abstract_data_type%29

//
// Reference: https://en.wikipedia.org/wiki/List_%28abstract_data_type%29
package arraylist

import (
Expand All @@ -41,7 +42,7 @@ import (
func assertInterfaceImplementation() {
var _ lists.List = (*List)(nil)
var _ containers.EnumerableWithIndex = (*List)(nil)
var _ containers.IteratorWithIndex = (*Iterator)(nil)
var _ containers.ReverseIteratorWithIndex = (*Iterator)(nil)
}

// List holds the elements in a slice
Expand Down Expand Up @@ -194,7 +195,19 @@ func (list *List) Iterator() Iterator {
// If Next() returns true, then next element's index and value can be retrieved by Index() and Value().
// Modifies the state of the iterator.
func (iterator *Iterator) Next() bool {
iterator.index++
if iterator.index < iterator.list.size {
iterator.index++
}
return iterator.list.withinRange(iterator.index)
}

// Prev moves the iterator to the previous element and returns true if there was a previous element in the container.
// If Prev() returns true, then previous element's index and value can be retrieved by Index() and Value().
// Modifies the state of the iterator.
func (iterator *Iterator) Prev() bool {
if iterator.index >= 0 {
iterator.index--
}
return iterator.list.withinRange(iterator.index)
}

Expand Down
57 changes: 53 additions & 4 deletions lists/arraylist/arraylist_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -299,11 +299,21 @@ func TestListChaining(t *testing.T) {
}
}

func TestListIterator(t *testing.T) {
func TestListIteratorNextOnEmpty(t *testing.T) {
list := New()
it := list.Iterator()
for it.Next() {
t.Errorf("Shouldn't iterate on empty list")
}
}

func TestListIteratorNext(t *testing.T) {
list := New()
list.Add("a", "b", "c")
it := list.Iterator()
count := 0
for it.Next() {
count++
index := it.Index()
value := it.Value()
switch index {
Expand All @@ -323,13 +333,52 @@ func TestListIterator(t *testing.T) {
t.Errorf("Too many")
}
}
list.Clear()
it = list.Iterator()
for it.Next() {
if actualValue, expectedValue := count, 3; actualValue != expectedValue {
t.Errorf("Got %v expected %v", actualValue, expectedValue)
}
}

func TestListIteratorPrevOnEmpty(t *testing.T) {
list := New()
it := list.Iterator()
for it.Prev() {
t.Errorf("Shouldn't iterate on empty list")
}
}

func TestListIteratorPrev(t *testing.T) {
list := New()
list.Add("a", "b", "c")
it := list.Iterator()
for it.Next() {
}
count := 0
for it.Prev() {
count++
index := it.Index()
value := it.Value()
switch index {
case 0:
if actualValue, expectedValue := value, "a"; actualValue != expectedValue {
t.Errorf("Got %v expected %v", actualValue, expectedValue)
}
case 1:
if actualValue, expectedValue := value, "b"; actualValue != expectedValue {
t.Errorf("Got %v expected %v", actualValue, expectedValue)
}
case 2:
if actualValue, expectedValue := value, "c"; actualValue != expectedValue {
t.Errorf("Got %v expected %v", actualValue, expectedValue)
}
default:
t.Errorf("Too many")
}
}
if actualValue, expectedValue := count, 3; actualValue != expectedValue {
t.Errorf("Got %v expected %v", actualValue, expectedValue)
}
}

func BenchmarkList(b *testing.B) {
for i := 0; i < b.N; i++ {
list := New()
Expand Down
34 changes: 28 additions & 6 deletions lists/doublylinkedlist/doublylinkedlist.go
Original file line number Diff line number Diff line change
Expand Up @@ -24,10 +24,11 @@ OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/

// Implementation of doubly linked list.
// Package doublylinkedlist implements the doubly-linked list.
//
// Structure is not thread safe.
// References: http://en.wikipedia.org/wiki/Doubly_linked_list

//
// Reference: https://en.wikipedia.org/wiki/List_%28abstract_data_type%29
package doublylinkedlist

import (
Expand All @@ -41,7 +42,7 @@ import (
func assertInterfaceImplementation() {
var _ lists.List = (*List)(nil)
var _ containers.EnumerableWithIndex = (*List)(nil)
var _ containers.IteratorWithIndex = (*Iterator)(nil)
var _ containers.ReverseIteratorWithIndex = (*Iterator)(nil)
}

// List holds the elements, where each element points to the next and previous element
Expand Down Expand Up @@ -315,19 +316,40 @@ func (list *List) Iterator() Iterator {
// If Next() returns true, then next element's index and value can be retrieved by Index() and Value().
// Modifies the state of the iterator.
func (iterator *Iterator) Next() bool {
iterator.index++
if iterator.index < iterator.list.size {
iterator.index++
}
if !iterator.list.withinRange(iterator.index) {
iterator.element = nil
return false
}
if iterator.element != nil {
if iterator.index != 0 {
iterator.element = iterator.element.next
} else {
iterator.element = iterator.list.first
}
return true
}

// Prev moves the iterator to the previous element and returns true if there was a previous element in the container.
// If Prev() returns true, then previous element's index and value can be retrieved by Index() and Value().
// Modifies the state of the iterator.
func (iterator *Iterator) Prev() bool {
if iterator.index >= 0 {
iterator.index--
}
if !iterator.list.withinRange(iterator.index) {
iterator.element = nil
return false
}
if iterator.index == iterator.list.size-1 {
iterator.element = iterator.list.last
} else {
iterator.element = iterator.element.prev
}
return iterator.list.withinRange(iterator.index)
}

// Value returns the current element's value.
// Does not modify the state of the iterator.
func (iterator *Iterator) Value() interface{} {
Expand Down
Loading