add in seq package, borrowed from github.com/mediocregopher/seq

This commit is contained in:
Brian Picciano 2014-10-06 18:29:52 -04:00
parent a92852bc06
commit a4554494e3
14 changed files with 2141 additions and 4 deletions

2
.gitignore vendored
View File

@ -1 +1 @@
*.gng .goat

2
.go.yaml Normal file
View File

@ -0,0 +1,2 @@
---
path: github.com/mediocregopher/ginger

125
seq/hashmap.go Normal file
View File

@ -0,0 +1,125 @@
package seq
import (
"fmt"
"github.com/mediocregopher/ginger/types"
)
// Hash maps are built on top of hash sets. KeyVal implements Setable, but the
// Hash and Equal methods only apply to the key and ignore the value.
// Container for a key/value pair, used by HashMap to hold its data
type KV struct {
Key types.Elem
Val types.Elem
}
func KeyVal(key, val types.Elem) *KV {
return &KV{key, val}
}
// Implementation of Hash for Setable. Only actually hashes the Key field
func (kv *KV) Hash(i uint32) uint32 {
return hash(kv.Key, i)
}
// Implementation of Equal for Setable. Only actually compares the key field. If
// compared to another KV, only compares the other key as well.
func (kv *KV) Equal(v types.Elem) bool {
if kv2, ok := v.(*KV); ok {
return equal(kv.Key, kv2.Key)
}
return equal(kv.Key, v)
}
// Implementation of String for Stringer
func (kv *KV) String() string {
return fmt.Sprintf("%v -> %v", kv.Key, kv.Val)
}
// HashMaps are actually built on top of Sets, just with some added convenience
// methods for interacting with them as actual key/val stores
type HashMap struct {
set *Set
}
// Returns a new HashMap of the given KVs (or possibly just an empty HashMap)
func NewHashMap(kvs ...*KV) *HashMap {
ints := make([]types.Elem, len(kvs))
for i := range kvs {
ints[i] = kvs[i]
}
return &HashMap{
set: NewSet(ints...),
}
}
// Implementation of FirstRest for Seq interface. First return value will
// always be a *KV or nil. Completes in O(log(N)) time.
func (hm *HashMap) FirstRest() (types.Elem, Seq, bool) {
if hm == nil {
return nil, nil, false
}
el, nset, ok := hm.set.FirstRest()
return el, &HashMap{nset.(*Set)}, ok
}
// Returns a new HashMap with the given value set on the given key. Also returns
// whether or not this was the first time setting that key (false if it was
// already there and was overwritten). Has the same complexity as Set's SetVal
// method.
func (hm *HashMap) Set(key, val types.Elem) (*HashMap, bool) {
if hm == nil {
hm = NewHashMap()
}
nset, ok := hm.set.SetVal(KeyVal(key, val))
return &HashMap{nset}, ok
}
// Returns a new HashMap with the given key removed from it. Also returns
// whether or not the key was already there (true if so, false if not). Has the
// same time complexity as Set's DelVal method.
func (hm *HashMap) Del(key types.Elem) (*HashMap, bool) {
if hm == nil {
hm = NewHashMap()
}
nset, ok := hm.set.DelVal(KeyVal(key, nil))
return &HashMap{nset}, ok
}
// Returns a value for a given key from the HashMap, along with a boolean
// indicating whether or not the value was found. Has the same time complexity
// as Set's GetVal method.
func (hm *HashMap) Get(key types.Elem) (types.Elem, bool) {
if hm == nil {
return nil, false
} else if kv, ok := hm.set.GetVal(KeyVal(key, nil)); ok {
return kv.(*KV).Val, true
} else {
return nil, false
}
}
// Same as FirstRest, but returns values already casted, which may be convenient
// in some cases.
func (hm *HashMap) FirstRestKV() (*KV, *HashMap, bool) {
if el, nhm, ok := hm.FirstRest(); ok {
return el.(*KV), nhm.(*HashMap), true
} else {
return nil, nil, false
}
}
// Implementation of String for Stringer interface
func (hm *HashMap) String() string {
return ToString(hm, "{", "}")
}
// Returns the number of KVs in the HashMap. Has the same complexity as Set's
// Size method.
func (hm *HashMap) Size() uint64 {
return hm.set.Size()
}

115
seq/hashmap_test.go Normal file
View File

@ -0,0 +1,115 @@
package seq
import (
. "testing"
"github.com/mediocregopher/ginger/types"
)
func kvints(kvs ...*KV) ([]*KV, []types.Elem) {
ints := make([]types.Elem, len(kvs))
for i := range kvs {
ints[i] = kvs[i]
}
return kvs, ints
}
// Test creating a Set and calling the Seq interface methods on it
func TestHashMapSeq(t *T) {
kvs, ints := kvints(
KeyVal(1, "one"),
KeyVal(2, "two"),
)
// Testing creation and Seq interface methods
m := NewHashMap(kvs...)
ms := testSeqNoOrderGen(t, m, ints)
// ms should be empty at this point
assertEmpty(ms, t)
}
// Test getting values from a HashMap
func TestHashMapGet(t *T) {
kvs := []*KV{
KeyVal(1, "one"),
KeyVal(2, "two"),
}
// Degenerate case
m := NewHashMap()
assertEmpty(m, t)
v, ok := m.Get(1)
assertValue(v, nil, t)
assertValue(ok, false, t)
m = NewHashMap(kvs...)
v, ok = m.Get(1)
assertSeqContentsHashMap(m, kvs, t)
assertValue(v, "one", t)
assertValue(ok, true, t)
v, ok = m.Get(3)
assertSeqContentsHashMap(m, kvs, t)
assertValue(v, nil, t)
assertValue(ok, false, t)
}
// Test setting values on a HashMap
func TestHashMapSet(t *T) {
// Set on empty
m := NewHashMap()
m1, ok := m.Set(1, "one")
assertEmpty(m, t)
assertSeqContentsHashMap(m1, []*KV{KeyVal(1, "one")}, t)
assertValue(ok, true, t)
// Set on same key
m2, ok := m1.Set(1, "wat")
assertSeqContentsHashMap(m1, []*KV{KeyVal(1, "one")}, t)
assertSeqContentsHashMap(m2, []*KV{KeyVal(1, "wat")}, t)
assertValue(ok, false, t)
// Set on second new key
m3, ok := m2.Set(2, "two")
assertSeqContentsHashMap(m2, []*KV{KeyVal(1, "wat")}, t)
assertSeqContentsHashMap(m3, []*KV{KeyVal(1, "wat"), KeyVal(2, "two")}, t)
assertValue(ok, true, t)
}
// Test deleting keys from sets
func TestHashMapDel(t *T) {
kvs := []*KV{
KeyVal(1, "one"),
KeyVal(2, "two"),
KeyVal(3, "three"),
}
kvs1 := []*KV{
KeyVal(2, "two"),
KeyVal(3, "three"),
}
// Degenerate case
m := NewHashMap()
m1, ok := m.Del(1)
assertEmpty(m, t)
assertEmpty(m1, t)
assertValue(ok, false, t)
// Delete actual key
m = NewHashMap(kvs...)
m1, ok = m.Del(1)
assertSeqContentsHashMap(m, kvs, t)
assertSeqContentsHashMap(m1, kvs1, t)
assertValue(ok, true, t)
// Delete it again!
m2, ok := m1.Del(1)
assertSeqContentsHashMap(m1, kvs1, t)
assertSeqContentsHashMap(m2, kvs1, t)
assertValue(ok, false, t)
}

