主頁 > 知識庫 > Lua中的metatable詳解

Lua中的metatable詳解

熱門標簽:如何利用高德地圖標注家 揚州電銷外呼系統(tǒng)軟件 開通400電話申請流程 400手機電話免費辦理 武漢百應人工智能電銷機器人 電腦外呼系統(tǒng)輻射大嗎 上海企業(yè)外呼系統(tǒng)排名 百度地圖標注位置網(wǎng)站 智能語音電銷的機器人

Lua 中 metatable 是一個普通的 table,但其主要有以下幾個功能:

1.定義算術操作符和關系操作符的行為
2.為 Lua 函數(shù)庫提供支持
3.控制對 table 的訪問

Metatables 定義操作符行為

Metatable 能夠被用于定義算術操作符和關系操作符的行為。例如:Lua 嘗試對兩個 table 進行加操作時,它會按順序檢查這兩個 table 中是否有一個存在 metatable 并且這個 metatable 是否存在 __add 域,如果 Lua 檢查到了這個 __add 域,那么會調(diào)用它,這個域被叫做 metamethod。

Lua 中每個 value 都可以有一個 metatable(在 Lua 5.0 只有 table 和 userdata 能夠存在 metatable)。每個 table 和 userdata value 都有一個屬于自己的 metatable,而其他每種類型的所有 value 共享一個屬于本類型的 metatable。在 Lua 代碼中,通過調(diào)用 setmetatable 來設置且只能設置 table 的 metatable,在 C/C++ 中調(diào)用 Lua C API 則可以設置所有 value 的 metatable。默認的情況下,string 類型有自己的 metatable,而其他類型則沒有:

復制代碼 代碼如下:

print(getmetatable('hi')) --> table: 003C86B8
print(getmetatable(10))  --> nil

Metamethod 的參數(shù)為操作數(shù)(operands),例如:

復制代碼 代碼如下:

local mt = {}
function mt.__add(a, b)
    return 'table + ' .. b
end
local t = {}
setmetatable(t, mt)
print(t + 1)

每個算術操作符有對應的 metamethod:

+ __add
* __mul
- __sub
/ __div
- __unm (for negation)
% __mod
^ __pow

對于連接操作符有對應的 metamethod:__concat

同樣,對于關系操作符也都有對應的 metamethod:

== __eq
__lt
= __le

其他的關系操作符都是用上面三種表示:
a ~= b 表示為 not (a == b)
a > b 表示為 b a
a >= b 表示為 b = a

和算術運算符不同的是,關系運算符用于比較擁有不同的 metamethod(而非 metatable)的兩個 value 時會產(chǎn)生錯誤,例外是比較運算符,擁有不同的 metamethod 的兩個 value 比較的結果是 false。

不過要注意的是,在整數(shù)類型的比較中 a = b 可以被轉換為 not (b a),但是如果某類型的所有元素并未適當排序,此條件則不一定成立。例如:浮點數(shù)中 NaN(Not a Number)表示一個未定義的值,NaN = x 總是為 false 并且 x NaN 也總為 false。

為 Lua 函數(shù)庫提供支持

Lua 庫可以定義和使用的 metamethod 來完成一些特定的操作,一個典型的例子是 Lua Base 庫中 tostring 函數(shù)(print 函數(shù)會調(diào)用此函數(shù)進行輸出)會檢查并調(diào)用 __tostring metamethod:

復制代碼 代碼如下:

local mt = {}
mt.__tostring = function(t)
    return '{' .. table.concat(t, ', ') .. '}'
end
 
local t = {1, 2, 3}
print(t)
setmetatable(t, mt)
print(t)

另外一個例子是 setmetatable 和 getmetatable 函數(shù),它們定義和使用了 __metatable 域。如果你希望設定的 value 的 metatable 不被修改,那么可以在 value 的 metatable 中設置 __metatable 域,getmetatable 將返回此域,而 setmetatable 則會產(chǎn)生一個錯誤:

復制代碼 代碼如下:

mt.__metatable = "not your business"
local t = {}
setmetatable(t, mt)
print(getmetatable(t)) --> not your business
setmetatable(t, {})
    stdin:1: cannot change protected metatable

看一個完整的例子:

復制代碼 代碼如下:

Set = {}
 
local mt = {}
 
function Set.new(l)
    local set = {}
    -- 為 Set 設置 metatable
    setmetatable(set, mt)
    for _, v in ipairs(l) do set[v] = true end
    return set
end
 
function Set.union(a, b)
    -- 檢查 a b 是否都是 Set
    if getmetatable(a) ~= mt or getmetatable(b) ~= mt then
        -- error 的第二個參數(shù)為 level
        -- level 指定了如何獲取錯誤的位置
        -- level 值為 1 表示錯誤的位置為 error 函數(shù)被調(diào)用的位置
        -- level 值為 2 表示錯誤的位置為調(diào)用 error 的函數(shù)被調(diào)用的地方
        error("attempt to 'add' a set with a not-set value", 2)
    end
    local res = Set.new{}
    for k in pairs(a) do res[k] = true end
    for k in pairs(b) do res[k] = true end
    return res
