Basics Lua learning Eight <Lua table yuan (Metatabble) && inheritance>

Element mentioned table, table look section merge operation.

t1 = {1,2}
t2 = {3,4}
t3 = t1 + t2
attempt to perform arithmetic on a table value (global 't1')

Program error, because they do not know how to execute the + operator, this time need to be defined by two element table Table, c is somewhat similar to the loading operator. We look at how to merge operations through metadata table.


local mt = {}
--定义mt.__add元方法(其实就是元表中一个特殊的索引值)为将两个表的元素合并后返回一个新表
mt.__add = function(t1,t2)
    local temp = {}
    for _,v in pairs(t1) do
        table.insert(temp,v)
    end
    for _,v in pairs(t2) do
        table.insert(temp,v)
    end
    return temp
end
local t1 = {1,2,3}
local t2 = {2}
--设置t1的元表为mt
setmetatable(t1,mt)

local t3 = t1 + t2
--输出t3
local st = "{"
for _,v in pairs(t3) do
    st = st..v..", "
end
st = st.."}"
print(st)
{1, 2, 3, 2, }

It can be seen in the implementation of the program, called mt._add element method.

Specific process is:
1. View the table t1 has yuan, and if so, to see whether there t1 metatable __add element method, if it is called.
2. Check whether meta t2 table, and if so, to see whether there is t2 metatable __add element method, if it is called.
3. If no error is reported.
** So, we define __add element method by t1 yuan table, to let two tables by adding the number of results ** +

Lua Find rules for a table element is actually three steps:
1. Look in the table, if found, return that element, it can not be found to continue
2. determine whether the table meta table, if there is no table yuan, return nil, meta table continues.
Table 3. Analyzing element there __index method, if the method is __index nil, nil is returned; __index is if a table is repeated 1,2,3; __index if the method is a function that returns The return value.

Lua provides a method for processing two table element

  • setmetatable(table, metatable)Membered table set for the table metatable, the table can not change the value of any other element of the type in Lua metatable(with debuglibrary exceptions) must do so using API C language.

  • getmetatable(table)Get Meta Table of metatableObjects

Element Method metatable :( subscripts are double bottom __ Oh)

function description
__add Operator +
__sub Operator -
__I have Operator *
__ div Operators /
__mod Operator%
__unm Operator - (inverted)
__concat Operators ..
__eq Operator ==
__lt Operators <
__the Operator <=
__call When the function call
__tostring Into a string
__index An index call
__newindex Assigned to an index

__indexRemove the standard operation for accessing `table [key]

__newindexAssigned to the specified index `table [key] = value

__tostringInto a string

__callCalled when Lua calls a value

