OpenResty Lua 元素

2021-08-12 16:30 更新

在 Lua 5.1 語(yǔ)言中,元表 (metatable) 的表現(xiàn)行為類似于 C++ 語(yǔ)言中的操作符重載,例如我們可以重載 "__add" 元方法 (metamethod),來(lái)計(jì)算兩個(gè) Lua 數(shù)組的并集;或者重載 "__index" 方法,來(lái)定義我們自己的 Hash 函數(shù)。Lua 提供了兩個(gè)十分重要的用來(lái)處理元表的方法,如下:

  • setmetatable(table, metatable):此方法用于為一個(gè)表設(shè)置元表。
  • getmetatable(table):此方法用于獲取表的元表對(duì)象。

設(shè)置元表的方法很簡(jiǎn)單,如下:

local mytable = {}
local mymetatable = {}
setmetatable(mytable, mymetatable)

上面的代碼可以簡(jiǎn)寫(xiě)成如下的一行代碼:

local mytable = setmetatable({}, {})

修改表的操作符行為

通過(guò)重載 "__add" 元方法來(lái)計(jì)算集合的并集實(shí)例:

local set1 = {10, 20, 30}   -- 集合
local set2 = {20, 40, 50}   -- 集合

-- 將用于重載__add的函數(shù),注意第一個(gè)參數(shù)是self
local union = function (self, another)
    local set = {}
    local result = {}

    -- 利用數(shù)組來(lái)確保集合的互異性
    for i, j in pairs(self) do set[j] = true end
    for i, j in pairs(another) do set[j] = true end

    -- 加入結(jié)果集合
    for i, j in pairs(set) do table.insert(result, i) end
    return result
end
setmetatable(set1, {__add = union}) -- 重載 set1 表的 __add 元方法

local set3 = set1 + set2
for _, j in pairs(set3) do
    io.write(j.." ")               -->output:30 50 20 40 10
end

除了加法可以被重載之外,Lua 提供的所有操作符都可以被重載:

元方法含義
"__add"+ 操作
"__sub"- 操作 其行為類似于 "add" 操作
"__mul"* 操作 其行為類似于 "add" 操作
"__div"/ 操作 其行為類似于 "add" 操作
"__mod"% 操作 其行為類似于 "add" 操作
"__pow"^ (冪)操作 其行為類似于 "add" 操作
"__unm"一元 - 操作
"__concat".. (字符串連接)操作
"__len"# 操作
"__eq"== 操作 函數(shù) getcomphandler 定義了 Lua 怎樣選擇一個(gè)處理器來(lái)作比較操作 僅在兩個(gè)對(duì)象類型相同且有對(duì)應(yīng)操作相同的元方法時(shí)才起效
"__lt"< 操作
"__le"<= 操作

除了操作符之外,如下元方法也可以被重載,下面會(huì)依次解釋使用方法:

元方法含義
"__index"取下標(biāo)操作用于訪問(wèn) table[key]
"__newindex"賦值給指定下標(biāo) table[key] = value
"__tostring"轉(zhuǎn)換成字符串
"__call"當(dāng) Lua 調(diào)用一個(gè)值時(shí)調(diào)用
"__mode"用于弱表(week table)
"__metatable"用于保護(hù)metatable不被訪問(wèn)

__index 元方法

下面的例子中,我們實(shí)現(xiàn)了在表中查找鍵不存在時(shí)轉(zhuǎn)而在元表中查找該鍵的功能:

mytable = setmetatable({key1 = "value1"},   --原始表
  {__index = function(self, key)            --重載函數(shù)
    if key == "key2" then
      return "metatablevalue"
    end
  end
})

print(mytable.key1,mytable.key2)  --> output:value1 metatablevalue

關(guān)于 __index 元方法,有很多比較高階的技巧,例如:__index 的元方法不需要非是一個(gè)函數(shù),他也可以是一個(gè)表。

t = setmetatable({[1] = "hello"}, {__index = {[2] = "world"}})
print(t[1], t[2])   -->hello world

第一句代碼有點(diǎn)繞,解釋一下:先是把 {__index = {}} 作為元表,但 __index 接受一個(gè)表,而不是函數(shù),這個(gè)表中包含 [2] = "world" 這個(gè)鍵值對(duì)。 所以當(dāng) t[2] 去在自身的表中找不到時(shí),在 __index 的表中去尋找,然后找到了 [2] = "world" 這個(gè)鍵值對(duì)。

__index 元方法還可以實(shí)現(xiàn)給表中每一個(gè)值賦上默認(rèn)值;和 __newindex 元方法聯(lián)合監(jiān)控對(duì)表的讀取、修改等比較高階的功能,待讀者自己去開(kāi)發(fā)吧。

__tostring 元方法

與 Java 中的 toString() 函數(shù)類似,可以實(shí)現(xiàn)自定義的字符串轉(zhuǎn)換。

arr = {1, 2, 3, 4}
arr = setmetatable(arr, {__tostring = function (self)
    local result = '{'
    local sep = ''
    for _, i in pairs(self) do
        result = result ..sep .. i
        sep = ', '
    end
    result = result .. '}'
    return result
end})
print(arr)  --> {1, 2, 3, 4}

__call 元方法

__call 元方法的功能類似于 C++ 中的仿函數(shù),使得普通的表也可以被調(diào)用。

functor = {}
function func1(self, arg)
  print ("called from", arg)
end

setmetatable(functor, {__call = func1})

functor("functor")  --> called from functor
print(functor)      --> output:0x00076fc8 (后面這串?dāng)?shù)字可能不一樣)

__metatable 元方法

假如我們想保護(hù)我們的對(duì)象使其使用者既看不到也不能修改 metatables。我們可以對(duì) metatable 設(shè)置了 __metatable 的值,getmetatable 將返回這個(gè)域的值,而調(diào)用 setmetatable 將會(huì)出錯(cuò):

Object = setmetatable({}, {__metatable = "You cannot access here"})

print(getmetatable(Object)) --> You cannot access here
setmetatable(Object, {})    --> 引發(fā)編譯器報(bào)錯(cuò)


以上內(nèi)容是否對(duì)您有幫助:
在線筆記
App下載
App下載

掃描二維碼

下載編程獅App

公眾號(hào)
微信公眾號(hào)

編程獅公眾號(hào)