关键词不能为空

当前您在: 主页 > 英语 >

LUA参考手册

作者:高考题库网
来源:https://www.bjmy2z.cn/gaokao
2021-02-28 01:02
tags:

-

2021年2月28日发(作者:明媚的阳光)


LTE


初始随机接入过程详解



Lua


参考手册



Wikipedia


,自由的百科全书



Lua 5.0


参考手册



作者:


Roberto Ierusalimschy, Luiz Henrique de Figueiredo, Waldemar Celes


Copyright ? 2003 Tecgraf, PUC


-Rio. All rights reserved.


主要译者、维护人员:曹力丁舜佳


[NirvanaStudio


(



)]


目录


[


隐藏


]


1 1 -


绪论



2 2 -


语言



2.1 2.1 -


词法约定



2.2 2.2 -


值和类型



2.2.1 2.2.1 -


类型转换



2.3 2.3 -


变量



2.4 2.4 -


语句



2.4.1 2.4.1 -


语句段



2.4.2 2.4.2 -


语句块



2.4.3 2.4.3 -


赋值



2.4.4 2.4.4 -


控制结构



2.4.5 2.4.5 - For


语句



2.4.6 2.4.6 -


语句式函数调用



2.4.7 2.4.7 -


局部变量声明



2.5 2.5 -


表达式



2.5.1 2.5.1 -


算术运算符



2.5.2 2.5.2 -


关系运算符



2.5.3 2.5.3 -


逻辑运算符



2.5.4 2.5.4 -


串联接



2.5.5 2.5.5 -


优先级



2.5.6 2.5.6 -


表构造器



2.5.7 2.5.7 -


函数调用



2.5.8 2.5.8 -


函数定义



2.6 2.6 -


可见性规则



2.7 2.7 -


错误处理



2.8 2.8 -


元表



2.9 2.9 -


垃圾收集



2.9.1 2.9.1 -


垃圾收集元方法



2.9.2 2.9.2 -


弱表



2.10 2.10 -


同步程序



页脚内容


1



LTE


初始随机接入过程详解



3 3 -


应用程序接口



3.1 3.2 -


堆栈和索引



3.2 3.3 -


堆栈操作



3.3 3.4 -


堆栈查询



3.4 3.5 -


堆栈取值



3.5 3.6 -


将值压入堆栈



3.6 3.7 -


控制垃圾收集



3.7 3.8 -


用户数据类型



3.8 3.9 -


元表



3.9 3.10 -


加载


Lua< /p>


语句段



3.10 3.11 -


表操作



3.11 3.13 -


将表作为数组使用



3.12 3.14 -


调用函数



3.13 3.15 -


受保护调用



3.14 3.16 -


定义


C


函数



3.15 3.17 -


定义


C


函数闭包



3.16 3.18 -


注册表



3.17 3.19 - C


中的错误处理



3.18 3.20 -


线程



4 4 -


调试接口



4.1 4.1 -


堆栈及函数信息



4.2 4.2 -


操作局部变量和上值



4.3 4.3 -


钩子



5 5 -


标准库



5.1 5.1 -


基本函数



5.2 5.2 - Coroutine Mani***tion


5.3 5.3 - String Mani***tion


5.3.1 (s [, i])


5.3.2 (i1, i2, ...)


5.3.3 (function)


5.3.4 (s, pattern [, init [, plain]])


5.3.5 (s)


5.3.6 (s)


5.3.7 (s, n)


5.3.8 (s, i [, j])


5.3.9 (s)


5.4 5.4 - Table Mani***tion


5.5 5.5 - Mathematical Functions


页脚内容


2



LTE


初始随机接入过程详解



5.6 5.6 - Input and Output Facilities


5.7 5.7 - Operating System Facilities


5.8 5.8 - The Reflexive Debug Interface


6 6 - Lua


独立程序



7


致谢



8


与以前版本的不兼容性



8.1


语言上的变动



8.2


库的变更



8.3 API


上的改动



9 Lua


完整语法参考



1 -


绪论



Lua


是一种为支持有数据描述机制的一般过程式编程语言而设计的扩展编程语言。


它同样可< /p>


以对面向对象语言、函数式程序设计(


Functional P rogramming


,如


Lisp


) 以及数据驱动编程



data-driven program ming


)提供很好的支持。它的目标是被用作一种强大的、轻型的配置语


言。


Lua


目前已经被实现为一个扩展库,是用


clean C



ANSI C/C++


的一个通用子集)编写的。


作为一个扩展语言,


Lua


没有


< /p>


函数的概念:它仅仅是嵌入一个宿主程序进行工作,可


以称之为嵌 入式编程



或者简单的说是宿主编程。这个宿主程序可以调用函 数来执行


Lua


的代码


片断,可以设置 和读取


Lua


的变量,可以注册


C


函数让


Lua


代码调用。

< br>Lua


的能力可以扩展到更


大范围,在不同的领域内,这 样就在同样的语法框架下创建了你自定义的编程语言。



Lu a


的发行版包括一个独立的嵌入式程序,


lua


,他使用


Lua


的扩展库来提供一个完全的

< p>
Lu


a


解释器。



Lua


是自由软件,通常不提供任何担保,如它的版权说明中叙述的 那样。手册中描述的实现



Lua


的官 方网站可以找到,





如果需要知道


Lua


设计背后的一些决定和讨论,< /p>


可以参考以下论文,


它们都可以在


Lua


的网


站上找到。




R. Ierusalimschy, L. H. de Figueiredo, and W. Celes. Lua-


--an extensible extension language. Software: Practice & Experience


26 #6 (1996) 635-652.




L. H. de Figueiredo, R. Ierusalimschy, and W. Celes. The


design and implementation of a language for extending applications.


Proceedings of XXI Brazilian Seminar on Software and Hardware (199


4) 273-283.




L. H. de Figueiredo, R. Ierusalimschy, and W. Celes. Lua:


an extensible embedded language. Dr. Dobb's Journal 21 #12 (Dec 19


96) 26-33.



页脚内容


3



LTE


初始随机接入过程详解




R. Ierusalimschy, L. H. de Figueiredo, and W. Celes. The


evolution of an extension language: a history of Lua, Proceedings o


f V Brazilian Symposium on Programming Languages (2001) B-14-B-28.



Lua


在葡萄牙语中的意思是“月亮 ”,发音是


LOO-ah




2 -


语言



这一章将描述


Lua


的词法、语法和语义结构。


换句话说,


这一章会讲什么标记是合法的,他


们是如 何组合的,以及他们的组合是什么含义。



语言结构会使用常 用的扩展


BNF


范式来解释,如


{a}


表示


0


或多个


a




表示


a


是可选的



0


个或


1


个)。非终端字体(不能显示的)用斜体表示,关键字是 粗体,其他终端符号用


typ


ewriter

< br>(等宽)字体,并用单引号引出。



2.1 -


词法约定



Lua

中的标识符(


Identifiers


)可以是任意的数字 、字符和下划线“


_


”,但不能以数字开


头。


这条规则符合大多数编程语言中的标识符的定义。


(字符 的具体定义要根据系统的地区设置:


任何区域设置可以认同的字母表中的字母都可以用在 标识符中。)



下面的关键字(


ke ywords


)为保留关键字不可以作为标识符出现:



and break do else elseif


end false for function if


in local nil not or


repeat return then true until while


Lua


对大小写敏感:


and


是一个保留字,但是


And



AND


是两个不一样的、但都合 法的标


识符。习惯上来说,以下划线开始且后面跟着大写字母的标识符

< br> (


例如


_VERSION)


是为


Lua



部变量所保留的。



下面的字符(串)是其他的一些标记:



+ - * / ^ =


~= <= >= < > ==


( ) { } [ ]



;


: , . .. ...


字符串



Literal strings




以单引号或者双引 号定界,


同时可以包含以下


C


语言风格 的转


义字符:








a ---


