表
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的世界编辑器比较有兴趣,所以写用得上的内容,其他内容因为我没有验证和练习的途径,也暂时用不到,就不写了。写这两篇教程,是为了更深刻地去理解,加深印象。