lua学习03——table

***************************************转载请注明出处: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"}

不仅仅是数字,table中的字段也可以是 字符串,

local weekDays = { "Monday" = 1, "Thuesday" = 2, "Wednesday" = 3, "Thursday" = 4, "Friday" = 5, "Saturday" = 6, "Sunday" = 7,  }

而且,对于构造这个table也有很多种方法去实现

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))

在table构造式中,最后一个字段后可以加个小逗号,不影响程序,也可以不加,

就像,我在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对 同一个 操作 有不同的操作方法,那就遵循第一个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

ok,常见,能用到的,差不多就这些了




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

同之前一样,我创建一个table,然后输出 table中 x的字段,没有,又没有__index方法,所以----nil,

然后,我给它的元表中加了__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

这里,最后在 table中,纯粹的去寻找 x字段,发现没有,所以,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

这里,也有 纯粹的方法,纯粹的给table更新———— rawset(table,key,value)

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

在这里,尤要注意,key的写法。




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!

当然,你可以把这个,封装成一个函数,得到 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

只需要在 __index所指向的函数中,返回你想设置的默认值就可以了。





***************************************转载请注明出处:http://blog.csdn.net/lttree********************************************

你可能感兴趣的:(table,lua,元表与元方法)