414
seq/hashset.go Normal file
View File

@ -0,0 +1,414 @@
package seq
import (
"fmt"
"hash/crc32"
"reflect"
"github.com/mediocregopher/ginger/types"
)
// This is an implementation of a persistent tree, which will then be used as
// the basis for vectors, hash maps, and hash sets.
type Setable interface {
// Returns an integer for the value. For two equivalent values (as defined
// by ==) Hash(i) should always return the same number. For multiple values
// of i, Hash should return different values if possible.
Hash(uint32) uint32
// Given an arbitrary value found in a Set, returns whether or not the two
// are equal
Equal(types.Elem) bool
}
// Returns an arbitrary integer for the given value/iteration tuple
func hash(v types.Elem, i uint32) uint32 {
switch vt := v.(type) {
case Setable:
return vt.Hash(i) % ARITY
case uint:
return uint32(vt) % ARITY
case uint8:
return uint32(vt) % ARITY
case uint32:
return uint32(vt) % ARITY
case uint64:
return uint32(vt) % ARITY
case int:
return uint32(vt) % ARITY
case int8:
return uint32(vt) % ARITY
case int16:
return uint32(vt) % ARITY
case int32:
return uint32(vt) % ARITY
case int64:
return uint32(vt) % ARITY
case float32:
return uint32(vt) % ARITY
case float64:
return uint32(vt) % ARITY
case string:
return crc32.ChecksumIEEE([]byte(vt)) % ARITY
case []byte:
return crc32.ChecksumIEEE(vt) % ARITY
default:
err := fmt.Sprintf("%s not hashable", reflect.TypeOf(v))
panic(err)
}
}
// Returns whether two values (potentially Setable's) are equivalent
func equal(v1, v2 types.Elem) bool {
if v1t, ok := v1.(Setable); ok {
return v1t.Equal(v2)
} else if v2t, ok := v2.(Setable); ok {
return v2t.Equal(v1)
} else if v1t, ok := v1.([]byte); ok {
if v2t, ok := v2.([]byte); ok {
if len(v1t) != len(v2t) {
return false
}
for i := range v1t {
if v1t[i] != v2t[i] {
return false
}
}
return true
}
return false
} else {
return v1 == v2
}
}
// The number of children each node in Set (implemented as a hash tree) can have
const ARITY = 32
// A Set is an implementation of Seq in the form of a persistant hash-tree. All
// public operations on it return a new, immutable form of the modified
// variable, leaving the old one intact. Immutability is implemented through
// node sharing, so operations aren't actually copying the entire hash-tree
// everytime, only the nodes which change, making the implementation very
// efficient compared to just copying.
//
// Items in sets need to be hashable and comparable. This means they either need
// to be some real numeric type (int, float32, etc...), string, []byte, or
// implement the Setable interface.
type Set struct {
// The value being held
val types.Elem
// Whether or not the held value has been set yet. Needed because the value
// could be nil
full bool
// Slice of kids of this node. Could be an empty slice
kids []*Set
// Number of values in this Set.
size uint64
}
// Returns a new Set of the given elements (or no elements, for an empty set)
func NewSet(vals ...types.Elem) *Set {
if len(vals) == 0 {
return nil
}
set := new(Set)
for i := range vals {
set.setValDirty(vals[i], 0)
}
set.size = uint64(len(vals))
return set
}
// Methods marked as "dirty" operate on the node in place, and potentially
// change it or its children.
// Dirty. Tries to set the val on this Set node, or initialize the kids slice if
// it can't. Returns whether or not the value was set and whether or not it was
// already set.
func (set *Set) shallowTrySetOrInit(val types.Elem) (bool, bool) {
if !set.full {
set.val = val
set.full = true
return true, false
} else if equal(set.val, val) {
set.val = val
set.full = true
return true, true
} else if set.kids == nil {
set.kids = make([]*Set, ARITY)
}
return false, false
}
// dirty (obviously). Sets a value on this node in place. Only used during
// initialization.
func (set *Set) setValDirty(val types.Elem, i uint32) {
if ok, _ := set.shallowTrySetOrInit(val); ok {
return
}
h := hash(val, i)
if kid := set.kids[h]; kid != nil {
kid.setValDirty(val, i+1)
} else {
set.kids[h] = NewSet(val)
}
}
// Returns a copy of this set node, including allocating and copying the kids
// slice.
func (set *Set) clone() *Set {
var newkids []*Set
if set.kids != nil {
newkids = make([]*Set, ARITY)
copy(newkids, set.kids)
}
cs := &Set{
val: set.val,
full: set.full,
kids: newkids,
size: set.size,
}
return cs
}
// The actual implementation of SetVal, because we need to pass i down the stack
func (set *Set) internalSetVal(val types.Elem, i uint32) (*Set, bool) {
if set == nil {
return NewSet(val), true
}
cset := set.clone()
if ok, prev := cset.shallowTrySetOrInit(val); ok {
return cset, !prev
}
h := hash(val, i)
newkid, ok := cset.kids[h].internalSetVal(val, i+1)
cset.kids[h] = newkid
return cset, ok
}
// Returns a new Set with the given value added to it. Also returns whether or
// not this is the first time setting this value (false if it was already there
// and was overwritten). Completes in O(log(N)) time.
func (set *Set) SetVal(val types.Elem) (*Set, bool) {
nset, ok := set.internalSetVal(val, 0)
if ok {
nset.size++
}
return nset, ok
}
// The actual implementation of DelVal, because we need to pass i down the stack
func (set *Set) internalDelVal(val types.Elem, i uint32) (*Set, bool) {
if set == nil {
return nil, false
} else if set.full && equal(val, set.val) {
cset := set.clone()
cset.val = nil
cset.full = false
return cset, true
} else if set.kids == nil {
return set, false
}
h := hash(val, i)
if newkid, ok := set.kids[h].internalDelVal(val, i+1); ok {
cset := set.clone()
cset.kids[h] = newkid
return cset, true
}
return set, false
}
// Returns a new Set with the given value removed from it and whether or not the
// value was actually removed. Completes in O(log(N)) time.
func (set *Set) DelVal(val types.Elem) (*Set, bool) {
nset, ok := set.internalDelVal(val, 0)
if ok && nset != nil {
nset.size--
}
return nset, ok
}
// The actual implementation of GetVal, because we need to pass i down the stack
func (set *Set) internalGetVal(val types.Elem, i uint32) (types.Elem, bool) {
if set == nil {
return nil, false
} else if set.full && equal(val, set.val) {
return set.val, true
} else if set.kids == nil {
return nil, false
}
h := hash(val, i)
return set.kids[h].internalGetVal(val, i+1)
}
// Returns a value from the Set, along with a boolean indiciating whether or
// not the value was found. Completes in O(log(N)) time.
func (set *Set) GetVal(val types.Elem) (types.Elem, bool) {
return set.internalGetVal(val, 0)
}
// Actual implementation of FirstRest. Because we need it to return a *Set
// instead of Seq for one case.
func (set *Set) internalFirstRest() (types.Elem, *Set, bool) {
if set == nil {
return nil, nil, false
}
if set.kids != nil {
var el types.Elem
var rest *Set
var ok bool
for i := range set.kids {
if el, rest, ok = set.kids[i].internalFirstRest(); ok {
cset := set.clone()
cset.kids[i] = rest
return el, cset, true
}
}
}
// We're not nil, but we don't have a value and no kids had values. We might
// as well be nil.
if !set.full {
return nil, nil, false
}
return set.val, nil, true
}
// Implementation of FirstRest for Seq interface. Completes in O(log(N)) time.
func (set *Set) FirstRest() (types.Elem, Seq, bool) {
el, restSet, ok := set.internalFirstRest()
if ok && restSet != nil {
restSet.size--
}
return el, Seq(restSet), ok
}
// Implementation of String for Stringer interface
func (set *Set) String() string {
return ToString(set, "#{", "}#")
}
// Returns the number of elements in the Set. Completes in O(1) time.
func (set *Set) Size() uint64 {
if set == nil {
return 0
}
return set.size
}
// Returns a Set with all of the elements of the original Set along with
// everything in the given Seq. If an element is present in both the Set and the
// Seq, the element in the Seq overwrites. Completes in O(M*log(N)), with M
// being the number of elements in the Seq and N the number of elements in the
// Set
func (set *Set) Union(s Seq) *Set {
if set == nil {
return ToSet(s)
}
cset := set.clone()
var el types.Elem
var ok bool
for {
if el, s, ok = s.FirstRest(); !ok {
return cset
} else if cset, ok = cset.SetVal(el); ok {
cset.size++
}
}
}
// Returns a Set with all of the elements in Seq that are also in Set. Completes
// in O(M*log(N)), with M being the number of elements in the Seq and N the
// number of elements in the Set
func (set *Set) Intersection(s Seq) *Set {
if set == nil {
return nil
}
iset := NewSet()
var el types.Elem
var ok bool
for {
if el, s, ok = s.FirstRest(); !ok {
return iset
} else if _, ok = set.GetVal(el); ok {
iset, _ = iset.SetVal(el)
}
}
}
// Returns a Set of all elements in the original Set that aren't in the Seq.
// Completes in O(M*log(N)), with M being the number of elements in the Seq and
// N the number of elements in the Set
func (set *Set) Difference(s Seq) *Set {
if set == nil {
return nil
}
cset := set.clone()
var el types.Elem
var ok bool
for {
if el, s, ok = s.FirstRest(); !ok {
return cset
} else {
cset, _ = cset.DelVal(el)
}
}
}
// Returns a Set of all elements that are either in the original Set or the
// given Seq, but not in both. Completes in O(M*log(N)), with M being the number
// of elements in the Seq and N the number of elements in the Set.
func (set *Set) SymDifference(s Seq) *Set {
if set == nil {
return ToSet(s)
}
cset := set.clone()
var cset2 *Set
var el types.Elem
var ok bool
for {
if el, s, ok = s.FirstRest(); !ok {
return cset
} else if cset2, ok = cset.DelVal(el); ok {
cset = cset2
} else {
cset, _ = cset.SetVal(el)
}
}
}
// Returns the elements in the Seq as a set. In general this completes in
// O(N*log(N)) time (I think...). If the given Seq is already a Set it will
// complete in O(1) time. If it is a HashMap it will complete in O(1) time, and
// the resultant Set will be comprised of all KVs
func ToSet(s Seq) *Set {
if set, ok := s.(*Set); ok {
return set
} else if hm, ok := s.(*HashMap); ok {
return hm.set
}
vals := ToSlice(s)
return NewSet(vals...)
}

