现在在北京实习,每天都挺忙的,很充实,但是,博客的更新可能就没准了,毕竟回到住的地方好晚了的说= =。
本篇文章,将lua的库来总结一下:
1. 数学库
2. table库
3. 字符串库
4. I/O库
5. 操作系统库
6. 调试库
1.数学库
数学库由一组标准的数学函数构成。
三角函数(sin、cos、tan、asin、acos等)
这些的三角函数都使用弧度单位,但可以用函数 deg、rad来转化角度和弧度。
所以,我们可以将 弧度单位的三角函数变成角度单位的:
math.sin = function(x) return sin(rad(x)) end
指数和对数函数(exp、log、log10)
math.exp(x) -- e为底x次方
math.log(x) -- x的自然对数
math.log10(x) -- 10为底,x的对数
取整函数(floor、ceil) and (max、min)
- floor是向下取整,
- ceil是向上取证,
- max与min就不用多说
math.floor(9.5) = 9
mtah.ceil(9.5) = 10
伪随机函数(random、randomseed)
- random 用于生成伪随机数,有三种方法调用:
没有参数,返回一个区间 [0, 1) 内均匀分布的伪随机实数 (大于等于0,小于1) - 提供一个整数n作为参数,返回一个在区间[1, n]内的伪随机整数 (大于等于1,小于等于n)
- 提供两个整数参数m和n,它将返回一个在区间[m, n]内的伪随机整数 (大于等于m,小于等于n)
- randomseed 用来设置伪随机数生成器的种子数,如果之前用过伪随机,都会知道种子。
- 我们一般将当前的时间作为种子值 os.time()
变量(pi、huge)
- pi 就是π的值,直接输出就是 3.1415926535898
- huge 浮点数 HUGE_VAL,这个数比任何数字值都大
2.table库
table库中的函数把table作为数组来操作。
lua中的数组,下标是从1开始的
插入和删除函数
table.insert(table, value)
table.insert(table, index, value)
table.remove(table)
table.remove(table, index)
table库的函数将table当做数组来操作,所以插入、删除操作都会有index,如果没有index则默认在末尾 添加/删除。
local show = function (t)
local temp = ""
for i,v in ipairs(t) do
temp = temp..v.." "
end
print(temp)
end
local tb = {2, 4, 6, 8, 10}
show(tb)
table.insert(tb, 1)
show(tb)
table.insert(tb, 3, 5)
show(tb)
table.remove(tb)
show(tb)
table.remove(tb, 5)
show(tb)
-- result
2 4 6 8 10
2 4 6 8 10 1
2 4 5 6 8 10 1
2 4 5 6 8 10
2 4 5 6 10
排序
table.sort(table, comp)
第二个参数 是一个函数,这个函数接受两个参数,当第一个元素需要排在第二个元素之前时,返回true。
第二个参数就是在传递table排序顺序。
还有一点,table的sort是不稳定的排序。
local tb = {10, 3, 14, 15, 11, 1, 5, 6, 7, 6}
show(tb)
table.sort(tb)
show(tb)
table.sort(tb, function (v1, v2)
if v1 > v2 then
return true
end
end)
show(tb)
-- result
10 3 14 15 11 1 5 6 7 6
1 3 5 6 6 7 10 11 14 15
15 14 11 10 7 6 6 5 3 1
连接
看我之前的show函数来输出,是不是好麻烦的感觉,其实,table库中有连接的函数。
table.concat(table, sep, i, j)
table —— 需要连接的table
sep —— 中间的分隔符(可选)
i, j —— 连接第i个到第j个,如果 i>j 返回空串(可选)
local tb = {1, 2, 3, 4, 5, 6, 7}
local temp = table.concat(tb)
print(temp)
temp = table.concat(tb, "*")
print(temp)
temp = table.concat(tb, "*", 3)
print(temp)
temp = table.concat(tb, "*", 2, 6)
print(temp)
temp = table.concat(tb, "*", 4, 3)
print(temp)
-- result
1234567
1*2*3*4*5*6*7
3*4*5*6*7
2*3*4*5*6
(空串)
3.字符串库
原始的lua解释器操作字符串的能力是有限的,一个程序只能创建字符串常量、连接字符串及获取字符串长度,无法提取或者检索字符串内容。
lua中真正的字符串操作能力来源于——字符串库
基础的字符串函数
- 字符串长度函数
string.len(str)
- 复制n次字符串
string.rep(str, num)
- 获得 大写/小写 的副本
string.upper(str)/lower(str)
- 截取字符串
string.sub(str, i, j)
注意,字符串的下标是从1开始的。
这里的下标也可以是负数,负数代表从尾部开始计数,-1就是字符串的最后一个字符,-2是字符串的倒数第二个字符,以此类推。
比如:
local str = "abcdefg"
print( string.sub(str, 2, -3) )
-- result
bcde
而且lua中的string是不可变的,就像上例,我用了 string.sub(str,2 , -3),这是 str没有变,只是返回了一个新串,内容为 bcde
- 转换字符及内部数值
string.char(...) & string.byte(str, i, j)
local val = 97
print(string.char(val, val+1, val+2))
print(string.byte("abcdefg"))
print(string.byte("abcdefg", 3))
print(string.byte("abcdefg", 4, -1))
-- result
abc
97
99
100 101 102 103
string.byte中 第二个参数,如果是单独一个数值,就返回相应下标的内部数值,当然可以是负数(倒着数)
如果 有第三个参数,就从第二个参数到第三个参数的内部数值,就像string.sub那样
模式匹配
字符串库中最强大的函数—— find、match、gsub、gmatch,这些都是基于 模式 的。
- string.find(s, pattern, [,init[, plain]])
根据模式 or 子串 进行查找操作
s是被查找的字符串,
pattern是匹配模式,
init指明从哪里开始搜索(可以是负数),
当plain为true时关闭模式匹配机制,直接查找子串。
函数返回两个数值,匹配到的起始位置与终止位置,如果没找到,返回nil - string.match(s, pattern, [,init])
基本上和string.find差不多,但是string.match返回的是字符串而非位置。 - string.gsub(s, pattern, repl[,n])
就是替换,作用是将(前n个) pattern 替换成 repl,并返回替换后的字符串,
repl可以是字符串、表、函数
gsub函数还会在第二个返回值返回 一共发生了多少次匹配 - string.gmatch(s, pattern)
gmatch会返回一个迭代器函数,通过这个迭代器函数可以遍历到每一个出现指定模式的地方。 - 模式
之前说的是模式匹配,关键点在于模式。
字符分类:
符号 | 意义 |
---|---|
x | 表示字符x自身(不能是魔法字符^$()%.[]*+-?中的一员) |
. | 表示任何字符 |
%a | 表示任何字母 |
%c | 表示任何控制字符 |
%d | 表示任何数字 |
%g | 表示任何除空白符外的可打印字符 |
%l | 表示所有小写字母 |
%p | 表示所有标点符号 |
%s | 表示所有空白字符 |
%u | 表示所有大写字母 |
%w | 表示所有字母及数字 |
%x | 表示所有16进制数字符号 |
[set] | 表示set 中所有字符的联合,可以使’-‘连接,升序书写范围两端的字符来表示一个范围的字符集 |
[^set] | 表示set的补集 |
上面的这些中,比如%字母,如果字母为大写,则表示该模式的补集;%S->表示所有非空白字符
模式条目:
单个字符类匹配该类别中任意单个字符
单个字符类后跟’+” 重复1次或多次
单个字符类后跟’*’ 重复0次或多次,匹配尽可能长的串
单个字符类后跟’-’ 也是重复0次或多次,匹配尽可能短的串
单个字符类后跟’?’ 可选部分(出现0次或1次)
%n,n可以从1到9,匹配一个等于n号捕获物的子串
%bxy, x与y是两个明确的字符,这个条目匹配以x开始y结束,其中x和y保持平衡的字符串。意思是,如果从左到右读这个字符串,对每次读到一个x就+1,读到一个y就-1,最终结束处的那个y是第一个记数到0的y
%f[set], 边境模式,匹配一个位于set内某个字符之前的一个空串,且这个位置的前一个字符不属于set。
4.I/O库
I/O库为文件操作提供了两种不同的模型:简单模型 & 完整模型。
简单模型假设有一个当前输入文件和一个当前输出文件,所有I/O操作都作用于这些文件。
完整模型则使用显式的句柄,采用面向对象的风格,将所有的操作定义为文件句柄上的方法。
简单的I/O模型
- io.input(filename) & io.output(filename)
io.input(filename)调用会以只读模式打开指定的文件,并将其设为输入文件,之后除非再次调用io.input(),否则所有的输入都来源于这个文件。
在输出方面,io.output也可以完成类似的工作,如果出现错误,这两个函数都会引发 raise 错误(必须用完整模型中的io.open来直接处理这些错误) - io.read() & io.write()
io.read() 从当前输入文件中读取字符串,它的参数决定了要读取的数据:
- *all 读取整个文件
- *line 读取下一行
- *number 读取一个数字
- (num) 读取一个不超过(num)个字符的字符串
io.write() 接受任意数量的字符串参数,并将它们写入当前输出文件,可以用string.format来辅助。
完整I/O模型
简单的I/O模型功能太受限,所以用的更多的还是完整的I/O模型。
完整I/O模型可以进行更多的I/O控制,它是基于文件句柄的,就好比C语言中的FILE*,表示一个正在操作的文件。
- io.open()
打开文件所需的函数,它有两个参数,一个表示要打开的文件名,一个表示操作的模式(以什么方式打开)
标识 | 模式 |
---|---|
“r” | 只读模式,只能对文件读取 |
“w” | 写入模式,对文件进行写入,但会覆盖文件原来的内容 |
“a” | 追加模式,对文件进行写入操作,会在原文件基础上,进行追加写入 |
“b” | 打开二进制文件,一般和前三种混合使用,比如”rb”,”wb” |
open函数会返回表示文件的一个句柄;如果发生错误,就返回nil,一条错误消息和一个错误代码。
- read & write
打开一个文件后,就可以用read/write方法读写文件了,这与之前的read/write函数 相似,但是需要冒号语法,将它们作为句柄方法来调用
local file = io.open("input.txt", r)
if file then
local all = file:read("*all")
...
end
这里的 file:read(“*all”) 也可以用 file.read(file, “*all”) 替代
简单模式 与 完整模式 的混合使用
尤其是在某个文件中,要操作另一个文件的时候,
用io.input可以获取当前输入文件的句柄,然后,可以再次input其他文件,进行操作,然后用 之前获取的句柄,就可以恢复当之前的状态。
local preFile = io.input()
io.input("input2.txt")
...
io.input():close()
io.input(preFile)
5.操作系统库
操作系统库定义在 table os中,包括了 文件库、之前设置伪随机种子中用到的获取时间、日期的函数 及其他功能函数。
文件库
lua为了保证可移植性,它的文件库非常简单,只有两个函数
-- 改名
os.rename()
-- 删除
os.remove()
日期&时间
- 时间 os.time()
如果不带任何参数调用time函数,它就会以数字形式返回当前的日期和时间。
如果参数是一个table,它会返回一个数字,表示该table种所描述的日期和时间,
这个参数table具有以下有效字段:
字段名 | 数值范围 |
---|---|
year | 一个完整的年份 |
month | 01-12 |
day | 01-31 |
hour | 00-23 |
min | 00-59 |
sec | 00-59 |
isdst | 布尔值,true表示夏令时 |
其中,前三个字段(year、month、day)是必须有的,其他字段默认为 中午(12:00:00)
- 日期 os.date()
返回一个包含日期及时刻的字符串或表。格式化的方法取决于所给字符串format。
第一个参数是格式字符串,指定了期望的表示形式;第二个参数是日期和时间的数字,默认为当前日期和时间。
第一个参数的种类非常繁多,但最常用的还是 “*t”,返回有后续域的表:year(四位数字),
month,
day,
hour,
min,
sec,
wday(星期几),
yday(当年第几天),
isdst(夏令时标记),
其他的系统调用
- os.exit()
如其名,就是来终止当前程序的执行 - os.getenv()
获取一个环境变量的值,并接受一个变量名,返回对应的字符串值 - os.execute()
运行一条系统命令,等价于C语言中的system函数。(它需要接受一个命令字符串,并返回一个错误代码) - os.setlocale
设置当前lua程序所使用的区域。区域定义了不同文化或着不同语言间的差异之处
6.调试库
这里只是简单的介绍下,不深究 = =。
调试库并没有提供给一个lua的调试器,而是提供了一个编写调试器所必须具有的原语。
考虑到性能等因素,这些原语的标准接口是通过C API给出的,lua中的调试库提供访问的接口。
我们应小心使用调试库,因为它的功能性不高,而且会打破一些语言的固有原则。
PS:栈层,是关于调试库的一个重要的概念。它是一个数字,表示某一时刻某个活动的函数,即一个已经被调用但是没有返回的函数。
调用调试库的函数是层1,调用这个函数的函数是层2,以此类推。
自省机制
自省函数允许检查一个正在运行中程序的各个方面,例如 活动函数栈、当前执行的行、局部变量的名称和值。
- debug.getinfo()
主要的自省函数,主要用来返回关于一个函数信息的表。
参数上,可以直接提供函数,也可以用数字,数字代表运行在指定线程的调用栈对应层次上的函数(0表示当前函数即getinfo函数自身,1表示调用getinfo的函数)
返回的表,字段有以下几种:
字段名 | 意义 |
---|---|
source | 函数定义的位置 |
short_src | source的短版本,用于错误信息中 |
linedefined | 该函数定义在源代码中第一行的行号 |
lastlinedefined | 该函数定义在源代码中最后一行的行号 |
what | 函数的类型,如果普通的lua函数,则为”lua”;如果是一个C函数,则为”C”;如果是程序块的主程序部分,则为”main” |
name | 该函数的一个适当的名称 |
namewhat | name字段的含义,可能是 global、local、method、field 或 空字符串(代表没找到该函数的名称) |
nups | 函数的一个upvalue的数量 |
activelines | 一个table,包含了该函数的所有活动行的集合(活动行即有代码的行,相对于 空行、只包含注释的行 而言 |
func | 函数本身 |
当函数是一个C函数,返回的表中只有字段 what、name 和 namewhat
我们可能只需要看某个字段的东西,所以第二个参数就是一个字符串,根据含有不同的字符,来决定返回哪些字段
字符 | 返回的字段 |
---|---|
‘n’ | name & namewhat |
‘f’ | func |
‘S’ | source & short_src & what & linedefined & lastlinedefined |
‘l’ | currentline |
‘L’ | activelines |
‘u’ | nups |
- debug.getlocal()
此函数用来检查任意活动函数的局部变量,返回两个值:变量的名字 和 它的当前值。
第一个参数是 所查询的函数栈层,第二个参数是 变量的索引
function fun(a, b)
local x
do
local c = a - b
end
local a = 1
while true do
local name, value = debug.getlocal(1, a)
if not name then break end
print(name, value)
a = a + 1
end
end
fun(10, 20)
-- result
a 10
b 20
x nil
a 4
没有c,因为在调用getlocal的时候,c已经离开了作用域,并不是活跃的变量。
debug.getupvalue(f, up)
返回函数f的第up个上值的名字和值,如果没有,返回nil
该函数可以让用户访问一个lua函数所使用的 非局部变量 , 与局部变量不同的是,被一个函数所引用的 非局部变量 会一直存在,即使已经执行完毕。
所以,getupvalue第一个参数不是栈层,而是一个函数,更确切一些,应该是一个closure。
不单可以获取,还可以用 debug.setupvalue 来修改 非局部变量debug.traceback([thread,][message[, level]])
调试库中所有的自省函数都接受一个可选的协同程序参数作为第一个参数,这样就可以从外部来检查这个协同程序。
如果message有,且不是字符串或nil,函数不做任何处理直接返回message;否则它返回调用栈的栈回溯信息。
钩子
调试库中,钩子机制可以让用户注册一个钩子函数,这个函数会在程序运行中某个特定时间发生时调用。
有四种事件会触发一个钩子:
lua调用一个函数产生的call事件;
每当函数返回时产生的return事件;
每当lua开始执行一行新代码时产生的line事件;
每当执行完指定数量的指令后产生的count时间。
要注册一个钩子,需要使用两个或三个参数来调用 debug.sethook;
第一个参数是钩子函数;第二个参数是一个字符串,描述需要监控的事件;第三个参数是一个可选的数字,用于说明多久获得一次count事件。