Lua:元表(metatable)与元方法(meatmethod)

原文地址为: Lua:元表(metatable)与元方法(meatmethod)

  • 元表概念:

    • 引言:Lua中的每个值都有一套预定义的操作集合,如数字相加等。但无法将两个table相加,此时可通过元表修改一个值的行为,使其在面对一个非预定义的操作时执行一个指定操作。

    • 访问机制:一般的元方法都只针对Lua的核心,也就是一个虚拟机。它会检测一个操作中的值是否有元表,这些元表是否定义了关于次操作的元方法。例如两个table相加,先检查两者之一是否有元表,之后检查是否有一个叫“__add”的字段,若找到,则调用对应的值。“__add”等即时字段,其对应的值(往往是一个函数或是table)就是“元方法”。

  • 元表实例

    • setmetatable(只能用于table)和getmetatable(用于任何对象)

      • 语法:setmetatable (table, metatable),对指定table设置metatable      【如果元表(metatable)中存在__metatable键值,setmetatable会失败】

      • 语法:tmeta = getmetatable (tab),返回对象的元表(metatable)             【如果元表(metatable)中存在__metatable键值,当返回__metatable的值】

      • 代码:

        print(getmetatable("lua")) -->table: 002F19B8
        print(getmetatable(10)) -->nil


        --使用__metatable可以保护元表,禁止用户访问元表中的成员或者修改元表。
        tA = {}
        mt = {}
        getmetatable(tA, mt)
        mt.__metatable = "lock"
        setmetatable(tA, mt)
        print(getmetatable(tA)) -->lock
    • 算术类元方法:     字段:__add  __mul  __ sub  __div  __unm  __mod  __pow  (__concat)
      • 代码:(两个table相加)
        tA = {1, 3}
        tB = {5, 7}

        --tSum = tA + tB
        mt = {}

        mt.__add = function(t1, t2)
        for _, item in ipairs(t2) do
        table.insert(t1, item)
        end
        return t1
        end

        setmetatable(tA, mt)

        tSum = tA + tB

        for k, v in pairs(tSum) do
        print(v)
        end
    • 关系类元方法: 字段:__eq __lt(<) __le(<=),其他Lua自动转换 a~=b --> not(a == b) a > b --> b < a a >= b --> b <= a 【注意NaN的情况】
      • 代码:
        --比较集合大小 <
        mt = {}
        function mt.__lt(tA, tB)
        return #tA < #tB
        end

        tA, tB = {3}, {1, 2}

        setmetatable(tA, mt)
        setmetatable(tB, mt)

        print(tA < tB)
    • table访问的元方法: 字段: __index __newindex

      • __index:
            查询:访问表中不存的字段
            rawget(t, i)

      • __newindex:
            更新:向表中不存在索引赋值
            rawswt(t, k, v)

  • 贯穿《Programming in Lua》元表与元方法整张的实例

    --[[
    Set = {}
    mt = {} --元表

    function Set.new(l)
    local set = {}
    setmetatable(set, mt)
    for _, v in ipairs(l) do
    set[v] = true
    end
    return set
    end


    --================================================
    function Set.tostring(set)
    local l = {}
    for e in pairs(set) do
    l[#l + 1] = e
    end
    return "{" .. table.concat(l, ",") .. "}"
    end


    function Set.print(s)
    print(Set.tostring(s))
    end


    --1 加(__add), 并集===============================
    function Set.union(a, b)
    --[[ if getmetatable(a) ~= mt or getmetatable(b) ~= mt then
    error("attemp to 'add' a set with a non-set value", 2) --error第二个参数的含义P116
    end]]
    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

    s1 = Set.new{10, 20, 30, 50}
    s2 = Set.new{30, 1}
    --print(getmetatable(s1))
    --print(getmetatable(s2))

    mt.__add = Set.union

    s3 = s1 + s2
    --Set.print(s3)

    --[[元表混用
    s = Set.new{1, 2, 3}
    s = s + 8
    Set.print(s + 8)
    ]]

    --2 乘(__mul), 交集==============================
    function Set.intersection(a, b)
    local res = Set.new{}
    for k in pairs(a) do
    res[k] = b[k]
    end
    return res
    end

    mt.__mul = Set.intersection

    --Set.print(s2 * s1)



    --3 关系类===================================NaN的概念====
    mt.__le = function(a, b)
    for k in pairs(a) do
    if not b[k] then return false end
    end
    return true
    end

    mt.__lt = function(a, b)
    return a <= b and not (b <= a)
    end

    mt.__eq = function(a, b) --竟然能这么用!?----
    return a <= b and b <= a
    end

    g1 = Set.new{2, 4, 3}
    g2 = Set.new{4, 10, 2}
    print(g1 <= g2)
    print(g1 < g2)
    print(g1 >= g2)
    print(g1 > g2)
    print(g1 == g1 * g2)

    --]]

    --============================================
    --4 table访问的元方法=========================
    --[[
    --__index有关继承的典型示例
    Window = {}
    Window.prototype = {x = 0, y = 0, width = 100, height}
    Window.mt = {}

    function Window.new(o)
    setmetatable(o, Window.mt)
    return o
    end

    Window.mt.__index = function (table, key)
    return Window.prototype[key]
    end

    w = Window.new{x = 10, y = 20}
    print(w.width)

    --__index修改table默认值
    function setDefault (t, d)
    local mt = {__index = function () return d end}
    setmetatable(t, mt)
    end

    tab = {x = 10, y = 20}
    print(tab.x, tab.z)
    setDefault(tab, 0)
    print(tab.x, tab.z)

    --]]

    --13.4.5 只读的table
    function readOnly(t)
    local proxy = {}
    local mt = {
    __index = t,
    __newindex = function(t, k, v)
    error("attempt to update a read-only table", 2)
    end
    }
    setmetatable(proxy, mt)
    return proxy
    end

    days = readOnly{"Sunday", "Monday", "Tuesday", "W", "T", "F", "S"}
    print(days[1])
    days[2] = "Noday"

  •            

 


转载请注明本文地址: Lua:元表(metatable)与元方法(meatmethod)

猜你喜欢

转载自blog.csdn.net/kkwant/article/details/81272253