table简介:
table是Lua中唯一的数据结构,其他语言所提供的数据结构如:arrays、lists、queues、sets等,Lua都是通过table来实现。
Array&Hash
在lua中通过整数下标访问table中元素,即是数组。并且数组大小不固定,可动态增长。
table的使用
--file: testTable.lua
--Description: 使用table创建数组
tableArray = {}
--使用默认索引,以及自定义key进行赋值
--Notice:Lua的数组下标默认从1开始,当然可以根据需要从负值或者0作为下标开始
--Lua的标准库遵循下标从1开始,因此你的数组下标必须也是从1开始,才可以使用标准库的函数
tableArray = {"c","c++","c#","oc","java","kotlin",["s1"]= "python",["s2"]= "lua", ["s3"]= "javascript"}
--使用默认索引对table内值进行修改
tableArray[1] = "php"
--使用自定义key值对table内值进行修改
tableArray["s3"] = "js"
--设置空值中断索引
tableArray[5] = nil
local getn = table.getn or
function(t)
local len = 0
for k in ipairs(t) do
len = len + 1
end
return len
end
local getnAll =function(t)
local len = 0
for k in pairs(t) do
len = len + 1
end
return len
end
function ipairsPrint(table)
--ipairs迭代器对索引升序遍历,索引中断遍历停止
for k,v in ipairs(table) do
print(k .. "---"..v)
end
end
function pairsPrint(table)
--pairs迭代器遍历所有非空元素
for k,v in pairs(table) do
print(k.."--"..v)
end
end
ipairsPrint(tableArray)
--lua5.1及之前版本的table.getn()方法,取得table中默认索引值的个数
--lua5.1之后的使用自定义方法,只取得默认索引值元素的个数
print("Array's index elements number : "..getn(tableArray))
--lua5.1-5.3都适用的#table 取得table中默认索引值的个数
print("Array's index number : " .. #tableArray)
--自定义方法获取table的全部元素个数
print("Array's all elemnets number : "..getnAll(tableArray).."\n")
pairsPrint(tableArray)
print("Array's index elements number : "..getn(tableArray))
print("Array's index number : " .. #tableArray)
print("Array's all elemnets number : "..getnAll(tableArray))
输出结果lua5.1,使用table.getn
输出结果lua5.3,使用自定义getn
总结:
- Lua 内部采用数组和哈希表分别保存普通值和键值对,不推荐混合使用这两种赋值方式
- lua5.3版本去掉了获取数组table中默认索引值个数的table.getn方法,但#table仍可以在不同版本中使用
- 不要在 Lua 的 table 中使用 nil 值,如果要删除一个元素,应直接 remove,不要用 nil 去代替
table相关方法
--file: testTabFunc.lua
--Description: table's function
tableArray = {"c","c++","c#","oc","java","kotlin",["s1"]= "python",["s2"]= "lua", ["s3"]= "javascript"}
--字符串数组连接
res1 = table.concat(tableArray)
--给定字符串分隔符连接字符串数组元素
res2 = table.concat(tableArray,"/")
res3 = table.concat(tableArray,"/",2,6)
print("table.concat(tableParam) :",res1)
print("table.concat(tableParam,splitMark) :",res2)
print("table.concat(tableParam,splitMark,startIndex,endIndex) :",res3)
--给数组插入元素,无其他参数直接在末尾插入
table.insert(tableArray,"swift")
print("table.insert(tableParam,insertValue) :",table.concat(tableArray,"/"))
table.insert(tableArray ,4,"go")
print("table.insert(tableParam,insertValue,position) :",table.concat(tableArray,"/"))
table.remove(tableArray,3)
print("table.remove(tableParam,position) :",table.concat(tableArray,"/"))
table相关问题
工作中会遇到接收到的数据有无效项,需要遍历table并且删除掉无效项的情况,在解决时当我们在table表中使用for迭代,将符合条件的元素删除之后,后面元素前移,然后产生跳跃——使用for i=1,#mytable 这样的循环时,由于在for时就已经确定了循环的终点是table的总索引长度,在整个for运行过程中,终点不会改变,以致于在循环到i = #mytable 时,会报错nil。
--file: rmtable.lua
t= {}
for i = 1,10 do
t[i] = i
end
function t.print()
for i =1 ,#t do
print(i..'-'..t[i])
end
end
t.print()
t[4] = nil
t[7] = nil
function t.check()
local temp = {}
for i = 1,#t do
if t[i] then
print(i.."-"..t[i])
else
table.remove(t,i)
--Check the current value in i index , or while be error
if t[i] then print(i.."-"..t[i]) end
end
end
end
--[[also can use the while loop]]
-- function t.check()
-- local i = 1
-- while i <= #t do
-- if t[i] then
-- print(i.."-"..t[i])
-- i = i + 1
-- else
-- table.remove(t,i)
-- end
-- end
-- end
print("After check:")
t.check()
Matrix
Lua中多维数组的表示方法:
数组的数组表示矩阵
Lua中矩阵的表示方法:一是“数组的数组”——table的每个元素是另一个table。
--file: matrix.lua
--creat a 4*3 matrix
data = {}
--对数组元素赋值
for i = 1,4 do
data[i] = {}
for j = 1 , 3 do
data[i][j] = 0
end
end
--输出数组元素
for i =1 ,#data do
for j =1 ,#data[i] do
print(data[i][j])
end
end
数组的下标表示矩阵
表示矩阵的另一方法,是将行和列组合起来。如果索引下标都是整数,通过第一个索引乘于一个常量(列)再加上第二个索引
data = {}
for i =0 ,3 do
for j = 0,2 do
data[i*3 + j] = 0
end
end
for i = 0 , #data do
print(i..'-'..data[i])
end
linkedList
--file: linkedList.lua
list = nil --链表的根节点
for i = 1,10 do
list = {next = list ,value = i} --这样实现的Lua链表类似于C/C++中LIFO的链表
end
--遍历该链表
local l = list
while l do
print(l.value)
l = l.next
end
对Lua链表的具体理解可以参考本文末尾REF链接中stackoverflow中的相关问题
自定义链表的实现:
--自定义链表类
LList = {}
LList.__index = LList
function LList.create()
local list = {} -- our new object
setmetatable(list,LList) -- make LList handle lookup
list.count = 0 -- initialize our object
list.head = nil
return list
end
function LList:add(newNode)
print("DEBUG PRINT: LList:add(): newNode.data: "..newNode:getData().." LList.count: "..self.count)
if(self.head) then
local curr = self.head
print("DEBUG PRINT: LList:add(): self.head:toString(): "..self.head:toString())
print("DEBUG PRINT: LList:add(): curr:toString(): "..curr:toString())
print("DEBUG PRINT: LList:add(): newNode:toString: "..newNode:toString())
while curr.nextNode do --this is the while loop in question
print("DEBUG PRINT: LList:add(): in while:"..curr:toString())
curr = curr.nextNode
end
curr:setNext(newNode)
print("DEBUG PRINT: LList:add(): curr.nextNode:toString(): "..curr.nextNode:toString())
self.count = self.count + 1
else
self.head = newNode
self.count = 1
print("DEBUG PRINT: LList:add(): self.count" .. self.count .." self.head:getData(): ".. self.head:getData())
end
print("DEBUG PRINT: LList:add(): EXITING\n")
end
function LList:getLen()
return self.count
end
function LList:toString()
print("Stubbed toString()")
if(self.head)then
print(self:toStringHelper(self.head))
else
print("emptyList?")
end
end
function LList:toStringHelper(currNode)
if(currNode.nextNode)then
return currNode:toString().. "\n" .. self:toStringHelper(currNode.nextNode)
else
return currNode:toString()
end
end
-----------------------------------------------------------------------
--Node类
Node = {}
Node.__index = Node
function Node.create(newData)
local tNode = {}
--[[
setmetatable(tNode, Node)
tNode.data = newData
return tNode
--]]
--以上代码可简写为:
tNode.data = newData
return setmetatable(tNode,Node)
end
function Node:getData()
return self.data
end
function Node:getNext()
return self.nextNode
end
function Node:setNext(newNode)
self.nextNode = newNode
print("DEBUG PRINT: Node:setNext(): self.nextNode:toString(): "..self.nextNode:toString())
end
function Node:hasNext()
if self.nextNode then
return true
else
return false
end
end
function Node:toString()
return tostring(self.data)
end
--测试代码:
testerList = LList.create()
print(testerList:getLen())
tNode1=Node.create(5)
tNode2=Node.create(7)
tNode3=Node.create(2)
testerList:add(tNode1)
testerList:add(tNode2)
testerList:add(tNode3)
print(testerList:getLen())
print(testerList:toString())
Queue
--file: queue.lua
list = {}
function list.new()
return {first = 0, last = -1}
end
function list.pushleft(list,value)
local first = list.first -1
list .first = first
list[first] = value
end
function list.pushright(list , value)
local last = list.last +1
list.last = last
list[last] = value
end
function list.popleft(list)
local first = list.first
if first > list.last then error("list is empty") end
local value = list[first]
list[first] = nil --to allow garbage collection
list.first = first+1
return value
end
function list.popright(list)
local last = list.last
if list.first > last then error("list is empty") end
local value = list[last]
list[last] = nil --to allow garbage collection
list.last = last -1
return value
end
function list.print(list)
local point = list.last
while list.first <= point do
print(list[point])
point = point -1
end
end
--The test code
l = list.new()
list.pushleft(l,1) --list[-1] = 1
list.pushright(l,2) --list[0] = 2
list.pushleft(l,3) --list[-2] = 3
list.pushleft(l,4) --list[-3] = 4
list.pushright(l,5) --list[1] = 5
--依次打印
list.print(l)
print("Test the queue's pop func:")
--移除一个右边的和左边的
print(list.popright(l)) -->5
print(list.popleft(l)) -->4
Set
Lua中表示这个集合的简单方法:将所有集合中的元素作为下标存放在一个table里,只需要测试给定的元素在table对应下标的值是否为nil
--file: set.lua
Set = {}
Set.mt = {}
function Set.new(t)
local set = {}
for _,v in pairs(t) do
set[v] = true
end
return setmetatable(set,Set.mt)
end
set1 = Set.new{1,2,3,4,5,}
set2 = Set.new{4,5,6,7,8,}
print("The same namber is :")
for k in pairs(set1) do
if set2[k] then
print(k)
end
end
StringBuffer
Lua使用真正的垃圾收集算法,当发现程序使用太多的内存他就会遍历他所有的数据结构去释放垃圾数据,一般情况下,这个垃圾收集算法有着很好的性能——Lua的快并非偶然的
--从一个文件中逐行读入字符串
-- WARNING: bad code ahead!!
local buff = ""
for line in io.lines() do
buff = buff .. line .. "\n"
end
上面代码的循环使得该情况下Lua的垃圾回收算法的效率极其低下:
“为了理解现象的本质,假定我们身在loop中间,buff已经是一个50KB的字符串,每一行的大小为20bytes,当Lua执行buff..line.."\n"时,她创建了一个新的字符串大小为50,020 bytes,并且从buff中将50KB的字符串拷贝到新串中。也就是说,对于每一行,都要移动50KB的内存,并且越来越多。读取100行的时候(仅仅2KB),Lua已经移动了5MB的内存”——Programing in Lua
使情况变遭的是下面的赋值语句:
buff = buff .. line .. "\n"
“老的字符串变成了垃圾数据,两轮循环之后,将有两个老串包含超过100KB的垃圾数据。这个时候Lua会做出正确的决定,进行他的垃圾收集并释放100KB的内存。问题在于每两次循环Lua就要进行一次垃圾收集,读取整个文件需要进行200次垃圾收集。并且它的内存使用是整个文件大小的三倍。
这个问题并不是Lua特有的:其它的采用垃圾收集算法的并且字符串不可变的语言也都存在这个问题。Java是最著名的例子,Java专门提供StringBuffer来改善这种情况。”——Programing in Lua
解决方法:
“它连接两个小串成为一个稍微大的串,然后连接稍微大的串成更大的串。。。算法的核心是:用一个栈,在栈的底部用来保存已经生成的大的字符串,而小的串从栈定入栈。栈的状态变化和经典的汉诺塔问题类似:位于栈下面的串肯定比上面的长,只要一个较长的串入栈后比它下面的串长,就将两个串合并成一个新的更大的串,新生成的串继续与相邻的串比较如果长于底部的将继续进行合并,循环进行到没有串可以合并或者到达栈底。”——Programing in Lua
function newStack ()
return {""} -- starts with an empty string
end
function addString (stack, s)
table.insert(stack, s) -- push 's' into the the stack
for i=table.getn(stack)-1, 1, -1 do
if string.len(stack[i]) > string.len(stack[i+1]) then
break
end
stack[i] = stack[i] .. table.remove(stack)
end
end
--使用
local s = newStack()
for line in io.lines() do
addString(s, line .. "\n")
end
s = toString(s)
REF
http://book.luaer.cn/
http://www.lua.org/pil/contents.html
http://www.jb51.net/article/55717.htm
https://stackoverflow.com/questions/15708621/how-does-this-linked-list-example-in-lua-actually-work
https://stackoverflow.com/questions/21180601/lua-custom-linked-list-for-class-creation-practice-fails-to-set-node-next