Lua知识点九

来源:互联网 发布:本地系统 网络受限 编辑:程序博客网 时间:2024/06/04 00:54

Weak表

Lua自动进行内存的管理。程序只能创建对象(表,函数等),而没有执行删除对象的函数。通过使用垃圾收集技术,Lua会自动删除那些失效的对象。
Weak表是一种用来告诉Lua一个引用不应该防止对象被回收的机制。
表有keys和values,而这两者都可能包含任何类型的对象。
要注意,只有对象才可以从一个weak table中被收集。

记忆函数

记忆技术在保持一些类型对象的唯一性上同样有用。

关联对象属性

weak tables的另一个重要的应用就是和对象的属性关联。Lua本身使用这种技术来保存数组的大小。

重述带有默认值的表

如果默认值没有weak的keys,它就会将所有的带有默认值的tables设定为永久存在。

Table库

table库由一些操作table的辅助函数组成。

数组大小

Table库定义了两个函数操纵array的大小:getn,返回array的大小;setn,设置array的大小。如前面我们所见到的,这两个方法和table的一个属性相关:要么我们在table的一个域中保存这个属性,要么我们使用一个独立的(weak)table来关联table和这个属性。
默认的,setn和getn使用内部表存储表的大小。
使用setn和getn操纵array的大小是个好的习惯,即使你知道大小在域n中。table库中的所有函数(sort、concat、insert等等)都遵循这个习惯。
print(table.getn{10,2,4}) –> 3
print(table.getn{10,2,nil}) –> 2
print(table.getn{10,2,nil; n=3}) –> 3
print(table.getn{n=1000}) –> 1000

a = {}
print(table.getn(a)) –> 0
table.setn(a, 10000)
print(table.getn(a)) –> 10000

a = {n=10}
print(table.getn(a)) –> 10
table.setn(a, 10000)
print(table.getn(a)) –> 10000

插入/删除

table库提供了从一个list的任意位置插入和删除元素的函数。table.remove函数删除数组中指定位置的元素,并返回这个元素,所有后面的元素前移,并且数组的大小改变。
a = {}
for line in io.lines() do
table.insert(a, line)
end
print(table.getn(a)) –> (number of lines read)
使用这两个函数,很容易实现栈、队列和双端队列。

排序

另一个有用的函数是table.sort。他有两个参数:存放元素的array和排序函数。对于Lua来说,数组也是无序的。

String库

Lua解释器对字符串的支持很有限。一个程序可以创建字符串并连接字符串,但不能截取子串,检查字符串的大小,检测字符串的内容。
String库中的一些函数是非常简单的:string.len(s)返回字符串s的长度;string.rep(s, n)返回重复n次字符串s的串;
Lua中的字符串是恒定不变的。String.sub函数以及Lua中其他的字符串操作函数都不会改变字符串的值,而是返回一个新的字符串。
函数string.format在用来对字符串进行格式化的时候,特别是字符串输出,是功能强大的工具。
Lua中的字符串是恒定不变的。String.sub函数以及Lua中其他的字符串操作函数都不会改变字符串的值,而是返回一个新的字符串。
调用string.sub(s,i,j)函数截取字符串s的从第i个字符到第j个字符之间的串。
string.char函数和string.byte函数用来将字符在字符和数字之间转换。string.char获取0个或多个整数,将每一个数字转换成字符,然后返回一个所有这些字符连接起来的字符串。

模式匹配函数

在string库中功能最强大的函数是:string.find(字符串查找),string.gsub(全局字符串替换),and string.gfind(全局字符串查找)。
string.find的基本应用就是用来在目标串(subject string)内搜索匹配指定的模式的串。
函数如果找到匹配的串返回他的位置,否则返回nil.最简单的模式就是一个单词,仅仅匹配单词本身。比如,模式’hello’仅仅匹配目标串中的”hello”。当查找到模式的时候,函数返回两个值:匹配串开始索引和结束索引。
s = “hello world”
i, j = string.find(s, “hello”)
print(i, j) –> 1 5
print(string.sub(s, i, j)) –> hello
print(string.find(s, “world”)) –> 7 11
i, j = string.find(s, “l”)
print(i, j) –> 3 3
print(string.find(s, “lll”)) –> nil
string.find函数第三个参数是可选的:标示目标串中搜索的起始位置。
tring.gsub函数有三个参数:目标串,模式串,替换串。
string.gsub的第二个返回值表示他进行替换操作的次数。

模式

Lua支持的所有字符类:
. 任意字符
%a 字母
%c 控制字符
%d 数字
%l 小写字母
%p 标点字符
%s 空白符
%u 大写字母
%w 字母和数字
%x 十六进制数字
%z 代表0的字符

