nil (or NAN=0/0 keys) in a table.
....
function() return ... end
Traditionally, if we want a define a table of variables, or key-value pairs, we use table construction syntax {...} :
-- traditional method. local squares = {}; for n=1,10 do squares[n] = n^2 end local v = { x = 50, squares = squares, hello = function() print("hello?") end }
However, by appropriate definition of a function collect, we may alternately construct as follows:
local v = collect(function() if true then x = 50 end squares = {}; for n=1,10 do squares[n] = n^2 end function hello() return "hello?" end end)
Note one potential benefit is that statements of code can be interspersed with and build the key-value definitions. It does impose a bit more overhead though.
collect is defined as follows:
function collect(f) -- This collects globals defined in the given function. local collector = setmetatable({}, {__index = _G}) -- Call function in collector environment setfenv(f, collector)() -- Extract collected variables. local result = {}; for k,v in pairs(collector) do result[k] = v end return result end
Test suite:
assert(v.x == 50) assert(#v.squares == 10) assert(v.hello() == "hello?") assert(v.print == nil) -- ensure _G not included. print("done")
This type of mechanism is used in the Lua 5.1 module system, where the collecting function is given in a file and the require/module functions implement the collect mechanism (as well as other things). See Chapter 15 of [Programming in Lua 2nd Edition].
module("mymodule") x = 50 color = "blue" function hello() return "hello?" end
--DavidManura, 2006-10, Lua 5.1
There are a bunch of interesting ways to execute a series of actions on a given event. One way that I've seen that was somewhat less than efficient looked like this:
for _,v in pairs(files_in_directory) do dofile(v) if action then action(args) end action = nil endwhere a file in the directory might look like this:
function action(something) print(something) endThis is inefficient; it requires everything to be reparsed each call, and it smashes the global called "action". It doen't provide for effective weighting, either. In naim, we use a hook system that's done in C that creates a bunch of chains, to which we can register C and Lua actions.
I wrote a system that allows one to create their own hook chains in Lua that can be executed like functions. The syntax seems fairly logical to me:
require"hooks" myhooks = {} myhooks.test = hooks:new() myhooks.ref1 = myhooks.test:insert(function (foo, bar) print("Test 1", foo, bar) end, 100) myhooks.ref2 = myhooks.test:insert(function (foo, bar) print("Test 2", foo, bar) end, 50) myhooks.ref3 = myhooks.test:insert(function (foo, bar) print("Test 3", foo, bar) end, 150) print"--" myhooks.test("Hello", "world") myhooks.test:remove(myhooks.ref1) print"--" myhooks.test("Hello", "world")
Running this would produce output like:
-- Test 2 Hello World Test 1 Hello World Test 3 Hello World -- Test 2 Hello World Test 3 Hello World
The code that drives this is available at [1]. Still to do: support for writable arguments. This is necessary in naim if one wishes to modify a string that gets passed through; i.e., a filter module might want to substitute all instances of "lol" to "<grin>" in the input string, and then pass the modified string through to further hooks in the chain. Patches thoughtfully accepted.
-- JoshuaWise, 2007年02月01日
One sometimes runs into a conflict where a variable should be lexically scoped to a particular function but should also have a lifetime longer than the function call. In the below case, the sounds table is used only by the function soundit, which would suggest bringing it inside the soundit function, but it would be wasteful to reconstruct sounds on each function call, so often the programmer will keep sounds outside:
local sounds = { a = "ay", b = "bee", c = "see", .... } local function soundit(x) assert(type(x) == "string") return sounds[x] end
In the C language, we might make sounds a static variable inside soundit. In Lua, the usual suggestion here, if one wants to limit the scope of sounds, is to surround sounds and soundit with a do block:
local soundit; do local sounds = { a = "ay", b = "bee", c = "see", .... } function soundit(x) assert(type(x) == "string") return sounds[x] end end -- note: sounds not visible outside the do-block.
One complaint is that now the implementation of the function is spread outside the function, the name soundit is duplicated, and the code is further indented/ugly, appearing less like a function definition. Furthermore, sounds will get initialized regardless whether soundit ever gets called (thereby imposing a load-time overhead). The following approach keeps sounds outside the function but moves its initialization inside the function. Due to the short-circuiting behavior of or, it will generally impose little additional overhead at call-time:
local soundit; do local sounds; function soundit(x) sounds = sounds or { a = "ay", b = "bee", c = "see", .... } assert(type(x) == "string") return sounds[x] end end
In fact, we may just give up perfection and let the lexical scope spill over for enhanced readability:
local sounds local function soundit(x) sounds = sounds or { a = "ay", b = "bee", c = "see", .... } assert(type(x) == "string") return sounds[x] end
Here are two variations involving the construction of closures. These are a bit more tidy than the do block approach but do impose the load-time overhead of at least constructing a temporary function.
local soundit = (function() local sounds = { a = "ay", b = "bee", c = "see", .... } return function(x) assert(type(x) == "string") return sounds[x] end end)()
local soundit = (function() local sounds return function(x) sounds = sounds or { a = "ay", b = "bee", c = "see", .... } assert(type(x) == "string") return sounds[x] end end)()
--DavidManura, 2007-03
<Pattern description> (Add more patterns here)