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:
@ -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]()
|
||||
}
|
||||
|
@ -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)
|
||||
}
|
||||
|
@ -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
2
go.mod
@ -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
2
go.sum
@ -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=
|
||||
|
Reference in New Issue
Block a user