diff --git a/README.md b/README.md index 59809f6..dc0525e 100644 --- a/README.md +++ b/README.md @@ -23,8 +23,6 @@ go get -u github.com/go-board/std ``` ## Packages Hierarchy -- [algorithm](https://github.com/go-board/std/blob/master/algorithm) common used algorithms - - [dp](https://github.com/go-board/std/blob/master/algorithm/dp) dynamic programming - [clone](https://github.com/go-board/std/blob/master/clone) clone a object - [codec](https://github.com/go-board/std/blob/master/codec) encode and decode - [collections](https://github.com/go-board/std/blob/master/collections) common used collections @@ -32,11 +30,12 @@ go get -u github.com/go-board/std - [linkedlist](https://github.com/go-board/std/blob/master/collections/linkedlist) linked list - [queue](https://github.com/go-board/std/blob/master/collections/queue) double ended queue - [cond](https://github.com/go-board/std/blob/master/cond) conditional operator -- [core](https://github.com/go-board/std/blob/master/core) core types & constraints +- [constraints](https://github.com/go-board/std/blob/master/constraints) core constraints +- [fp](https://github.com/go-board/std/blob/master/fp) functional programing - [hash](https://github.com/go-board/std/blob/master/hash) hash a object -- [iterator](https://github.com/go-board/std/blob/master/iterator) iterators - - [adapters](https://github.com/go-board/std/blob/master/iterator/adapters) adapter to create iterators & streams - - [stream](https://github.com/go-board/std/blob/master/iterator/stream) stream processing +- [iter](https://github.com/go-board/std/blob/master/iter) iterators + - [collector](https://github.com/go-board/std/blob/master/iterator/collector) consume iter and collect to another type + - [source](https://github.com/go-board/std/blob/master/iterator/source) adapter to create iterators & streams - [lazy](https://github.com/go-board/std/blob/master/lazy) lazy evaluation & variables - [optional](https://github.com/go-board/std/blob/master/optional) optional values - [ptr](https://github.com/go-board/std/blob/master/ptr) convenient pointer operator diff --git a/algorithm/dp/dp.go b/algorithm/dp/dp.go deleted file mode 100644 index 77e60ad..0000000 --- a/algorithm/dp/dp.go +++ /dev/null @@ -1,19 +0,0 @@ -package dp - -func LcsBy[T any, S ~[]T](left S, right S, eq func(T, T) bool) S { - if len(left) == 0 || len(right) == 0 { - return nil - } - if eq(left[0], right[0]) { - return append([]T{left[0]}, LcsBy(left[1:], right[1:], eq)...) - } - lcs1, lcs2 := LcsBy(left[1:], right, eq), LcsBy(left, right[1:], eq) - if len(lcs1) > len(lcs2) { - return lcs1 - } - return lcs2 -} - -func Lcs[T comparable, S ~[]T](left S, right S) S { - return LcsBy(left, right, func(a, b T) bool { return a == b }) -} diff --git a/algorithm/dp/dp_test.go b/algorithm/dp/dp_test.go deleted file mode 100644 index 4cd5ed7..0000000 --- a/algorithm/dp/dp_test.go +++ /dev/null @@ -1,76 +0,0 @@ -package dp_test - -import ( - "reflect" - "testing" - - "github.com/go-board/std/algorithm/dp" -) - -func TestLcs(t *testing.T) { - t.Run("lcs", func(t *testing.T) { - testCases := []struct { - left []string - right []string - want []string - }{ - {}, - {left: []string{"a"}}, - {[]string{"a"}, []string{"a"}, []string{"a"}}, - {[]string{"a", "c", "d"}, []string{"b", "c", "a", "d"}, []string{"a", "d"}}, - {[]string{"a", "c", "d", "e"}, []string{"b", "c", "a", "d", "e"}, []string{"a", "d", "e"}}, - } - for _, tc := range testCases { - got := dp.Lcs(tc.left, tc.right) - if !reflect.DeepEqual(got, tc.want) { - t.Errorf("got:%v, want:%v", got, tc.want) - } - } - }) - t.Run("lcs_by", func(t *testing.T) { - type A struct { - a int - b []int - } - testCases := []struct { - left []A - right []A - want []A - }{ - {}, - {left: []A{{a: 1}}}, - {[]A{{a: 1}}, []A{{a: 1}}, []A{{a: 1}}}, - {[]A{{a: 1}, {a: 2}, {a: 3}}, []A{{a: 1}, {a: 2}, {a: 3}}, []A{{a: 1}, {a: 2}, {a: 3}}}, - {[]A{{a: 1}, {a: 2}, {a: 3}}, []A{{a: 1}, {a: 2}, {a: 3}, {a: 4}}, []A{{a: 1}, {a: 2}, {a: 3}}}, - } - for _, tc := range testCases { - got := dp.LcsBy(tc.left, tc.right, func(a, b A) bool { return a.a == b.a }) - if !reflect.DeepEqual(got, tc.want) { - t.Errorf("got:%v, want:%v", got, tc.want) - } - } - }) - t.Run("lcs_by_pointer", func(t *testing.T) { - type A struct { - a int - b []int - } - testCases := []struct { - left []*A - right []*A - want []*A - }{ - {}, - {left: []*A{{a: 1}}}, - {[]*A{{a: 1}}, []*A{{a: 1}}, []*A{{a: 1}}}, - {[]*A{{a: 1}, {a: 2}, {a: 3}}, []*A{{a: 1}, {a: 2}, {a: 3}}, []*A{{a: 1}, {a: 2}, {a: 3}}}, - {[]*A{{a: 1}, {a: 2}, {a: 3}}, []*A{{a: 1}, {a: 2}, {a: 3}, {a: 4}}, []*A{{a: 1}, {a: 2}, {a: 3}}}, - } - for _, tc := range testCases { - got := dp.LcsBy(tc.left, tc.right, func(a, b *A) bool { return a.a == b.a }) - if !reflect.DeepEqual(got, tc.want) { - t.Errorf("got:%v, want:%v", got, tc.want) - } - } - }) -} diff --git a/cmp/cmp.go b/cmp/cmp.go index 04d743e..19b4629 100644 --- a/cmp/cmp.go +++ b/cmp/cmp.go @@ -6,6 +6,17 @@ import ( type Order int // -1, 0, 1 +func MakeOrder(o int) Order { + switch { + case o < 0: + return OrderLess + case o > 0: + return OrderGreater + default: + return OrderEqual + } +} + func (o Order) IsEq() bool { return o == 0 } func (o Order) IsNe() bool { return o != 0 } func (o Order) IsLt() bool { return o < 0 } @@ -68,3 +79,51 @@ type Ord[A any] interface { PartialOrd[A] Cmp(A) Order } + +type Comparator[A any] interface { + Cmp(lhs A, rhs A) int + Lt(lhs A, rhs A) bool + Le(lhs A, rhs A) bool + Gt(lhs A, rhs A) bool + Ge(lhs A, rhs A) bool + Eq(lhs A, rhs A) bool + Ne(lhs A, rhs A) bool +} + +type ComparatorFunc[A any] func(A, A) int + +func (f ComparatorFunc[A]) Cmp(lhs A, rhs A) int { + return f(lhs, rhs) +} + +func (f ComparatorFunc[A]) Lt(lhs A, rhs A) bool { + return f.Cmp(lhs, rhs) < 0 +} + +func (f ComparatorFunc[A]) Le(lhs A, rhs A) bool { + return f.Cmp(lhs, rhs) <= 0 +} + +func (f ComparatorFunc[A]) Gt(lhs A, rhs A) bool { + return f.Cmp(lhs, rhs) > 0 +} + +func (f ComparatorFunc[A]) Ge(lhs A, rhs A) bool { + return f.Cmp(lhs, rhs) >= 0 +} + +func (f ComparatorFunc[A]) Eq(lhs A, rhs A) bool { + return f.Cmp(lhs, rhs) == 0 +} + +func (f ComparatorFunc[A]) Ne(lhs A, rhs A) bool { + return f.Cmp(lhs, rhs) != 0 +} + +func MakeComparator[A Ordered]() Comparator[A] { + return ComparatorFunc[A](Compare[A]) +} + +func MakeComparatorFunc[A any](f func(A, A) int) Comparator[A] { + return ComparatorFunc[A](f) +} diff --git a/cmp/cmp_1_20.go b/cmp/cmp_1_20.go index a1ea230..89acb66 100644 --- a/cmp/cmp_1_20.go +++ b/cmp/cmp_1_20.go @@ -3,7 +3,7 @@ package cmp import ( - "github.com/go-board/std/core" + "github.com/go-board/std/constraints" ) // isNaN reports whether x is a NaN without requiring the math package. @@ -12,7 +12,7 @@ func isNaN[T Ordered](x T) bool { return x != x } -type Ordered = core.Ordered +type Ordered = constraints.Ordered func Compare[T Ordered](lhs T, rhs T) int { xNaN := isNaN(lhs) diff --git a/cmp/impl.go b/cmp/impl.go index 4f6916e..822e5c0 100644 --- a/cmp/impl.go +++ b/cmp/impl.go @@ -12,10 +12,13 @@ func MaxBy[A any, F ~func(A, A) Order](cmp F, lhs, rhs A) A { return ternary(cmp(lhs, rhs).IsGt(), lhs, rhs) } +func MaxFunc[A any](cmp func(A, A) int, lhs, rhs A) A { + return ternary(cmp(lhs, rhs) > 0, lhs, rhs) +} + // MaxByKey calculates the maximum value of two values by a key function. func MaxByKey[A any, F ~func(A) K, K Ordered](key F, lhs, rhs A) A { - keyLhs, keyRhs := key(lhs), key(rhs) - return ternary(keyLhs > keyRhs, lhs, rhs) + return ternary(key(lhs) > key(rhs), lhs, rhs) } // MaxOrdered calculates the maximum value of two ordered values. @@ -28,13 +31,26 @@ func MinBy[A any, F ~func(A, A) Order](cmp F, lhs, rhs A) A { return ternary(cmp(lhs, rhs).IsLt(), lhs, rhs) } +func MinFunc[A any](cmp func(A, A) int, lhs, rhs A) A { + return ternary(cmp(lhs, rhs) < 0, lhs, rhs) +} + // MinByKey calculates the minimum value of two values by a key function. func MinByKey[A any, F ~func(A) K, K Ordered](key F, lhs, rhs A) A { - keyLhs, keyRhs := key(lhs), key(rhs) - return ternary(keyLhs < keyRhs, lhs, rhs) + return ternary(key(lhs) < key(rhs), lhs, rhs) } // MinOrdered calculates the minimum value of two ordered values. func MinOrdered[A Ordered](lhs, rhs A) A { return ternary(lhs < rhs, lhs, rhs) } + +func Or[E comparable](elems ...E) E { + var empty E + for _, x := range elems { + if empty != x { + return x + } + } + return empty +} diff --git a/collections/ordered/map.go b/collections/ordered/map.go index 80db1fe..9981f33 100644 --- a/collections/ordered/map.go +++ b/collections/ordered/map.go @@ -16,16 +16,16 @@ func invert[T any](cmp func(T, T) int) func(T, T) int { // MapEntry is a tuple of key and value. type MapEntry[K, V any] struct{ inner tuple.Pair[K, V] } -// MapEntryOf creates a new MapEntry. -func MapEntryOf[K, V any](key K, value V) MapEntry[K, V] { +// MakeMapEntry creates a new MapEntry. +func MakeMapEntry[K, V any](key K, value V) MapEntry[K, V] { return MapEntry[K, V]{inner: tuple.MakePair(key, value)} } // Key returns the key of the MapEntry. -func (self MapEntry[K, V]) Key() K { return self.inner.First } +func (self MapEntry[K, V]) Key() K { return self.inner.First() } // Value returns the value of the MapEntry. -func (self MapEntry[K, V]) Value() V { return self.inner.Second } +func (self MapEntry[K, V]) Value() V { return self.inner.Second() } // Map is an ordered map based on a B-Tree. type Map[K, V any] struct { @@ -49,12 +49,13 @@ func NewOrderedMap[K cmp.Ordered, V any]() *Map[K, V] { } func (self *Map[K, V]) keyEntry(k K) MapEntry[K, V] { - return MapEntry[K, V]{inner: tuple.Pair[K, V]{First: k}} + var v V + return MapEntry[K, V]{inner: tuple.MakePair(k, v)} } // Insert inserts a new MapEntry. func (self *Map[K, V]) Insert(key K, value V) optional.Optional[V] { - e, ok := self.inner.Set(MapEntryOf(key, value)) + e, ok := self.inner.Set(MakeMapEntry(key, value)) return optional.Map(optional.FromPair(e, ok), MapEntry[K, V].Value) } diff --git a/core/constraint.go b/constraints/constraints.go similarity index 87% rename from core/constraint.go rename to constraints/constraints.go index a8e6a2d..d68873c 100644 --- a/core/constraint.go +++ b/constraints/constraints.go @@ -1,4 +1,4 @@ -package core +package constraints type Primitive interface{ Numeric | ~string | ~bool } @@ -21,6 +21,3 @@ type Integer interface{ Signed | Unsigned } type Numeric interface{ Integer | Float | Complex } type Ordered interface{ Integer | Float | ~string } - -// Deprecated: use [Numeric] instead -type Number = Numeric diff --git a/core/core.go b/core/core.go deleted file mode 100644 index 89a3261..0000000 --- a/core/core.go +++ /dev/null @@ -1,61 +0,0 @@ -package core - -type Unit struct{} - -type Byte byte - -type Char rune - -type Int int - -type Int8 int8 - -type Int16 int16 - -type Int32 int32 - -type Int64 int64 - -type Uint uint - -type Uint8 uint8 - -type Uint16 uint16 - -type Uint32 uint32 - -type Uint64 uint64 - -type Float32 float32 - -type Float64 float64 - -type Bool bool - -type String string - -type Complex64 complex128 - -type Complex32 complex64 - -type Slice[T any] []T - -type Map[K comparable, V any] map[K]V - -type Chan[T any] chan T - -func (self Complex32) Real() Float32 { return Float32(real(self)) } -func (self Complex32) Imag() Float32 { return Float32(imag(self)) } -func (self Complex64) Real() Float64 { return Float64(real(self)) } -func (self Complex64) Imag() Float64 { return Float64(imag(self)) } - -func IsPrimitive[T any]() bool { - var t0 T - switch any(t0).(type) { - case int, int8, int16, int32, int64, uint, uint8, uint16, uint32, uint64, float32, float64, bool, string, complex64, complex128: - return true - case Int, Int8, Int16, Int32, Int64, Uint, Uint8, Uint16, Uint32, Uint64, Float32, Float64, Bool, String, Complex64, Complex32: - return true - } - return false -} diff --git a/core/core_test.go b/core/core_test.go deleted file mode 100644 index 2b32832..0000000 --- a/core/core_test.go +++ /dev/null @@ -1,23 +0,0 @@ -package core_test - -import ( - "reflect" - "testing" - - . "github.com/go-board/std/core" -) - -func TestSlice(t *testing.T) { - x := Slice[int]{} - y := Slice[string]{} - rx := reflect.TypeOf(x) - ry := reflect.TypeOf(y) - if rx.Name() != "Slice[int]" { - t.Fail() - } - if ry.Name() != "Slice[string]" { - t.Fail() - } - t.Logf("%+v, %s\n", rx, rx.Name()) - t.Logf("%+v, %s\n", ry, ry.Name()) -} diff --git a/fp/compose.go b/fp/compose.go index 5e3fcac..6394db0 100644 --- a/fp/compose.go +++ b/fp/compose.go @@ -1,95 +1,6 @@ package fp -// Compose1 is a function that compose one function. -func Compose1[A, R any]( - f func(A) R, -) func(A) R { - return f -} - -// Compose2 is a function that compose two functions. -func Compose2[A, B, R any]( - f func(A) B, - g func(B) R, -) func(A) R { - return func(x A) R { return g(f(x)) } -} - -// Compose3 is a function that compose three functions. -func Compose3[A, B, C, R any]( - f func(A) B, - g func(B) C, - h func(C) R, -) func(A) R { - return func(x A) R { return h(g(f(x))) } -} - -// Compose4 is a function that compose four functions. -func Compose4[A, B, C, D, R any]( - f func(A) B, - g func(B) C, - h func(C) D, - i func(D) R, -) func(A) R { - return func(x A) R { return i(h(g(f(x)))) } -} - -// Compose5 is a function that compose five functions. -func Compose5[A, B, C, D, E, R any]( - f func(A) B, - g func(B) C, - h func(C) D, - i func(D) E, - j func(E) R, -) func(A) R { - return func(x A) R { return j(i(h(g(f(x))))) } -} - -// ComposeFunction1 is a function that compose one function. -func ComposeFunction1[A, R any](f Function1[A, R]) Function1[A, R] { return f } - -// ComposeFunction2 is a function that compose two functions. -func ComposeFunction2[A, B, R any]( - f Function1[A, B], - g Function1[B, R], -) Function1[A, R] { - return Fn1[A, R](func(x A) R { - return g.Apply(f.Apply(x)) - }) -} - -// ComposeFunction3 is a function that compose three functions. -func ComposeFunction3[A, B, C, R any]( - f Function1[A, B], - g Function1[B, C], - h Function1[C, R], -) Function1[A, R] { - return Fn1[A, R](func(x A) R { - return h.Apply(g.Apply(f.Apply(x))) - }) -} - -// ComposeFunction4 is a function that compose four functions. -func ComposeFunction4[A, B, C, D, R any]( - f Function1[A, B], - g Function1[B, C], - h Function1[C, D], - i Function1[D, R], -) Function1[A, R] { - return Fn1[A, R](func(x A) R { - return i.Apply(h.Apply(g.Apply(f.Apply(x)))) - }) -} - -// ComposeFunction5 is a function that compose five functions. -func ComposeFunction5[A, B, C, D, E, R any]( - f Function1[A, B], - g Function1[B, C], - h Function1[C, D], - i Function1[D, E], - j Function1[E, R], -) Function1[A, R] { - return Fn1[A, R](func(x A) R { - return j.Apply(i.Apply(h.Apply(g.Apply(f.Apply(x))))) - }) +// Compose is a function that compose two functions. +func Compose[A, B, C any](f func(A) B, g func(B) C) func(A) C { + return func(a A) C { return g(f(a)) } } diff --git a/fp/either.go b/fp/either.go new file mode 100644 index 0000000..019de19 --- /dev/null +++ b/fp/either.go @@ -0,0 +1,93 @@ +package fp + +type Either[L, R any] struct { + left L + right R + isLeft bool +} + +// Left create an [Either] from left value. +func Left[L, R any](l L) Either[L, R] { return Either[L, R]{left: l, isLeft: true} } + +// Right create an [Either] from right value. +func Right[L, R any](r R) Either[L, R] { return Either[L, R]{right: r, isLeft: false} } + +// IsLeft test if left value. +func (e Either[L, R]) IsLeft() bool { return e.isLeft } + +// IsRight test if right value. +func (e Either[L, R]) IsRight() bool { return !e.isLeft } + +// InspectLeft call f if is left value. +func (e Either[L, R]) InspectLeft(f func(L)) { + if e.IsLeft() { + f(e.left) + } +} + +// InspectRight call f if is right value. +func (e Either[L, R]) InspectRight(f func(R)) { + if e.IsRight() { + f(e.right) + } +} + +// ToLeft convert right value to left value. +func (e Either[L, R]) ToLeft(f func(R) L) Either[L, R] { + if e.IsLeft() { + return Left[L, R](e.left) + } + return Left[L, R](f(e.right)) +} + +// ToRight convert left value to right value. +func (e Either[L, R]) ToRight(f func(L) R) Either[L, R] { + if e.IsRight() { + return Right[L](e.right) + } + return Right[L](f(e.left)) +} + +// Left unwrap to left value, if not, panic. +func (e Either[L, R]) Left() L { + if e.IsLeft() { + return e.left + } + panic("Left on Right value") +} + +func (e Either[L, R]) LeftOr(v L) L { + if e.IsLeft() { + return e.left + } + return v +} + +func (e Either[L, R]) LeftOrElse(f func() L) L { + if e.IsLeft() { + return e.left + } + return f() +} + +// Right unwrap to right value, if not, panic. +func (e Either[L, R]) Right() R { + if e.IsRight() { + return e.right + } + panic("Right on Left value") +} + +func (e Either[L, R]) RightOr(v R) R { + if e.IsRight() { + return e.right + } + return v +} + +func (e Either[L, R]) RightOrElse(f func() R) R { + if e.IsRight() { + return e.right + } + return f() +} diff --git a/fp/either_test.go b/fp/either_test.go new file mode 100644 index 0000000..40256a3 --- /dev/null +++ b/fp/either_test.go @@ -0,0 +1,54 @@ +package fp_test + +import ( + "testing" + + qt "github.com/frankban/quicktest" + "github.com/go-board/std/fp" +) + +func TestEither_IsLeft(t *testing.T) { + l := fp.Left[int, string](1) + qt.Assert(t, l.IsLeft(), qt.IsTrue) + r := fp.Right[int, string]("") + qt.Assert(t, r.IsLeft(), qt.IsFalse) +} + +func TestEither_IsRight(t *testing.T) { + l := fp.Left[int, string](1) + qt.Assert(t, l.IsRight(), qt.IsFalse) + r := fp.Right[int, string]("") + qt.Assert(t, r.IsRight(), qt.IsTrue) +} + +func TestEither_Left(t *testing.T) { + a := qt.New(t) + a.Run("left", func(c *qt.C) { + l := fp.Left[int, string](1) + c.Assert(l.Left(), qt.Equals, 1) + }) + a.Run("right", func(c *qt.C) { + defer func() { + x := recover() + a.Assert(x, qt.IsNotNil) + }() + r := fp.Right[int, string]("") + c.Assert(r.Left(), qt.Equals, 0) + }) +} + +func TestEither_Right(t *testing.T) { + a := qt.New(t) + a.Run("left", func(c *qt.C) { + defer func() { + x := recover() + a.Assert(x, qt.IsNotNil) + }() + l := fp.Left[int, string](1) + c.Assert(l.Right(), qt.Equals, "") + }) + a.Run("right", func(c *qt.C) { + r := fp.Right[int, string]("") + c.Assert(r.Right(), qt.Equals, "") + }) +} diff --git a/fp/fn1.go b/fp/fn1.go index dd8f2e2..11302e6 100644 --- a/fp/fn1.go +++ b/fp/fn1.go @@ -1,27 +1,15 @@ package fp -// Function1 is a function that takes one argument. -type Function1[A, R any] interface { - Apply(A) R - Curry() func(A) R -} - // Fn1 is a function that takes one argument. type Fn1[A, R any] func(A) R func (f Fn1[A, R]) Apply(a A) R { return f(a) } -func (f Fn1[A, R]) Curry() func(A) R { return Fn1[A, R](func(a A) R { return f.Apply(a) }) } +func (f Fn1[A, R]) Curry() Fn1[A, R] { return func(a A) R { return f.Apply(a) } } -// Apply1 is a function that apply one argument to a function. -func Apply1[A, R any](f func(A) R, a A) R { - return Fn1[A, R](f).Apply(a) -} +func MakeFn1[A, R any](f func(A) R) Fn1[A, R] { return f } -// Curry1 is a function that curry one argument function. -func Curry1[A, R any](f func(A) R) func(A) R { - return Fn1[A, R](f).Curry() -} +func Curry1[A, R any](f func(A) R) Fn1[A, R] { return f } // Uncurry1 is a function that uncurry one argument function. func Uncurry1[A, R any](f func(A) R) func(A) R { diff --git a/fp/fn2.go b/fp/fn2.go index 80514d6..354a032 100644 --- a/fp/fn2.go +++ b/fp/fn2.go @@ -1,36 +1,27 @@ package fp -// Function2 is a function that takes two arguments. -type Function2[A, B, R any] interface { - Apply(A, B) R - Curry() func(A) func(B) R - Partial1(A) Function1[B, R] -} - // Fn2 is a function that takes two arguments. type Fn2[A, B, R any] func(A, B) R func (f Fn2[A, B, R]) Apply(a A, b B) R { return f(a, b) } -func (f Fn2[A, B, R]) Curry() func(A) func(B) R { - return func(a A) func(B) R { return func(b B) R { return f.Apply(a, b) } } +func (f Fn2[A, B, R]) Curry() Fn1[A, Fn1[B, R]] { + return func(a A) Fn1[B, R] { + return func(b B) R { + return f.Apply(a, b) + } + } } -func (f Fn2[A, B, R]) Partial1(a A) Function1[B, R] { - return Fn1[B, R](func(b B) R { return f.Apply(a, b) }) +func (f Fn2[A, B, R]) Partial1(a A) Fn1[B, R] { + return func(b B) R { return f.Apply(a, b) } } -// Apply2 is a function that takes two arguments. -func Apply2[A, B, R any](f func(A, B) R, a A, b B) R { - return Fn2[A, B, R](f).Apply(a, b) -} +func MakeFn2[A, B, R any](f func(A, B) R) Fn2[A, B, R] { return f } -// Curry2 is a function that curry two arguments function. -func Curry2[A, B, R any](f func(A, B) R) func(A) func(B) R { - return Fn2[A, B, R](f).Curry() -} +func Curry2[A, B, R any](f func(A, B) R) Fn1[A, Fn1[B, R]] { return MakeFn2(f).Curry() } // Uncurry2 is a function that uncurry two arguments function. -func Uncurry2[A, B, R any](f func(A) func(B) R) func(A, B) R { +func Uncurry2[A, B, R any](f func(A) func(B) R) Fn2[A, B, R] { return func(a A, b B) R { return f(a)(b) } } diff --git a/fp/fn3.go b/fp/fn3.go index 02e04f1..dd00a1a 100644 --- a/fp/fn3.go +++ b/fp/fn3.go @@ -1,43 +1,37 @@ package fp -// Function3 is a function that takes three arguments. -type Function3[A, B, C, R any] interface { - Apply(A, B, C) R - Curry() func(A) func(B) func(C) R - Partial1(A) Function2[B, C, R] - Partial2(A, B) Function1[C, R] -} - // Fn3 is a function that takes three arguments. type Fn3[A, B, C, R any] func(A, B, C) R func (f Fn3[A, B, C, R]) Apply(a A, b B, c C) R { return f(a, b, c) } -func (f Fn3[A, B, C, R]) Curry() func(A) func(B) func(C) R { - return func(a A) func(B) func(C) R { - return func(b B) func(C) R { return func(c C) R { return f.Apply(a, b, c) } } +func (f Fn3[A, B, C, R]) Curry() Fn1[A, Fn1[B, Fn1[C, R]]] { + return func(a A) Fn1[B, Fn1[C, R]] { + return func(b B) Fn1[C, R] { + return func(c C) R { + return f.Apply(a, b, c) + } + } } } -func (f Fn3[A, B, C, R]) Partial1(a A) Function2[B, C, R] { - return Fn2[B, C, R](func(b B, c C) R { return f.Apply(a, b, c) }) +func (f Fn3[A, B, C, R]) Partial1(a A) Fn2[B, C, R] { + return func(b B, c C) R { return f.Apply(a, b, c) } } -func (f Fn3[A, B, C, R]) Partial2(a A, b B) Function1[C, R] { - return Fn1[C, R](func(c C) R { return f.Apply(a, b, c) }) +func (f Fn3[A, B, C, R]) Partial2(a A, b B) Fn1[C, R] { + return func(c C) R { return f.Apply(a, b, c) } } -// Apply3 is a function that takes three arguments. -func Apply3[A, B, C, R any](f func(A, B, C) R, a A, b B, c C) R { - return Fn3[A, B, C, R](f).Apply(a, b, c) +func MakeFn3[A, B, C, R any](f func(A, B, C) R) Fn3[A, B, C, R] { + return f } -// Curry3 is a function that curry three arguments function. -func Curry3[A, B, C, R any](f func(A, B, C) R) func(A) func(B) func(C) R { - return Fn3[A, B, C, R](f).Curry() +func Curry3[A, B, C, R any](f func(A, B, C) R) Fn1[A, Fn1[B, Fn1[C, R]]] { + return MakeFn3(f).Curry() } // Uncurry3 is a function that uncurry three arguments function. -func Uncurry3[A, B, C, R any](f func(A) func(B) func(C) R) func(A, B, C) R { +func Uncurry3[A, B, C, R any](f func(A) func(B) func(C) R) Fn3[A, B, C, R] { return func(a A, b B, c C) R { return f(a)(b)(c) } } diff --git a/fp/fn4.go b/fp/fn4.go index 92f9374..99cac68 100644 --- a/fp/fn4.go +++ b/fp/fn4.go @@ -1,52 +1,45 @@ package fp -// Function4 is a function that takes four arguments. -type Function4[A, B, C, D, R any] interface { - Apply(A, B, C, D) R - Curry() func(A) func(B) func(C) func(D) R - Partial1(A) Function3[B, C, D, R] - Partial2(A, B) Function2[C, D, R] - Partial3(A, B, C) Function1[D, R] -} - // Fn4 is a function that takes four arguments. type Fn4[A, B, C, D, R any] func(A, B, C, D) R func (f Fn4[A, B, C, D, R]) Apply(a A, b B, c C, d D) R { return f(a, b, c, d) } -func (f Fn4[A, B, C, D, R]) Curry() func(A) func(B) func(C) func(D) R { - return func(a A) func(B) func(C) func(D) R { - return func(b B) func(C) func(D) R { - return func(c C) func(D) R { return func(d D) R { return f.Apply(a, b, c, d) } } +func (f Fn4[A, B, C, D, R]) Curry() Fn1[A, Fn1[B, Fn1[C, Fn1[D, R]]]] { + return func(a A) Fn1[B, Fn1[C, Fn1[D, R]]] { + return func(b B) Fn1[C, Fn1[D, R]] { + return func(c C) Fn1[D, R] { + return func(d D) R { + return f.Apply(a, b, c, d) + } + } } } } -func (f Fn4[A, B, C, D, R]) Partial1(a A) Function3[B, C, D, R] { - return Fn3[B, C, D, R](func(b B, c C, d D) R { +func (f Fn4[A, B, C, D, R]) Partial1(a A) Fn3[B, C, D, R] { + return func(b B, c C, d D) R { return f.Apply(a, b, c, d) - }) + } } -func (f Fn4[A, B, C, D, R]) Partial2(a A, b B) Function2[C, D, R] { - return Fn2[C, D, R](func(c C, d D) R { return f.Apply(a, b, c, d) }) +func (f Fn4[A, B, C, D, R]) Partial2(a A, b B) Fn2[C, D, R] { + return func(c C, d D) R { return f.Apply(a, b, c, d) } } -func (f Fn4[A, B, C, D, R]) Partial3(a A, b B, c C) Function1[D, R] { - return Fn1[D, R](func(d D) R { return f.Apply(a, b, c, d) }) +func (f Fn4[A, B, C, D, R]) Partial3(a A, b B, c C) Fn1[D, R] { + return func(d D) R { return f.Apply(a, b, c, d) } } -// Apply4 is a function that takes four arguments. -func Apply4[A, B, C, D, R any](f func(A, B, C, D) R, a A, b B, c C, d D) R { - return Fn4[A, B, C, D, R](f).Apply(a, b, c, d) +func MakeFn4[A, B, C, D, R any](f func(A, B, C, D) R) Fn4[A, B, C, D, R] { + return f } -// Curry4 is a function that curry four argument function. -func Curry4[A, B, C, D, R any](f func(A, B, C, D) R) func(A) func(B) func(C) func(D) R { - return Fn4[A, B, C, D, R](f).Curry() +func Curry4[A, B, C, D, R any](f func(A, B, C, D) R) Fn1[A, Fn1[B, Fn1[C, Fn1[D, R]]]] { + return MakeFn4(f).Curry() } // Uncurry4 is a function that uncurry four argument function. -func Uncurry4[A, B, C, D, R any](f func(A) func(B) func(C) func(D) R) func(A, B, C, D) R { +func Uncurry4[A, B, C, D, R any](f func(A) func(B) func(C) func(D) R) Fn4[A, B, C, D, R] { return func(a A, b B, c C, d D) R { return f(a)(b)(c)(d) } } diff --git a/fp/fn5.go b/fp/fn5.go index 30584d7..d9d1c31 100644 --- a/fp/fn5.go +++ b/fp/fn5.go @@ -1,59 +1,47 @@ package fp -// Function5 is a function that takes five arguments. -type Function5[A, B, C, D, E, R any] interface { - Apply(A, B, C, D, E) R - Curry() func(A) func(B) func(C) func(D) func(E) R - Partial1(A) Function4[B, C, D, E, R] - Partial2(A, B) Function3[C, D, E, R] - Partial3(A, B, C) Function2[D, E, R] - Partial4(A, B, C, D) Function1[E, R] -} - // Fn5 is a function that takes five arguments. type Fn5[A, B, C, D, E, R any] func(A, B, C, D, E) R func (f Fn5[A, B, C, D, E, R]) Apply(a A, b B, c C, d D, e E) R { return f(a, b, c, d, e) } -func (f Fn5[A, B, C, D, E, R]) Curry() func(A) func(B) func(C) func(D) func(E) R { - return func(a A) func(B) func(C) func(D) func(E) R { - return func(b B) func(C) func(D) func(E) R { - return func(c C) func(D) func(E) R { - return func(d D) func(E) R { return func(e E) R { return f.Apply(a, b, c, d, e) } } +func (f Fn5[A, B, C, D, E, R]) Curry() Fn1[A, Fn1[B, Fn1[C, Fn1[D, Fn1[E, R]]]]] { + return func(a A) Fn1[B, Fn1[C, Fn1[D, Fn1[E, R]]]] { + return func(b B) Fn1[C, Fn1[D, Fn1[E, R]]] { + return func(c C) Fn1[D, Fn1[E, R]] { + return func(d D) Fn1[E, R] { + return func(e E) R { + return f.Apply(a, b, c, d, e) + } + } } } } } -func (f Fn5[A, B, C, D, E, R]) Partial1(a A) Function4[B, C, D, E, R] { - return Fn4[B, C, D, E, R](func(b B, c C, d D, e E) R { +func (f Fn5[A, B, C, D, E, R]) Partial1(a A) Fn4[B, C, D, E, R] { + return func(b B, c C, d D, e E) R { return f.Apply(a, b, c, d, e) - }) -} - -func (f Fn5[A, B, C, D, E, R]) Partial2(a A, b B) Function3[C, D, E, R] { - return Fn3[C, D, E, R](func(c C, d D, e E) R { return f.Apply(a, b, c, d, e) }) + } } -func (f Fn5[A, B, C, D, E, R]) Partial3(a A, b B, c C) Function2[D, E, R] { - return Fn2[D, E, R](func(d D, e E) R { return f.Apply(a, b, c, d, e) }) +func (f Fn5[A, B, C, D, E, R]) Partial2(a A, b B) Fn3[C, D, E, R] { + return func(c C, d D, e E) R { return f.Apply(a, b, c, d, e) } } -func (f Fn5[A, B, C, D, E, R]) Partial4(a A, b B, c C, d D) Function1[E, R] { - return Fn1[E, R](func(e E) R { return f.Apply(a, b, c, d, e) }) +func (f Fn5[A, B, C, D, E, R]) Partial3(a A, b B, c C) Fn2[D, E, R] { + return func(d D, e E) R { return f.Apply(a, b, c, d, e) } } -// Apply5 is a function that takes five arguments. -func Apply5[A, B, C, D, E, R any](f func(A, B, C, D, E) R, a A, b B, c C, d D, e E) R { - return Fn5[A, B, C, D, E, R](f).Apply(a, b, c, d, e) +func (f Fn5[A, B, C, D, E, R]) Partial4(a A, b B, c C, d D) Fn1[E, R] { + return func(e E) R { return f.Apply(a, b, c, d, e) } } -// Curry5 is a function that curry five argument function. -func Curry5[A, B, C, D, E, R any](f func(A, B, C, D, E) R) func(A) func(B) func(C) func(D) func(E) R { - return Fn5[A, B, C, D, E, R](f).Curry() +func MakeFn5[A, B, C, D, E, R any](f func(A, B, C, D, E) R) Fn5[A, B, C, D, E, R] { + return f } // Uncurry5 is a function that uncurry five argument function. -func Uncurry5[A, B, C, D, E, R any](f func(A) func(B) func(C) func(D) func(E) R) func(A, B, C, D, E) R { +func Uncurry5[A, B, C, D, E, R any](f func(A) func(B) func(C) func(D) func(E) R) Fn5[A, B, C, D, E, R] { return func(a A, b B, c C, d D, e E) R { return f(a)(b)(c)(d)(e) } } diff --git a/fp/fp_test.go b/fp/fp_test.go index 5a0aba9..6009d3f 100644 --- a/fp/fp_test.go +++ b/fp/fp_test.go @@ -10,29 +10,12 @@ import ( func TestCurry(t *testing.T) { a := qt.New(t) a.Run("curry1", func(c *qt.C) { - f := func(a int) int { return a + 2 } - curryAdd1 := Curry1(f) - c.Assert(curryAdd1(1), qt.Equals, 3) - }) - a.Run("curry2", func(t *qt.C) { - add2 := func(a, b int) int { return a + b } - curryAdd2 := Curry2(add2) - t.Assert(curryAdd2(1)(2), qt.Equals, 3) - }) - a.Run("curry3", func(c *qt.C) { - add3 := func(a, b, c int) int { return a + b + c } - curryAdd3 := Curry3(add3) - c.Assert(curryAdd3(1)(2)(3), qt.Equals, 6) - }) - a.Run("curry4", func(c *qt.C) { - add4 := func(a, b, c, d int) int { return a + b + c + d } - curryAdd4 := Curry4(add4) - c.Assert(curryAdd4(1)(2)(3)(4), qt.Equals, 10) + f := MakeFn1(func(i int) string { return strconv.Itoa(i) }) + c.Assert(f.Curry()(3), qt.Equals, "3") }) - a.Run("curry5", func(c *qt.C) { - add5 := func(a, b, c, d, e int) int { return a + b + c + d + e } - curryAdd5 := Curry5(add5) - c.Assert(curryAdd5(1)(2)(3)(4)(5), qt.Equals, 15) + a.Run("curry2", func(c *qt.C) { + f := MakeFn2(func(a int, b int) string { return strconv.Itoa(a + b) }) + c.Assert(f.Curry()(1)(2), qt.Equals, "3") }) } @@ -80,76 +63,6 @@ func TestUncurry(t *testing.T) { }) } -func TestApply(t *testing.T) { - a := qt.New(t) - a.Run("apply1", func(c *qt.C) { - f := func(a int) int { return a + 2 } - c.Assert(Apply1(f, 1), qt.Equals, 3) - }) - a.Run("apply2", func(c *qt.C) { - f := func(a, b int) int { return a + b } - c.Assert(Apply2(f, 1, 2), qt.Equals, 3) - }) - a.Run("apply3", func(c *qt.C) { - f := func(a, b, c int) int { return a + b + c } - c.Assert(Apply3(f, 1, 2, 3), qt.Equals, 6) - }) - a.Run("apply4", func(c *qt.C) { - f := func(a, b, c, d int) int { return a + b + c + d } - c.Assert(Apply4(f, 1, 2, 3, 4), qt.Equals, 10) - }) - a.Run("apply5", func(c *qt.C) { - f := func(a, b, c, d, e int) int { return a + b + c + d + e } - c.Assert(Apply5(f, 1, 2, 3, 4, 5), qt.Equals, 15) - }) -} - -func TestCompose(t *testing.T) { - a := qt.New(t) - a.Run("compose", func(c *qt.C) { - f := func(a int) int { return a + 1 } - c.Assert(Compose1(f)(1), qt.Equals, 2) - }) - a.Run("compose2", func(c *qt.C) { - f := func(a int) int { return a + 1 } - g := func(a int) int { return a + 2 } - c.Assert(Compose2(f, g)(1), qt.Equals, 4) - }) - - a.Run("compose3", func(c *qt.C) { - f := func(a int) int { return a + 1 } - g := func(a int) int { return a + 2 } - h := func(a int) int { return a + 3 } - c.Assert(Compose3(f, g, h)(1), qt.Equals, 7) - }) - - a.Run("compose4", func(c *qt.C) { - f := func(a int) int { return a + 1 } - g := func(a int) int { return a + 2 } - h := func(a int) int { return a + 3 } - i := func(a int) int { return a + 4 } - c.Assert(Compose4(f, g, h, i)(1), qt.Equals, 11) - }) - - a.Run("compose5", func(c *qt.C) { - f := func(a int) int { return a + 1 } - g := func(a int) int { return a + 2 } - h := func(a int) int { return a + 3 } - i := func(a int) int { return a + 4 } - j := func(a int) int { return a + 5 } - c.Assert(Compose5(f, g, h, i, j)(1), qt.Equals, 16) - }) - - a.Run("multiple type compose", func(c *qt.C) { - f := func(a int) int32 { return int32(a) + 2 } - g := func(a int32) int64 { return int64(a) * 3 } - h := func(a int64) int { return int(a) + 4 } - i := func(a int) int { return a * 100 } - j := func(a int) string { return strconv.FormatInt(int64(a), 10) } - c.Assert(Compose5(f, g, h, i, j)(1), qt.Equals, "1300") - }) -} - func TestState(t *testing.T) { a := qt.New(t) a.Run("state", func(c *qt.C) { @@ -171,10 +84,8 @@ func TestPartial(t *testing.T) { a.Run("partial1", func(c *qt.C) { m := Fn2[func(int) int, []int, []int](map_[int, int]).Partial1(func(i int) int { return i * 2 }) n := Fn2[func(int) string, []int, []string](map_[int, string]).Partial1(strconv.Itoa) - x := Compose2(m.(Fn1[[]int, []int]), n.(Fn1[[]int, []string])) - y := ComposeFunction2(m, n) + x := Compose(m, n) c.Assert(x([]int{1, 2, 3, 4, 5}), qt.DeepEquals, []string{"2", "4", "6", "8", "10"}) - c.Assert(y.Apply([]int{1, 2, 3, 4, 5}), qt.DeepEquals, []string{"2", "4", "6", "8", "10"}) }) } diff --git a/fp/identity.go b/fp/identity.go new file mode 100644 index 0000000..573226b --- /dev/null +++ b/fp/identity.go @@ -0,0 +1,9 @@ +package fp + +func Identity[T any](x T) T { return x } + +func Flip[A, B, R any](f func(A, B) R) func(B, A) R { + return func(b B, a A) R { + return f(a, b) + } +} diff --git a/fp/state.go b/fp/state.go index d83caa5..bc08a5a 100644 --- a/fp/state.go +++ b/fp/state.go @@ -1,17 +1,19 @@ package fp +type state[T any] struct{ value T } + +func (s *state[T]) get() T { return s.value } +func (s *state[T]) set(v T) { s.value = v } +func (s *state[T]) setFunc(f func(T) T) { s.value = f(s.value) } + // UseState is a convenience function for creating a stateful function. func UseState[T any](initial T) (func() T, func(T)) { - state := initial - getter := func() T { return state } - setter := func(newState T) { state = newState } - return getter, setter + s := &state[T]{value: initial} + return s.get, s.set } // UseFuncState is a convenience function for creating a stateful function. func UseFuncState[T any](initial T) (func() T, func(func(T) T)) { - state := initial - getter := func() T { return state } - setter := func(f func(prevState T) T) { state = f(state) } - return getter, setter + s := &state[T]{value: initial} + return s.get, s.setFunc } diff --git a/iter/collector/collector.go b/iter/collector/collector.go index f026b65..8f6baa7 100644 --- a/iter/collector/collector.go +++ b/iter/collector/collector.go @@ -41,9 +41,17 @@ func newCollectorImpl[S, E, C any](state S, collectSeq func(state S, s iter.Seq[ return &collectorImpl[S, E, C]{state: state, collectSeq: collectSeq, collect: collect, finish: finish} } -func (c *collectorImpl[S, E, C]) CollectSeq(s iter.Seq[E]) { c.state = c.collectSeq(c.state, s) } -func (c *collectorImpl[S, E, C]) Collect(x E) { c.state = c.collect(c.state, x) } -func (c *collectorImpl[S, E, C]) Finish() C { return c.finish(c.state) } +func (self *collectorImpl[S, E, C]) CollectSeq(s iter.Seq[E]) { + self.state = self.collectSeq(self.state, s) +} + +func (self *collectorImpl[S, E, C]) Collect(x E) { + self.state = self.collect(self.state, x) +} + +func (self *collectorImpl[S, E, C]) Finish() C { + return self.finish(self.state) +} // ToSlice collects all elements in [iter.Seq] to slice. func ToSlice[E any]() Collector[E, []E] { @@ -76,7 +84,7 @@ func ToOrderedMapFunc[E, K, V any](cmp func(K, K) int, f func(E) (K, V)) Collect return newCollectorImpl( ordered.NewMap[K, V](cmp), func(state *ordered.Map[K, V], s iter.Seq[E]) *ordered.Map[K, V] { - state.InsertIter(iter.Map(s, func(e E) ordered.MapEntry[K, V] { return ordered.MapEntryOf(f(e)) })) + state.InsertIter(iter.Map(s, func(e E) ordered.MapEntry[K, V] { return ordered.MakeMapEntry(f(e)) })) return state }, func(state *ordered.Map[K, V], x E) *ordered.Map[K, V] { @@ -136,23 +144,25 @@ func Distinct[E comparable]() Collector[E, iter.Seq[E]] { return newCollectorImpl( 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.First[e]; !ok { - state.First[e] = struct{}{} - state.Second = append(state.Second, e) + return iter.Fold(s, state, func(p tuple.Pair[map[E]struct{}, []E], e E) tuple.Pair[map[E]struct{}, []E] { + first, second := p.First(), p.Second() + if _, ok := first[e]; !ok { + first[e] = struct{}{} + second = append(second, e) } + return tuple.MakePair(first, second) }) - return state }, 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) + first, second := state.First(), state.Second() + if _, ok := first[x]; !ok { + first[x] = struct{}{} + second = append(second, x) } - return state + return tuple.MakePair(first, second) }, func(state tuple.Pair[map[E]struct{}, []E]) iter.Seq[E] { - return sliceSeq(state.Second) + return sliceSeq(state.Second()) }, ) } @@ -163,54 +173,58 @@ func DistinctFunc[E any](f func(E, E) int) Collector[E, iter.Seq[E]] { return newCollectorImpl( 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.First.Contains(e) { - state.First.Insert(e) - state.Second = append(state.Second, e) + return iter.Fold(s, state, func(p tuple.Pair[*ordered.Set[E], []E], e E) tuple.Pair[*ordered.Set[E], []E] { + first, second := p.First(), p.Second() + if !first.Contains(e) { + first.Insert(e) + second = append(second, e) } + return tuple.MakePair(first, second) }) - return state }, 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) + first, second := state.First(), state.Second() + if !first.Contains(x) { + first.Insert(x) + second = append(second, x) } - return state + return tuple.MakePair(first, second) }, func(state tuple.Pair[*ordered.Set[E], []E]) iter.Seq[E] { - return sliceSeq(state.Second) + return sliceSeq(state.Second()) }, ) } -// Partition creates two [iter.Seq], split by the given predicate function. -// -// The first [iter.Seq] contains elements that satisfies the predicate. -// The second [iter.Seq] -func Partition[E any](f func(E) bool) Collector[E, tuple.Pair[iter.Seq[E], iter.Seq[E]]] { +func chunk[E any](chunks [][]E, e E, n int) [][]E { + if len(chunks) == 0 { + chunks = append(chunks, []E{e}) + } else if len(chunks[len(chunks)-1]) == n { + chunks = append(chunks, []E{e}) + } else { + chunks[len(chunks)-1] = append(chunks[len(chunks)-1], e) + } + return chunks +} + +func Chunk[E any](n int) Collector[E, iter.Seq[iter.Seq[E]]] { return newCollectorImpl( - 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.First = append(state.First, e) - } else { - state.Second = append(state.Second, e) - } - }) + make([][]E, 0), + func(state [][]E, s iter.Seq[E]) [][]E { + iter.ForEach(s, func(e E) { state = chunk(state, e, n) }) return state }, - func(state tuple.Pair[[]E, []E], x E) tuple.Pair[[]E, []E] { - if f(x) { - state.First = append(state.First, x) - } else { - state.Second = append(state.Second, x) - } - return state + func(state [][]E, x E) [][]E { + return chunk(state, x, n) }, - func(state tuple.Pair[[]E, []E]) tuple.Pair[iter.Seq[E], iter.Seq[E]] { - return tuple.MakePair(sliceSeq(state.First), sliceSeq(state.Second)) + func(state [][]E) iter.Seq[iter.Seq[E]] { + return func(yield func(iter.Seq[E]) bool) { + for _, chunk := range state { + if !yield(sliceSeq(chunk)) { + break + } + } + } }, ) } diff --git a/iter/collector/collector_test.go b/iter/collector/collector_test.go index fbfb496..1ddac6b 100644 --- a/iter/collector/collector_test.go +++ b/iter/collector/collector_test.go @@ -77,3 +77,11 @@ func TestCollectOne(t *testing.T) { c.Collect(2) qt.Assert(t, c.Finish(), qt.DeepEquals, []int{1, 2}) } + +func TestChunk(t *testing.T) { + m := collector.Collect(seq(1, 2, 3, 4, 5), collector.Chunk[int](2)) + x := collector.Collect(iter.Map(m, func(e iter.Seq[int]) []int { + return collector.Collect(e, collector.ToSlice[int]()) + }), collector.ToSlice[[]int]()) + qt.Assert(t, x, qt.DeepEquals, [][]int{{1, 2}, {3, 4}, {5}}) +} diff --git a/iter/iter_test.go b/iter/iter_test.go index 4ba836e..cf5af69 100644 --- a/iter/iter_test.go +++ b/iter/iter_test.go @@ -9,6 +9,7 @@ import ( "github.com/go-board/std/cmp" "github.com/go-board/std/iter" "github.com/go-board/std/tuple" + cmp2 "github.com/google/go-cmp/cmp" ) func seq[E any](slice ...E) iter.Seq[E] { @@ -23,13 +24,15 @@ func seq[E any](slice ...E) iter.Seq[E] { func collect[E any](s iter.Seq[E]) []E { var slice []E - iter.CollectFunc(s, func(e E) bool { slice = append(slice, e); return true }) + iter.CollectFunc[E](s, func(e E) bool { slice = append(slice, e); return true }) return slice } func TestEnumerate(t *testing.T) { x := iter.Enumerate(seq(1, 2, 3)) - qt.Assert(t, collect(x), qt.DeepEquals, []tuple.Pair[int, int]{{0, 1}, {1, 2}, {2, 3}}) + qt.Assert(t, collect(x), qt.CmpEquals(cmp2.Comparer(func(l, r tuple.Pair[int, int]) bool { + return l.First() == r.First() && l.Second() == r.Second() + })), []tuple.Pair[int, int]{tuple.MakePair(0, 1), tuple.MakePair(1, 2), tuple.MakePair(2, 3)}) } func TestAppend(t *testing.T) { @@ -37,6 +40,11 @@ func TestAppend(t *testing.T) { qt.Assert(t, collect(x), qt.DeepEquals, []int{1, 2, 3}) } +func TestPrepend(t *testing.T) { + x := iter.Prepend(seq(1), 2, 3) + qt.Assert(t, collect(x), qt.DeepEquals, []int{2, 3, 1}) +} + func TestTryForEach(t *testing.T) { err := iter.TryForEach(seq(1, 2, 3), func(i int) error { if i < 2 { @@ -91,6 +99,14 @@ func TestFind(t *testing.T) { qt.Assert(t, ok, qt.IsTrue) qt.Assert(t, e, qt.Equals, -1) }) + t.Run("first option", func(t *testing.T) { + x := iter.FirstOption(seq(1, 2, -1, 0, 3), func(i int) bool { + return i < 0 + }) + qt.Assert(t, x.IsSomeAnd(func(i int) bool { + return i == -1 + }), qt.IsTrue) + }) t.Run("last", func(t *testing.T) { e, ok := iter.Last(seq(1, 2, 3, -1, 0, 5, -9, 0), func(i int) bool { return i < 0 @@ -101,8 +117,41 @@ func TestFind(t *testing.T) { } func TestIndex(t *testing.T) { - x := iter.Index(seq(1, 2, 3), func(i int) bool { return i > 2 }) - qt.Assert(t, x, qt.Equals, 2) + t.Run("index first", func(t *testing.T) { + x := iter.IndexFirst(seq(1, 2, 3), 2) + qt.Assert(t, x, qt.Equals, 1) + }) + t.Run("index first func", func(t *testing.T) { + x := iter.IndexFirstFunc(seq(1, 2, 3), func(i int) bool { return i > 2 }) + qt.Assert(t, x, qt.Equals, 2) + }) + t.Run("index last", func(t *testing.T) { + x := iter.IndexLast(seq(1, 2, 3, 2, 1), 2) + qt.Assert(t, x, qt.Equals, 3) + }) + t.Run("index last func", func(t *testing.T) { + x := iter.IndexLastFunc(seq(1, 2, 3, 2, 1), func(i int) bool { + return i == 2 + }) + qt.Assert(t, x, qt.Equals, 3) + }) +} + +func TestNth(t *testing.T) { + t.Run("nth", func(t *testing.T) { + x, ok := iter.Nth(seq(1, 2, 3), 1) + qt.Assert(t, ok, qt.IsTrue) + qt.Assert(t, x, qt.Equals, 2) + y, ok := iter.Nth(seq(1, 2, 3), 3) + qt.Assert(t, ok, qt.IsFalse) + qt.Assert(t, y, qt.Equals, 0) + }) + t.Run("nth option", func(t *testing.T) { + x := iter.NthOption(seq(1, 2, 3), 0) + qt.Assert(t, x.IsSomeAnd(func(i int) bool { return i == 1 }), qt.IsTrue) + y := iter.NthOption(seq(1, 2, 3), 1) + qt.Assert(t, y.IsSomeAnd(func(i int) bool { return i == 2 }), qt.IsTrue) + }) } func TestMap(t *testing.T) { @@ -121,7 +170,6 @@ func TestMap(t *testing.T) { }) qt.Assert(t, collect(x), qt.DeepEquals, []string{"1", "2"}) }) - } func TestFold(t *testing.T) { @@ -142,6 +190,17 @@ func TestReduce(t *testing.T) { _, ok := iter.Reduce(s, func(a, b int) int { return a + b }) qt.Assert(t, ok, qt.IsFalse) }) + t.Run("has elems option", func(t *testing.T) { + s := seq(1, 2, 3) + x := iter.ReduceOption(s, func(i int, i2 int) int { return i + i2 }) + qt.Assert(t, x.IsSome(), qt.IsTrue) + qt.Assert(t, x.Value(), qt.Equals, 6) + }) + t.Run("no elems option", func(t *testing.T) { + s := seq[int]() + x := iter.ReduceOption(s, func(i int, i2 int) int { return i + i2 }) + qt.Assert(t, x.IsNone(), qt.IsTrue) + }) } func TestFilter(t *testing.T) { @@ -205,6 +264,11 @@ func TestMax(t *testing.T) { qt.Assert(t, ok, qt.IsTrue) qt.Assert(t, x, qt.Equals, 3) }) + t.Run("max option", func(t *testing.T) { + x := iter.MaxOption(s) + qt.Assert(t, x.IsSome(), qt.IsTrue) + qt.Assert(t, x.Value(), qt.Equals, 3) + }) t.Run("max_func", func(t *testing.T) { x, ok := iter.MaxFunc(s, cmp.Compare[int]) qt.Assert(t, ok, qt.IsTrue) @@ -234,7 +298,28 @@ func TestMin(t *testing.T) { qt.Assert(t, ok, qt.IsTrue) qt.Assert(t, x, qt.Equals, 1) }) +} +func TestMinMax(t *testing.T) { + s := seq(1, 2, 3) + t.Run("minmax", func(t *testing.T) { + x, ok := iter.MinMax(s) + qt.Assert(t, ok, qt.IsTrue) + qt.Assert(t, x.First(), qt.Equals, 1) + qt.Assert(t, x.Second(), qt.Equals, 3) + }) + t.Run("minmax_func", func(t *testing.T) { + x, ok := iter.MinMaxFunc(s, cmp.Compare[int]) + qt.Assert(t, ok, qt.IsTrue) + qt.Assert(t, x.First(), qt.Equals, 1) + qt.Assert(t, x.Second(), qt.Equals, 3) + }) + t.Run("minmax_by_key", func(t *testing.T) { + x, ok := iter.MinMaxByKey(s, func(e int) int { return e }) + qt.Assert(t, ok, qt.IsTrue) + qt.Assert(t, x.First(), qt.Equals, 1) + qt.Assert(t, x.Second(), qt.Equals, 3) + }) } func TestCountFunc(t *testing.T) { @@ -284,6 +369,16 @@ func TestSkip(t *testing.T) { s := seq(1, 2, 3, 4, 5) s = iter.Skip(s, 2) qt.Assert(t, collect(s), qt.DeepEquals, []int{3, 4, 5}) + t.Run("nest skip", func(t *testing.T) { + s := seq(1, 2, 3, 4, 5) + s = iter.Skip(s, 1) + s = iter.Skip(s, 1) + qt.Assert(t, collect(s), qt.DeepEquals, []int{3, 4, 5}) + }) + t.Run("recursive", func(t *testing.T) { + x := iter.Skip(iter.Skip(seq(1, 2, 3), 1), 1) + qt.Assert(t, collect(x), qt.DeepEquals, []int{3}) + }) } func TestSkipWhile(t *testing.T) { @@ -338,3 +433,89 @@ func TestCollectFunc(t *testing.T) { }) qt.Assert(t, s, qt.DeepEquals, []int{2, 6, 12}) } + +func TestCollect(t *testing.T) { + x := iter.Collect(seq(1, 2, 3), collect[int]) + qt.Assert(t, x, qt.DeepEquals, []int{1, 2, 3}) +} + +func TestPartition(t *testing.T) { + s := seq(1, 2, 3, 4, 5, 6) + x := iter.Partition(s, func(i int) bool { + return i%2 == 0 + }) + qt.Assert(t, collect(x.First()), qt.DeepEquals, []int{2, 4, 6}) + qt.Assert(t, collect(x.Second()), qt.DeepEquals, []int{1, 3, 5}) +} + +func TestIsPartitioned(t *testing.T) { + s := seq(2, 4, 6) + qt.Assert(t, iter.IsPartitioned(s, func(i int) bool { + return i%2 == 0 + }), qt.IsTrue) + qt.Assert(t, iter.IsPartitioned(s, func(i int) bool { + return i%3 == 0 + }), qt.IsFalse) +} + +func TestUnzip(t *testing.T) { + t.Run("unzip", func(t *testing.T) { + x := seq(tuple.MakePair(1, 2), tuple.MakePair(3, 4), tuple.MakePair(5, 6)) + z := iter.Unzip(x) + qt.Assert(t, collect(z.First()), qt.DeepEquals, []int{1, 3, 5}) + qt.Assert(t, collect(z.Second()), qt.DeepEquals, []int{2, 4, 6}) + }) + t.Run("unzip func", func(t *testing.T) { + x := seq(1, 2, 3) + z := iter.UnzipFunc(x, func(a int) (int, int) { return a, a - 2 }) + qt.Assert(t, collect(z.First()), qt.DeepEquals, []int{1, 2, 3}) + qt.Assert(t, collect(z.Second()), qt.DeepEquals, []int{-1, 0, 1}) + }) +} + +func TestContains(t *testing.T) { + t.Run("contains", func(t *testing.T) { + x := iter.Contains(seq(1, 2, 3), 2) + qt.Assert(t, x, qt.IsTrue) + y := iter.Contains(seq(1, 2, 3), 4) + qt.Assert(t, y, qt.IsFalse) + }) + t.Run("contains func", func(t *testing.T) { + x := iter.ContainsFunc(seq(1, 2, 3), func(e int) bool { + return e == 2 + }) + qt.Assert(t, x, qt.IsTrue) + y := iter.ContainsFunc(seq(1, 2, 3), func(e int) bool { + return e == 5 + }) + qt.Assert(t, y, qt.IsFalse) + }) +} + +// Go 中没有不可变参数,因此在迭代过程中会存在对原始seq修改的可能。 +// 真是糟糕的设计呀。 +func TestMutate(t *testing.T) { + type user struct{ Age int } + x := seq(&user{1}, &user{2}) + iter.ForEach(x, func(user *user) { user.Age *= 2 }) + qt.Assert(t, collect(x), qt.DeepEquals, []*user{{2}, {4}}) +} + +func TestHead(t *testing.T) { + t.Run("option", func(t *testing.T) { + x := seq(1, 2, 3) + h := iter.HeadOption(x) + qt.Assert(t, h.IsSomeAnd(func(i int) bool { return i == 1 }), qt.IsTrue) + x = iter.Tail(x) + h = iter.HeadOption(x) + qt.Assert(t, h.IsSomeAnd(func(i int) bool { return i == 2 }), qt.IsTrue) + }) +} + +func TestTail(t *testing.T) { + x := seq(1, 2, 3) + x = iter.Tail(x) + qt.Assert(t, collect(x), qt.DeepEquals, []int{2, 3}) + x = iter.Tail(x) + qt.Assert(t, collect(x), qt.DeepEquals, []int{3}) +} diff --git a/iter/ops.go b/iter/ops.go index a6bad38..d300f74 100644 --- a/iter/ops.go +++ b/iter/ops.go @@ -2,6 +2,8 @@ package iter import ( "github.com/go-board/std/cmp" + "github.com/go-board/std/optional" + "github.com/go-board/std/result" "github.com/go-board/std/tuple" ) @@ -9,7 +11,7 @@ import ( // // Example: // -// iter.Enumerate(seq(1,2,3)) => seq: (0, 1), (1, 2), (2, 3) +// iter.Enumerate(seq(1,2,3)) => seq: pair(0, 1), pair(1, 2), pair(2, 3) func Enumerate[E any](s Seq[E]) Seq[tuple.Pair[int, E]] { i := -1 return func(yield func(tuple.Pair[int, E]) bool) { @@ -23,11 +25,8 @@ func Enumerate[E any](s Seq[E]) Seq[tuple.Pair[int, E]] { // Example: // // iter.Append(seq(1), 2, 3) => seq: 1,2,3 -// iter.Append(seq(1,2),) => seq: 1,2 +// iter.Append(seq(1,2),) => seq: 1,2 func Append[E any](s Seq[E], elems ...E) Seq[E] { - if len(elems) == 0 { - return s - } return func(yield func(E) bool) { breakEarly := false s(func(e E) bool { @@ -44,6 +43,24 @@ func Append[E any](s Seq[E], elems ...E) Seq[E] { } } +// Prepend create a new Seq which yield each element from +// additional elements and origin Seq. +// +// Example: +// +// iter.Prepend(seq(1), 2, 3) => seq: 2,3,1 +// iter.Prepend(seq(1,2),) => seq: 1,2 +func Prepend[E any](s Seq[E], elems ...E) Seq[E] { + return func(yield func(E) bool) { + for _, e := range elems { + if !yield(e) { + return + } + } + s(yield) + } +} + // TryForEach call f on each element in [Seq], // stopping at the first error and returning that error. func TryForEach[E any](s Seq[E], f func(E) error) (err error) { @@ -81,15 +98,14 @@ func Filter[E any](s Seq[E], f func(E) bool) Seq[E] { // // slices.FilterMap(seq(1,2,3,4,5), func(x int) (string, bool) { // if x%2 == 1 { -// return strconv.Itoa(x), true +// return strconv.Itoa(x), true // } // return "", false // }) => seq: "1", "3", "5" func FilterMap[E, T any](s Seq[E], f func(E) (T, bool)) Seq[T] { return func(yield func(T) bool) { s(func(e E) bool { - t, ok := f(e) - if ok { + if t, ok := f(e); ok { return yield(t) } return true @@ -108,31 +124,39 @@ func FilterZero[E comparable](s Seq[E]) Seq[E] { return Filter(s, func(e E) bool { return e != zero }) } -// First try to find first element in [Seq], -// if no elements in [Seq], return zero var and false. +// First try to find first element in [Seq] that satisfies the given predicate, +// if no matching elements in [Seq], return zero var and false. // // Example: // -// iter.First(seq(1,2,3)) => 1, true -// iter.First(seq[int]()) => 0, false -func First[E any](s Seq[E], f func(E) bool) (result E, ok bool) { - s(func(x E) bool { - if f(x) { - result, ok = x, true - return false - } - return true - }) - return +// iter.First(seq(1,2,3), func(x) bool { return true }) => 1, true +// iter.First(seq[int](), func(x) bool { return true }) => 0, false +func First[E any](s Seq[E], f func(E) bool) (E, bool) { + return FirstOption(s, f).Get() +} + +// FirstOption try to find first element in [Seq] that satisfies the given predicate, +// if no matching elements in [Seq], return None. +// +// Example: +// +// iter.FirstOption(seq(1,2,3), func(x) bool { return true }) => Some(1) +// iter.FirstOption(seq[int](), func(x) bool { return true }) => None +func FirstOption[E any](s Seq[E], f func(E) bool) optional.Optional[E] { + head := HeadOption(s) + if head.IsNone() || head.IsSomeAnd(f) { + return head + } + return FirstOption(Tail(s), f) } -// Last try to find last element in [Seq], -// if no elements in [Seq], return zero var and false. +// Last try to find last element in [Seq] that satisfies the given predicate, +// if no matching elements in [Seq], return zero var and false. // // Example: // -// iter.Last(seq(1,2,3)) => 3, true -// iter.Last(seq[int]()) => 0, false +// iter.Last(seq(1,2,3), func(x) bool { return true }) => 3, true +// iter.Last(seq[int](), func(x) bool { return true }) => 0, false func Last[E any](s Seq[E], f func(E) bool) (m E, ok bool) { s(func(e E) bool { if f(e) { @@ -143,6 +167,17 @@ func Last[E any](s Seq[E], f func(E) bool) (m E, ok bool) { return } +// LastOption try to find last element in [Seq] that satisfies the given predicate, +// if no matching element in [Seq], return None. +// +// Example: +// +// iter.LastOption(seq(1,2,3), func(x) bool { return true }) => Some(3) +// iter.LastOption(seq[int](), func(x) bool { return true }) => None +func LastOption[E any](s Seq[E], f func(E) bool) optional.Optional[E] { + return optional.FromPair(Last(s, f)) +} + // Map call f on each element in [Seq], and map each element to another type. // // Example: @@ -199,6 +234,10 @@ func TryFold[E, A any](s Seq[E], init A, f func(A, E) (A, error)) (res A, err er return } +func TryFoldResult[E, A any](s Seq[E], init A, f func(A, E) (A, error)) result.Result[A] { + return result.FromPair(TryFold(s, init, f)) +} + // Fold each element into an accumulator by applying an operation, // returning the final result. // @@ -234,20 +273,34 @@ func Scan[E, A any](s Seq[E], init A, f func(state A, elem E) A) Seq[A] { // // iter.Reduce(seq(1,2,3), func(x, y int) int { return x * y }) => 6, true // iter.Reduce(seq[int](), func(x, y int) int { return x * y }) => 0, false -func Reduce[E any](s Seq[E], f func(E, E) E) (result E, hasElem bool) { - s(func(x E) bool { - if !hasElem { - result, hasElem = x, true - } else { - result = f(result, x) - } - return true - }) - return +func Reduce[E any](s Seq[E], f func(E, E) E) (E, bool) { + return ReduceOption(s, f).Get() +} + +// ReduceOption reduces the elements to a single one, by repeatedly applying a reducing +// operation. +// +// Example: +// +// iter.ReduceOption(seq(1,2,3), func(x, y int) int { return x * y }) => some(6) +// iter.ReduceOption(seq[int](), func(x, y int) int { return x * y }) => none +func ReduceOption[E any](s Seq[E], f func(E, E) E) optional.Optional[E] { + head := HeadOption(s) + if head.IsNone() { + return head + } + return optional.Some(Fold(Tail(s), head.Value(), f)) } // Find searches for an element of an iterator that satisfies a predicate. -func Find[E any](s Seq[E], f func(E) bool) (E, bool) { return First(s, f) } +func Find[E any](s Seq[E], f func(E) bool) (E, bool) { + return First(s, f) +} + +// FindOption searches for an element of an iterator that satisfies a predicate. +func FindOption[E any](s Seq[E], f func(E) bool) optional.Optional[E] { + return FirstOption(s, f) +} // FindMap applies function to the elements of iterator and returns // the first non-none result. @@ -262,21 +315,54 @@ func FindMap[E, T any](s Seq[E], f func(E) (T, bool)) (result T, ok bool) { return } +func FindMapOption[E, T any](s Seq[E], f func(E) (T, bool)) optional.Optional[T] { + return optional.FromPair(FindMap(s, f)) +} + // Contains returns true if element is found in the [Seq]. func Contains[E comparable](s Seq[E], x E) bool { return ContainsFunc(s, func(e E) bool { return x == e }) } -// Contains returns true if element is satisfies the given predicate in the [Seq]. +// ContainsFunc returns true if element satisfies the given predicate in the [Seq]. func ContainsFunc[E any](s Seq[E], f func(e E) bool) bool { - return Index(s, f) >= 0 + return IndexFirstFunc(s, f) >= 0 } -// Index searches index of element which satisfying the given predicate. -func Index[E any](s Seq[E], f func(E) bool) int { - i := -1 - s(func(x E) bool { defer func() { i++ }(); return !f(x) }) - return i +func IndexFirst[E comparable](s Seq[E], v E) int { + return IndexFirstFunc(s, func(e E) bool { return e == v }) +} + +// IndexFirstFunc searches first index of element which satisfying the given predicate. +func IndexFirstFunc[E any](s Seq[E], f func(E) bool) int { + x := Filter(Enumerate(s), func(t tuple.Pair[int, E]) bool { return f(t.Second()) }) + z := FirstOption(x, func(t tuple.Pair[int, E]) bool { return true }) + return optional.Map(z, tuple.Pair[int, E].First).ValueOr(-1) +} + +func IndexLast[E comparable](s Seq[E], v E) int { + return IndexLastFunc(s, func(e E) bool { return e == v }) +} + +// IndexLastFunc searches last index of element which satisfying the given predicate. +func IndexLastFunc[E any](s Seq[E], f func(E) bool) int { + x := Filter(Enumerate(s), func(t tuple.Pair[int, E]) bool { return f(t.Second()) }) + z := LastOption(x, func(t tuple.Pair[int, E]) bool { return true }) + return optional.Map(z, tuple.Pair[int, E].First).ValueOr(-1) +} + +// Nth find the element which index is n. +// +// If index out of range, return zero val and false. +func Nth[E any](s Seq[E], n int) (E, bool) { + return NthOption(s, n).Get() +} + +// NthOption find the element which index is n. +// +// If index out of range, return [optional.None]. +func NthOption[E any](s Seq[E], n int) optional.Optional[E] { + return HeadOption(Skip(s, n)) } // All tests if every element of the iterator matches a predicate. @@ -284,7 +370,7 @@ func Index[E any](s Seq[E], f func(E) bool) int { // Example: // // iter.All(seq(1,2,3), func(x int) bool { return i > 0 }) => true -// iter.Any(seq(1,2,3), func(x int) bool { return i > 2 }) => false +// iter.All(seq(1,2,3), func(x int) bool { return i > 2 }) => false func All[E any](s Seq[E], f func(E) bool) bool { ok := true s(func(x E) bool { ok = f(x); return ok }) @@ -318,6 +404,16 @@ func Max[E cmp.Ordered](s Seq[E]) (E, bool) { return MaxFunc(s, cmp.Compare[E]) } +// MaxOption returns the maximum element of an iterator. +// +// Example: +// +// iter.Max(seq(1,2,3)) => Some(3) +// iter.Max(seq[int]()) => None +func MaxOption[E cmp.Ordered](s Seq[E]) optional.Optional[E] { + return optional.FromPair(Max(s)) +} + // MaxFunc returns the element that gives the maximum value with respect to the // specified comparison function. // @@ -326,12 +422,18 @@ func Max[E cmp.Ordered](s Seq[E]) (E, bool) { // iter.MaxFunc(seq(1,2,3), cmp.Compare[int]) => 3, true // iter.MaxFunc(seq[int](), cmp.Compare[int]) => 0, false func MaxFunc[E any](s Seq[E], f func(E, E) int) (E, bool) { - return Reduce(s, func(l, r E) E { - if f(l, r) > 0 { - return l - } - return r - }) + return MaxFuncOption(s, f).Get() +} + +// MaxFuncOption returns the element that gives the maximum value with respect to the +// specified comparison function. +// +// Example: +// +// iter.MaxFunc(seq(1,2,3), cmp.Compare[int]) => Some(3) +// iter.MaxFunc(seq[int](), cmp.Compare[int]) => None +func MaxFuncOption[E any](s Seq[E], f func(E, E) int) optional.Optional[E] { + return ReduceOption(s, func(x, y E) E { return cmp.MaxFunc(f, x, y) }) } // MaxByKey returns the element that gives the maximum value from the @@ -345,6 +447,10 @@ func MaxByKey[E any, K cmp.Ordered](s Seq[E], f func(E) K) (E, bool) { return MaxFunc(s, func(x E, y E) int { return cmp.Compare(f(x), f(y)) }) } +func MaxByKeyOption[E any, K cmp.Ordered](s Seq[E], f func(E) K) optional.Optional[E] { + return optional.FromPair(MaxByKey(s, f)) +} + // Min returns the minimum element of an iterator. // // Example: @@ -355,6 +461,10 @@ func Min[E cmp.Ordered](s Seq[E]) (E, bool) { return MinFunc(s, cmp.Compare[E]) } +func MinOption[E cmp.Ordered](s Seq[E]) optional.Optional[E] { + return optional.FromPair(Min(s)) +} + // MinFunc returns the element that gives the minimum value with respect to the // specified comparison function. // @@ -363,12 +473,11 @@ func Min[E cmp.Ordered](s Seq[E]) (E, bool) { // iter.MinFunc(seq(1,2,3), cmp.Compare[int]) // 1, true // iter.MinFunc(seq[int](), cmp.Compare[int]) // 0, false func MinFunc[E any](s Seq[E], f func(E, E) int) (E, bool) { - return Reduce(s, func(l, r E) E { - if f(l, r) < 0 { - return l - } - return r - }) + return Reduce(s, func(l, r E) E { return cmp.MinFunc(f, l, r) }) +} + +func MinFuncOption[E any](s Seq[E], f func(E, E) int) optional.Optional[E] { + return optional.FromPair(MinFunc(s, f)) } // MinByKey returns the element that gives the minimum value from the @@ -382,7 +491,60 @@ func MinByKey[E any, K cmp.Ordered](s Seq[E], f func(E) K) (E, bool) { return MinFunc(s, func(x E, y E) int { return cmp.Compare(f(x), f(y)) }) } -// Count consumes the iterator, counting the number of elements +func MinByKeyOption[E any, K cmp.Ordered](s Seq[E], f func(E) K) optional.Optional[E] { + return optional.FromPair(MinByKey(s, f)) +} + +// MinMax returns the minimum and maximum element in [Seq]. +// +// If [Seq] is empty, the second return value is false. +// +// Example: +// +// iter.MinMax(seq(1,2,3)) => pair(1, 3), true +// iter.MinMax(seq[int]()) => pair(0, 0), false +func MinMax[E cmp.Ordered](s Seq[E]) (tuple.Pair[E, E], bool) { + return MinMaxFunc(s, cmp.Compare[E]) +} + +// MinMaxOption returns the minimum and maximum element in [Seq]. +// +// If [Seq] is empty, the second return value is false. +// +// Example: +// +// iter.MinMax(seq(1,2,3)) => pair(1, 3), true +// iter.MinMax(seq[int]()) => pair(0, 0), false +func MinMaxOption[E cmp.Ordered](s Seq[E]) optional.Optional[tuple.Pair[E, E]] { + return optional.FromPair(MinMax(s)) +} + +func MinMaxFunc[E any](s Seq[E], f func(E, E) int) (tuple.Pair[E, E], bool) { + return MinMaxFuncOption(s, f).Get() +} + +func MinMaxFuncOption[E any](s Seq[E], f func(E, E) int) optional.Optional[tuple.Pair[E, E]] { + var minimum, maximum E + var ok bool + ForEach(s, func(e E) { + if !ok { + minimum, maximum, ok = e, e, true + } else { + minimum, maximum = cmp.MinFunc(f, minimum, e), cmp.MaxFunc(f, maximum, e) + } + }) + return optional.FromPair(tuple.MakePair(minimum, maximum), ok) +} + +func MinMaxByKey[E any, K cmp.Ordered](s Seq[E], f func(E) K) (tuple.Pair[E, E], bool) { + return MinMaxFunc(s, func(x E, y E) int { return cmp.Compare(f(x), f(y)) }) +} + +func MinMaxByKeyOption[E any, K cmp.Ordered](s Seq[E], f func(E) K) optional.Optional[tuple.Pair[E, E]] { + return MinMaxFuncOption(s, func(x E, y E) int { return cmp.Compare(f(x), f(y)) }) +} + +// Count counting the number of elements in [Seq] that // equal to the given one and returning it. // // Example: @@ -393,30 +555,28 @@ func Count[E comparable](s Seq[E], value E) int { return CountFunc(s, func(e E) bool { return value == e }) } -// CountFunc consumes the iterator, counting the number of elements +// CountFunc counting the number of elements in [Seq] that // match the predicate function and returning it. // // Example: // // iter.CountFunc(seq(1,2,3), func(x int) bool { return x % 2 == 0 }) // 1 func CountFunc[E any](s Seq[E], f func(E) bool) int { - n := 0 - s(func(x E) bool { - if f(x) { - n++ + return Fold(s, 0, func(i int, e E) int { + if f(e) { + i += 1 } - return true + return i }) - return n } -// Size consumes the iterator, counting the number of iterations and returning it. +// Size counting the number of iterations and returning it. // // Example: // // iter.Size(seq(1,2,3)) // 3 func Size[E any](s Seq[E]) int { - return CountFunc(s, func(E) bool { return true }) + return Fold(s, 0, func(i int, e E) int { return i + 1 }) } // IsSorted checks if the elements of this iterator are sorted. @@ -449,6 +609,17 @@ func IsSortedFunc[E any](s Seq[E], f func(E, E) int) bool { return ok } +// IsSortedByKey checks if the elements of this iterator are sorted +// using the key produced by given function. +// +// Example: +// +// iter.IsSortedFunc(seq(1, 2, 3), cmp.Compare[int]) // true +// iter.IsSortedFunc(seq(2, 1, 3), cmp.Compare[int]) // false +func IsSortedByKey[E any, K cmp.Ordered](s Seq[E], f func(E) K) bool { + return IsSortedFunc(s, func(x, y E) int { return cmp.Compare(f(x), f(y)) }) +} + // StepBy creates an iterator starting at the same point, but stepping by // the given amount at each iteration. // @@ -456,16 +627,10 @@ func IsSortedFunc[E any](s Seq[E], f func(E, E) int) bool { // // iter.StepBy(seq(1,2,3,4,5), 2) // seq: 1,3,5 func StepBy[E any](s Seq[E], n int) Seq[E] { - var i int - return func(yield func(E) bool) { - s(func(e E) bool { - if i%n == 0 && !yield(e) { - return false - } - i++ - return true - }) - } + return Map( + Filter(Enumerate(s), func(t tuple.Pair[int, E]) bool { return t.First()%n == 0 }), + func(e tuple.Pair[int, E]) E { return e.Second() }, + ) } // Take creates an iterator that yields the first `n` elements, or fewer @@ -476,13 +641,9 @@ func StepBy[E any](s Seq[E], n int) Seq[E] { // iter.Take(seq(1,2,3), 2) // seq: 1,2 // iter.Take(seq(1,2,3), 5) // seq: 1,2,3 func Take[E any](s Seq[E], n int) Seq[E] { - return func(yield func(E) bool) { - i := -1 - s(func(x E) bool { - i++ - return i < n && yield(x) - }) - } + takeFunc := func(t tuple.Pair[int, E]) bool { return t.First() < n } + takeIter := TakeWhile(Enumerate(s), takeFunc) + return Map(takeIter, tuple.Pair[int, E].Second) } // TakeWhile creates an iterator that yields elements based on a predicate. @@ -501,16 +662,9 @@ func TakeWhile[E any](s Seq[E], f func(E) bool) Seq[E] { // iter.Skip(seq(1,2,3), 1) // seq: 2,3 // iter.Skip(seq(1,2), 3) // seq: none func Skip[E any](s Seq[E], n int) Seq[E] { - return func(yield func(E) bool) { - i := -1 - s(func(x E) bool { - i++ - if i >= n { - return yield(x) - } - return true - }) - } + skipFunc := func(t tuple.Pair[int, E]) bool { return t.First() < n } + skipIter := SkipWhile(Enumerate(s), skipFunc) + return Map(skipIter, tuple.Pair[int, E].Second) } // SkipWhile creates an iterator that [`skip`]s elements based on a predicate. @@ -637,6 +791,35 @@ func CollectFunc[E any](s Seq[E], collect func(E) bool) { s(func(x E) bool { return collect(x) }) } +func Collect[E any, C any](s Seq[E], collect func(s Seq[E]) C) C { + return collect(s) +} + +func empty[E any]() Seq[E] { return func(func(E) bool) {} } + +// Partition creates two [iter.Seq], split by the given predicate function. +// +// The first [iter.Seq] contains elements that satisfies the predicate. +// The second [iter.Seq] contains elements that not satisfies the predicate. +// +// Example: +// +// iter.Partition(seq(1,2,3,4,5,6), func(x int) bool { return i%2==0 }) => (seq: 2,4,6, seq: 1,3,5) +func Partition[E any](s Seq[E], f func(E) bool) tuple.Pair[Seq[E], Seq[E]] { + return tuple.MakePair(Filter(s, f), Filter(s, func(e E) bool { return !f(e) })) +} + +// IsPartitioned checks if the elements of this iterator are partitioned +// according to the given predicate, such that all those that return true +// precede all those that return false. +// +// Example: +// +// iter.IsPartitioned(seq(1,2,3,4), func(x int) bool { return x>0 }) => true +func IsPartitioned[E any](s Seq[E], f func(E) bool) bool { + return All(s, f) || !Any(s, f) +} + // Intersperse creates a new iterator which places a copy of `separator` // between adjacent items of the original iterator. // @@ -673,3 +856,61 @@ func Inspect[E any](s Seq[E], f func(E)) Seq[E] { }) } } + +// Unzip converts a [Seq] of pair into a pair of [Seq]. +// +// Example: +// +// iter.Unzip(seq(pair(1,2), pair(3,4), pair(5,6))) => (seq: 1,3,5 seq: 2,4,6) +func Unzip[A, B any](x Seq[tuple.Pair[A, B]]) tuple.Pair[Seq[A], Seq[B]] { + return UnzipFunc(x, tuple.Pair[A, B].Unpack) +} + +// UnzipFunc converts a [Seq] into a pair of [Seq] by given unzip function. +// +// Example: +// +// iter.UnzipFunc(seq(pair(1,2), pair(3,4), pair(5,6)), func(p pair) (int, int) { return p.first, p.second }) +// => (seq: 1,3,5 seq:2,4,6) +func UnzipFunc[A, B, C any](x Seq[A], f func(A) (B, C)) tuple.Pair[Seq[B], Seq[C]] { + return Fold(x, tuple.MakePair(empty[B](), empty[C]()), func(acc tuple.Pair[Seq[B], Seq[C]], elem A) tuple.Pair[Seq[B], Seq[C]] { + b, c := f(elem) + return tuple.MakePair(Append(acc.First(), b), Append(acc.Second(), c)) + }) +} + +func Head[E any](s Seq[E]) (E, bool) { + return HeadOption(s).Get() +} + +// HeadOption return head element or None. +// +// Example: +// +// iter.HeadOption(seq(1,2,3)) => Some(1) +// iter.HeadOption(seq[int]()) => None +func HeadOption[E any](s Seq[E]) optional.Optional[E] { + var head E + var ok bool + s(func(e E) bool { head, ok = e, true; return false }) + return optional.FromPair(head, ok) +} + +// Tail returns a new [Seq] that skip head. +// +// Example: +// +// iter.Tail(seq(1,2,3)) => seq: 2,3 +// iter.Tail(seq[int]()) => seq: +func Tail[E any](s Seq[E]) Seq[E] { + return func(yield func(E) bool) { + skip := false + s(func(e E) bool { + if !skip { + skip = true + return true + } + return yield(e) + }) + } +} diff --git a/iter/rangefunc.go b/iter/rangefunc.go index df52066..8227e6b 100644 --- a/iter/rangefunc.go +++ b/iter/rangefunc.go @@ -6,6 +6,8 @@ import ( "iter" "github.com/go-board/std/cmp" + "github.com/go-board/std/optional" + "github.com/go-board/std/tuple" ) // Cmp compares two [Seq] and return first non-equal result. @@ -15,8 +17,8 @@ import ( // iter.Cmp(seq(1,2,3), seq(1,2,3)) => 0 // iter.Cmp(seq(1,2,3), seq(1,2,4)) => -1 // iter.Cmp(seq(1,2,3), seq(1,2,2)) => 1 -// iter.Cmp(seq(1,2), seq(1)) => 1 -// iter.Cmp(seq(1), seq(1,2)) => -1 +// iter.Cmp(seq(1,2), seq(1)) => 1 +// iter.Cmp(seq(1), seq(1,2)) => -1 func Cmp[E cmp.Ordered](x Seq[E], y Seq[E]) int { return CmpFunc(x, y, cmp.Compare[E]) } @@ -24,19 +26,21 @@ func Cmp[E cmp.Ordered](x Seq[E], y Seq[E]) int { // CmpFunc compares two [Seq] using the give compare function. // // And returning at first non-equal result. -func CmpFunc[E, F any](x Seq[E], y Seq[F], f func(E, F) int) int { - itx, sx := iter.Pull(iter.Seq[E](x)) - ity, sy := iter.Pull(iter.Seq[F](y)) +func CmpFunc[A, B any](x Seq[A], y Seq[B], f func(A, B) int) int { + itx, sx := iter.Pull(iter.Seq[A](x)) + ity, sy := iter.Pull(iter.Seq[B](y)) defer sx() defer sy() for { xe, oke := itx() xf, okf := ity() - if oke == okf { + if oke && okf { if c := f(xe, xf); c != 0 { return c } - continue + } + if !oke && !okf { + return 0 } if oke { return +1 @@ -64,20 +68,85 @@ func Eq[E comparable](x Seq[E], y Seq[E]) bool { } // EqFunc test whether two [Seq] are equality using the give compare function. -func EqFunc[E, F any](x Seq[E], y Seq[F], f func(E, F) bool) bool { - itx, sx := iter.Pull(iter.Seq[E](x)) - ity, sy := iter.Pull(iter.Seq[F](y)) +func EqFunc[A, B any](x Seq[A], y Seq[B], f func(A, B) bool) bool { + itx, sx := iter.Pull(iter.Seq[A](x)) + ity, sy := iter.Pull(iter.Seq[B](y)) defer sx() defer sy() for { xe, oke := itx() xf, okf := ity() - return oke == okf && f(xe, xf) + if oke && okf && f(xe, xf) { + continue + } + if !oke && !okf { + return true + } + return false } } // Ne test whether two [Seq] are not equality. -func Ne[E comparable](x Seq[E], y Seq[E]) bool { return !Eq(x, y) } +func Ne[E comparable](x Seq[E], y Seq[E]) bool { + return NeFunc(x, y, func(l E, r E) bool { return l == r }) +} + +// NeFunc test whether two [Seq] are not equality using the give equality function. +func NeFunc[A, B any](x Seq[A], y Seq[B], f func(A, B) bool) bool { + itx, sx := iter.Pull(iter.Seq[A](x)) + ity, sy := iter.Pull(iter.Seq[B](y)) + defer sx() + defer sy() + for { + xe, oke := itx() + xf, okf := ity() + if oke && okf && f(xe, xf) { + continue + } + if !oke && !okf { + return false + } + return true + } +} -// NeFunc test whether two [Seq] are not equality using the give compare function. -func NeFunc[E, F any](x Seq[E], y Seq[F], f func(E, F) bool) bool { return !EqFunc(x, y, f) } +// Zip zips two [Seq] into a single [Seq] of pairs. +// +// If either [Seq] finished, the zipped [Seq] also finish. +// +// Example: +// +// iter.Zip(seq(1,3,5), seq(2,4,6)) => seq: pair(1,2), pair(3,4), pair(5,6) +func Zip[A, B any](x Seq[A], y Seq[B]) Seq[tuple.Pair[A, B]] { + return ZipFunc(x, y, tuple.MakePair[A, B]) +} + +// ZipFunc zips two [Seq] into a single [Seq] of a new type zipped by the given function. +// +// If either [Seq] finished, the zipped [Seq] also finish. +// +// Example: +// +// iter.ZipFunc(seq(1,3,5), seq(2,4,6), func(x, y int) int { return x+y }) => seq: 3,7,11 +func ZipFunc[A, B, C any](x Seq[A], y Seq[B], f func(A, B) C) Seq[C] { + itx, sx := iter.Pull(iter.Seq[A](x)) + ity, sy := iter.Pull(iter.Seq[B](y)) + return func(yield func(C) bool) { + defer sx() + defer sy() + for { + xa, oka := itx() + xb, okb := ity() + if !(oka && okb && yield(f(xa, xb))) { + break + } + } + } +} + +// PullOption turn a [Seq] into a pull style iterator and a stop function, +// iterator function returns an optional value on each call. +func PullOption[E any](s Seq[E]) (func() optional.Optional[E], func()) { + it, stop := iter.Pull(iter.Seq[E](s)) + return func() optional.Optional[E] { return optional.FromPair(it()) }, stop +} diff --git a/iter/rangefunc_test.go b/iter/rangefunc_test.go index cfce76a..46283e0 100644 --- a/iter/rangefunc_test.go +++ b/iter/rangefunc_test.go @@ -1,4 +1,4 @@ -//go:build goexperiment.rangefunc +//go:build goexpriments.rangefunc package iter_test @@ -7,6 +7,7 @@ import ( qt "github.com/frankban/quicktest" "github.com/go-board/std/iter" + "github.com/go-board/std/tuple" ) func TestCmp(t *testing.T) { @@ -46,3 +47,35 @@ func TestEqual(t *testing.T) { qt.Assert(t, ok, qt.IsTrue) }) } + +func TestZip(t *testing.T) { + t.Run("zip", func(t *testing.T) { + x := seq(1, 2, 3) + y := seq(1, 2, 3) + z := iter.Zip(x, y) + qt.Assert(t, collect(z), qt.DeepEquals, []tuple.Pair[int, int]{tuple.MakePair(1, 1), tuple.MakePair(2, 2), tuple.MakePair(3, 3)}) + }) + t.Run("zip func", func(t *testing.T) { + x := seq(1, 2, 3) + y := seq(1, 2, 3) + z := iter.ZipFunc(x, y, func(a, b int) int { return a + b }) + qt.Assert(t, collect(z), qt.DeepEquals, []int{2, 4, 6}) + }) +} + +func TestPullOption(t *testing.T) { + it, stop := iter.PullOption(seq(1, 2)) + defer stop() + first := it() + qt.Assert(t, first.IsSomeAnd(func(i int) bool { return i == 1 }), qt.IsTrue) + second := it() + qt.Assert(t, second.IsSomeAnd(func(i int) bool { return i == 2 }), qt.IsTrue) + third := it() + qt.Assert(t, third.IsNone(), qt.IsTrue) +} + +func TestRangeSeq(t *testing.T) { + for x := range seq(1, 2, 3) { + t.Logf("%+v\n", x) + } +} diff --git a/iter/source/source.go b/iter/source/source.go index 0349934..efa6d53 100644 --- a/iter/source/source.go +++ b/iter/source/source.go @@ -1,11 +1,62 @@ package source import ( - "github.com/go-board/std/core" + "github.com/go-board/std/constraints" "github.com/go-board/std/iter" + "github.com/go-board/std/tuple" ) +// Bytes create an [iter.Seq] of byte from underlying string type. +func Bytes[E ~string](s E) iter.Seq[byte] { + return func(yield func(byte) bool) { + for _, x := range []byte(s) { + if !yield(x) { + return + } + } + } +} + +// Runes create an [iter.Seq] of rune from underlying string type. +func Runes[E ~string](s E) iter.Seq[rune] { + return func(yield func(rune) bool) { + for _, x := range s { + if !yield(x) { + return + } + } + } +} + +// Chan create an [iter.Seq] from readable channel. +func Chan[E any](c <-chan E) iter.Seq[E] { + return func(yield func(E) bool) { + for x := range c { + if !yield(x) { + return + } + } + } +} + +// Map create an [iter.Seq] of k-v pair from map type. +// +// The pair order is unordered. +func Map[K comparable, V any, M ~map[K]V](m M) iter.Seq[tuple.Pair[K, V]] { + return func(yield func(tuple.Pair[K, V]) bool) { + for k, v := range m { + if !yield(tuple.MakePair(k, v)) { + return + } + } + } +} + // Variadic creates an [iter.Seq] from variadic elements. +// +// Example: +// +// source.Variadic(1,2,3) => seq: 1,2,3 func Variadic[E any](elems ...E) iter.Seq[E] { return func(yield func(E) bool) { for _, elem := range elems { @@ -16,17 +67,50 @@ func Variadic[E any](elems ...E) iter.Seq[E] { } } -// Gen creates an [iter.Seq] that yield number in range. +// Range1 creates an [iter.Seq] that yield number in range. +// +// The range start from 0, but not includes [stop]. +// +// If `stop` less-equal to 0, yield nothing as [Empty]. +// +// Example: +// +// source.Range1(0) => seq: +// source.Range1(2) => seq: 0,1 +func Range1[N constraints.Integer](stop N) iter.Seq[N] { + return Range3(0, stop, 1) +} + +// Range2 creates an [iter.Seq] that yield number in range. +// +// The range includes [start], but not includes [stop]. +// +// If `stop` less-equal than `start`, yield nothing as [Empty]. +// +// Example: +// +// source.Range2(0, 3) => seq: 0,1,2 +// source.Range2(2, 5) => seq: 2,3,4 +func Range2[N constraints.Integer](start N, stop N) iter.Seq[N] { + return Range3(start, stop, 1) +} + +// Range3 creates an [iter.Seq] that yield number in range every [step]. // -// If `to` less-equal than `from`, yield nothing as [Empty]. +// The range includes [start], but not includes [stop]. +// +// If `stop` less-equal than `start` or `step` less-than 1, yield nothing as [Empty]. // // Example: // -// source.Gen(2,5) => seq: 2,3,4 -// source.Gen(2,1) => seq: -func Gen[N core.Integer](from N, to N) iter.Seq[N] { +// source.Range3(0, 10, 3) => seq: 0,3,6,9 +// source.Range3(2, 5, 0) => seq: +func Range3[N constraints.Integer](start N, stop N, step N) iter.Seq[N] { return func(yield func(N) bool) { - for i := from; i < to; i++ { + if start >= stop || step < 1 { + return + } + for i := start; i < stop; i += step { if !yield(i) { break } @@ -48,8 +132,12 @@ func Repeat[E any](v E) iter.Seq[E] { } // RepeatTimes creates an [iter.Seq] that repeats a single element a given number of times. -func RepeatTimes[E any, N core.Integer](v E, n N) iter.Seq[E] { - return iter.Map(Gen(0, n), func(N) E { return v }) +// +// Example: +// +// source.RepeatTimes(1, 3) => seq: 1,1,1 +func RepeatTimes[E any, N constraints.Integer](v E, n N) iter.Seq[E] { + return iter.Map(Range1(n), func(N) E { return v }) } // Empty creates an [iter.Seq] that yields nothing. diff --git a/iter/source/source_test.go b/iter/source/source_test.go index f03bbbe..2becf2e 100644 --- a/iter/source/source_test.go +++ b/iter/source/source_test.go @@ -17,8 +17,21 @@ func collect[E any](s iter.Seq[E]) []E { return rs } -func TestGen(t *testing.T) { - qt.Assert(t, collect(source.Gen(2, 5)), qt.DeepEquals, []int{2, 3, 4}) +func TestRange(t *testing.T) { + t.Run("range 1", func(t *testing.T) { + qt.Assert(t, collect(source.Range1(3)), qt.DeepEquals, []int{0, 1, 2}) + qt.Assert(t, collect(source.Range1(0)), qt.DeepEquals, []int{}) + }) + t.Run("range 2", func(t *testing.T) { + qt.Assert(t, collect(source.Range2(1, 3)), qt.DeepEquals, []int{1, 2}) + qt.Assert(t, collect(source.Range2(3, 1)), qt.DeepEquals, []int{}) + qt.Assert(t, collect(source.Range2(3, 3)), qt.DeepEquals, []int{}) + }) + t.Run("range 3", func(t *testing.T) { + qt.Assert(t, collect(source.Range3(1, 3, 1)), qt.DeepEquals, []int{1, 2}) + qt.Assert(t, collect(source.Range3(0, 10, 3)), qt.DeepEquals, []int{0, 3, 6, 9}) + qt.Assert(t, collect(source.Range3(0, 5, 0)), qt.DeepEquals, []int{}) + }) } func TestRepeatTimes(t *testing.T) { diff --git a/maps/maps.go b/maps/maps.go index 0ab4a35..daf1181 100644 --- a/maps/maps.go +++ b/maps/maps.go @@ -9,8 +9,8 @@ import ( // MapEntry persist k-v pair of a map. type MapEntry[K, V any] struct{ inner tuple.Pair[K, V] } -func (e MapEntry[K, V]) Key() K { return e.inner.First } -func (e MapEntry[K, V]) Value() V { return e.inner.Second } +func (e MapEntry[K, V]) Key() K { return e.inner.First() } +func (e MapEntry[K, V]) Value() V { return e.inner.Second() } func entry[K, V any](key K, value V) MapEntry[K, V] { return MapEntry[K, V]{inner: tuple.MakePair(key, value)} @@ -70,6 +70,10 @@ func Collect[K comparable, V any](s iter.Seq[MapEntry[K, V]]) map[K]V { return collector.Collect(s, collector.ToMap(extract)) } +func CollectMap[K comparable, V any, E any](s iter.Seq[E], f func(E) (K, V)) map[K]V { + return collector.Collect(s, collector.ToMap(f)) +} + // ForEach iter over the map, and call the udf on each k-v pair. func ForEach[K comparable, V any, M ~map[K]V](m M, f func(K, V)) { iter.ForEach(Entries(m), func(x MapEntry[K, V]) { f(x.Key(), x.Value()) }) diff --git a/operator/operator.go b/operator/operator.go index 3b7e502..43948f7 100644 --- a/operator/operator.go +++ b/operator/operator.go @@ -2,14 +2,14 @@ package operator import ( "github.com/go-board/std/cond" - "github.com/go-board/std/core" + "github.com/go-board/std/constraints" ) // Identify return self. func Identify[E any](x E) E { return x } // Neg return negative value of input value. -func Neg[T core.Numeric](v T) T { return -v } +func Neg[T constraints.Numeric](v T) T { return -v } // Eq test two value whether equal or not. // @@ -24,22 +24,22 @@ func Ne[T comparable](lhs T, rhs T) bool { return lhs != rhs } // Lt test whether lhs less than rhs value. // // if lhs less than rhs, return true, otherwise return false. -func Lt[T core.Ordered](lhs T, rhs T) bool { return lhs < rhs } +func Lt[T constraints.Ordered](lhs T, rhs T) bool { return lhs < rhs } // Le test whether lhs less than or equal to rhs value. // // if lhs less than or equal to rhs, return true, otherwise return false. -func Le[T core.Ordered](lhs T, rhs T) bool { return lhs <= rhs } +func Le[T constraints.Ordered](lhs T, rhs T) bool { return lhs <= rhs } // Gt test whether lhs greater than rhs value. // // if lhs greater than rhs, return true, otherwise return false. -func Gt[T core.Ordered](lhs T, rhs T) bool { return lhs > rhs } +func Gt[T constraints.Ordered](lhs T, rhs T) bool { return lhs > rhs } // Ge test whether lhs greater than or equal to rhs value. // // if lhs greater than or equal to rhs, return true, otherwise return false. -func Ge[T core.Ordered](lhs T, rhs T) bool { return lhs >= rhs } +func Ge[T constraints.Ordered](lhs T, rhs T) bool { return lhs >= rhs } // And act same as lhs && rhs. func And[T ~bool](lhs T, rhs T) T { return lhs && rhs } @@ -60,64 +60,64 @@ func OrAssign[T ~bool](lhs *T, rhs T) { *lhs = *lhs || rhs } func NotAssign[T ~bool](v *T) { *v = !*v } // Add act same as lhs + rhs. -func Add[T core.Numeric | ~string](lhs T, rhs T) T { return lhs + rhs } +func Add[T constraints.Numeric | ~string](lhs T, rhs T) T { return lhs + rhs } // Sub act same as lhs - rhs. -func Sub[T core.Numeric](lhs T, rhs T) T { return lhs - rhs } +func Sub[T constraints.Numeric](lhs T, rhs T) T { return lhs - rhs } // Mul act same as lhs * rhs. -func Mul[T core.Numeric](lhs T, rhs T) T { return lhs * rhs } +func Mul[T constraints.Numeric](lhs T, rhs T) T { return lhs * rhs } // Div act same as lhs / rhs. -func Div[T core.Numeric](lhs T, rhs T) T { return lhs / rhs } +func Div[T constraints.Numeric](lhs T, rhs T) T { return lhs / rhs } // Rem act same as lhs % rhs. -func Rem[T core.Integer](lhs T, rhs T) T { return lhs % rhs } +func Rem[T constraints.Integer](lhs T, rhs T) T { return lhs % rhs } // AddAssign act same as lhs += rhs in place. -func AddAssign[T core.Numeric | ~string](lhs *T, rhs T) { *lhs += rhs } +func AddAssign[T constraints.Numeric | ~string](lhs *T, rhs T) { *lhs += rhs } // SubAssign act same as lhs -= rhs in place. -func SubAssign[T core.Numeric](lhs *T, rhs T) { *lhs -= rhs } +func SubAssign[T constraints.Numeric](lhs *T, rhs T) { *lhs -= rhs } // MulAssign act same as lhs *= rhs in place. -func MulAssign[T core.Numeric](lhs *T, rhs T) { *lhs *= rhs } +func MulAssign[T constraints.Numeric](lhs *T, rhs T) { *lhs *= rhs } // DivAssign act same as lhs /= rhs in place. -func DivAssign[T core.Numeric](lhs *T, rhs T) { *lhs /= rhs } +func DivAssign[T constraints.Numeric](lhs *T, rhs T) { *lhs /= rhs } // RemAssign act same as lhs %= rhs in place. -func RemAssign[T core.Integer](lhs *T, rhs T) { *lhs %= rhs } +func RemAssign[T constraints.Integer](lhs *T, rhs T) { *lhs %= rhs } // BitAnd act same as lhs & rhs. -func BitAnd[T core.Integer](lhs T, rhs T) T { return lhs & rhs } +func BitAnd[T constraints.Integer](lhs T, rhs T) T { return lhs & rhs } // BitOr act same as lhs | rhs. -func BitOr[T core.Integer](lhs T, rhs T) T { return lhs | rhs } +func BitOr[T constraints.Integer](lhs T, rhs T) T { return lhs | rhs } // BitXor act same as lhs ^ rhs. -func BitXor[T core.Integer](lhs T, rhs T) T { return lhs ^ rhs } +func BitXor[T constraints.Integer](lhs T, rhs T) T { return lhs ^ rhs } // BitAndAssign act same as lhs &= rhs in place. -func BitAndAssign[T core.Integer](lhs *T, rhs T) { *lhs &= rhs } +func BitAndAssign[T constraints.Integer](lhs *T, rhs T) { *lhs &= rhs } // BitOrAssign act same as lhs |= rhs in place. -func BitOrAssign[T core.Integer](lhs *T, rhs T) { *lhs |= rhs } +func BitOrAssign[T constraints.Integer](lhs *T, rhs T) { *lhs |= rhs } // BitXorAssign act same as lhs ^= rhs in place. -func BitXorAssign[T core.Integer](lhs *T, rhs T) { *lhs ^= rhs } +func BitXorAssign[T constraints.Integer](lhs *T, rhs T) { *lhs ^= rhs } // Shl act same as v << bit. -func Shl[T core.Integer, S core.Integer](v T, bit S) T { return v << bit } +func Shl[T constraints.Integer, S constraints.Integer](v T, bit S) T { return v << bit } // Shr act same as v >> bit. -func Shr[T core.Integer, S core.Integer](v T, bit S) T { return v >> bit } +func Shr[T constraints.Integer, S constraints.Integer](v T, bit S) T { return v >> bit } // ShlAssign act same as v <<= bit. -func ShlAssign[T core.Integer, S core.Integer](v *T, bit S) { *v = *v << bit } +func ShlAssign[T constraints.Integer, S constraints.Integer](v *T, bit S) { *v = *v << bit } // ShrAssign act same as v >>= bit. -func ShrAssign[T core.Integer, S core.Integer](v *T, bit S) { *v = *v >> bit } +func ShrAssign[T constraints.Integer, S constraints.Integer](v *T, bit S) { *v = *v >> bit } // Exchange change order of the two value. func Exchange[Lhs, Rhs any](lhs Lhs, rhs Rhs) (Rhs, Lhs) { return rhs, lhs } @@ -135,7 +135,7 @@ func LazyTernary[T any](ok bool, lhs func() T, rhs func() T) T { } // RangeInteger returns a slice that contains all integers in [start, end). -func RangeInteger[T core.Integer](start T, end T) []T { +func RangeInteger[T constraints.Integer](start T, end T) []T { if end < start { return nil } @@ -147,7 +147,7 @@ func RangeInteger[T core.Integer](start T, end T) []T { } // Max return maximum of the given values. -func Max[T core.Ordered](elem T, rest ...T) T { +func Max[T constraints.Ordered](elem T, rest ...T) T { max := elem for _, e := range rest { if e > max { @@ -158,7 +158,7 @@ func Max[T core.Ordered](elem T, rest ...T) T { } // Min return minimum of the given values. -func Min[T core.Ordered](elem T, rest ...T) T { +func Min[T constraints.Ordered](elem T, rest ...T) T { min := elem for _, e := range rest { if e < min { diff --git a/optional/optional.go b/optional/optional.go index dbb6acf..f9dad28 100644 --- a/optional/optional.go +++ b/optional/optional.go @@ -27,6 +27,17 @@ func Some[T any](data T) Optional[T] { return Optional[T]{data: &data} } // None returns an Optional from a value. func None[T any]() Optional[T] { return Optional[T]{} } +func (self Optional[T]) Compare(other Optional[T], cmp func(T, T) int) int { + if self.IsSome() && other.IsSome() { + return cmp(self.Value(), other.Value()) + } else if self.IsSome() { + return +1 + } else if other.IsSome() { + return -1 + } + return 0 +} + func (self Optional[T]) String() string { if self.IsSome() { return fmt.Sprintf("Some(%+v)", *self.data) @@ -48,6 +59,13 @@ func (self Optional[T]) IsSomeAnd(f func(T) bool) bool { return false } +func (self Optional[T]) Expect(msg string) T { + if self.IsSome() { + return *self.data + } + panic(msg) +} + // Value returns the value of the Optional. func (self Optional[T]) Value() T { if self.IsSome() { @@ -128,22 +146,6 @@ func (self Optional[T]) Map(f func(T) T) Optional[T] { return Map(self, f) } -// MapOr returns None if the option is None, otherwise calls the given function and returns the result. -func (self Optional[T]) MapOr(v T, f func(T) T) T { - if self.IsSome() { - return f(self.Value()) - } - return v -} - -// MapOrElse returns None if the option is None, otherwise calls the given function and returns the result. -func (self Optional[T]) MapOrElse(df func() T, f func(T) T) T { - if self.IsSome() { - return f(self.Value()) - } - return df() -} - // Xor returns None if the option is None, otherwise returns the given opt. func (self Optional[T]) Xor(opt Optional[T]) Optional[T] { if self.IsSome() && opt.IsNone() { @@ -172,6 +174,13 @@ func (self Optional[T]) CloneBy(clone func(T) T) Optional[T] { return Some(clone(self.Value())) } +func (self Optional[T]) Get() (data T, ok bool) { + if self.IsSome() { + data, ok = self.Value(), true + } + return +} + func (self Optional[T]) MarshalJSON() ([]byte, error) { if self.IsSome() { return json.Marshal(*self.data) @@ -202,7 +211,18 @@ func Map[A, B any](opt Optional[A], f func(A) B) Optional[B] { // if the value is not convertible to the given type, // then the function returns None, // otherwise, it returns Some. +// +// Deprecated: use [TypeAssert] instead. func As[T any](v any) Optional[T] { + return TypeAssert[T](v) +} + +// TypeAssert try to convert the value to the given type. +// +// if the value is not convertible to the given type, +// then the function returns None, +// otherwise, it returns Some. +func TypeAssert[T any](v any) Optional[T] { val, ok := v.(T) return FromPair(val, ok) } diff --git a/optional/optional_test.go b/optional/optional_test.go index 8c477fd..14f99f8 100644 --- a/optional/optional_test.go +++ b/optional/optional_test.go @@ -111,16 +111,4 @@ func TestMap(t *testing.T) { y := optional.None[int]() c.Assert(y.Map(func(i int) int { return i * 2 }).IsNone(), quicktest.IsTrue) }) - a.Run("map_or", func(c *quicktest.C) { - x := optional.Some(100) - c.Assert(x.MapOr(300, func(i int) int { return i * 2 }), quicktest.Equals, 200) - y := optional.None[int]() - c.Assert(y.MapOr(300, func(i int) int { return i * 2 }), quicktest.Equals, 300) - }) - a.Run("map_or_else", func(c *quicktest.C) { - x := optional.Some(100) - c.Assert(x.MapOrElse(func() int { return 300 }, func(i int) int { return i * 2 }), quicktest.Equals, 200) - y := optional.None[int]() - c.Assert(y.MapOrElse(func() int { return 300 }, func(i int) int { return i * 2 }), quicktest.Equals, 300) - }) } diff --git a/result/result.go b/result/result.go index 31aba6b..fbce9fa 100644 --- a/result/result.go +++ b/result/result.go @@ -35,9 +35,23 @@ func (self Result[T]) String() string { // IsOk returns true if the Result is Ok. func (self Result[T]) IsOk() bool { return self.err == nil } +func (self Result[T]) IsOkAnd(f func(T) bool) bool { + if self.IsOk() { + return f(self.Value()) + } + return false +} + // IsErr returns true if the Result is Err. func (self Result[T]) IsErr() bool { return !self.IsOk() } +func (self Result[T]) IsErrAnd(f func(err error) bool) bool { + if self.IsErr() { + return f(self.Error()) + } + return false +} + // And return self if self is error, otherwise return other. func (self Result[T]) And(res Result[T]) Result[T] { if self.IsOk() { @@ -46,6 +60,13 @@ func (self Result[T]) And(res Result[T]) Result[T] { return Err[T](self.err) } +func (self Result[T]) AndThen(f func(T) Result[T]) Result[T] { + if self.IsOk() { + return f(self.Value()) + } + return self +} + // Or return self if self is ok, otherwise return other. func (self Result[T]) Or(res Result[T]) Result[T] { if self.IsOk() { @@ -54,10 +75,15 @@ func (self Result[T]) Or(res Result[T]) Result[T] { return res } +func (self Result[T]) OrElse(f func(error) Result[T]) Result[T] { + if self.IsErr() { + return f(self.Error()) + } + return self +} + // Map returns a new Result from the result of applying // the given function to the value of self if ok, else return err. -// -// FIXME: for now, go doesn't support type parameter on method, so we have to use the same type parameter on the method. func (self Result[T]) Map(fn func(T) T) Result[T] { if self.IsOk() { return Ok(fn(self.data)) @@ -92,12 +118,11 @@ func (self Result[T]) ValueOrElse(f func() T) T { // ValueOrZero returns the value of the Result if it is Ok, // otherwise return the zero value of the type of the Result. -func (self Result[T]) ValueOrZero() T { +func (self Result[T]) ValueOrZero() (empty T) { if self.IsOk() { return self.data } - var t T - return t + return } // Error unwrap the error of the Result, panic if the Result is Ok. @@ -108,6 +133,22 @@ func (self Result[T]) Error() error { panic("unwrap ok value") } +// ErrorOr returns error or default error. +func (self Result[T]) ErrorOr(v error) error { + if self.IsErr() { + return self.Error() + } + return v +} + +// ErrorOrElse returns error or create a new error use given function. +func (self Result[T]) ErrorOrElse(f func() error) error { + if self.IsErr() { + return self.Error() + } + return f() +} + // IfOk call the function if the Result is Ok. func (self Result[T]) IfOk(f func(T)) { if self.IsOk() { @@ -122,8 +163,8 @@ func (self Result[T]) IfErr(f func(error)) { } } -// AsRawParts return tuple of value ptr and error. -func (self Result[T]) AsRawParts() (data T, err error) { +// Get return tuple of value ptr and error. +func (self Result[T]) Get() (data T, err error) { if self.IsOk() { data = self.data } else { diff --git a/sets/set.go b/sets/set.go index 09e80d5..add4b0c 100644 --- a/sets/set.go +++ b/sets/set.go @@ -1,16 +1,17 @@ package sets import ( - "github.com/go-board/std/core" "github.com/go-board/std/iter" ) -var unit = struct{}{} +type _Unit = struct{} -type HashSet[E comparable] struct{ inner map[E]core.Unit } +var unit _Unit + +type HashSet[E comparable] struct{ inner map[E]_Unit } func New[E comparable]() HashSet[E] { - return HashSet[E]{inner: make(map[E]core.Unit)} + return HashSet[E]{inner: make(map[E]_Unit)} } // FromSlice returns a new empty hash set. @@ -95,10 +96,10 @@ func (self HashSet[E]) IsEmpty() bool { return self.Size() == 0 } -func (self HashSet[E]) ToMap() map[E]struct{} { - m := make(map[E]struct{}) +func (self HashSet[E]) ToMap() map[E]_Unit { + m := make(map[E]_Unit) for k := range self.inner { - m[k] = struct{}{} + m[k] = unit } return m } diff --git a/slices/slice.go b/slices/slice.go index c0a3382..8594e94 100644 --- a/slices/slice.go +++ b/slices/slice.go @@ -7,8 +7,9 @@ import ( "github.com/go-board/std/clone" "github.com/go-board/std/cmp" "github.com/go-board/std/collections/ordered" - "github.com/go-board/std/core" + "github.com/go-board/std/constraints" "github.com/go-board/std/iter" + "github.com/go-board/std/iter/collector" "github.com/go-board/std/operator" "github.com/go-board/std/optional" "github.com/go-board/std/result" @@ -86,20 +87,8 @@ func Any[T any, S ~[]T](slice S, f func(T) bool) bool { // slices.Chunk(seq(1,2,3,4,5,6,7,8,9), 3) => [][]int{{1,2,3}, {4,5,6}, {7,8,9}} // slices.Chunk(seq(1,2,3,4,5,6,7), 3) => [][]int{{1,2,3}, {4,5,6}, {7}} func Chunk[T any, S ~[]T](slice S, chunk int) []S { - size := len(slice) - res := make([]S, 0, len(slice)/chunk+1) - for i := 0; i < size; i += chunk { - if i+chunk > size { - tmp := make([]T, len(slice[i:])) - copy(tmp, slice[i:]) - res = append(res, tmp) - } else { - tmp := make([]T, chunk) - copy(tmp, slice[i:i+chunk]) - res = append(res, tmp) - } - } - return res + x := collector.Collect(Forward(slice), collector.Chunk[T](chunk)) + return Collect(iter.Map(x, func(e iter.Seq[T]) S { return Collect(e) })) } // Clone returns a new slice with the same elements as the given slice. @@ -107,6 +96,14 @@ func Clone[T any, S ~[]T](slice S) S { return Collect(Forward(slice)) } +func CountValue[T comparable, S ~[]T](slice S, value T) int { + return iter.Count(Forward(slice), value) +} + +func CountBy[T any, S ~[]T](slice S, f func(T) bool) int { + return iter.CountFunc(Forward(slice), f) +} + // DeepClone returns a new slice with the cloned elements. func DeepClone[T clone.Cloneable[T], S ~[]T](slice S) S { return DeepCloneBy(slice, func(t T) T { return t.Clone() }) @@ -180,15 +177,20 @@ func Filter[T any, S ~[]T](slice S, f func(T) bool) S { // FilterIndexed returns a new slice with all elements that satisfy the given predicate. func FilterIndexed[T any, S ~[]T](slice S, f func(T, int) bool) S { - return Collect( - iter.Map( - iter.Filter( - iter.Enumerate(Forward(slice)), - func(t tuple.Pair[int, T]) bool { return f(t.Second, t.First) }, - ), - func(e tuple.Pair[int, T]) T { return e.Second }, - ), - ) + enumerated := iter.Enumerate(Forward(slice)) + filter := iter.Filter(enumerated, func(t tuple.Pair[int, T]) bool { return f(t.Second(), t.First()) }) + mapped := iter.Map(filter, func(e tuple.Pair[int, T]) T { return e.Second() }) + return Collect(mapped) +} + +func FilterMap[A, B any, S ~[]A](slice S, f func(A) (B, bool)) []B { + return Collect(iter.FilterMap(Forward(slice), f)) +} + +func FilterMapIndexed[A, B any, S ~[]A](slice S, f func(A, int) (B, bool)) []B { + return Collect(iter.FilterMap(iter.Enumerate(Forward(slice)), func(t tuple.Pair[int, A]) (B, bool) { + return f(t.Second(), t.First()) + })) } // Flatten returns a new slice with all elements in the given slice and all elements in all sub-slices. @@ -213,12 +215,20 @@ func FlatMap[T, E any, S ~[]T](slice S, f func(T) []E) []E { // Returns the final accum value or initial value if the slice is empty. // If error occurred, return error early. func TryFold[T, A any, S ~[]T](slice S, initial A, accumulator func(A, T) (A, error)) (res A, err error) { - return iter.TryFold(Forward(slice), initial, accumulator) + return TryFoldLeft(slice, initial, accumulator) } // Fold accumulates value starting with initial value and applying accumulator from left to right to current accum value and each element. // Returns the final accum value or initial value if the slice is empty. func Fold[T, A any, S ~[]T](slice S, initial A, accumulator func(A, T) A) A { + return FoldLeft(slice, initial, accumulator) +} + +func TryFoldLeft[T, A any, S ~[]T](slice S, initial A, accumulator func(A, T) (A, error)) (A, error) { + return iter.TryFold(Forward(slice), initial, accumulator) +} + +func FoldLeft[T, A any, S ~[]T](slice S, initial A, accumulator func(A, T) A) A { return iter.Fold(Forward(slice), initial, accumulator) } @@ -239,16 +249,18 @@ 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 tuple.Pair[int, T]) { f(t.Second, t.First) }) + 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. func GroupBy[T any, TKey comparable, S ~[]T](slice S, f func(T) TKey) map[TKey]S { + x := collector.Collect(Forward(slice), collector.GroupBy(f)) res := make(map[TKey]S) - for _, v := range slice { - key := f(v) - res[key] = append(res[key], v) - } + iter.ForEach(x, func(t tuple.Pair[TKey, iter.Seq[T]]) { + iter.ForEach(t.Second(), func(e T) { + res[t.First()] = append(res[t.First()], e) + }) + }) return res } @@ -326,7 +338,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 tuple.Pair[int, T]) U { return f(x.Second, x.First) })) + 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. @@ -335,16 +347,16 @@ func MapIndexed[T, U any, S ~[]T](slice S, f func(T, int) U) []U { // // slices.Max([]int{1,2,1}) => 2 func Max[T cmp.Ordered, S ~[]T](slice S) optional.Optional[T] { - return optional.FromPair(iter.Max(Forward(slice))) + return iter.MaxOption(Forward(slice)) } // MaxBy returns the maximum element in the given slice that satisfies the given function. func MaxBy[T any, S ~[]T](slice S, f func(T, T) int) optional.Optional[T] { - return optional.FromPair(iter.MaxFunc(Forward(slice), f)) + return iter.MaxFuncOption(Forward(slice), f) } func MaxByKey[T any, K cmp.Ordered, S ~[]T](slice S, keyFn func(T) K) optional.Optional[T] { - return optional.FromPair(iter.MaxByKey(Forward(slice), keyFn)) + return iter.MaxByKeyOption(Forward(slice), keyFn) } // Min returns the minimum element in the given slice. @@ -352,17 +364,17 @@ func MaxByKey[T any, K cmp.Ordered, S ~[]T](slice S, keyFn func(T) K) optional.O // Example: // // slices.Min([]int{1,2,1}) => 1 -func Min[T core.Ordered, S ~[]T](slice S) optional.Optional[T] { - return optional.FromPair(iter.Min(Forward(slice))) +func Min[T constraints.Ordered, S ~[]T](slice S) optional.Optional[T] { + return iter.MinOption(Forward(slice)) } // MinBy returns the minimum element in the given slice that satisfies the given function. func MinBy[T any, S ~[]T](slice S, f func(T, T) int) optional.Optional[T] { - return optional.FromPair(iter.MinFunc(Forward(slice), f)) + return iter.MinFuncOption(Forward(slice), f) } func MinByKey[T any, K cmp.Ordered, S ~[]T](slice S, keyFn func(T) K) optional.Optional[T] { - return optional.FromPair(iter.MinByKey(Forward(slice), keyFn)) + return iter.MinByKeyOption(Forward(slice), keyFn) } // None returns true if no element in the given slice satisfies the given predicate. @@ -375,10 +387,7 @@ func None[T any, S ~[]T](slice S, f func(T) bool) bool { // If n is negative, it returns the last element plus one. // If n is greater than the length of the slice, it returns [optional.None]. func Nth[T any, S ~[]T](slice S, n int) optional.Optional[T] { - if n < 0 || n >= len(slice) { - return optional.None[T]() - } - return optional.Some(slice[n]) + return iter.NthOption(Forward(slice), n) } // Partition split slice into two slices according to a predicate. @@ -391,8 +400,8 @@ func Nth[T any, S ~[]T](slice S, n int) optional.Optional[T] { // Partition([]int{1, 2, 3}, func(s int) bool { return s % 2 == 0 }) // returns: ([2], [1, 3]) func Partition[T any, S ~[]T](slice S, f func(T) bool) (S, S) { - groups := GroupBy(slice, func(t T) bool { return f(t) }) - return groups[true], groups[false] + x := iter.Partition(Forward(slice), f) + return Collect(x.First()), Collect(x.Second()) } // Reduce returns the result of applying the given function to each element in the given slice. @@ -401,12 +410,16 @@ func Partition[T any, S ~[]T](slice S, f func(T) bool) (S, S) { // // slices.Reduce([]int{1,2,3}, func(x, y int) int {return x+y}) => 6 func Reduce[T any, S ~[]T](slice S, f func(T, T) T) optional.Optional[T] { - return optional.FromPair(iter.Reduce(Forward(slice), f)) + return ReduceLeft(slice, f) +} + +func ReduceLeft[T any, S ~[]T](slice S, f func(T, T) T) optional.Optional[T] { + return iter.ReduceOption(Forward(slice), f) } // ReduceRight returns the result of applying the given function to each element in the given slice. func ReduceRight[T any, S ~[]T](slice S, f func(T, T) T) optional.Optional[T] { - return optional.FromPair(iter.Reduce(Backward(slice), f)) + return iter.ReduceOption(Backward(slice), f) } // Reverse returns a new slice with the elements in the given slice in reverse order. @@ -468,20 +481,20 @@ func ToHashSet[T comparable, S ~[]T](slice S) map[T]struct{} { // First finds first element func First[T any, S ~[]T](slice S) optional.Optional[T] { - return optional.FromPair(iter.First(Forward(slice), func(T) bool { return true })) + return iter.FirstOption(Forward(slice), func(T) bool { return true }) } func FirstFunc[E any, S ~[]E](slice S, f func(E) bool) optional.Optional[E] { - return optional.FromPair(iter.First(Forward(slice), f)) + return iter.FirstOption(Forward(slice), f) } // Last finds last element func Last[T any, S ~[]T](slice S) optional.Optional[T] { - return optional.FromPair(iter.Last(Forward(slice), func(T) bool { return true })) + return iter.LastOption(Forward(slice), func(T) bool { return true }) } func LastFunc[E any, S ~[]E](slice S, f func(E) bool) optional.Optional[E] { - return optional.FromPair(iter.Last(Forward(slice), f)) + return iter.LastOption(Forward(slice), f) } // SpliceFirst return first element and rest if len > 0, else return (None, []T) @@ -522,7 +535,5 @@ func SpliceLast[T any, S ~[]T](slice S) (optional.Optional[T], S) { // interface: nil // chan/map/slice: nil func FirstNonZero[T comparable, S ~[]T](slice S) T { - var zero T - e, _ := iter.First(Forward(slice), func(t T) bool { return t != zero }) - return e + return iter.HeadOption(iter.FilterZero(Forward(slice))).ValueOrZero() } diff --git a/slices/slice_1_20.go b/slices/slice_1_20.go index eab2cf7..cc57390 100644 --- a/slices/slice_1_20.go +++ b/slices/slice_1_20.go @@ -6,7 +6,7 @@ import ( "sort" "github.com/go-board/std/cmp" - "github.com/go-board/std/core" + constraints "github.com/go-board/std/constraints" "github.com/go-board/std/iter" "github.com/go-board/std/operator" "github.com/go-board/std/optional" @@ -46,18 +46,18 @@ func SortBy[T any, S ~[]T](slice S, cmp func(lhs, rhs T) int) S { } // Sort sorts the given slice in-place. -func Sort[T core.Ordered, S ~[]T](slice S) S { +func Sort[T constraints.Ordered, S ~[]T](slice S) S { return SortBy(slice, cmp.Compare[T]) } // IsSorted returns true if the given slice is sorted. -func IsSorted[T core.Ordered, S ~[]T](slice S) bool { - return IsSortedBy(slice, cmp.Compare[T]) +func IsSorted[T constraints.Ordered, S ~[]T](slice S) bool { + return iter.IsSorted(Forward(slice), cmp.Compare[T]) } // IsSortedBy returns true if the given slice is sorted by the given less function. func IsSortedBy[T any, S ~[]T](slice S, cmp func(lhs, rhs T) int) bool { - return iter.IsSortedFunc(ForwardSeq(slice), cmp) + return iter.IsSortedFunc(Forward(slice), cmp) } // Index returns the index of the first element in the given slice that same with the given element. @@ -67,21 +67,19 @@ func Index[T comparable, S ~[]T](slice S, v T) optional.Optional[int] { // IndexBy returns the index of the first element in the given slice that satisfies the given predicate. func IndexBy[T any, S ~[]T](slice S, predicate func(T) bool) optional.Optional[int] { - for i, v := range slice { - if predicate(v) { - return optional.Some(i) - } + x := iter.IndexFirstFunc(Forward(slice), predicate) + if x < 0 { + return optional.None[int]() } - return optional.None[int]() + return optional.Some(x) } // Contains returns true if the given slice contains the given element. func Contains[T comparable, S ~[]T](slice S, v T) bool { - return Index(slice, v).IsSome() + return iter.Contains(Forward(slice), v) } // ContainsBy returns true if the given slice contains an element that satisfies the given predicate. func ContainsBy[T any, S ~[]T](slice S, predicate func(T) bool) bool { - _, ok := iter.Find(ForwardSeq(slice), predicate) - return ok + return iter.ContainsFunc(Forward(slice), predicate) } diff --git a/slices/slice_test.go b/slices/slice_test.go index d4befa0..278d28a 100644 --- a/slices/slice_test.go +++ b/slices/slice_test.go @@ -18,6 +18,11 @@ func (i item) Compare(o item) int { return cmp.Compare(i.Value, o.Value) } +func TestBackward(t *testing.T) { + x := slices.Backward([]int{1, 2, 3}) + qt.Assert(t, slices.Collect(x), qt.DeepEquals, []int{3, 2, 1}) +} + func TestSort(t *testing.T) { t.Run("sort_by", func(t *testing.T) { t.Run("comparable", func(t *testing.T) { diff --git a/sync/sync.go b/sync/sync.go index 1215156..08c222e 100644 --- a/sync/sync.go +++ b/sync/sync.go @@ -3,7 +3,9 @@ package sync import ( "sync" + "github.com/go-board/std/iter" "github.com/go-board/std/optional" + "github.com/go-board/std/tuple" ) // Map is generic version of sync.Map. @@ -14,16 +16,11 @@ func (self *Map[K, V]) Insert(key K, v V) { } func (self *Map[K, V]) Get(key K) (V, bool) { - val, ok := self.inner.Load(key) - if !ok { - var t V - return t, false - } - return val.(V), true + return self.GetOptional(key).Get() } func (self *Map[K, V]) GetOptional(key K) optional.Optional[V] { - return optional.FromPair(self.Get(key)) + return optional.Map(optional.FromPair(self.inner.Load(key)), func(x any) V { return x.(V) }) } func (self *Map[K, V]) GetOrPut(key K, val V) (V, bool) { @@ -39,6 +36,24 @@ func (self *Map[K, V]) Range(fn func(K, V) bool) { self.inner.Range(func(key, value any) bool { return fn(key.(K), value.(V)) }) } +func (self *Map[K, V]) Keys() iter.Seq[K] { + return func(yield func(K) bool) { + self.Range(func(k K, v V) bool { return yield(k) }) + } +} + +func (self *Map[K, V]) Values() iter.Seq[V] { + return func(yield func(V) bool) { + self.Range(func(k K, v V) bool { return yield(v) }) + } +} + +func (self *Map[K, V]) Entries() iter.Seq[tuple.Pair[K, V]] { + return func(yield func(tuple.Pair[K, V]) bool) { + self.Range(func(k K, v V) bool { return yield(tuple.MakePair(k, v)) }) + } +} + func (self *Map[K, V]) Delete(key K) { self.inner.Delete(key) } diff --git a/tuple/tuple.go b/tuple/tuple.go index 9540a27..ad3cab1 100644 --- a/tuple/tuple.go +++ b/tuple/tuple.go @@ -2,143 +2,264 @@ package tuple import ( "fmt" + + "github.com/go-board/std/cmp" ) // Pair is a tuple of two elements. type Pair[A, B any] struct { - First A - Second B + first A + second B } func (self Pair[A, B]) String() string { - return fmt.Sprintf("Pair(%+v, %+v)", self.First, self.Second) + return fmt.Sprintf("Pair(%+v, %+v)", self.first, self.second) } +func (self Pair[A, B]) Unpack() (A, B) { return self.first, self.second } +func (self Pair[A, B]) Clone() Pair[A, B] { return MakePair(self.first, self.second) } +func (self Pair[A, B]) First() A { return self.first } +func (self Pair[A, B]) Second() B { return self.second } + // MakePair create a pair from a and b. func MakePair[A, B any](a A, b B) Pair[A, B] { return Pair[A, B]{a, b} } +func PairComparator[A, B any](cmpA func(A, A) int, cmpB func(B, B) int) cmp.Comparator[Pair[A, B]] { + return cmp.MakeComparatorFunc(func(lhs Pair[A, B], rhs Pair[A, B]) int { + if c := cmpA(lhs.first, rhs.first); c != 0 { + return c + } + return cmpB(lhs.second, rhs.second) + }) +} + // Triple is a tuple of three elements. type Triple[A, B, C any] struct { - First A - Second B - Third C + first A + second B + third C } func (self Triple[A, B, C]) String() string { - return fmt.Sprintf("Triple(%+v, %+v, %+v)", self.First, self.Second, self.Third) + return fmt.Sprintf("Triple(%+v, %+v, %+v)", self.first, self.second, self.third) +} + +func (self Triple[A, B, C]) Unpack() (A, B, C) { + return self.first, self.second, self.third +} + +func (self Triple[A, B, C]) Clone() Triple[A, B, C] { + return MakeTriple(self.first, self.second, self.third) } +func (self Triple[A, B, C]) First() A { return self.first } +func (self Triple[A, B, C]) Second() B { return self.second } +func (self Triple[A, B, C]) Third() C { return self.third } + func MakeTriple[A, B, C any](a A, b B, c C) Triple[A, B, C] { return Triple[A, B, C]{a, b, c} } // Tuple4 is a tuple of four elements. type Tuple4[A, B, C, D any] struct { - First A - Second B - Third C - Fourth D + first A + second B + third C + fourth D } func (self Tuple4[A, B, C, D]) String() string { - return fmt.Sprintf("Tuple4(%+v, %+v, %+v, %+v)", self.First, self.Second, self.Third, self.Fourth) + return fmt.Sprintf("Tuple4(%+v, %+v, %+v, %+v)", self.first, self.second, self.third, self.fourth) +} + +func (self Tuple4[A, B, C, D]) Unpack() (A, B, C, D) { + return self.first, self.second, self.third, self.fourth +} + +func (self Tuple4[A, B, C, D]) Clone() Tuple4[A, B, C, D] { + return MakeTuple4(self.first, self.second, self.third, self.fourth) } +func (self Tuple4[A, B, C, D]) First() A { return self.first } +func (self Tuple4[A, B, C, D]) Second() B { return self.second } +func (self Tuple4[A, B, C, D]) Third() C { return self.third } +func (self Tuple4[A, B, C, D]) Fourth() D { return self.fourth } + func MakeTuple4[A, B, C, D any](a A, b B, c C, d D) Tuple4[A, B, C, D] { return Tuple4[A, B, C, D]{a, b, c, d} } // Tuple5 is a tuple of five elements. type Tuple5[A, B, C, D, E any] struct { - First A - Second B - Third C - Fourth D - Fifth E + first A + second B + third C + fourth D + fifth E } func (self Tuple5[A, B, C, D, E]) String() string { - return fmt.Sprintf("Tuple5(%+v, %+v, %+v, %+v, %+v)", self.First, self.Second, self.Third, self.Fourth, self.Fifth) + return fmt.Sprintf("Tuple5(%+v, %+v, %+v, %+v, %+v)", self.first, self.second, self.third, self.fourth, self.fifth) +} + +func (self Tuple5[A, B, C, D, E]) Unpack() (A, B, C, D, E) { + return self.first, self.second, self.third, self.fourth, self.fifth +} + +func (self Tuple5[A, B, C, D, E]) Clone() Tuple5[A, B, C, D, E] { + return MakeTuple5(self.first, self.second, self.third, self.fourth, self.fifth) } +func (self Tuple5[A, B, C, D, E]) First() A { return self.first } +func (self Tuple5[A, B, C, D, E]) Second() B { return self.second } +func (self Tuple5[A, B, C, D, E]) Third() C { return self.third } +func (self Tuple5[A, B, C, D, E]) Fourth() D { return self.fourth } +func (self Tuple5[A, B, C, D, E]) Fifth() E { return self.fifth } + func MakeTuple5[A, B, C, D, E any](a A, b B, c C, d D, e E) Tuple5[A, B, C, D, E] { return Tuple5[A, B, C, D, E]{a, b, c, d, e} } // Tuple6 is a tuple of six elements. type Tuple6[A, B, C, D, E, F any] struct { - First A - Second B - Third C - Fourth D - Fifth E - Sixth F + first A + second B + third C + fourth D + fifth E + sixth F } func (self Tuple6[A, B, C, D, E, F]) String() string { - return fmt.Sprintf("Tuple6(%+v, %+v, %+v, %+v, %+v, %+v)", self.First, self.Second, self.Third, self.Fourth, self.Fifth, self.Sixth) + return fmt.Sprintf("Tuple6(%+v, %+v, %+v, %+v, %+v, %+v)", self.first, self.second, self.third, self.fourth, self.fifth, self.sixth) } +func (self Tuple6[A, B, C, D, E, F]) Unpack() (A, B, C, D, E, F) { + return self.first, self.second, self.third, self.fourth, self.fifth, self.sixth +} + +func (self Tuple6[A, B, C, D, E, F]) Clone() Tuple6[A, B, C, D, E, F] { + return MakeTuple6(self.first, self.second, self.third, self.fourth, self.fifth, self.sixth) +} + +func (self Tuple6[A, B, C, D, E, F]) First() A { return self.first } +func (self Tuple6[A, B, C, D, E, F]) Second() B { return self.second } +func (self Tuple6[A, B, C, D, E, F]) Third() C { return self.third } +func (self Tuple6[A, B, C, D, E, F]) Fourth() D { return self.fourth } +func (self Tuple6[A, B, C, D, E, F]) Fifth() E { return self.fifth } +func (self Tuple6[A, B, C, D, E, F]) Sixth() F { return self.sixth } + func MakeTuple6[A, B, C, D, E, F any](a A, b B, c C, d D, e E, f F) Tuple6[A, B, C, D, E, F] { return Tuple6[A, B, C, D, E, F]{a, b, c, d, e, f} } // Tuple7 is a tuple of seven elements. type Tuple7[A, B, C, D, E, F, G any] struct { - First A - Second B - Third C - Fourth D - Fifth E - Sixth F - Seventh G + first A + second B + third C + fourth D + fifth E + sixth F + seventh G } func (self Tuple7[A, B, C, D, E, F, G]) String() string { - return fmt.Sprintf("Tuple7(%+v, %+v, %+v, %+v, %+v, %+v, %+v)", self.First, self.Second, self.Third, self.Fourth, self.Fifth, self.Sixth, self.Seventh) + return fmt.Sprintf("Tuple7(%+v, %+v, %+v, %+v, %+v, %+v, %+v)", self.first, self.second, self.third, self.fourth, self.fifth, self.sixth, self.seventh) +} + +func (self Tuple7[A, B, C, D, E, F, G]) Unpack() (A, B, C, D, E, F, G) { + return self.first, self.second, self.third, self.fourth, self.fifth, self.sixth, self.seventh +} + +func (self Tuple7[A, B, C, D, E, F, G]) Clone() Tuple7[A, B, C, D, E, F, G] { + return MakeTuple7(self.first, self.second, self.third, self.fourth, self.fifth, self.sixth, self.seventh) } +func (self Tuple7[A, B, C, D, E, F, G]) First() A { return self.first } +func (self Tuple7[A, B, C, D, E, F, G]) Second() B { return self.second } +func (self Tuple7[A, B, C, D, E, F, G]) Third() C { return self.third } +func (self Tuple7[A, B, C, D, E, F, G]) Fourth() D { return self.fourth } +func (self Tuple7[A, B, C, D, E, F, G]) Fifth() E { return self.fifth } +func (self Tuple7[A, B, C, D, E, F, G]) Sixth() F { return self.sixth } +func (self Tuple7[A, B, C, D, E, F, G]) Seventh() G { return self.seventh } + func MakeTuple7[A, B, C, D, E, F, G any](a A, b B, c C, d D, e E, f F, g G) Tuple7[A, B, C, D, E, F, G] { return Tuple7[A, B, C, D, E, F, G]{a, b, c, d, e, f, g} } // Tuple8 is a tuple of eight elements. type Tuple8[A, B, C, D, E, F, G, H any] struct { - First A - Second B - Third C - Fourth D - Fifth E - Sixth F - Seventh G - Eighth H + first A + second B + third C + fourth D + fifth E + sixth F + seventh G + eighth H } func (self Tuple8[A, B, C, D, E, F, G, H]) String() string { - return fmt.Sprintf("Tuple8(%+v, %+v, %+v, %+v, %+v, %+v, %+v, %+v)", self.First, self.Second, self.Third, self.Fourth, self.Fifth, self.Sixth, self.Seventh, self.Eighth) + return fmt.Sprintf("Tuple8(%+v, %+v, %+v, %+v, %+v, %+v, %+v, %+v)", self.first, self.second, self.third, self.fourth, self.fifth, self.sixth, self.seventh, self.eighth) } +func (self Tuple8[A, B, C, D, E, F, G, H]) Unpack() (A, B, C, D, E, F, G, H) { + return self.first, self.second, self.third, self.fourth, self.fifth, self.sixth, self.seventh, self.eighth +} + +func (self Tuple8[A, B, C, D, E, F, G, H]) Clone() Tuple8[A, B, C, D, E, F, G, H] { + return MakeTuple8(self.first, self.second, self.third, self.fourth, self.fifth, self.sixth, self.seventh, self.eighth) +} + +func (self Tuple8[A, B, C, D, E, F, G, H]) First() A { return self.first } +func (self Tuple8[A, B, C, D, E, F, G, H]) Second() B { return self.second } +func (self Tuple8[A, B, C, D, E, F, G, H]) Third() C { return self.third } +func (self Tuple8[A, B, C, D, E, F, G, H]) Fourth() D { return self.fourth } +func (self Tuple8[A, B, C, D, E, F, G, H]) Fifth() E { return self.fifth } +func (self Tuple8[A, B, C, D, E, F, G, H]) Sixth() F { return self.sixth } +func (self Tuple8[A, B, C, D, E, F, G, H]) Seventh() G { return self.seventh } +func (self Tuple8[A, B, C, D, E, F, G, H]) Eighth() H { return self.eighth } + func MakeTuple8[A, B, C, D, E, F, G, H any](a A, b B, c C, d D, e E, f F, g G, h H) Tuple8[A, B, C, D, E, F, G, H] { return Tuple8[A, B, C, D, E, F, G, H]{a, b, c, d, e, f, g, h} } // Tuple9 is a tuple of nine elements. type Tuple9[A, B, C, D, E, F, G, H, I any] struct { - First A - Second B - Third C - Fourth D - Fifth E - Sixth F - Seventh G - Eighth H - Ninth I + first A + second B + third C + fourth D + fifth E + sixth F + seventh G + eighth H + ninth I } func (self Tuple9[A, B, C, D, E, F, G, H, I]) String() string { - return fmt.Sprintf("Tuple9(%+v, %+v, %+v, %+v, %+v, %+v, %+v, %+v, %+v)", self.First, self.Second, self.Third, self.Fourth, self.Fifth, self.Sixth, self.Seventh, self.Eighth, self.Ninth) + return fmt.Sprintf("Tuple9(%+v, %+v, %+v, %+v, %+v, %+v, %+v, %+v, %+v)", self.first, self.second, self.third, self.fourth, self.fifth, self.sixth, self.seventh, self.eighth, self.ninth) } +func (self Tuple9[A, B, C, D, E, F, G, H, I]) Unpack() (A, B, C, D, E, F, G, H, I) { + return self.first, self.second, self.third, self.fourth, self.fifth, self.sixth, self.seventh, self.eighth, self.ninth +} + +func (self Tuple9[A, B, C, D, E, F, G, H, I]) Clone() Tuple9[A, B, C, D, E, F, G, H, I] { + return MakeTuple9(self.first, self.second, self.third, self.fourth, self.fifth, self.sixth, self.seventh, self.eighth, self.ninth) +} + +func (self Tuple9[A, B, C, D, E, F, G, H, I]) First() A { return self.first } +func (self Tuple9[A, B, C, D, E, F, G, H, I]) Second() B { return self.second } +func (self Tuple9[A, B, C, D, E, F, G, H, I]) Third() C { return self.third } +func (self Tuple9[A, B, C, D, E, F, G, H, I]) Fourth() D { return self.fourth } +func (self Tuple9[A, B, C, D, E, F, G, H, I]) Fifth() E { return self.fifth } +func (self Tuple9[A, B, C, D, E, F, G, H, I]) Sixth() F { return self.sixth } +func (self Tuple9[A, B, C, D, E, F, G, H, I]) Seventh() G { return self.seventh } +func (self Tuple9[A, B, C, D, E, F, G, H, I]) Eighth() H { return self.eighth } +func (self Tuple9[A, B, C, D, E, F, G, H, I]) Ninth() I { return self.ninth } + func MakeTuple9[A, B, C, D, E, F, G, H, I any](a A, b B, c C, d D, e E, f F, g G, h H, i I) Tuple9[A, B, C, D, E, F, G, H, I] { return Tuple9[A, B, C, D, E, F, G, H, I]{a, b, c, d, e, f, g, h, i} }