245
seq/hashset_test.go Normal file
View File

@ -0,0 +1,245 @@
package seq
import (
. "testing"
"github.com/mediocregopher/ginger/types"
)
// Test creating a Set and calling the Seq interface methods on it
func TestSetSeq(t *T) {
ints := []types.Elem{1, "a", 5.0}
// Testing creation and Seq interface methods
s := NewSet(ints...)
ss := testSeqNoOrderGen(t, s, ints)
// ss should be empty at this point
s = ToSet(ss)
var nilpointer *Set
assertEmpty(s, t)
assertValue(s, nilpointer, t)
assertValue(len(ToSlice(s)), 0, t)
}
// Test setting a value on a Set
func TestSetVal(t *T) {
ints := []types.Elem{0, 1, 2, 3, 4}
ints1 := []types.Elem{0, 1, 2, 3, 4, 5}
// Degenerate case
s := NewSet()
assertEmpty(s, t)
s, ok := s.SetVal(0)
assertSeqContentsSet(s, []types.Elem{0}, t)
assertValue(ok, true, t)
s = NewSet(ints...)
s1, ok := s.SetVal(5)
assertSeqContentsSet(s, ints, t)
assertSeqContentsSet(s1, ints1, t)
assertValue(ok, true, t)
s2, ok := s1.SetVal(5)
assertSeqContentsSet(s1, ints1, t)
assertSeqContentsSet(s2, ints1, t)
assertValue(ok, false, t)
}
// Test deleting a value from a Set
func TestDelVal(t *T) {
ints := []types.Elem{0, 1, 2, 3, 4}
ints1 := []types.Elem{0, 1, 2, 3}
ints2 := []types.Elem{1, 2, 3, 4}
ints3 := []types.Elem{1, 2, 3, 4, 5}
// Degenerate case
s := NewSet()
assertEmpty(s, t)
s, ok := s.DelVal(0)
assertEmpty(s, t)
assertValue(ok, false, t)
s = NewSet(ints...)
s1, ok := s.DelVal(4)
assertSeqContentsSet(s, ints, t)
assertSeqContentsSet(s1, ints1, t)
assertValue(ok, true, t)
s1, ok = s1.DelVal(4)
assertSeqContentsSet(s1, ints1, t)
assertValue(ok, false, t)
// 0 is the value on the root node of s, which is kind of a special case. We
// want to test deleting it and setting a new value (which should get put on
// the root node).
s2, ok := s.DelVal(0)
assertSeqContentsSet(s, ints, t)
assertSeqContentsSet(s2, ints2, t)
assertValue(ok, true, t)
s2, ok = s2.DelVal(0)
assertSeqContentsSet(s2, ints2, t)
assertValue(ok, false, t)
s3, ok := s2.SetVal(5)
assertSeqContentsSet(s2, ints2, t)
assertSeqContentsSet(s3, ints3, t)
assertValue(ok, true, t)
}
// Test getting values from a Set
func GetVal(t *T) {
//Degenerate case
s := NewSet()
v, ok := s.GetVal(1)
assertValue(v, nil, t)
assertValue(ok, false, t)
s = NewSet(0, 1, 2, 3, 4)
v, ok = s.GetVal(1)
assertValue(v, 1, t)
assertValue(ok, true, t)
// After delete
s, _ = s.DelVal(1)
v, ok = s.GetVal(1)
assertValue(v, nil, t)
assertValue(ok, false, t)
// After set
s, _ = s.SetVal(1)
v, ok = s.GetVal(1)
assertValue(v, 1, t)
assertValue(ok, true, t)
// After delete root node
s, _ = s.DelVal(0)
v, ok = s.GetVal(0)
assertValue(v, nil, t)
assertValue(ok, false, t)
// After set root node
s, _ = s.SetVal(5)
v, ok = s.GetVal(5)
assertValue(v, 5, t)
assertValue(ok, true, t)
}
// Test that Size functions properly for all cases
func TestSetSize(t *T) {
// Degenerate case
s := NewSet()
assertValue(s.Size(), uint64(0), t)
// Initialization case
s = NewSet(0, 1, 2)
assertValue(s.Size(), uint64(3), t)
// Setting (both value not in and a value already in)
s, _ = s.SetVal(3)
assertValue(s.Size(), uint64(4), t)
s, _ = s.SetVal(3)
assertValue(s.Size(), uint64(4), t)
// Deleting (both value already in and a value not in)
s, _ = s.DelVal(3)
assertValue(s.Size(), uint64(3), t)
s, _ = s.DelVal(3)
assertValue(s.Size(), uint64(3), t)
// Deleting and setting the root node
s, _ = s.DelVal(0)
assertValue(s.Size(), uint64(2), t)
s, _ = s.SetVal(5)
assertValue(s.Size(), uint64(3), t)
}
// Test that Union functions properly
func TestUnion(t *T) {
// Degenerate case
empty := NewSet()
assertEmpty(empty.Union(empty), t)
ints1 := []types.Elem{0, 1, 2}
ints2 := []types.Elem{3, 4, 5}
intsu := append(ints1, ints2...)
s1 := NewSet(ints1...)
s2 := NewSet(ints2...)
assertSeqContentsSet(s1.Union(empty), ints1, t)
assertSeqContentsSet(empty.Union(s1), ints1, t)
su := s1.Union(s2)
assertSeqContentsSet(s1, ints1, t)
assertSeqContentsSet(s2, ints2, t)
assertSeqContentsSet(su, intsu, t)
}
// Test that Intersection functions properly
func TestIntersection(t *T) {
// Degenerate case
empty := NewSet()
assertEmpty(empty.Intersection(empty), t)
ints1 := []types.Elem{0, 1, 2}
ints2 := []types.Elem{1, 2, 3}
ints3 := []types.Elem{4, 5, 6}
intsi := []types.Elem{1, 2}
s1 := NewSet(ints1...)
s2 := NewSet(ints2...)
s3 := NewSet(ints3...)
assertEmpty(s1.Intersection(empty), t)
assertEmpty(empty.Intersection(s1), t)
si := s1.Intersection(s2)
assertEmpty(s1.Intersection(s3), t)
assertSeqContentsSet(s1, ints1, t)
assertSeqContentsSet(s2, ints2, t)
assertSeqContentsSet(s3, ints3, t)
assertSeqContentsSet(si, intsi, t)
}
// Test that Difference functions properly
func TestDifference(t *T) {
// Degenerate case
empty := NewSet()
assertEmpty(empty.Difference(empty), t)
ints1 := []types.Elem{0, 1, 2, 3}
ints2 := []types.Elem{2, 3, 4}
intsd := []types.Elem{0, 1}
s1 := NewSet(ints1...)
s2 := NewSet(ints2...)
assertSeqContentsSet(s1.Difference(empty), ints1, t)
assertEmpty(empty.Difference(s1), t)
sd := s1.Difference(s2)
assertSeqContentsSet(s1, ints1, t)
assertSeqContentsSet(s2, ints2, t)
assertSeqContentsSet(sd, intsd, t)
}
// Test that SymDifference functions properly
func TestSymDifference(t *T) {
// Degenerate case
empty := NewSet()
assertEmpty(empty.SymDifference(empty), t)
ints1 := []types.Elem{0, 1, 2, 3}
ints2 := []types.Elem{2, 3, 4}
intsd := []types.Elem{0, 1, 4}
s1 := NewSet(ints1...)
s2 := NewSet(ints2...)
assertSeqContentsSet(s1.SymDifference(empty), ints1, t)
assertSeqContentsSet(empty.SymDifference(s1), ints1, t)
sd := s1.SymDifference(s2)
assertSeqContentsSet(s1, ints1, t)
assertSeqContentsSet(s2, ints2, t)
assertSeqContentsSet(sd, intsd, t)
}

