元表与元方法

元表与元方法

简述:系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)  

猜你喜欢

转载自blog.csdn.net/u013320664/article/details/80061210