1
0
mirror of https://github.com/IBM/fp-go.git synced 2025-06-19 00:17:48 +02:00

fix: add a single element cache

Signed-off-by: Dr. Carsten Leue <carsten.leue@de.ibm.com>
This commit is contained in:
Dr. Carsten Leue
2023-12-21 14:10:46 +01:00
parent 3aa55c74d4
commit 5ac47440a1
5 changed files with 76 additions and 5 deletions

View File

@ -28,3 +28,14 @@ func Memoize[K comparable, T any](f func(K) T) func(K) T {
func ContramapMemoize[A any, K comparable, T any](kf func(A) K) func(func(A) T) func(A) T {
return G.ContramapMemoize[func(A) T](kf)
}
// CacheCallback converts a unary function into a unary function that caches the value depending on the parameter
func CacheCallback[
A any, K comparable, T any](kf func(A) K, getOrCreate func(K, func() func() T) func() T) func(func(A) T) func(A) T {
return G.CacheCallback[func(func(A) T) func(A) T](kf, getOrCreate)
}
// SingleElementCache creates a cache function for use with the [CacheCallback] method that has a maximum capacity of one single item
func SingleElementCache[K comparable, T any]() func(K, func() func() T) func() T {
return G.SingleElementCache[func() func() T, K]()
}

View File

@ -16,6 +16,8 @@
package function
import (
"fmt"
"math/rand"
"testing"
"github.com/stretchr/testify/assert"
@ -48,3 +50,21 @@ func TestCache(t *testing.T) {
assert.Equal(t, 10, cached(10))
assert.Equal(t, 2, count)
}
func TestSingleElementCache(t *testing.T) {
f := func(key string) string {
return fmt.Sprintf("%s: %d", key, rand.Int())
}
cb := CacheCallback(func(s string) string { return s }, SingleElementCache[string, string]())
cf := cb(f)
v1 := cf("1")
v2 := cf("1")
v3 := cf("2")
v4 := cf("1")
assert.Equal(t, v1, v2)
assert.NotEqual(t, v2, v3)
assert.NotEqual(t, v3, v4)
assert.NotEqual(t, v1, v4)
}

View File

@ -28,7 +28,7 @@ func Memoize[F ~func(K) T, K comparable, T any](f F) F {
// ContramapMemoize converts a unary function into a unary function that caches the value depending on the parameter
func ContramapMemoize[F ~func(A) T, KF func(A) K, A any, K comparable, T any](kf KF) func(F) F {
return CacheCallback[F](kf, getOrCreate[K, T]())
return CacheCallback[func(F) F, func() func() T](kf, getOrCreate[K, T]())
}
// getOrCreate is a naive implementation of a cache, without bounds
@ -50,13 +50,51 @@ func getOrCreate[K comparable, T any]() func(K, func() func() T) func() T {
}
}
// SingleElementCache is a cache with a capacity of a single element
func SingleElementCache[
LLT ~func() LT, // generator of the generator
K comparable, // key into the cache
LT ~func() T, // generator of a value
T any, // the cached data type
]() func(K, LLT) LT {
var l sync.Mutex
var key K
var value LT
hasKey := false
return func(k K, gen LLT) LT {
l.Lock()
existing := value
if !hasKey || key != k {
existing = gen()
// update state
key = k
value = existing
hasKey = true
}
l.Unlock()
return existing
}
}
// CacheCallback converts a unary function into a unary function that caches the value depending on the parameter
func CacheCallback[F ~func(A) T, KF func(A) K, C ~func(K, func() func() T) func() T, A any, K comparable, T any](kf KF, getOrCreate C) func(F) F {
func CacheCallback[
EM ~func(F) F, // endomorphism of the function
LLT ~func() LT, // generator of the generator
LT ~func() T, // generator of a value
F ~func(A) T, // function to actually cache
KF func(A) K, // extracts the cache key from the input
C ~func(K, LLT) LT, // the cache callback function
A any, K comparable, T any](kf KF, getOrCreate C) EM {
return func(f F) F {
return func(a A) T {
// cache entry
return getOrCreate(kf(a), func() func() T {
return L.Memoize[func() T](func() T {
return getOrCreate(kf(a), func() LT {
return L.Memoize[LT](func() T {
return f(a)
})
})()

2
go.mod
View File

@ -12,6 +12,6 @@ require (
github.com/davecgh/go-spew v1.1.1 // indirect
github.com/pmezard/go-difflib v1.0.0 // indirect
github.com/russross/blackfriday/v2 v2.1.0 // indirect
github.com/xrash/smetrics v0.0.0-20201216005158-039620a65673 // indirect
github.com/xrash/smetrics v0.0.0-20231213231151-1d8dd44e695e // indirect
gopkg.in/yaml.v3 v3.0.1 // indirect
)

2
go.sum
View File

@ -14,6 +14,8 @@ github.com/urfave/cli/v2 v2.26.0 h1:3f3AMg3HpThFNT4I++TKOejZO8yU55t3JnnSr4S4QEI=
github.com/urfave/cli/v2 v2.26.0/go.mod h1:8qnjx1vcq5s2/wpsqoZFndg2CE5tNFyrTvS6SinrnYQ=
github.com/xrash/smetrics v0.0.0-20201216005158-039620a65673 h1:bAn7/zixMGCfxrRTfdpNzjtPYqr8smhKouy9mxVdGPU=
github.com/xrash/smetrics v0.0.0-20201216005158-039620a65673/go.mod h1:N3UwUGtsrSj3ccvlPHLoLsHnpR27oXr4ZE984MbSER8=
github.com/xrash/smetrics v0.0.0-20231213231151-1d8dd44e695e h1:+SOyEddqYF09QP7vr7CgJ1eti3pY9Fn3LHO1M1r/0sI=
github.com/xrash/smetrics v0.0.0-20231213231151-1d8dd44e695e/go.mod h1:N3UwUGtsrSj3ccvlPHLoLsHnpR27oXr4ZE984MbSER8=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=