146
seq/lazy.go Normal file
View File

@ -0,0 +1,146 @@
package seq
import (
"github.com/mediocregopher/ginger/types"
)
// A Lazy is an implementation of a Seq which only actually evaluates its
// contents as those contents become needed. Lazys can be chained together, so
// if you have three steps in a pipeline there aren't two intermediate Seqs
// created, only the final resulting one. Lazys are also thread-safe, so
// multiple routines can interact with the same Lazy pointer at the same time
// but the contents will only be evalutated once.
type Lazy struct {
this types.Elem
next *Lazy
ok bool
ch chan struct{}
}
// Given a Thunk, returns a Lazy around that Thunk.
func NewLazy(t Thunk) *Lazy {
l := &Lazy{ch: make(chan struct{})}
go func() {
l.ch <- struct{}{}
el, next, ok := t()
l.this = el
l.next = NewLazy(next)
l.ok = ok
close(l.ch)
}()
return l
}
// Implementation of FirstRest for Seq interface. Completes in O(1) time.
func (l *Lazy) FirstRest() (types.Elem, Seq, bool) {
if l == nil {
return nil, l, false
}
// Reading from the channel tells the Lazy to populate the data and prepare
// the next item in the seq, it closes the channel when it's done that.
if _, ok := <-l.ch; ok {
<-l.ch
}
if l.ok {
return l.this, l.next, true
} else {
return nil, nil, false
}
}
// Implementation of String for Stringer
func (l *Lazy) String() string {
return ToString(l, "<<", ">>")
}
// Thunks are the building blocks a Lazy. A Thunk returns an element, another
// Thunk, and a boolean representing if the call yielded any results or if it
// was actually empty (true indicates it yielded results).
type Thunk func() (types.Elem, Thunk, bool)
func mapThunk(fn func(types.Elem) types.Elem, s Seq) Thunk {
return func() (types.Elem, Thunk, bool) {
el, ns, ok := s.FirstRest()
if !ok {
return nil, nil, false
}
return fn(el), mapThunk(fn, ns), true
}
}
// Lazy implementation of Map
func LMap(fn func(types.Elem) types.Elem, s Seq) Seq {
return NewLazy(mapThunk(fn, s))
}
func filterThunk(fn func(types.Elem) bool, s Seq) Thunk {
return func() (types.Elem, Thunk, bool) {
for {
el, ns, ok := s.FirstRest()
if !ok {
return nil, nil, false
}
if keep := fn(el); keep {
return el, filterThunk(fn, ns), true
} else {
s = ns
}
}
}
}
// Lazy implementation of Filter
func LFilter(fn func(types.Elem) bool, s Seq) Seq {
return NewLazy(filterThunk(fn, s))
}
func takeThunk(n uint64, s Seq) Thunk {
return func() (types.Elem, Thunk, bool) {
el, ns, ok := s.FirstRest()
if !ok || n == 0 {
return nil, nil, false
}
return el, takeThunk(n-1, ns), true
}
}
// Lazy implementation of Take
func LTake(n uint64, s Seq) Seq {
return NewLazy(takeThunk(n, s))
}
func takeWhileThunk(fn func(types.Elem) bool, s Seq) Thunk {
return func() (types.Elem, Thunk, bool) {
el, ns, ok := s.FirstRest()
if !ok || !fn(el) {
return nil, nil, false
}
return el, takeWhileThunk(fn, ns), true
}
}
// Lazy implementation of TakeWhile
func LTakeWhile(fn func(types.Elem) bool, s Seq) Seq {
return NewLazy(takeWhileThunk(fn, s))
}
func toLazyThunk(s Seq) Thunk {
return func() (types.Elem, Thunk, bool) {
el, ns, ok := s.FirstRest()
if !ok {
return nil, nil, false
}
return el, toLazyThunk(ns), true
}
}
// Returns the Seq as a Lazy. Pointless for linked-lists, but possibly useful
// for other implementations where FirstRest might be costly and the same Seq
// needs to be iterated over many times.
func ToLazy(s Seq) *Lazy {
return NewLazy(toLazyThunk(s))
}

