5

I have a number of functions operating on strings to extract interesting properties from those strings. One particular function which is called by many of those functions is very expensive and ultimately generates a table of values:

local function expensive(s)
  local t = nil
  return function()
    if not t then
      t = {}
      -- some expensive operations with s which add items to t
    end
    return t
  end
end

local function fn1(s)
  local t = expensive(s)
  -- some other fast operations using t and s
end

local function fn2(s)
  local t = expensive(s)
  -- some other fast operations using t and s
end

local s1, s2 = 'a', 'b'
fn1(s1) -- should create the 't' table for s1
fn1(s2) -- should create the 't' table for s2
fn2(s1) -- should not create the 't' table again for s1
fn1(s2) -- should also not create the 't' table again for s2

How can I make it so that the expensive function creates the table exactly once per string, returning the table in either case? I would rather not have the table exposed to the global environment. I think this could probably be accomplished by some clever use of closures, but I don't know the construct well enough.

ruser9575ba6f
  • 258
  • 1
  • 10

3 Answers3

4
local cache = {}

local function expensive(s)
  local t = cache[s]
  if not t then
    t = {}
    -- some expensive operations with s which add items to t
    cache[s] = t
  end
  return t
end
Egor Skriptunoff
  • 23,359
  • 2
  • 34
  • 64
2

Egor's answer will do the job, but the cache table is accessible by the whole file. To hide it, you have a couple of options. The first is a simple do/end block.

local expensive
do
    local cache = {}
    expensive = function (s)
      local t = cache[s]
      if not t then
        t = {}
        -- some expensive operations with s which add items to t
        cache[s] = t
      end
      return t
    end
end

The other is a self-executing function.

local expensive = (function ()
    local cache = {}
    return function (s)
      local t = cache[s]
      if not t then
        t = {}
        -- some expensive operations with s which add items to t
        cache[s] = t
      end
      return t
    end
end)()

The self-executing function has the advantage that you only have to define the expensive function name once, but the disadvantage that it is a little harder to read than the do/end block. Otherwise they are pretty much the same.

Jack Taylor
  • 5,588
  • 19
  • 35
  • The do ... end block is clever! They have a similar example in the lua reference manual but I hadn't thought of using it in this case. – ruser9575ba6f May 19 '17 at 13:58
2

You can use "memoization" to cache the values returned by the function depending on the parameters. You can read a chapter on it in Programming in Lua and use one of the memoization modules that do the work for you, for example, memoize.

See also this SO answer for related interesting solutions.

Community
  • 1
  • 1
Paul Kulchenko
  • 25,884
  • 3
  • 38
  • 56