Lua - 元表

  • 简述

    元表是一个表,它有助于在键集和相关元方法的帮助下修改它所附加的表的行为。这些元方法是强大的 Lua 功能,可实现以下功能:
    • 为表上的运算符更改/添加功能。
    • 当键在表中不可用时使用元表中的 __index 查找元表。
    有两种重要的方法用于处理元表,包括 -
    • setmetatable(table,metatable) − 此方法用于为表设置元表。
    • getmetatable(table) - 此方法用于获取表的元表。
    让我们首先看看如何将一个表设置为另一个表的元表。它如下所示。
    
    mytable = {}
    mymetatable = {}
    setmetatable(mytable,mymetatable)
    
    上面的代码可以用一行表示,如下所示。
    
    mytable = setmetatable({},{})
    
  • __index

    当元表在表中不可用时用于查找元表的元表的简单示例如下所示。
    
    mytable = setmetatable({key1 = "value1"}, {
       __index = function(mytable, key)
        
          if key == "key2" then
             return "metatablevalue"
          else
             return mytable[key]
          end
       end
    })
    print(mytable.key1,mytable.key2)
    
    当我们运行上面的程序时,我们将得到以下输出。
    
    value1 metatablevalue
    
    让我们分步解释上面例子中发生的事情。
    • 这里的表 mytable 是 {key1 = "value1"}.
    • Metatable 是为包含 __index 函数的 mytable 设置的,我们称之为元方法。
    • 元方法做了一个简单的查找索引“key2”的工作,如果找到,它返回“metatablevalue”,否则返回对应索引的 mytable 值。
    我们可以有一个上述程序的简化版本,如下所示。
    
    mytable = setmetatable({key1 = "value1"}, 
       { __index = { key2 = "metatablevalue" } })
    print(mytable.key1,mytable.key2)
    
  • __newindex

    当我们将 __newindex 添加到元表时,如果表中没有可用的键,则新键的行为将由元方法定义。下面给出了一个简单的例子,当主表中没有索引时,设置元表的索引。
    
    mymetatable = {}
    mytable = setmetatable({key1 = "value1"}, { __newindex = mymetatable })
    print(mytable.key1)
    mytable.newkey = "new value 2"
    print(mytable.newkey,mymetatable.newkey)
    mytable.key1 = "new  value 1"
    print(mytable.key1,mymetatable.newkey1)
    
    当你运行上面的程序时,你会得到以下输出。
    
    value1
    nil new value 2
    new  value 1    nil
    
    你可以在上面的程序中看到,如果主表中存在一个键,它只会更新它。当主表中的键不可用时,它会将该键添加到元表中。
    下面显示了使用 rawset 函数更新同一个表的另一个示例。
    
    mytable = setmetatable({key1 = "value1"}, {
       __newindex = function(mytable, key, value)
          rawset(mytable, key, "\""..value.."\"")
       end
    })
    mytable.key1 = "new value"
    mytable.key2 = 4
    print(mytable.key1,mytable.key2)
    
    当我们运行上面的程序时,我们将得到以下输出。
    
    new value   "4"
    
    rawset 在不使用元表的 __newindex 的情况下设置值。类似地,rawget 可以在不使用 __index 的情况下获取值。
  • 向表中添加运算行为

    使用 &plus 运算符组合两个表的简单示例如下所示 -
    
    mytable = setmetatable({ 1, 2, 3 }, {
       __add = function(mytable, newtable)
        
          for i = 1, table.maxn(newtable) do
             table.insert(mytable, table.maxn(mytable)+1,newtable[i])
          end
          return mytable
       end
    })
    secondtable = {4,5,6}
    mytable = mytable + secondtable
    for k,v in ipairs(mytable) do
       print(k,v)
    end
    
    当我们运行上面的程序时,我们将得到以下输出。
    
    1   1
    2   2
    3   3
    4   4
    5   5
    6   6
    
    __add 键包含在元表中以添加运算符 &plus 的行为。按键表和对应的操作符如下所示。
    序号 模式和描述
    1
    __add
    更改运算符“&plus”的行为。
    2
    __sub
    更改运算符“-”的行为。
    3
    __mul
    更改运算符“*”的行为。
    4
    __div
    更改运算符“/”的行为。
    5
    __mod
    更改运算符“%”的行为。
    6
    __unm
    更改运算符“-”的行为。
    7
    __concat
    更改运算符“...”的行为。
    8
    __eq
    更改运算符“==”的行为。
    9
    __lt
    更改运算符“<”的行为。
    10
    __le
    更改运算符“<=”的行为。
  • __call

    添加方法调用的行为是使用 __call 语句完成的。一个简单的例子,返回主表中的值与传递的表的总和。
    
    mytable = setmetatable({10}, {
       __call = function(mytable, newtable)
       sum = 0
        
          for i = 1, table.maxn(mytable) do
             sum = sum + mytable[i]
          end
        
          for i = 1, table.maxn(newtable) do
             sum = sum + newtable[i]
          end
        
          return sum
       end
    })
    newtable = {10,20,30}
    print(mytable(newtable))
    
    当我们运行上面的程序时,我们将得到以下输出。
    
    70
    
  • __tostring

    要更改打印语句的行为,我们可以使用 __tostring 元方法。一个简单的例子如下所示。
    
    mytable = setmetatable({ 10, 20, 30 }, {
       __tostring = function(mytable)
       sum = 0
        
          for k, v in pairs(mytable) do
             sum = sum + v
          end
            
          return "The sum of values in the table is " .. sum
       end
    })
    print(mytable)
    
    当我们运行上面的程序时,我们将得到以下输出。
    
    The sum of values in the table is 60
    
    如果你完全了解元表的功能,你真的可以执行很多不使用它会非常复杂的操作。因此,请尝试更多地使用具有元表中可用的不同选项的元表,如示例中所述,并创建您自己的示例。