1、表
表(table)是Lua语言中非常独特的地方;
Lua中的表即支持数组风格的实现(顺序表),也支持key-value风格的实现(哈希表),并且这两种方式还能混用(即使不推荐);
表中可以存放任何数据类型;
Lua有一个很重要的特性:函数是一级类型,也就是说,表中存放的类型也包括函数;
(补充:单纯利用表,就已经可以实现一个简单的类了)
需要注意的地方:
需要注意,不同于大多数编程语言,Lua数组风格的表,元素的下标是从 1 开始的;
需要注意,当需要删除表中的元素时,正确的做法是调用table.remove()函数,而不是直接将value置为nil;
需要注意,表中的变量和函数,都是不能用local关键词进行修饰的
2、元表
元表(metatale),可以看作是表的一种扩展,缺省情况下我们创建的表是没有元表的
setmetatable(table, metatable)
可以通过上述方法为指定的表设置对应的元表,元表和表的大致关系如下图:
接下来介绍2个有关元表的函数:
setmetatable(table, metatable); -- 将 metatable 设置成 table 的元表getmetatable(table) -- 将返回 table 的元表
具体的使用:
local table = { 1, 2, 3 } -- 表 local metatable = { __add = myAdd } -- 元表
上面这段代码的作用是: 将 metatable 设置成 table 的元表
其中在元表 metatable 实现了 + 操作符 的重载,有关表的函数重载,接下来会介绍
3、重载
从上面的图片可以看到, 元表metatable中有若干的key,其中每个key代表一个方法
当我们用自己实现的函数作为value 填充 这个表格,就实现了类似C++ 的重载
支持重载的方法及对应的key如下:
__add(a, b) --加法__sub(a, b) --减法__mul(a, b) --乘法__div(a, b) --除法__mod(a, b) --取模__pow(a, b) --乘幂__unm(a) --相反数__concat(a, b) --连接__len(a) --长度__eq(a, b) --相等__lt(a, b) --小于__le(a, b) --小于等于__index(a, b) --索引查询__newindex(a, b, c) --索引更新__call(a, ...) --执行方法调用__tostring(a) --字符串输出__metatable --保护元表
接下来分别演示两个比较有代表性的方法的重载: __add() 和 __index():
①__add():
“+”操作符的重载可以实现两个功能:取并集,或者 每个元素相加,这里 实现取并集的功能
所以首先需要实现一个取并集的函数 myAdd(self, val) 其中两个形参,分别传 "+ " 左边的变量 self(关键字) 和 右边的变量 tbRight
local function myAdd(self, tbRight) local tmp = {} -- 临时表 local tbResult = {} -- 存放最终的并集 -- 哈希处理 for k, v in pairs(self) do tmp[v] = 1 end for k, v in pairs(tbRight) do tmp[v] = 1 end -- 加入到 tbResult(哈希映射) local count = 1 for k, v in pairs(tmp) do table.insert(tbResult, k) end return tbResultend
接下来 创建一个表 tb, 给 tb 设置元表 metatb,并将 metatb 的 __index 字段 赋值为 myAdd方法,实现重载:
local tb1 = {10, 20, 30}local metatb = {__add = myAdd}setmetatable(tb1, metatb)-- tb1 和 tb2 取并集local tb2 = {20, 30, 50}local rettb = tb1 + tb2
②__index()
__index()字段 重载的函数,描述了通过进行key访问某个table 的方式,包括: "." 以及 "[]"
当然,table 本身是可以通过key进行元素的访问的,
而重载的作用就在于,当我们对指定table的key进行访问,并且这个key 在 table 本身不存在时,才会调用 __index()函数,从而对表进行扩展
这是一个非常重要的重载,正是因为能实现__index()的重载,我们才能用Lua实现一系列比较高级的特性
要实现这个工作,我们依然需要以下步骤:
1、实现一个函数myIndex,
2、将metatable的__index字段设置成myIndex,
3、调用setmetatable方法:
local function myIndex(self, key) local TbAddition = {d = 4, e = 5} return TbAddition[key]endlocal tb = {a = 1, b = 2, c = 3}-- 没有在上文单独定义metatable,而是直接作参数setmetatable(tb, {__index = myIndex})
这样设置完以后,访问 tb["d"] 、 tb["e"],或者 tb.d、tb.e 由于tb本身没有这两个key,因此程序会调用 myIndex方法,并返回正确的结果
补充:
一些语法糖:
1、. [] 传入的参数
"." 是一种语法糖,当key为非数字类型,比如说 "d"时, tb["d"] 和 tb.d 这两种方式是等价的,因此在重载__index时,并不用额外关心这两种情况
2、直接将扩展的表赋值给 __index 字段
如下:
setmetatable(tb, {__index = {d = 4, e = 5} } )
这种方式没有手动定义 __index的方法,但仍然能实现重载
4、实现面向对象
正文4
5、闭包
5、实现继承
正文5
6、实现私有属性的保护
正文6