概要:1.table特性;2.table的构造;3.table常用函数;4.table遍历;5.table面向对象
原文地址:http://blog.csdn.net/dingkun520wy/article/details/50231603
1.table特性
table是一个“关联数组”,数组的索引可以是数字或者是字符串,所有索引值都需要用 "["和"]" 括起来;如果是字符串,还可以去掉引号和中括号; 即如果没有[]括起,则认为是字符串索引
table 的默认初始索引一般以 1 开始,如果不写索引,则索引就会被认为是数字,并按顺序自动从1往后编;
table 的变量只是一个地址引用,对 table 的操作不会产生数据影响
table 不会固定长度大小,有新数据插入时长度会自动增长
table 里保存数据可以是任何类型,包括function和table;
table所有元素之间,总是用逗号 "," 隔开;
2.table的构造
创建table
- t = {} --定义一个空表
- t["jun"] = 6 --字符串key值的属性
- t[1] = 1 --数字key值的属性
- t.jun = 16 --字符串key值的简写
- t.test = {num=28,str="test"} --属性可以是table
- print(t.test.num) --输出28
- t.testFunction = function() print("函数") end --属性可以是function
- t.testFunction() --调用函数
- t:testFunction() --同上
-
- --上面的table还可以这么写
- t=
- {
- 1,
- jun = 6,
- test=
- {
- num = 28,
- str = "test",
- }
- testFunction = function() print("函数") end,
- }
3.table常用函数
table.pack(...)
获取一个索引从 1 开始的参数表 table,并会对这个 table 预定义一个字段 n,表示该表的长度
- function table_pack(param, ...)
- local arg = table.pack(...)
- print("this arg table length is", arg.n)
- for i = 1, arg.n do
- print(i, arg[i])
- end
- end
-
- table_pack("test", "param1", "param2", "param3")
table.concat(table, sep, start, end)
table.concat()函数列出参数中指定table的数组部分从start位置到end位置的所有元素, 元素间以指定的分隔符(sep)隔开。除了table外, 其他的参数都不是必须的, 分隔符的默认值是空字符, start的默认值是1, end的默认值是数组部分的总长.
sep, start, end这三个参数是顺序读入的, 所以虽然它们都不是必须参数, 但如果要指定靠后的参数, 必须同时指定前面的参数.
lua 中字符串的存储方式与 C 不一样,lua 中的每个字符串都是单独的一个拷贝,拼接两个字符串会产生一个新的拷贝,如果拼接操作特别多,就会影响性能,所以对于密集型的字符并接,table.concat 比用 ".." 连接更高效。
- local tbl = {"apple", "pear", "orange", "grape"}
-
- print(table.concat(tbl))
-
- print(table.concat(tbl, "、"))
-
- print(table.concat(tbl, "、", 2))
-
- print(table.concat(tbl, "、", 2, 3))
table.unpack(table, start, end)
用于返回 table 里的元素,参数 start 是开始返回的元素位置,默认是 1,参数 end 是返回最后一个元素的位置,默认是 table 最后一个元素的位置,参数 start、end 都是可选
- local tbl = {"apple", "pear", "orange", "grape"}
- print(table.unpack(tbl))
-
- local a, b, c, d = table.unpack(tbl)
- print(a, b, c, d)
-
- print(table.unpack(tbl, 2))
- print(table.unpack(tbl, 2, 3))
table.maxn(table)
返回指定table中所有正数key值中最大的key值. 如果不存在key值为正数的元素, 则返回0. 此函数不限于table的数组部分.
- tbl = {[1] = "a", [2] = "b", [3] = "c", [26] = "z"}
- print(#tbl) --3因为26和之前的数字不连续, 所以不算在数组部分内
- print(table.maxn(tbl)) --26
- tbl[91.32] = true
- print(table.maxn(tbl)) --91.32
table.getn(table)
返回table中元素的个数
- t1 = {1, 2, 3, 5};
- print(getn(t1))
- --4
table.insert(table, pos, value)
用于向 table 的指定位置(pos)插入值为value的一个元素. pos参数可选, 默认为数组部分末尾.
- local tbl = {"apple", "pear", "orange", "grape"}
-
- table.insert(tbl, "watermelon")
- print(table.concat(tbl, "、"))
-
- table.insert(tbl, 2, "watermelon")
- print(table.concat(tbl, "、"))
table.remove(table, pos)
删除并返回table数组部分位于pos位置的元素. 其后的元素会被前移. pos参数可选, 默认为table长度, 即从最后一个元素删起,并且参数 pos 的类型只能是数字 number 类型。
- local tbl = {"apple", "pear", "orange", "grape"}
- table.remove(tbl, 2)
- print(table.concat(tbl, "、"))
-
- table.remove(tbl)
- print(table.concat(tbl, "、"))
table.sort(table, comp)
用于对 table 里的元素作排序操作,参数 comp 是一个排序对比函数,它有两个参数 param1、param2,如果 param1 排在 param2 前面,那么排序函数返回 true,否则返回 false。参数 comp 可选,缺省 comp 的情况下是对表作升序排序。
- local tbl = {"apple", "pear", "orange", "grape"}
- local sort_func1 = function(a, b) return a > b end
- table.sort(tbl, sort_func1)
- print(table.concat(tbl, "、"))
-
- local sort_func2 = function(a, b) return a < b end
- table.sort(tbl, sort_func2)
- print(table.concat(tbl, "、"))
- local tbl = {"apple", "pear", "orange", "grape"}
- table.sort(tbl)
- print(table.concat(tbl, "、"))
table.foreachi(table, function(i, v))
会期望一个从 1(数字 1)开始的连续整数范围,遍历table中的key和value逐对进行function(i, v)操作
- t1 = {2, 4, 6, language="Lua", version="5", 8, 10, 12, web="hello lua"};
- table.foreachi(t1, function(i, v) print (i, v) end) ;
- --结果1 2
- --2 4
- --3 6
- --4 8
- --5 10
- --6 12
table.foreach(table, function(i, v))
与foreachi不同的是,foreach会对整个表进行迭代
- t1 = {2, 4, 6, language="Lua", version="5", 8, 10, 12, web="hello lua"};
- table.foreach(t1, function(i, v) print (i, v) end) ;
- --[[
- 输出结果:
- 1 2
- 2 4
- 3 6
- 4 8
- 5 10
- 6 12
- web hello lua
- language Lua
- version 5
- ]]
4.table遍历
for key, value in pairs(tbtest) do
print(value)
end
这样的遍历顺序并非是tbtest中table的排列顺序,而是根据tbtest中key的hash值排列的顺序来遍历的。
for key, value in ipairs(tbtest) do
print(value)
end
这样的循环必须要求tbtest中的key为顺序的,而且必须是从1开始,ipairs只会从1开始按连续的key顺序遍历到key不连续为止。
5.table面向对象
和编译型的面向对象语言不同,在lua中不存在类的定义这样一个概念,不管是类的定义还是类的实例都需要通过lua table来模拟。
- Test = {jun = 0}
- function Test.withdraw(self,v)
- self.jun = self.jun - v
- end
- --下面是代码的调用:
- a = Test
- a.withdraw(a,10)
Lua提供了一种更为便利的语法,即将点
(.)
替换为冒号
(:)
,这样可以在定义和调用时均隐藏self参数
- function Test:withdraw(v)
- self.jun = self.jun - v
- end
- a = Test
- a:withdraw(10)
类
Lua 没有类的概念,不过可以通过元表(metatable)来实现与原型 prototype 类似的功能,而 prototype 与类的工作机制一样,都是定义了特定对象行为。Lua 里的原型特性主要使用元表的 __index 事件来实现,这样当调用对象没定义的方法时,会向其元表的 __index 键(事件)查找。例如有 a 和 b 两个对象,想让 b 作为 a 的原型 prototype,只需要把 b 设置为 a 元表的 __index 值就行:当对象 a 调用任何不存在的成员都会到对象 b 中查找,a 可以拥有或调用 b 的属性或方法,从某种意义上看,b 可以看作是一个类,a 是 b 的对象。
- --[[
- 在这段代码中,我们可以将Account视为class的声明,如Java中的:
- public class Account
- {
- public float balance = 0;
- public Account(Account o);
- public void deposite(float f);
- }
- --]]
- --这里balance是一个公有的成员变量。
- Account = {balance = 0}
-
- --new可以视为构造函数
- function Account:new(o)
- o = o or {} --如果参数中没有提供table,则创建一个空的。
- --将新对象实例的metatable指向Account表(类),这样就可以将其视为模板了。
- setmetatable(o,self)
- --在将Account的__index字段指向自己,以便新对象在访问Account的函数和字段时,可被直接重定向。
- self.__index = self
- --最后返回构造后的对象实例
- return o
- end
-
- --deposite被视为Account类的公有成员函数
- function Account:deposit(v)
- --这里的self表示对象实例本身
- self.balance = self.balance + v
- end
-
- --下面的代码创建两个Account的对象实例
-
- --通过Account的new方法构造基于该类的示例对象。
- a = Account:new()
- --[[
- 这里需要具体解释一下,此时由于table a中并没有deposite字段,因此需要重定向到Account,
- 同时调用Account的deposite方法。在Account.deposite方法中,由于self(a对象)并没有balance
- 字段,因此在执行self.balance + v时,也需要重定向访问Account中的balance字段,其缺省值为0。
- 在得到计算结果后,再将该结果直接赋值给a.balance。此后a对象就拥有了自己的balance字段和值。
- 下次再调用该方法,balance字段的值将完全来自于a对象,而无需在重定向到Account了。
- --]]
- a:deposit(100.00)
- print(a.balance) --输出100
-
- b = Account:new()
- b:deposit(200.00)
- print(b.balance) --输出200
继承
- --需要说明的是,这段代码仅提供和继承相关的注释,和类相关的注释在上面的代码中已经给出。
- Account = {balance = 0}
-
- function Account:new(o)
- o = o or {}
- setmetatable(o,self)
- self.__index = self
- return o
- end
-
- function Account:deposit(v)
- self.balance = self.balance + v
- end
-
- function Account:withdraw(v)
- if v > self.balance then
- error("Insufficient funds")
- end
- self.balance = self.balance - v
- end
-
- --下面将派生出一个Account的子类,以使客户可以实现透支的功能。
- SpecialAccount = Account:new() --此时SpecialAccount仍然为Account的一个对象实例
-
- --派生类SpecialAccount扩展出的方法。
- --下面这些SpecialAccount中的方法代码(getLimit/withdraw),一定要位于SpecialAccount被Account构造之后。
- function SpecialAccount:getLimit()
- --此时的self将为对象实例。
- return self.limit or 0
- end
-
- --SpecialAccount将为Account的子类,下面的方法withdraw可以视为SpecialAccount
- --重写的Account中的withdraw方法,以实现自定义的功能。
- function SpecialAccount:withdraw(v)
- --此时的self将为对象实例。
- if v - self.balance >= self:getLimit() then
- error("Insufficient funds")
- end
- self.balance = self.balance - v
- end
-
- --在执行下面的new方法时,table s的元表已经是SpecialAccount了,而不再是Account。
- s = SpecialAccount:new{limit = 1000.00}
- --在调用下面的deposit方法时,由于table s和SpecialAccount均未提供该方法,因此访问的仍然是
- --Account的deposit方法。
- s:deposit(100)
-
-
- --此时的withdraw方法将不再是Account中的withdraw方法,而是SpecialAccount中的该方法。
- --这是因为Lua先在SpecialAccount(即s的元表)中找到了该方法。
- s:withdraw(200.00)
- print(s.balance) --输出-100
私密性
私密性对于面向对象语言来说是不可或缺的,否则将直接破坏对象的封装性。Lua作为一种面向过程的脚本语言,更是没有提供这样的功能,然而和模拟支持类与继承一样,我们仍然可以在Lua中通过特殊的编程技巧来实现它,这里我们应用的是Lua中的闭包函数。
- --这里我们需要一个闭包函数作为类的创建工厂
- function newAccount(initialBalance)
- --这里的self仅仅是一个普通的局部变量,其含义完全不同于前面示例中的self。
- --这里之所以使用self作为局部变量名,也是为了方便今后的移植。比如,以后
- --如果改为上面的实现方式,这里应用了self就可以降低修改的工作量了。
- local self = {balance = initialBalance} --这里我们可以将self视为私有成员变量
- local withdraw = function(v) self.balance = self.balance - v end
- local deposit = function(v) self.balance = self.balance + v end
- local getBalance = function() return self.balance end
- --返回对象中包含的字段仅仅为公有方法。事实上,我们通过该种方式,不仅可以实现
- --成员变量的私有性,也可以实现方法的私有性,如:
- --local privateFunction = function() --do something end
- --只要我们不在输出对象中包含该方法的字段即可。
- return {withdraw = withdraw, deposit = deposit, getBalance = getBalance}
- end
-
- --和前面两个示例不同的是,在调用对象方法时,不再需要self变量,因此我们可以直接使用点(.),
- --而不再需要使用冒号(:)操作符了。
- accl = newAccount(100.00)
- --在函数newAccount返回之后,该函数内的“非局部变量”表self就不再能被外部访问了,只能通过
- --该函数返回的对象的方法来操作它们。
- accl.withdraw(40.00)
- print(acc1.getBalance())