15.5 其他一些技巧(Other Facilities) |
正如前面我所说的,用表来实现packages过程中可以使用Lua的所有强大的功能。这里面有无限的可能性。在这里,我只给出一些建议。
我们不需要将package的所有公有成员的定义放在一起,例如,我们可以在一个独立分开的chunk中给我们的复数package增加一个新的函数:
function complex.div (c1, c2)
return complex.mul(c1, complex.inv(c2))
end
(但是注意:私有成员必须限制在一个文件之内,我认为这是一件好事)反过来,我们可以在同一个文件之内定义多个packages,我们需要做的只是将每一个package放在一个do代码块内,这样local变量才能被限制在那个代码块中。
在package外部,如果我们需要经常使用某个函数,我们可以给他们定义一个局部变量名:
local add, i = complex.add, complex.i
c1 = add(complex.new(10, 20), i)
如果我们不想一遍又一遍的重写package名,我们用一个短的局部变量表示package:
local C = complex
c1 = C.add(C.new(10, 20), C.i)
写一个函数拆开package也是很容易的,将package中所有的名字放到全局命名空间即可:
function openpackage (ns)
for n,v in pairs(ns) do _G[n] = v end
end
openpackage(complex)
c1 = mul(new(10, 20), i)
如果你担心打开package的时候会有命名冲突,可以在赋值以前检查一下名字是否存在:
function openpackage (ns)
for n,v in pairs(ns) do
if _G[n] ~= nil then
error("name clash: " .. n .. " is already defined")
end
_G[n] = v
end
end
由于packages本身也是表,我们甚至可以在packages中嵌套packages;也就是说我们在一个package内还可以创建package,然后很少有必要这么做。
另一个有趣之处是自动加载:函数只有被实际使用的时候才会自动加载。当我们加载一个自动加载的package,会自动创建一个新的空表来表示package并且设置表的__index metamethod来完成自动加载。当我们调用任何一个没有被加载的函数的时候,__index metamethod将被触发去加载着个函数。当调用发现函数已经被加载,__index将不会被触发。
下面有一个简单的实现自动加载的方法。每一个函数定义在一个辅助文件中。(也可能一个文件内有多个函数)这些文件中的每一个都以标准的方式定义函数,例如:
function pack1.foo ()
...
end
function pack1.goo ()
...
end
然而,文件并不会创建package,因为当函数被加载的时候package已经存在了。
在主package中我们定义一个辅助表来记录函数存放的位置:
local location = {
foo = "/usr/local/lua/lib/pack1_1.lua",
goo = "/usr/local/lua/lib/pack1_1.lua",
foo1 = "/usr/local/lua/lib/pack1_2.lua",
goo1 = "/usr/local/lua/lib/pack1_3.lua",
}
下面我们创建package并且定义她的metamethod:
pack1 = {}
setmetatable(pack1, {__index = function (t, funcname)
local file = location[funcname]
if not file then
error("package pack1 does not define " .. funcname)
end
assert(loadfile(file))() -- load and run definition
return t[funcname] -- return the function
end})
return pack1
加载这个package之后,第一次程序执行pack1.foo()将触发__index metamethod,接着发现函数有一个相应的文件,并加载这个文件。微妙之处在于:加载了文件,同时返回函数作为访问的结果。
因为整个系统(译者:这里可能指复数吧?)都使用Lua写的,所以很容易改变系统的行为。例如,函数可以是用C写的,在metamethod中用loadlib加载他。或者我们我们可以在全局表中设定一个metamethod来自动加载整个packages.这里有无限的可能等着你去发掘。