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


14.1 – 使用动态名称访问全局变量

通常,赋值足以获取和设置全局变量。但是,我们经常需要某种形式的元编程,例如当我们需要操作一个全局变量时,其名称存储在另一个变量中,或者在运行时以某种方式计算出来。为了获取此变量的值,许多程序员会忍不住编写类似以下内容的东西

    loadstring("value = " .. varname)()
    value = loadstring("return " .. varname)()
例如,如果 varnamex,则连接将产生 "return x"(或 "value = x",使用第一种形式),在运行时将实现所需的结果。但是,此类代码涉及创建和编译新块以及大量额外工作。您可以使用以下代码实现相同的效果,其效率比前一种方法高一个数量级以上
    value = _G[varname]
因为环境是一个常规表,所以您可以简单地使用所需的键(变量名)对其进行索引。

以类似的方式,您可以将值分配给动态计算的全局变量,编写 _G[varname] = value。但请注意:一些程序员对这些函数有点兴奋,最终编写了类似 _G["a"] = _G["var1"] 的代码,这只是编写 a = var1 的一种复杂方式。



前一个问题的概括是允许动态名称中的字段,例如 "io.read""a.b.c.d"。我们使用循环解决此问题,该循环从 _G 开始,逐个字段演化

    function getfield (f)
      local v = _G    -- start with the table of globals
      for w in string.gfind(f, "[%w_]+") do
        v = v[w]
      end
      return v
    end
我们依赖于 string 库中的 gfind 来迭代 f 中的所有单词(其中“单词”是一系列一个或多个字母数字字符和下划线)。

用于设置字段的相应函数稍微复杂一些。像这样的赋值

    a.b.c.d.e = v
与以下完全等效
    local temp = a.b.c.d
    temp.e = v
也就是说,我们必须检索到最后一个名称;我们必须单独处理最后一个字段。新的 setfield 函数还会在路径中创建中间表(如果它们不存在)。
    function setfield (f, v)
      local t = _G    -- start with the table of globals
      for w, d in string.gfind(f, "([%w_]+)(.?)") do
        if d == "." then      -- not last field?
          t[w] = t[w] or {}   -- create table if absent
          t = t[w]            -- get the table
        else                  -- last field
          t[w] = v            -- do the assignment
        end
      end
    end
此新模式在变量 w 中捕获字段名称,并在变量 d 中捕获可选的后续点。如果字段名称后面没有点,则它是最后一个名称。(我们将在 第 20 章 中详细讨论模式匹配。)

使用前面的函数,调用

    setfield("t.x.y", 10)
将创建一个全局表 t、另一个表 t.x,并将 10 分配给 t.x.y
    print(t.x.y)     --> 10
    print(getfield("t.x.y"))   --> 10