Skip to content

Commit

Permalink
feat(iter,slices,tuple): use tuple
Browse files Browse the repository at this point in the history
  • Loading branch information
leaxoy committed Jan 3, 2024
1 parent 99aab3b commit 2b30c5e
Show file tree
Hide file tree
Showing 8 changed files with 107 additions and 116 deletions.
73 changes: 37 additions & 36 deletions iter/collector/collector.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import (
"github.com/go-board/std/cmp"
"github.com/go-board/std/collections/ordered"
"github.com/go-board/std/iter"
"github.com/go-board/std/tuple"
)

func sliceSeq[E any, S ~[]E](slice S) iter.Seq[E] {
Expand Down Expand Up @@ -109,18 +110,18 @@ func ToOrderedSet[E any, V cmp.Ordered](f func(E) V) Collector[E, *ordered.Set[V
}

// GroupBy collects all elements in [iter.Seq] and group by key using given function.
func GroupBy[E any, K comparable](f func(E) K) Collector[E, iter.Seq[iter.Tuple[K, iter.Seq[E]]]] {
func GroupBy[E any, K comparable](f func(E) K) Collector[E, iter.Seq[tuple.Pair[K, iter.Seq[E]]]] {
return newCollectorImpl(
make(map[K][]E),
func(state map[K][]E, s iter.Seq[E]) map[K][]E {
iter.ForEach(s, func(e E) { k := f(e); state[k] = append(state[k], e) })
return state
},
func(state map[K][]E, x E) map[K][]E { k := f(x); state[k] = append(state[k], x); return state },
func(state map[K][]E) iter.Seq[iter.Tuple[K, iter.Seq[E]]] {
return func(yield func(iter.Tuple[K, iter.Seq[E]]) bool) {
func(state map[K][]E) iter.Seq[tuple.Pair[K, iter.Seq[E]]] {
return func(yield func(tuple.Pair[K, iter.Seq[E]]) bool) {
for k, rs := range state {
if !yield(iter.MakeTuple(k, sliceSeq(rs))) {
if !yield(tuple.MakePair(k, sliceSeq(rs))) {
break
}
}
Expand All @@ -133,25 +134,25 @@ func GroupBy[E any, K comparable](f func(E) K) Collector[E, iter.Seq[iter.Tuple[
// returns a new [iter.Seq] which elements yield in visit order.
func Distinct[E comparable]() Collector[E, iter.Seq[E]] {
return newCollectorImpl(
iter.MakeTuple(make(map[E]struct{}), make([]E, 0)),
func(state iter.Tuple[map[E]struct{}, []E], s iter.Seq[E]) iter.Tuple[map[E]struct{}, []E] {
tuple.MakePair(make(map[E]struct{}), make([]E, 0)),
func(state tuple.Pair[map[E]struct{}, []E], s iter.Seq[E]) tuple.Pair[map[E]struct{}, []E] {
iter.ForEach(s, func(e E) {
if _, ok := state.Left[e]; !ok {
state.Left[e] = struct{}{}
state.Right = append(state.Right, e)
if _, ok := state.First[e]; !ok {
state.First[e] = struct{}{}
state.Second = append(state.Second, e)
}
})
return state
},
func(state iter.Tuple[map[E]struct{}, []E], x E) iter.Tuple[map[E]struct{}, []E] {
if _, ok := state.Left[x]; !ok {
state.Left[x] = struct{}{}
state.Right = append(state.Right, x)
func(state tuple.Pair[map[E]struct{}, []E], x E) tuple.Pair[map[E]struct{}, []E] {
if _, ok := state.First[x]; !ok {
state.First[x] = struct{}{}
state.Second = append(state.Second, x)
}
return state
},
func(state iter.Tuple[map[E]struct{}, []E]) iter.Seq[E] {
return sliceSeq(state.Right)
func(state tuple.Pair[map[E]struct{}, []E]) iter.Seq[E] {
return sliceSeq(state.Second)
},
)
}
Expand All @@ -160,25 +161,25 @@ func Distinct[E comparable]() Collector[E, iter.Seq[E]] {
// returns a new [iter.Seq] which elements yield in visit order.
func DistinctFunc[E any](f func(E, E) int) Collector[E, iter.Seq[E]] {
return newCollectorImpl(
iter.MakeTuple(ordered.NewSet(f), make([]E, 0)),
func(state iter.Tuple[*ordered.Set[E], []E], s iter.Seq[E]) iter.Tuple[*ordered.Set[E], []E] {
tuple.MakePair(ordered.NewSet(f), make([]E, 0)),
func(state tuple.Pair[*ordered.Set[E], []E], s iter.Seq[E]) tuple.Pair[*ordered.Set[E], []E] {
iter.ForEach(s, func(e E) {
if !state.Left.Contains(e) {
state.Left.Insert(e)
state.Right = append(state.Right, e)
if !state.First.Contains(e) {
state.First.Insert(e)
state.Second = append(state.Second, e)
}
})
return state
},
func(state iter.Tuple[*ordered.Set[E], []E], x E) iter.Tuple[*ordered.Set[E], []E] {
if !state.Left.Contains(x) {
state.Left.Insert(x)
state.Right = append(state.Right, x)
func(state tuple.Pair[*ordered.Set[E], []E], x E) tuple.Pair[*ordered.Set[E], []E] {
if !state.First.Contains(x) {
state.First.Insert(x)
state.Second = append(state.Second, x)
}
return state
},
func(state iter.Tuple[*ordered.Set[E], []E]) iter.Seq[E] {
return sliceSeq(state.Right)
func(state tuple.Pair[*ordered.Set[E], []E]) iter.Seq[E] {
return sliceSeq(state.Second)
},
)
}
Expand All @@ -187,29 +188,29 @@ func DistinctFunc[E any](f func(E, E) int) Collector[E, iter.Seq[E]] {
//
// The first [iter.Seq] contains elements that satisfies the predicate.
// The second [iter.Seq]
func Partition[E any](f func(E) bool) Collector[E, iter.Tuple[iter.Seq[E], iter.Seq[E]]] {
func Partition[E any](f func(E) bool) Collector[E, tuple.Pair[iter.Seq[E], iter.Seq[E]]] {
return newCollectorImpl(
iter.MakeTuple(make([]E, 0), make([]E, 0)),
func(state iter.Tuple[[]E, []E], s iter.Seq[E]) iter.Tuple[[]E, []E] {
tuple.MakePair(make([]E, 0), make([]E, 0)),
func(state tuple.Pair[[]E, []E], s iter.Seq[E]) tuple.Pair[[]E, []E] {
iter.ForEach(s, func(e E) {
if f(e) {
state.Left = append(state.Left, e)
state.First = append(state.First, e)
} else {
state.Right = append(state.Right, e)
state.Second = append(state.Second, e)
}
})
return state
},
func(state iter.Tuple[[]E, []E], x E) iter.Tuple[[]E, []E] {
func(state tuple.Pair[[]E, []E], x E) tuple.Pair[[]E, []E] {
if f(x) {
state.Left = append(state.Left, x)
state.First = append(state.First, x)
} else {
state.Right = append(state.Right, x)
state.Second = append(state.Second, x)
}
return state
},
func(state iter.Tuple[[]E, []E]) iter.Tuple[iter.Seq[E], iter.Seq[E]] {
return iter.MakeTuple(sliceSeq(state.Left), sliceSeq(state.Right))
func(state tuple.Pair[[]E, []E]) tuple.Pair[iter.Seq[E], iter.Seq[E]] {
return tuple.MakePair(sliceSeq(state.First), sliceSeq(state.Second))
},
)
}
12 changes: 8 additions & 4 deletions iter/iter_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import (
qt "github.com/frankban/quicktest"
"github.com/go-board/std/cmp"
"github.com/go-board/std/iter"
"github.com/go-board/std/tuple"
)

func seq[E any](slice ...E) iter.Seq[E] {
Expand All @@ -28,7 +29,12 @@ func collect[E any](s iter.Seq[E]) []E {

func TestEnumerate(t *testing.T) {
x := iter.Enumerate(seq(1, 2, 3))
qt.Assert(t, collect(x), qt.DeepEquals, []iter.Tuple[int, int]{{0, 1}, {1, 2}, {2, 3}})
qt.Assert(t, collect(x), qt.DeepEquals, []tuple.Pair[int, int]{{0, 1}, {1, 2}, {2, 3}})
}

func TestAppend(t *testing.T) {
x := iter.Append(seq(1), 2, 3)
qt.Assert(t, collect(x), qt.DeepEquals, []int{1, 2, 3})
}

func TestTryForEach(t *testing.T) {
Expand All @@ -39,9 +45,7 @@ func TestTryForEach(t *testing.T) {
return errors.New("err: elem must less than 2")
})
qt.Assert(t, err, qt.ErrorMatches, "err: elem must less than 2")
err2 := iter.TryForEach(seq(1, 2, 3), func(i int) error {
return nil
})
err2 := iter.TryForEach(seq(1, 2, 3), func(i int) error { return nil })
qt.Assert(t, err2, qt.IsNil)
}

Expand Down
14 changes: 4 additions & 10 deletions iter/ops.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,24 +2,18 @@ package iter

import (
"github.com/go-board/std/cmp"
"github.com/go-board/std/tuple"
)

type Tuple[A, B any] struct {
Left A
Right B
}

func MakeTuple[A, B any](a A, b B) Tuple[A, B] { return Tuple[A, B]{Left: a, Right: b} }

// Enumerate create a new seq which yield (index, item) pair.
//
// Example:
//
// iter.Enumerate(seq(1,2,3)) => seq: (0, 1), (1, 2), (2, 3)
func Enumerate[E any](s Seq[E]) Seq[Tuple[int, E]] {
func Enumerate[E any](s Seq[E]) Seq[tuple.Pair[int, E]] {
i := -1
return func(yield func(Tuple[int, E]) bool) {
s(func(e E) bool { i++; return yield(MakeTuple(i, e)) })
return func(yield func(tuple.Pair[int, E]) bool) {
s(func(e E) bool { i++; return yield(tuple.MakePair(i, e)) })
}
}

Expand Down
5 changes: 4 additions & 1 deletion iter/rangefunc_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -41,5 +41,8 @@ func TestEqual(t *testing.T) {
ok := iter.Eq(seq(1), seq(1))
qt.Assert(t, ok, qt.IsTrue)
})
t.Run()
t.Run("ne", func(t *testing.T) {
ok := iter.Ne(seq(1), seq(2))
qt.Assert(t, ok, qt.IsTrue)
})
}
49 changes: 2 additions & 47 deletions iter/source/source.go
Original file line number Diff line number Diff line change
@@ -1,8 +1,6 @@
package source

import (
"net"

"github.com/go-board/std/core"
"github.com/go-board/std/iter"
)
Expand Down Expand Up @@ -38,18 +36,13 @@ func Gen[N core.Integer](from N, to N) iter.Seq[N] {

// Once creates an [iter.Seq] that yields an element exactly once.
func Once[E any](v E) iter.Seq[E] {
return func(yield func(E) bool) {
yield(v)
}
return func(yield func(E) bool) { yield(v) }
}

// Repeat creates an [iter.Seq] that endlessly repeats a single element.
func Repeat[E any](v E) iter.Seq[E] {
return func(yield func(E) bool) {
for {
if !yield(v) {
break
}
for yield(v) {
}
}
}
Expand All @@ -63,41 +56,3 @@ func RepeatTimes[E any, N core.Integer](v E, n N) iter.Seq[E] {
func Empty[E any]() iter.Seq[E] {
return func(yield func(E) bool) {}
}

// Chan creates an [iter.Seq] from channel.
func Chan[E any, C <-chan E](c C) iter.Seq[E] {
return func(yield func(E) bool) {
for x := range c {
if !yield(x) {
break
}
}
}
}

func Tcp(l net.TCPListener) iter.Seq[iter.Tuple[net.Conn, error]] {
return func(yield func(iter.Tuple[net.Conn, error]) bool) {
for yield(iter.MakeTuple(l.Accept())) {
}
}
}

func Runes(s string) iter.Seq[rune] {
return func(yield func(rune) bool) {
for _, x := range s {
if !yield(x) {
break
}
}
}
}

func Bytes(s string) iter.Seq[byte] {
return func(yield func(byte) bool) {
for _, x := range []byte(s) {
if !yield(x) {
break
}
}
}
}
38 changes: 38 additions & 0 deletions iter/source/source_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
package source_test

import (
"testing"

qt "github.com/frankban/quicktest"
"github.com/go-board/std/iter"
"github.com/go-board/std/iter/source"
)

func collect[E any](s iter.Seq[E]) []E {
rs := make([]E, 0)
s(func(e E) bool {
rs = append(rs, e)
return true
})
return rs
}

func TestGen(t *testing.T) {
qt.Assert(t, collect(source.Gen(2, 5)), qt.DeepEquals, []int{2, 3, 4})
}

func TestRepeatTimes(t *testing.T) {
qt.Assert(t, collect(source.RepeatTimes(2, 5)), qt.DeepEquals, []int{2, 2, 2, 2, 2})
}

func TestEmpty(t *testing.T) {
qt.Assert(t, collect(source.Empty[int]()), qt.DeepEquals, []int{})
}

func TestOnce(t *testing.T) {
qt.Assert(t, collect(source.Once(2)), qt.DeepEquals, []int{2})
}

func TestVariadic(t *testing.T) {
qt.Assert(t, collect(source.Variadic(1, 2, 3)), qt.DeepEquals, []int{1, 2, 3})
}
9 changes: 5 additions & 4 deletions slices/slice.go
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ import (
"github.com/go-board/std/operator"
"github.com/go-board/std/optional"
"github.com/go-board/std/result"
"github.com/go-board/std/tuple"
)

// Forward create a [iter.Seq] in forward order.
Expand Down Expand Up @@ -183,9 +184,9 @@ func FilterIndexed[T any, S ~[]T](slice S, f func(T, int) bool) S {
iter.Map(
iter.Filter(
iter.Enumerate(Forward(slice)),
func(t iter.Tuple[int, T]) bool { return f(t.Right, t.Left) },
func(t tuple.Pair[int, T]) bool { return f(t.Second, t.First) },
),
func(e iter.Tuple[int, T]) T { return e.Right },
func(e tuple.Pair[int, T]) T { return e.Second },
),
)
}
Expand Down Expand Up @@ -238,7 +239,7 @@ func ForEach[T any, S ~[]T](slice S, f func(T)) {

// ForEachIndexed iterates over the given slice and calls the given function for each element.
func ForEachIndexed[T any, S ~[]T](slice S, f func(T, int)) {
iter.ForEach(iter.Enumerate(Forward(slice)), func(t iter.Tuple[int, T]) { f(t.Right, t.Left) })
iter.ForEach(iter.Enumerate(Forward(slice)), func(t tuple.Pair[int, T]) { f(t.Second, t.First) })
}

// GroupBy returns a new map with the given slice split into smaller slices of the given size.
Expand Down Expand Up @@ -325,7 +326,7 @@ func TryMapIndexed[T, U any, S ~[]T](slice S, f func(T, int) (U, error)) ([]U, e

// MapIndexed returns a new slice with the results of applying the given function to each element in the given slice.
func MapIndexed[T, U any, S ~[]T](slice S, f func(T, int) U) []U {
return Collect(iter.Map(iter.Enumerate(Forward(slice)), func(x iter.Tuple[int, T]) U { return f(x.Right, x.Left) }))
return Collect(iter.Map(iter.Enumerate(Forward(slice)), func(x tuple.Pair[int, T]) U { return f(x.Second, x.First) }))
}

// Max returns the maximum element in the given slice.
Expand Down
Loading

0 comments on commit 2b30c5e

Please sign in to comment.