diff --git a/.idea/.gitignore b/.idea/.gitignore new file mode 100644 index 0000000..73f69e0 --- /dev/null +++ b/.idea/.gitignore @@ -0,0 +1,8 @@ +# Default ignored files +/shelf/ +/workspace.xml +# Datasource local storage ignored files +/dataSources/ +/dataSources.local.xml +# Editor-based HTTP Client requests +/httpRequests/ diff --git a/functional.go b/functional.go new file mode 100644 index 0000000..52e9eec --- /dev/null +++ b/functional.go @@ -0,0 +1,219 @@ +package FunctionalLib + +import Seq "github.com/geniussportsgroup/Slist" + +type SequentialIterator interface { + ResetFirst() interface{} + HasCurr() bool + GetCurr() interface{} + Next() interface{} +} + +type Sequence interface { + Traverse(func(interface{}) bool) bool + Append(item interface{}, items ...interface{}) interface{} + Size() int + Swap(other interface{}) interface{} + IsEmpty() bool + CreateIterator() interface{} +} + +// Execute operation receiving every item of the sequence. Return seq +func ForEach(seq Sequence, operation func(interface{})) interface{} { + + seq.Traverse(func(i interface{}) bool { + + operation(i) + return true + }) + + return seq +} + +// Return true if all the elements of the sequence meets predicate +func All(seq Sequence, predicate func(interface{}) bool) bool { + + return seq.Traverse(predicate) +} + +func Exist(seq Sequence, predicate func(interface{}) bool) bool { + + return !All(seq, func(i interface{}) bool { + return !predicate(i) + }) +} + +// Return a new seq with the items of sequence transformed with transformation operation +func Map(seq Sequence, transformation func(interface{}) interface{}) *Seq.Slist { + + ret := Seq.New() + ForEach(seq, func(item interface{}) { + ret.Append(transformation(item)) + }) + return ret +} + +// Return a new seq with the items of sequence transformed with transformation operation for the items +// satisfying the predicate +func MapIf(seq Sequence, + transformation func(interface{}) interface{}, + predicate func(interface{}) bool) *Seq.Slist { + + ret := Seq.New() + ForEach(seq, func(item interface{}) { + if predicate(item) { + ret.Append(transformation(item)) + } + }) + return ret +} + +// Return a list containing the items satisfying predicate +func Filter(seq Sequence, predicate func(interface{}) bool) *Seq.Slist { + + ret := Seq.New() + ForEach(seq, func(item interface{}) { + if predicate(item) { + ret.Append(item) + } + }) + return ret +} + +type Pair struct { + item1, item2 interface{} +} + +// Zip two lists into one list of pair. The result is truncated to the shortest list +func Zip(s1, s2 Sequence) *Seq.Slist { + + ret := Seq.New() + + it1, it2 := s1.CreateIterator().(SequentialIterator), s2.CreateIterator().(SequentialIterator) + for it1.HasCurr() && it2.HasCurr() { + + ret.Append(Pair{ + item1: it1.GetCurr(), + item2: it2.GetCurr(), + }) + + it1.Next() + it2.Next() + } + + return ret +} + +// Unzip a list of pairs into two separated lists +func Unzip(seq *Seq.Slist) (*Seq.Slist, *Seq.Slist) { + + l1 := Seq.New() + l2 := Seq.New() + + for it := seq.CreateIterator().(*Seq.Iterator); it.HasCurr(); it.Next() { + + curr := it.GetCurr().(Pair) + l1.Append(curr.item1) + l2.Append(curr.item2) + } + + return l1, l2 +} + +// Split seq into two lists. First contains items satisfying predicate and the second the complement +func Split(seq Sequence, predicate func(item interface{}) bool) (*Seq.Slist, *Seq.Slist) { + + l1 := Seq.New() + l2 := Seq.New() + + ForEach(seq, func(i interface{}) { + if predicate(i) { + l1.Append(i) + } else { + l2.Append(i) + } + }) + + return l1, l2 +} + +// Return the first item in seq satisfying predicate. If not item is found, the it returns nil +func Find(seq Sequence, predicate func(item interface{}) bool) interface{} { + + for it := seq.CreateIterator().(SequentialIterator); it.HasCurr(); it.Next() { + item := it.GetCurr() + if predicate(item) { + return item + } + } + return nil +} + +// Return a sequence containing the first n items from the sequence +func Take(seq Sequence, n int) *Seq.Slist { + + ret := Seq.New() + for it := seq.CreateIterator().(SequentialIterator); it.HasCurr() && n > 0; it.Next() { + ret.Append(it.GetCurr()) + n-- + } + + return ret +} + +// Return a sequence containing the items after the first n from the sequence +func Drop(seq Sequence, n int) *Seq.Slist { + + ret := Seq.New() + for i, it := 0, seq.CreateIterator().(SequentialIterator); it.HasCurr(); it.Next() { + if i < n { + i++ + continue + } + ret.Append(it.GetCurr()) + } + + return ret +} + +// Return f(in, ..., f(i2, f(i1, initVal) ... )) +func Foldl(seq Sequence, initVal interface{}, + f func(acu, item interface{}) interface{}) interface{} { + + retVal := initVal + ForEach(seq, func(i interface{}) { + retVal = f(retVal, i) + }) + return retVal +} + +// Return the n-th item in the sequence. Return nil if n is negative o greater than seq.Size() +func Nth(seq Sequence, n int) interface{} { + + if n < 0 || n >= seq.Size() { + return nil + } + + for it := seq.CreateIterator().(SequentialIterator); it.HasCurr(); it.Next() { + if n == 0 { + return it.GetCurr() + } + n-- + } + + return nil // it should not be reached! +} + +// Return the position in the sequence of the first element satisfying predicate. If no element satisfies +// predicate then it returns -1 +func Position(seq Sequence, predicate func(item interface{}) bool) int { + + for pos, it := 0, seq.CreateIterator().(SequentialIterator); it.HasCurr(); it.Next() { + if predicate(it.GetCurr()) { + return pos + } + pos++ + } + + return -1 +} diff --git a/functional_test.go b/functional_test.go new file mode 100644 index 0000000..f076fb8 --- /dev/null +++ b/functional_test.go @@ -0,0 +1,231 @@ +package FunctionalLib + +import ( + Seq "github.com/geniussportsgroup/Slist" + Set "github.com/geniussportsgroup/treaps" + "github.com/stretchr/testify/assert" + "testing" +) + +func cmpInt(i1, i2 interface{}) bool { + item1, ok := i1.(int) + if !ok { + panic("First parameter is not int") + } + item2, ok := i2.(int) + if !ok { + panic("Second parameter is not int") + } + return item1 < item2 +} + +const N = 100 + +func createSet() *Set.Treap { + + tree := Set.New(3, cmpInt) + for i := 0; i < N; i++ { + tree.Insert(i) + } + + return tree +} + +func TestForEach(t *testing.T) { + + tree := createSet() + + i := 0 + ForEach(tree, func(k interface{}) { + assert.Equal(t, k.(int), i) + i++ + }) +} + +func TestAll(t *testing.T) { + + tree := createSet() + + assert.True(t, All(tree, func(i interface{}) bool { + return i.(int) < N + })) + + assert.False(t, All(tree, func(i interface{}) bool { + return i.(int) >= N + })) +} + +func TestExist(t *testing.T) { + + tree := createSet() + + assert.True(t, Exist(tree, func(i interface{}) bool { + return i.(int) <= N-1 + })) + + assert.False(t, Exist(tree, func(i interface{}) bool { + return i.(int) >= N + })) +} + +func TestMap(t *testing.T) { + + tree := createSet() + + m := Map(tree, func(i interface{}) interface{} { + return 2 * i.(int) + }) + + assert.True(t, All(Zip(tree, m), func(i interface{}) bool { + p := i.(Pair) + return 2*p.item1.(int) == p.item2.(int) + })) +} + +func TestZipUnzip(t *testing.T) { + + l1 := createSet() + l2 := createSet() + + lzip := Zip(l1, l2) + assert.Equal(t, lzip.Size(), l1.Size()) + assert.Equal(t, lzip.Size(), l2.Size()) + + assert.True(t, All(lzip, func(i interface{}) bool { + p := i.(Pair) + return p.item1 == p.item2 + })) + + r1, r2 := Unzip(lzip) + assert.Equal(t, r1.Size(), r2.Size()) + assert.Equal(t, r1.Size(), l1.Size()) + for it1, it2 := r1.CreateIterator().(*Seq.Iterator), r2.CreateIterator().(*Seq.Iterator); it1.HasCurr() && it2.HasCurr(); /* nothing */ { + assert.Equal(t, it1.GetCurr().(int), it2.GetCurr().(int)) + + it1.Next() + it2.Next() + } +} + +func TestFilter(t *testing.T) { + + l := createSet() + + l50 := Filter(l, func(i interface{}) bool { + return i.(int) < N/2 + }) + + assert.True(t, All(l50, func(i interface{}) bool { + return i.(int) < N/2 + })) + + assert.False(t, Exist(l50, func(i interface{}) bool { + return i.(int) >= N/2 + })) +} + +func TestMapIf(t *testing.T) { + + pred := func(item interface{}) bool { + return item.(int) >= 20 && item.(int) <= 60 + } + + tree := createSet() + + lmap := MapIf(tree, func(i interface{}) interface{} { + return 2 * i.(int) + }, pred) + + assert.True(t, All(Zip(lmap, Filter(tree, pred)), func(i interface{}) bool { + p := i.(Pair) + return p.item1.(int) == 2*p.item2.(int) + })) +} + +func TestSplit(t *testing.T) { + + l := createSet() + + l1, l2 := Split(l, func(item interface{}) bool { + return item.(int) < N/2 + }) + + assert.True(t, All(l1, func(i interface{}) bool { + return i.(int) < N/2 + })) + + assert.True(t, All(l2, func(i interface{}) bool { + return i.(int) >= N/2 + })) +} + +func TestFind(t *testing.T) { + + tree := createSet() + iN2 := Find(tree, func(item interface{}) bool { + return item.(int) == N/2 + }) + + assert.Equal(t, iN2.(int), N/2) + + assert.Nil(t, Find(tree, func(item interface{}) bool { + return item.(int) >= N + })) +} + +func TestTake(t *testing.T) { + + tree := createSet() + l10 := Take(tree, 10) + assert.Equal(t, l10.Size(), 10) + assert.True(t, All(l10, func(i interface{}) bool { + return i.(int) <= 9 + })) +} + +func TestDrop(t *testing.T) { + + tree := createSet() + l90 := Drop(tree, 10) + assert.Equal(t, l90.Size(), N-10) + assert.True(t, All(l90, func(i interface{}) bool { + return i.(int) > 9 + })) +} + +func TestFoldl(t *testing.T) { + + assert.Equal(t, Foldl(createSet(), 0, func(acu, item interface{}) interface{} { + return acu.(int) + item.(int) + }).(int), N*(N-1)/2) +} + +func TestNth(t *testing.T) { + + tree := createSet() + + assert.Nil(t, Nth(tree, -1)) + assert.Nil(t, Nth(tree, N)) + for i := 0; i < tree.Size(); i++ { + assert.Equal(t, Nth(tree, i), i) + } +} + +func TestPosition(t *testing.T) { + + tree := createSet() + + assert.Equal(t, Position(tree, func(item interface{}) bool { + return item.(int) == -1 + }), -1) + + assert.Equal(t, Position(tree, func(item interface{}) bool { + return item.(int) == N + }), -1) + + for i := 0; i < tree.Size(); i++ { + assert.Equal(t, Position(tree, func(item interface{}) bool { + return item.(int) == i + }), i) + } +} diff --git a/go.mod b/go.mod new file mode 100644 index 0000000..547c050 --- /dev/null +++ b/go.mod @@ -0,0 +1,9 @@ +module github.com/geniussportsgroup/FunctionalLib + +go 1.15 + +require ( + github.com/geniussportsgroup/Slist v1.0.4 + github.com/geniussportsgroup/treaps v1.1.8 + github.com/stretchr/testify v1.7.0 +)