Difference (from prior major revision)
(minor diff, author diff)
Changed: 106c106
-- return a memoizer than can handle the n-to-m case
|
-- return a memoizer that dispatches between the different m-argument cases.
|
Changed: 131c131
== Writing catch() in C ==
|
== Implementing catch() in C ==
|
Added: 136a137
luaL_checkstack(L,n1-1,"too many upvalues");
|
Changed: 150c151
lua_push_new_cclosure(L,throw_upvalues,n1);
|
lua_pushcclosure(L,throw_upvalues,n1);
|
Yet another function memoization implementation. It handles the general case of M argument, N return value functions. It also takes some care to preserve nils appearing in either the argument or result lists.
For example:
local mtest = weak_memoize_m_to_n( function(...) print 'exec' return ... end )
print( mtest(nil,2) )
print( mtest(nil,2) )
collectgarbage()
print( mtest ( nil,2 ) )
print( mtest ( ) )
print( mtest ( nil ) )
The design is equivalent to an argument tree technique like [memoize.lua]. However, the tree is built implicitly, rather than explicitly, by recursively reducing M>1 cases to M-1 cases.
Lua Code
local _ENV = setmetatable({},{__index=_G})
function catch(...)
local rvals = {...}
local n = select('#',...)
return function()
return table.unpack(rvals,1,n)
end
end
local weak_mt= {__mode='kv'}
local function weak_table() return setmetatable({},weak_mt) end
local function strong_table() return {} end
local null = {}
local function arg2key(arg)
return (arg == nil and null) or arg
end
local function new_memoizer_1_to_n(newtable)
return function(fun)
local lookup = newtable()
return function (arg)
local k = arg2key(arg)
local r=lookup[k]
if r then
return r()
end
r=catch( fun(arg) )
lookup[k] = r
return r()
end
end
end
local function new_memoizer_m_to_n( newtable, memoize_1_to_n )
local function memoize_m_to_n(m,f)
if m==0 then
local memoized
return function()
if memoized then
return memoized()
end
memoized = catch(f())
return memoized()
end
end
if m==1 then
return memoize_1_to_n(f)
end
local lookup = newtable()
return function(arg, ...)
local k = arg2key(arg)
local r = lookup[k]
if r then
return r(...)
end
r = memoize_m_to_n(m-1, function(...)
return f(arg,...)
end)
lookup[k]=r
return r(...)
end
end
return function(f)
local m_to_memoized = newtable()
return function(...)
local m = select('#',...)
local memoized = m_to_memoized[m]
if memoized then
return memoized(...)
end
memoized = memoize_m_to_n(m,f)
m_to_memoized[m]=memoized
return memoized(...)
end
end
end
weak_memoize_1_to_n = new_memoizer_1_to_n(weak_table)
strong_memoize_1_to_n = new_memoizer_1_to_n(strong_table)
weak_memoize_m_to_n = new_memoizer_m_to_n(weak_table,weak_memoize_1_to_n)
strong_memoize_m_to_n = new_memoizer_m_to_n(strong_table,strong_memoize_1_to_n)
return _ENV
Implementing catch() in C
Memory use and performance can both be improved by implementing catch() inside the C-API. While their storage capacity is limited to 255 values; C-closures are lighter, faster datastructures than Lua's generic tables.
static int throw_upvalues(lua_State *L) {
int n1=lua_tointeger(L,lua_upvalueindex(1));
luaL_checkstack(L,n1-1,"too many upvalues");
for(int i=2; i<=n1; i++) {
lua_pushvalue(L,lua_upvalueindex(i));
}
return n1-1;
}
static int catch_args(lua_State *L) {
int n1 = lua_gettop(L)+1;
if(n1>MAXUPVAL) {
return luaL_error(L,"can't catch more than %d args. (catch() called with %d arguments).",MAXUPVAL-1, n1-1);
}
lua_pushinteger(L,n1);
lua_insert(L,1);
lua_pushcclosure(L,throw_upvalues,n1);
return 1;
}
See Also
Many other Lua memoization implementations are scattered around the web. The FuncTables page appears to be the de-facto wiki link hub. But, the topic also comes up frequently on the lua users list; and there are a couple nice implementations of string-serialization based approaches posted in the archives [1], [2].
RecentChanges · preferences
edit · history
Last edited December 31, 2013 6:50 pm GMT (diff)