打开APP
userphoto
未登录

开通VIP,畅享免费电子书等14项超值服

开通VIP
lua程序设计第二版 读书笔记(11
分类: Lua Lua 笔记 读书笔记 第二版 lua程序 2013-05-11 15:14 176人阅读 评论(0) 收藏 举报

目录(?)[+]

书本下载地址                       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中实现数组。例如:

  1. a={}  
  2.   
  3. for  i = -5, 5 do   
  4.   
  5. a[i]=0  
  6.   
  7. end  


 

然而,在Lua中的习惯一般是以1为数组的起始索引,Lua库和长度操作符都遵循这个预定。(如果你的数组不是从1开始的,那就无法使用这些功能了)

11.2 矩阵与多维数组

Lua中,有2中方式来表示矩阵。

第一种:使用“数组的数组”,即一个table中的每个元素是另一个table

例如:

  1. mt={}  
  2.   for  i=1, N do   
  3.    for  j=1, M do  
  4.                  Mt[i][j] = 0  
  5. end  
  6.   end  

 

第二种:将2个索引合并为一个索引。

例如:

  1. mt={}  
  2.       for i=1, N do  
  3. for j=1, M do  
  4. t[(i-1)+j]=0  
  5.              end   
  6. nd  


 

稀疏矩阵:大多数元素为0nil

11.3链表

由于table是动态的实体,所以在Lua中实现链表是很方便的。每个节点以一个table来表示,一个“链接”只是结点table中一个字段,该字段包含了对其他table的引用。

例如:

  1. list = nil   
  2.         list ={ next = list, value = v}  
  3.   local l=list  
  4.    <访问 l.value>  
  5.   l=l.next  
  6. nd  


 

11.4 队列与双向队列

 

  1. List={}  
  2. function List.new()  
  3.     return {first=0, last=-1}  
  4. end  


 

--------------

  1. function List.pushfirst(list, value)  
  2.     local first=list.first+1  
  3.     list.first=first  
  4.     list[first]=value  
  5. end  
  6. function List.pushlast(list, value)  
  7.     local last=list.last-1  
  8.     list.last=last  
  9.     list[last]=value  
  10. end  


 

---------------

  1. function List.popfirst(list)  
  2.     local first=list.first  
  3.     if first<list.last then  
  4.         error("list is empty")  
  5.     end  
  6.     local value=list[first]  
  7.     list[first]=nil  
  8.     list.first=first-1  
  9.     return value  
  10. end  
  11. function List.poplast(list)  
  12.     local last=list.last  
  13.     if list.first<last then  
  14.         error("list is empty")  
  15.     end  
  16.     local value=list[last]  
  17.     list[last]=nil  
  18.     list.last=last+1  
  19.     return value  
  20. end  


 

11.5 集合与无序组(bag

 

将集合元素作为索引放入一个table中,那么对于任意值都无须搜索table,只需用该值来索引table,并查看结果是否为nil

包,有时候也称为“多重集合”,与普通的集合不同之处在于每个元素可以出现多次。

11.6字符串缓冲

Lua中,我们可以使用函数table.concat将一个table作为字符串缓冲,它会将给定列表中的所有字符串连接起来,并返回连接的结果。

例如

  1.   local t={}  
  2.         for  line in io.lines() do  
  3. t[#t + 1] = line  
  4. end  
  5. local s = table.concat(t, “\n”)..”\n”  
  6.   
  7. a={"good", "girl", "bad", "boy"}  
  8. local s=table.concat(a)  
  9.   
  10. for k, v in ipairs(a) do  
  11.     print(k, v)  
  12. end  
  13. print(s)    --goodgirlbadboy  


 

11.7

接下来介绍一种简单的面向对象的实现:结点表示为对象、边表示为结点间的引用

--根据给定的名称返回对应的结点

  1. local function name2node(graph, name)  
  2.     if not graph[name] then  
  3.         graph[name]={name=name, adj={}}  
  4.     end  
  5.     return graph[name]  
  6. end  


 

-- 构造图

 

  1. function readgraph()  
  2.     local graph={}  
  3.     for line in io.lines() do  
  4.         local namefrom, nameto = string.match(line, "(%S+)%s+(%S+)")  
  5.         local from = name2node(graph, nameto)   -- 查找相应的结点  
  6.         from.adj[to] = true     -- 将“to“添加到”from“的邻接集合  
  7.     end  
  8.     return graph  
  9. end  


 

-- 函数findpath采用深度优先遍历算法,在两个结点间搜索一条路径

-- 第一个参数是当前结点,第二个参数是目标节点,

-- 第三个参数用于保存从起点到当前结点的路径,第四个参数是已访问结点的集合

-- 【注意】该算法直接对结点进行操作,而不是它们的名称

 

  1. function findpath (curr, to, path, xisited)  
  2.     path = path or {}  
  3.     visited = visited or {}  
  4.     if visited[curr] then       --结点是否已访问过?  
  5.         return nil              --这里没有路径  
  6.     end  
  7.     visited[curr] = true        --将结点标记为已访问  
  8.     path[#path + 1] = curr      --将其加到路径中  
  9.     if curr == to then          --最后的结点吗?  
  10.         return path  
  11.     end  
  12.     for node in pairs(curr.adj) do      --尝试所有的邻接结点  
  13.         local p = findpath(node, to, path, visited)  
  14.         if p then  
  15.             return p  
  16.         end  
  17.     end  
  18.     path[#path] = nil           -- 从路径中删除节点  
  19. end  


 

 

-- 测试上面所列函数

  1. function printpath(path)  
  2.     for i=1, #path do  
  3.         print(path[i].name)  
  4.     end  
  5. end  
  6.   
  7. g = readgraph()  
  8. a = name2node(g, "a")  
  9. b = name2node(g, "b")  
  10. p = findpath(a,b)  
  11.   
  12. if p then  
  13.     printpath (p)  
  14. end  


 

第十二章 数据文件与持久性

12.1 数据文件

将数据作为Lua代码来输出,当运行这些代码时,程序也就读取了数据。而table的构造式可以使这些输出代码看上去更像是一个普通的数据文件。

CSVComma-Separated Values 逗号分隔值):利用构造式作为格式。

自描述的数据(self-describing data):每项数据都伴随一个表示其含义的简短描述。

Lua不仅运行速度快,而且编译速度快。这不是偶然的结果,自从Lua创建之初就把数据描述作为Lua的主要应用之一来考虑的,开发人员能较快地编译大型程序投入了更多的努力。

12.2串行化

通常需要串行化一些数据,也就是将数据转换为一个字节流或字符流,然后就可以将其存储到一个文件中,或者通过网络连接发送出去了。串行化后的数据可以用Lua代码来表示,这样当运行这些代码时,存储的数据就可以的读取程序中得到重构了。

编写创建一个值的代码,方法如下:

  1. function serialize(o)  
  2.   if type(o) == “number” then  
  3.     io.write(o)  
  4.   elseif type(o) == “string” then  
  5.     io.write(string.format(“%q”, o))  
  6.   else   
  7.   <其他情况>  
  8.   end  
  9. end   


 

 

可以使用一个简单且安全的方法来括住一个字符串,那就是以“%q”来使用string.format 函数。这样它就会用双引号来括住字符串,并且正确地转移其中的双引号和换行符等其他特殊字符。

例如: 

  1. s=’a”problematic”\\string’  
  2. print(string.format(“%q”, s))  --"a\"problematic\"\\string"  


 

Lua 5.1还提供了另一种可以以一种安全的方法来括住任意字符串的方法:[=[...]=]

--查找最长的等号序列

  1. function quote(s)  
  2.     local n=-1  
  3.     for w in string.gmatch(s, "]=*") do  
  4.         n=math.max(n, #w-1)  
  5.     end  
  6.     local eq=string.rep("=", n+1)  
  7.     return string.format(" [%s[\n%s]%s] ", eq, s, eq)  
  8. end  
  9. print(quote("good]=girl bad=boy"))  

 

保存无环的table

保存有环的table

  1. function basicSerialize(o)  
  2.     if type(o) == "number" then  
  3.         return tostring(o)  
  4.     else  
  5.         return string.format("%q", o)  
  6.     end  
  7. end  
  8. print(basicSerialize(45))  
  9. print(basicSerialize('three'))  

 

第十三章 元表(metatable)与元方法(meatmethod

可以通过元表来修改一个值的行为,使其在面对一个非预定义的操作时执行一个指定的操作。例如:ab都是table,通过元表可以定义如何计算表达式a+b

Lua试图将两个table相加时,它会先检查两者之一是否有元表,然后检查该元表中是否有一个叫_add的字段;如果找到该字段,就调用该字段对应的值(即元方法)。

Lua中的每个值都有一个元表。Tableuserdata可以有各自独立的元表,而其他类型的值则共享所属的单一元表。

标准的字符串程序库为所有的字符串都设置了一个元表

 

  1. t={}  
  2. print(getmetatable(t)) --nil  
  3. print(getmetatable(12)) --nil  
  4. print(getmetatable("hi")) --table: 0042EB20  
  5.   
  6. t1={}  
  7. setmetatable(t, t1)  
  8. print(getmetatable(t)) --table: 0063B378  

 

13.1算术类元表 

 

假设使用加号(+)来计算两个集合的并集,那么需要让所有用于表示集合的table共享一个元表,并且在该元表中定义如何执行一个加法操作。

算术类元方法字段:__add __mul __ sub __div __unm __mod __pow (__concat)

  1. tA = {1, 3}  
  2. tB = {5, 7}  
  3. mt = {}  
  4. setmetatable(tA, mt)  
  5.    
  6. function union(t1, t2)  
  7.  for _, item in ipairs(t2) do  
  8.    table.insert(t1, item)  
  9.  end  
  10.  return t1  
  11. end  

  1. mt.__add = union  
  2. tAB = tA + tB  
  3. for k, v in pairs(tAB) do  
  4.     print(v)    --1 3 5 7  
  5. end  


 

13.2 关系类的元方法

字段:__eq(==) __lt(<)、 __le(<=),其他Lua自动转换 a~=b --> not(a == b)  a > b --> b < a    a >= b --> b <= a

例如:比较集合大小 <

  1. tA, tB = {3}, {1, 2}  
  2. mt = {}  
  3. function lessthan(tA, tB)  
  4.     return #tA < #tB  
  5. end  
  6.   
  7. mt.__lt=lessthan  
  8.   
  9. setmetatable(tA, mt)  
  10. setmetatable(tB, mt)  
  11.   
  12. print(tA < tB)   --true  


 

与算术类的元方法不同的是,关系类的元方法不能应用于混合的类型。对于混合类型而言,关系类元方法的行为就模拟这些操作在Lua中普通的行为。如果试图将一个字符串与一个数字作顺序性比较,Lua会引发一个错误;同样,如果试图比较两个不同元方法的对象,Lua也会引发一个错误。

13.3 库定义的元方法

13.4 table访问的元方法

算术类和关系类运算符的元方法都为各种错误情况定义了行为,它们不会改变语言的常规行为。但是Lua还提供了一种可以改变table行为的方法。有两种可以改变的table行为:查询table及修改table中不存在的字段。

当访问一个table中不存在的字段时,得到的结果为nil。这是对的,但并非完全正确;实际上,这些访问会促使解释器去查找一个叫__index的元方法。如果没有这个元方法,那么访问结果如前述的为nil;否则,就由这个元方法来提供最终结果。

__index:  当访问table中不存在的字段时,得到的结果为nil

  1. Window = {}  
  2. Window.prototype = {x = 0, y = 0, width = 100, height = 100}  
  3. Window.mt = {}  --Window的元表  
  4.   
  5. function Window.new(o)  
  6.  setmetatable(o, Window.mt)  
  7.  return o  
  8. end  

 

--Window的元方法__index指向一个匿名函数

--匿名函数的参数tablekey取自于table.key

  1. Window.mt.__index = function(table,key)   
  2.   return Window.prototype[key]   
  3. end  

 

--下面是测试代码:

 

  1. w = Window.new{x = 10, y = 20}  
  2. print(w.width)   --100  
  3. print(w.height)   --100  
  4. print(w.width1)  --nil  


 

说明:将一个table作为__index元方法是一种快捷、实现单一继承的方式。__index来实现相同功能的开销比较大,但方式更加灵活。

__newindex元方法:该元方法用于不存在键的赋值。当对一个table中不存在的索引赋值时,解释器就会查找__newindex元方法。如果有这个元方法,解释器就调用它,而不是执行赋值;如果这个元方法是一个table,解释器就在此table中执行赋值,而不是对原来的table

具有默认值的table:缺省情况下,table的字段默认值为nil。但是我们可以通过元表修改这个默认值

  1. function setDefault(table,default)  
  2.     local mt = {__index = function() return default end }  
  3.     setmetatable(table,mt)  
  4. end  
  5. tab = {x = 10, y = 20}  
  6. print(tab.x,tab.z)  --10    nil  
  7. setDefault(tab,0)  
  8. print(tab.x,tab.z)  --10    0  


 

跟踪table的访问:__index__newindex都是在table中没有所需访问的index时才发挥作用的。因此,为了监控某个table的访问状况,我们可以为其提供一个空table作为代理,之后再将__index__newindex元方法重定向到原来的table

 

  1. t = {}        --原来的table  
  2. local _t = t    --保持对原有table的私有访问。  
  3. t = {}        --创建代理  
  4. --创建元表  
  5. local mt = {  
  6.  __index = function(table,key)  
  7.      print("access to element " .. tostring(key))  
  8.      return _t[key]  --通过访问原来的表返回字段值  
  9.  end,  
  10.   
  11.  __newindex = function(table,key,value)  
  12.      print("update of element " .. tostring(key) .. " to " .. tostring(value))  
  13.      _t[key] = value  --更新原来的table  
  14.  end  
  15. }  
  16. setmetatable(t,mt)  


 

 

  1. t[2] = "hello"  
  2. 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函数将继承该函数的全局变量表。

 

本站仅提供存储服务,所有内容均由用户发布,如发现有害或侵权内容,请点击举报
打开APP,阅读全文并永久保存 查看更多类似文章
猜你喜欢
类似文章
【热】打开小程序,算一算2024你的财运
Step By Step(Lua元表与元方法)(转)
Lua笔记
lua基础语法
【笨木头Lua专栏】基础补充22:弱引用table | 笨木头与游戏开发
Lua变量类型简明总结
Lua1
更多类似文章 >>
生活服务
热点新闻
分享 收藏 导长图 关注 下载文章
绑定账号成功
后续可登录账号畅享VIP特权!
如果VIP功能使用有故障,
可点击这里联系客服!

联系客服