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


17.3 – 重新审视具有默认值的表

第 13.4.3 节 中,我们讨论了如何实现具有非 nil 默认值的表。我们看到了一个特定技术,并评论说其他两种技术需要弱表,所以我们推迟了它们。现在是重新审视该主题的时候了。正如我们将看到的,这两种默认值技术实际上是我们在这里看到的两种通用技术的特定应用:对象属性和记忆化。

在第一个解决方案中,我们使用一个弱表将每个表的默认值与其关联起来

    local defaults = {}
    setmetatable(defaults, {__mode = "k"})
    local mt = {__index = function (t) return defaults[t] end}
    function setDefault (t, d)
      defaults[t] = d
      setmetatable(t, mt)
    end
如果 defaults 没有弱键,它将使所有具有默认值的表永久存在。

在第二个解决方案中,我们为不同的默认值使用不同的元表,但是当我们重复一个默认值时,我们重复使用同一个元表。这是记忆化的典型用法

    local metas = {}
    setmetatable(metas, {__mode = "v"})
    function setDefault (t, d)
      local mt = metas[d]
      if mt == nil then
        mt = {__index = function () return d end}
        metas[d] = mt     -- memoize
      end
      setmetatable(t, mt)
    end
在这种情况下,我们使用弱值来允许收集不再使用的元表。

对于这两种默认值实现,哪种最好?和往常一样,这取决于具体情况。两者具有相似的复杂性和相似的性能。第一个实现需要对每个具有默认值的表使用几个单词(defaults 中的一个条目)。第二个实现需要对每个不同的默认值使用几十个单词(一个新表、一个新闭包,以及 metas 中的一个条目)。因此,如果您的应用程序有数千个具有几个不同默认值的表,则第二个实现明显优越。另一方面,如果很少有表共享公共默认值,那么您应该使用第一个实现。