From eb03946eb20810ebabd07ce9486a8a458761b7a3 Mon Sep 17 00:00:00 2001 From: Christopher Brannon Date: Mon, 25 Dec 2023 03:37:14 -0800 Subject: [PATCH 1/2] Add a rockspec to allow building straight from git. --- rockspecs/memoize-scm-0.rockspec | 29 +++++++++++++++++++++++++++++ 1 file changed, 29 insertions(+) create mode 100644 rockspecs/memoize-scm-0.rockspec diff --git a/rockspecs/memoize-scm-0.rockspec b/rockspecs/memoize-scm-0.rockspec new file mode 100644 index 0000000..fdd627d --- /dev/null +++ b/rockspecs/memoize-scm-0.rockspec @@ -0,0 +1,29 @@ +package = "memoize" +version = "scm-0" +source = { + url = "git+https://github.com/kikito/memoize.lua.git" +} +description = { + summary = "Memoized functions in Lua", + detailed = [[ + * Caches the results based on multiple parameters instead of just 1. + * Doesn't rely on `tostring`; instead, it uses operator `==` on all the + parameters (this is accomplished by structuring the cache in a + tree structure, where each tree node corresponds to one + parameter). + * Works well with functions returning multiple values + * Can memoize both functions and "callable tables" (tables with a `__call` + metamethod) + ]], + homepage = "https://github.com/kikito/memoize.lua", + license = "MIT" +} +dependencies = { + "lua >= 5.1" +} +build = { + type = "builtin", + modules = { + memoize = "memoize.lua" + } +} From cb1c38327d2465fd7885407710d8b9096e685bb1 Mon Sep 17 00:00:00 2001 From: Christopher Brannon Date: Mon, 25 Dec 2023 03:26:02 -0800 Subject: [PATCH 2/2] Add a procedure for invalidating the cache. --- README.md | 17 +++++++++++++++++ memoize.lua | 9 +++++++++ spec/memoize_spec.lua | 28 ++++++++++++++++++++++++++-- 3 files changed, 52 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index b4166df..1449afd 100644 --- a/README.md +++ b/README.md @@ -212,6 +212,23 @@ erased, but the memory storing `mf(4)` is still in use. The second parameter also allows sharing a single cache amongst two memoized functions. +Optionally, the second parameter may contain a validator method for determining +the validity of a cache entry. When the validator returns nil or false, +the current cache entry is discarded and a new one will be computed. +For example, suppose that we want to declare a cache entry stale after 10 +seconds. + +``` lua +local cache = {} +function cache:validator(current_validity) + if current_validity == nil then + return os.time() + elseif (os.time() - current_validity) > 10 then + return nil + end + return current_validity +end +``` ## Changelog diff --git a/memoize.lua b/memoize.lua index b574ee0..d5fcd30 100644 --- a/memoize.lua +++ b/memoize.lua @@ -50,6 +50,12 @@ local function cache_get(cache, params) node = node.children and node.children[params[i]] if not node then return nil end end + if node.results and cache.validator then + node.validity = cache:validator(node.validity) + if not node.validity then + node.results = nil + end + end return node.results end @@ -63,6 +69,9 @@ local function cache_put(cache, params, results) node = node.children[param] end node.results = results + if cache.validator then + node.validity = cache:validator(nil) + end end -- public function diff --git a/spec/memoize_spec.lua b/spec/memoize_spec.lua index 6ab15b0..0a39ea9 100644 --- a/spec/memoize_spec.lua +++ b/spec/memoize_spec.lua @@ -187,6 +187,30 @@ describe('memoize', function() assert.equal(4, mlen('a', 'b', 'c', 'd')) assert.equal(11, counter) end) - end) -end) + it("invalidates based on an optional validator", function() + local cache = {} + function cache:validator(current_validity) + if not current_validity then + return 3 + elseif current_validity == 0 then + return nil + end + return current_validity - 1 + end + local mlen = memoize(len, cache) + + assert.equal(1, mlen('a')) + assert.equal(1, counter) + assert.equal(1, mlen('a')) + assert.equal(1, counter) + assert.equal(1, mlen('a')) + assert.equal(1, counter) + assert.equal(1, mlen('a')) + assert.equal(1, counter) + assert.equal(1, mlen('a')) + assert.equal(2, counter) + end) + + end) + end)