Lua元表


元表(metatable)是一個表,它是使用鍵集和相關元方法來修改附加到的表的行為。 這些元方法是強大的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"}
  • 元表設定為mytable,其中包含__index的函式,它稱為元方法。
  • 元方法執行查詢索引key2,如果找到它,則返回metatablevalue,否則返回相應索引的mytable值。

上述程式的簡化版本,如下所示 -

mytable = setmetatable({key1 = "value1"},{ __index = { key2 = "metatablevalue" } })
print(mytable.key1,mytable.key2)

__newindex

當將__newindex新增到metatable時,如果表中沒有鍵,則新鍵的行為將由元方法定義。 下面給出了當主表中沒有索引時設定metatable索引的簡單範例。

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

在上面的程式中看到,如果主表中存在一個鍵,它只會更新它。 當維護中的鍵不可用時,它會將該鍵新增到metatable中。

使用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的情況下獲取值。

向表中新增運算子行為

使用+運算子組合兩個表的簡單範例如下所示 -

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鍵包含在元表中以新增運算子 + 的行為。鍵表和相應的操作符如下所示。

編號 模式 描述
1 __add 改變運算子+的行為。
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

要更改print語句的行為,可以使用__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

如果完全了解元表的功能,那麼可以真正執行很多非常複雜的操作。 因此,嘗試使用元表中可用的不同選項的元表更多地工作。