mirror of
https://github.com/IBM/fp-go.git
synced 2025-11-25 22:21:49 +02:00
initial checkin
Signed-off-by: Dr. Carsten Leue <carsten.leue@de.ibm.com>
This commit is contained in:
279
array/array.go
Normal file
279
array/array.go
Normal file
@@ -0,0 +1,279 @@
|
||||
package array
|
||||
|
||||
import (
|
||||
G "github.com/ibm/fp-go/array/generic"
|
||||
F "github.com/ibm/fp-go/function"
|
||||
"github.com/ibm/fp-go/internal/array"
|
||||
M "github.com/ibm/fp-go/monoid"
|
||||
O "github.com/ibm/fp-go/option"
|
||||
"github.com/ibm/fp-go/tuple"
|
||||
)
|
||||
|
||||
// From constructs an array from a set of variadic arguments
|
||||
func From[A any](data ...A) []A {
|
||||
return G.From[[]A](data...)
|
||||
}
|
||||
|
||||
// MakeBy returns a `Array` of length `n` with element `i` initialized with `f(i)`.
|
||||
func MakeBy[A any](n int, f func(int) A) []A {
|
||||
// sanity check
|
||||
if n <= 0 {
|
||||
return Empty[A]()
|
||||
}
|
||||
// run the generator function across the input
|
||||
as := make([]A, n)
|
||||
for i := n - 1; i >= 0; i-- {
|
||||
as[i] = f(i)
|
||||
}
|
||||
return as
|
||||
}
|
||||
|
||||
// Replicate creates a `Array` containing a value repeated the specified number of times.
|
||||
func Replicate[A any](n int, a A) []A {
|
||||
return MakeBy(n, F.Constant1[int](a))
|
||||
}
|
||||
|
||||
func MonadMap[A, B any](as []A, f func(a A) B) []B {
|
||||
return G.MonadMap[[]A, []B](as, f)
|
||||
}
|
||||
|
||||
func MonadMapRef[A, B any](as []A, f func(a *A) B) []B {
|
||||
count := len(as)
|
||||
bs := make([]B, count)
|
||||
for i := count - 1; i >= 0; i-- {
|
||||
bs[i] = f(&as[i])
|
||||
}
|
||||
return bs
|
||||
}
|
||||
|
||||
func Map[A, B any](f func(a A) B) func([]A) []B {
|
||||
return F.Bind2nd(MonadMap[A, B], f)
|
||||
}
|
||||
|
||||
func MapRef[A, B any](f func(a *A) B) func([]A) []B {
|
||||
return F.Bind2nd(MonadMapRef[A, B], f)
|
||||
}
|
||||
|
||||
func filter[A any](fa []A, pred func(A) bool) []A {
|
||||
var result []A
|
||||
count := len(fa)
|
||||
for i := 0; i < count; i++ {
|
||||
a := fa[i]
|
||||
if pred(a) {
|
||||
result = append(result, a)
|
||||
}
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
func filterRef[A any](fa []A, pred func(a *A) bool) []A {
|
||||
var result []A
|
||||
count := len(fa)
|
||||
for i := 0; i < count; i++ {
|
||||
a := fa[i]
|
||||
if pred(&a) {
|
||||
result = append(result, a)
|
||||
}
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
func filterMapRef[A, B any](fa []A, pred func(a *A) bool, f func(a *A) B) []B {
|
||||
var result []B
|
||||
count := len(fa)
|
||||
for i := 0; i < count; i++ {
|
||||
a := fa[i]
|
||||
if pred(&a) {
|
||||
result = append(result, f(&a))
|
||||
}
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
func Filter[A any](pred func(A) bool) func([]A) []A {
|
||||
return F.Bind2nd(filter[A], pred)
|
||||
}
|
||||
|
||||
func FilterRef[A any](pred func(*A) bool) func([]A) []A {
|
||||
return F.Bind2nd(filterRef[A], pred)
|
||||
}
|
||||
|
||||
func MonadFilterMap[A, B any](fa []A, f func(a A) O.Option[B]) []B {
|
||||
return G.MonadFilterMap[[]A, []B](fa, f)
|
||||
}
|
||||
|
||||
func FilterMap[A, B any](f func(a A) O.Option[B]) func([]A) []B {
|
||||
return G.FilterMap[[]A, []B](f)
|
||||
}
|
||||
|
||||
func FilterMapRef[A, B any](pred func(a *A) bool, f func(a *A) B) func([]A) []B {
|
||||
return func(fa []A) []B {
|
||||
return filterMapRef(fa, pred, f)
|
||||
}
|
||||
}
|
||||
|
||||
func reduceRef[A, B any](fa []A, f func(B, *A) B, initial B) B {
|
||||
current := initial
|
||||
count := len(fa)
|
||||
for i := 0; i < count; i++ {
|
||||
current = f(current, &fa[i])
|
||||
}
|
||||
return current
|
||||
}
|
||||
|
||||
func Reduce[A, B any](f func(B, A) B, initial B) func([]A) B {
|
||||
return func(as []A) B {
|
||||
return array.Reduce(as, f, initial)
|
||||
}
|
||||
}
|
||||
|
||||
func ReduceRef[A, B any](f func(B, *A) B, initial B) func([]A) B {
|
||||
return func(as []A) B {
|
||||
return reduceRef(as, f, initial)
|
||||
}
|
||||
}
|
||||
|
||||
func Append[A any](as []A, a A) []A {
|
||||
return G.Append(as, a)
|
||||
}
|
||||
|
||||
func IsEmpty[A any](as []A) bool {
|
||||
return array.IsEmpty(as)
|
||||
}
|
||||
|
||||
func IsNonEmpty[A any](as []A) bool {
|
||||
return len(as) > 0
|
||||
}
|
||||
|
||||
func Empty[A any]() []A {
|
||||
return G.Empty[[]A]()
|
||||
}
|
||||
|
||||
func Zero[A any]() []A {
|
||||
return Empty[A]()
|
||||
}
|
||||
|
||||
// Of constructs a single element array
|
||||
func Of[A any](a A) []A {
|
||||
return G.Of[[]A](a)
|
||||
}
|
||||
|
||||
func MonadChain[A, B any](fa []A, f func(a A) []B) []B {
|
||||
return array.Reduce(fa, func(bs []B, a A) []B {
|
||||
return append(bs, f(a)...)
|
||||
}, Zero[B]())
|
||||
}
|
||||
|
||||
func Chain[A, B any](f func(a A) []B) func([]A) []B {
|
||||
return F.Bind2nd(MonadChain[A, B], f)
|
||||
}
|
||||
|
||||
func MonadAp[A, B any](fab []func(A) B, fa []A) []B {
|
||||
return MonadChain(fab, F.Bind1st(MonadMap[A, B], fa))
|
||||
}
|
||||
|
||||
func Ap[A, B any](fa []A) func([]func(A) B) []B {
|
||||
return F.Bind2nd(MonadAp[A, B], fa)
|
||||
}
|
||||
|
||||
func Match[A, B any](onEmpty func() B, onNonEmpty func([]A) B) func([]A) B {
|
||||
return func(as []A) B {
|
||||
if IsEmpty(as) {
|
||||
return onEmpty()
|
||||
}
|
||||
return onNonEmpty(as)
|
||||
}
|
||||
}
|
||||
|
||||
func Tail[A any](as []A) O.Option[[]A] {
|
||||
return G.Tail(as)
|
||||
}
|
||||
|
||||
func Head[A any](as []A) O.Option[A] {
|
||||
return G.Head(as)
|
||||
}
|
||||
|
||||
func First[A any](as []A) O.Option[A] {
|
||||
return G.First(as)
|
||||
}
|
||||
|
||||
func Last[A any](as []A) O.Option[A] {
|
||||
return G.Last(as)
|
||||
}
|
||||
|
||||
func PrependAll[A any](middle A) func([]A) []A {
|
||||
return func(as []A) []A {
|
||||
count := len(as)
|
||||
dst := count * 2
|
||||
result := make([]A, dst)
|
||||
for i := count - 1; i >= 0; i-- {
|
||||
dst--
|
||||
result[dst] = as[i]
|
||||
dst--
|
||||
result[dst] = middle
|
||||
}
|
||||
return result
|
||||
}
|
||||
}
|
||||
|
||||
func Intersperse[A any](middle A) func([]A) []A {
|
||||
prepend := PrependAll(middle)
|
||||
return func(as []A) []A {
|
||||
if IsEmpty(as) {
|
||||
return as
|
||||
}
|
||||
return prepend(as)[1:]
|
||||
}
|
||||
}
|
||||
|
||||
func Intercalate[A any](m M.Monoid[A]) func(A) func([]A) A {
|
||||
concatAll := ConcatAll[A](m)(m.Empty())
|
||||
return func(middle A) func([]A) A {
|
||||
return Match(m.Empty, F.Flow2(Intersperse(middle), concatAll))
|
||||
}
|
||||
}
|
||||
|
||||
func Flatten[A any](mma [][]A) []A {
|
||||
return MonadChain(mma, F.Identity[[]A])
|
||||
}
|
||||
|
||||
func Slice[A any](low, high int) func(as []A) []A {
|
||||
return array.Slice[[]A](low, high)
|
||||
}
|
||||
|
||||
func Lookup[A any](idx int) func([]A) O.Option[A] {
|
||||
return G.Lookup[[]A](idx)
|
||||
}
|
||||
|
||||
func UpsertAt[A any](a A) func([]A) []A {
|
||||
return G.UpsertAt[[]A](a)
|
||||
}
|
||||
|
||||
func Size[A any](as []A) int {
|
||||
return G.Size(as)
|
||||
}
|
||||
|
||||
func MonadPartition[A any](as []A, pred func(A) bool) tuple.Tuple2[[]A, []A] {
|
||||
return G.MonadPartition(as, pred)
|
||||
}
|
||||
|
||||
// Partition creates two new arrays out of one, the left result contains the elements
|
||||
// for which the predicate returns false, the right one those for which the predicate returns true
|
||||
func Partition[A any](pred func(A) bool) func([]A) tuple.Tuple2[[]A, []A] {
|
||||
return G.Partition[[]A](pred)
|
||||
}
|
||||
|
||||
// IsNil checks if the array is set to nil
|
||||
func IsNil[A any](as []A) bool {
|
||||
return array.IsNil(as)
|
||||
}
|
||||
|
||||
// IsNonNil checks if the array is set to nil
|
||||
func IsNonNil[A any](as []A) bool {
|
||||
return array.IsNonNil(as)
|
||||
}
|
||||
|
||||
// ConstNil returns a nil array
|
||||
func ConstNil[A any]() []A {
|
||||
return array.ConstNil[[]A]()
|
||||
}
|
||||
129
array/array_test.go
Normal file
129
array/array_test.go
Normal file
@@ -0,0 +1,129 @@
|
||||
package array
|
||||
|
||||
import (
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
F "github.com/ibm/fp-go/function"
|
||||
"github.com/ibm/fp-go/internal/utils"
|
||||
O "github.com/ibm/fp-go/option"
|
||||
S "github.com/ibm/fp-go/string"
|
||||
T "github.com/ibm/fp-go/tuple"
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
func TestMap1(t *testing.T) {
|
||||
|
||||
src := []string{"a", "b", "c"}
|
||||
|
||||
up := Map(strings.ToUpper)(src)
|
||||
|
||||
var up1 = []string{}
|
||||
for _, s := range src {
|
||||
up1 = append(up1, strings.ToUpper(s))
|
||||
}
|
||||
|
||||
var up2 = []string{}
|
||||
for i := range src {
|
||||
up2 = append(up2, strings.ToUpper(src[i]))
|
||||
}
|
||||
|
||||
assert.Equal(t, up, up1)
|
||||
assert.Equal(t, up, up2)
|
||||
}
|
||||
|
||||
func TestMap(t *testing.T) {
|
||||
|
||||
mapper := Map(utils.Upper)
|
||||
|
||||
src := []string{"a", "b", "c"}
|
||||
|
||||
dst := mapper(src)
|
||||
|
||||
assert.Equal(t, dst, []string{"A", "B", "C"})
|
||||
}
|
||||
|
||||
func TestReduce(t *testing.T) {
|
||||
|
||||
values := MakeBy(101, F.Identity[int])
|
||||
|
||||
sum := func(val int, current int) int {
|
||||
return val + current
|
||||
}
|
||||
reducer := Reduce(sum, 0)
|
||||
|
||||
result := reducer(values)
|
||||
assert.Equal(t, result, 5050)
|
||||
|
||||
}
|
||||
|
||||
func TestEmpty(t *testing.T) {
|
||||
assert.True(t, IsNonEmpty(MakeBy(101, F.Identity[int])))
|
||||
assert.True(t, IsEmpty([]int{}))
|
||||
}
|
||||
|
||||
func TestAp(t *testing.T) {
|
||||
assert.Equal(t,
|
||||
[]int{2, 4, 6, 3, 6, 9},
|
||||
F.Pipe1(
|
||||
[]func(int) int{
|
||||
utils.Double,
|
||||
utils.Triple,
|
||||
},
|
||||
Ap[int, int]([]int{1, 2, 3}),
|
||||
),
|
||||
)
|
||||
}
|
||||
|
||||
func TestIntercalate(t *testing.T) {
|
||||
is := Intercalate(S.Monoid)("-")
|
||||
|
||||
assert.Equal(t, "", is(Empty[string]()))
|
||||
assert.Equal(t, "a", is([]string{"a"}))
|
||||
assert.Equal(t, "a-b-c", is([]string{"a", "b", "c"}))
|
||||
assert.Equal(t, "a--c", is([]string{"a", "", "c"}))
|
||||
assert.Equal(t, "a-b", is([]string{"a", "b"}))
|
||||
assert.Equal(t, "a-b-c-d", is([]string{"a", "b", "c", "d"}))
|
||||
}
|
||||
|
||||
func TestPrependAll(t *testing.T) {
|
||||
empty := Empty[int]()
|
||||
prep := PrependAll(0)
|
||||
assert.Equal(t, empty, prep(empty))
|
||||
assert.Equal(t, []int{0, 1, 0, 2, 0, 3}, prep([]int{1, 2, 3}))
|
||||
assert.Equal(t, []int{0, 1}, prep([]int{1}))
|
||||
assert.Equal(t, []int{0, 1, 0, 2, 0, 3, 0, 4}, prep([]int{1, 2, 3, 4}))
|
||||
}
|
||||
|
||||
func TestFlatten(t *testing.T) {
|
||||
assert.Equal(t, []int{1, 2, 3}, Flatten([][]int{{1}, {2}, {3}}))
|
||||
}
|
||||
|
||||
func TestLookup(t *testing.T) {
|
||||
data := []int{0, 1, 2}
|
||||
none := O.None[int]()
|
||||
|
||||
assert.Equal(t, none, Lookup[int](-1)(data))
|
||||
assert.Equal(t, none, Lookup[int](10)(data))
|
||||
assert.Equal(t, O.Some(1), Lookup[int](1)(data))
|
||||
}
|
||||
|
||||
func TestSlice(t *testing.T) {
|
||||
data := []int{0, 1, 2, 3}
|
||||
|
||||
assert.Equal(t, []int{1, 2}, Slice[int](1, 3)(data))
|
||||
}
|
||||
|
||||
func TestFrom(t *testing.T) {
|
||||
assert.Equal(t, []int{1, 2, 3}, From(1, 2, 3))
|
||||
}
|
||||
|
||||
func TestPartition(t *testing.T) {
|
||||
|
||||
pred := func(n int) bool {
|
||||
return n > 2
|
||||
}
|
||||
|
||||
assert.Equal(t, T.MakeTuple2(Empty[int](), Empty[int]()), Partition(pred)(Empty[int]()))
|
||||
assert.Equal(t, T.MakeTuple2(From(1), From(3)), Partition(pred)(From(1, 3)))
|
||||
}
|
||||
25
array/eq.go
Normal file
25
array/eq.go
Normal file
@@ -0,0 +1,25 @@
|
||||
package array
|
||||
|
||||
import (
|
||||
E "github.com/ibm/fp-go/eq"
|
||||
)
|
||||
|
||||
func equals[T any](left []T, right []T, eq func(T, T) bool) bool {
|
||||
if len(left) != len(right) {
|
||||
return false
|
||||
}
|
||||
for i, v1 := range left {
|
||||
v2 := right[i]
|
||||
if !eq(v1, v2) {
|
||||
return false
|
||||
}
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
func Eq[T any](e E.Eq[T]) E.Eq[[]T] {
|
||||
eq := e.Equals
|
||||
return E.FromEquals(func(left, right []T) bool {
|
||||
return equals(left, right, eq)
|
||||
})
|
||||
}
|
||||
109
array/generic/array.go
Normal file
109
array/generic/array.go
Normal file
@@ -0,0 +1,109 @@
|
||||
package generic
|
||||
|
||||
import (
|
||||
F "github.com/ibm/fp-go/function"
|
||||
"github.com/ibm/fp-go/internal/array"
|
||||
O "github.com/ibm/fp-go/option"
|
||||
"github.com/ibm/fp-go/tuple"
|
||||
)
|
||||
|
||||
// Of constructs a single element array
|
||||
func Of[GA ~[]A, A any](value A) GA {
|
||||
return GA{value}
|
||||
}
|
||||
|
||||
// From constructs an array from a set of variadic arguments
|
||||
func From[GA ~[]A, A any](data ...A) GA {
|
||||
return data
|
||||
}
|
||||
|
||||
func Lookup[GA ~[]A, A any](idx int) func(GA) O.Option[A] {
|
||||
none := O.None[A]()
|
||||
if idx < 0 {
|
||||
return F.Constant1[GA](none)
|
||||
}
|
||||
return func(as GA) O.Option[A] {
|
||||
if idx < len(as) {
|
||||
return O.Some(as[idx])
|
||||
}
|
||||
return none
|
||||
}
|
||||
}
|
||||
|
||||
func Tail[GA ~[]A, A any](as GA) O.Option[GA] {
|
||||
if array.IsEmpty(as) {
|
||||
return O.None[GA]()
|
||||
}
|
||||
return O.Some(as[1:])
|
||||
}
|
||||
|
||||
func Head[GA ~[]A, A any](as GA) O.Option[A] {
|
||||
if array.IsEmpty(as) {
|
||||
return O.None[A]()
|
||||
}
|
||||
return O.Some(as[0])
|
||||
}
|
||||
|
||||
func First[GA ~[]A, A any](as GA) O.Option[A] {
|
||||
return Head(as)
|
||||
}
|
||||
|
||||
func Last[GA ~[]A, A any](as GA) O.Option[A] {
|
||||
if array.IsEmpty(as) {
|
||||
return O.None[A]()
|
||||
}
|
||||
return O.Some(as[len(as)-1])
|
||||
}
|
||||
|
||||
func Append[GA ~[]A, A any](as GA, a A) GA {
|
||||
return array.Append(as, a)
|
||||
}
|
||||
|
||||
func Empty[GA ~[]A, A any]() GA {
|
||||
return array.Empty[GA]()
|
||||
}
|
||||
|
||||
func UpsertAt[GA ~[]A, A any](a A) func(GA) GA {
|
||||
return array.UpsertAt[GA](a)
|
||||
}
|
||||
|
||||
func MonadMap[GA ~[]A, GB ~[]B, A, B any](as GA, f func(a A) B) GB {
|
||||
return array.MonadMap[GA, GB](as, f)
|
||||
}
|
||||
|
||||
func Size[GA ~[]A, A any](as GA) int {
|
||||
return len(as)
|
||||
}
|
||||
|
||||
func filterMap[GA ~[]A, GB ~[]B, A, B any](fa GA, f func(a A) O.Option[B]) GB {
|
||||
return array.Reduce(fa, func(bs GB, a A) GB {
|
||||
return O.MonadFold(f(a), F.Constant(bs), F.Bind1st(Append[GB, B], bs))
|
||||
}, Empty[GB]())
|
||||
}
|
||||
|
||||
func MonadFilterMap[GA ~[]A, GB ~[]B, A, B any](fa GA, f func(a A) O.Option[B]) GB {
|
||||
return filterMap[GA, GB](fa, f)
|
||||
}
|
||||
|
||||
func FilterMap[GA ~[]A, GB ~[]B, A, B any](f func(a A) O.Option[B]) func(GA) GB {
|
||||
return F.Bind2nd(MonadFilterMap[GA, GB, A, B], f)
|
||||
}
|
||||
|
||||
func MonadPartition[GA ~[]A, A any](as GA, pred func(A) bool) tuple.Tuple2[GA, GA] {
|
||||
left := Empty[GA]()
|
||||
right := Empty[GA]()
|
||||
array.Reduce(as, func(c bool, a A) bool {
|
||||
if pred(a) {
|
||||
right = append(right, a)
|
||||
} else {
|
||||
left = append(left, a)
|
||||
}
|
||||
return c
|
||||
}, true)
|
||||
// returns the partition
|
||||
return tuple.MakeTuple2(left, right)
|
||||
}
|
||||
|
||||
func Partition[GA ~[]A, A any](pred func(A) bool) func(GA) tuple.Tuple2[GA, GA] {
|
||||
return F.Bind2nd(MonadPartition[GA, A], pred)
|
||||
}
|
||||
26
array/generic/sort.go
Normal file
26
array/generic/sort.go
Normal file
@@ -0,0 +1,26 @@
|
||||
package generic
|
||||
|
||||
import (
|
||||
"sort"
|
||||
|
||||
O "github.com/ibm/fp-go/ord"
|
||||
)
|
||||
|
||||
// Sort implements a stable sort on the array given the provided ordering
|
||||
func Sort[GA ~[]T, T any](ord O.Ord[T]) func(ma GA) GA {
|
||||
|
||||
return func(ma GA) GA {
|
||||
// nothing to sort
|
||||
l := len(ma)
|
||||
if l < 2 {
|
||||
return ma
|
||||
}
|
||||
// copy
|
||||
cpy := make(GA, l)
|
||||
copy(cpy, ma)
|
||||
sort.Slice(cpy, func(i, j int) bool {
|
||||
return ord.Compare(cpy[i], cpy[j]) < 0
|
||||
})
|
||||
return cpy
|
||||
}
|
||||
}
|
||||
10
array/magma.go
Normal file
10
array/magma.go
Normal file
@@ -0,0 +1,10 @@
|
||||
package array
|
||||
|
||||
import (
|
||||
F "github.com/ibm/fp-go/function"
|
||||
M "github.com/ibm/fp-go/magma"
|
||||
)
|
||||
|
||||
func ConcatAll[A any](m M.Magma[A]) func(A) func([]A) A {
|
||||
return F.Bind1st(Reduce[A, A], m.Concat)
|
||||
}
|
||||
21
array/magma_test.go
Normal file
21
array/magma_test.go
Normal file
@@ -0,0 +1,21 @@
|
||||
package array
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
|
||||
M "github.com/ibm/fp-go/magma"
|
||||
)
|
||||
|
||||
var subInt = M.MakeMagma(func(first int, second int) int {
|
||||
return first - second
|
||||
})
|
||||
|
||||
func TestConcatAll(t *testing.T) {
|
||||
|
||||
var subAll = ConcatAll(subInt)(0)
|
||||
|
||||
assert.Equal(t, subAll([]int{1, 2, 3}), -6)
|
||||
|
||||
}
|
||||
43
array/monoid.go
Normal file
43
array/monoid.go
Normal file
@@ -0,0 +1,43 @@
|
||||
package array
|
||||
|
||||
import (
|
||||
"github.com/ibm/fp-go/internal/array"
|
||||
M "github.com/ibm/fp-go/monoid"
|
||||
)
|
||||
|
||||
func concat[T any](left, right []T) []T {
|
||||
// some performance checks
|
||||
ll := len(left)
|
||||
lr := len(right)
|
||||
if ll == 0 {
|
||||
return right
|
||||
}
|
||||
if lr == 0 {
|
||||
return left
|
||||
}
|
||||
// need to copy
|
||||
buf := make([]T, ll+lr)
|
||||
copy(buf[copy(buf, left):], right)
|
||||
return buf
|
||||
}
|
||||
|
||||
func Monoid[T any]() M.Monoid[[]T] {
|
||||
return M.MakeMonoid(concat[T], Empty[T]())
|
||||
}
|
||||
|
||||
func addLen[A any](count int, data []A) int {
|
||||
return count + len(data)
|
||||
}
|
||||
|
||||
// ConcatAll efficiently concatenates the input arrays into a final array
|
||||
func ArrayConcatAll[A any](data ...[]A) []A {
|
||||
// get the full size
|
||||
count := array.Reduce(data, addLen[A], 0)
|
||||
buf := make([]A, count)
|
||||
// copy
|
||||
array.Reduce(data, func(idx int, seg []A) int {
|
||||
return idx + copy(buf[idx:], seg)
|
||||
}, 0)
|
||||
// returns the final array
|
||||
return buf
|
||||
}
|
||||
11
array/monoid_test.go
Normal file
11
array/monoid_test.go
Normal file
@@ -0,0 +1,11 @@
|
||||
package array
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
M "github.com/ibm/fp-go/monoid/testing"
|
||||
)
|
||||
|
||||
func TestMonoid(t *testing.T) {
|
||||
M.AssertLaws(t, Monoid[int]())([][]int{{}, {1}, {1, 2}})
|
||||
}
|
||||
43
array/sequence.go
Normal file
43
array/sequence.go
Normal file
@@ -0,0 +1,43 @@
|
||||
package array
|
||||
|
||||
import (
|
||||
F "github.com/ibm/fp-go/function"
|
||||
O "github.com/ibm/fp-go/option"
|
||||
)
|
||||
|
||||
// We need to pass the members of the applicative explicitly, because golang does neither support higher kinded types nor template methods on structs or interfaces
|
||||
|
||||
// HKTA = HKT<A>
|
||||
// HKTRA = HKT<[]A>
|
||||
// HKTFRA = HKT<func(A)[]A>
|
||||
|
||||
// Sequence takes an `Array` where elements are `HKT<A>` (higher kinded type) and,
|
||||
// using an applicative of that `HKT`, returns an `HKT` of `[]A`.
|
||||
// e.g. it can turn an `[]Either[error, string]` into an `Either[error, []string]`.
|
||||
//
|
||||
// Sequence requires an `Applicative` of the `HKT` you are targeting, e.g. to turn an
|
||||
// `[]Either[E, A]` into an `Either[E, []A]`, it needs an
|
||||
// Applicative` for `Either`, to to turn an `[]Option[A]` into an `Option[ []A]`,
|
||||
// it needs an `Applicative` for `Option`.
|
||||
func Sequence[A, HKTA, HKTRA, HKTFRA any](
|
||||
_of func([]A) HKTRA,
|
||||
_map func(HKTRA, func([]A) func(A) []A) HKTFRA,
|
||||
_ap func(HKTFRA, HKTA) HKTRA,
|
||||
) func([]HKTA) HKTRA {
|
||||
ca := F.Curry2(Append[A])
|
||||
return Reduce(func(fas HKTRA, fa HKTA) HKTRA {
|
||||
return _ap(
|
||||
_map(fas, ca),
|
||||
fa,
|
||||
)
|
||||
}, _of(Empty[A]()))
|
||||
}
|
||||
|
||||
// ArrayOption returns a function to convert sequence of options into an option of a sequence
|
||||
func ArrayOption[A any]() func([]O.Option[A]) O.Option[[]A] {
|
||||
return Sequence(
|
||||
O.Of[[]A],
|
||||
O.MonadMap[[]A, func(A) []A],
|
||||
O.MonadAp[A, []A],
|
||||
)
|
||||
}
|
||||
16
array/sequence_test.go
Normal file
16
array/sequence_test.go
Normal file
@@ -0,0 +1,16 @@
|
||||
package array
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
|
||||
O "github.com/ibm/fp-go/option"
|
||||
)
|
||||
|
||||
func TestSequenceOption(t *testing.T) {
|
||||
seq := ArrayOption[int]()
|
||||
|
||||
assert.Equal(t, O.Of([]int{1, 3}), seq([]O.Option[int]{O.Of(1), O.Of(3)}))
|
||||
assert.Equal(t, O.None[[]int](), seq([]O.Option[int]{O.Of(1), O.None[int]()}))
|
||||
}
|
||||
11
array/sort.go
Normal file
11
array/sort.go
Normal file
@@ -0,0 +1,11 @@
|
||||
package array
|
||||
|
||||
import (
|
||||
G "github.com/ibm/fp-go/array/generic"
|
||||
O "github.com/ibm/fp-go/ord"
|
||||
)
|
||||
|
||||
// Sort implements a stable sort on the array given the provided ordering
|
||||
func Sort[T any](ord O.Ord[T]) func(ma []T) []T {
|
||||
return G.Sort[[]T](ord)
|
||||
}
|
||||
21
array/sort_test.go
Normal file
21
array/sort_test.go
Normal file
@@ -0,0 +1,21 @@
|
||||
package array
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
O "github.com/ibm/fp-go/ord"
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
func TestSort(t *testing.T) {
|
||||
|
||||
ordInt := O.FromStrictCompare[int]()
|
||||
|
||||
input := []int{2, 1, 3}
|
||||
|
||||
res := Sort(ordInt)(input)
|
||||
|
||||
assert.Equal(t, []int{1, 2, 3}, res)
|
||||
assert.Equal(t, []int{2, 1, 3}, input)
|
||||
|
||||
}
|
||||
23
array/traverse.go
Normal file
23
array/traverse.go
Normal file
@@ -0,0 +1,23 @@
|
||||
package array
|
||||
|
||||
import "github.com/ibm/fp-go/internal/array"
|
||||
|
||||
func Traverse[A, B, HKTB, HKTAB, HKTRB any](
|
||||
_of func([]B) HKTRB,
|
||||
_map func(HKTRB, func([]B) func(B) []B) HKTAB,
|
||||
_ap func(HKTAB, HKTB) HKTRB,
|
||||
|
||||
f func(A) HKTB) func([]A) HKTRB {
|
||||
return array.Traverse[[]A](_of, _map, _ap, f)
|
||||
}
|
||||
|
||||
func MonadTraverse[A, B, HKTB, HKTAB, HKTRB any](
|
||||
_of func([]B) HKTRB,
|
||||
_map func(HKTRB, func([]B) func(B) []B) HKTAB,
|
||||
_ap func(HKTAB, HKTB) HKTRB,
|
||||
|
||||
ta []A,
|
||||
f func(A) HKTB) HKTRB {
|
||||
|
||||
return array.MonadTraverse(_of, _map, _ap, ta, f)
|
||||
}
|
||||
28
array/traverse_test.go
Normal file
28
array/traverse_test.go
Normal file
@@ -0,0 +1,28 @@
|
||||
package array
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
O "github.com/ibm/fp-go/option"
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
type ArrayType = []int
|
||||
|
||||
func TestTraverse(t *testing.T) {
|
||||
|
||||
traverse := Traverse(
|
||||
O.Of[ArrayType],
|
||||
O.MonadMap[ArrayType, func(int) ArrayType],
|
||||
O.MonadAp[int, ArrayType],
|
||||
|
||||
func(n int) O.Option[int] {
|
||||
if n%2 == 0 {
|
||||
return O.None[int]()
|
||||
}
|
||||
return O.Of(n)
|
||||
})
|
||||
|
||||
assert.Equal(t, O.None[[]int](), traverse(ArrayType{1, 2}))
|
||||
assert.Equal(t, O.Of(ArrayType{1, 3}), traverse(ArrayType{1, 3}))
|
||||
}
|
||||
Reference in New Issue
Block a user