-
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
目前已经被实现为一个扩展库,是用
p>
clean C
(
ANSI
C/C++
的一个通用子集)编写的。
作为一个扩展语言,
Lua
没有
<
/p>
函数的概念:它仅仅是嵌入一个宿主程序进行工作,可
以称之为嵌
入式编程
或者简单的说是宿主编程。这个宿主程序可以调用函
数来执行
Lua
的代码
片断,可以设置
和读取
Lua
的变量,可以注册
C
p>
函数让
Lua
代码调用。
< br>Lua
的能力可以扩展到更
大范围,在不同的领域内,这
样就在同样的语法框架下创建了你自定义的编程语言。
Lu
a
的发行版包括一个独立的嵌入式程序,
lua
,他使用
Lua
的扩展库来提供一个完全的
Lu
a
解释器。
p>
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
内
部变量所保留的。
p>
下面的字符(串)是其他的一些标记:
+ - * / ^ =
~= <= >= < > ==
( ) { } [ ]
;
: , . .. ...
字符串
(
Literal
strings
)
以单引号或者双引
号定界,
同时可以包含以下
C
语言风格
的转
义字符:
a ---
铃声(
bell
)
b --- <
/p>
回退(
backspace
)
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
?通过数字值来指定。
ddd
是最多为
3
个十
进制数字的序
列。
Lua
中的字符串也
可以包含
8
进制数字,包括嵌入零,它可以表示为
`0
?。
字符串也可以用双方括号来定界
[[
·
·
·
]]
。这种括号方式的语法,字符
串可以跨越多
行,也可以包含嵌套的,同时不会转义任何序列。方便起见,当开始的
p>
`[[
?
后面
紧跟着一个换
行符的话,这个换行符不会包括在字符串内。
举个
例子:在一个使用
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>
]]
结束。
长注释可以跨越多行,
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
中,
p>
nil and false
代表
成假
条件;其他任何值都代表成真条件。
Number
数字类型
表示实数(双精度浮点数)。
(构建
Lua
解释器时也可以很容易地用其他内部的表示方式表示数字,如单精度浮点数或者长整型)。
< br> String
字符串类型表示一个字符的序列。
Lua
字符串可以包含
8
位字符,
包括嵌
入的
('0')
(见
2.1
)。
页脚内容
5
LTE
初始随机接入过程详解
函数是
Lua
中的第一类值(
first-class values
)。
也就是说
函数可以保存在变量中,
当作
参数传递给其他函数,或者被当作
结果返回。
Lua
可以调用(和处理)
Lua
写的函数和
C
写的函
数
(见
2.5.7
)。
用户数据类型
p>
(
userdata
)
提供了让任意
C
数据储存在
Lua
变量中的功能。
这种类型直接对
应着一
块内存,
Lua
中也没有任何预先定义的操作,除了赋值和一致
性比较。然而,通过使用元
表(
metatables
),程序员可以定义处理
userdata
的操
作。(见
2.8
)。
Userdata
值不能在
Lua
中
建立或者修改,只能通过
C
API
。这保证了宿主程序的数据完整性。
线程(
thread
)类型代表了相互独立的执行线程
,用来实现同步程序。
表(
tab
le
)类型实现了联合数组,也就是说,数组不仅可以使用数字,还能使用其他的值
p>
(除了
nil
)。
而且,
tables
可以是互异的(
heterogeneous
),他们可以保存任何类型的值<
/p>
(除了
nil
)。
Tables
是
Lua
中唯一的数据结构机制;他们可以用来表示一般数组,特征表,
集合,记录,图,树等等。如果要表示记录,
Lua
使用字段名作为索引。语言支持
这种
比较优美的表示方式,还有
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
中有三种不同的变量:全局变量,局部变量和表字段。
一个名称可以表示全局变量或局部变量
(或者
一个函数的正式参数,
一种局部变量的特殊形
式):
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
写的每个函数
(a Lua
函数
)
都有一个它自己的环境的引
用,
这样这个函数中的所有的全局
变量都会指向这个环境变量表。
当新创建一个函数时,
它会继<
/p>
承创建它的函数的环境。要改变或者获得
Lua
< br>函数的环境表,可以调用
setfenv or getfenv
(见
5.1
)。
访问全局变量
x
等同于
_env.x
,又等同于
gettable_event(_env,
_env
是运行的函数的环境。
(
_env <
/p>
变量并没有在
Lua
中定义。
我们这里仅仅用来解释原理)
2.4 -
语句
< br>Lua
支持一种很通俗的语句集,和
Pascal
或者
C
中的很相似。他包括赋值,控制结构,
过
程调用,表构造和变量声明。
2.4.1 -
语句段
Lua
执行的最小单元称之为一个段(
chunk
)。一段语句就是简单的语句的序列,以顺序执
行。每一个语句
后面都可以加上一个分号(可选):
chunk
::= {stat [`;
?
]}
Lua
将语句段作为一个匿名函数(见
2.5.8
)
的本体进行处理。这样
,语句段可以定义局
部变量或者返回值。
< br>一段语句可以储存在文件内或者宿主程序的一个字符串中。
当语句段被执行时,<
/p>
他首先被预
编译成虚拟机使用的字节码,然后虚拟机用一个解释器
执行被编译的代码。
语句段也可以被预编译为二进制代码;
详情参看
luac
程序。
源代码和编译形态可以互相转
换;
Lua
自动监测文件类型然后作相应操作。
2.4.2 -
语句块
一个语句块是一系列语句;从语句构成上来看,语句块等同于语句段:
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
)。
p>
赋值语句首先计算出所有的表达式,然后才会执行赋值,所以代码:
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
中并未定
义该函数,他也无法直接调用。我们这里只是用它来进行解释。)
p>
对全局变量的赋值
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
注意:
三种控制表达式只会被计算一次,在循环开始之前。他们的结果必
须是数值。
_limit
和
_step
是不可见的变量。这里只是为了进行解释。
如果你在程序块内给
var
赋值,结果行为将会不确定。
如果没
有给出第三个表达式(步长),那么默认为
1
。
你可以使用
break
来退出
for
循环。
循环变量
var
是局部变量;你不可以在
for
循
环结束之后继续使
用。如果你需要使用这个值,请在退出循环之前把它们传给其他变量。
for
的语句的一般形式是操作于函数之上的,称之为迭代器(
iterators
)。每一个迭代过
程,它调用迭代函数来产生新的值,直到新的值是
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
I
在这里,所有的返回值都会被忽略。函数调用将在
2.5.7
详细解释。
2.4.7 -
局部变量声明
p>
局部变量可以在语句块中任何地方声明。声明时也可以添加一个初始赋值:
< 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
p>
返回第
页脚内容
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 -
串联接
在
Lua
中字符串连接操作符是两个点
(`..
?
)
。如果两
边的操作数都是字符或者数字,他们
就都会按照
2.2.1
的规则被转换成字符串。否则,将调用
元方法(见
2.8
)。
[
编辑
]
2.5.5 -
优先级
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>同时表达式又是一个函数调用,那么调用返回的所
有值会依次进入列表
(见
2.5.7
)
。<
/p>
如果要避免这种情况,
在函数调用两边加上括号
< br>(见
2.5
)
。
字段列表可以有一个结尾的分隔符,这个对由机器生成的列表十分方便。
[
编辑
]
2.5.7 -
函数调用
Lua
中的一个函数调用有如下语法:
functioncall
::= prefixexp
args
在函数调用中,
首先会计算
prefixexp
和
args
。
如果
prefixexp
的值是
function
类型,<
/p>
那么那个函数就会被调用,同时使用给出的参数。否则,
他的
p>
元方法就会被调用,
第一
个参数是
prefixexp
的值,接下来是原来的调用参数(见
2.8
)。
形式
functioncall
::= prefixexp `:
?
Name args
可以用来调用“方法”
(
)。调用
v:name(...)
语法上比
(v,...)
,要好
p>
一些,除非表达式
v
只计算一次。
参数可以有以下几种语法:
args
::=
`(
?
[explist1]
`)
?
args
::=
tableconstructor args
::= Literal
所有的参数表达式都会在实际调用之前进行计算。
f{...}
的调用形式在语法上较
f({...})
要好,是因为,参数列表示一个单独的新表。
f'...'
(或者
f
或者
f...
)
较
f('...
')
要好,是因为参数列表是一个单独的字符串。
因为函数可以返回任意个结果(见
2.4.4
),
结果的数量必须在使用它们前进行调整。
如果
p>
函数按照语句进行调用(见
2.4.6
)
,
那么它的返回列表就会被调整为零个元素,这样就舍弃了
所有
的返回值。如果调用函数时,他是一个表达式列表的最后一个元素,
那么不会做调整(除
非
调用时加了括号)。
以下是一些例子:
f() --
调整为
0
个结果
g(f(), x) -- f()
被调整成
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
预编译语句
段的时候,他的函数体也会被预编译。这样,当
p>
Lua
执行函数定义的时候,函数被
实例化
(封
装
closed
)。这个函数实例(或闭包
closure
p>
)是表达式的最终结果。同一个函数的不同的实
例可以引用不同的外
部局部变量也可以有不同的环境表。
形式参数(代表参数的
变量,简称形参)就像用实际参数值(简称实参)初始化的局部变量
一样。
parlist1
::= namelist
[`,
?
`...
?
] parlist1
::= `...
?
当调用一个函数时,实参表会调整为和形参一样的长度,除非函数是
variadic
或者
变长
参数函数(
vararg
function
)。变长参数函数在其参数列表最后有三个点
(`...
?
)
。变长参数
函数不会对参数列表进行调整;而是,它把所有的额外实参放到一个隐含的形参
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 -
可见性规则
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
指代的是外
面的变量。
由于词法范围的规则,在局部变量的范围内定义的函数可以任意访问这些变量。例如:
local counter = 0
function inc (x)
counter =
counter + x
return counter
end
内部函数使用的局部变量在函数内部称之为上值(
p>
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
)。无论错误发生在
p>
Lua
编译过程时或执行时,控制返回
C<
/p>
,然后可以做相应的
处理(比如打印一个错误)。
Lua
代码可以通过调用
error
函数来产生一个错误
(见
5.1
)
。
如果你要在
Lua
中捕获错误,
你可以使用
pcall
函数(见
5.1
)。
[
编辑
]
2.8 -
元表
< br>(
Metatables
)
Lua
中的每一个表和用户数据都可以拥有一个元表(
metatable
)。这个
元表是一个普通的
Lua
表,定义了在特定操作下原始
表和用户数据的行为。你可以通过设置一个对象的元表中的特
定字段来更改它某些方面的
行为。例如,当一个对象是一个加法的操作数时,
Lua
检查它
的元表
中的
字段是不是一个函数。如
果是,
Lua
调用它来执行加法。
我们称元表中的键(字段名,
key
)
为事件(
events
)
,值为
元方法(
metamethods
)。在
上一个例子中,
是事件,执行加法的函数是元方法。
你可以通过
set/getmetatable
函数来查询和更改一个对象的元表(见
5.1
)。
元表可以控制对象在算
术操作、比较、串连接、
索引取值中如何运行。
元表也可以定义
一个
函数当收集内存垃圾时调用。每一个操作这里
Lua
都用一个特定的键关联,称之为事件。当
Lua
对一个表或是一个用户数据执行上面中的一个操作时,它先检查元表控制的操作已经罗列在下
< br>面。
每个操作有一个相应的名称,
代表了他的含义。
p>
他们在元表中的键是由名称前加上两条下划
线;如,操作
的键是
。这些操作的语义
这里给出的
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
)。终结器允许你用外部的资源管理来调整
p>
Lua
的垃圾收集(如关闭文件,网
络或数
据库连接,或者释放你自己的内存。
用元表中包含
__gc
字段的自由
用户数据不会立即被垃圾收集器回收。而是,
Lua
把它们放<
/p>
在一个列表中。收集完毕之后,
Lua
会
对这个列表中的用户数据执行和以下函数相等的操作:
function gc_event (udata)
local h = metatable(udata).__gc
if h then
h(udata)
end
end
在每个垃圾收集过程
最后,
调用用户数据的终结器的顺序,
将按照他们在收集过程中
添加到
列表中的相反顺序进行。
也就是,
第一个被调用的终结器是和在程序中创建的最后一个用户数据
相关的那个终结器。
p>
[
编辑
]
2.9.2 -
弱表
一个弱表(
weak
table
)
是一个包含的元素是
弱引用(
weak references
)的表。垃圾收
集器会忽略弱引用。
换句话说,
如果指向一个对象的引用只有弱引用,
那么这个对象还是要被垃
圾收集器回收。
弱表可以包含弱的键,弱的值
,或者两者皆有。一个包含弱键的表允许它的键被回收,
但值
不
可以。
一个同时包含弱键和弱值的表允许键和值的回收。
无论哪
种情况,
只要键或者值中的一
个被回收了,
那么这一对键值将会从表中删除。
这个表的弱属性是由它的元表的
< br> __mode
字段控
制的。如果
__mode
字段是一个包含字符
`k
?的字符串,那么表中的键是弱键。如果
__mode
字段是一个包含字符
`v
?
的字符串,那么表中的值是弱值。
在你将表用作元表之后,
你不应该更改
__mode
字段的值。
否则,
这个
元表控制的表的弱表
行为将会不确定。
[
编辑
]
2.10 -
同步程序
Lua
支持同步程序,也称为半同步程序(
sem
i-coroutines
)
或
协同多线程(
collaborati
ve multithreading
)。
Lua
中的一个同步程序代表了一个独立的执行线程。
然而,不像在多线
程系统中的线程那样,一个同步程序只有在调用了一个
yield
(产生结果)函数才能挂起它的执
行。
p>
你可以调用
来创建一个同步程序。
它唯一的一个参数是一个函数,
代表<
/p>
同步程序的主函数。
create
函数仅仅建立一个新的同步程序然后返回一个它的句柄
(一个线程
thread
类型的对象);它不会启动该同步程序。
页脚内容
23
LTE
初始随机接入过程详解
当你第一次调用
,将
返回的线程对象作为第一个参
数传递给它,然后同步程序就启动了,从它的主函数的第一行开始。传给
的额外的参数会作为同步程序主函数的参数传递过去。
在同步程序开始执行之后,
它一直运行到
它结束或产生结果
。
一个同步程序通过两种方式结束它的运行:
正常情况下,
当它的主函数返回
(显式地或隐式
p>
的,在最后一个指令之后)时结束;异常地,如果有未保护的错误。第一各情况下,
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
这个函数以外,
LUA
库中每个函数的第一个参数都必须是一个指向这样一个状态的指针。
lua_open
则是创建一个原
始的
Lua
状态。
在调用任何
API
函数之前,你必须通过调用
lua_open
创建一个状态:
lua_State *lua_open (void);
调用
lua_open
去释放这个由
lua_close
创建的状态:
void
lua_close (lua_State *L);
这个函数销毁所有被给予
p>
Lua
状态的对象
(调用相应的垃圾收集元
方法)
并且释放那个状态
使用的所有动态内存。
在个别的平台上,
你或许不需要调用这个函数,
因为当
宿主程序结束的时
候会自然的释放所有的资源。另一方面,长时间运行的程序,像一些守
护进程或者
Web
服务器,
可能需要立
即释放那些不需要的状态资源,以避免占用太多内存。
[
编辑
]
3.2 -
堆栈和索引
Lua
利用虚拟栈来将值传给
C
< br>和从
C
获取值。
栈里面的每一个
元素都代表一个
Lua
值
(nil,
number, string,
等等
)
。
只要
Lua
调用
C
语言函数,
这个所调用的函数将得到一个新的栈,
这个栈将独立于先前的栈
以及那些仍然活跃的
C
函数的栈。
p>
这个栈最初包含了传递给
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
p>
调用
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
p>
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
中的测试一样,任何不等于
false
或者
nil
< br>的
Lua
值通过
lua_toboolean
都将返回
1
;否则
将返回
0
。当然,如果是一个无效索引,也将返回
0
。(如果你只想接受真实的布尔值,使用
lua_isboolean
去测试值的类型。)
lua_tonumber
将索引指向的
Lua
值转换成一个数字(默认情况下,
lua_Numb
er
是
double
类型)。
p>
Lua
值必须是一个数字或者可转化为数字的字符串(见
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 *
p>
)。这个值必须是一个
线程;否则;
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
会关心内存
p>
分配问题(和内存释放问题,通过垃圾收集机制)。
转换受到限制。这里没有标志、宽
度或精度。转换操作的修饰符可
以是简单的
`%%
?(在字符串中插入一个
`%
?),
`%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
语句段
你可以通过
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
(只弹出一个键,
不压入任何键
-
值对)
。
使用一个
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
,除非你知道这个键确实是一个
字符串。再次调用
p>
lua_tostring
改变了所给索引指向的值;这使
lua_next
的调用发生混乱。
3.12 -
环境变量操作
(
Mani***ting
Environments
)
所有的全局变量保存在普通的
Lua
表中,叫做环境变量。初始的环境变量被称作全局环境
变量。这张表总是在
LUA_GLOBALSINDEX
这个伪索引处。
要访问或改变全局
变量的值,
你可以对环境变量表使用常规的表操作。
举个例子,
存取一个
全局变量的值:
页脚内容
32