lua程序设计第二版 读书笔记(11-14章)

书本下载地址                       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


 

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

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的构造式可以使这些输出代码看上去更像是一个普通的数据文件。

CSVComma-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'))

 

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

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

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

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

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

 

  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指向一个匿名函数

--匿名函数的参数tablekey取自于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函数将继承该函数的全局变量表。

 

        

你可能感兴趣的:(Lua,Lua,笔记,读书笔记,第二版,lua程序)