A:
-- 使用构造的方式。
squares = {1, 4, 9, 16, 25, 36, 49, 64, 81}
-- 使用"for"循环 + 连续的索引。
a = {} -- new array
for i=1, 1000 do
a[i] = 0
end
-- 当然,连续的索引非必须都是正数。
a = {}
for i=-5, 5 do
a[i] = 0
end
A:
-- 这里以二维数组举例,多维数组原理相同。
-- 使用嵌套"table"的方式。
mt = {} -- create the matrix
for i=1, N do
mt[i] = {} -- create a new row
for j = 1, M do
mt[i][j] = 0
end
end
A:
list = nil
-- 插入。
list = {next = list, value = v}
-- 遍历。
local l = list
while l do
print(l.value)
l = l.next
end
在Lua中你很少会需要使用链表,因为有比使用链表组织数据更好的方式。比如实现一个栈,可以使用一个数组加上一个存储栈顶索引的变量,而无需使用链表。
A:以下这个例子即实现了队列,又实现了栈,
-- 为了避免污染全局空间,将函数和数据都存放在了名为List的table中。
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
当作为队列使用时,可以使用pushright()
入队,使用popleft()
出队(或者pushleft()
入队,popright()
出队)。
当作为栈使用时,可以使用pushright()
入栈,使用popright()
出栈(或者pushleft()
入队,popright()
出队)。
A:将集合中元素的名称作为”table”的索引,并为”table”中该元素的值赋值为true
,
--[[ "while","end","function","local"都是Lua的关键字,
所以不能直接写成"while = true"的样式,会报错。]]
reserved = {
["while"] = true, ["end"] = true,
["function"] = true, ["local"] = true,
my_identifiers = true,
}
-- 可以快速判断某一元素是否在集合中。
if reserved[w] then -- 在集合中。
...
else -- 不在集合中。
...
end
A:以下例子是从文件中一行一行读取数据,最后将读取的数据组合为一个字符串整体,
local t = {}
for line in io.lines("file") do -- 从文件中一行一行的读取数据。
table.insert(t, line) -- 读取的每一行数据作为"table"的一个成员。
end
--[[ "table.concat()"对于"table"中最后一个字符串后不会添加分隔符, 所以在"t"的最后插入一个空字符串, 让"table.concat()"在这个空字符串与原本的最后一个字符串之间插入分隔符。]]
table.insert(t, "")
--[[ "table.concat()"将"t"中所有字符串成员连接在一起, 并在每个字符串之间添加指定的分隔符。]]
s = table.concat(t, "\n")
1、”table”不是Lua中的一种数据结构,而是Lua中唯一的数据结构。许多其他语言提供的数据结构,比如array,list,queue,set等等都可以在Lua使用”table”高效的实现。
2、创建集合还可以使用辅助函数的方式来实现,
function Set (list)
local set = {}
for _, l in ipairs(list) do set[l] = true end
return set
end
reserved = Set{"while", "end", "function", "local", }
3、实现字符串缓存有一种不使用”table”的方式,
-- WARNING: bad code ahead!!
local buff = ""
for line in io.lines("file") do
buff = buff .. line .. "\n"
end
这种实现方式对于小文件来说性能还可以接受,但对于大文件来说性能会非常低。在我的机器上测试,使用这种方式读取一个1M大小,20000行的文件耗时5.1秒,而用”Q & A”的例子中的程序读取相同的文件仅耗时0.030秒。
为什么会有这么大的差距?罪魁祸首是字符串连接符..
。
Lua使用真正的垃圾收集算法,当他发现程序使用太多的内存时,他就会遍历程序使用的所有数据结构,并释放那些不再被使用的数据结构(垃圾)。一般情况下,这个算法有很好的性能(Lua的高效并非偶然),但是上面那段代码的循环使得算法的效率极其低下。
为了理解现象的本质,假定我们的for
循环已经运行了一会儿,buff
中已经存储了50KB的字符串,每一行的大小为20bytes。当程序再次执行到buff..line.."\n"
时,他创建了一个新的大小为50,020bytes的字符串(Lua中所有的字符串都是常量,不能改变其中的任意字符,所以..
是创建了一个新的字符串),并且从buff中将50KB的字符串拷贝到新串中。也就是说,对于每一行都要移动50KB的内存,并且会越来越多。读取到100行的时候(仅仅2KB),Lua已经移动了5MB的内存。
这些内存拷贝仅仅是一部分原因,使情况变的更遭的是下面的赋值语句,
buff = buff .. line .. "\n"
旧的字符串变成了垃圾数据,两轮循环之后将有两个旧字符串包含超过100KB的垃圾数据。这个时候Lua会做出正确的决定,进行他的垃圾收集并释放这100KB的内存。问题在于每两次循环Lua就要进行一次垃圾收集,读取到20000行的时候需要进行10000次垃圾收集,回收的内存高达1GB,光这些被回收的内存就已经是原文件大小的1000倍了。
4、如果一定要用..
的方式实现字符串缓存,我们将使用一种稍微高效些的算法。最初的算法通过将循环每一行的字符串连接到旧字符串上来解决问题,新的算法将避免如此。它连接两个短字符串成为一个稍长的字符串,然后连接两个稍长的字符串成为更长的字符串。算法的核心是用一个栈,在栈的底部用来保存已经生成的长字符串,而短的字符串从栈顶入栈。栈的状态变化和经典的汉诺塔问题类似:位于栈下面的字符串肯定比上面的长。只要一个字符串入栈后比它下面的字符串长,那么就将这两个字符串合并成一个新的更长的字符串。新生成的字符串继续与相邻的字符串比较,如果长于下面的字符串将继续进行合并,循环进行到没有串可以合并或者到达栈底为止。
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 = #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("file") do
addString(s, line)
end
s = table.concat(s)
使用这种实现方式读取与”Q & A”中相同的文件耗时0.055秒。
5、如果读取文件,最快的方式还是io.read(*all)
,
io.input("file")
t = io.read("*all")
使用io.read(*all)
读取与”Q & A”中相同的文件仅耗时0.007秒。