49
seq/lazy_test.go Normal file
View File

@ -0,0 +1,49 @@
package seq
import (
. "testing"
"time"
"github.com/mediocregopher/ginger/types"
)
// Test lazy operation and thread-safety
func TestLazyBasic(t *T) {
ch := make(chan int)
mapfn := func(el types.Elem) types.Elem {
i := el.(int)
ch <- i
return i
}
intl := []types.Elem{0, 1, 2, 3, 4}
l := NewList(intl...)
ml := LMap(mapfn, l)
for i := 0; i < 10; i++ {
go func() {
mlintl := ToSlice(ml)
if !intSlicesEq(mlintl, intl) {
panic("contents not right")
}
}()
}
for _, el := range intl {
select {
case elch := <-ch:
assertValue(el, elch, t)
case <-time.After(1 * time.Millisecond):
t.Fatalf("Took too long reading result")
}
}
close(ch)
}
// Test that arbitrary Seqs can turn into Lazy
func TestToLazy(t *T) {
intl := []types.Elem{0, 1, 2, 3, 4}
l := NewList(intl...)
ll := ToLazy(l)
assertSeqContents(ll, intl, t)
}

142
seq/list.go Normal file
View File

@ -0,0 +1,142 @@
package seq
import (
"github.com/mediocregopher/ginger/types"
)
// A List is an implementation of Seq in the form of a single-linked-list, and
// is used as the underlying structure for Seqs for most methods that return a
// Seq. It is probably the most efficient and simplest of the implementations.
// Even though, conceptually, all Seq operations return a new Seq, the old Seq
// can actually share nodes with the new Seq (if both are Lists), thereby saving
// memory and copies.
type List struct {
el types.Elem
next *List
}
// Returns a new List comprised of the given elements (or no elements, for an
// empty list)
func NewList(els ...types.Elem) *List {
elsl := len(els)
if elsl == 0 {
return nil
}
var cur *List
for i := 0; i < elsl; i++ {
cur = &List{els[elsl-i-1], cur}
}
return cur
}
// Implementation of FirstRest for Seq interface. Completes in O(1) time.
func (l *List) FirstRest() (types.Elem, Seq, bool) {
if l == nil {
return nil, l, false
} else {
return l.el, l.next, true
}
}
// Implementation of String for Stringer interface.
func (l *List) String() string {
return ToString(l, "(", ")")
}
// Prepends the given element to the front of the list, returning a copy of the
// new list. Completes in O(1) time.
func (l *List) Prepend(el types.Elem) *List {
return &List{el, l}
}
// Prepends the argument Seq to the beginning of the callee List, returning a
// copy of the new List. Completes in O(N) time, N being the length of the
// argument Seq
func (l *List) PrependSeq(s Seq) *List {
var first, cur, prev *List
var el types.Elem
var ok bool
for {
el, s, ok = s.FirstRest()
if !ok {
break
}
cur = &List{el, nil}
if first == nil {
first = cur
}
if prev != nil {
prev.next = cur
}
prev = cur
}
// prev will be nil if s is empty
if prev == nil {
return l
}
prev.next = l
return first
}
// Appends the given element to the end of the List, returning a copy of the new
// List. While most methods on List don't actually copy much data, this one
// copies the entire list. Completes in O(N) time.
func (l *List) Append(el types.Elem) *List {
var first, cur, prev *List
for l != nil {
cur = &List{l.el, nil}
if first == nil {
first = cur
}
if prev != nil {
prev.next = cur
}
prev = cur
l = l.next
}
final := &List{el, nil}
if prev == nil {
return final
}
prev.next = final
return first
}
// Returns the nth index element (starting at 0), with bool being false if i is
// out of bounds. Completes in O(N) time.
func (l *List) Nth(n uint64) (types.Elem, bool) {
var el types.Elem
var ok bool
s := Seq(l)
for i := uint64(0); ; i++ {
el, s, ok = s.FirstRest()
if !ok {
return nil, false
} else if i == n {
return el, true
}
}
}
// Returns the elements in the Seq as a List. Has similar properties as
// ToSlice. In general this completes in O(N) time. If the given Seq is already
// a List it will complete in O(1) time.
func ToList(s Seq) *List {
var ok bool
var l *List
if l, ok = s.(*List); ok {
return l
}
var el types.Elem
for ret := NewList(); ; {
if el, s, ok = s.FirstRest(); ok {
ret = ret.Prepend(el)
} else {
return Reverse(ret).(*List)
}
}
}

156
seq/list_test.go Normal file
View File

@ -0,0 +1,156 @@
package seq
import (
. "testing"
"github.com/mediocregopher/ginger/types"
)
// Asserts that the given list is properly formed and has all of its size fields
// filled in correctly
func assertSaneList(l *List, t *T) {
if Size(l) == 0 {
var nilpointer *List
assertValue(l, nilpointer, t)
return
}
size := Size(l)
assertValue(Size(l.next), size-1, t)
assertSaneList(l.next, t)
}
// Test creating a list and calling the Seq interface methods on it
func TestListSeq(t *T) {
ints := []types.Elem{1, "a", 5.0}
// Testing creation and Seq interface methods
l := NewList(ints...)
sl := testSeqGen(t, l, ints)
// sl should be empty at this point
l = ToList(sl)
var nilpointer *List
assertEmpty(l, t)
assertValue(l, nilpointer, t)
assertValue(len(ToSlice(l)), 0, t)
// Testing creation of empty List.
emptyl := NewList()
assertValue(emptyl, nilpointer, t)
}
// Test the string representation of a List
func TestStringSeq(t *T) {
l := NewList(0, 1, 2, 3)
assertValue(l.String(), "( 0 1 2 3 )", t)
l = NewList(0, 1, 2, NewList(3, 4), 5, NewList(6, 7, 8))
assertValue(l.String(), "( 0 1 2 ( 3 4 ) 5 ( 6 7 8 ) )", t)
}
// Test prepending an element to the beginning of a list
func TestPrepend(t *T) {
// Normal case
intl := []types.Elem{3, 2, 1, 0}
l := NewList(intl...)
nl := l.Prepend(4)
assertSaneList(l, t)
assertSaneList(nl, t)
assertSeqContents(l, intl, t)
assertSeqContents(nl, []types.Elem{4, 3, 2, 1, 0}, t)
// Degenerate case
l = NewList()
nl = l.Prepend(0)
assertEmpty(l, t)
assertSaneList(nl, t)
assertSeqContents(nl, []types.Elem{0}, t)
}
// Test prepending a Seq to the beginning of a list
func TestPrependSeq(t *T) {
//Normal case
intl1 := []types.Elem{3, 4}
intl2 := []types.Elem{0, 1, 2}
l1 := NewList(intl1...)
l2 := NewList(intl2...)
nl := l1.PrependSeq(l2)
assertSaneList(l1, t)
assertSaneList(l2, t)
assertSaneList(nl, t)
assertSeqContents(l1, intl1, t)
assertSeqContents(l2, intl2, t)
assertSeqContents(nl, []types.Elem{0, 1, 2, 3, 4}, t)
// Degenerate cases
blank1 := NewList()
blank2 := NewList()
nl = blank1.PrependSeq(blank2)
assertEmpty(blank1, t)
assertEmpty(blank2, t)
assertEmpty(nl, t)
nl = blank1.PrependSeq(l1)
assertEmpty(blank1, t)
assertSaneList(nl, t)
assertSeqContents(nl, intl1, t)
nl = l1.PrependSeq(blank1)
assertEmpty(blank1, t)
assertSaneList(nl, t)
assertSeqContents(nl, intl1, t)
}
// Test appending to the end of a List
func TestAppend(t *T) {
// Normal case
intl := []types.Elem{3, 2, 1}
l := NewList(intl...)
nl := l.Append(0)
assertSaneList(l, t)
assertSaneList(nl, t)
assertSeqContents(l, intl, t)
assertSeqContents(nl, []types.Elem{3, 2, 1, 0}, t)
// Edge case (algorithm gets weird here)
l = NewList(1)
nl = l.Append(0)
assertSaneList(l, t)
assertSaneList(nl, t)
assertSeqContents(l, []types.Elem{1}, t)
assertSeqContents(nl, []types.Elem{1, 0}, t)
// Degenerate case
l = NewList()
nl = l.Append(0)
assertEmpty(l, t)
assertSaneList(nl, t)
assertSeqContents(nl, []types.Elem{0}, t)
}
// Test retrieving items from a List
func TestNth(t *T) {
// Normal case, in bounds
intl := []types.Elem{0, 2, 4, 6, 8}
l := NewList(intl...)
r, ok := l.Nth(3)
assertSaneList(l, t)
assertSeqContents(l, intl, t)
assertValue(r, 6, t)
assertValue(ok, true, t)
// Normal case, out of bounds
r, ok = l.Nth(8)
assertSaneList(l, t)
assertSeqContents(l, intl, t)
assertValue(r, nil, t)
assertValue(ok, false, t)
// Degenerate case
l = NewList()
r, ok = l.Nth(0)
assertEmpty(l, t)
assertValue(r, nil, t)
assertValue(ok, false, t)
}