__modeTable `week table for weak

__metatableUsed to protect metatablenot be accessed

  • __add

When Lua tries to two tables are added, will first check whether there are elements of one of the two tables, and then check whether the feature table is called a __addfield. If Lua found the field that the corresponding value is called. This value is called the "Element Method."


local tbl1 = {1,2,3}
local tbl2 = {5,1,1}
-- print(#tbl1, #tbl2)

-- 无法直接相加两个表
-- printf(tbl1 + tbl2)

-- 实现表的相加操作
mt = {}
mt.__add = function(x, y)
    local result = {}
    local length = #x
    for i=1,length do
        result[i] = x[i] + y[i]
    end
    return result
end
-- test
-- 设置表的元表
setmetatable(tbl1, mt)
setmetatable(tbl2, mt)
-- 执行表的相加操作
local result = tbl1  + tbl2
-- 循环输出
for k,v in ipairs(result) do
    print(k, v)
end
1   6
2   3
3   4
  • __concat table join
-- 表的连接
local tbl1 = {1,2,3}
local tbl2 = {2,3,4}

-- 实现表的相加操作
mt = {}
mt.__add = function(x, y)
    local result = {}
    local length = #x
    for i=1,length do
        result[i] = x[i] + y[i]
    end
    return result
end
-- 实现表的连接操作
mt.__concat = function(x, y)
    local length = #x
    local result = {}
    for i=1,length do
        result[i] = x[i].."**"..y[i]
    end
    return result
end
-- 设置表的元表
setmetatable(tbl1, mt)
setmetatable(tbl2, mt)
-- 执行表的连接操作
local result = tbl1..tbl2
-- 循环输出
for k,v in ipairs(result) do
    print(k, v)
end
1   1**2
2   2**3
3   3**4

Here is an example, set operation

Table tablerepresents a set, the set of calculations to achieve union, intersection, and so on. Look at the wording to experience the use of meta table


-- 定义集合
Set = {}
-- 定义集合的元表
local mt = {}


-- 创建集合,根据参数列表值创建新集合
function Set.new(arr)
    local set = {}
    setmetatable(set, mt) --创建集合均具有一个相同的元表
    for i,v in ipairs(arr) do
        set[v] = true
    end
    return set
end
-- 求集合并集
function Set.union(x, y)
    local result = Set.new{}
    for k,v in pairs(x) do
        result[k] = true
    end
    for k,v in pairs(y) do
        result[k] = true
    end
    return result
end
-- 求集合交集
function Set.intersection(x, y)
    -- 创建空集合
    local result = Set.new{}
    for k in pairs(x) do
        result[k] = y[k]
    end
    return result
end
-- 将集合转换为字符串
function Set.tostring(set)
    -- 定义存放集合中所有元素的列表
    local result = {}
    for k in pairs(set) do
        result[#result + 1] = k
    end
    return "{"..table.concat(result, ", ").."}"
end
-- 打印输出集合元素
function Set.print(set)
    print(Set.tostring(set))
end

-- 设置集合元方法
mt.__add = Set.union

-- 测试
local set1 = Set.new{10,20,30,40}
local set2 = Set.new{30, 1,50}


Set.print(set1 + set2) -- {1, 40, 10, 20, 30, 50}
Set.print(Set.intersection(set1, set2)) -- {30}
  • __call

__call allows table as a function to use.

local mt = {}
--__call的第一参数是表自己
mt.__call = function(mytable,...)
    --输出所有参数
    for _,v in ipairs{...} do
        print(v)
    end
end

t = {}
setmetatable(t,mt)
--将t当作一个函数调用
t(1,2,3)
1
2
3

As another example, to call attention to call inside the parameters

local mt = {}
sum = 0
--__call的第一参数是表自己
mt.__call = function(mytable,val)
    --输出所有参数
    for i = 1,#mytable do
        sum = sum +mytable[i]*val
    end
    return sum
end

t = {1,2,3}
setmetatable(t,mt)
--将t当作一个函数调用
print(t(5))
--30
  • __tostring

__tostring table may be modified into a string behavior

local mt = {}
--参数是表自己
mt.__tostring = function(t)
    local s = "{"
    for i,v in ipairs(t) do
        if i > 1 then
            s = s..", "
        end
        s = s..v
    end
    s = s .."}"
    return s
end

t = {1,2,3}
--直接输出t
print(t)
--将t的元表设为mt
setmetatable(t,mt)
--输出t
print(t)
table: 0x7fcfe7c06a80
{1, 2, 3}
  • __index

When the index does not exist in the call table, it uses the table element __index element method, and the first few distinct element method, __ index may also be a function of a table.
As a function of:
the tables and indexes __index element method as a parameter, return a return value

local mt = {}
--第一个参数是表自己,第二个参数是调用的索引
mt.__index = function(t,key)
    return "it is "..key
end

t = {1,2,3}
--输出未定义的key索引,输出为nil
print(t.key)
setmetatable(t,mt)
--设置元表后输出未定义的key索引,调用元表的__index函数,返回"it is key"输出
print(t.key)
local tbl = {x=1, y=2}
-- table中字段默认值为nil
print(tbl.x, tbl.y, tbl.z) -- 1 2 nil
-- 通过metatable修改table的默认值
function setTableDefault(tbl, default)
    local mt = {
        __index = function()
            return default 
        end
    }
    setmetatable(tbl, mt)
end
-- 调用setTableDefault后,任何对tbl中存在的字段的访问都回调用它的__index
setTableDefault(tbl, 1)
print(tbl.x, tbl.y, tbl.z) -- 1 2 1

As the table:
lookup table __index element method, if the index corresponding to the index value is returned, otherwise nil

local mt = {}
mt.__index = {key = "it is key"}

t = {1,2,3}
--输出未定义的key索引,输出为nil
print(t.key)
setmetatable(t,mt)
--输出表中未定义,但元表的__index中定义的key索引时,输出__index中的key索引值"it is key"
print(t.key)
--输出表中未定义,但元表的__index中也未定义的值时,输出为nil
print(t.key2)
nil
it is key
nil
  • __newindex

__newindexAnd __indexsimilar, except that __newindexfor tableupdating, __indexfor tablethe query; when assigning an index table does not exist, the method will be to call __newindex membered table element
1 as a function
__newindex will be a function of the time the value of the assignment statement tables, indexes, Fu as a parameter to call. Table does not change

local mt = {}
--第一个参数时表自己,第二个参数是索引,第三个参数是赋的值
mt.__newindex = function(t,index,value)
    print("index is "..index)
    print("value is "..value)
end

t = {key = "it is key"}
setmetatable(t,mt)
--输出表中已有索引key的值
print(t.key)
--为表中不存在的newKey索引赋值,调用了元表的__newIndex元方法,输出了参数信息
t.newKey = 10
--表中的newKey索引值还是空,上面看着是一个赋值操作,其实只是调用了__newIndex元方法,并没有对t中的元素进行改动
print(t.newKey)
it is key
index is newKey
value is 10
nil

-- 定义原表
local mt = {}
mt.__index = function(tbl, key)
    return mt[key]
end
mt.__newindex = function(tbl, key, value)
    mt[key] = value
    print(string.format("modify: key=%s value=%s", key, value))
end

local window = {x=1}
setmetatable(window, mt)

print(window.x) -- 1
print(rawget(window, x)) -- nil

-- 添加属性
print ("-------------")
window.y = 2
print ("-------------")
for k,v in pairs(mt) do
    print (k,v)
end
print ("-------------")
for k  in pairs(mt) do
    print (k)
end
print ("-------------")
print(window.y) -- 2
print(rawget(window, y)) -- nil
1
nil
-------------
modify: key=y value=2
-------------
__index function: 0x7fde254066f0
y   2
__newindex  function: 0x7fde25406b00
-------------
__index
y
__newindex
-------------
2
nil

2. As the table
when a table is __newindex, t is the index does not exist in the assignment and the index value will be assigned to the table pointed to by __newindex, does not change the original table.

local mt = {}
--将__newindex元方法设置为一个空表newTable
local newTable = {}
mt.__newindex = newTable
t = {}
setmetatable(t,mt)
print(t.newKey,newTable.newKey)

--对t中不存在的索引进行负值时,由于t的元表中的__newindex元方法指向了一个表,所以并没有对t中的索引进行赋值操作将,而是将__newindex所指向的newTable的newKey索引赋值为了"it is newKey"
t.newKey = "it is newKey"

print(t.newKey,newTable.newKey)
nil nil
nil it is newKey

Of course, if it exists in the primary table, will naturally direct assignment, assignment table element is not transmitted. We can also direct overwrite newindex, direct assignment with rawset


Window = {}
Window.mt = {}

function Window.new(o)
    setmetatable(o ,Window.mt)
    return o
end
Window.mt.__index = function (t ,key)
    return 1000
end
Window.mt.__newindex = function (table ,key ,value)
    if key == "wangbin" then
        rawset(table ,"wangbin" ,"yes,i am")
    end
end
w = Window.new{x = 10 ,y = 20}
w.wangbin = "55"
print(w.wangbin)
yes,i am

rawget and rawset

Sometimes we want to directly change or get the value of the table, we need to rawget and rawset way to go.
** rawget allows you to get directly to the actual value of the indices in the table, not through __index Element Method yuan tables. **

rawget is to bypass __index emergence of direct point is to let __index method of rewriting invalid

local mt = {}
mt.__index = {key = "it is key"}
t = {}
setmetatable(t,mt)
print(t.key)
--通过rawget直接获取t中的key索引
print(rawget(t,"key"))
it is key
nil

** rawset allows you to directly assign indices in the table, not through __newindex Element Method yuan tables. **

local mt = {}
local newTable = {}
mt.__newindex = newTable
t = {}
setmetatable(t,mt)
print(t.newKey,newTable.newKey)
--通过rawset直接向t的newKey索引赋值
rawset(t,"newKey","it is newKey")
print(t.newKey,newTable.newKey)
nil nil
it is newKey    nil
local mt = {}
t = {}
setmetatable(t,mt)
rawset(t,"newKey","it is newKey")
for k ,v in pairs (t) do
    print (k,v)
end

print(t.newKey)
newKey  it is newKey
it is newKey

Here are a few examples, say something about the relationship between the various methods.

local tb = {}
setmetatable(tb, { __index = function()
    return "not find"
end })
setmetatable(tb, { __newindex = function(table, key, value)
    local patchKey = "version"
    if key == patchKey then
        rawset(table, patchKey, "补丁值")
    else
        rawset(table, key, value)
    end
end })
-- setmetatable(tb, { __index = function()
--     return "not find"
-- end })
tb.version = "正常版本"
tb.date = "2018"
print(tb.version) --打印 补丁值
print(tb.server) --打印nil,不会调用__index方法了?
print(tb.date)  --打印2018

It has been tested and found:

If __index before __newindex, is not called __index

If _ index on after __newindex, call the value does not exist, the method will be called __index

- who would later call who, before a fail. However, this depends on the way you scheduled Element Method (we generally define as follows Element Method), see the following wording no problem;


local tb = {}
local mt ={}
mt.__newindex = function(table, key, value)
    local patchKey = "version"
    if key == patchKey then
        rawset(table, patchKey, "补丁值")
    else
        rawset(table, key, value)
    end
end 

mt.__index = function()
    return "not find"
end
setmetatable(tb,mt)

tb.version = "正常版本"
tb.date = "2018"
print(tb.version) 
print(tb.server) 
print(tb.date)  
补丁值
not find
2018

rawget is to bypass __index emergence of direct point is to let __index method of rewriting invalid

--- Gets the real value of `table[index]`, the `__index` metamethod. `table`
--- must be a table; `index` may be any value.
---@param table table
---@param index any
---@return any
function rawget(table, index) end
local tb = {}
local mt ={mm = "test"}
mt.__index = function()
    return "not find"
end
setmetatable(tb,mt)

tb.version = "正常版本"
print(tb.version)
print(tb.server) ---不存在的值,调用__index方法
--rawget是为了绕过__index而出现的,直接点,就是让__index方法的重写无效
print(rawget(tb, "version")) --打印 正常版本
print(rawget(tb, "server")) --打印nil

Metatable use features to achieve the object inheritance


local function class( super )
    local cls
    if super then
        cls = {}
        cls.super = super
        setmetatable(cls, {__index = super})
    else
        -- ctor是构造函数的命名
        cls = {ctor = function () end}
    end

    cls.__index = cls
    function cls.new( ... )
        local instance = setmetatable({}, cls)
        instance:ctor()
        return instance
    end

    return cls
end
--测试实现部分
local Test = class()
function Test:doSomething()
    print("test doSomething")
end
local test = Test.new()
test:doSomething()

--测试继承部分
local Test = class()
function Test:doSomething()
    print("test doSomething")
end
local Test2 = class(Test)
local test = Test2.new()
test:doSomething()

In the new time, create a table and returns, that is, create an instance, the instance can have its own field, such as doSomething Test class, the field is a function that can be called. Yuan is cls table instance, if the call is no instance of the field, will go to where to find cls
cls set element method __index = cls
if not super, only a constructor method
if there are super, cls metatable is super, yuan Element method table is also set up correctly
so, in Test2 is inherited from the test, which is an instance of test calls doSomething, can not find, to find a table yuan, yuan table find themselves parent, the parent class to find, successfully found.

Multiple Inheritance

If I want to inherit more than one parent class, how to do?

The idea is to change the function element method


local function search(key, tables)
    for _, super in ipairs(tables) do
        if super[key] then
            return super[key]
        end
    end
    return nil
end

local function class(...)
    local cls = { ctor = function () end}
    local supers = {...}
    setmetatable(cls, {__index = function (_, key)
        -- 在查找table的时候,会把table的key传进来
        return search(key, supers)
    end})
    
    function cls.new(...)
        local instance = {}
        setmetatable(instance, {__index = cls})
        instance:ctor(...)
        return instance
    end
    return cls
end

local Human = class()
function Human:life()
    print("almost 100 years.")
end
local Programmer = class()
function Programmer:coding()
    print("sub 1 year.")
end
local My = class(Human, Programmer)
local You = My.new()
You:life()
You:coding()
almost 100 years.
sub 1 year.

Resolution:

You can not find in life and coding field, go metatable cls, call element method __index, __ index calls the function search, all the parent classes find it again
successfully found

Guess you like

Origin www.cnblogs.com/xiaoqiangink/p/12082908.html