end
 
function Set.intersection(a, b)
    local res = Set.new{}
    for k in pairs(a) do
        res[k] = b[k]
    end
    return res
end
 
mt.__add = Set.union
mt.__mul = Set.intersection
 
mt.__tostring = function(s)
    local l = {}
    for e in pairs(s) do
        l[#l + 1] = e
    end
    return '{' .. table.concat(l, ', ') .. '}'
end
 
mt.__le = function(a, b)
    for k in pairs(a) do
        if not b[k] then return false end
    end
    return true
end
 
mt.__lt = function(a, b)
    return a = b and not (b = a)
end
 
mt.__eq = function(a, b)
    return a = b and b = a
end
 
local s1 = Set.new({1, 2, 3})
local s2 = Set.new({4, 5, 6})
print(s1 + s2)
print(s1 ~= s2)

控制 table 的訪問

__index metamethod

在我們訪問 table 的不存在的域時,Lua 會嘗試調(diào)用 __index metamethod。__index metamethod 接受兩個參數(shù) table 和 key:

復制代碼 代碼如下:

local mt = {}
mt.__index = function(table, key)
    print('table -- ' .. tostring(table))
    print('key -- ' .. key)
end
 
local t = {}
setmetatable(t, mt)
local v = t.a

__index 域也可以是一個 table,那么 Lua 會嘗試在 __index table 中訪問對應的域:

復制代碼 代碼如下:

local mt = {}
mt.__index = {
    a = 'Hello World'
}
 
local t = {}
setmetatable(t, mt)
print(t.a) --> Hello World

我們通過 __index 可以容易的實現(xiàn)單繼承(類似于 JavaScrpit 通過 prototype 實現(xiàn)單繼承),如果 __index 是一個函數(shù),則可以實現(xiàn)更加復雜的功能:多重繼承、caching 等。我們可以通過 rawget(t, i) 來訪問 table t 的域 i,而不會訪問 __index metamethod,注意的是,不要太指望通過 rawget 來提高對 table 的訪問速度(Lua 中函數(shù)的調(diào)用開銷遠遠大于對表的訪問的開銷)。

__newindex metamethod

如果對 table 的一個不存在的域賦值時,Lua 將檢查 __newindex metamethod:

1.如果 __newindex 為函數(shù),Lua 將調(diào)用函數(shù)而不是進行賦值
2.如果 __newindex 為一個 table,Lua 將對此 table 進行賦值

如果 __newindex 為一個函數(shù),它可以接受三個參數(shù) table key value。如果希望忽略 __newindex 方法對 table 的域進行賦值,可以調(diào)用 rawset(t, k, v)

結合 __index 和 __newindex 可以實現(xiàn)很多功能,例如:

1.OOP
2.Read-only table
3.Tables with default values

Read-only table

復制代碼 代碼如下:

function readOnly(t)
    local proxy = {}
    local mt = {
        __index = t,
        __newindex = function(t, k, v)
            error('attempt to update a read-only table', 2)
        end
    }
    setmetatable(proxy, mt)
    return proxy
end
 
days = readOnly{'Sun', 'Mon', 'Tues', 'Wed', 'Thur', 'Fri', 'Sat'}
print(days[1])
days[2] = 'Noday' --> stdin:1: attempt to update a read-only table

有時候,我們需要為 table 設定一個唯一的 key,那么可以使用這樣的技巧:

復制代碼 代碼如下:

local key = {} -- unique key
local t = {}
t[key] = value

您可能感興趣的文章:
  • Lua的table庫函數(shù)insert、remove、concat、sort詳細介紹
  • Lua中table的幾種構造方式詳解
  • Lua中對table排序?qū)嵗?/li>
  • Lua中遍歷數(shù)組和table的4種方法
  • Lua中的table學習筆記
  • Lua中使用table.concat連接大量字符串實例
  • Lua中的table淺析
  • 獲取Lua表結構(table)數(shù)據(jù)實例
  • Lua教程之弱引用table
  • Lua Table轉C# Dictionary的方法示例

標簽:延邊 張掖 黑龍江 宜賓 江西 嘉峪關 武漢 新余

巨人網(wǎng)絡通訊聲明:本文標題《Lua中的metatable詳解》,本文關鍵詞  Lua,中的,metatable,詳解,Lua,;如發(fā)現(xiàn)本文內(nèi)容存在版權問題,煩請?zhí)峁┫嚓P信息告之我們,我們將及時溝通與處理。本站內(nèi)容系統(tǒng)采集于網(wǎng)絡,涉及言論、版權與本站無關。
  • 相關文章
  • 下面列出與本文章《Lua中的metatable詳解》相關的同類信息!
  • 本頁收集關于Lua中的metatable詳解的相關信息資訊供網(wǎng)民參考!
  • 推薦文章