Skip to content

Commit

Permalink
Merge pull request #3 from jteeuwen/master
Browse files Browse the repository at this point in the history
Allow Hash and List Iterators to break out of iteration.
  • Loading branch information
zond committed Jul 6, 2013
2 parents a0651f4 + 4cfa3f4 commit 5b217c5
Show file tree
Hide file tree
Showing 4 changed files with 92 additions and 16 deletions.
21 changes: 14 additions & 7 deletions hash.go
Expand Up @@ -11,7 +11,7 @@ import (
const max_exponent = 32
const default_load_factor = 0.5

type HashIterator func(k Hashable, v Thing)
type HashIterator func(k Hashable, v Thing) bool

type hashHit hit

Expand Down Expand Up @@ -154,12 +154,15 @@ func (self *Hash) Size() int {

/*
Each will run i on each key and value.
It returns true if the iteration was interrupted.
This is the case when one of the HashIterator calls returned true, indicating
the iteration should be stopped.
*/
func (self *Hash) Each(i HashIterator) {
self.getBucketByHashCode(0).each(func(t Thing) {
if e := t.(*entry); e.real() {
i(e.key, e.val())
}
func (self *Hash) Each(i HashIterator) bool {
return self.getBucketByHashCode(0).each(func(t Thing) bool {
e := t.(*entry)
return e.real() && i(e.key, e.val())
})
}

Expand Down Expand Up @@ -192,11 +195,15 @@ func (self *Hash) Verify() error {
*/
func (self *Hash) ToMap() map[Hashable]Thing {
rval := make(map[Hashable]Thing)
self.Each(func(k Hashable, v Thing) {

self.Each(func(k Hashable, v Thing) bool {
rval[k] = v
return false
})

return rval
}

func (self *Hash) isBucket(n *element) (isBucket bool, index, superIndex, subIndex uint32) {
e := n.value.(*entry)
index = e.hashCode & ((1 << self.exponent) - 1)
Expand Down
32 changes: 31 additions & 1 deletion hash_test.go
Expand Up @@ -214,20 +214,50 @@ func TestHashEach(t *testing.T) {
h.Put(StringKey("b"), "2")
h.Put(StringKey("c"), "3")
h.Put(StringKey("d"), "4")

cmp := make(map[Hashable]Thing)
cmp[StringKey("a")] = "1"
cmp[StringKey("b")] = "2"
cmp[StringKey("c")] = "3"
cmp[StringKey("d")] = "4"

m := make(map[Hashable]Thing)
h.Each(func(k Hashable, v Thing) {

h.Each(func(k Hashable, v Thing) bool {
m[k] = v
return false
})

if !reflect.DeepEqual(cmp, m) {
t.Error(m, "should be", cmp)
}
}

func TestHashEachInterrupt(t *testing.T) {
h := NewHash()
h.Put(StringKey("a"), "1")
h.Put(StringKey("b"), "2")
h.Put(StringKey("c"), "3")
h.Put(StringKey("d"), "4")

m := make(map[Hashable]Thing)

interrupted := h.Each(func(k Hashable, v Thing) bool {
m[k] = v

// Break the iteration when we reach 2 elements
return len(m) == 2
})

if !interrupted {
t.Error("Iteration should have been interrupted.")
}

if len(m) != 2 {
t.Error(m, "should have 2 elements. Have", len(m))
}
}

func TestPutDelete(t *testing.T) {
h := NewHash()
if v, _ := h.Delete(StringKey("e")); v != nil {
Expand Down
24 changes: 17 additions & 7 deletions list.go
Expand Up @@ -9,7 +9,7 @@ import (

var deletedElement = "deleted"

type ListIterator func(t Thing)
type ListIterator func(t Thing) bool

type hit struct {
left *element
Expand Down Expand Up @@ -67,13 +67,16 @@ func (self *List) Pop() (rval Thing, ok bool) {

/*
Each will run i on each element.
It returns true if the iteration was interrupted.
This is the case when one of the ListIterator calls returned true, indicating
the iteration should be stopped.
*/
func (self *List) Each(i ListIterator) {
func (self *List) Each(i ListIterator) bool {
n := self.element.next()
if n != nil {
n.each(i)
}
return n != nil && n.each(i)
}

func (self *List) String() string {
return fmt.Sprint(self.ToSlice())
}
Expand Down Expand Up @@ -147,13 +150,20 @@ func (self *element) next() *element {
*/
return nil
}
func (self *element) each(i ListIterator) {

func (self *element) each(i ListIterator) bool {
n := self

for n != nil {
i(n.value)
if i(n.value) {
return true
}
n = n.next()
}

return false
}

func (self *element) val() Thing {
if self == nil {
return nil
Expand Down
31 changes: 30 additions & 1 deletion list_test.go
Expand Up @@ -174,16 +174,45 @@ func TestListEach(t *testing.T) {
nr.add("d")
nr.add("c")
nr.add("b")

var a []Thing
nr.each(func(t Thing) {

nr.each(func(t Thing) bool {
a = append(a, t)
return false
})

exp := []Thing{nil, "b", "c", "d", "f", "g", "h"}
if !reflect.DeepEqual(a, exp) {
t.Error(a, "should be", exp)
}
}

func TestListEachInterrupt(t *testing.T) {
nr := new(element)
nr.add("h")
nr.add("g")
nr.add("f")
nr.add("d")
nr.add("c")
nr.add("b")

var a []Thing

interrupted := nr.each(func(t Thing) bool {
a = append(a, t)
return len(a) == 2
})

if !interrupted {
t.Error("Iteration should have been interrupted.")
}

if len(a) != 2 {
t.Error("List should have 2 elements. Have", len(a))
}
}

func TestPushBefore(t *testing.T) {
runtime.GOMAXPROCS(runtime.NumCPU())
nr := new(element)
Expand Down

0 comments on commit 5b217c5

Please sign in to comment.