书本下载地址 http://download.csdn.net/detail/myy2012/5349646
本部分下载地址 http://download.csdn.net/detail/myy2012/5355935
lua程序设计第二版 读书笔记(1-4章)
第一章 开始
第二章 类型与值
第三章 表达式
第四章 语句
http://blog.csdn.net/myy2012/article/details/8900424
lua程序设计第二版 读书笔记(5-8章)
第五章 函数
第六章 深入函数
第七章 迭代器与泛型for
第八章 编译执行与错误
http://blog.csdn.net/myy2012/article/details/8906466
lua程序设计第二版 读书笔记(9-10章)
第九章 协同程序
第十章 完整的实例
http://blog.csdn.net/myy2012/article/details/8911206
lua程序设计第二版 读书笔记(11-14章)
第十一章 数据结构
第十二章 数据文件与持久性
第十三章 元表metatable与元方法meatmethod
第十四章 环境
http://blog.csdn.net/myy2012/article/details/8914457
lua程序设计第二版 读书笔记(15-17章)
第十五章 模块与包
第十六章 面向对象编程
第十七章 弱引用 table
http://blog.csdn.net/myy2012/article/details/8921632
lua程序设计第二版 读书笔记(18-21章)
第十八章 数学库
第十九章 table库
第二十章 字符串库
第二十一章 IO库
http://blog.csdn.net/myy2012/article/details/8925895
lua程序设计第二版 读书笔记(22-23章)
第二十二章 操作系统库
第二十三章 调试库
http://blog.csdn.net/myy2012/article/details/8930181
table本身 就比数据和列表的功能强大的多,因此许多算法都可以忽略一些细节问题,从而简化它们的实现。
11.1数组
使用整数来索引table即可在Lua中实现数组。例如:
a={}
for i = -5, 5 do
a[i]=0
end
然而,在Lua中的习惯一般是以1为数组的起始索引,Lua库和长度操作符都遵循这个预定。(如果你的数组不是从1开始的,那就无法使用这些功能了)
11.2 矩阵与多维数组
在Lua中,有2中方式来表示矩阵。
第一种:使用“数组的数组”,即一个table中的每个元素是另一个table
例如:
mt={}
for i=1, N do
for j=1, M do
Mt[i][j] = 0
end
end
第二种:将2个索引合并为一个索引。
例如:
mt={}
for i=1, N do
for j=1, M do
Mt[(i-1)+j]=0
end
end
稀疏矩阵:大多数元素为0或nil。
11.3链表
由于table是动态的实体,所以在Lua中实现链表是很方便的。每个节点以一个table来表示,一个“链接”只是结点table中一个字段,该字段包含了对其他table的引用。
例如:
list = nil
list ={ next = list, value = v}
local l=list
<访问 l.value>
l=l.next
end
11.4 队列与双向队列
List={}
function List.new()
return {first=0, last=-1}
end
--------------
function List.pushfirst(list, value)
local first=list.first+1
list.first=first
list[first]=value
end
function List.pushlast(list, value)
local last=list.last-1
list.last=last
list[last]=value
end
---------------
function List.popfirst(list)
local first=list.first
if first
11.5 集合与无序组(bag)
将集合元素作为索引放入一个table中,那么对于任意值都无须搜索table,只需用该值来索引table,并查看结果是否为nil。
包,有时候也称为“多重集合”,与普通的集合不同之处在于每个元素可以出现多次。
11.6字符串缓冲
在Lua中,我们可以使用函数table.concat将一个table作为字符串缓冲,它会将给定列表中的所有字符串连接起来,并返回连接的结果。
例如
local t={}
for line in io.lines() do
t[#t + 1] = line
end
local s = table.concat(t, “\n”)..”\n”
a={"good", "girl", "bad", "boy"}
local s=table.concat(a)
for k, v in ipairs(a) do
print(k, v)
end
print(s) --goodgirlbadboy
11.7图
接下来介绍一种简单的面向对象的实现:结点表示为对象、边表示为结点间的引用。
--根据给定的名称返回对应的结点
local function name2node(graph, name)
if not graph[name] then
graph[name]={name=name, adj={}}
end
return graph[name]
end
-- 构造图
function readgraph()
local graph={}
for line in io.lines() do
local namefrom, nameto = string.match(line, "(%S+)%s+(%S+)")
local from = name2node(graph, nameto) -- 查找相应的结点
from.adj[to] = true -- 将“to“添加到”from“的邻接集合
end
return graph
end
-- 函数findpath采用深度优先遍历算法,在两个结点间搜索一条路径
-- 第一个参数是当前结点,第二个参数是目标节点,
-- 第三个参数用于保存从起点到当前结点的路径,第四个参数是已访问结点的集合
-- 【注意】该算法直接对结点进行操作,而不是它们的名称
function findpath (curr, to, path, xisited)
path = path or {}
visited = visited or {}
if visited[curr] then --结点是否已访问过?
return nil --这里没有路径
end
visited[curr] = true --将结点标记为已访问
path[#path + 1] = curr --将其加到路径中
if curr == to then --最后的结点吗?
return path
end
for node in pairs(curr.adj) do --尝试所有的邻接结点
local p = findpath(node, to, path, visited)
if p then
return p
end
end
path[#path] = nil -- 从路径中删除节点
end
-- 测试上面所列函数
function printpath(path)
for i=1, #path do
print(path[i].name)
end
end
g = readgraph()
a = name2node(g, "a")
b = name2node(g, "b")
p = findpath(a,b)
if p then
printpath (p)
end
12.1 数据文件
将数据作为Lua代码来输出,当运行这些代码时,程序也就读取了数据。而table的构造式可以使这些输出代码看上去更像是一个普通的数据文件。
CSV(Comma-Separated Values 逗号分隔值):利用构造式作为格式。
自描述的数据(self-describing data):每项数据都伴随一个表示其含义的简短描述。
Lua不仅运行速度快,而且编译速度快。这不是偶然的结果,自从Lua创建之初就把数据描述作为Lua的主要应用之一来考虑的,开发人员能较快地编译大型程序投入了更多的努力。
12.2串行化
通常需要串行化一些数据,也就是将数据转换为一个字节流或字符流,然后就可以将其存储到一个文件中,或者通过网络连接发送出去了。串行化后的数据可以用Lua代码来表示,这样当运行这些代码时,存储的数据就可以的读取程序中得到重构了。
编写创建一个值的代码,方法如下:
function serialize(o)
if type(o) == “number” then
io.write(o)
elseif type(o) == “string” then
io.write(string.format(“%q”, o))
else
<其他情况>
end
end
可以使用一个简单且安全的方法来括住一个字符串,那就是以“%q”来使用string.format 函数。这样它就会用双引号来括住字符串,并且正确地转移其中的双引号和换行符等其他特殊字符。
例如:
s=’a”problematic”\\string’
print(string.format(“%q”, s)) --"a\"problematic\"\\string"
Lua 5.1还提供了另一种可以以一种安全的方法来括住任意字符串的方法:[=[...]=]
--查找最长的等号序列
function quote(s)
local n=-1
for w in string.gmatch(s, "]=*") do
n=math.max(n, #w-1)
end
local eq=string.rep("=", n+1)
return string.format(" [%s[\n%s]%s] ", eq, s, eq)
end
print(quote("good]=girl bad=boy"))
保存无环的table
保存有环的table
function basicSerialize(o)
if type(o) == "number" then
return tostring(o)
else
return string.format("%q", o)
end
end
print(basicSerialize(45))
print(basicSerialize('three'))
可以通过元表来修改一个值的行为,使其在面对一个非预定义的操作时执行一个指定的操作。例如:a和b都是table,通过元表可以定义如何计算表达式a+b。
当Lua试图将两个table相加时,它会先检查两者之一是否有元表,然后检查该元表中是否有一个叫_add的字段;如果找到该字段,就调用该字段对应的值(即元方法)。
Lua中的每个值都有一个元表。Table和userdata可以有各自独立的元表,而其他类型的值则共享所属的单一元表。
标准的字符串程序库为所有的字符串都设置了一个元表
t={}
print(getmetatable(t)) --nil
print(getmetatable(12)) --nil
print(getmetatable("hi")) --table: 0042EB20
t1={}
setmetatable(t, t1)
print(getmetatable(t)) --table: 0063B378
13.1算术类元表
假设使用加号(+)来计算两个集合的并集,那么需要让所有用于表示集合的table共享一个元表,并且在该元表中定义如何执行一个加法操作。
算术类元方法: 字段:__add __mul __ sub __div __unm __mod __pow (__concat)
tA = {1, 3}
tB = {5, 7}
mt = {}
setmetatable(tA, mt)
function union(t1, t2)
for _, item in ipairs(t2) do
table.insert(t1, item)
end
return t1
end
mt.__add = union
tAB = tA + tB
for k, v in pairs(tAB) do
print(v) --1 3 5 7
end
13.2 关系类的元方法
字段:__eq(==)、 __lt(<)、 __le(<=),其他Lua自动转换 a~=b --> not(a == b) a > b --> b < a a >= b --> b <= a
例如:比较集合大小 <
tA, tB = {3}, {1, 2}
mt = {}
function lessthan(tA, tB)
return #tA < #tB
end
mt.__lt=lessthan
setmetatable(tA, mt)
setmetatable(tB, mt)
print(tA < tB) --true
与算术类的元方法不同的是,关系类的元方法不能应用于混合的类型。对于混合类型而言,关系类元方法的行为就模拟这些操作在Lua中普通的行为。如果试图将一个字符串与一个数字作顺序性比较,Lua会引发一个错误;同样,如果试图比较两个不同元方法的对象,Lua也会引发一个错误。
13.3 库定义的元方法
13.4 table访问的元方法
算术类和关系类运算符的元方法都为各种错误情况定义了行为,它们不会改变语言的常规行为。但是Lua还提供了一种可以改变table行为的方法。有两种可以改变的table行为:查询table及修改table中不存在的字段。
当访问一个table中不存在的字段时,得到的结果为nil。这是对的,但并非完全正确;实际上,这些访问会促使解释器去查找一个叫__index的元方法。如果没有这个元方法,那么访问结果如前述的为nil;否则,就由这个元方法来提供最终结果。
__index: 当访问table中不存在的字段时,得到的结果为nil。
Window = {}
Window.prototype = {x = 0, y = 0, width = 100, height = 100}
Window.mt = {} --Window的元表
function Window.new(o)
setmetatable(o, Window.mt)
return o
end
--将Window的元方法__index指向一个匿名函数
--匿名函数的参数table和key取自于table.key。
Window.mt.__index = function(table,key)
return Window.prototype[key]
end
--下面是测试代码:
w = Window.new{x = 10, y = 20}
print(w.width) --100
print(w.height) --100
print(w.width1) --nil
说明:将一个table作为__index元方法是一种快捷、实现单一继承的方式。__index来实现相同功能的开销比较大,但方式更加灵活。
__newindex元方法:该元方法用于不存在键的赋值。当对一个table中不存在的索引赋值时,解释器就会查找__newindex元方法。如果有这个元方法,解释器就调用它,而不是执行赋值;如果这个元方法是一个table,解释器就在此table中执行赋值,而不是对原来的table。
具有默认值的table:缺省情况下,table的字段默认值为nil。但是我们可以通过元表修改这个默认值。
function setDefault(table,default)
local mt = {__index = function() return default end }
setmetatable(table,mt)
end
tab = {x = 10, y = 20}
print(tab.x,tab.z) --10 nil
setDefault(tab,0)
print(tab.x,tab.z) --10 0
跟踪table的访问:__index和__newindex都是在table中没有所需访问的index时才发挥作用的。因此,为了监控某个table的访问状况,我们可以为其提供一个空table作为代理,之后再将__index和__newindex元方法重定向到原来的table上。
t = {} --原来的table
local _t = t --保持对原有table的私有访问。
t = {} --创建代理
--创建元表
local mt = {
__index = function(table,key)
print("access to element " .. tostring(key))
return _t[key] --通过访问原来的表返回字段值
end,
__newindex = function(table,key,value)
print("update of element " .. tostring(key) .. " to " .. tostring(value))
_t[key] = value --更新原来的table
end
}
setmetatable(t,mt)
t[2] = "hello"
print(t[2])
--输出结果为
--update of element 2 to hello
--access to element 2
--hello
只读的table:
通过代理的概念,可以很容易的实现只读table。只需跟踪所有对table的更新操作,并引发一个错误即可。
环境:Lua将其所有的全局变量保存在一个常规的table(环境)中。
14.1具有动态名字的全局变量
正因为环境是一个常规的table,才可以使用一个key(变量名)去直接索引它。类似地,还可以动态地计算出一个名称,然后将一个值赋予具有该名称的全局变量。
例如:_G[varname] = name
14.2全局变量声明
Lua中的全局变量不需要声明就可以使用。尽管很方便,但是一旦出现笔误就会造成难以发现的错误。我们可以通过给_G表加元表的方式来保护全局变量的读取和设置,这样就能降低这种笔误问题的发生几率了。
14.3. 非全局的环境:
全局环境存在一个刚性的问题,即它的修改将影响到程序的所有部分。Lua 5为此做了一些改进,新的特征可以支持每个函数拥有自己独立的全局环境,而由该函数创建的closure函数将继承该函数的全局变量表。