281
seq/seq.go Normal file
View File

@ -0,0 +1,281 @@
// This package describes ginger datastructures, and many of the operations
// which can be used on those data structures
package seq
import (
"bytes"
"fmt"
"github.com/mediocregopher/ginger/types"
)
// The general interface which most operations will actually operate on. Acts as
// an interface onto any data structure
type Seq interface {
// Returns the "first" element in the data structure as well as a Seq
// containing a copy of the rest of the elements in the data structure. The
// "first" element can be random for structures which don't have a concept
// of order (like Set). Calling FirstRest on an empty Seq (Size() == 0) will
// return "first" as nil, the same empty Seq , and false. The third return
// value is true in all other cases.
FirstRest() (types.Elem, Seq, bool)
}
// Returns the number of elements contained in the data structure. In general
// this completes in O(N) time, except for Set and HashMap for which it
// completes in O(1)
func Size(s Seq) uint64 {
switch st := s.(type) {
case *Set:
return st.Size()
case *HashMap:
return st.Size()
default:
}
var ok bool
for i := uint64(0); ; {
if _, s, ok = s.FirstRest(); ok {
i++
} else {
return i
}
}
}
// Returns the elements in the Seq as a slice. If the underlying Seq has any
// implicit order to it that order will be kept. An empty Seq will return an
// empty slice; nil is never returned. In general this completes in O(N) time.
func ToSlice(s Seq) []types.Elem {
var el types.Elem
var ok bool
for ret := make([]types.Elem, 0, 8); ; {
if el, s, ok = s.FirstRest(); ok {
ret = append(ret, el)
} else {
return ret
}
}
}
// Turns a Seq into a string, with each element separated by a space and with a
// dstart and dend wrapping the whole thing
func ToString(s Seq, dstart, dend string) string {
buf := bytes.NewBufferString(dstart)
buf.WriteString(" ")
var el types.Elem
var strel fmt.Stringer
var rest Seq
var ok bool
for {
if el, rest, ok = s.FirstRest(); ok {
if strel, ok = el.(fmt.Stringer); ok {
buf.WriteString(strel.String())
} else {
buf.WriteString(fmt.Sprintf("%v", el))
}
buf.WriteString(" ")
s = rest
} else {
break
}
}
buf.WriteString(dend)
return buf.String()
}
// Returns a reversed copy of the List. Completes in O(N) time.
func Reverse(s Seq) Seq {
l := NewList()
var el types.Elem
var ok bool
for {
if el, s, ok = s.FirstRest(); ok {
l = l.Prepend(el)
} else {
return l
}
}
}
// Returns a Seq consisting of the result of applying fn to each element in the
// given Seq. Completes in O(N) time.
func Map(fn func(types.Elem) types.Elem, s Seq) Seq {
l := NewList()
var el types.Elem
var ok bool
for {
if el, s, ok = s.FirstRest(); ok {
l = l.Prepend(fn(el))
} else {
break
}
}
return Reverse(l)
}
// A function used in a reduce. The first argument is the accumulator, the
// second is an element from the Seq being reduced over. The ReduceFn returns
// the accumulator to be used in the next iteration, wherein that new
// accumulator will be called alongside the next element in the Seq. ReduceFn
// also returns a boolean representing whether or not the reduction should stop
// at this step. If true, the reductions will stop and any remaining elements in
// the Seq will be ignored.
type ReduceFn func(acc, el types.Elem) (types.Elem, bool)
// Reduces over the given Seq using ReduceFn, with acc as the first accumulator
// value in the reduce. See ReduceFn for more details on how it works. The
// return value is the result of the reduction. Completes in O(N) time.
func Reduce(fn ReduceFn, acc types.Elem, s Seq) types.Elem {
var el types.Elem
var ok, stop bool
for {
if el, s, ok = s.FirstRest(); ok {
acc, stop = fn(acc, el)
if stop {
break
}
} else {
break
}
}
return acc
}
// Returns the first element in Seq for which fn returns true, or nil. The
// returned boolean indicates whether or not a matching element was found.
// Completes in O(N) time.
func Any(fn func(el types.Elem) bool, s Seq) (types.Elem, bool) {
var el types.Elem
var ok bool
for {
if el, s, ok = s.FirstRest(); ok {
if fn(el) {
return el, true
}
} else {
return nil, false
}
}
}
// Returns true if fn returns true for all elements in the Seq. Completes in
// O(N) time.
func All(fn func(types.Elem) bool, s Seq) bool {
var el types.Elem
var ok bool
for {
if el, s, ok = s.FirstRest(); ok {
if !fn(el) {
return false
}
} else {
return true
}
}
}
// Returns a Seq containing all elements in the given Seq for which fn returned
// true. Completes in O(N) time.
func Filter(fn func(el types.Elem) bool, s Seq) Seq {
l := NewList()
var el types.Elem
var ok bool
for {
if el, s, ok = s.FirstRest(); ok {
if fn(el) {
l = l.Prepend(el)
}
} else {
return Reverse(l)
}
}
}
// Flattens the given Seq into a single, one-dimensional Seq. This method only
// flattens Seqs found in the top level of the given Seq, it does not recurse
// down to multiple layers. Completes in O(N*M) time, where N is the number of
// elements in the Seq and M is how large the Seqs in those elements actually
// are.
func Flatten(s Seq) Seq {
l := NewList()
var el types.Elem
var ok bool
for {
if el, s, ok = s.FirstRest(); ok {
if els, ok := el.(Seq); ok {
l = l.PrependSeq(Reverse(els))
} else {
l = l.Prepend(el)
}
} else {
return Reverse(l)
}
}
}
// Returns a Seq containing the first n elements in the given Seq. If n is
// greater than the length of the given Seq then the whole Seq is returned.
// Completes in O(N) time.
func Take(n uint64, s Seq) Seq {
l := NewList()
var el types.Elem
var ok bool
for i := uint64(0); i < n; i++ {
el, s, ok = s.FirstRest()
if !ok {
break
}
l = l.Prepend(el)
}
return Reverse(l)
}
// Goes through each item in the given Seq until an element returns false from
// pred. Returns a new Seq containing these truthful elements. Completes in O(N)
// time.
func TakeWhile(pred func(types.Elem) bool, s Seq) Seq {
l := NewList()
var el types.Elem
var ok bool
for {
el, s, ok = s.FirstRest()
if !ok || !pred(el) {
break
}
l = l.Prepend(el)
}
return Reverse(l)
}
// Returns a Seq the is the previous Seq without the first n elements. If n is
// greater than the length of the Seq, returns an empty Seq. Completes in O(N)
// time.
func Drop(n uint64, s Seq) Seq {
var ok bool
for i := uint64(0); i < n; i++ {
_, s, ok = s.FirstRest()
if !ok {
break
}
}
return s
}
// Drops elements from the given Seq until pred returns false for an element.
// Returns a Seq of the remaining elements (including the one which returned
// false). Completes in O(N) time.
func DropWhile(pred func(types.Elem) bool, s Seq) Seq {
var el types.Elem
var curs Seq
var ok bool
for {
el, curs, ok = s.FirstRest()
if !ok || !pred(el) {
break
}
s = curs
}
return s
}

