1
0
mirror of https://github.com/IBM/fp-go.git synced 2025-08-10 22:31:32 +02:00

fix: implement FoldMap

Signed-off-by: Dr. Carsten Leue <carsten.leue@de.ibm.com>
This commit is contained in:
Dr. Carsten Leue
2023-08-10 18:08:11 +02:00
parent 39c6108bf5
commit 1b1dccc551
11 changed files with 306 additions and 1 deletions

View File

@@ -16,11 +16,14 @@
package generic
import (
"sort"
F "github.com/IBM/fp-go/function"
G "github.com/IBM/fp-go/internal/record"
Mg "github.com/IBM/fp-go/magma"
Mo "github.com/IBM/fp-go/monoid"
O "github.com/IBM/fp-go/option"
"github.com/IBM/fp-go/ord"
T "github.com/IBM/fp-go/tuple"
)
@@ -40,6 +43,46 @@ func Values[M ~map[K]V, GV ~[]V, K comparable, V any](r M) GV {
return collect[M, GV](r, F.Second[K, V])
}
func KeysOrd[M ~map[K]V, GK ~[]K, K comparable, V any](o ord.Ord[K]) func(r M) GK {
return func(r M) GK {
return collectOrd[M, GK](o, r, F.First[K, V])
}
}
func ValuesOrd[M ~map[K]V, GV ~[]V, K comparable, V any](o ord.Ord[K]) func(r M) GV {
return func(r M) GV {
return collectOrd[M, GV](o, r, F.Second[K, V])
}
}
func collectOrd[M ~map[K]V, GR ~[]R, K comparable, V, R any](o ord.Ord[K], r M, f func(K, V) R) GR {
// create the entries
entries := toEntriesOrd[M, []T.Tuple2[K, V]](o, r)
// collect this array
ft := T.Tupled2(f)
count := len(entries)
result := make(GR, count)
for i := count - 1; i >= 0; i-- {
result[i] = ft(entries[i])
}
// done
return result
}
func reduceOrd[M ~map[K]V, K comparable, V, R any](o ord.Ord[K], r M, f func(K, R, V) R, initial R) R {
// create the entries
entries := toEntriesOrd[M, []T.Tuple2[K, V]](o, r)
// collect this array
current := initial
count := len(entries)
for i := 0; i < count; i++ {
t := entries[i]
current = f(T.First(t), current, T.Second(t))
}
// done
return current
}
func collect[M ~map[K]V, GR ~[]R, K comparable, V, R any](r M, f func(K, V) R) GR {
count := len(r)
result := make(GR, count)
@@ -250,6 +293,27 @@ func ToArray[M ~map[K]V, GT ~[]T.Tuple2[K, V], K comparable, V any](r M) GT {
return collect[M, GT](r, T.MakeTuple2[K, V])
}
func toEntriesOrd[M ~map[K]V, GT ~[]T.Tuple2[K, V], K comparable, V any](o ord.Ord[K], r M) GT {
// total number of elements
count := len(r)
// produce an array that we can sort by key
entries := make(GT, count)
idx := 0
for k, v := range r {
entries[idx] = T.MakeTuple2(k, v)
idx++
}
sort.Slice(entries, func(i, j int) bool {
return o.Compare(T.First(entries[i]), T.First(entries[j])) < 0
})
// final entries
return entries
}
func ToEntriesOrd[M ~map[K]V, GT ~[]T.Tuple2[K, V], K comparable, V any](o ord.Ord[K]) func(r M) GT {
return F.Bind1st(toEntriesOrd[M, GT, K, V], o)
}
func ToEntries[M ~map[K]V, GT ~[]T.Tuple2[K, V], K comparable, V any](r M) GT {
return ToArray[M, GT](r)
}
@@ -379,3 +443,67 @@ func IsNonNil[M ~map[K]V, K comparable, V any](m M) bool {
func ConstNil[M ~map[K]V, K comparable, V any]() M {
return (M)(nil)
}
func FoldMap[AS ~map[K]A, K comparable, A, B any](m Mo.Monoid[B]) func(func(A) B) func(AS) B {
return func(f func(A) B) func(AS) B {
return Reduce[AS](func(cur B, a A) B {
return m.Concat(cur, f(a))
}, m.Empty())
}
}
func Fold[AS ~map[K]A, K comparable, A any](m Mo.Monoid[A]) func(AS) A {
return Reduce[AS](m.Concat, m.Empty())
}
func FoldMapWithIndex[AS ~map[K]A, K comparable, A, B any](m Mo.Monoid[B]) func(func(K, A) B) func(AS) B {
return func(f func(K, A) B) func(AS) B {
return ReduceWithIndex[AS](func(k K, cur B, a A) B {
return m.Concat(cur, f(k, a))
}, m.Empty())
}
}
func ReduceOrdWithIndex[M ~map[K]V, K comparable, V, R any](o ord.Ord[K]) func(func(K, R, V) R, R) func(M) R {
return func(f func(K, R, V) R, initial R) func(M) R {
return func(m M) R {
return reduceOrd(o, m, f, initial)
}
}
}
func ReduceOrd[M ~map[K]V, K comparable, V, R any](o ord.Ord[K]) func(func(R, V) R, R) func(M) R {
ro := ReduceOrdWithIndex[M, K, V, R](o)
return func(f func(R, V) R, initial R) func(M) R {
return ro(F.Ignore1of3[K](f), initial)
}
}
func FoldMapOrd[AS ~map[K]A, K comparable, A, B any](o ord.Ord[K]) func(m Mo.Monoid[B]) func(func(A) B) func(AS) B {
red := ReduceOrd[AS, K, A, B](o)
return func(m Mo.Monoid[B]) func(func(A) B) func(AS) B {
return func(f func(A) B) func(AS) B {
return red(func(cur B, a A) B {
return m.Concat(cur, f(a))
}, m.Empty())
}
}
}
func FoldOrd[AS ~map[K]A, K comparable, A any](o ord.Ord[K]) func(m Mo.Monoid[A]) func(AS) A {
red := ReduceOrd[AS, K, A, A](o)
return func(m Mo.Monoid[A]) func(AS) A {
return red(m.Concat, m.Empty())
}
}
func FoldMapOrdWithIndex[AS ~map[K]A, K comparable, A, B any](o ord.Ord[K]) func(m Mo.Monoid[B]) func(func(K, A) B) func(AS) B {
red := ReduceOrdWithIndex[AS, K, A, B](o)
return func(m Mo.Monoid[B]) func(func(K, A) B) func(AS) B {
return func(f func(K, A) B) func(AS) B {
return red(func(k K, cur B, a A) B {
return m.Concat(cur, f(k, a))
}, m.Empty())
}
}
}

View File

@@ -19,6 +19,7 @@ import (
Mg "github.com/IBM/fp-go/magma"
Mo "github.com/IBM/fp-go/monoid"
O "github.com/IBM/fp-go/option"
"github.com/IBM/fp-go/ord"
G "github.com/IBM/fp-go/record/generic"
T "github.com/IBM/fp-go/tuple"
)
@@ -215,3 +216,53 @@ func FilterChainWithIndex[V1 any, K comparable, V2 any](m Mo.Monoid[map[K]V2]) f
func FilterChain[V1 any, K comparable, V2 any](m Mo.Monoid[map[K]V2]) func(func(V1) O.Option[map[K]V2]) func(map[K]V1) map[K]V2 {
return G.FilterChain[map[K]V1](m)
}
// FoldMap maps and folds a record. Map the record passing each value to the iterating function. Then fold the results using the provided Monoid.
func FoldMap[K comparable, A, B any](m Mo.Monoid[B]) func(func(A) B) func(map[K]A) B {
return G.FoldMap[map[K]A](m)
}
// FoldMapWithIndex maps and folds a record. Map the record passing each value to the iterating function. Then fold the results using the provided Monoid.
func FoldMapWithIndex[K comparable, A, B any](m Mo.Monoid[B]) func(func(K, A) B) func(map[K]A) B {
return G.FoldMapWithIndex[map[K]A](m)
}
// Fold folds the record using the provided Monoid.
func Fold[K comparable, A any](m Mo.Monoid[A]) func(map[K]A) A {
return G.Fold[map[K]A](m)
}
// ReduceOrdWithIndex reduces a map into a single value via a reducer function making sure that the keys are passed to the reducer in the specified order
func ReduceOrdWithIndex[V, R any, K comparable](o ord.Ord[K]) func(func(K, R, V) R, R) func(map[K]V) R {
return G.ReduceOrdWithIndex[map[K]V, K, V, R](o)
}
// ReduceOrd reduces a map into a single value via a reducer function making sure that the keys are passed to the reducer in the specified order
func ReduceOrd[V, R any, K comparable](o ord.Ord[K]) func(func(R, V) R, R) func(map[K]V) R {
return G.ReduceOrd[map[K]V, K, V, R](o)
}
// FoldMap maps and folds a record. Map the record passing each value to the iterating function. Then fold the results using the provided Monoid and the items in the provided order
func FoldMapOrd[A, B any, K comparable](o ord.Ord[K]) func(m Mo.Monoid[B]) func(func(A) B) func(map[K]A) B {
return G.FoldMapOrd[map[K]A, K, A, B](o)
}
// Fold folds the record using the provided Monoid with the items passed in the given order
func FoldOrd[A any, K comparable](o ord.Ord[K]) func(m Mo.Monoid[A]) func(map[K]A) A {
return G.FoldOrd[map[K]A, K, A](o)
}
// FoldMapWithIndex maps and folds a record. Map the record passing each value to the iterating function. Then fold the results using the provided Monoid and the items in the provided order
func FoldMapOrdWithIndex[K comparable, A, B any](o ord.Ord[K]) func(m Mo.Monoid[B]) func(func(K, A) B) func(map[K]A) B {
return G.FoldMapOrdWithIndex[map[K]A, K, A, B](o)
}
// KeysOrd returns the keys in the map in their given order
func KeysOrd[V any, K comparable](o ord.Ord[K]) func(r map[K]V) []K {
return G.KeysOrd[map[K]V, []K, K, V](o)
}
// ValuesOrd returns the values in the map ordered by their keys in the given order
func ValuesOrd[V any, K comparable](o ord.Ord[K]) func(r map[K]V) []V {
return G.ValuesOrd[map[K]V, []V, K, V](o)
}

View File

@@ -18,10 +18,12 @@ package record
import (
"fmt"
"sort"
"strings"
"testing"
"github.com/IBM/fp-go/internal/utils"
O "github.com/IBM/fp-go/option"
S "github.com/IBM/fp-go/string"
"github.com/stretchr/testify/assert"
)
@@ -99,3 +101,33 @@ func TestFilterChain(t *testing.T) {
"c": "c3",
}, res)
}
func ExampleFoldMap() {
src := map[string]string{
"a": "a",
"b": "b",
"c": "c",
}
fold := FoldMap[string, string](S.Monoid)(strings.ToUpper)
fmt.Println(fold(src))
// Output: ABC
}
func ExampleValuesOrd() {
src := map[string]string{
"c": "a",
"b": "b",
"a": "c",
}
getValues := ValuesOrd[string](S.Ord)
fmt.Println(getValues(src))
// Output: [c b a]
}