Lua中的模式修饰符有四个:
+ 匹配前一字符1次或多次
* 匹配前一字符0次或多次
- 匹配前一字符0次或多次
? 匹配前一字符0次或1次

Lua的字符类依赖于本地环境,所以 ‘[a-z]’ 可能与 ‘%l’ 表示的字符集不同。
对Lua而言,模式串就是普通的字符串。他们和其他的字符串没有区别,也不会受到特殊对待。只有他们被用作模式串用于函数的时候,’%’ 才作为转义字符。
‘%’ 用作特殊字符的转义字符,因此 ‘%.’ 匹配点;’%%’ 匹配字符 ‘%’。转义字符 ‘%’不仅可以用来转义特殊字符,还可以用于所有的非字母的字符。
‘+’,匹配一个或多个字符,总是进行最长的匹配。
常用的这种模式有:’%b()’ ,’%b[]’,’%b%{%}’ 和 ‘%b<>’。你也可以使用任何字符作为分隔符。

Capture

Capture是这样一种机制:可以使用模式串的一部分匹配目标串的一部分。
在string.find使用captures的时候,函数会返回捕获的值作为额外的结果。
‘%a+’ 表示非空的字母序列;’%s*’ 表示0个或多个空白。
整个模式代表:一个字母序列,后面是任意多个空白,然后是 ‘=’ 再后面是任意多个空白,然后是一个字母序列。两个字母序列都是使用圆括号括起来的子模式,当他们被匹配的时候,他们就会被捕获。

转换的技巧(Tricks of the Trade)

模式匹配对于字符串操纵来说是强大的工具,你可能只需要简单的调用string.gsub和find就可以完成复杂的操作
匹配空串的模式。

IO库

I/O库为文件操作提供两种模式。简单模式(simple model)拥有一个当前输入文件和一个当前输出文件,并且提供针对这些文件相关的操作。完全模式(complete model)使用外部的文件句柄来实现。

简单I/O模式

I/O库将当前输入文件作为标准输入(stdin),将当前输出文件作为标准输出(stdout)。
read函数从当前输入文件读取串,由它的参数控制读取的内容:
“*all” 读取整个文件
“*line” 读取下一行
“*number” 从串中转换出一个数值
num 读取num个字符到串
io.read(“*all”)函数从当前位置读取整个输入文件。
io.read(“*line”)函数返回当前输入文件的下一行(不包含最后的换行符)。

完全I/O 模式

为了对输入输出的更全面的控制,可以使用完全模式。完全模式的核心在于文件句柄(file handle)。模式字符串可以是 “r”(读模式),”w”(写模式,对数据进行覆盖),或者是 “a”(附加模式)。文件打开后就可以用read和write方法对他们进行读写操作。

I/O优化的一个小技巧

Lua中读取整个文件要比一行一行的读取一个文件快的多。尽管我们有时候针对较大的文件(几十,几百兆),不可能把一次把它们读取出来。

二进制文件

默认的简单模式总是以文本模式打开。在Lua中二进制文件的控制和文本类似。一个串可以包含任何字节值,库中几乎所有的函数都可以用来处理任意字节值。

关于文件的其它操作

函数tmpfile函数用来返回零时文件的句柄,并且其打开模式为read/write模式。该零时文件在程序执行完后会自动进行清除。函数flush用来应用针对文件的所有修改。

操作系统库

所以在系统库里并没有提供这些功能。另外有一些没有包含在主体发行版中的Lua库提供了操作系统扩展属性的访问。文件管理方面操作系统库就提供了os.rename函数(修改文件名)和os.remove函数(删除文件)。

Date和Time

time和date两个函数在Lua中实现所有的时钟查询功能。函数time在没有参数时返回当前时钟的数值。
year a full year
month 01-12
day 01-31
hour 01-31
min 00-59
sec 00-59
isdst a boolean, true if daylight saving

其它的系统调用

函数os.getenv得到“环境变量”的值。
os.execute函数较为强大,同时也更加倚赖于计算机系统。

Debug库

debug库并不给你一个可用的Lua调试器,而是给你提供一些为Lua写一个调试器的方便。出于性能方面的考虑,关于这方面官方的接口是通过C API实现的。Lua中的debug库就是一种在Lua代码中直接访问这些C函数的方法。Debug库在一个debug表内声明了他所有的函数。
debug库由两种函数组成:自省(introspective)函数和hooks。自省函数使得我们可以检查运行程序的某些方面,比如活动函数栈、当前执行代码的行号、本地变量的名和值。Hooks可以跟踪程序的执行情况。
Debug库中的一个重要的思想是栈级别(stack level)。一个栈级别就是一个指向在当前时刻正在活动的特殊函数的数字,也就是说,这个函数正在被调用但还没有返回。调用debug库的函数级别为1,调用他(他指调用debug库的函数)的函数级别为2,以此类推。

