【问题标题】:Difference Between Tables and Metatables in LuaLua中表和元表的区别
【发布时间】:2012-06-05 05:16:33
【问题描述】:

Corona 中的表和元表有什么区别?元表的类型有哪些?我如何以及在哪里可以使用它们?使用表和元表的主要目的是什么?

【问题讨论】:

    标签: lua coronasdk


    【解决方案1】:

    Lua 中的表是可用于创建动态结构化数据的主要数据类型。其他语言有数组、列表、字典(键值存储),在 Lua 中你只有表。您可以对基本表执行的唯一操作是使用 tab[key] 语法索引和存储值,即:

    local tab = {}
    tab['key1'] = 'Hello' -- storing a value using a string key
    tab.key2 = 'World'    -- this is syntax sugar, equivalent to previous
    print(tab.key1, tab['key2'])  -- indexing, the syntax is interchangable
    

    您不能对基本表做任何其他事情,例如添加它们:

    local v1={x=0,y=0}
    local v2={x=1,y=1}
    print(v1+v2)
    --> stdin:1: attempt to perform arithmetic on local 'v1' (a table value)
    

    metatable 允许您修改表的行为,指定在添加、相乘、连接 (..) 等表时应该执行的操作。元表只是一个表,其中包含带有特殊键的函数,也称为元方法。您可以使用setmetatable() 将元表分配给表。例如:

    local Vector = {} -- this will be the metatable for vectors
    
    function Vector.__add(v1, v2) -- what to do when vectors are added
        -- create a new table and assign it a Vector metatable
        return setmetatable({x=v1.x+v2.x, y=v1.y+v2.y}, Vector)
    end
    function Vector.__tostring(v) -- how a vector should be displayed
        -- this is used by tostring() and print()
        return '{x=' .. v.x .. ',y=' .. v.y .. '}'
    end
    
    local v1 = setmetatable({x=1, y=2}, Vector)
    local v2 = setmetatable({x=3, y=4}, Vector)
    
    -- vectors are added and the resulting vector is printed
    print(v1 + v2) --> {x=4,y=6}
    

    如果你想更好地理解元表,你绝对应该read the Programming in Lua chapter on metatables

    【讨论】:

      【解决方案2】:

      Lua(Corona 所基于的语言)将元表用于不同目的。

      手册中的相关条目是Section 2.8。 可以在herehere 找到一个不错的教程。

      元表与其他表一样只是一个表,但在另一个表上设置为元表(我将进一步将其称为基表,以在两个表之间产生差异)。

      元表可以包含任何内容,但特殊键(以双下划线开头)是有趣的键。在特殊情况下将调用此表中此键设置的值。哪个场合取决于哪​​个键。最有趣的是:

      • __index:每当在基表中查找一个键但不存在时将使用该键。这可以包含将在其中查找键的表,也可以包含将传递原始表和键的函数。这可用于在表上实现方法(OOP 样式)、重定向、失败案例、设置默认值等
      • __newindex:每当要在表中分配新键时都会使用(以前为 nil)。如果它是一个表,则将在该表中分配键。如果它是一个函数,该函数将传递原始表、键和值。这可用于控制对表的访问、预处理数据、重定向分配。
      • __call:使您可以设置要在使用例如时调用的函数。 table()
      • __add,__sub,__mul,__div,__mod用于实现二元运算,
      • __unm用于实现一元运算,
      • __concat 用于实现连接(.. 运算符)
      • __len 用于实现长度运算符(#)
      • __eq,__lt,__le 用于实现比较

      使用 __index & co. 时要知道的小事:在这些方法中,您应该使用 rawget 和 rawset 以防止每次再次调用元方法,从而导致循环。 举个小例子:

      t={1,2,3}  -- basetable
      mt={} -- metatable
      mt.__index=function(t,k)
          print("__index event from "..tostring(t).." key "..k)
          return "currently unavailable"
      end
      mt.__newindex=function(t,k,v)
          print("__newindex event from "..tostring(t).." key: "..k.." value: "..v)
          if type(k)=="string" then
              rawset(t,k,v:reverse())
          else
              rawset(t,k,v)
          end
      end
      mt.__call=function(t,...)
          print("call to table "..tostring(t).." with arguments: ".. table.concat({...},','))
          print("All elements of the table:")
          for k,v in pairs(t) do print(k,v) end
      end
      setmetatable(t,mt)
      
      t[4]="foo" -- this will run the __newindex method
      print(t[5]) -- this will run the __index method
      t("foo","bar")
      -- Multiple fall through example:
      t={}
      mt={}
      mt2={}
      setmetatable(t,mt)  -- metatable on base table
      setmetatable(mt,mt2) -- second layer of metatable
      mt.__index=function(t,k) print('key '..k..' not found in '..namelookup[t]) return getmetatable(t)[k] end -- tries looking nonexistant indexes up in mt.
      mt2.__index=mt.__index -- function was written portably, reuse it.
      
      t[1]='A'
      mt[2]='B'
      mt2[3]='C'
      namelookup={[t]="t",[mt]="mt",[mt2]="mt2"}
      print(t[1],t[2],t[3],t[4])
      

      现在这些只是愚蠢的例子,你可以做更复杂的事情。看例子,看Programming in Lua中的相关章节,进行实验。并尽量不要混淆;)

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2016-08-15
        • 1970-01-01
        • 2022-06-14
        • 2016-05-29
        • 1970-01-01
        • 2018-11-14
        • 1970-01-01
        • 2011-06-22
        相关资源
        最近更新 更多