铃声(


bell





b --- < /p>


回退(


backspace


< p>



f --- form feed



n ---


新行(


newline





r ---


回车(


carriage return






t ---


水平制表符(


horizontal tab






v ---


垂直制表符(


vertical tab





页脚内容


4



LTE


初始随机接入过程详解








---


反斜杠(


backslash






双引号(


quotation mark





' ---


单引号(


apostro phe





[ ---


左方括号(


left square bracket





] ---


右方括号(


right square bracket





另外,一个


`newline


?



(一个反斜杠加 上一个真正的换行符)


会导致字符串内的分行。字


符串中的字符 也可以使用转义字符


`ddd


?通过数字值来指定。

< p>
ddd


是最多为


3


个十 进制数字的序


列。


Lua


中的字符串也 可以包含


8


进制数字,包括嵌入零,它可以表示为


`0


?。



字符串也可以用双方括号来定界


[[


·



·



·


]]


。这种括号方式的语法,字符 串可以跨越多


行,也可以包含嵌套的,同时不会转义任何序列。方便起见,当开始的


`[[


?



后面 紧跟着一个换


行符的话,这个换行符不会包括在字符串内。


举个 例子:在一个使用


ASCII


编码


(其 中


`a


?



的编


码是


97


,换行符是


10


,字符


`1


?




49


)的系统中,以下四种格式得到的都是同一个字符串:



1.



2.



3.



4.



4.



5.



6.




'97lo1004923


[[alo


123


[[


alo


123


数值常量



Numer ical constants




可 以有一个可选的底数部分和一个可选的指数部分。



下是有效的 数值常量:



3 3.0 3.1416 314.16e-2 0.31416E1


注释(


Comme nts


)可以在任何地方出现,必须在最前面加上双减号


(--)


。如果紧接着


--


的文本不是


[[


,那么会认为是一个



短注释(


short comment


),这一行往后到行尾都是注释。


否则,


会认为是一个



常注释



long comment




注释直到相应的< /p>


]]


结束。


长注释可以跨越多行,


同时可以包含嵌套的


[[


·



·



·


]]


括号对。



为了方便起见,


文件的第一行如果是以


#


开始,


这个机制允许


Lua



Uni x


系统中用做一个脚


本解释器(见


6


)。



2.2 -


值和类型



Lua

是一种动态类型语言(


dynamically typed language< /p>


)。这意味着变量是没有类型的;


只有值才有。语言中没有类型定 义。所有的值都包含他自身的类型。



Lua


中有八种基本类型:


nil, boolean, number, string, function, userdata, thread



table



Nil


空类型只对应


nil


值,他的属性和 其他任何值都有区别;通常它代表没有有效


的值。


Boolean


布尔类型有两种不同的值


false and true


。在


Lua


中,


nil and false


代表


成假 条件;其他任何值都代表成真条件。


Number


数字类型 表示实数(双精度浮点数)。


(构建


Lua

解释器时也可以很容易地用其他内部的表示方式表示数字,如单精度浮点数或者长整型)。

< br> String


字符串类型表示一个字符的序列。


Lua

字符串可以包含


8


位字符,


包括嵌 入的


('0')


(见


2.1


)。



页脚内容


5



LTE


初始随机接入过程详解



函数是


Lua


中的第一类值(


first-class values


)。


也就是说 函数可以保存在变量中,


当作


参数传递给其他函数,或者被当作 结果返回。


Lua


可以调用(和处理)


Lua


写的函数和


C


写的函

< p>



(见


2.5.7


)。



用户数据类型



userdata


提供了让任意


C


数据储存在


Lua


变量中的功能。


这种类型直接对


应着一 块内存,


Lua


中也没有任何预先定义的操作,除了赋值和一致 性比较。然而,通过使用元


表(


metatables


),程序员可以定义处理


userdata


的操 作。(见


2.8


)。


Userdata


值不能在


Lua


中 建立或者修改,只能通过


C API


。这保证了宿主程序的数据完整性。



线程(


thread


)类型代表了相互独立的执行线程 ,用来实现同步程序。



表(


tab le


)类型实现了联合数组,也就是说,数组不仅可以使用数字,还能使用其他的值


(除了


nil


)。



而且,


tables


可以是互异的(


heterogeneous


),他们可以保存任何类型的值< /p>


(除了


nil


)。


Tables



Lua


中唯一的数据结构机制;他们可以用来表示一般数组,特征表,


集合,记录,图,树等等。如果要表示记录,


Lua

< p>
使用字段名作为索引。语言支持



这种


比较优美的表示方式,还有


a[


。在


Lua


中有几种建立表的简便方法



(见


2.5.6


)。



就像索引一样,表字段的值也可以是任何类型(除了


nil< /p>


)。特别需要注意地是,由于函数


是第一型的值,表字段也可以包 含函数。这样表也可以支持



方法(


m ethods




(见


2.5.8


)。



表,函数,和用户 数据类型的值都是对象(


objects


):变量不会包含他们 的实际值,只是


一个他们的引用(


references


)。赋值,参数传递和函数返回只是操作这些值的引用,这些操作


不会 暗含任何拷贝。



库函数


type


返回一个字符串描述给出值所表示的类型



(见


5.1


)。



2.2.1 -


类型转换



Lua

提供运行时的数字和字符串值得自动转换。


任何对字符串的算术操作都会现尝试把字 符


串转换成数字,使用一般规则转换。反过来,当一个数值用在需要字符串的地方时,< /p>


数字会自动


转换成字符串,


遵循一种合理 的格式。


如果要指定数值如何转换成字符串,


请使用字符串库中 的


format


函数(见


5.3


)。



2.3 -


变量



变量是储存值的地方。


Lua


中有三种不同的变量:全局变量,局部变量和表字段。

< p>


一个名称可以表示全局变量或局部变量


(或者 一个函数的正式参数,


一种局部变量的特殊形


式):

< p>


var


::= Name


Lua


假设变量是全局变量,除非明确地用


loca l


进行声明(见


2.4.7


)。局部变量有



词义


范围(


lexically scoped


):局部变量可以被在它们范围内的函数自由访问



(见


2.6


)。



在变量第一次赋值之前,它的值是


nil




方括号用于对表进行检索:



var


::= prefixexp `[


?


exp `]


?



第一个表达式


(prefixexp)


结果必须是表;第二个表达式


(exp)


识别表中一个特定条目。


给出表的表达式有一个限 制语法;详细见


2.5





语法是


var[


的较好形式:



var


::= prefixexp `.


?


Name


页脚内容


6



LTE


初始随机接入过程详解




访问全局变量和表字段的实质可以通过元表进行改变。


对索引变量


t



的访问等同于调用


gettable_event(t,i)


。(关于


gettable_event


的完整描述见


2.8


。这个函数并没有在


Lua


中定义,也无法调 用。我们在这里仅仅用来解释原理)。



所有的全局变量存在 一个普通的


Lua


表中,称之为环境变量表(

< br>environment tables





简称



环境 (


environments


)。由


C


写的并导入到


Lua


中的函数


(C


函数


)


全部 共享一个通用全


局环境(


global environmen t


)。


Lua


写的每个函数

< p>
(a Lua


函数


)


都有一个它自己的环境的引


用,


这样这个函数中的所有的全局 变量都会指向这个环境变量表。


当新创建一个函数时,


它会继< /p>


承创建它的函数的环境。要改变或者获得


Lua

< br>函数的环境表,可以调用


setfenv or getfenv


(见


5.1


)。



访问全局变量


x


等同于


_env.x


,又等同于



gettable_event(_env,


_env


是运行的函数的环境。



_env < /p>


变量并没有在


Lua


中定义。

< p>
我们这里仅仅用来解释原理)




2.4 -


语句


< br>Lua


支持一种很通俗的语句集,和


Pascal


或者


C


中的很相似。他包括赋值,控制结构, 过


程调用,表构造和变量声明。




2.4.1 -


语句段


< p>
Lua


执行的最小单元称之为一个段(


chunk


)。一段语句就是简单的语句的序列,以顺序执


行。每一个语句 后面都可以加上一个分号(可选):



chunk


::= {stat [`;


?


]}


Lua


将语句段作为一个匿名函数(见


2.5.8




的本体进行处理。这样 ,语句段可以定义局


部变量或者返回值。


< br>一段语句可以储存在文件内或者宿主程序的一个字符串中。


当语句段被执行时,< /p>


他首先被预


编译成虚拟机使用的字节码,然后虚拟机用一个解释器 执行被编译的代码。



语句段也可以被预编译为二进制代码;


详情参看


luac


程序。


源代码和编译形态可以互相转


换;


Lua


自动监测文件类型然后作相应操作。




2.4.2 -


语句块


< p>
一个语句块是一系列语句;从语句构成上来看,语句块等同于语句段:



block


::= chunk


一个语句块可以明确定界来替换单个语句:



stat


::= do block end


显式语句块可以很好地控制变量的声明范围。


显示语句块有时也常会在另一个 语句块的中间


添加


return



break


语句



(见


2.4.4


)。



页脚内容


7



LTE


初始随机接入过程详解




2.4.3 -


赋值



Lua


允许多重赋值。因此,赋值的语法定义为:等号左边是一个变量表,右边是一个表达式


表 。两边的表中的元素都用逗号分隔开来:



stat


::= varlist1 `=


?


explist1


varlist1


::= var {`,


?


var}


explist1


::= exp {`,


?


exp}


我们将在


2.5


讨论表达式。



在赋值之前,值的表长度会被调整



为 和变量的表一样。如果值比需要的多,多出的值就会


被扔掉。如果值的数量不够,就会用 足够多的


nil


来填充表直到满足数量要求。如果表达式表


以一个函数调用结束,


那么在赋值之前,


函数返回的所有的值都会添加到值的表中


(除非把函数


调用放 在括号里面;见


2.5


)。



赋值语句首先计算出所有的表达式,然后才会执行赋值,所以代码:



i = 3


i, a


= i+1, 20



中的


i

< br>在赋值为


4


之前是等于


3


。同样


设置


a[3]



20


,但不影响


a[4]


。因为在


a


的,下面这行:



x, y = y, x


可以交换


x



y


的值。



对全局变量和表字段的赋值 可以看作是通过元表进行的。对一个索引变量的赋值


t


= v


al


等同于


settable_event(t,i,val)





settable_event


详细介绍参 看


2.8



Lua


中并未定


义该函数,他也无法直接调用。我们这里只是用它来进行解释。)



对全局变量的赋值


x = val


等同于赋值语句


_env.x = val


,像前面也等同于:



settable_event(_env,


_env


是运行函数的环境。(


_env


变量 并未在


Lua


中定义。我们这里只是用来进行解释。)




2.4.4 -


控制结构



控制结构


if, while



repeat


具有通用的含义和类似的语法:



stat


::= while exp do block end stat


::= repeat block until exp stat


::= if


exp then block {elseif exp then block} [else block] end


Lua


也有


for


语句,有两种格式



(见


2.4.5


)。



控制结构的条件表达式


exp


可以返回任意值。


false



nil


都表示假。


所有其他的值都认


为是真(特别要说明的:数字


0< /p>


和空字符串也表示真)。



语句


return


用来从函数或者 是语句段中返回一个值。


函数和语句段都可以返回多个值,


所< /p>



return


语句的语法为:



stat


::= return [explist1]


页脚内容


8



LTE


初始随机接入过程详解



break


语句可以用来终止


while, repeat


或者


for


循环的执行,直接跳到 循环后面的语


句。



stat


::= break


break


结束最里面的一个循环。



由于语法的原因,


return



break


语句只能作为语句块的



最后一个



语句。如果确实需


要在语句块的中间使用


return


或者


break


< br>需要使用一个显示语句块:


`do return end


?





`do break end


?,


这样现在


return



break


就成为他们


(内部)


语句块中的最后一个语句了。


实际上, 这两种用法一般只用在调试中。




2.4.5 - For


语句



for


语句有两种形式:数值形式和一般形式。



数值形式的


for


循环根据一个控制变量用算术过程重复一语句块。语法如下:



stat


::= for Name `=


?


exp `,


?


exp [`,


?


exp] do block end


block


语句块根据


name


以第一个


exp


的值开始,


直到他以第三个


exp


为步长达到了第二



exp


。一个这样的


for


语句:



for var = e1, e2, e3 do block end


等价于一下代码:



do


local var, _limit, _step = tonumber(e1), tonumber(e2), tonumber(e3)


if not (var and _limit and _step) then error() end


while (_step>0 and var<=_limit) or (_step<=0 and var>=_limit) do


block


var = var + _step


end


end


注意:




三种控制表达式只会被计算一次,在循环开始之前。他们的结果必


须是数值。

< p>




_limit



_step


是不可见的变量。这里只是为了进行解释。





如果你在程序块内给


var


赋值,结果行为将会不确定。







如果没 有给出第三个表达式(步长),那么默认为


1





你可以使用


break


来退出


for


循环。




循环变量


var


是局部变量;你不可以在


for


循 环结束之后继续使


用。如果你需要使用这个值,请在退出循环之前把它们传给其他变量。




for


的语句的一般形式是操作于函数之上的,称之为迭代器(


iterators

< p>
)。每一个迭代过


程,它调用迭代函数来产生新的值,直到新的值是


nil


。一般形式


for


循环有如下语法:



stat


::= for Name {`,


?


Name} in explist1 do block end


一个这样的


for


语句



页脚内容


9



LTE


初始随机接入过程详解



for var_1, ..., var_n in explist do block end


等同于以下代码:



do


local _f, _s, var_1 = explist


local var_2, ... , var_n


while true do


var_1, ..., var_n = _f(_s, var_1)


if var_1 == nil then break end


block


end


end


注意




explist


只会计算一次。他的结果是一个



迭代



函数,一个



状态,


和给第一个



迭代变量的一个初始值。








_f



_s


是不可见的变量。这里只是用来进行解释说明。




如果你在语句块中给


var_1


赋值,那么行为就会变得不确定。




你可以使用


break


来退出


for


循环。




循环变量


var_i


是局部变量;你不可以在


for


循 环结束之后继续


使用。如果你需要使用这个值,请在退出循环之前把它们传给其他变量。





2.4.6 -


语句式函数调用



如果要忽略可能的影响,函数调用可以按照语句执行:



stat


::= functioncall

< p>
I


在这里,所有的返回值都会被忽略。函数调用将在


2.5.7


详细解释。



2.4.7 -


局部变量声明



局部变量可以在语句块中任何地方声明。声明时也可以添加一个初始赋值:

< br>


stat


::= local namelist [`=


?


explist1] namelist


::= Name {`,


?


Name}


如果出现初始赋值,他的语法和多重赋值语句一样(见


2.4 .3


)。否则,所有的变量都会初


始化为


nil




一个语句段也是一个语句块


(见


2. 4.1




所以语句段之内的任何显式 语句块之外也可以声


明局部变量。这种局部变量在语句段结束就会销毁。



局部变量的可见规则会在


2.6


解释。



2.5 -


表达式



Lua


中有以下几种基本表达式:



exp


::= prefixexp exp


::= nil | false | true exp


::= Number exp


::= Literal


exp


::= function exp


::= tableconstructor prefixexp


::= var | functioncall | `


(


?


exp `)


?



页脚内容


10



LTE


初始随机接入过程详解



数字和字符串已经在


2.1


中解释;变量在


2.3


中解释;函数定义在


2.5.8


;函数调用在



2.5.7


;表构造器在


2.5.6




一个用括号括起的表 达式只会返回一个值。这样,


(f(x,y,z))


将只会返回单一的一个值,


即使


f


可以返回多个值,



(f(x,y,z ))


的值将是


f


返回的第一个值或者如果


f


没有返回任何


值就是


nil


)。



表达式也可以使用各种算术运 算符,关系运算符和逻辑运算符,下面几节就会讲到。



2.5.1 -


算术运算符



Lua


支持常见的几种运算符:二元


+


(加),


-


(减),


*


(乘),


/


(除),



以及



^


(指数运算);



一元


-


(负号)。如果操作数是数 字,或者是可以转换成数字的字符串(见


2.2.1


),那么 所有的操作都和算术意义上的运算一致(除了指数)。指数运算其实是调用一个


全局函数


__pow


,否则一个合适的元方法将会被调用(见


2.8


)。标准数学库定义了函数


__po

< br>w


,给出了指数运算的定义(见


5.5


)。



.5.2 -


关系运算符




Lua


中的关系运算符有



== ~= < > <= >=


这些运算只会产生


false



true


值。



等于


(==)


先比较操作数的类型 。如果类型不一样,


结果便是


false


否则,


再比较操作数


的值。对象 (表,用户数据,线程,和函数)是按照引用进行比较:只有两个对象是同一个对象


的时 候,才认为是相等。每次你创建一个新的对象(表,用户数据,或者是函数)。这个新的对


象将不同于前面存在的任何对象。



你可以用



元方法改变


Lua


比较表的 方式(见


2.8


)。



2.2.1


的转换规则



不适用



于相等比较。这样,



结果是


false


,同样


t[0]




t[


给出的是表中不同的字段。



而操作符


~=


是等于


(==)


的相反的操作。



T


操作符的执行顺序如下。如果两个参数都是数字,那么它们就直接进行比较。如果,两个


参数都是字符串,那么它们的值会根据当前的区域设置进行比较。否则,


Lua


尝试调用



或者



元方法(见


2.8


)。



[


编辑


]


2.5.3 -


逻辑运算符



Lua


中的逻辑运算符是:



and or not


和控制结构一样(见


2.4.4


),所有的逻辑操作符认为


false



nil


都是假,


其他的值都


是真。



not


操作符总是返回


false



true




合取运算


and


如果第一个参数是


false


或者


nil


则返回第一个参数;


否则


and < /p>


返回第二


个参数。


析取运算


or


如果第一个参数不是


nil



false


则返回第一个参数,


否则


or


返回第


页脚内容


11



LTE


初始随机接入过程详解



二个参数。


and



or


都使用截取计算,也就是, 只有有必要的情况下才计算第二个参数。例


如:



10 or error() -> 10


nil or


nil and 10 -> nil


false and error() -> false


false and nil -> false


false or nil -> nil


10 and 20 -> 20


[


编辑


]


2.5.4 -


串联接


< p>


Lua


中字符串连接操作符是两个点

< p>
(`..


?


)


。如果两 边的操作数都是字符或者数字,他们


就都会按照


2.2.1


的规则被转换成字符串。否则,将调用



元方法(见


2.8


)。



[


编辑


]


2.5.5 -


优先级


< p>
Lua


中的操作符的优先级如下表所示,从低到高优先级:



or


and


< > <= >= ~= ==


..


+ -


* /


not - (unary)


^


表达式中,你可以使用括号来改变优先顺序。串联接符


(`..


?


)


和指数符


(`^


?


)


都是右结


合的。其他二元操作都是左结合的。



[


编辑


]


2.5.6 -


表构造器



表构造器是创建表的表达式。


当计算构造器的时候,

就会创建一个新的表。


构造器可以用来


创建空的表,或者创 建表并初始化一些字段。一般的语法如下:



tableconstructor


::= `{


?


[fieldlist] `}


?


fieldlist


::= field {fieldsep field}


[fieldsep] field


::= `[


?


exp `]


?


`=


?


exp | Name `=


?


exp | exp fieldsep


::= `,


?


|


`;


?



[exp1] = exp2


形式的每一个添加到新表中的字段条目以


exp1


为键并以


exp2


为值。


nam


e = exp


形式的字段,等同于


[



最后,


exp


形式的字段等同于



i


是连续的整数,从


1


开始。其它格式的字段不会影响 它的计数。例如:



a = {[f(1)] = g;


等同于:



页脚内容


= exp


其中



12



LTE


初始随机接入过程详解



do


local temp = {}


temp[f(1)] = g


temp[1] =


temp[2] =


temp.x = 1 -- temp[


temp[3] = f(x) -- 3rd exp


temp[30] = 23


temp[4] = 45 -- 4th exp


a = temp


end


如果列表中最后一个字段的形式是


exp

< br>同时表达式又是一个函数调用,那么调用返回的所


有值会依次进入列表

< p>
(见


2.5.7



。< /p>


如果要避免这种情况,


在函数调用两边加上括号

< br>(见


2.5





字段列表可以有一个结尾的分隔符,这个对由机器生成的列表十分方便。



[


编辑


]


2.5.7 -


函数调用



Lua


中的一个函数调用有如下语法:



functioncall


::= prefixexp args


在函数调用中,


首先会计算


prefixexp



args



如果


prefixexp


的值是


function


类型,< /p>


那么那个函数就会被调用,同时使用给出的参数。否则,


他的



元方法就会被调用,


第一


个参数是


prefixexp


的值,接下来是原来的调用参数(见


2.8


)。



形式



functioncall


::= prefixexp `:


?


Name args


可以用来调用“方法”




)。调用


v:name(...)


语法上比


(v,...)


,要好


一些,除非表达式


v


只计算一次。



参数可以有以下几种语法:



args


::= `(


?


[explist1] `)


?


args


::= tableconstructor args


::= Literal


所有的参数表达式都会在实际调用之前进行计算。


f{...}


的调用形式在语法上较


f({...})



要好,是因为,参数列表示一个单独的新表。


f'...'


(或者


f


或者


f...





f('...


')


要好,是因为参数列表是一个单独的字符串。



因为函数可以返回任意个结果(见


2.4.4


),


结果的数量必须在使用它们前进行调整。


如果


函数按照语句进行调用(见


2.4.6


) ,


那么它的返回列表就会被调整为零个元素,这样就舍弃了


所有 的返回值。如果调用函数时,他是一个表达式列表的最后一个元素,


那么不会做调整(除 非


调用时加了括号)。



以下是一些例子:



f() --


调整为


0


个结果



g(f(), x) -- f()


被调整成

< p>
1


个结果



页脚内容


13



LTE


初始随机接入过程详解



g(x, f()) -- g


获得


x


加上


f()


返回的所有值



a,b,c = f(), x -- f()

被调整成


1


个结果(此时


c


获得


nil


值)



a,b,c = x, f() -- f()


被调整为两个结果



a,b,c = f() -- f()


被调整为


3


个 结果



return f() --


返回所有


f()


返回的值



return x,y,f() --


建立一个表包含所有


f()


返回的值



{f()} -- creates a list with all values returned by f()


{f(), nil} -- f()


被调整为一个结果



如果你用括号括起 调用的函数,那么它就会被调整为返回一个值。



return x,y,(f()) -- returns x, y, and the first value from f()


{(f())} -- creates a table with exactly one element

作为


Lua


语法自由格式的一个例外,你不能在函数调用的


`(


?



前 面加入一个换行。这个限


制可以避免语言中的一些二义性。如果你写:

< br>


a = f


(g).x(a)


Lua


会读作


a = f(g).x (a)


。这样,如果你想执行为两条语句,你必须在中间加分号。如果

< br>你实际上想调用


f


,你就必须删除


(g)


前面的换行。



return functioncall


的调用格式称之为



尾部调用(


tail call


)。< /p>


Lua


实现了


proper ta


il calls


;在一个尾部调用中,被调用的函数将会 重新使用调用程序的栈。因此,程序执行对嵌


套尾部调用的次数没有任何限制。


然而,


尾部调用会清楚调用函数的调试信息。


注 意尾部调用只


有在特殊的语法中才能出现,


也就是


return


只有一个函数调用作为参数,


这种 语法保证了调用


函数确切返回被调用函数的返回值。所以,下面的例子都不是尾部调用:



return (f(x)) -- results adjusted to 1


return 2 * f(x)


return x, f(x) -- additional results


f(x); return -- results discarded


return x or f(x) -- results adjusted to 1


[


编辑


]


2.5.8 -


函数定义



函数定义的语法是:



function


::= function funcbody funcbody


::= `(


?


[parlist1] `)


?


block end


下面较好的语法简化了函数定义:



stat


::= function funcname funcbody stat


::= local function Name funcbody fun


cname


::= Name {`.


?


Name} [`:


?


Name]


语句



function f () ... end


会被翻译为



f = function () ... end


页脚内容


14



LTE


初始随机接入过程详解



语句



function t.a.b.c.f () ... end


会被翻译为



t.a.b.c.f = function () ... end


语句



local function f () ... end


会被翻译为



local f; f = function () ... end


一个函数定义是一个可执行的表达式,他的类型为函数(


function< /p>




。当


Lua


预编译语句


段的时候,他的函数体也会被预编译。这样,当


Lua


执行函数定义的时候,函数被



实例化



(封



closed


)。这个函数实例(或闭包


closure


)是表达式的最终结果。同一个函数的不同的实


例可以引用不同的外 部局部变量也可以有不同的环境表。



形式参数(代表参数的 变量,简称形参)就像用实际参数值(简称实参)初始化的局部变量


一样。



parlist1


::= namelist [`,


?


`...


?


] parlist1


::= `...


?



当调用一个函数时,实参表会调整为和形参一样的长度,除非函数是


variadic


或者



变长


参数函数(


vararg function


)。变长参数函数在其参数列表最后有三个点


(`...


?


)


。变长参数

< p>
函数不会对参数列表进行调整;而是,它把所有的额外实参放到一个隐含的形参

arg


中。


arg


的值是一个表,包含一个字段


`n


?



表示额外参数的个数,位置


1, 2, ..., n


是额外的参数。



请思考以下函数定义的例子:



function f(a, b) end


function g(a, b, ...) end


function r() return 1,2,3 end


然后,我们有以下实参到形参的对应关系:



CALL PARAMETERS


f(3) a=3, b=nil


f(3, 4) a=3, b=4


f(3, 4, 5) a=3, b=4


f(r(), 10) a=1, b=10


f(r()) a=1, b=2


g(3) a=3, b=nil, arg={n=0}


g(3, 4) a=3, b=4, arg={n=0}


g(3, 4, 5, 8) a=3, b=4, arg={5, 8; n=2}


g(5, r()) a=5, b=1, arg={2, 3; n=2}


结果使用


return


语句返回(见


2.4.4


)。如果控制到达了函数尾部而没有遇到


return


语句,那么函数没有返回值。



冒号(


:


)语法是用来定义


methods


的,也就是,函数有一个隐含的额外参数


self.


。这


样,语句:



页脚内容


15



LTE


初始随机接入过程详解



function t.a.b.c:f (...) ... end


相对以下是较好的形式:



t.a.b.c.f = function (self, ...) ... end


[


编辑


]


2.6 -


可见性规则


< p>
Lua


是一个有词法范围的语言。


变量的范围从声 明语句后的第一个语句开始到包含声明的最


内部的语句块为止。例如:

< br>


x = 10 -- global variable


do -- new block


local x = x -- new `x', with value 10


print(x) --> 10


x = x+1


do -- another block


local x = x+1 -- another `x'


print(x) --> 12


end


print(x) --> 11


end


print(x) --> 10 (the global one)


注意:


在类似


local x = x



正在声明的新的


x


尚未进入范围,


所以第二个


x


指代的是外


面的变量。


< p>
由于词法范围的规则,在局部变量的范围内定义的函数可以任意访问这些变量。例如:



local counter = 0


function inc (x)


counter = counter + x


return counter


end


内部函数使用的局部变量在函数内部称之为上值(


upvalue


),或者



外局部变量(


externa


l local variable


)。



注意每个


local


语句执行时会定义一个新的局部变量。看以下例子:



a = {}


local x = 20


for i=1,10 do


local y = 0


a


= function () y=y+1; return x+y end


end


页脚内容


16



LTE


初始随机接入过程详解



循环产生了十个闭包(也就是,十个匿名函数的实例)。每个闭包使用不同的

< br> y


变量,但


他们共享同一个


x


变量。



[


编辑


]


2.7 -


错误处理



因为


Lua


是一个扩展语言,


所有的


Lua


动作都是从宿主程序中调用

Lua


库中函数的


C


代码开


始的(见


3.15


)。无论错误发生在


Lua


编译过程时或执行时,控制返回


C< /p>


,然后可以做相应的


处理(比如打印一个错误)。



Lua


代码可以通过调用


error


函数来产生一个错误


(见


5.1




如果你要在


Lua


中捕获错误,


你可以使用


pcall


函数(见


5.1


)。



[


编辑


]


2.8 -


元表


< br>(


Metatables




Lua


中的每一个表和用户数据都可以拥有一个元表(


metatable


)。这个



元表是一个普通的


Lua


表,定义了在特定操作下原始 表和用户数据的行为。你可以通过设置一个对象的元表中的特


定字段来更改它某些方面的 行为。例如,当一个对象是一个加法的操作数时,


Lua


检查它 的元表


中的



字段是不是一个函数。如 果是,


Lua


调用它来执行加法。



我们称元表中的键(字段名,


key


) 为事件(


events




,值为



元方法(

metamethods


)。在


上一个例子中,

< p>


是事件,执行加法的函数是元方法。



你可以通过


set/getmetatable


函数来查询和更改一个对象的元表(见


5.1


)。



元表可以控制对象在算 术操作、比较、串连接、


索引取值中如何运行。


元表也可以定义 一个


函数当收集内存垃圾时调用。每一个操作这里


Lua


都用一个特定的键关联,称之为事件。当


Lua


对一个表或是一个用户数据执行上面中的一个操作时,它先检查元表控制的操作已经罗列在下

< br>面。


每个操作有一个相应的名称,


代表了他的含义。


他们在元表中的键是由名称前加上两条下划


线;如,操作

< p>


的键是



。这些操作的语义



这里给出的


Lua


代码仅仅是说明性的;


真正的行为是 硬编码在解释器中的,


比下面的的模拟


的效率要高很多。


描述中用到的函数


(rawget, tonumber,


等等


)



5.1


中会对他们进行描述。


特别地,要获得一个给定对象的 元方法,我们使用这个表达式:



metatable(obj)[event]


这个要读作:



rawget(metatable(obj) or {}, event)


也就是,


访问元方法时不会调用其它元方法,


同时调用没有元表的对象不会出错


(它返回一



nil


值)。





加法操作。




下面的


getbinhandler


函数定义了


Lua


如何给一个二元操作选择一个处理器。首先 ,


Lua


尝试第一个操作数。


如果它的 类型没有定义这个操作的处理器,


那么然后


Lua


尝试第二个操作数。



function getbinhandler (op1, op2, event)


页脚内容


17



LTE


初始随机接入过程详解



return metatable(op1)[event] or metatable(op2)[event]


end


利用该函数,


op1 + op2


的行为方式可看作是



function add_event (op1, op2)


local o1, o2 = tonumber(op1), tonumber(op2)


if o1 and o2 then -- both operands are numeric?


return o1 + o2 -- `+' here is the primitive `add'


else -- at least one of the operands is not numeric


local h = getbinhandler(op1, op2,


if h then


-- call the handler with both operands


return h(op1, op2)


else -- no handler available: default behavior


error(


end


end


end







操作。行为方式类似



操作。





操作。行为方式类似



操作。





操作。行为方式类似



操作。





(指数)



操作




function pow_event (op1, op2)


local o1, o2 = tonumber(op1), tonumber(op2)


if o1 and o2 then -- both operands are numeric?


return __pow(o1, o2) -- call global `__pow'


else -- at least one of the operands is not numeric


local h = getbinhandler(op1, op2,


if h then


-- call the handler with both operands


return h(op1, op2)


else -- no handler available: default behavior


error(


end


end


end




一元取负


-


操作。




function unm_event (op)


local o = tonumber(op)


页脚内容


18



LTE


初始随机接入过程详解



if o then -- operand is numeric?


return -o -- `-' here is the primitive `unm'


else -- the operand is not numeric.


-- Try to get a handler from the operand


local h = metatable(op).__unm


if h then


-- call the handler with the operand and nil


return h(op, nil)


else -- no handler available: default behavior


error(


end


end


end




(串连接)操作。




function concat_event (op1, op2)


if (type(op1) ==


(type(op2) ==


return op1 .. op2 -- primitive string concatenation


else


local h = getbinhandler(op1, op2,


if h then


return h(op1, op2)


else


error(


end


end


end




操作。函数


getcomphandler


定义了


Lua


是如何为比较


操作选择一个元方法的。


只有当参与比较 的两个对象属于同一类型而且需要的


元方法一样时,才会选择这个元方法。




function getcomphandler (op1, op2, event)


if type(op1) ~= type(op2) then return nil end


local mm1 = metatable(op1)[event]


local mm2 = metatable(op2)[event]


if mm1 == mm2 then return mm1 else return nil end


end


事件如下定义:



function eq_event (op1, op2)


页脚内容


19



LTE


初始随机接入过程详解



if type(op1) ~= type(op2) then -- different types?


return false -- different objects


end


if op1 == op2 then -- primitive equal?


return true -- objects are equal


end


-- try metamethod


local h = getcomphandler(op1, op2,


if h then


return h(op1, op2)


else


return false


end


end


a ~= b is equivalent to not (a == b).




操作。




function lt_event (op1, op2)


if type(op1) ==


return op1 < op2 -- numeric comparison


elseif type(op1) ==


return op1 < op2 -- lexicographic comparison


else


local h = getcomphandler(op1, op2,


if h then


return h(op1, op2)


else


error(


end


end


end


a > b is equivalent to b < a.




操作。




function le_event (op1, op2)


if type(op1) ==


return op1 <= op2 -- numeric comparison


elseif type(op1) ==


return op1 <= op2 -- lexicographic comparison


else


页脚内容


20



LTE


初始随机接入过程详解



local h = getcomphandler(op1, op2,


if h then


return h(op1, op2)


else


h = getcomphandler(op1, op2,


if h then


return not h(op2, op1)


else


error(


end


end


end


end


a >= b is equivalent to b <= a. Note that, in the absence of a


Lua tries the




通过索引访问


table[key]





function gettable_event (table, key)


local h


if type(table) ==


local v = rawget(table, key)


if v ~= nil then return v end


h = metatable(table).__index


if h == nil then return nil end


else


h = metatable(table).__index


if h == nil then


error(


end


end


if type(h) ==


return h(table, key) -- call the handler


else return h[key] -- or repeat operation on it


end




给表的索引赋值


table[key] = value





function settable_event (table, key, value)


local h


if type(table) ==


local v = rawget(table, key)


页脚内容


21



LTE


初始随机接入过程详解



if v ~= nil then rawset(table, key, value); return end


h = metatable(table).__newindex


if h == nil then rawset(table, key, value); return end


else


h = metatable(table).__newindex


if h == nil then


error(


end


end


if type(h) ==


return h(table, key,value) -- call the handler


else h[key] = value -- or repeat operation on it


end





Lua


调用 某个值时调用。




function function_event (func, ...)


if type(func) ==


return func(unpack(arg)) -- primitive call


else


local h = metatable(func).__call


if h then


return h(func, unpack(arg))


else


error(


end


end


end


[


编辑


]


2.9 -


垃圾收集



Lua


会自动进行内存管理。


这意味 着你不需要担心新对象的内存分配问题,


也不需要释放不


用的对 象。


Lua


通过不断地运行



垃圾收集器



收集


dead objects


( 也就是那些


Lua


中无法访问


的对象) 来自动管理内存。


Lua


中所有的对象都是自动管理的目标:< /p>


表,用户数据,函数,


线程,


和字符串。


Lua


使用两个数字控制垃圾收集循环。一个数字表示


Lua


使用的动态内存的字节数,


另一个是阀值 。当内存字节数到达阀值时,


Lua


就运行垃圾收集器,来释放 死对象的空间。一旦


字节计数器被调整,那么阀值就会被设为字节计数器新值的两倍。< /p>



通过


C API


你可以查询和更改阀值


(见


3 .7




将阀值设为零时会强制立刻进 行垃圾收集,


同时把他设为足够大就可以停止垃圾收集。仅使用


Lua


代码中的


gcinfo



collectgarbage


函数



(见


5.1


)可以获得一定程度上对垃圾收集循环的控制。



[


编辑


]


页脚内容


22



LTE


初始随机接入过程详解



2.9.1 -


垃圾收集元方法




Garbage-Collection Metamethods




使用


C API


,你可以对用户数据 设置一个垃圾收集元方法


(见


2.8




这些元方法也称为终


结器(


finalizers


)。终结器允许你用外部的资源管理来调整


Lua


的垃圾收集(如关闭文件,网


络或数 据库连接,或者释放你自己的内存。



用元表中包含


__gc


字段的自由 用户数据不会立即被垃圾收集器回收。而是,


Lua


把它们放< /p>


在一个列表中。收集完毕之后,


Lua


会 对这个列表中的用户数据执行和以下函数相等的操作:



function gc_event (udata)


local h = metatable(udata).__gc


if h then


h(udata)


end


end


在每个垃圾收集过程 最后,


调用用户数据的终结器的顺序,


将按照他们在收集过程中 添加到


列表中的相反顺序进行。


也就是,


第一个被调用的终结器是和在程序中创建的最后一个用户数据


相关的那个终结器。



[


编辑


]


2.9.2 -


弱表



一个弱表(


weak table




是一个包含的元素是



弱引用(


weak references

)的表。垃圾收


集器会忽略弱引用。


换句话说,

< p>
如果指向一个对象的引用只有弱引用,


那么这个对象还是要被垃

< p>
圾收集器回收。



弱表可以包含弱的键,弱的值 ,或者两者皆有。一个包含弱键的表允许它的键被回收,


但值


不 可以。


一个同时包含弱键和弱值的表允许键和值的回收。


无论哪 种情况,


只要键或者值中的一


个被回收了,

那么这一对键值将会从表中删除。


这个表的弱属性是由它的元表的

< br> __mode


字段控


制的。如果


__mode


字段是一个包含字符


`k


?的字符串,那么表中的键是弱键。如果


__mode


字段是一个包含字符


`v


?



的字符串,那么表中的值是弱值。



在你将表用作元表之后,


你不应该更改


__mode


字段的值。


否则,


这个 元表控制的表的弱表


行为将会不确定。



[


编辑


]


2.10 -


同步程序


< p>
Lua


支持同步程序,也称为半同步程序(


sem i-coroutines






协同多线程(

collaborati


ve multithreading

)。


Lua


中的一个同步程序代表了一个独立的执行线程。 然而,不像在多线


程系统中的线程那样,一个同步程序只有在调用了一个


yield


(产生结果)函数才能挂起它的执


行。



你可以调用



来创建一个同步程序。


它唯一的一个参数是一个函数,


代表< /p>


同步程序的主函数。


create


函数仅仅建立一个新的同步程序然后返回一个它的句柄



(一个线程


thread


类型的对象);它不会启动该同步程序。



页脚内容


23



LTE


初始随机接入过程详解



当你第一次调用



,将



返回的线程对象作为第一个参


数传递给它,然后同步程序就启动了,从它的主函数的第一行开始。传给



的额外的参数会作为同步程序主函数的参数传递过去。

在同步程序开始执行之后,


它一直运行到


它结束或产生结果 。



一个同步程序通过两种方式结束它的运行:


正常情况下,


当它的主函数返回


(显式地或隐式


的,在最后一个指令之后)时结束;异常地,如果有未保护的错误。第一各情况下,


coroutine.


resume


返回


true


,加上同步程序主函数 返回的其它值。在有错误的情况下,



e


返回


false


,并附上错误信息。



一个同步程序通过调用



来产生结果 。


当一个同步程序产生结果,


相应的




就立刻返回,即使操作发生在嵌套函数调用中(也就是,不在 主函数中,而


在被主函数直接或间接调用的函数中)。在这种情况下,

< br>


也返回


true


,以


及传给



。的所有参数。下次你继续同一个同步程序时,它会从它原来


yield


的地方继续执行,




将返回给主程序传给



的额外参数。




函数创建一个和



一样的同步程序, 但它不返回同步程


序本身,而是返回一个继续同步程序的函数(当调用的时候)。


传递给这个函数的参数作为继续


resume


的额外参数。函数将返回


resume


返回的所有值,出除了第 一个(布尔值的错误代码)。


不像



,这个函数不捕获错误;出现任何错误都传回给调用者。



请考虑以下例子:



function foo1 (a)


print(


return (2*a)


end


co = (function (a,b)


print(


local r = foo1(a+1)


print(


local r, s = (a+b, a-b)


print(


return b,


end)


a, b = (co, 1, 10)


print(


a, b, c = (co,


print(


a, b, c = (co,


print(


a, b = (co,


print(


页脚内容


24



LTE


初始随机接入过程详解



当你运行它的时候,它会产生以下输出结果:



co-body 1 10


foo 2


main true 4


co-body r


main true 11 -9


co-body x y


main true 10 end


main false cannot resume dead coroutine


[


编辑


]


3 -


应用程序接口



这一节描述


Lua


中的


C API


,也就是宿主程序用来与


LUA


通讯的


C


函数集合。所有的


API



数及其相关类型和常量都声明在头文件


lua.h

中。



即便每次我都使用“函数”这个词,


任何设施在


API


里面都可能被一个宏所替代。所有 这些


宏(


macro


)都只使用一次它 的参数(除了第一个参数、这个每次总是一个


Lua


状态),所 以不


会产生隐藏的副作用。



3.1 -


状态



Lua

< br>库是可重入的(


reentrant


)的:它没有全局变 量。整个


Lua


解释器的状态(全局变量、

栈、等等)储存在一个动态分配的


lua_State


结构类型中。除了


lua_open


这个函数以外,

< p>
LUA


库中每个函数的第一个参数都必须是一个指向这样一个状态的指针。


lua_open


则是创建一个原


始的


Lua


状态。


在调用任何


API


函数之前,你必须通过调用


lua_open


创建一个状态:



lua_State *lua_open (void);


调用


lua_open


去释放这个由


lua_close


创建的状态:



void lua_close (lua_State *L);


这个函数销毁所有被给予


Lua


状态的对象


(调用相应的垃圾收集元 方法)


并且释放那个状态


使用的所有动态内存。


在个别的平台上,


你或许不需要调用这个函数,


因为当 宿主程序结束的时


候会自然的释放所有的资源。另一方面,长时间运行的程序,像一些守 护进程或者


Web


服务器,


可能需要立 即释放那些不需要的状态资源,以避免占用太多内存。



[


编辑


]


3.2 -


堆栈和索引


< p>
Lua


利用虚拟栈来将值传给


C

< br>和从


C


获取值。


栈里面的每一个 元素都代表一个


Lua



(nil,


number, string,


等等

< p>
)




只要

< p>
Lua


调用


C


语言函数,


这个所调用的函数将得到一个新的栈,


这个栈将独立于先前的栈


以及那些仍然活跃的


C


函数的栈。


这个栈最初包含了传递给


C


函数的所有参数 ,


并且


C


函数将它

的结果通过这个栈返回给它的调用函数(见


3.16


)。



页脚内容


25



LTE


初始随机接入过程详解



为了方便起见,大多数查询操作的


API


不需 要遵守一个严格的栈定义(注:即不需要遵循


F


ILO


)。相反他们可以使用


索引


< br>index


)引用任何栈中元素:一个正数索引代表了栈中的绝对位


置(从


1


开始);一个负数索引代表了从栈顶的偏移 量。更特别的是,如果栈有


n


个元素,那


么索引


1


代表第一个元素(这就是说,这个元素首先入栈)并且索引


n


代表了最后一个元素;


索引


-1 < /p>


也代表了最后一个元素


(也就是栈顶)


并 且索引


-n


代表了第一个元素。


我 们说一个索


引存在于


1


和栈顶之间是有效的,换句话说,如果


1 <= abs(index) <= top




在任何时间里,你可以调用


lua_gettop


得到栈顶元素的索引:



int lua_gettop (lua_State *L);


因为索引从


1


开始,


lua_gettop


的结果 等于栈中的元素数量


(如果是


0


就意味 着栈为空)




当你与


Lua API


交互的时候,你有责任控制堆栈以避免溢出。。这个函数



int lua_checkstack (lua_State *L, int extra);


使栈的大小增长为


top + extra


个元素;如果无法将栈增加到那个大小将返回


false


。这


个函数从不对栈进行收缩;如果栈已经比新的大小更大,它将不产 生任何作用那个。



只要


Lua


调用


C


函数,它必须至少保证


LUA_MINSTACK


这个栈中的位置是可用的。


LUA_MIN


ST ACK


定义在


lua.h


中,


它的值是


20

< br>,


所以你不需要总担心栈空间除非你的代码通过循环将元


素压入栈。



大多数插叙函数接受指向有效栈空间的索引,< /p>


那就是说,


索引达到栈空间的最大值是你需要

使用


lua_checkstack


。这样的索引称为可 接受索引(


acceptable indices


)。更正规 的说法,


我们给出一个严格的定义如下:



(index < 0 && abs(index) <= top) || (index > 0 && index <= stackspace)


注意,< /p>


0


永远不是一个可接受索引。



除非另外说明,任何可以接受有效索引的查询函数也能够接受一个伪索引(

pseudo-indice


s


)作为它的参数,伪索引描 述的是可以被


C


访问但却不存在于栈上的


Lua


值。伪索引通常用于


访问全局环境变量,


Registry


,和一个


C


函数的


Up- Value


(见


3.17


)。



3.3 -


堆栈操作



一下的

API


提供了基本的栈操作:



void lua_settop (lua_State *L, int index);


void lua_pushvalue (lua_State *L, int index);


void lua_remove (lua_State *L, int index);


void lua_insert (lua_State *L, int index);


void lua_replace (lua_State *L, int index);


lua_settop


接受任何可接受的索 引,或者


0


,并且将该索引设置为栈顶。如果新的栈顶比


旧的更大,那么新元素被填上


nil


值。如果索引为


0


,那么所有栈元素会被清除。在


lua.h


里面定义了一个有用的宏



#define lua_pop(L,n) lua_settop(L, -(n)-1)


用以从栈中弹出


n


个元素。



页脚内容


26



LTE


初始随机接入过程详解



lua_pushvalue


将一个索引指向的元素的拷贝压入栈。


lua_remove


删除指定位置的元素,


将该元素上方的所有元素下移以填满空缺 。


lua_insert


将栈顶元素移动到指定位置,将该位 置


以上的元素上移。


lua_replace


将栈顶元素移动到指定位置而不移动其他任何其他元素


(因此替


代了给定位置的元素的值)。所有这些函数只接受有效的索引。(你不能使用伪索引调用


lua_


remove



lua_insert


,因为他们不代表栈中的位置。)



举个例子,如果栈开始于


10 20 30 40 50*< /p>


(自底向上;


`*


?


标记了栈顶),那么:



lua_pushvalue(L, 3) --> 10 20 30 40 50 30*


lua_pushvalue(L, -1) --> 10 20 30 40 50 30 30*


lua_remove(L, -3) --> 10 20 30 40 30 30*


lua_remove(L, 6) --> 10 20 30 40 30*


lua_insert(L, 1) --> 30 10 20 30 40*


lua_insert(L, -1) --> 30 10 20 30 40* (no effect)


lua_replace(L, 2) --> 30 40 20 30*


lua_settop(L, -3) --> 30 40*


lua_settop(L, 6) --> 30 40 nil nil nil nil*


[


编辑


]


3.4 -


堆栈查询



下面的函数可以用来检测栈内元素的类型:



int lua_type (lua_State *L, int index);


int lua_isnil (lua_State *L, int index);


int lua_isboolean (lua_State *L, int index);


int lua_isnumber (lua_State *L, int index);


int lua_isstring (lua_State *L, int index);


int lua_istable (lua_State *L, int index);


int lua_isfunction (lua_State *L, int index);


int lua_iscfunction (lua_State *L, int index);


int lua_isuserdata (lua_State *L, int index);


int lua_islightuserdata (lua_State *L, int index);


这些函数只能使用可接受的索引。



lua_type


返回栈中元素值的类型,如果所有索引无效则返回


LUA_T NONE


(就是说如果栈为


空)。这些


lua_type


代表的返回值作为常量定义在


lua.h


中:


LUA_TNIL, LUA_TNUMBER, LUA_


TBOOLEAN, LUA_TSTRING, LUA_TTABLE, LUA_TFUNCTION, LUA_TUSERDATA, LUA_TTHREAD, LUA_T


LIGHTUSERDATA


。下面的函数将这些常量转换成字符串 :



const char *lua_typename (lua_State *L, int type);


lua_is*


函数返回


1


当对象与所给类型兼容的时候,其他情况返回


0



lua_isboolean


是一个例外:它只针对布尔值时才会成功(否则将是无用的,因为任何值都是一个布尔值)。这

< br>些函数对于无效引用返回


0



lua_isnumber


接受数字和用数字表示的字符串;


lua_isstring


接受字符串和数字(见


2.2.1


);


lua_isfunction


接受


Lua


函数和


C


函数;


lua_isuserdata


页脚内容


27



LTE


初始随机接入过程详解



接受完整的和轻量的用户数据。要区分


C


函数和


Lua


函数,你可以使用


lua_iscfunction


。要


区分用户数据,


你可以使用


lua_islightuserdata



要区分 数字还是用数字表示的字符串,


你可


以使用

lua_type




这些< /p>


API


还包含了用于比较栈中的两个值的操作:

< br>


int lua_equal (lua_State *L, int index1, int index2);


int lua_rawequal (lua_State *L, int index1, int index2);


int lua_lessthan (lua_State *L, int index1, int index2);


lua_equal



lua_lessthan


在比较他们的副本的时候是等效的(见


2.5.2


)。


lua_rawe


qual


用于比较基本类型但不包括元方法 。如果有任何形式的无效索引,这些函数都返回


0



fa


lse


)。



[


编辑


]


3.5 -


堆栈取值



为了将一个栈中的值转变为指定的


C


语言类型,你需 要使用以下的转换函数:



int lua_toboolean (lua_State *L, int index);


lua_Number lua_tonumber (lua_State *L, int index);


const char *lua_tostring (lua_State *L, int index);


size_t lua_strlen (lua_State *L, int index);


lua_CFunction lua_tocfunction (lua_State *L, int index);


void *lua_touserdata (lua_State *L, int index);


lua_State *lua_tothread (lua_State *L, int index);


void *lua_topointer (lua_State *L, int index);


这些函数由任何可接受索引 作为参数进行调用。


当遇到一个无效索引,


函数表现为就好像接


受了一个错误类型的值。



lua_toboolean


将索引指向的

< br>Lua


值转换为


C


语言类型的布 尔值(


0



1

)。就像所有


Lua


中的测试一样,任何不等于

< p>
false


或者


nil

< br>的


Lua


值通过


lua_toboolean


都将返回


1


;否则


将返回


0


。当然,如果是一个无效索引,也将返回


0


。(如果你只想接受真实的布尔值,使用



lua_isboolean


去测试值的类型。)



lua_tonumber


将索引指向的

Lua


值转换成一个数字(默认情况下,


lua_Numb er



double


类型)。


Lua


值必须是一个数字或者可转化为数字的字符串(见

< p>
2.2.1


);否则,


lua_tonumbe r



返回


0




lua_tostring


将索引指向的


Lua


值转换成字符串(


const char*


)。


Lua< /p>


值必须是一个字符


串或者数字;否则,函数返回

< br> NULL


。如果值是一个数字,


lua_tostring < /p>


会将栈中的真实值变成


一个字符串类型。(当

lua_tostring


应用于键时这个改变将引起


lua_next


的混乱。)


lua_t


ostring



Lua


状态内部返回一个字符串的指针。这个字符串总是以


0



'0'


)结尾,就像


C


语言里的一样,


但是也可能包含其他


0


在其中。


如果你不知道一个字符串中是否存在


0



你可


以使用


lua_strlen


得到它的实际长度。因为


Lua


具有垃圾收集机制,所以不能保证


lua_tost


ring


返回的指针仍然有效,当相应的值 从栈中删除之后。如果你在当前函数返回之后还需要这


个字符串,你需要复制它并且将它 存入注册表(见


3.18


)。



页脚内容


28



LTE


初始随机接入过程详解



lua_tocfunction


将栈中的值转换为


C


函数。这个值必须是一个


C


函数;否 则,


lua_tocf


unction


返回


NULL


。类型


lua_CFunction



3.16


中有详细解释。



lua_tothread


将栈中的值转换为


Lua


线程(被描绘成


lua_State *


)。这个值必须是一个


线程;否则;


lua _tothread


返回


NULL




lua_topointer


将栈中的值转换为通用的


C


语言指针(


void *


)。这个值可 能是一个用户


数据、表、线程、或者函数;否则,


lua_to pointer


返回


NULL


。< /p>


Lua


保证同种类型的不同对象


将返回不 同指针。没有直接的方法将指针转换回原来的值。这个函数通常用于调试。



lua_touserdata



3.8


中有详细解释。



[


编辑


]


3.6 -


将值压入堆栈



以下的


API


函数将


C


语言值压入栈:



void lua_pushboolean (lua_State *L, int b);


void lua_pushnumber (lua_State *L, lua_Number n);


void lua_pushlstring (lua_State *L, const char *s, size_t len);


void lua_pushstring (lua_State *L, const char *s);


void lua_pushnil (lua_State *L);


void lua_pushcfunction (lua_State *L, lua_CFunction f);


void lua_pushlightuserdata (lua_State *L, void *p);


这些函数接受一个


C


语言值,将其转换成相应的


Lua


值 ,并且将结果压入栈。需要特别注


意的是,


lua_pushl string



lua_pushstring


将对所给的字符串做一个内部拷贝。


lua_pushs


tring


只能压入合适的


C


语言字符串(也就是说,字符串要以


'0'


结尾,并且不能包含内嵌



0


);否则,你需要使用更通用的


lua_pushlstring


函数,它可以接受一个指定的大小。



你可以压入“格式化的”字符串:



const char *lua_pushfstring (lua_State *L, const char *fmt, ...);


const char *lua_pushvfstring (lua_State *L, const char *fmt, va_list argp);


这些函数将格式化的字符串压入栈并且返回这个字符串 的指针。它们和


sprintf



vsprint


f


类似,但是有一些重要的不同之处:




你不需要为结果分配空间:结果是


L ua


字符串并且


Lua


会关心内存


分配问题(和内存释放问题,通过垃圾收集机制)。





转换受到限制。这里没有标志、宽 度或精度。转换操作的修饰符可


以是简单的


`%%


?(在字符串中插入一个


`%


?),


`%s


?(插入一个没有大小限制


的以


0


结尾的字符串),


`%f


?(插入一个


lua_Number


),

< br>`%d


?(插入一个



int< /p>


),


`%c


?(插入一个


int


作为一个字符)。




这个函数



void lua_concat (lua_State *L, int n);


连接栈顶的


n


个值,将它们弹出, 并且将结果留在栈顶。


如果


n



1



结果 是单个字符串


(也就是说,函数什么也不做);如果


n



0


,结果是空字符串。连接的完成 依据


Lua


的语义


(见


2.5.4


)。



页脚内容


29



LTE


初始随机接入过程详解



[


编辑


]


3.7 -


控制垃圾收集



Lua


使用两个数字控制垃圾收集循环。一个数字表示

< br>Lua


使用的动态内存的字节数,另一个


是阀值。


(见


2.9


)。一个数字表示


Lua


使用的动态内存的字节数,另一个是阀值。当内存字节


数到达阀值时,


Lua


就运行垃圾收集器,来释放死对 象的空间。一旦字节计数器被调整,那么阀


值就会被设为字节计数器新值的两倍。



你可以通过以下的函数得到这两个量的当前值:



int lua_getgccount (lua_State *L);


int lua_getgcthreshold (lua_State *L);


它们的返回值的单位都是千字节(


K bytes


)。你可以通过下面的函数改变阀值



void lua_setgcthreshold (lua_State *L, int newthreshold);


然后,新的阀值得单位也是千字节。当你调用 这个函数,


Lua


设置阀新值并且和字节计数器


作比较。如果新的阀值小于字节计数器,


Lua


将立刻 运行垃圾收集器。特别是


lua_setgcthresh


old(L,0)


强迫进行 垃圾收集。在这之后,一个新值根据先前的规则被设置。



[


编辑


]


3.8 -


用户数据类型




Userdata




用户数据代表了


Lua


中使用的


C


语言值。


Lua


支 持两种用户数据:完整用户数据(


full use


rdata






轻量用户数据(


light userdata


)。



一个完整用 户数据代表了一块内存。它是一个对象(像一个表):你必须创建它,它有自己


的元表,


当它被回收的时候你可以检测到。


一个完整用户数据只能与自己 相等


(基于原始的相等


规则)。


< /p>


一个轻量用户数据代表一个指针。它是一个值(像一个数字):你并没有创建它,它也没有


元表、



它不能被回收


(因为它从未被创建)



轻量用户数据相等的条件是指 针指向的地址相同。




Lua < /p>


代码里,没办法测试用户数据类型是完整的还是轻量的;两者都是



用户数据类型。



C


代码里,如果是完整用户数据,


lua_type


返回


LUA_TUSERDATA


,反之,返回


LUA_TLIG HTU


SERDATA




你可以通过下面的函数创建完整用户数据:



void *lua_newuserdata (lua_State *L, size_t size);


这个函数根据指定大小分配一个内存块,将用户数据的地 址压入栈并且返回这个地址。



要将轻量用户数据压入栈,你需要使用


lua_pushlightuserdata


(见


3.6


)。



lua_touserdata


(见


3.5


)用来取回用户数据的值。当你用在完整用户数据的时候,它返

< br>回这个块的地址,当你用在轻量用户数据的时候,它返回它的指针,当你用在非用数据的时候,

< p>
返回


NULL



< /p>



Lua


回收一个完整用户数据,


它调用该用户数据的


gc


元方法,


然后释放该用户数据相应


的内存。



页脚内容


30



LTE


初始随机接入过程详解



[


编辑


]


3.9 -


元表


< br>(


Metatables




下面的函数允许你操作对象的元表:



int lua_getmetatable (lua_State *L, int index);


int lua_setmetatable (lua_State *L, int index);


lua_getmetatable


将所给对象的元表压入栈。如果索引无效,或这个对象不含有元表,该


函数返 回


0


并且不对栈进行任何操作。



lua_setmetatable


从栈中弹出一张表并且为 所给对象设置一个新的元表。当无法给所给对


象设置元表的时候该函数返回


0


(也就是说,


这个对象既不是一个用户数据也不 是一张表)




管那样,它仍从栈中弹 出这张表。



[


编辑


]


3.10 -


加载


Lua

< p>
语句段



你可以通过


lua_load


加载一个


Lua


块 :



typedef const char * (*lua_Chunkreader)


(lua_State *L, void *data, size_t *size);


int lua_load (lua_State *L, lua_Chunkreader reader, void *data,


const char *chunkname);


lua_load


的返回值是:



* 0 ---


没有错误



* LUA_ERRSYNTAX ---


预编译时句法错误



* LUA_ERRMEM ---


内存分配错误



如果没有错误,


lua_load


将编译过的语句段作为


Lua


函数压 入栈顶。否则,它将压入一个


错误信息。



lua_load


自动检测语句段的类型是文本还是二进制数 据,


并且根据类型将其载入


(见程序



luac


)。



lua_load


使用一个用户提供的


reader


函数读取语句段的内容。当需要调用其它段时,


lu


a_loa d


调用


reader



传递其


data < /p>


参数。


必须返回指向语句段所在的新内存块的指针,


并将段


大小设置为


0



为了标志块尾,


reader


必须返回


NULL



reader


函数可以返回任何大于零的值。



在当前的实现中,


reader


函数不能调用任何


Lua


函数;为了保证这一点,它总是会得到为


NULL



Lua


状态。


< /p>


语句段名(


chunkname


)用于错 误信息和调试信息(见


4


)。



参考辅助库


(lauxlib.c)


了解如何使用


lua_load


以 及如何使用现成的函数从文件和字符


串中加载语句段。



[


编辑


]


页脚内容


31



LTE


初始随机接入过程详解



3.11 -


表操作



通过调用以下函数可以创建表:



void lua_newtable (lua_State *L);


这个函数创建一张新的空表,并将其压入栈。



要从栈中的表里读取值,使用:



void lua_gettable (lua_State *L, int index);


index


代表表的位置。


lua_gettable

< br>从栈中弹出一个键,并且返回该键对应的值,表仍然


留在堆栈中。在


Lua


中,这个函数可能触发一个针对


index


事件的元方法(见


2.8


)。想要在


不调用任何元方法的情况下得到表主键所对应的真实值,使用这个原始(


raw


)版本:



void lua_rawget (lua_State *L, int index);


要 将一个值储存到栈中的一张表中,你需要将键压入栈,再将值压入栈,调用:



void lua_settable (lua_State *L, int index);


index


代表表的位置。


lua_settable

< br>从栈中弹出主键和值。表仍然留在栈中。在


Lua


中,< /p>


这个操作可能触发针对


settable


或者


newindex


事件的元方 法。


想要不受这些元方法的影响并


且为任意表设置值,使用这个 原始(


raw


)版本:



void lua_rawset (lua_State *L, int index);


你可以通过这个函数遍历一张表:



int lua_next (lua_State *L, int index);


index


指向需要被遍历的表。


这 个函数从堆栈中弹出一个键,


从表中取一对键


-


值压入栈


(所


给键的下一对)



如果没有更多的元素,


lua_next


返回


0


(只弹出一个键,

< p>
不压入任何键


-


值对)



使用一个


nil


键标示遍历的开始。




一个典型的遍历操作看起来像这样:



/* table is in the stack at index `t' */


lua_pushnil(L); /* first key */


while (lua_next(L, t)


!= 0) {


/* `key' is at index -2 and `value' at index -1 */


printf(


lua_typename(L, lua_type(L, -2)), lua_typename(L, lua_type(L, -1)));


lua_pop(L, 1); /* removes `value'; keeps `key' for next iteration */


}


当遍历一张表的时候,不要在键上直接调用


lua_tost ring


,除非你知道这个键确实是一个


字符串。再次调用


lua_tostring


改变了所给索引指向的值;这使


lua_next


的调用发生混乱。



3.12 -


环境变量操作




Mani***ting Environments




所有的全局变量保存在普通的


Lua


表中,叫做环境变量。初始的环境变量被称作全局环境


变量。这张表总是在


LUA_GLOBALSINDEX


这个伪索引处。



要访问或改变全局 变量的值,


你可以对环境变量表使用常规的表操作。


举个例子,


存取一个


全局变量的值:



页脚内容


32


-


-


-


-


-


-


-


-



本文更新与2021-02-28 01:02,由作者提供,不代表本网站立场,转载请注明出处:https://www.bjmy2z.cn/gaokao/677344.html

LUA参考手册的相关文章