摘记 – 《Lua程序设计》
3 数值
- 数学库提供了三个取整函数:floor,ceil和modf。其中,floor向负无穷取整,ceil向正无穷取整,modf向零取整。
5 表
- 可以认为,表是一种动态分配的对象,程序只能操作指向表的引用。除此之外,Lua语言不会进行隐藏的拷贝或创建新的表。(注:此处所谓的隐藏的拷贝是指深拷贝,即拷贝的对象的引用而非整个对象本身。
- Lua5.3引入了一个更通用的函数table.move(a, f, e, t),调用该函数可以将表a中f到e的元素移动到位置t上。函数table.move还支持使用一个表作为可选的参数,当有该可选参数的时候,该函数将第一个表中的元素移动到第二个表中。
--在表a的开头插入一个元素
table.move(a, 1, #a, 2)
a[1] = newElement
--删除表a第一个元素
table.move(a, 2, #a, 1)
a[#a] = nil
--返回表a的一个克隆
table.move(a, 1, #a, 1, {})
--将表a所有元素复制到表b末尾
table.move(a, 1, #a, #b+1, b)
6 函数
- 默认参数
function func(n)
n = n or 1
end
- 将函数用一对圆括号括起来可以强制其只返回一个结果
print((func()))
-
可变长参数函数
- …
function add(...) for _,v in ipairs{...} do --do something end end
- select函数
--函数select具有一个固定的参数selector,以及数量可变的参数 --如果selector是数值n,那么函数select返回第n个参数后的所有参数 --否则,selector应是字符串"#",以便函数select返回额外参数的总数 function add(...) local s = 0 for i=1,select("#", ...) do s = s + select(i, ...) end return s end
- 正确的尾调用
形如 return func(args)
它不会在调用堆栈上增加新的堆栈帧
10 模式匹配
- 相关函数
-- 1.string.find用于在指定的目标字符串中搜索指定的模式
string.find("hello", "l") --> 3 4
-- 2.和string.find相似,但他返回的是目标字符串中与模式相匹配的那部分子串,而不是该模式在的位置
string.match("hello", "llo") --> llo
string.match("today is 17/7/1990", "%d+/%d+/%d+") --> 17/7/1990
-- 3.string.gsub把目标字符串中出现模式的地方替换成字符串
string.gsub("lua is cute", "cute", "great", 1) --> lua is great (第四个参数限制次数
-- 4. string.gmatch返回一个函数,通过该函数可以遍历一个字符串中所有出现的指定模式
s = "some thing"
for w in string.gmatch(s, "%a+") do
print(w)
end
--> some
--> thing
- 模式
1.字符分类
字符 | 含义 | 字符 | 含义 |
---|---|---|---|
. | 任意字符 | %l | 小写字母 |
%a | 字母 | %p | 标点符号 |
%c | 控制字符 | %s | 空白字符 |
%d | 数字 | %u | 大写字母 |
%g | 除空格外的可打印字符 | %w | 字母和数字 |
%x | 十六进制数字 |
这些类的大写形式表示类的补集。
2.魔法字符
( ) . % + – * ? [ ] ^ $
以^开头的模式表示从目标字符串的开头开始匹配
以$结尾的模式表示匹配到目标字符串的结尾
(只有位于模式的开头和结尾才具有特殊意义,否则只是与自身相匹配的普通字符)
3.修饰符
字符 | 含义 | 字符 | 含义 |
---|---|---|---|
+ | 重复一次或多次 | * | 重复零次或多次 |
– | 重复零次或多次(最小匹配) | ? | 可选(出现零次或一次) |
15 数据文件和序列化
我们常常需要将某些数据序列/串行化,即将数据转换为字节流或字符流,以便将其存储到文件中或者通过网络传输。
16 编译、执行和错误
Lua中处理错误,可以使用pcall来包装需要执行的代码。pcall接收一个函数和要传递给后者的参数,并执行。执行结果:有错误、无错误;返回值true或者false,errorinfo。
if pcall(function_name, ...) then
-- no error
else
-- some error
end
pcall以一种“保护模式”来调用第一个参数,因此pcall可以捕获函数执行中的任何错误。
17 模块和包
函数require在表package.loaded中检查模块是否已被加载。如果模块已经被加载,函数require就返回相应的值。因此,一旦一个模块被加载过,后续的对于同一模块的所有require调用都将返回同一个值,而不会再运行任何代码。
要强制函数require加载统一模块两次,可以先将模块从package.loaded中删除:
package.loaded.modname = nil
18 迭代器和泛型for
泛型for为一次迭代循环做了所有的记录工作:它在内部保存了迭代函数,因此不需要变量iter;它在每次做新的迭代时都会再次调用迭代器,并在迭代器返回nil时结束循环。
实际上,泛型for保存了三个值:一个迭代函数,一个不可变状态和一个控制变量。
local function iter(t, i)
i = i+1
local v = t[i]
if v then
return i,v
end
end
function ipairs(t)
return iter,t,0
end
20 元表和元方法
元表可以修改一个值在面对一个未知操作时的行为。
算术运算相关的元方法:加法 __add,减法 __sub,乘法 __mul,除法 __div,负数 __unm,取模 __mod,幂运算 __pow。
位操作相关的元方法:按位与 __band,按位或 __bor,按位异或 __bxor,按位取反 __bnot,向左移位 __shl,向右移位 __shr,连接符行为 __concat。
关系运算相关的元方法:等于 __eq,小于 __lt,小于等于 __le。其他三个关系运算符没有单独的元方法,lua语言会将 a~=b 转换为 not (a == b) , a > b 转换为 b < a , a >= b 转换为 b <= a。
库定义相关的元方法:__tostring, __metatable, __pairs。
表相关的元方法:__index, __newindex。
21 面向对象(object-oriented)编程
Account = {balance = 0}
function Account:withdraw(v)
self.balance = self.balance - v
end
- 类(Class)
类和原型都是一种组织多个对象间共享行为的方式。
为了创建其他与Account行为类似的账号,我们可以使用__index元方法让这些新对象从Account中继承这些操作。
function Account:new(o)
o = o or {}
self.__index = self
setmetatable(o, self)
return o
end
- 继承(Inheritance)
SpecialAccount = Account:new()
s = SpecialAccount:new{limit = 1000}
SpecialAccount就像继承其他方法一样从Account继承了new。
- 私有性(Privacy)
许多人认为,私有性是一门面向对象语言不可或缺的一部分:每个对象的状态都应该由它自己控制。
此前,我们所学习的Lua语言中标准的对象实现方式没有提供私有性机制。一方面,这是使用普通结构(表)来表示对象所带来的后果;另一方面,这也是Lua语言为了避免冗余和人为限制所采取的方法。
不过尽管如此,Lua语言还是可以用其他方式来实现具有访问控制能力的对象。
function newAccount(initalBalance)
local self = {balance = initalBalance}
local withdraw = function(v)
self.balance = self.balance - v
end
return{
withdraw = withdraw
}
end
22 环境
Lua语言中处理全局变量的方式:
– 编译器在编译所有代码段前,在外层创建局部变量_ENV
– 编译器将所有自由名称var变换为_ENV.var;
– 函数load(或函数loadfile)使用全局环境初始化代码段的第一个上值,及Lua语言内部维护的一个普通的表。
23 垃圾收集
- 弱引用表
弱引用表、析构器和函数是在Lua语言中用来辅助垃圾收集器的主要机制。弱引用表允许收集Lua语言中还可以被程序访问的对象;析构器允许收集不在垃圾收集器直接控制下的外部对象;函数collectgarbage则允许我们控制垃圾收集器的步长。
弱引用表就是一种用来告知Lua语言一个引用不应阻止对一个对象回收的机制。所谓弱引用是一种不在垃圾收集器考虑范围内的对象引用。如果对一个对象的所有引用都是弱引用,那么垃圾收集器将会回收这个对象并删除这些弱引用。
一个表是否为弱引用表示由其元表中的__mode字段所决定的。”k”,”v”,”kv”
注意:只有对象可以从弱引用表中被移除,而像数字和布尔这样的“值”是不可回收的。
对于一个字符串类型的键来说,除非它对应的值被回收,否则是不会从弱引用表中被移除的。
- 垃圾收集器
i. Lua5.0及之前
一直到Lua5.0,Lua语言使用的都是一个简单的标记-清除式垃圾收集器。意味着会时不时停止主程序的运行来执行一次完整的垃圾收集周期。每个垃圾收集周期由四个阶段组成:标记、清理、清除和析构。
标记阶段把根节点集合标记为活跃。
清理阶段处理析构器和弱引用表。
清除阶段遍历所有对象。如果一个对象没有被标记为活跃,Lua语言就将其回收。否则,Lua语言清理标记,然后准备进行下一个清理周期。
最后再析构阶段,Lua语言调用清理阶段被分离出的对象的析构器。
ii. Lua5.1
Lua5.1使用了增量式垃圾收集器,这种垃圾收集器像老版的垃圾收集器一样执行相同的步骤,但是不需要在垃圾收集期间停止主程序的运行,相反,它与解释器一起交替运行。
iii. Lua5.2
Lua5.2引入了紧急垃圾收集。当内存分配失败时,Lua语言会强制进行一次完整的垃圾收集,然后再次尝试分配。
24 协程
从多线程的角度看,协程coroutine与线程thread类似:协程是一系列的可执行语句,拥有自己的栈、局部变量和指令指针,同时协程又与其他协程共享了全局变量和其他几乎一切资源。线程与协程的主要区别在于,一个多线程程序可以并行运行多个线程,而协程却需要彼此协作地运行,即在任意指定的时刻只能有一个协程在运行,且只有当正在运行的协程显式地要求被挂起时其执行才会暂停。
一个协程有以下四种状态,即挂起 suspended、运行 running、正常 normal和死亡 dead。
当协程A唤醒协程B时,协程A既不是挂起状态也不是运行状态,因为既不能唤醒协程A 同时正在运行的是协程,所以,协程A此时的状态就被称为正常状态。
Lua语言中有一个非常有用的机制是通过一对 resume-yield来交换数据。第一个resume函数会把所有的额外参数传递给协程的主函数:
co = coroutine.create(function(a,b,c)
print("co",a,b,c+2)
end)
cortoutine.resume(co,1,2,3) -> co 1 2 5
Lua语言提供的是所谓的非对称协程,也就是说需要两个函数来控制协程的执行,一个用于挂起协程的执行,另一个用于恢复协程的执行。而其他一些语言提供的事对称协程,只提供一个函数用于在一个协程和另一个协程之间的切换控制权。
25 反射
反射是程序用来检查和修改自身某些部分的能力。像Lua语言这样的动态语言支持几种反射环境:
- 环境允许运行时观察全局变量
- 诸如type和require这样的函数允许运行时检查和遍历未知数据结构
- 诸如load和require这样的函数允许程序在自身中追加代码或更新代码