自省(Introspective)

在debug库中主要的自省函数是debug.getinfo。他的第一个参数可以是一个函数或者栈级别。对于函数foo调用debug.getinfo(foo),将返回关于这个函数信息的一个表。这个表有下列一些域:
1.source,标明函数被定义的地方。如果函数在一个字符串内被定义(通过loadstring),source就是那个字符串。如果函数在一个文件中定义,source是@加上文件名。
2.short_src,source的简短版本(最多60个字符),记录一些有用的错误信息。
3.linedefined,source中函数被定义之处的行号。
4.what,标明函数类型。如果foo是一个普通得Lua函数,结果为 “Lua”;如果是一个C函数,结果为 “C”;如果是一个Lua的主chunk,结果为 “main”。
5.name,函数的合理名称。
6.namewhat,上一个字段代表的含义。这个字段的取值可能为:W”global”、”local”、”method”、”field”,或者 “”(空字符串)。空字符串意味着Lua没有找到这个函数名。
7.nups,函数的upvalues的个数。
8.func,函数本身;详细情况看后面。
当foo是一个C函数的时候,Lua无法知道很多相关的信息,所以对这种函数,只有what、name、namewhat这几个域的值可用。

以数字 n调用debug.getinfo(n)时,返回在n级栈的活动函数的信息数据。比如,如果n=1,返回的是正在进行调用的那个函数的信息。(n=0表示C函数getinfo本身)如果n比栈中活动函数的个数大的话,debug.getinfo返回nil。当你使用数字n调用debug.getinfo查询活动函数的信息的时候,返回的结果table中有一个额外的域:currentline,即在那个时刻函数所在的行号。另外,func表示指定n级的活动函数。
字段名的写法有些技巧。记住:因为在Lua中函数是第一类值,所以一个函数可能有多个函数名。查找指定值的函数的时候,Lua会首先在全局变量中查找,如果没找到才会到调用这个函数的代码中看它是如何被调用的。后面这种情况只有在我们使用数字调用getinfo的时候才会起作用,也就是这个时候我们能够获取调用相关的详细信息。
函数getinfo 的效率并不高。Lua以不消弱程序执行的方式保存debug信息(Lua keeps debug information in a form that does not impair program execution),效率被放在第二位。为了获取比较好地执行性能,getinfo可选的第二个参数可以用来指定选取哪些信息。指定了这个参数之后,程序不会浪费时间去收集那些用户不关心的信息。这个参数的格式是一个字符串,每一个字母代表一种类型的信息,可用的字母的含义如下:
‘n’ selects fields name and namewhat
‘f’ selects field func
‘S’ selects fields source, short_src, what, and linedefined
‘l’ selects field currentline
‘u’ selects field nup

访问局部变量

调用debug库的getlocal函数可以访问任何活动状态的局部变量。这个函数由两个参数:将要查询的函数的栈级别和变量的索引。函数有两个返回值:变量名和变量当前值。如果指定的变量的索引大于活动变量个数,getlocal返回nil。如果指定的栈级别无效,函数会抛出错误。(

访问Upvalues

和局部变量不同的是,即使函数不在活动状态他依然有upvalues(这也就是闭包的意义所在)。所以,getupvalue的第一个参数不是栈级别而是一个函数(精确的说应该是一个闭包),第二个参数是upvalue的索引。

Hooks

debug库的hook是这样一种机制:注册一个函数,用来在程序运行中某一事件到达时被调用。有四种可以触发一个hook的事件:当Lua调用一个函数的时候call事件发生;每次函数返回的时候,return事件发生;Lua开始执行代码的新行时候,line事件发生;运行指定数目的指令之后,count事件发生。Lua使用单个参数调用hooks,参数为一个描述产生调用的事件:”call”、”return”、”line” 或 “count”。另外,对于line事件,还可以传递第二个参数:新行号。

Profiles

尽管debug库名字上看来是一个调式库,除了用于调式以外,还可以用于完成其他任务。这种常见的任务就是profiling。对于一个实时的profile来说(For a profile with timing),最好使用C接口来完成:对于每一个hook过多的Lua调用代价太大并且通常会导致测量的结果不准确。然而,对于计数的profiles而言,Lua代码可以很好的胜任。下面这部分我们将实现一个简单的profiler:列出在程序运行过程中,每一个函数被调用的次数。

我们可以访问函数名数据,但是记住:在函数在活动状态的情况下,可以得到比较好的结果,因为那时候Lua会察看正在运行的函数的代码来查找指定的函数名。

原创粉丝点击