元表与元方法
简述:系lua程序设计第13章笔记,并做了一些拓展,lua版本5.3
----------------------------
--集合计算,算术元方法
----------------------------
local set = {}
local mt = {}
function set.new(l)
local t = {}
setmetatable(t, mt)
for _, v in pairs(l) do
t[v] = true
end
return t
end
function set.intersection(a, b)
local res = set.new{}
for k in pairs(a) do
res[k] = b[k]
end
return res
end
function set.reduce(a, b)
local res = set.new{}
for k in pairs(a) do
res[k] = not b[k] or nil
end
return res
end
function set.tostring(a)
local str = {}
for k in pairs(a) do
table.insert(str, k)
end
return string.format("{%s}", table.concat(str, ","))
end
function set.check(a, b)
if getmetatable(a) ~= getmetatable(b) then
error("dont have calc fun", 2)
end
end
function set.union(a, b)
set.check(a, b)
local res = set.new{}
for k in pairs(a) do res[k] = true end
for k in pairs(b) do res[k] = true end
return res
end
mt.__add = set.union
mt.__mul = set.intersection
mt.__sub = set.reduce
mt.__tostring = set.tostring
-----------------------------------------------------------------------------------------
--关系类元方法
--需要定义 __eq 等于, __lt 小于, __le 小于等于 以上可转换为其他所需关系
--在集合关系中只需定义小于等于 因为只需判断是否为子集(即所有元素是否包含在另一集合中)
-----------------------------------------------------------------------------------------
function set.__le(a, b)
for k,v in pairs(a) do
if not b[k] then return false end
end
return true
end
function set.__lt(a, b)
-- "a >= b" 即 b <= a ;
return a <= b and not (a >= b)
end
function set.__eq(a, b)
-- 互为子集 即相等
return a <= b and a >= b
end
mt.__le = set.__le
mt.__lt = set.__lt
mt.__eq = set.__eq
local res1 = set.new{"a", "b", "d"}
local res2 = set.new{"a", "b", "d"}
-- print(res1)
-- print(res2 + 8)
-- print("cd------------>what", res1 == res2)
-- print("cd------------>what", res1 <= res2)
-- print("cd------------>what", res1 >= res2)
-------------------------------------
--__index, __newindex 元方法
-- 1 使用 {} 构造式 获得一个唯一地址的字符串, 即不会有重复风险的索引;
-- 2 表跟踪 代理表 空表->访问元表index 和 newindex方法; 代理表中持有原表引用
-------------------------------------
Window = {} --命名空间
Window.mt = {}
Window.new = function (t)
local t = t or {}
setmetatable(t, Window.mt)
return t
end
Window.prototype = {x = 1, y = 2}
Window.mt.__index = Window.prototype
--使用函数更灵活 但开销更大
Window.mt.__index = function (table, k)
-- table 调用该元方法的表
return Window.prototype[k]
end
--测试用表
local t = Window.new{x = 2, y = 3}
local t2 = Window.new{z = 666}
-- print("cd------------>__index",t.x)
-- print("cd------------>__index",t2.x)
--rawget(t, i) 绕过元表 调用 t[i]
-- print("cd------------>rawget",rawget(t2, "y"))
--对表中不存在的索引进行赋值时调用的 元方法
--传入 table key value
Window.mt.__newindex = function (...)
for k,v in pairs{...} do
-- print("newindex", k,v)
end
end
t2.x = 10 --存在newindex则 调用 否则才赋值
-- print("cd------------>t2.x ",t2.x)
--具有默认值的table
local key = {} --避免与表内索引重名 {}每次构造时地址都不一样,故不会有重名风险
local mt = {__index = function (t)
return t[key]
end, }
function setDefault(t, d)
t[key] = d
setmetatable(t, mt)
end
local d_t = {}
setDefault(d_t, 10)
-- print("cd------------>what",d_t.x)
--跟踪table访问记录
t = {x = 1, y = 4}
local _t = t --保持对原表的引用
t = {} -- 代理表 空表->访问元表index 和 newindex方法
local mt = {
__index = function (t, k)
print("cd------------>visit")
return _t[k]
end,
__newindex = function (t, k, v)
print("cd------------>set")
_t[k] = v
end,
}
setmetatable(t, mt)
t[2] = "hello"
--使用同一张元表跟踪不同的table 原table存储在代理中
local index = {}
local f_mt = {
__index = function (t, k)
print("cd------------>f_mt:visit")
return t[index][k]
end,
__newindex = function (t, k, v)
print("cd------------>f_mt:set")
t[index][k] = v
end,
}
function track(t)
local proxy = {}
proxy[index] = t
setmetatable(proxy, f_mt)
return proxy
end
local p_t = track{x = 1, y = 3}
p_t[1] = 3
--只读table
function readOnly(t)
local proxy = {}
--获得环境中的 t
local mt = {
__index = t,
__newindex = function (t, k, v)
error("attemp to write a readOnly table", 2)
end,
}
setmetatable(proxy, mt)
return proxy
end
local t = readOnly{x = 1, y = 3}
-- t.x = 1
print("cd------------>t.x",t.x)