第一版是为 Lua 5.0 编写的。虽然在很大程度上仍然适用于后续版本,但有一些差异。
第四版针对 Lua 5.3,可在 Amazon 和其他书店购买。
购买本书,您还可以帮助支持 Lua 项目。
用 Lua 编程 | ||
第二部分。表和对象 第 15 章。包 |
如前所述,使用表来实现包使我们能够使用 Lua 的全部功能来操作它们。可能性是无限的。这里我只给出一些建议。
我们不必一起定义包的所有公共项。例如,我们可以在一个单独的块中向我们的 complex
包添加一个新项
function complex.div (c1, c2) return complex.mul(c1, complex.inv(c2)) end(但请注意,私有部分仅限于一个文件,我认为这是一件好事。)相反,我们可以在同一个文件中定义多个包。我们所要做的就是将每个包都包含在一个 do 块中,以便其局部变量仅限于该块。
在包的外部,如果我们要经常使用某些操作,我们可以给它们起本地名称
local add, i = complex.add, complex.i c1 = add(complex.new(10, 20), i)或者,如果我们不想一遍又一遍地写包名,我们可以给包本身起一个较短的本地名称
local C = complex c1 = C.add(C.new(10, 20), C.i)
很容易编写一个函数来解包一个包,将它的所有名称放入全局名称空间
function openpackage (ns) for n,v in pairs(ns) do _G[n] = v end end openpackage(complex) c1 = mul(new(10, 20), i)如果您在打开包时担心名称冲突,可以在赋值之前检查名称
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
由于包本身是表,我们甚至可以嵌套包;也就是说,我们可以在一个包内创建另一个包。但是,这种功能很少需要。
另一个有趣的功能是自动加载,它只在程序实际使用函数时才加载函数。当我们加载一个自动加载包时,它会创建一个空表来表示该包,并将表的 __index
元方法设置为执行自动加载。然后,当我们调用任何尚未加载的函数时,将调用 __index
元方法来加载它。后续调用会发现函数已加载;因此,它们不会激活元方法。
实现自动加载的一种简单方法如下。每个函数都在一个辅助文件中定义。(每个文件中可以有多个函数。)每个文件都以一种标准的方式定义其函数,例如这里
function pack1.foo () ... end function pack1.goo () ... end但是,文件不会创建包,因为在加载函数时包已经存在。
在主包中,我们定义一个辅助表,用于描述可以在哪里找到每个函数
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", }然后我们创建包并定义其元方法
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加载此包后,程序第一次执行
pack1.foo()
时,它将调用 __index
元方法,它非常简单。它检查函数是否具有相应的文件并加载该文件。唯一巧妙之处在于它不仅必须加载文件,还必须将函数作为访问结果返回。
因为整个系统都是用 Lua 编写的,所以很容易改变其行为。例如,这些函数可以用 C 定义,元方法使用 loadlib
加载它们。或者,我们可以在全局表中设置一个元方法来自动加载整个包。可能性是无穷无尽的。
版权所有 © 2003–2004 Roberto Ierusalimschy。保留所有权利。 |