\

In the cone of light, all is fate

Lua基础(二)

Lua的表非常灵活,尤其是结合元表,功能会非常强大,先根据下面的代码,自己找规律去理解表的用法:

-- 初始化表
mytable = {}
​
-- 指定值
mytable[1] = "Lua"
mytable["index"] = 1    --这种写法[]内一定要加“”
mytable.abc = "abc"
​
print(mytable.index)    -- 1
print(mytable["abc"])   -- abc
​
-- 移除引用,lua 垃圾回收会释放内存
mytable = nil
​
--Lua为表的操作提供了一些方法,这里不详细说,要用的时候查一下得了。

 

元表

元表并不是区别于表的一种结构,为了方便理解,我们从实际需要来解释元表是什么,能做什么。

table1 = {1,2,3}
table2 = {"a","b","c"}
table3 = table1 + table2

运行后会发现报错了,相信你也知道报错的原因,在看到上面代码的时候就开始怀疑这两个表相加能行得通吗?

当然行不通啦!

但是我们有办法让它行得通,学过C语言的话,你肯定知道运算符重载,对于一些程序不知道该如何去计算的表达式,我们要告诉程序怎么写,这就是元表的作用之一,加法在元表中有元方法__add来处理,相应的,不同的运算符也有各自的方法:

__add       --运算符 +
__sub       --运算符 -
__mul       --运算符 *
__ div      --运算符 /
__mod       --运算符 %
__unm       --运算符 -(取反)
__concat    --运算符 ..
__eq        --运算符 ==
__lt        --运算符 <
__le        --运算符 <=

当然并不是只要__add()就可以了,2个表具体怎么加还是要由我们来决定。

metaTable = {}
metaTable.__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
​
table1 = {1,2,3}
table2 = {4}
--setmetatable是设置参数2成为参数1的元表的方法
setmetatable(table1,metaTable)
​
table3 = table1 + table2

将两表之间是+号时,就会调用__add方法,最终返回相加的结果给table3。那么为什么只有table1设置了元表,table2不用设置吗?

不用的,两表之间的运算,只要有一个设置了就行,参考以下情况

table1 = {1,2,3}
table2 = {4}
table3 = {5}
​
setmetatable(table1,metaTable)
​
table4 = table1 + table2 -- 正确
table4 = table1 + table2 + table1 -- 正确
table4 = table1 + table2 + table3 -- 错误

其他运算符类似,除了运算符,另外还以下几种元方法

__call
metaTable = {}
​
--在metaTable中定义__call方法的内容
metaTable.__call = function(table,...)
    for _,v in ipairs{...} do
        print(v)
    end
end
​
table = {}
setmetatable(table,metaTable)
​
--像方法一样调用table
table(1,2,3)
__toString
--正常当我们打印一个表时,只输出以下
table = {1,2}
print(table) -- table: 00DB9340
​
--定义__toString后,我们可以打印额外的内容
metaTable = {}
metaTable.__tostring = function(t)
    local str = ""
    for i,v in ipairs(t) do
        if i > 1 then
            str = str..","
        end
        str = str..v
    end
    return str
end
​
table = {1,2,3}
setmetatable(table,metaTable)
print(table)    --table: 00DB9340   1,2,3
​
__index
--__index可以是一个函数也可是一个table
--作为函数时,作用是在查找时,没有找到的情况下,定义返回返回的内容
t = {1,2}
print(t[1]) -- 1
print(t[3]) -- nil
print(t.key) -- nil
--以上找不到项时,会返回nil,__index就是让其不要返回nil,把nil替换成其他内容
​
mt.__index = function(t,key)
    return key
end
setmetatable(t,mt)
​
print(t.A)  -- A
​
--作为table时,t中找不到就上__index中找,还是找不到就返回nil,类似原型链中的父子关系
mt.__index = {a = 1}
print(t.a)  -- 1
print(t.b)  -- nil
__newindex
--__newindex和__index是类似的,只不过__newindex是赋值时起作用
--作为函数时,对table中一个不存在的key赋值,就会调用__newindex,相当于在这种情况下让我们能写一些逻辑,可以看作钩子函数,并且不会对表本身做出改变
--需要注意的是,在没有对__newindex的内容定义时,对table中不存在的key赋值是可是改变table的
t = {}
t.a = 1
print(t.a) -- 1
​
--反之
local mt = {}
mt.__newindex = function(t,index,value)
    --随便写点
end
​
t = {}
setmetatable(t,mt)
t.q = 10
print(t.q)  -- nil
​
--作为table时,就负责存储那些不存在却被赋值的项,举一反三,不写例子了
rawget、rawset

发现没有,上面那些元方法没有对表本身做操作,一会儿从表获得数据,一会儿从元表获得数据、设置数据,有没有比较正常的操作方式?

还好是有的。

rawget不管数据是在表中还是元表中,只要数据存在,就都能获得。

rawset直接向表中赋值,不会因为__newindex而让数据跑到元表中。

这两个方法,让表和元表真正统一成一个数据结构,不过越灵活的东西越容易出错,我感觉阅读和维护代码时会比较头痛吧。

我学习Lua主要是长长眼界增加知识面,对WAR3的世界编辑器比较有兴趣,所以写用得上的内容,其他内容因为我没有验证和练习的途径,也暂时用不到,就不写了。写这两篇教程,是为了更深刻地去理解,加深印象。

posted @ 2021-11-25 16:20  Ymrt  阅读(47)  评论(0编辑  收藏  举报