***************************************转载请注明出处:http://blog.csdn.net/lttree********************************************
之前几篇文章:
工具:sublime text2
第一篇:lua学习01(关于 基本类型、function、table部分)
第二篇:lua学习02(关于表达式 与 语句)
本篇文章,就是对 table 的一些东西,
> 构造
> 元表与元方法
> __index 与 __newindex
> __index 与 __newindex 的应用(默认值table and 只读table)
在第一篇对table讲的 不是很多,主要是看后面有些东西,
如果一次写完,可能太杂太多,所以现有了些概念再学习,也是极好的。
正文:
1. 初识 table
之前也有说过,table这个东西功能很强大,是lua最主要的数据结构,
温习一下之前的,
当作为数组是,table会默认从1开始赋值,比如:
t = { [1] = "r" , [2] = "g" , [3] = "b" } 等价于 t = {"r", "g" , "b"}
local weekDays = { "Monday" = 1, "Thuesday" = 2, "Wednesday" = 3, "Thursday" = 4, "Friday" = 5, "Saturday" = 6, "Sunday" = 7, }
a = {x=10,y=20} 等价于 a = { ["x"] = 10 , ["y"] = 20 } a = {} a.x = 10, a.y = 20
player = { id = 1001, name = "Tom", level = 15, } player.sex = "Boy" print("id: "..player.id..", name: "..player.name..", level: "..player.level..", sex: "..player.sex) player.sex = nil print("id: "..player.id..", name: "..player.name..", level: "..player.level..", sex: "..tostring(player.sex))
就像,我在player构造式中 level = 15 后,有一个小逗号,
我推荐是加上,因为,这样,当别人维护你的代码的时候,就不需要帮你加上了(这的确是在帮你做的,而不是维护的人不得不做的),
不要让后面的人帮你做你该做的事————这也是一种良好的编程习惯。
2. table的元表
什么是元表呢?
我们知道,可以对两个 number类型的数相加,但不能对两个table进行相加——why?
没有对应的规则。
that's right,元表,就是存放这个规则的地方。
元表,就是存放关于本table各种规则的地方,这个规则,也叫 元方法。
对于 lua中,每一个值都有一个元表,除了 userdata 和 table,其他值的元表是按所属类型的单一元表,就是说 你定义了一个 number的值,它就有了所有 number相关的元方法,像继承一样;但是 对于 userdata 和 table,它们可以拥有各自的元表,在新建它们的时候,元表是空的。
在lua中,我们可以添加、删除 table元表中的元方法,但是,无法修改userdata的元表(除了table类型,都不能用lua来修改),需要用C代码完成。
-- 这里先给出一些基本的元方法
比如,我们在用 + 时,调用的元方法就是 __add(a,b) ,
试试~:
local add1, add2 = {num = 1, }, {num = 2, } -- 创建一个元表 local mt = {} -- 创建一个 元方法 function addNum( a, b ) return a.num + b.num end -- 将元表赋给 add1 这个table setmetatable(add1, mt) -- 将 + 调用的函数设置为 addNum mt.__add = addNum print( add1 + add2 )
我们不需要给两个table赋上 同样的元方法,元表是一个table执行各项操作所遵循的准则(我的理解),它来决定与什么类型进行什么操作,不需要对方也给出方法,但如果两个table对 同一个 操作 有不同的操作方法,那就遵循第一个table的准则——先来后到嘛~
local add1, add2 = {num = 1, }, {num = 2, } -- 创建一个元表 local mt = {} local mt2 = {} -- 创建一个 元方法 function addNum( a, b ) return a.num + b.num end function addNum2( a, b ) return a.num - b.num end setmetatable(add1, mt) setmetatable(add2, mt2) mt.__add = addNum mt2.__add = addNum2 print( add1 + add2 ) print( add2 + add1 )
-- 基本元方法 __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) -- 字符串输出 print
3. __index
写点lua代码就知道,当我们访问lua的table中不存在的字段时,将会返回nil,
但是,真像表面上那样,在table溜达一圈,发现没有,就返回nil吗?
—— 当然不是!
lua解释器会先在 table字段中找相应的字段,如果没有,它会去寻找 __index元方法,如果没有这个元方法,那就返回 nil,
否则,返回值,就由__index来决定,
local table = {} local mt = {} setmetatable(table, mt) print( table.x ) mt.__index = function ( ) return "find here" end print( table.x ) -- result nil find here
然后,我给它的元表中加了__index的方法,返回 一个字符串,
当我再次访问 table中,没有的字段时,它会去找__index,将操作给 __index来完成。
但是,如果我们不想找__index方法,只想在 table中看看,有没有某个字段,如何避过它呢?
—— rawget( table, 字段 )
local table = {} local mt = {} setmetatable(table, mt) print( table.x ) mt.__index = function ( ) return "find here" end print( table.x ) print( rawget(table,x) ) -- result nil find here nil
__index ,作用是虾米呢?
在 lua中,没有继承啊,神马的,但我们需要用到这方面,这时,__index优势就体现出来了。
我们可以用 __index来 实现面向对象的部分中的 继承,还有缓存。(稍后,会有文章写 在lua模拟面向对象)
4. __newindex
上面说到,__index ,它是 用于对 table 的查询,而 __newindex 则是 用于对 table 的更新。
当我们对一个 table中,不存在的值,进行赋值,我们就要去看看元表中有没有 __newindex方法,如果有,那就调用它,
如果,该方法是个 table,那就对这个table进行 赋值的操作(当然,还会找有没有__newindex,如果有..并且是table..如果没有...)
local table = {} local mt = {} setmetatable(table, mt) local newindex_table = {} mt.__newindex = newindex_table table.x = 3 print(table.x) print(newindex_table.x) -- local mt2 = {} local newindex_table2 = {} mt2.__newindex = newindex_table2 setmetatable(newindex_table,mt2) table.name = "tree" print(table.name) print(newindex_table.name) print(newindex_table2.name) -- result nil 3 nil nil tree
local table = {} local mt = {} setmetatable(table, mt) local newindex_table = {} mt.__newindex = newindex_table table.x = 3 print(table.x) print(newindex_table.x) -- local mt2 = {} local newindex_table2 = {} mt2.__newindex = newindex_table2 setmetatable(newindex_table,mt2) table.name = "tree" print(table.name) print(newindex_table.name) print(newindex_table2.name) -- rawset(table,"y",7) print(table.y) print(newindex_table.y) print(newindex_table2.y) -- result nil 3 nil nil tree 7 nil nil
5. 只读的table & 有默认值的 table
刚弄懂了__index 与 __newindex,用这俩小东西,我们可以做一些 不同寻常的 table。
—— 只读的 table,
table,只读不可写,显然,我们要在 table的更新上,下手。
所以,用到了 __newindex,
当我们,要对table不存在的字段更新时,要先找 __newindex,
既如此,我们设置 __newindex,它就会吞掉更新的请求,转而去实现函数内容,或者对其他的table操作;
Ok,不存在的字段解决了,但如果要更改已存在的字段,就不需要去找 __newindex 了啊!
这里,继续用到 __index,我们可以将我们实际的table,作为空table的__index,
所以,当我们找字段的时候,的确可以找到(在 __index中),
但,当我们要修改时,哼哼~,table是空的,不得不去调用 __newindex了,然后,就会走上面,我们埋得坑啦~
local table = { x = 3, y = 10, } local readOnly = {} local mt = {} setmetatable(readOnly, mt) mt.__index = table mt.__newindex = function ( ) print("this is read-only table!") end readOnly.z = 1 readOnly.x = 7 -- result this is read-only table! this is read-only table!
—— 有默认值的table
让一个table有默认的值,肯定是你去访问了某个字段,
所以—— 查询—— __index
local defaultValueTable = { x = 1, y = 2, } local mt = {} setmetatable(defaultValueTable, mt) mt.__index = function () return 10 end print(defaultValueTable.x) print(defaultValueTable.z) -- result 1 10
***************************************转载请注明出处:http://blog.csdn.net/lttree********************************************