zoukankan      html  css  js  c++  java
  • lua元表

    转载:https://www.cnblogs.com/blueberryzzz/p/8947446.html

    元表的作用

    元表是用来定义对table或userdata操作方式的表

    举个例子

    local t1 = {1}
    local t2 = {2}
    local t3 = t1 + t2

    我们直接对两个table执行+运算,会报错

    lua: /usercode/file.lua:3: attempt to perform arithmetic on local 't1' (a table value)

    因为程序不知道如何对两个表执行+运行,这时候就需要通过元表来定义如何执行t1的+运算,有点类似于c语言中的运算符重载。

    local mt = {}
    --定义mt.__add元方法(其实就是元表中一个特殊的索引值)为将两个表的元素合并后返回一个新表
    mt.__add = function(t1,t2)
        local temp = {}
        for _,v in pairs(t1) do
            table.insert(temp,v)
        end
        for _,v in pairs(t2) do
            table.insert(temp,v)
        end
        return temp
    end
    local t1 = {1,2,3}
    local t2 = {2}
    --设置t1的元表为mt
    setmetatable(t1,mt)
    
    local t3 = t1 + t2
    --输出t3
    local st = "{"
    for _,v in pairs(t3) do
        st = st..v..", "
    end
    st = st.."}"
    print(st)

    结果为:

    {1, 2, 3, 2, }

    因为程序在执行t1+t2的时候,会去调用t1的元表mt的__add元方法进行计算。
    具体的过程是:
    1.查看t1是否有元表,若有,则查看t1的元表是否有__add元方法,若有则调用。
    2.查看t2是否有元表,若有,则查看t2的元表是否有__add元方法,若有则调用。
    3.若都没有则会报错。
    所以说,我们通过定义了t1元表的__add元方法,达到了让两个表通过+号来相加的效果

    元表的元方法

    函数描述
    __add 运算符 +
    __sub 运算符 -
    __mul 运算符 *
    __ div 运算符 /
    __mod 运算符 %
    __unm 运算符 -(取反)
    __concat 运算符 ..
    __eq 运算符 ==
    __lt 运算符 <
    __le 运算符 <=
    __call 当函数调用
    __tostring 转化为字符串
    __index 调用一个索引
    __newindex 给一个索引赋值

    由于那几个运算符使用类似,所以就不单独说明了,接下来说 __call, __tostring, __index, __newindex四个元方法。

    __call

    __call可以让table当做一个函数来使用。

    local mt = {}
    --__call的第一参数是表自己
    mt.__call = function(mytable,...)
        --输出所有参数
        for _,v in ipairs{...} do
            print(v)
        end
    end
    
    t = {}
    setmetatable(t,mt)
    --将t当作一个函数调用
    t(1,2,3)

    结果:

    1
    2
    3

    __tostring

    __tostring可以修改table转化为字符串的行为

    local mt = {}
    --参数是表自己
    mt.__tostring = function(t)
        local s = "{"
        for i,v in ipairs(t) do
            if i > 1 then
                s = s..", "
            end
            s = s..v
        end
        s = s .."}"
        return s
    end
    
    t = {1,2,3}
    --直接输出t
    print(t)
    --将t的元表设为mt
    setmetatable(t,mt)
    --输出t
    print(t)

    结果:

    table: 0x14e2050
    {1, 2, 3}

    __index

    调用table的一个不存在的索引时,会使用到元表的__index元方法,和前几个元方法不同,__index可以是一个函数也可是一个table。
    作为函数:
    将表和索引作为参数传入__index元方法,return一个返回值

    local mt = {}
    --第一个参数是表自己,第二个参数是调用的索引
    mt.__index = function(t,key)
        return "it is "..key
    end
    
    t = {1,2,3}
    --输出未定义的key索引,输出为nil
    print(t.key)
    setmetatable(t,mt)
    --设置元表后输出未定义的key索引,调用元表的__index函数,返回"it is key"输出
    print(t.key)

    结果:

    nil
    it is key

    作为table:
    查找__index元方法表,若有该索引,则返回该索引对应的值,否则返回nil

    local mt = {}
    mt.__index = {key = "it is key"}
    
    t = {1,2,3}
    --输出未定义的key索引,输出为nil
    print(t.key)
    setmetatable(t,mt)
    --输出表中未定义,但元表的__index中定义的key索引时,输出__index中的key索引值"it is key"
    print(t.key)
    --输出表中未定义,但元表的__index中也未定义的值时,输出为nil
    print(t.key2)

    结果:

    nil
    it is key
    nil

    __newindex

    当为table中一个不存在的索引赋值时,会去调用元表中的__newindex元方法
    作为函数
    __newindex是一个函数时会将赋值语句中的表、索引、赋的值当作参数去调用。不对表进行改变

    local mt = {}
    --第一个参数时表自己,第二个参数是索引,第三个参数是赋的值
    mt.__newindex = function(t,index,value)
        print("index is "..index)
        print("value is "..value)
    end
    
    t = {key = "it is key"}
    setmetatable(t,mt)
    --输出表中已有索引key的值
    print(t.key)
    --为表中不存在的newKey索引赋值,调用了元表的__newIndex元方法,输出了参数信息
    t.newKey = 10
    --表中的newKey索引值还是空,上面看着是一个赋值操作,其实只是调用了__newIndex元方法,并没有对t中的元素进行改动
    print(t.newKey)

    结果:

    it is key
    index is newKey
    value is 10
    nil

    作为table
    __newindex是一个table时,为t中不存在的索引赋值会将该索引和值赋到__newindex所指向的表中,不对原来的表进行改变。

    local mt = {}
    --将__newindex元方法设置为一个空表newTable
    local newTable = {}
    mt.__newindex = newTable
    t = {}
    setmetatable(t,mt)
    print(t.newKey,newTable.newKey)
    --对t中不存在的索引进行负值时,由于t的元表中的__newindex元方法指向了一个表,所以并没有对t中的索引进行赋值操作将,而是将__newindex所指向的newTable的newKey索引赋值为了"it is newKey"
    t.newKey = "it is newKey"
    print(t.newKey,newTable.newKey)

    结果:

    nil nil
    nil it is newKey

    rawget 和 rawset

    有时候我们希望直接改动或获取表中的值时,就需要rawget和rawset方法了。
    rawget可以让你直接获取到表中索引的实际值,而不通过元表的__index元方法。

    local mt = {}
    mt.__index = {key = "it is key"}
    t = {}
    setmetatable(t,mt)
    print(t.key)
    --通过rawget直接获取t中的key索引
    print(rawget(t,"key"))

    结果:

    it is key
    nil

    rawset可以让你直接为表中索引的赋值,而不通过元表的__newindex元方法。

    local mt = {}
    local newTable = {}
    mt.__newindex = newTable
    t = {}
    setmetatable(t,mt)
    print(t.newKey,newTable.newKey)
    --通过rawset直接向t的newKey索引赋值
    rawset(t,"newKey","it is newKey")
    print(t.newKey,newTable.newKey)

    结果:

    nil nil
    it is newKey    nil

    元表的使用场景

    作为table的元表

    通过为table设置元表可以在lua中实现面向对象编程。

    作为userdata的元表

    通过对userdata和元表可以实现在lua中对c中的结构进行面向对象式的访问。

  • 相关阅读:
    设计模式之观察者模式
    设计模式之备忘录模式
    设计模式之中介者模式
    设计模式之迭代器模式
    设计模式之解释器模式
    设计模式之命令模式
    设计模式之职责链模式
    设计模式之代理模式
    设计模式之享元模式
    设计模式之外观模式
  • 原文地址:https://www.cnblogs.com/eggtomcato/p/10411530.html
Copyright © 2011-2022 走看看