make Elem interface have a method on it, implement that in seq (bleh), implement GoType
This commit is contained in:
parent
99b67fa801
commit
4188d0b84a
@ -28,9 +28,18 @@ func (kv *KV) Hash(i uint32) uint32 {
|
|||||||
// compared to another KV, only compares the other key as well.
|
// compared to another KV, only compares the other key as well.
|
||||||
func (kv *KV) Equal(v types.Elem) bool {
|
func (kv *KV) Equal(v types.Elem) bool {
|
||||||
if kv2, ok := v.(*KV); ok {
|
if kv2, ok := v.(*KV); ok {
|
||||||
return equal(kv.Key, kv2.Key)
|
return kv.Key.Equal(kv2.Key)
|
||||||
}
|
}
|
||||||
return equal(kv.Key, v)
|
return kv.Key.Equal(v)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (kv *KV) fullEqual(v types.Elem) bool {
|
||||||
|
kv2, ok := v.(*KV)
|
||||||
|
if !ok {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
return kv.Key.Equal(kv2.Key) && kv.Val.Equal(kv2.Val)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Implementation of String for Stringer
|
// Implementation of String for Stringer
|
||||||
@ -65,6 +74,38 @@ func (hm *HashMap) FirstRest() (types.Elem, Seq, bool) {
|
|||||||
return el, &HashMap{nset.(*Set)}, ok
|
return el, &HashMap{nset.(*Set)}, ok
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Implementation of Equal for types.Elem interface. Completes in O(Nlog(M))
|
||||||
|
// time if e is another HashMap, where M is the size of the given HashMap
|
||||||
|
func (hm *HashMap) Equal(e types.Elem) bool {
|
||||||
|
// This can't just use Set's Equal because that would end up using KeyVal's
|
||||||
|
// Equal, which is not a true Equal
|
||||||
|
|
||||||
|
hm2, ok := e.(*HashMap)
|
||||||
|
if !ok {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
var el types.Elem
|
||||||
|
s := Seq(hm)
|
||||||
|
size := uint64(0)
|
||||||
|
|
||||||
|
for {
|
||||||
|
el, s, ok = s.FirstRest()
|
||||||
|
if !ok {
|
||||||
|
return size == hm2.Size()
|
||||||
|
}
|
||||||
|
size++
|
||||||
|
|
||||||
|
kv := el.(*KV)
|
||||||
|
k, v := kv.Key, kv.Val
|
||||||
|
|
||||||
|
v2, ok := hm2.Get(k)
|
||||||
|
if !ok || !v.Equal(v2) {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Returns a new HashMap with the given value set on the given key. Also returns
|
// 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
|
// 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
|
// already there and was overwritten). Has the same complexity as Set's SetVal
|
||||||
|
@ -14,11 +14,16 @@ func kvints(kvs ...*KV) ([]*KV, []types.Elem) {
|
|||||||
return kvs, ints
|
return kvs, ints
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Test that HashMap implements types.Elem (compile-time check)
|
||||||
|
func TestHashMapElem(t *T) {
|
||||||
|
_ = types.Elem(NewHashMap())
|
||||||
|
}
|
||||||
|
|
||||||
// Test creating a Set and calling the Seq interface methods on it
|
// Test creating a Set and calling the Seq interface methods on it
|
||||||
func TestHashMapSeq(t *T) {
|
func TestHashMapSeq(t *T) {
|
||||||
kvs, ints := kvints(
|
kvs, ints := kvints(
|
||||||
KeyVal(1, "one"),
|
keyValV(1, "one"),
|
||||||
KeyVal(2, "two"),
|
keyValV(2, "two"),
|
||||||
)
|
)
|
||||||
|
|
||||||
// Testing creation and Seq interface methods
|
// Testing creation and Seq interface methods
|
||||||
@ -29,27 +34,50 @@ func TestHashMapSeq(t *T) {
|
|||||||
assertEmpty(ms, t)
|
assertEmpty(ms, t)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Test that the Equal method on HashMaps works
|
||||||
|
func TestHashMapEqual(t *T) {
|
||||||
|
hm, hm2 := NewHashMap(), NewHashMap()
|
||||||
|
assertValue(hm.Equal(hm2), true, t)
|
||||||
|
assertValue(hm2.Equal(hm), true, t)
|
||||||
|
|
||||||
|
hm = NewHashMap(keyValV(1, "one"), keyValV(2, "two"))
|
||||||
|
assertValue(hm.Equal(hm2), false, t)
|
||||||
|
assertValue(hm2.Equal(hm), false, t)
|
||||||
|
|
||||||
|
hm2 = NewHashMap(keyValV(1, "one"))
|
||||||
|
assertValue(hm.Equal(hm2), false, t)
|
||||||
|
assertValue(hm2.Equal(hm), false, t)
|
||||||
|
|
||||||
|
hm2 = NewHashMap(keyValV(1, "one"), keyValV(2, "three?"))
|
||||||
|
assertValue(hm.Equal(hm2), false, t)
|
||||||
|
assertValue(hm2.Equal(hm), false, t)
|
||||||
|
|
||||||
|
hm2 = NewHashMap(keyValV(1, "one"), keyValV(2, "two"))
|
||||||
|
assertValue(hm.Equal(hm2), true, t)
|
||||||
|
assertValue(hm2.Equal(hm), true, t)
|
||||||
|
}
|
||||||
|
|
||||||
// Test getting values from a HashMap
|
// Test getting values from a HashMap
|
||||||
func TestHashMapGet(t *T) {
|
func TestHashMapGet(t *T) {
|
||||||
kvs := []*KV{
|
kvs := []*KV{
|
||||||
KeyVal(1, "one"),
|
keyValV(1, "one"),
|
||||||
KeyVal(2, "two"),
|
keyValV(2, "two"),
|
||||||
}
|
}
|
||||||
|
|
||||||
// Degenerate case
|
// Degenerate case
|
||||||
m := NewHashMap()
|
m := NewHashMap()
|
||||||
assertEmpty(m, t)
|
assertEmpty(m, t)
|
||||||
v, ok := m.Get(1)
|
v, ok := m.Get(types.GoType{1})
|
||||||
assertValue(v, nil, t)
|
assertValue(v, nil, t)
|
||||||
assertValue(ok, false, t)
|
assertValue(ok, false, t)
|
||||||
|
|
||||||
m = NewHashMap(kvs...)
|
m = NewHashMap(kvs...)
|
||||||
v, ok = m.Get(1)
|
v, ok = m.Get(types.GoType{1})
|
||||||
assertSeqContentsHashMap(m, kvs, t)
|
assertSeqContentsHashMap(m, kvs, t)
|
||||||
assertValue(v, "one", t)
|
assertValue(v, types.GoType{"one"}, t)
|
||||||
assertValue(ok, true, t)
|
assertValue(ok, true, t)
|
||||||
|
|
||||||
v, ok = m.Get(3)
|
v, ok = m.Get(types.GoType{3})
|
||||||
assertSeqContentsHashMap(m, kvs, t)
|
assertSeqContentsHashMap(m, kvs, t)
|
||||||
assertValue(v, nil, t)
|
assertValue(v, nil, t)
|
||||||
assertValue(ok, false, t)
|
assertValue(ok, false, t)
|
||||||
@ -60,21 +88,21 @@ func TestHashMapSet(t *T) {
|
|||||||
|
|
||||||
// Set on empty
|
// Set on empty
|
||||||
m := NewHashMap()
|
m := NewHashMap()
|
||||||
m1, ok := m.Set(1, "one")
|
m1, ok := m.Set(types.GoType{1}, types.GoType{"one"})
|
||||||
assertEmpty(m, t)
|
assertEmpty(m, t)
|
||||||
assertSeqContentsHashMap(m1, []*KV{KeyVal(1, "one")}, t)
|
assertSeqContentsHashMap(m1, []*KV{keyValV(1, "one")}, t)
|
||||||
assertValue(ok, true, t)
|
assertValue(ok, true, t)
|
||||||
|
|
||||||
// Set on same key
|
// Set on same key
|
||||||
m2, ok := m1.Set(1, "wat")
|
m2, ok := m1.Set(types.GoType{1}, types.GoType{"wat"})
|
||||||
assertSeqContentsHashMap(m1, []*KV{KeyVal(1, "one")}, t)
|
assertSeqContentsHashMap(m1, []*KV{keyValV(1, "one")}, t)
|
||||||
assertSeqContentsHashMap(m2, []*KV{KeyVal(1, "wat")}, t)
|
assertSeqContentsHashMap(m2, []*KV{keyValV(1, "wat")}, t)
|
||||||
assertValue(ok, false, t)
|
assertValue(ok, false, t)
|
||||||
|
|
||||||
// Set on second new key
|
// Set on second new key
|
||||||
m3, ok := m2.Set(2, "two")
|
m3, ok := m2.Set(types.GoType{2}, types.GoType{"two"})
|
||||||
assertSeqContentsHashMap(m2, []*KV{KeyVal(1, "wat")}, t)
|
assertSeqContentsHashMap(m2, []*KV{keyValV(1, "wat")}, t)
|
||||||
assertSeqContentsHashMap(m3, []*KV{KeyVal(1, "wat"), KeyVal(2, "two")}, t)
|
assertSeqContentsHashMap(m3, []*KV{keyValV(1, "wat"), keyValV(2, "two")}, t)
|
||||||
assertValue(ok, true, t)
|
assertValue(ok, true, t)
|
||||||
|
|
||||||
}
|
}
|
||||||
@ -83,31 +111,31 @@ func TestHashMapSet(t *T) {
|
|||||||
func TestHashMapDel(t *T) {
|
func TestHashMapDel(t *T) {
|
||||||
|
|
||||||
kvs := []*KV{
|
kvs := []*KV{
|
||||||
KeyVal(1, "one"),
|
keyValV(1, "one"),
|
||||||
KeyVal(2, "two"),
|
keyValV(2, "two"),
|
||||||
KeyVal(3, "three"),
|
keyValV(3, "three"),
|
||||||
}
|
}
|
||||||
kvs1 := []*KV{
|
kvs1 := []*KV{
|
||||||
KeyVal(2, "two"),
|
keyValV(2, "two"),
|
||||||
KeyVal(3, "three"),
|
keyValV(3, "three"),
|
||||||
}
|
}
|
||||||
|
|
||||||
// Degenerate case
|
// Degenerate case
|
||||||
m := NewHashMap()
|
m := NewHashMap()
|
||||||
m1, ok := m.Del(1)
|
m1, ok := m.Del(types.GoType{1})
|
||||||
assertEmpty(m, t)
|
assertEmpty(m, t)
|
||||||
assertEmpty(m1, t)
|
assertEmpty(m1, t)
|
||||||
assertValue(ok, false, t)
|
assertValue(ok, false, t)
|
||||||
|
|
||||||
// Delete actual key
|
// Delete actual key
|
||||||
m = NewHashMap(kvs...)
|
m = NewHashMap(kvs...)
|
||||||
m1, ok = m.Del(1)
|
m1, ok = m.Del(types.GoType{1})
|
||||||
assertSeqContentsHashMap(m, kvs, t)
|
assertSeqContentsHashMap(m, kvs, t)
|
||||||
assertSeqContentsHashMap(m1, kvs1, t)
|
assertSeqContentsHashMap(m1, kvs1, t)
|
||||||
assertValue(ok, true, t)
|
assertValue(ok, true, t)
|
||||||
|
|
||||||
// Delete it again!
|
// Delete it again!
|
||||||
m2, ok := m1.Del(1)
|
m2, ok := m1.Del(types.GoType{1})
|
||||||
assertSeqContentsHashMap(m1, kvs1, t)
|
assertSeqContentsHashMap(m1, kvs1, t)
|
||||||
assertSeqContentsHashMap(m2, kvs1, t)
|
assertSeqContentsHashMap(m2, kvs1, t)
|
||||||
assertValue(ok, false, t)
|
assertValue(ok, false, t)
|
||||||
|
@ -30,63 +30,41 @@ func hash(v types.Elem, i uint32) uint32 {
|
|||||||
case Setable:
|
case Setable:
|
||||||
return vt.Hash(i) % ARITY
|
return vt.Hash(i) % ARITY
|
||||||
|
|
||||||
|
case types.GoType:
|
||||||
|
switch gvt := vt.V.(type) {
|
||||||
case uint:
|
case uint:
|
||||||
return uint32(vt) % ARITY
|
return uint32(gvt) % ARITY
|
||||||
case uint8:
|
case uint8:
|
||||||
return uint32(vt) % ARITY
|
return uint32(gvt) % ARITY
|
||||||
case uint32:
|
case uint32:
|
||||||
return uint32(vt) % ARITY
|
return uint32(gvt) % ARITY
|
||||||
case uint64:
|
case uint64:
|
||||||
return uint32(vt) % ARITY
|
return uint32(gvt) % ARITY
|
||||||
case int:
|
case int:
|
||||||
return uint32(vt) % ARITY
|
return uint32(gvt) % ARITY
|
||||||
case int8:
|
case int8:
|
||||||
return uint32(vt) % ARITY
|
return uint32(gvt) % ARITY
|
||||||
case int16:
|
case int16:
|
||||||
return uint32(vt) % ARITY
|
return uint32(gvt) % ARITY
|
||||||
case int32:
|
case int32:
|
||||||
return uint32(vt) % ARITY
|
return uint32(gvt) % ARITY
|
||||||
case int64:
|
case int64:
|
||||||
return uint32(vt) % ARITY
|
return uint32(gvt) % ARITY
|
||||||
case float32:
|
case float32:
|
||||||
return uint32(vt) % ARITY
|
return uint32(gvt) % ARITY
|
||||||
case float64:
|
case float64:
|
||||||
return uint32(vt) % ARITY
|
return uint32(gvt) % ARITY
|
||||||
|
|
||||||
case string:
|
case string:
|
||||||
return crc32.ChecksumIEEE([]byte(vt)) % ARITY
|
return crc32.ChecksumIEEE([]byte(gvt)) % ARITY
|
||||||
|
|
||||||
case []byte:
|
case []byte:
|
||||||
return crc32.ChecksumIEEE(vt) % ARITY
|
return crc32.ChecksumIEEE(gvt) % ARITY
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
default:
|
|
||||||
err := fmt.Sprintf("%s not hashable", reflect.TypeOf(v))
|
err := fmt.Sprintf("%s not hashable", reflect.TypeOf(v))
|
||||||
panic(err)
|
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
|
// The number of children each node in Set (implemented as a hash tree) can have
|
||||||
@ -142,7 +120,7 @@ func (set *Set) shallowTrySetOrInit(val types.Elem) (bool, bool) {
|
|||||||
set.val = val
|
set.val = val
|
||||||
set.full = true
|
set.full = true
|
||||||
return true, false
|
return true, false
|
||||||
} else if equal(set.val, val) {
|
} else if set.val.Equal(val) {
|
||||||
set.val = val
|
set.val = val
|
||||||
set.full = true
|
set.full = true
|
||||||
return true, true
|
return true, true
|
||||||
@ -215,7 +193,7 @@ func (set *Set) SetVal(val types.Elem) (*Set, bool) {
|
|||||||
func (set *Set) internalDelVal(val types.Elem, i uint32) (*Set, bool) {
|
func (set *Set) internalDelVal(val types.Elem, i uint32) (*Set, bool) {
|
||||||
if set == nil {
|
if set == nil {
|
||||||
return nil, false
|
return nil, false
|
||||||
} else if set.full && equal(val, set.val) {
|
} else if set.full && set.val.Equal(val) {
|
||||||
cset := set.clone()
|
cset := set.clone()
|
||||||
cset.val = nil
|
cset.val = nil
|
||||||
cset.full = false
|
cset.full = false
|
||||||
@ -247,7 +225,7 @@ func (set *Set) DelVal(val types.Elem) (*Set, bool) {
|
|||||||
func (set *Set) internalGetVal(val types.Elem, i uint32) (types.Elem, bool) {
|
func (set *Set) internalGetVal(val types.Elem, i uint32) (types.Elem, bool) {
|
||||||
if set == nil {
|
if set == nil {
|
||||||
return nil, false
|
return nil, false
|
||||||
} else if set.full && equal(val, set.val) {
|
} else if set.full && set.val.Equal(val) {
|
||||||
return set.val, true
|
return set.val, true
|
||||||
} else if set.kids == nil {
|
} else if set.kids == nil {
|
||||||
return nil, false
|
return nil, false
|
||||||
@ -301,6 +279,32 @@ func (set *Set) FirstRest() (types.Elem, Seq, bool) {
|
|||||||
return el, Seq(restSet), ok
|
return el, Seq(restSet), ok
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Implementation of Equal for types.Elem interface. Completes in O(Nlog(M))
|
||||||
|
// time if e is another Set, where M is the size of the given Set
|
||||||
|
func (set *Set) Equal(e types.Elem) bool {
|
||||||
|
set2, ok := e.(*Set)
|
||||||
|
if !ok {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
var el types.Elem
|
||||||
|
s := Seq(set)
|
||||||
|
size := uint64(0)
|
||||||
|
|
||||||
|
for {
|
||||||
|
el, s, ok = s.FirstRest()
|
||||||
|
if !ok {
|
||||||
|
return size == set2.Size()
|
||||||
|
}
|
||||||
|
size++
|
||||||
|
|
||||||
|
_, ok = set2.GetVal(el)
|
||||||
|
if !ok {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Implementation of String for Stringer interface
|
// Implementation of String for Stringer interface
|
||||||
func (set *Set) String() string {
|
func (set *Set) String() string {
|
||||||
return ToString(set, "#{", "}#")
|
return ToString(set, "#{", "}#")
|
||||||
|
@ -6,9 +6,14 @@ import (
|
|||||||
"github.com/mediocregopher/ginger/types"
|
"github.com/mediocregopher/ginger/types"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// Test that HashSet implements types.Elem (compile-time check)
|
||||||
|
func TestSetElem(t *T) {
|
||||||
|
_ = types.Elem(NewSet())
|
||||||
|
}
|
||||||
|
|
||||||
// Test creating a Set and calling the Seq interface methods on it
|
// Test creating a Set and calling the Seq interface methods on it
|
||||||
func TestSetSeq(t *T) {
|
func TestSetSeq(t *T) {
|
||||||
ints := []types.Elem{1, "a", 5.0}
|
ints := elemSliceV(nil, 1, "a", 5.0)
|
||||||
|
|
||||||
// Testing creation and Seq interface methods
|
// Testing creation and Seq interface methods
|
||||||
s := NewSet(ints...)
|
s := NewSet(ints...)
|
||||||
@ -22,25 +27,48 @@ func TestSetSeq(t *T) {
|
|||||||
assertValue(len(ToSlice(s)), 0, t)
|
assertValue(len(ToSlice(s)), 0, t)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Test that the Equal method on Sets works
|
||||||
|
func TestSetEqual(t *T) {
|
||||||
|
s, s2 := NewSet(), NewSet()
|
||||||
|
assertValue(s.Equal(s2), true, t)
|
||||||
|
assertValue(s2.Equal(s), true, t)
|
||||||
|
|
||||||
|
s = NewSet(elemSliceV(0, 1, 2)...)
|
||||||
|
assertValue(s.Equal(s2), false, t)
|
||||||
|
assertValue(s2.Equal(s), false, t)
|
||||||
|
|
||||||
|
s2 = NewSet(elemSliceV(0, 1)...)
|
||||||
|
assertValue(s.Equal(s2), false, t)
|
||||||
|
assertValue(s2.Equal(s), false, t)
|
||||||
|
|
||||||
|
s2 = NewSet(elemSliceV(0, 1, 3)...)
|
||||||
|
assertValue(s.Equal(s2), false, t)
|
||||||
|
assertValue(s2.Equal(s), false, t)
|
||||||
|
|
||||||
|
s2 = NewSet(elemSliceV(0, 1, 2)...)
|
||||||
|
assertValue(s.Equal(s2), true, t)
|
||||||
|
assertValue(s2.Equal(s), true, t)
|
||||||
|
}
|
||||||
|
|
||||||
// Test setting a value on a Set
|
// Test setting a value on a Set
|
||||||
func TestSetVal(t *T) {
|
func TestSetVal(t *T) {
|
||||||
ints := []types.Elem{0, 1, 2, 3, 4}
|
ints := elemSliceV(0, 1, 2, 3, 4)
|
||||||
ints1 := []types.Elem{0, 1, 2, 3, 4, 5}
|
ints1 := elemSliceV(0, 1, 2, 3, 4, 5)
|
||||||
|
|
||||||
// Degenerate case
|
// Degenerate case
|
||||||
s := NewSet()
|
s := NewSet()
|
||||||
assertEmpty(s, t)
|
assertEmpty(s, t)
|
||||||
s, ok := s.SetVal(0)
|
s, ok := s.SetVal(types.GoType{0})
|
||||||
assertSeqContentsSet(s, []types.Elem{0}, t)
|
assertSeqContentsSet(s, elemSliceV(0), t)
|
||||||
assertValue(ok, true, t)
|
assertValue(ok, true, t)
|
||||||
|
|
||||||
s = NewSet(ints...)
|
s = NewSet(ints...)
|
||||||
s1, ok := s.SetVal(5)
|
s1, ok := s.SetVal(types.GoType{5})
|
||||||
assertSeqContentsSet(s, ints, t)
|
assertSeqContentsSet(s, ints, t)
|
||||||
assertSeqContentsSet(s1, ints1, t)
|
assertSeqContentsSet(s1, ints1, t)
|
||||||
assertValue(ok, true, t)
|
assertValue(ok, true, t)
|
||||||
|
|
||||||
s2, ok := s1.SetVal(5)
|
s2, ok := s1.SetVal(types.GoType{5})
|
||||||
assertSeqContentsSet(s1, ints1, t)
|
assertSeqContentsSet(s1, ints1, t)
|
||||||
assertSeqContentsSet(s2, ints1, t)
|
assertSeqContentsSet(s2, ints1, t)
|
||||||
assertValue(ok, false, t)
|
assertValue(ok, false, t)
|
||||||
@ -48,41 +76,41 @@ func TestSetVal(t *T) {
|
|||||||
|
|
||||||
// Test deleting a value from a Set
|
// Test deleting a value from a Set
|
||||||
func TestDelVal(t *T) {
|
func TestDelVal(t *T) {
|
||||||
ints := []types.Elem{0, 1, 2, 3, 4}
|
ints := elemSliceV(0, 1, 2, 3, 4)
|
||||||
ints1 := []types.Elem{0, 1, 2, 3}
|
ints1 := elemSliceV(0, 1, 2, 3)
|
||||||
ints2 := []types.Elem{1, 2, 3, 4}
|
ints2 := elemSliceV(1, 2, 3, 4)
|
||||||
ints3 := []types.Elem{1, 2, 3, 4, 5}
|
ints3 := elemSliceV(1, 2, 3, 4, 5)
|
||||||
|
|
||||||
// Degenerate case
|
// Degenerate case
|
||||||
s := NewSet()
|
s := NewSet()
|
||||||
assertEmpty(s, t)
|
assertEmpty(s, t)
|
||||||
s, ok := s.DelVal(0)
|
s, ok := s.DelVal(types.GoType{0})
|
||||||
assertEmpty(s, t)
|
assertEmpty(s, t)
|
||||||
assertValue(ok, false, t)
|
assertValue(ok, false, t)
|
||||||
|
|
||||||
s = NewSet(ints...)
|
s = NewSet(ints...)
|
||||||
s1, ok := s.DelVal(4)
|
s1, ok := s.DelVal(types.GoType{4})
|
||||||
assertSeqContentsSet(s, ints, t)
|
assertSeqContentsSet(s, ints, t)
|
||||||
assertSeqContentsSet(s1, ints1, t)
|
assertSeqContentsSet(s1, ints1, t)
|
||||||
assertValue(ok, true, t)
|
assertValue(ok, true, t)
|
||||||
|
|
||||||
s1, ok = s1.DelVal(4)
|
s1, ok = s1.DelVal(types.GoType{4})
|
||||||
assertSeqContentsSet(s1, ints1, t)
|
assertSeqContentsSet(s1, ints1, t)
|
||||||
assertValue(ok, false, t)
|
assertValue(ok, false, t)
|
||||||
|
|
||||||
// 0 is the value on the root node of s, which is kind of a special case. We
|
// 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
|
// want to test deleting it and setting a new value (which should get put on
|
||||||
// the root node).
|
// the root node).
|
||||||
s2, ok := s.DelVal(0)
|
s2, ok := s.DelVal(types.GoType{0})
|
||||||
assertSeqContentsSet(s, ints, t)
|
assertSeqContentsSet(s, ints, t)
|
||||||
assertSeqContentsSet(s2, ints2, t)
|
assertSeqContentsSet(s2, ints2, t)
|
||||||
assertValue(ok, true, t)
|
assertValue(ok, true, t)
|
||||||
|
|
||||||
s2, ok = s2.DelVal(0)
|
s2, ok = s2.DelVal(types.GoType{0})
|
||||||
assertSeqContentsSet(s2, ints2, t)
|
assertSeqContentsSet(s2, ints2, t)
|
||||||
assertValue(ok, false, t)
|
assertValue(ok, false, t)
|
||||||
|
|
||||||
s3, ok := s2.SetVal(5)
|
s3, ok := s2.SetVal(types.GoType{5})
|
||||||
assertSeqContentsSet(s2, ints2, t)
|
assertSeqContentsSet(s2, ints2, t)
|
||||||
assertSeqContentsSet(s3, ints3, t)
|
assertSeqContentsSet(s3, ints3, t)
|
||||||
assertValue(ok, true, t)
|
assertValue(ok, true, t)
|
||||||
@ -92,36 +120,36 @@ func TestDelVal(t *T) {
|
|||||||
func GetVal(t *T) {
|
func GetVal(t *T) {
|
||||||
//Degenerate case
|
//Degenerate case
|
||||||
s := NewSet()
|
s := NewSet()
|
||||||
v, ok := s.GetVal(1)
|
v, ok := s.GetVal(types.GoType{1})
|
||||||
assertValue(v, nil, t)
|
assertValue(v, nil, t)
|
||||||
assertValue(ok, false, t)
|
assertValue(ok, false, t)
|
||||||
|
|
||||||
s = NewSet(0, 1, 2, 3, 4)
|
s = NewSet(elemSliceV(0, 1, 2, 3, 4)...)
|
||||||
v, ok = s.GetVal(1)
|
v, ok = s.GetVal(types.GoType{1})
|
||||||
assertValue(v, 1, t)
|
assertValue(v, 1, t)
|
||||||
assertValue(ok, true, t)
|
assertValue(ok, true, t)
|
||||||
|
|
||||||
// After delete
|
// After delete
|
||||||
s, _ = s.DelVal(1)
|
s, _ = s.DelVal(types.GoType{1})
|
||||||
v, ok = s.GetVal(1)
|
v, ok = s.GetVal(types.GoType{1})
|
||||||
assertValue(v, nil, t)
|
assertValue(v, nil, t)
|
||||||
assertValue(ok, false, t)
|
assertValue(ok, false, t)
|
||||||
|
|
||||||
// After set
|
// After set
|
||||||
s, _ = s.SetVal(1)
|
s, _ = s.SetVal(types.GoType{1})
|
||||||
v, ok = s.GetVal(1)
|
v, ok = s.GetVal(types.GoType{1})
|
||||||
assertValue(v, 1, t)
|
assertValue(v, 1, t)
|
||||||
assertValue(ok, true, t)
|
assertValue(ok, true, t)
|
||||||
|
|
||||||
// After delete root node
|
// After delete root node
|
||||||
s, _ = s.DelVal(0)
|
s, _ = s.DelVal(types.GoType{0})
|
||||||
v, ok = s.GetVal(0)
|
v, ok = s.GetVal(types.GoType{0})
|
||||||
assertValue(v, nil, t)
|
assertValue(v, nil, t)
|
||||||
assertValue(ok, false, t)
|
assertValue(ok, false, t)
|
||||||
|
|
||||||
// After set root node
|
// After set root node
|
||||||
s, _ = s.SetVal(5)
|
s, _ = s.SetVal(types.GoType{5})
|
||||||
v, ok = s.GetVal(5)
|
v, ok = s.GetVal(types.GoType{5})
|
||||||
assertValue(v, 5, t)
|
assertValue(v, 5, t)
|
||||||
assertValue(ok, true, t)
|
assertValue(ok, true, t)
|
||||||
}
|
}
|
||||||
@ -133,25 +161,25 @@ func TestSetSize(t *T) {
|
|||||||
assertValue(s.Size(), uint64(0), t)
|
assertValue(s.Size(), uint64(0), t)
|
||||||
|
|
||||||
// Initialization case
|
// Initialization case
|
||||||
s = NewSet(0, 1, 2)
|
s = NewSet(elemSliceV(0, 1, 2)...)
|
||||||
assertValue(s.Size(), uint64(3), t)
|
assertValue(s.Size(), uint64(3), t)
|
||||||
|
|
||||||
// Setting (both value not in and a value already in)
|
// Setting (both value not in and a value already in)
|
||||||
s, _ = s.SetVal(3)
|
s, _ = s.SetVal(types.GoType{3})
|
||||||
assertValue(s.Size(), uint64(4), t)
|
assertValue(s.Size(), uint64(4), t)
|
||||||
s, _ = s.SetVal(3)
|
s, _ = s.SetVal(types.GoType{3})
|
||||||
assertValue(s.Size(), uint64(4), t)
|
assertValue(s.Size(), uint64(4), t)
|
||||||
|
|
||||||
// Deleting (both value already in and a value not in)
|
// Deleting (both value already in and a value not in)
|
||||||
s, _ = s.DelVal(3)
|
s, _ = s.DelVal(types.GoType{3})
|
||||||
assertValue(s.Size(), uint64(3), t)
|
assertValue(s.Size(), uint64(3), t)
|
||||||
s, _ = s.DelVal(3)
|
s, _ = s.DelVal(types.GoType{3})
|
||||||
assertValue(s.Size(), uint64(3), t)
|
assertValue(s.Size(), uint64(3), t)
|
||||||
|
|
||||||
// Deleting and setting the root node
|
// Deleting and setting the root node
|
||||||
s, _ = s.DelVal(0)
|
s, _ = s.DelVal(types.GoType{0})
|
||||||
assertValue(s.Size(), uint64(2), t)
|
assertValue(s.Size(), uint64(2), t)
|
||||||
s, _ = s.SetVal(5)
|
s, _ = s.SetVal(types.GoType{5})
|
||||||
assertValue(s.Size(), uint64(3), t)
|
assertValue(s.Size(), uint64(3), t)
|
||||||
|
|
||||||
}
|
}
|
||||||
@ -162,8 +190,8 @@ func TestUnion(t *T) {
|
|||||||
empty := NewSet()
|
empty := NewSet()
|
||||||
assertEmpty(empty.Union(empty), t)
|
assertEmpty(empty.Union(empty), t)
|
||||||
|
|
||||||
ints1 := []types.Elem{0, 1, 2}
|
ints1 := elemSliceV(0, 1, 2)
|
||||||
ints2 := []types.Elem{3, 4, 5}
|
ints2 := elemSliceV(3, 4, 5)
|
||||||
intsu := append(ints1, ints2...)
|
intsu := append(ints1, ints2...)
|
||||||
s1 := NewSet(ints1...)
|
s1 := NewSet(ints1...)
|
||||||
s2 := NewSet(ints2...)
|
s2 := NewSet(ints2...)
|
||||||
@ -183,10 +211,10 @@ func TestIntersection(t *T) {
|
|||||||
empty := NewSet()
|
empty := NewSet()
|
||||||
assertEmpty(empty.Intersection(empty), t)
|
assertEmpty(empty.Intersection(empty), t)
|
||||||
|
|
||||||
ints1 := []types.Elem{0, 1, 2}
|
ints1 := elemSliceV(0, 1, 2)
|
||||||
ints2 := []types.Elem{1, 2, 3}
|
ints2 := elemSliceV(1, 2, 3)
|
||||||
ints3 := []types.Elem{4, 5, 6}
|
ints3 := elemSliceV(4, 5, 6)
|
||||||
intsi := []types.Elem{1, 2}
|
intsi := elemSliceV(1, 2)
|
||||||
s1 := NewSet(ints1...)
|
s1 := NewSet(ints1...)
|
||||||
s2 := NewSet(ints2...)
|
s2 := NewSet(ints2...)
|
||||||
s3 := NewSet(ints3...)
|
s3 := NewSet(ints3...)
|
||||||
@ -208,9 +236,9 @@ func TestDifference(t *T) {
|
|||||||
empty := NewSet()
|
empty := NewSet()
|
||||||
assertEmpty(empty.Difference(empty), t)
|
assertEmpty(empty.Difference(empty), t)
|
||||||
|
|
||||||
ints1 := []types.Elem{0, 1, 2, 3}
|
ints1 := elemSliceV(0, 1, 2, 3)
|
||||||
ints2 := []types.Elem{2, 3, 4}
|
ints2 := elemSliceV(2, 3, 4)
|
||||||
intsd := []types.Elem{0, 1}
|
intsd := elemSliceV(0, 1)
|
||||||
s1 := NewSet(ints1...)
|
s1 := NewSet(ints1...)
|
||||||
s2 := NewSet(ints2...)
|
s2 := NewSet(ints2...)
|
||||||
|
|
||||||
@ -229,9 +257,9 @@ func TestSymDifference(t *T) {
|
|||||||
empty := NewSet()
|
empty := NewSet()
|
||||||
assertEmpty(empty.SymDifference(empty), t)
|
assertEmpty(empty.SymDifference(empty), t)
|
||||||
|
|
||||||
ints1 := []types.Elem{0, 1, 2, 3}
|
ints1 := elemSliceV(0, 1, 2, 3)
|
||||||
ints2 := []types.Elem{2, 3, 4}
|
ints2 := elemSliceV(2, 3, 4)
|
||||||
intsd := []types.Elem{0, 1, 4}
|
intsd := elemSliceV(0, 1, 4)
|
||||||
s1 := NewSet(ints1...)
|
s1 := NewSet(ints1...)
|
||||||
s2 := NewSet(ints2...)
|
s2 := NewSet(ints2...)
|
||||||
|
|
||||||
|
14
seq/lazy.go
14
seq/lazy.go
@ -50,6 +50,20 @@ func (l *Lazy) FirstRest() (types.Elem, Seq, bool) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Implementation of Equal for types.Elem interface. Treats a List as another
|
||||||
|
// Lazy. Completes in O(N) time if e is another List or List.
|
||||||
|
func (l *Lazy) Equal(e types.Elem) bool {
|
||||||
|
var ls2 *List
|
||||||
|
if l2, ok := e.(*Lazy); ok {
|
||||||
|
ls2 = ToList(l2)
|
||||||
|
} else if ls2, ok = e.(*List); ok {
|
||||||
|
} else {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
ls := ToList(l)
|
||||||
|
return ls.Equal(ls2)
|
||||||
|
}
|
||||||
|
|
||||||
// Implementation of String for Stringer
|
// Implementation of String for Stringer
|
||||||
func (l *Lazy) String() string {
|
func (l *Lazy) String() string {
|
||||||
return ToString(l, "<<", ">>")
|
return ToString(l, "<<", ">>")
|
||||||
|
@ -7,19 +7,26 @@ import (
|
|||||||
"github.com/mediocregopher/ginger/types"
|
"github.com/mediocregopher/ginger/types"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// Test that Lazy implements types.Elem (compile-time check)
|
||||||
|
func TestLazyElem(t *T) {
|
||||||
|
_ = types.Elem(NewLazy(nil))
|
||||||
|
}
|
||||||
|
|
||||||
// Test lazy operation and thread-safety
|
// Test lazy operation and thread-safety
|
||||||
func TestLazyBasic(t *T) {
|
func TestLazyBasic(t *T) {
|
||||||
ch := make(chan int)
|
ch := make(chan types.GoType)
|
||||||
mapfn := func(el types.Elem) types.Elem {
|
mapfn := func(el types.Elem) types.Elem {
|
||||||
i := el.(int)
|
i := el.(types.GoType)
|
||||||
ch <- i
|
ch <- i
|
||||||
return i
|
return i
|
||||||
}
|
}
|
||||||
|
|
||||||
intl := []types.Elem{0, 1, 2, 3, 4}
|
intl := elemSliceV(0, 1, 2, 3, 4)
|
||||||
l := NewList(intl...)
|
l := NewList(intl...)
|
||||||
ml := LMap(mapfn, l)
|
ml := LMap(mapfn, l)
|
||||||
|
|
||||||
|
// ml is a lazy list of intl, which will write to ch the first time any of
|
||||||
|
// the elements are read. This for loop ensures ml is thread-safe
|
||||||
for i := 0; i < 10; i++ {
|
for i := 0; i < 10; i++ {
|
||||||
go func() {
|
go func() {
|
||||||
mlintl := ToSlice(ml)
|
mlintl := ToSlice(ml)
|
||||||
@ -29,6 +36,8 @@ func TestLazyBasic(t *T) {
|
|||||||
}()
|
}()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// This loop and subsequent close ensure that ml only ever "creates" each
|
||||||
|
// element once
|
||||||
for _, el := range intl {
|
for _, el := range intl {
|
||||||
select {
|
select {
|
||||||
case elch := <-ch:
|
case elch := <-ch:
|
||||||
@ -42,7 +51,7 @@ func TestLazyBasic(t *T) {
|
|||||||
|
|
||||||
// Test that arbitrary Seqs can turn into Lazy
|
// Test that arbitrary Seqs can turn into Lazy
|
||||||
func TestToLazy(t *T) {
|
func TestToLazy(t *T) {
|
||||||
intl := []types.Elem{0, 1, 2, 3, 4}
|
intl := elemSliceV(0, 1, 2, 3, 4)
|
||||||
l := NewList(intl...)
|
l := NewList(intl...)
|
||||||
ll := ToLazy(l)
|
ll := ToLazy(l)
|
||||||
assertSeqContents(ll, intl, t)
|
assertSeqContents(ll, intl, t)
|
||||||
|
30
seq/list.go
30
seq/list.go
@ -39,6 +39,36 @@ func (l *List) FirstRest() (types.Elem, Seq, bool) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Implementation of Equal for types.Elem interface. Completes in O(N) time if e
|
||||||
|
// is another List.
|
||||||
|
func (l *List) Equal(e types.Elem) bool {
|
||||||
|
l2, ok := e.(*List)
|
||||||
|
if !ok {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
var el, el2 types.Elem
|
||||||
|
var ok2 bool
|
||||||
|
|
||||||
|
s, s2 := Seq(l), Seq(l2)
|
||||||
|
|
||||||
|
for {
|
||||||
|
el, s, ok = s.FirstRest()
|
||||||
|
el2, s2, ok2 = s2.FirstRest()
|
||||||
|
|
||||||
|
if !ok && !ok2 {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
if ok != ok2 {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
if !el.Equal(el2) {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Implementation of String for Stringer interface.
|
// Implementation of String for Stringer interface.
|
||||||
func (l *List) String() string {
|
func (l *List) String() string {
|
||||||
return ToString(l, "(", ")")
|
return ToString(l, "(", ")")
|
||||||
|
@ -6,6 +6,11 @@ import (
|
|||||||
"github.com/mediocregopher/ginger/types"
|
"github.com/mediocregopher/ginger/types"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// Test that List implements types.Elem (compile-time check)
|
||||||
|
func TestListElem(t *T) {
|
||||||
|
_ = types.Elem(NewList())
|
||||||
|
}
|
||||||
|
|
||||||
// Asserts that the given list is properly formed and has all of its size fields
|
// Asserts that the given list is properly formed and has all of its size fields
|
||||||
// filled in correctly
|
// filled in correctly
|
||||||
func assertSaneList(l *List, t *T) {
|
func assertSaneList(l *List, t *T) {
|
||||||
@ -22,7 +27,7 @@ func assertSaneList(l *List, t *T) {
|
|||||||
|
|
||||||
// Test creating a list and calling the Seq interface methods on it
|
// Test creating a list and calling the Seq interface methods on it
|
||||||
func TestListSeq(t *T) {
|
func TestListSeq(t *T) {
|
||||||
ints := []types.Elem{1, "a", 5.0}
|
ints := elemSliceV(1, "a", 5.0)
|
||||||
|
|
||||||
// Testing creation and Seq interface methods
|
// Testing creation and Seq interface methods
|
||||||
l := NewList(ints...)
|
l := NewList(ints...)
|
||||||
@ -40,39 +45,62 @@ func TestListSeq(t *T) {
|
|||||||
assertValue(emptyl, nilpointer, t)
|
assertValue(emptyl, nilpointer, t)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Test that the Equal method on Lists works
|
||||||
|
func TestListEqual(t *T) {
|
||||||
|
l, l2 := NewList(), NewList()
|
||||||
|
assertValue(l.Equal(l2), true, t)
|
||||||
|
assertValue(l2.Equal(l), true, t)
|
||||||
|
|
||||||
|
l2 = NewList(elemSliceV(1, 2, 3)...)
|
||||||
|
assertValue(l.Equal(l2), false, t)
|
||||||
|
assertValue(l2.Equal(l), false, t)
|
||||||
|
|
||||||
|
l = NewList(elemSliceV(1, 2, 3, 4)...)
|
||||||
|
assertValue(l.Equal(l2), false, t)
|
||||||
|
assertValue(l2.Equal(l), false, t)
|
||||||
|
|
||||||
|
l2 = NewList(elemSliceV(1, 2, 3, 4)...)
|
||||||
|
assertValue(l.Equal(l2), true, t)
|
||||||
|
assertValue(l2.Equal(l), true, t)
|
||||||
|
}
|
||||||
|
|
||||||
// Test the string representation of a List
|
// Test the string representation of a List
|
||||||
func TestStringSeq(t *T) {
|
func TestStringSeq(t *T) {
|
||||||
l := NewList(0, 1, 2, 3)
|
l := NewList(elemSliceV(0, 1, 2, 3)...)
|
||||||
assertValue(l.String(), "( 0 1 2 3 )", t)
|
assertValue(l.String(), "( 0 1 2 3 )", t)
|
||||||
|
|
||||||
l = NewList(0, 1, 2, NewList(3, 4), 5, NewList(6, 7, 8))
|
l = NewList(elemSliceV(
|
||||||
|
0, 1, 2,
|
||||||
|
NewList(elemSliceV(3, 4)...),
|
||||||
|
5,
|
||||||
|
NewList(elemSliceV(6, 7, 8)...))...)
|
||||||
assertValue(l.String(), "( 0 1 2 ( 3 4 ) 5 ( 6 7 8 ) )", t)
|
assertValue(l.String(), "( 0 1 2 ( 3 4 ) 5 ( 6 7 8 ) )", t)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Test prepending an element to the beginning of a list
|
// Test prepending an element to the beginning of a list
|
||||||
func TestPrepend(t *T) {
|
func TestPrepend(t *T) {
|
||||||
// Normal case
|
// Normal case
|
||||||
intl := []types.Elem{3, 2, 1, 0}
|
intl := elemSliceV(3, 2, 1, 0)
|
||||||
l := NewList(intl...)
|
l := NewList(intl...)
|
||||||
nl := l.Prepend(4)
|
nl := l.Prepend(types.GoType{4})
|
||||||
assertSaneList(l, t)
|
assertSaneList(l, t)
|
||||||
assertSaneList(nl, t)
|
assertSaneList(nl, t)
|
||||||
assertSeqContents(l, intl, t)
|
assertSeqContents(l, intl, t)
|
||||||
assertSeqContents(nl, []types.Elem{4, 3, 2, 1, 0}, t)
|
assertSeqContents(nl, elemSliceV(4, 3, 2, 1, 0), t)
|
||||||
|
|
||||||
// Degenerate case
|
// Degenerate case
|
||||||
l = NewList()
|
l = NewList()
|
||||||
nl = l.Prepend(0)
|
nl = l.Prepend(types.GoType{0})
|
||||||
assertEmpty(l, t)
|
assertEmpty(l, t)
|
||||||
assertSaneList(nl, t)
|
assertSaneList(nl, t)
|
||||||
assertSeqContents(nl, []types.Elem{0}, t)
|
assertSeqContents(nl, elemSliceV(0), t)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Test prepending a Seq to the beginning of a list
|
// Test prepending a Seq to the beginning of a list
|
||||||
func TestPrependSeq(t *T) {
|
func TestPrependSeq(t *T) {
|
||||||
//Normal case
|
//Normal case
|
||||||
intl1 := []types.Elem{3, 4}
|
intl1 := elemSliceV(3, 4)
|
||||||
intl2 := []types.Elem{0, 1, 2}
|
intl2 := elemSliceV(0, 1, 2)
|
||||||
l1 := NewList(intl1...)
|
l1 := NewList(intl1...)
|
||||||
l2 := NewList(intl2...)
|
l2 := NewList(intl2...)
|
||||||
nl := l1.PrependSeq(l2)
|
nl := l1.PrependSeq(l2)
|
||||||
@ -81,7 +109,7 @@ func TestPrependSeq(t *T) {
|
|||||||
assertSaneList(nl, t)
|
assertSaneList(nl, t)
|
||||||
assertSeqContents(l1, intl1, t)
|
assertSeqContents(l1, intl1, t)
|
||||||
assertSeqContents(l2, intl2, t)
|
assertSeqContents(l2, intl2, t)
|
||||||
assertSeqContents(nl, []types.Elem{0, 1, 2, 3, 4}, t)
|
assertSeqContents(nl, elemSliceV(0, 1, 2, 3, 4), t)
|
||||||
|
|
||||||
// Degenerate cases
|
// Degenerate cases
|
||||||
blank1 := NewList()
|
blank1 := NewList()
|
||||||
@ -105,34 +133,34 @@ func TestPrependSeq(t *T) {
|
|||||||
// Test appending to the end of a List
|
// Test appending to the end of a List
|
||||||
func TestAppend(t *T) {
|
func TestAppend(t *T) {
|
||||||
// Normal case
|
// Normal case
|
||||||
intl := []types.Elem{3, 2, 1}
|
intl := elemSliceV(3, 2, 1)
|
||||||
l := NewList(intl...)
|
l := NewList(intl...)
|
||||||
nl := l.Append(0)
|
nl := l.Append(types.GoType{0})
|
||||||
assertSaneList(l, t)
|
assertSaneList(l, t)
|
||||||
assertSaneList(nl, t)
|
assertSaneList(nl, t)
|
||||||
assertSeqContents(l, intl, t)
|
assertSeqContents(l, intl, t)
|
||||||
assertSeqContents(nl, []types.Elem{3, 2, 1, 0}, t)
|
assertSeqContents(nl, elemSliceV(3, 2, 1, 0), t)
|
||||||
|
|
||||||
// Edge case (algorithm gets weird here)
|
// Edge case (algorithm gets weird here)
|
||||||
l = NewList(1)
|
l = NewList(elemSliceV(1)...)
|
||||||
nl = l.Append(0)
|
nl = l.Append(types.GoType{0})
|
||||||
assertSaneList(l, t)
|
assertSaneList(l, t)
|
||||||
assertSaneList(nl, t)
|
assertSaneList(nl, t)
|
||||||
assertSeqContents(l, []types.Elem{1}, t)
|
assertSeqContents(l, elemSliceV(1), t)
|
||||||
assertSeqContents(nl, []types.Elem{1, 0}, t)
|
assertSeqContents(nl, elemSliceV(1, 0), t)
|
||||||
|
|
||||||
// Degenerate case
|
// Degenerate case
|
||||||
l = NewList()
|
l = NewList()
|
||||||
nl = l.Append(0)
|
nl = l.Append(types.GoType{0})
|
||||||
assertEmpty(l, t)
|
assertEmpty(l, t)
|
||||||
assertSaneList(nl, t)
|
assertSaneList(nl, t)
|
||||||
assertSeqContents(nl, []types.Elem{0}, t)
|
assertSeqContents(nl, elemSliceV(0), t)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Test retrieving items from a List
|
// Test retrieving items from a List
|
||||||
func TestNth(t *T) {
|
func TestNth(t *T) {
|
||||||
// Normal case, in bounds
|
// Normal case, in bounds
|
||||||
intl := []types.Elem{0, 2, 4, 6, 8}
|
intl := elemSliceV(0, 2, 4, 6, 8)
|
||||||
l := NewList(intl...)
|
l := NewList(intl...)
|
||||||
r, ok := l.Nth(3)
|
r, ok := l.Nth(3)
|
||||||
assertSaneList(l, t)
|
assertSaneList(l, t)
|
||||||
|
@ -50,11 +50,11 @@ func testSeqNoOrderGen(t *T, s Seq, ints []types.Elem) Seq {
|
|||||||
// Test reversing a Seq
|
// Test reversing a Seq
|
||||||
func TestReverse(t *T) {
|
func TestReverse(t *T) {
|
||||||
// Normal case
|
// Normal case
|
||||||
intl := []types.Elem{3, 2, 1}
|
intl := elemSliceV(3, 2, 1)
|
||||||
l := NewList(intl...)
|
l := NewList(intl...)
|
||||||
nl := Reverse(l)
|
nl := Reverse(l)
|
||||||
assertSeqContents(l, intl, t)
|
assertSeqContents(l, intl, t)
|
||||||
assertSeqContents(nl, []types.Elem{1, 2, 3}, t)
|
assertSeqContents(nl, elemSliceV(1, 2, 3), t)
|
||||||
|
|
||||||
// Degenerate case
|
// Degenerate case
|
||||||
l = NewList()
|
l = NewList()
|
||||||
@ -65,15 +65,15 @@ func TestReverse(t *T) {
|
|||||||
|
|
||||||
func testMapGen(t *T, mapFn func(func(types.Elem) types.Elem, Seq) Seq) {
|
func testMapGen(t *T, mapFn func(func(types.Elem) types.Elem, Seq) Seq) {
|
||||||
fn := func(n types.Elem) types.Elem {
|
fn := func(n types.Elem) types.Elem {
|
||||||
return n.(int) + 1
|
return types.GoType{n.(types.GoType).V.(int) + 1}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Normal case
|
// Normal case
|
||||||
intl := []types.Elem{1, 2, 3}
|
intl := elemSliceV(1, 2, 3)
|
||||||
l := NewList(intl...)
|
l := NewList(intl...)
|
||||||
nl := mapFn(fn, l)
|
nl := mapFn(fn, l)
|
||||||
assertSeqContents(l, intl, t)
|
assertSeqContents(l, intl, t)
|
||||||
assertSeqContents(nl, []types.Elem{2, 3, 4}, t)
|
assertSeqContents(nl, elemSliceV(2, 3, 4), t)
|
||||||
|
|
||||||
// Degenerate case
|
// Degenerate case
|
||||||
l = NewList()
|
l = NewList()
|
||||||
@ -95,27 +95,31 @@ func TestLMap(t *T) {
|
|||||||
// Test reducing over a Seq
|
// Test reducing over a Seq
|
||||||
func TestReduce(t *T) {
|
func TestReduce(t *T) {
|
||||||
fn := func(acc, el types.Elem) (types.Elem, bool) {
|
fn := func(acc, el types.Elem) (types.Elem, bool) {
|
||||||
return acc.(int) + el.(int), false
|
acci := acc.(types.GoType).V.(int)
|
||||||
|
eli := el.(types.GoType).V.(int)
|
||||||
|
return types.GoType{acci + eli}, false
|
||||||
}
|
}
|
||||||
|
|
||||||
// Normal case
|
// Normal case
|
||||||
intl := []types.Elem{1, 2, 3, 4}
|
intl := elemSliceV(1, 2, 3, 4)
|
||||||
l := NewList(intl...)
|
l := NewList(intl...)
|
||||||
r := Reduce(fn, 0, l)
|
r := Reduce(fn, types.GoType{0}, l)
|
||||||
assertSeqContents(l, intl, t)
|
assertSeqContents(l, intl, t)
|
||||||
assertValue(r, 10, t)
|
assertValue(r, 10, t)
|
||||||
|
|
||||||
// Short-circuit case
|
// Short-circuit case
|
||||||
fns := func(acc, el types.Elem) (types.Elem, bool) {
|
fns := func(acc, el types.Elem) (types.Elem, bool) {
|
||||||
return acc.(int) + el.(int), el.(int) > 2
|
acci := acc.(types.GoType).V.(int)
|
||||||
|
eli := el.(types.GoType).V.(int)
|
||||||
|
return types.GoType{acci + eli}, eli > 2
|
||||||
}
|
}
|
||||||
r = Reduce(fns, 0, l)
|
r = Reduce(fns, types.GoType{0}, l)
|
||||||
assertSeqContents(l, intl, t)
|
assertSeqContents(l, intl, t)
|
||||||
assertValue(r, 6, t)
|
assertValue(r, 6, t)
|
||||||
|
|
||||||
// Degenerate case
|
// Degenerate case
|
||||||
l = NewList()
|
l = NewList()
|
||||||
r = Reduce(fn, 0, l)
|
r = Reduce(fn, types.GoType{0}, l)
|
||||||
assertEmpty(l, t)
|
assertEmpty(l, t)
|
||||||
assertValue(r, 0, t)
|
assertValue(r, 0, t)
|
||||||
}
|
}
|
||||||
@ -123,11 +127,11 @@ func TestReduce(t *T) {
|
|||||||
// Test the Any function
|
// Test the Any function
|
||||||
func TestAny(t *T) {
|
func TestAny(t *T) {
|
||||||
fn := func(el types.Elem) bool {
|
fn := func(el types.Elem) bool {
|
||||||
return el.(int) > 3
|
return el.(types.GoType).V.(int) > 3
|
||||||
}
|
}
|
||||||
|
|
||||||
// Value found case
|
// Value found case
|
||||||
intl := []types.Elem{1, 2, 3, 4}
|
intl := elemSliceV(1, 2, 3, 4)
|
||||||
l := NewList(intl...)
|
l := NewList(intl...)
|
||||||
r, ok := Any(fn, l)
|
r, ok := Any(fn, l)
|
||||||
assertSeqContents(l, intl, t)
|
assertSeqContents(l, intl, t)
|
||||||
@ -135,7 +139,7 @@ func TestAny(t *T) {
|
|||||||
assertValue(ok, true, t)
|
assertValue(ok, true, t)
|
||||||
|
|
||||||
// Value not found case
|
// Value not found case
|
||||||
intl = []types.Elem{1, 2, 3}
|
intl = elemSliceV(1, 2, 3)
|
||||||
l = NewList(intl...)
|
l = NewList(intl...)
|
||||||
r, ok = Any(fn, l)
|
r, ok = Any(fn, l)
|
||||||
assertSeqContents(l, intl, t)
|
assertSeqContents(l, intl, t)
|
||||||
@ -153,18 +157,18 @@ func TestAny(t *T) {
|
|||||||
// Test the All function
|
// Test the All function
|
||||||
func TestAll(t *T) {
|
func TestAll(t *T) {
|
||||||
fn := func(el types.Elem) bool {
|
fn := func(el types.Elem) bool {
|
||||||
return el.(int) > 3
|
return el.(types.GoType).V.(int) > 3
|
||||||
}
|
}
|
||||||
|
|
||||||
// All match case
|
// All match case
|
||||||
intl := []types.Elem{4, 5, 6}
|
intl := elemSliceV(4, 5, 6)
|
||||||
l := NewList(intl...)
|
l := NewList(intl...)
|
||||||
ok := All(fn, l)
|
ok := All(fn, l)
|
||||||
assertSeqContents(l, intl, t)
|
assertSeqContents(l, intl, t)
|
||||||
assertValue(ok, true, t)
|
assertValue(ok, true, t)
|
||||||
|
|
||||||
// Not all match case
|
// Not all match case
|
||||||
intl = []types.Elem{3, 4, 2, 5}
|
intl = elemSliceV(3, 4, 2, 5)
|
||||||
l = NewList(intl...)
|
l = NewList(intl...)
|
||||||
ok = All(fn, l)
|
ok = All(fn, l)
|
||||||
assertSeqContents(l, intl, t)
|
assertSeqContents(l, intl, t)
|
||||||
@ -179,15 +183,15 @@ func TestAll(t *T) {
|
|||||||
|
|
||||||
func testFilterGen(t *T, filterFn func(func(types.Elem) bool, Seq) Seq) {
|
func testFilterGen(t *T, filterFn func(func(types.Elem) bool, Seq) Seq) {
|
||||||
fn := func(el types.Elem) bool {
|
fn := func(el types.Elem) bool {
|
||||||
return el.(int)%2 != 0
|
return el.(types.GoType).V.(int)%2 != 0
|
||||||
}
|
}
|
||||||
|
|
||||||
// Normal case
|
// Normal case
|
||||||
intl := []types.Elem{1, 2, 3, 4, 5}
|
intl := elemSliceV(1, 2, 3, 4, 5)
|
||||||
l := NewList(intl...)
|
l := NewList(intl...)
|
||||||
r := filterFn(fn, l)
|
r := filterFn(fn, l)
|
||||||
assertSeqContents(l, intl, t)
|
assertSeqContents(l, intl, t)
|
||||||
assertSeqContents(r, []types.Elem{1, 3, 5}, t)
|
assertSeqContents(r, elemSliceV(1, 3, 5), t)
|
||||||
|
|
||||||
// Degenerate cases
|
// Degenerate cases
|
||||||
l = NewList()
|
l = NewList()
|
||||||
@ -209,19 +213,19 @@ func TestLFilter(t *T) {
|
|||||||
// Test Flatten-ing of a Seq
|
// Test Flatten-ing of a Seq
|
||||||
func TestFlatten(t *T) {
|
func TestFlatten(t *T) {
|
||||||
// Normal case
|
// Normal case
|
||||||
intl1 := []types.Elem{0, 1, 2}
|
intl1 := elemSliceV(0, 1, 2)
|
||||||
intl2 := []types.Elem{3, 4, 5}
|
intl2 := elemSliceV(3, 4, 5)
|
||||||
l1 := NewList(intl1...)
|
l1 := NewList(intl1...)
|
||||||
l2 := NewList(intl2...)
|
l2 := NewList(intl2...)
|
||||||
blank := NewList()
|
blank := NewList()
|
||||||
intl := []types.Elem{-1, l1, l2, 6, blank, 7}
|
intl := elemSliceV(-1, l1, l2, 6, blank, 7)
|
||||||
l := NewList(intl...)
|
l := NewList(intl...)
|
||||||
nl := Flatten(l)
|
nl := Flatten(l)
|
||||||
assertSeqContents(l1, intl1, t)
|
assertSeqContents(l1, intl1, t)
|
||||||
assertSeqContents(l2, intl2, t)
|
assertSeqContents(l2, intl2, t)
|
||||||
assertEmpty(blank, t)
|
assertEmpty(blank, t)
|
||||||
assertSeqContents(l, intl, t)
|
assertSeqContents(l, intl, t)
|
||||||
assertSeqContents(nl, []types.Elem{-1, 0, 1, 2, 3, 4, 5, 6, 7}, t)
|
assertSeqContents(nl, elemSliceV(-1, 0, 1, 2, 3, 4, 5, 6, 7), t)
|
||||||
|
|
||||||
// Degenerate case
|
// Degenerate case
|
||||||
nl = Flatten(blank)
|
nl = Flatten(blank)
|
||||||
@ -231,11 +235,11 @@ func TestFlatten(t *T) {
|
|||||||
|
|
||||||
func testTakeGen(t *T, takeFn func(uint64, Seq) Seq) {
|
func testTakeGen(t *T, takeFn func(uint64, Seq) Seq) {
|
||||||
// Normal case
|
// Normal case
|
||||||
intl := []types.Elem{0, 1, 2, 3, 4}
|
intl := elemSliceV(0, 1, 2, 3, 4)
|
||||||
l := NewList(intl...)
|
l := NewList(intl...)
|
||||||
nl := takeFn(3, l)
|
nl := takeFn(3, l)
|
||||||
assertSeqContents(l, intl, t)
|
assertSeqContents(l, intl, t)
|
||||||
assertSeqContents(nl, []types.Elem{0, 1, 2}, t)
|
assertSeqContents(nl, elemSliceV(0, 1, 2), t)
|
||||||
|
|
||||||
// Edge cases
|
// Edge cases
|
||||||
nl = takeFn(5, l)
|
nl = takeFn(5, l)
|
||||||
@ -269,28 +273,28 @@ func TestLTake(t *T) {
|
|||||||
|
|
||||||
func testTakeWhileGen(t *T, takeWhileFn func(func(types.Elem) bool, Seq) Seq) {
|
func testTakeWhileGen(t *T, takeWhileFn func(func(types.Elem) bool, Seq) Seq) {
|
||||||
pred := func(el types.Elem) bool {
|
pred := func(el types.Elem) bool {
|
||||||
return el.(int) < 3
|
return el.(types.GoType).V.(int) < 3
|
||||||
}
|
}
|
||||||
|
|
||||||
// Normal case
|
// Normal case
|
||||||
intl := []types.Elem{0, 1, 2, 3, 4, 5}
|
intl := elemSliceV(0, 1, 2, 3, 4, 5)
|
||||||
l := NewList(intl...)
|
l := NewList(intl...)
|
||||||
nl := takeWhileFn(pred, l)
|
nl := takeWhileFn(pred, l)
|
||||||
assertSeqContents(l, intl, t)
|
assertSeqContents(l, intl, t)
|
||||||
assertSeqContents(nl, []types.Elem{0, 1, 2}, t)
|
assertSeqContents(nl, elemSliceV(0, 1, 2), t)
|
||||||
|
|
||||||
// Edge cases
|
// Edge cases
|
||||||
intl = []types.Elem{5, 5, 5}
|
intl = elemSliceV(5, 5, 5)
|
||||||
l = NewList(intl...)
|
l = NewList(intl...)
|
||||||
nl = takeWhileFn(pred, l)
|
nl = takeWhileFn(pred, l)
|
||||||
assertSeqContents(l, intl, t)
|
assertSeqContents(l, intl, t)
|
||||||
assertEmpty(nl, t)
|
assertEmpty(nl, t)
|
||||||
|
|
||||||
intl = []types.Elem{0, 1, 2}
|
intl = elemSliceV(0, 1, 2)
|
||||||
l = NewList(intl...)
|
l = NewList(intl...)
|
||||||
nl = takeWhileFn(pred, l)
|
nl = takeWhileFn(pred, l)
|
||||||
assertSeqContents(l, intl, t)
|
assertSeqContents(l, intl, t)
|
||||||
assertSeqContents(nl, []types.Elem{0, 1, 2}, t)
|
assertSeqContents(nl, elemSliceV(0, 1, 2), t)
|
||||||
|
|
||||||
// Degenerate case
|
// Degenerate case
|
||||||
l = NewList()
|
l = NewList()
|
||||||
@ -312,11 +316,11 @@ func TestLTakeWhile(t *T) {
|
|||||||
// Test dropping from a Seq
|
// Test dropping from a Seq
|
||||||
func TestDrop(t *T) {
|
func TestDrop(t *T) {
|
||||||
// Normal case
|
// Normal case
|
||||||
intl := []types.Elem{0, 1, 2, 3, 4}
|
intl := elemSliceV(0, 1, 2, 3, 4)
|
||||||
l := NewList(intl...)
|
l := NewList(intl...)
|
||||||
nl := Drop(3, l)
|
nl := Drop(3, l)
|
||||||
assertSeqContents(l, intl, t)
|
assertSeqContents(l, intl, t)
|
||||||
assertSeqContents(nl, []types.Elem{3, 4}, t)
|
assertSeqContents(nl, elemSliceV(3, 4), t)
|
||||||
|
|
||||||
// Edge cases
|
// Edge cases
|
||||||
nl = Drop(5, l)
|
nl = Drop(5, l)
|
||||||
@ -341,24 +345,24 @@ func TestDrop(t *T) {
|
|||||||
// Test dropping from a Seq until a given condition
|
// Test dropping from a Seq until a given condition
|
||||||
func TestDropWhile(t *T) {
|
func TestDropWhile(t *T) {
|
||||||
pred := func(el types.Elem) bool {
|
pred := func(el types.Elem) bool {
|
||||||
return el.(int) < 3
|
return el.(types.GoType).V.(int) < 3
|
||||||
}
|
}
|
||||||
|
|
||||||
// Normal case
|
// Normal case
|
||||||
intl := []types.Elem{0, 1, 2, 3, 4, 5}
|
intl := elemSliceV(0, 1, 2, 3, 4, 5)
|
||||||
l := NewList(intl...)
|
l := NewList(intl...)
|
||||||
nl := DropWhile(pred, l)
|
nl := DropWhile(pred, l)
|
||||||
assertSeqContents(l, intl, t)
|
assertSeqContents(l, intl, t)
|
||||||
assertSeqContents(nl, []types.Elem{3, 4, 5}, t)
|
assertSeqContents(nl, elemSliceV(3, 4, 5), t)
|
||||||
|
|
||||||
// Edge cases
|
// Edge cases
|
||||||
intl = []types.Elem{5, 5, 5}
|
intl = elemSliceV(5, 5, 5)
|
||||||
l = NewList(intl...)
|
l = NewList(intl...)
|
||||||
nl = DropWhile(pred, l)
|
nl = DropWhile(pred, l)
|
||||||
assertSeqContents(l, intl, t)
|
assertSeqContents(l, intl, t)
|
||||||
assertSeqContents(nl, intl, t)
|
assertSeqContents(nl, intl, t)
|
||||||
|
|
||||||
intl = []types.Elem{0, 1, 2}
|
intl = elemSliceV(0, 1, 2)
|
||||||
l = NewList(intl...)
|
l = NewList(intl...)
|
||||||
nl = DropWhile(pred, l)
|
nl = DropWhile(pred, l)
|
||||||
assertSeqContents(l, intl, t)
|
assertSeqContents(l, intl, t)
|
||||||
|
29
seq/util.go
29
seq/util.go
@ -19,6 +19,18 @@ func intSlicesEq(a, b []types.Elem) bool {
|
|||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func elemSliceV(a ...interface{}) []types.Elem {
|
||||||
|
ret := make([]types.Elem, 0, len(a))
|
||||||
|
for i := range a {
|
||||||
|
if e, ok := a[i].(types.Elem); ok {
|
||||||
|
ret = append(ret, e)
|
||||||
|
} else {
|
||||||
|
ret = append(ret, types.GoType{a[i]})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return ret
|
||||||
|
}
|
||||||
|
|
||||||
// Asserts that the given Seq is empty (contains no elements)
|
// Asserts that the given Seq is empty (contains no elements)
|
||||||
func assertEmpty(s Seq, t *testing.T) {
|
func assertEmpty(s Seq, t *testing.T) {
|
||||||
if Size(s) != 0 {
|
if Size(s) != 0 {
|
||||||
@ -59,7 +71,7 @@ func assertSeqContentsSet(s Seq, ints []types.Elem, t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func assertSeqContentsHashMap(s Seq, kvs []*KV, t *testing.T) {
|
func assertSeqContentsHashMap(s Seq, kvs []*KV, t *testing.T) {
|
||||||
m := map[types.Elem]bool{}
|
m := map[KV]bool{}
|
||||||
for i := range kvs {
|
for i := range kvs {
|
||||||
m[*kvs[i]] = true
|
m[*kvs[i]] = true
|
||||||
}
|
}
|
||||||
@ -76,9 +88,16 @@ func assertSeqContentsHashMap(s Seq, kvs []*KV, t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Asserts that v1 is the same as v2
|
// Asserts that v1 is the same as v2
|
||||||
func assertValue(v1, v2 types.Elem, t *testing.T) {
|
func assertValue(v1, v2 interface{}, t *testing.T) {
|
||||||
|
if gv1, ok := v1.(types.GoType); ok {
|
||||||
|
v1 = gv1.V
|
||||||
|
}
|
||||||
|
if gv2, ok := v2.(types.GoType); ok {
|
||||||
|
v2 = gv2.V
|
||||||
|
}
|
||||||
if v1 != v2 {
|
if v1 != v2 {
|
||||||
t.Fatalf("Value wrong: %v not %v", v1, v2)
|
t.Logf("Value wrong: %v not %v", v1, v2)
|
||||||
|
panic("bail")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -88,3 +107,7 @@ func assertInMap(v1 types.Elem, m map[types.Elem]bool, t *testing.T) {
|
|||||||
t.Fatalf("Value not in set: %v not in %v", v1, m)
|
t.Fatalf("Value not in set: %v not in %v", v1, m)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func keyValV(k, v interface{}) *KV {
|
||||||
|
return KeyVal(types.GoType{k}, types.GoType{v})
|
||||||
|
}
|
||||||
|
@ -1,11 +1,38 @@
|
|||||||
// This package describes ginger's base types and the interfaces covered by them
|
// This package describes ginger's base types and the interfaces covered by them
|
||||||
package types
|
package types
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
)
|
||||||
|
|
||||||
// Elem is a generic type which can be used as a wrapper type for all ginger
|
// Elem is a generic type which can be used as a wrapper type for all ginger
|
||||||
// types, both base types and data structures
|
// types, both base types and data structures
|
||||||
type Elem interface {
|
type Elem interface {
|
||||||
|
|
||||||
|
// Returns whether one element is equal to another. Since all ginger values
|
||||||
|
// are immutable, this must be a deep-equals check.
|
||||||
|
Equal(Elem) bool
|
||||||
}
|
}
|
||||||
|
|
||||||
// Number can either be either an Int or a Float
|
// Number can either be either an Int or a Float
|
||||||
type Number interface {
|
type Number interface {
|
||||||
|
Elem
|
||||||
|
}
|
||||||
|
|
||||||
|
// Wraps a go type like int, string, or []byte. GoType is a struct whose only
|
||||||
|
// field is an interface{}, so using a pointer to is not necessary. Just pass
|
||||||
|
// around the value type.
|
||||||
|
type GoType struct {
|
||||||
|
V interface{}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (g GoType) Equal(e Elem) bool {
|
||||||
|
if g2, ok := e.(GoType); ok {
|
||||||
|
return g.V == g2.V
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
func (g GoType) String() string {
|
||||||
|
return fmt.Sprintf("%v", g.V)
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user