372
seq/seq_test.go Normal file
View File

@ -0,0 +1,372 @@
package seq
import (
. "testing"
"github.com/mediocregopher/ginger/types"
)
// Tests the FirstRest, Size, and ToSlice methods of a Seq
func testSeqGen(t *T, s Seq, ints []types.Elem) Seq {
intsl := uint64(len(ints))
for i := range ints {
assertSaneList(ToList(s), t)
assertValue(Size(s), intsl-uint64(i), t)
assertSeqContents(s, ints[i:], t)
first, rest, ok := s.FirstRest()
assertValue(ok, true, t)
assertValue(first, ints[i], t)
s = rest
}
return s
}
// Tests the FirstRest, Size, and ToSlice methods of an unordered Seq
func testSeqNoOrderGen(t *T, s Seq, ints []types.Elem) Seq {
intsl := uint64(len(ints))
m := map[types.Elem]bool{}
for i := range ints {
m[ints[i]] = true
}
for i := range ints {
assertSaneList(ToList(s), t)
assertValue(Size(s), intsl-uint64(i), t)
assertSeqContentsNoOrderMap(s, m, t)
first, rest, ok := s.FirstRest()
assertValue(ok, true, t)
assertInMap(first, m, t)
delete(m, first)
s = rest
}
return s
}
// Test reversing a Seq
func TestReverse(t *T) {
// Normal case
intl := []types.Elem{3, 2, 1}
l := NewList(intl...)
nl := Reverse(l)
assertSeqContents(l, intl, t)
assertSeqContents(nl, []types.Elem{1, 2, 3}, t)
// Degenerate case
l = NewList()
nl = Reverse(l)
assertEmpty(l, t)
assertEmpty(nl, t)
}
func testMapGen(t *T, mapFn func(func(types.Elem) types.Elem, Seq) Seq) {
fn := func(n types.Elem) types.Elem {
return n.(int) + 1
}
// Normal case
intl := []types.Elem{1, 2, 3}
l := NewList(intl...)
nl := mapFn(fn, l)
assertSeqContents(l, intl, t)
assertSeqContents(nl, []types.Elem{2, 3, 4}, t)
// Degenerate case
l = NewList()
nl = mapFn(fn, l)
assertEmpty(l, t)
assertEmpty(nl, t)
}
// Test mapping over a Seq
func TestMap(t *T) {
testMapGen(t, Map)
}
// Test lazily mapping over a Seq
func TestLMap(t *T) {
testMapGen(t, LMap)
}
// Test reducing over a Seq
func TestReduce(t *T) {
fn := func(acc, el types.Elem) (types.Elem, bool) {
return acc.(int) + el.(int), false
}
// Normal case
intl := []types.Elem{1, 2, 3, 4}
l := NewList(intl...)
r := Reduce(fn, 0, l)
assertSeqContents(l, intl, t)
assertValue(r, 10, t)
// Short-circuit case
fns := func(acc, el types.Elem) (types.Elem, bool) {
return acc.(int) + el.(int), el.(int) > 2
}
r = Reduce(fns, 0, l)
assertSeqContents(l, intl, t)
assertValue(r, 6, t)
// Degenerate case
l = NewList()
r = Reduce(fn, 0, l)
assertEmpty(l, t)
assertValue(r, 0, t)
}
// Test the Any function
func TestAny(t *T) {
fn := func(el types.Elem) bool {
return el.(int) > 3
}
// Value found case
intl := []types.Elem{1, 2, 3, 4}
l := NewList(intl...)
r, ok := Any(fn, l)
assertSeqContents(l, intl, t)
assertValue(r, 4, t)
assertValue(ok, true, t)
// Value not found case
intl = []types.Elem{1, 2, 3}
l = NewList(intl...)
r, ok = Any(fn, l)
assertSeqContents(l, intl, t)
assertValue(r, nil, t)
assertValue(ok, false, t)
// Degenerate case
l = NewList()
r, ok = Any(fn, l)
assertEmpty(l, t)
assertValue(r, nil, t)
assertValue(ok, false, t)
}
// Test the All function
func TestAll(t *T) {
fn := func(el types.Elem) bool {
return el.(int) > 3
}
// All match case
intl := []types.Elem{4, 5, 6}
l := NewList(intl...)
ok := All(fn, l)
assertSeqContents(l, intl, t)
assertValue(ok, true, t)
// Not all match case
intl = []types.Elem{3, 4, 2, 5}
l = NewList(intl...)
ok = All(fn, l)
assertSeqContents(l, intl, t)
assertValue(ok, false, t)
// Degenerate case
l = NewList()
ok = All(fn, l)
assertEmpty(l, t)
assertValue(ok, true, t)
}
func testFilterGen(t *T, filterFn func(func(types.Elem) bool, Seq) Seq) {
fn := func(el types.Elem) bool {
return el.(int)%2 != 0
}
// Normal case
intl := []types.Elem{1, 2, 3, 4, 5}
l := NewList(intl...)
r := filterFn(fn, l)
assertSeqContents(l, intl, t)
assertSeqContents(r, []types.Elem{1, 3, 5}, t)
// Degenerate cases
l = NewList()
r = filterFn(fn, l)
assertEmpty(l, t)
assertEmpty(r, t)
}
// Test the Filter function
func TestFilter(t *T) {
testFilterGen(t, Filter)
}
// Test the lazy Filter function
func TestLFilter(t *T) {
testFilterGen(t, LFilter)
}
// Test Flatten-ing of a Seq
func TestFlatten(t *T) {
// Normal case
intl1 := []types.Elem{0, 1, 2}
intl2 := []types.Elem{3, 4, 5}
l1 := NewList(intl1...)
l2 := NewList(intl2...)
blank := NewList()
intl := []types.Elem{-1, l1, l2, 6, blank, 7}
l := NewList(intl...)
nl := Flatten(l)
assertSeqContents(l1, intl1, t)
assertSeqContents(l2, intl2, t)
assertEmpty(blank, t)
assertSeqContents(l, intl, t)
assertSeqContents(nl, []types.Elem{-1, 0, 1, 2, 3, 4, 5, 6, 7}, t)
// Degenerate case
nl = Flatten(blank)
assertEmpty(blank, t)
assertEmpty(nl, t)
}
func testTakeGen(t *T, takeFn func(uint64, Seq) Seq) {
// Normal case
intl := []types.Elem{0, 1, 2, 3, 4}
l := NewList(intl...)
nl := takeFn(3, l)
assertSeqContents(l, intl, t)
assertSeqContents(nl, []types.Elem{0, 1, 2}, t)
// Edge cases
nl = takeFn(5, l)
assertSeqContents(l, intl, t)
assertSeqContents(nl, intl, t)
nl = takeFn(6, l)
assertSeqContents(l, intl, t)
assertSeqContents(nl, intl, t)
// Degenerate cases
empty := NewList()
nl = takeFn(1, empty)
assertEmpty(empty, t)
assertEmpty(nl, t)
nl = takeFn(0, l)
assertSeqContents(l, intl, t)
assertEmpty(nl, t)
}
// Test taking from a Seq
func TestTake(t *T) {
testTakeGen(t, Take)
}
// Test lazily taking from a Seq
func TestLTake(t *T) {
testTakeGen(t, LTake)
}
func testTakeWhileGen(t *T, takeWhileFn func(func(types.Elem) bool, Seq) Seq) {
pred := func(el types.Elem) bool {
return el.(int) < 3
}
// Normal case
intl := []types.Elem{0, 1, 2, 3, 4, 5}
l := NewList(intl...)
nl := takeWhileFn(pred, l)
assertSeqContents(l, intl, t)
assertSeqContents(nl, []types.Elem{0, 1, 2}, t)
// Edge cases
intl = []types.Elem{5, 5, 5}
l = NewList(intl...)
nl = takeWhileFn(pred, l)
assertSeqContents(l, intl, t)
assertEmpty(nl, t)
intl = []types.Elem{0, 1, 2}
l = NewList(intl...)
nl = takeWhileFn(pred, l)
assertSeqContents(l, intl, t)
assertSeqContents(nl, []types.Elem{0, 1, 2}, t)
// Degenerate case
l = NewList()
nl = takeWhileFn(pred, l)
assertEmpty(l, t)
assertEmpty(nl, t)
}
// Test taking from a Seq until a given condition
func TestTakeWhile(t *T) {
testTakeWhileGen(t, TakeWhile)
}
// Test lazily taking from a Seq until a given condition
func TestLTakeWhile(t *T) {
testTakeWhileGen(t, LTakeWhile)
}
// Test dropping from a Seq
func TestDrop(t *T) {
// Normal case
intl := []types.Elem{0, 1, 2, 3, 4}
l := NewList(intl...)
nl := Drop(3, l)
assertSeqContents(l, intl, t)
assertSeqContents(nl, []types.Elem{3, 4}, t)
// Edge cases
nl = Drop(5, l)
assertSeqContents(l, intl, t)
assertEmpty(nl, t)
nl = Drop(6, l)
assertSeqContents(l, intl, t)
assertEmpty(nl, t)
// Degenerate cases
empty := NewList()
nl = Drop(1, empty)
assertEmpty(empty, t)
assertEmpty(nl, t)
nl = Drop(0, l)
assertSeqContents(l, intl, t)
assertSeqContents(nl, intl, t)
}
// Test dropping from a Seq until a given condition
func TestDropWhile(t *T) {
pred := func(el types.Elem) bool {
return el.(int) < 3
}
// Normal case
intl := []types.Elem{0, 1, 2, 3, 4, 5}
l := NewList(intl...)
nl := DropWhile(pred, l)
assertSeqContents(l, intl, t)
assertSeqContents(nl, []types.Elem{3, 4, 5}, t)
// Edge cases
intl = []types.Elem{5, 5, 5}
l = NewList(intl...)
nl = DropWhile(pred, l)
assertSeqContents(l, intl, t)
assertSeqContents(nl, intl, t)
intl = []types.Elem{0, 1, 2}
l = NewList(intl...)
nl = DropWhile(pred, l)
assertSeqContents(l, intl, t)
assertEmpty(nl, t)
// Degenerate case
l = NewList()
nl = DropWhile(pred, l)
assertEmpty(l, t)
assertEmpty(nl, t)
}

