add in seq package, borrowed from github.com/mediocregopher/seq
This commit is contained in:
parent
a92852bc06
commit
a4554494e3
2
.gitignore
vendored
2
.gitignore
vendored
@ -1 +1 @@
|
|||||||
*.gng
|
.goat
|
||||||
|
125
seq/hashmap.go
Normal file
125
seq/hashmap.go
Normal 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
115
seq/hashmap_test.go
Normal 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
414
seq/hashset.go
Normal 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
245
seq/hashset_test.go
Normal 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
146
seq/lazy.go
Normal 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
49
seq/lazy_test.go
Normal 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
142
seq/list.go
Normal 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
156
seq/list_test.go
Normal 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
281
seq/seq.go
Normal 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
372
seq/seq_test.go
Normal 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
90
seq/util.go
Normal 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)
|
||||||
|
}
|
||||||
|
}
|
@ -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
|
||||||
|
Loading…
Reference in New Issue
Block a user