17.3 重述带有默认值的表 |
在章节13.4.3,我们讨论了怎样使用非nil的默认值来实现表。我们提到一种特殊的技术并注释说另外两种技术需要使用weak tables,所以我们推迟在这里介绍他们。现在,介绍她们的时候了。就像我们说的那样,这两种默认值的技术实际上来源于我们前面提到的两种通用的技术的特殊应用:对象属性和记忆。
在第一种解决方案中,我们使用weak table来将默认vaules和每一个table相联系:
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
如果默认值没有weak的keys,它就会将所有的带有默认值的tables设定为永久存在。在第二种方法中,我们使用不同的metatables来保存不同的默认值,但当我们重复使用一个默认值的时候,重用同一个相同的metatable。这是一个典型的记忆技术的应用:
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
这种情况下,我们使用weak vaules,允许将不会被使用的metatables可以被回收。
把这两种方法放在一起,哪个更好?通常,取决于具体情况。它们都有相似的复杂性和相似的性能。第一种方法需要在每个默认值的tables中添加一些文字(一个默认的入口)。第二种方法需要在每个不同的默认值加入一些文字(一个新的表,一个新的闭包,metas中新增入口)。所以,如果你的程序有数千个tables,而这些表只有很少数带有不同默认值的,第二种方法显然更优秀。另一方面,如果只有很少的tabels可以共享相同的默认vaules,那么你还是用第一种方法吧。