90
seq/util.go Normal file
View File

@ -0,0 +1,90 @@
package seq
import (
"testing"
"github.com/mediocregopher/ginger/types"
)
// Returns whether or not two types.Elem slices contain the same elements
func intSlicesEq(a, b []types.Elem) bool {
if len(a) != len(b) {
return false
}
for i := range a {
if a[i] != b[i] {
return false
}
}
return true
}
// Asserts that the given Seq is empty (contains no elements)
func assertEmpty(s Seq, t *testing.T) {
if Size(s) != 0 {
t.Fatalf("Seq isn't empty: %v", ToSlice(s))
}
}
// Asserts that the given Seq has the given elements
func assertSeqContents(s Seq, intl []types.Elem, t *testing.T) {
if ls := ToSlice(s); !intSlicesEq(ls, intl) {
t.Fatalf("Slice contents wrong: %v not %v", ls, intl)
}
}
// Asserts that the given Seq has all elements, and only the elements, in the
// given map
func assertSeqContentsNoOrderMap(s Seq, m map[types.Elem]bool, t *testing.T) {
ls := ToSlice(s)
if len(ls) != len(m) {
t.Fatalf("Slice contents wrong: %v not %v", ls, m)
}
for i := range ls {
if _, ok := m[ls[i]]; !ok {
t.Fatalf("Slice contents wrong: %v not %v", ls, m)
}
}
}
// Asserts that the given Seq has all the elements, and only the elements
// (duplicates removed), in the given slice, although no necessarily in the
// order given in the slice
func assertSeqContentsSet(s Seq, ints []types.Elem, t *testing.T) {
m := map[types.Elem]bool{}
for i := range ints {
m[ints[i]] = true
}
assertSeqContentsNoOrderMap(s, m, t)
}
func assertSeqContentsHashMap(s Seq, kvs []*KV, t *testing.T) {
m := map[types.Elem]bool{}
for i := range kvs {
m[*kvs[i]] = true
}
ls := ToSlice(s)
if len(ls) != len(m) {
t.Fatalf("Slice contents wrong: %v not %v", ls, m)
}
for i := range ls {
kv := ls[i].(*KV)
if _, ok := m[*kv]; !ok {
t.Fatalf("Slice contents wrong: %v not %v", ls, m)
}
}
}
// Asserts that v1 is the same as v2
func assertValue(v1, v2 types.Elem, t *testing.T) {
if v1 != v2 {
t.Fatalf("Value wrong: %v not %v", v1, v2)
}
}
// Asserts that v1 is a key in the given map
func assertInMap(v1 types.Elem, m map[types.Elem]bool, t *testing.T) {
if _, ok := m[v1]; !ok {
t.Fatalf("Value not in set: %v not in %v", v1, m)
}
}

View File

@ -6,12 +6,12 @@ package types
type Elem interface { type Elem interface {
} }
type String string type Str string
type Integer int type Int int
type Float float32 type Float float32
type Char rune type Char rune
type Error error type Err error