第一版是为 Lua 5.0 编写的。虽然在很大程度上仍然适用于后续版本,但有一些差异。
第四版针对 Lua 5.3,可在 Amazon 和其他书店购买。
购买本书,您还可以帮助支持 Lua 项目


15.5 – 其他功能

如前所述,使用表来实现包使我们能够使用 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 加载它们。或者,我们可以在全局表中设置一个元方法来自动加载整个包。可能性是无穷无尽的。