zoukankan      html  css  js  c++  java
  • lua的Metatables和Metamethods

    Metatable:

      lua中的每一个表都有其Metatable,默认情况下Metatable为nil。可通过setmetatable函数设置或者改变一个表的Metatable,

    也可以通过getmetatable得到一个表的Metatable。任何一个表都可以是其它表的Metatable,可以多个表共享一个Metatable,

    一个表也可以是自身的Metatable。

    作用:使我们可以改变table的行为

      在metatable中设置__add, __sub, __mul, __div, __eq(等于), __lt(小于), __le(小于等于), __unm(负), __pow(幂),

    __concat (定义连接行为),__tostring等metamethod方法,可以实现类似于C++中运算符重载的效果。

    实例:

     1 Set = {}
     2 Set.mt = {}
     3 --setmetatable(Set.mt, Set.mt)
     4 
     5 function Set:new( t )
     6     local tb = t or {}
     7     setmetatable(tb, Set.mt)
     8     return tb
     9 end
    10 
    11 function Set.union( a, b )
    12     local res = Set.new()
    13     for _, k in pairs(a) do
    14         table.insert(res, k)
    15     end
    16     for _, k in pairs(b) do
    17         table.insert(res, k)
    18     end
    19     return res
    20 end
    21 
    22 function Set.tostring( set )
    23     local s = "{
    "
    24     local sep = "  "
    25     for _, k in pairs(set) do
    26         if type(k) == "boolean" then
    27             k = "boolean"
    28         elseif type(k) == "function" then
    29             k = "function"
    30         elseif type(k) == "table" then
    31             -- k = "table"
    32             k = Set.tostring(k)
    33         end
    34         s = s.."   [".._.."]".." = "..k.."
    "
    35     end
    36     return s.."}"
    37 end
    38 
    39 local s1 = Set:new({10, 20, 30, 50})
    40 local s2 = Set:new({40, 60})
    41 
    42 Set.mt.__add = Set.union
    43 Set.mt.__concat = Set.union
    44 Set.mt.__tostring = Set.tostring
    45 
    46 local s3 = s1 + s2
    47 print(s3)
    48 
    49 local s4 = s1..s2
    50 print(s4)
    51 
    52 这里+和..是等价的,所以s3==s4,结果:
    53 {
    54     [1] = 10
    55     [2] = 20
    56     [3] = 30
    57     [4] = 40
    58     [5] = 50
    59     [6] = 60
    60 }
    61 {
    62     [1] = 10
    63     [2] = 20
    64     [3] = 30
    65     [4] = 40
    66     [5] = 50
    67     [6] = 60
    68 }

      当我们对两个有不同metatable的表进行加操作时,则检查第一个表的metatabled是否有__add,有则用之,
    没有就再去检查第二个表。。。还没有就报错。

    __index:

      我们在访问一个表不存在的域时,lua解释器会去查找metatabled中是否有__index方法(metamethod),如果不存在则返回nil,

    否则由__index方法返回。__index可以是函数也可以是表

     1 Set.mt.__index = function (tb, key)--这里会传进来一个table和key
     2     return "get "..key.." is null"
     3 end
     4 
     5 -- Set.mt.__index = {sex = "man"}
     6 
     7 local myTb = Set:new({name = "Mical", age = 12})
     8 print(myTb.name)
     9 print(myTb.age)
    10 print(myTb.sex)
    11 
    12 结果:
    13 Mical
    14 12
    15 get sex is null

    __newindex:

       当你给表不存在的值赋值时,lua解释器就会查找对应Metatable中的__newindex方法(metamethod)。如果存在则调用这个函数而不进行赋值。

    1 Set.mt.__newindex = function (tb, key, val)
    2     print("can't set "..key.." value")
    3 end
    4 local myTb = Set:new({name = "Mical", age = 12})
    5 myTb.sex = "man"

    结果:
    can't set sex value

    rawset(t, k, v):

      不调用任何的metamethod 对表t的k域赋值为v,上述例子就可以用rawset(myTb, "sex", "man")赋值。

    给表设置默认值:

    1 local key = {}
    2 local mt = {__index = function(t) return t[key] end}
    3 function setDefault(t, d)
    4     t[key] = d
    5     setmetatable(t, mt)
    6 end

    监控表: 

    index用于查询,__newindex用于更新,它们都是在访问的域不存在的时候才起作用。如果我们想捕获对一个表的所有反问,可以通过保持这个表为空来实现。

     1 local index = {}
     2 local mt = { __index = function(t, k)
     3                 print("access to element "..tostring(k))
     4                 return t[index][k]
     5                 end,
     6              __newindex = function(t, k, v)
     7                 print("update of element "..tostring(k).." to "..tostring(v))
     8                 t[index][k] = v
     9                 end
    10             }
    11 local function track(t)
    12     local proxy = {}
    13     proxy[index] = t
    14     setmetatable(proxy, mt)
    15     return proxy
    16 end
    17 
    18 local myTb = {}
    19 local nowTb = track(myTb)
    20 nowTb.name = "mical"
    21 print(nowTb.name)
    22 
    23 nowTb.age = 12
    24 print(nowTb.sex)
    25 
    26 for k, v in pairs(nowTb) do
    27     print(k, "
    ", v);
    28 end
  • 相关阅读:
    水题大战Vol.3 B. DP搬运工2
    火题小战 C. 情侣?给我烧了!
    火题小战 B. barbeque
    火题小战 A.玩个球
    P6087 [JSOI2015]送礼物 01分数规划+单调队列+ST表
    NOI2020D1T1美食家
    Java 的随机数
    MySQL 配置文件的配置
    NOIP2020准(ge)备(zi)日记
    android开发EditText禁止输入中文密码的解决方法
  • 原文地址:https://www.cnblogs.com/wrbxdj/p/5365272.html
Copyright © 2011-2022 走看看