LUA参考手册

合集下载
  1. 1、下载文档前请自行甄别文档内容的完整性,平台不提供额外的编辑、内容补充、找答案等附加服务。
  2. 2、"仅部分预览"的文档,不可在线预览部分如存在完整性等问题,可反馈申请退款(可完整预览的文档不适用该条件!)。
  3. 3、如文档侵犯您的权益,请联系客服反馈,我们会尽快为您处理(人工客服工作时间:9:00-18:30)。

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 - 语句式函数调用
页脚内容1
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 - 同步程序
3 3 - 应用程序接口
3.1 3.2 - 堆栈和索引
3.2 3.3 - 堆栈操作
页脚内容2
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语句段
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 - 钩子
页脚内容3
5 5 - 标准库
5.1 5.1 - 基本函数
5.2 5.2 - Coroutine Mani***tion
5.3 5.3 - String Mani***tion
5.3.1 string.byte (s [, i])
5.3.2 string.char (i1, i2, ...)
5.3.3 string.dump (function)
5.3.4 string.find (s, pattern [, init [, plain]])
5.3.5 string.len (s)
5.3.6 string.lower (s)
5.3.7 string.rep (s, n)
5.3.8 string.sub (s, i [, j])
5.3.9 string.upper (s)
5.4 5.4 - Table Mani***tion
5.5 5.5 - Mathematical Functions
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 致谢
页脚内容4
8 与以前版本的不兼容性
8.1 语言上的变动
8.2 库的变更
8.3 API 上的改动
9 Lua 完整语法参考
1 - 绪论
Lua是一种为支持有数据描述机制的一般过程式编程语言而设计的扩展编程语言。

它同样可以对面向对象语言、函数式程序设计(Functional Programming,如Lisp)以及数据驱动编程(data-driven programm ing)提供很好的支持。

它的目标是被用作一种强大的、轻型的配置语言。

Lua目前已经被实现为一个扩展库,是用clean C (ANSI C/C++的一个通用子集)编写的。

作为一个扩展语言,Lua没有"Main"函数的概念:它仅仅是嵌入一个宿主程序进行工作,可以称之为嵌入式编程或者简单的说是宿主编程。

这个宿主程序可以调用函数来执行Lua的代码片断,可以设置和读取Lua的变量,可以注册C函数让Lua代码调用。

Lua的能力可以扩展到更大范围,在不同的领域内,这样就在同样的语法框架下创建了你自定义的编程语言。

Lua的发行版包括一个独立的嵌入式程序,lua,他使用Lua的扩展库来提供一个完全的Lua解释器。

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

手册中描述的实现在Lua的官方网站可以找到,。

如果需要知道Lua设计背后的一些决定和讨论,可以参考以下论文,它们都可以在Lua的网站上找到。

R. Ierusalimschy, L. H. de Figueiredo, and W. Celes. Lua---an extensible extension langu age. 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 an
d Hardwar
e (1994) 273-283.
页脚内容5
L. H. de Figueiredo, R. Ierusalimschy, and W. Celes. Lua: an extensible embedded lang uage. Dr. Dobb's Journal 21 #12 (Dec 1996) 26-33.
R. Ierusalimschy, L. H. de Figueiredo, and W. Celes. The evolution of an extension lang uage: a history of Lua, Proceedings of V Brazilian Symposium on Programming Languages
(2001) B-14-B-28.
Lua在葡萄牙语中的意思是“月亮”,发音是LOO-ah。

2 - 语言
这一章将描述Lua的词法、语法和语义结构。

换句话说,这一章会讲什么标记是合法的,他们是如何组合的,以及他们的组合是什么含义。

语言结构会使用常用的扩展BNF范式来解释,如{a} 表示0或多个a,表示a是可选的(0个或1个)。

非终端字体(不能显示的)用斜体表示,关键字是粗体,其他终端符号用typewriter(等宽)字体,并用单引号引出。

2.1 - 词法约定
Lua中的标识符(Identifiers)可以是任意的数字、字符和下划线“_”,但不能以数字开头。

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

(字符的具体定义要根据系统的地区设置:任何区域设置可以认同的字母表中的字母都可以用在标识符中。


下面的关键字(keywords)为保留关键字不可以作为标识符出现:
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 是两个不一样的、但都合法的标识符。

习惯上来说,以下划线开始且后面跟着大写字母的标识符(例如_VERSION) 是为Lua内部变量所保留的。

页脚内容6
下面的字符(串)是其他的一些标记:
+ - * / ^ =
~= <= >= < > ==
( ) { } [ ]
; : , . .. ...
字符串(Literal strings)以单引号或者双引号定界,同时可以包含以下C语言风格的转义字符:
\a --- 铃声(bell)
\b --- 回退(backspace)
\f --- form feed
\n --- 新行(newline)
\r --- 回车(carriage return)
\t --- 水平制表符(horizontal tab)
\v --- 垂直制表符(vertical tab)
\\ --- 反斜杠(backslash)
\" --- 双引号(quotation mark)
\' --- 单引号(apostrophe)
\[ --- 左方括号(left square bracket)
\] --- 右方括号(right square bracket)
另外,一个`\newline´(一个反斜杠加上一个真正的换行符)会导致字符串内的分行。

字符串中的字符也可以使用转义字符`\ddd´通过数字值来指定。

ddd 是最多为3个十进制数字的序列。

Lua中的字符串也可以包含8进制数字,包括嵌入零,它可以表示为`\0´。

页脚内容7
字符串也可以用双方括号来定界[[ ···]]。

这种括号方式的语法,字符串可以跨越多行,也可以包含嵌套的,同时不会转义任何序列。

方便起见,当开始的`[[´后面紧跟着一个换行符的话,这个换行符不会包括在字符串内。

举个例子:在一个使用ASCII编码(其中`a´的编码是97,换行符是10,字符`1´是49)的系统中,以下四种格式得到的都是同一个字符串:
1."alo\n123\""
2.'\97lo\10\04923"'
3.[[alo
4.123"]]
4.[[
5.alo
6.123"]]
数值常量(Numerical constants)可以有一个可选的底数部分和一个可选的指数部分。

以下是有效的数值常量:
3 3.0 3.1416 314.16e-2 0.31416E1
注释(Comments)可以在任何地方出现,必须在最前面加上双减号(--)。

如果紧接着-- 的文本不是[[,那么会认为是一个短注释(short comment),这一行往后到行尾都是注释。

否则,会认为是一个常注释(long comment),注释直到相应的]]结束。

长注释可以跨越多行,同时可以包含嵌套的[[ ···]]括号对。

为了方便起见,文件的第一行如果是以#开始,这个机制允许Lua在Unix系统中用做一个脚本解释器(见6)。

2.2 - 值和类型
Lua是一种动态类型语言(dynamically typed language)。

这意味着变量是没有类型的;只有值才有。

语言中没有类型定义。

所有的值都包含他自身的类型。

页脚内容8
Lua中有八种基本类型:nil, boolean, number, string, function, userdata, thread 和table。

Nil 空类型只对应nil值,他的属性和其他任何值都有区别;通常它代表没有有效的值。

Boolean 布尔类型有两种不同的值false and true。

在Lua中,nil and false 代表成假条件;其他任何值都代表成真条件。

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

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

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

Lua 字符串可以包含8位字符,包括嵌入的('\0') (见2.1)。

函数是Lua中的第一类值(first-class values)。

也就是说函数可以保存在变量中,当作参数传递给其他函数,或者被当作结果返回。

Lua可以调用(和处理)Lua写的函数和C写的函数(见2.5.7)。

用户数据类型(userdata)提供了让任意C数据储存在Lua变量中的功能。

这种类型直接对应着一块内存,Lua中也没有任何预先定义的操作,除了赋值和一致性比较。

然而,通过使用元表(metatables),程序员可以定义处理userdata的操作。

(见2.8)。

Userdata 值不能在Lua中建立或者修改,只能通过C API。

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

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

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

而且,tables 可以是互异的(heterogeneous),他们可以保存任何类型的值(除了nil)。

Tables 是L ua中唯一的数据结构机制;他们可以用来表示一般数组,特征表,集合,记录,图,树等等。

如果要表示记录,Lua使用字段名作为索引。

语言支持 这种比较优美的表示方式,还有a["name"]。

在Lua中有几种建立表的简便方法(见2.5.6)。

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

特别需要注意地是,由于函数是第一型的值,表字段也可以包含函数。

这样表也可以支持方法(methods)(见2.5.8)。

表,函数,和用户数据类型的值都是对象(objects):变量不会包含他们的实际值,只是一个他们的引用(references)。

赋值,参数传递和函数返回只是操作这些值的引用,这些操作不会暗含任何拷贝。

库函数type 返回一个字符串描述给出值所表示的类型(见5.1)。

2.2.1 - 类型转换
Lua提供运行时的数字和字符串值得自动转换。

任何对字符串的算术操作都会现尝试把字符串转换成数字,使用一般规则转换。

反过来,当一个数值用在需要字符串的地方时,数字会自动转换成字符串,
页脚内容9
遵循一种合理的格式。

如果要指定数值如何转换成字符串,请使用字符串库中的format 函数(见5.3)。

2.3 - 变量
变量是储存值的地方。

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

一个名称可以表示全局变量或局部变量(或者一个函数的正式参数,一种局部变量的特殊形式):
var ::= Name
Lua假设变量是全局变量,除非明确地用local进行声明(见2.4.7)。

局部变量有词义范围(lexically scoped):局部变量可以被在它们范围内的函数自由访问(见2.6)。

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

方括号用于对表进行检索:
var ::= prefixexp `[´exp `]´
第一个表达式(prefixexp)结果必须是表;第二个表达式(exp) 识别表中一个特定条目。

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

语法是var["NAME"] 的较好形式:
var ::= prefixexp `.´Name
访问全局变量和表字段的实质可以通过元表进行改变。

对索引变量t的访问等同于调用gettable_ev ent(t,i)。

(关于gettable_event 的完整描述见2.8。

这个函数并没有在Lua中定义,也无法调用。

我们在这里仅仅用来解释原理)。

所有的全局变量存在一个普通的Lua表中,称之为环境变量表(environment tables)或简称环境(en vironments)。

由C写的并导入到Lua中的函数(C 函数) 全部共享一个通用全局环境(global environme nt)。

Lua写的每个函数(a Lua 函数) 都有一个它自己的环境的引用,这样这个函数中的所有的全局变
页脚内容10
量都会指向这个环境变量表。

当新创建一个函数时,它会继承创建它的函数的环境。

要改变或者获得Lua函数的环境表,可以调用setfenv or getfenv (见5.1)。

访问全局变量x 等同于_env.x,又等同于
gettable_event(_env, "x")
_env 是运行的函数的环境。

(_env 变量并没有在Lua中定义。

我们这里仅仅用来解释原理)
2.4 - 语句
Lua支持一种很通俗的语句集,和Pascal或者C中的很相似。

他包括赋值,控制结构,过程调用,表构造和变量声明。

2.4.1 - 语句段
Lua执行的最小单元称之为一个段(chunk)。

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

每一个语句后面都可以加上一个分号(可选):
chunk ::= {stat [`;´]}
Lua将语句段作为一个匿名函数(见2.5.8)的本体进行处理。

这样,语句段可以定义局部变量或者返回值。

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

当语句段被执行时,他首先被预编译成虚拟机使用的字节码,然后虚拟机用一个解释器执行被编译的代码。

语句段也可以被预编译为二进制代码;详情参看luac 程序。

源代码和编译形态可以互相转换;Lua自动监测文件类型然后作相应操作。

2.4.2 - 语句块
一个语句块是一系列语句;从语句构成上来看,语句块等同于语句段:
页脚内容11
block ::= chunk
一个语句块可以明确定界来替换单个语句:
stat ::= do block end
显式语句块可以很好地控制变量的声明范围。

显示语句块有时也常会在另一个语句块的中间添加retur n 或break 语句(见2.4.4)。

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
设置a[3] 为20,但不影响a[4]。

因为在a中的i 在赋值为4之前是等于3。

同样的,下面这行:x, y = y, x
页脚内容12
可以交换x 和y 的值。

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

对一个索引变量的赋值t= val 等同于set table_event(t,i,val)。

(settable_event详细介绍参看2.8 ,Lua中并未定义该函数,他也无法直接调用。

我们这里只是用它来进行解释。


对全局变量的赋值x = val 等同于赋值语句_env.x = val,像前面也等同于:
settable_event(_env, "x", val)
_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 blo ck} [else block] end
Lua也有for 语句,有两种格式(见2.4.5)。

控制结构的条件表达式exp 可以返回任意值。

false 和nil 都表示假。

所有其他的值都认为是真(特别要说明的:数字0和空字符串也表示真)。

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

函数和语句段都可以返回多个值,所以return 语句的语法为:
stat ::= return [explist1]
break 语句可以用来终止while, repeat 或者for 循环的执行,直接跳到循环后面的语句。

stat ::= break
break 结束最里面的一个循环。

页脚内容13
由于语法的原因,return 和break 语句只能作为语句块的最后一个语句。

如果确实需要在语句块的中间使用return 或者break,需要使用一个显示语句块:`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
注意:
页脚内容14
三种控制表达式只会被计算一次,在循环开始之前。

他们的结果必须是数值。

_limit 和_step 是不可见的变量。

这里只是为了进行解释。

如果你在程序块内给var 赋值,结果行为将会不确定。

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

你可以使用break 来退出for 循环。

循环变量var 是局部变量;你不可以在for 循环结束之后继续使用。

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

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

每一个迭代过程,它调用迭代函数来产生新的值,直到新的值是nil 。

一般形式for 循环有如下语法:
stat ::= for Name {`,´Name} in explist1 do block end
一个这样的for 语句
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
页脚内容15
end
注意
explist 只会计算一次。

他的结果是一个迭代函数,一个状态,和给第一个迭代变量的一个初始值。

_f 和_s 是不可见的变量。

这里只是用来进行解释说明。

如果你在语句块中给var_1 赋值,那么行为就会变得不确定。

你可以使用break 来退出for 循环。

循环变量var_i 是局部变量;你不可以在for 循环结束之后继续使用。

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

2.4.6 - 语句式函数调用
如果要忽略可能的影响,函数调用可以按照语句执行:
stat ::= functioncall
I在这里,所有的返回值都会被忽略。

函数调用将在2.5.7 详细解释。

2.4.7 - 局部变量声明
局部变量可以在语句块中任何地方声明。

声明时也可以添加一个初始赋值:
stat ::= local namelist [`=´explist1] namelist ::= Name {`,´Name}
如果出现初始赋值,他的语法和多重赋值语句一样(见2.4.3)。

否则,所有的变量都会初始化为nil。

一个语句段也是一个语句块(见2.4.1),所以语句段之内的任何显式语句块之外也可以声明局部变量。

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

局部变量的可见规则会在2.6解释。

页脚内容16
2.5 - 表达式
Lua中有以下几种基本表达式:
exp ::= prefixexp exp ::= nil | false | true exp ::= Number exp ::= Literal exp ::= function exp ::= tableconst ructor prefixexp ::= var | functioncall | `(´exp `)´
数字和字符串已经在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)。

标准数学库定义了函数__pow,给出了指数运算的定义(见5.5)。

.5.2 - 关系运算符
Lua中的关系运算符有
== ~= < > <= >=
这些运算只会产生false 或true值。

等于(==) 先比较操作数的类型。

如果类型不一样,结果便是false。

否则,再比较操作数的值。

对象(表,用户数据,线程,和函数)是按照引用进行比较:只有两个对象是同一个对象的时候,才认为是相等。

每次你创建一个新的对象(表,用户数据,或者是函数)。

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

你可以用"eq"元方法改变Lua比较表的方式(见2.8)。

页脚内容17
2.2.1 的转换规则不适用于相等比较。

这样," "0"==0 结果是false ,同样t[0] 和t["0"] 给出的是表中不同的字段。

而操作符~= 是等于(==) 的相反的操作。

T操作符的执行顺序如下。

如果两个参数都是数字,那么它们就直接进行比较。

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

否则,Lua尝试调用"lt"或者"le" 元方法(见2. 8)。

[编辑]
2.5.3 - 逻辑运算符
Lua中的逻辑运算符是:
and or not
和控制结构一样(见2.4.4),所有的逻辑操作符认为false 和nil 都是假,其他的值都是真。

not 操作符总是返回false 或true。

合取运算and 如果第一个参数是false 或者nil 则返回第一个参数;否则and 返回第二个参数。

析取运算or 如果第一个参数不是nil 或false 则返回第一个参数,否则or 返回第二个参数。

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

例如:
10 or error() -> 10
nil or "a" -> "a"
nil and 10 -> nil
false and error() -> false
false and nil -> false
false or nil -> nil
10 and 20 -> 20
页脚内容18
[编辑]
2.5.4 - 串联接
在Lua中字符串连接操作符是两个点(`..´)。

如果两边的操作数都是字符或者数字,他们就都会按照2.
2.1的规则被转换成字符串。

否则,将调用"concat" 元方法(见2.8)。

[编辑]
2.5.5 - 优先级
Lua中的操作符的优先级如下表所示,从低到高优先级:
or
and
< > <= >= ~= ==
..
+ -
* /
not - (unary)
^
表达式中,你可以使用括号来改变优先顺序。

串联接符(`..´) 和指数符(`^´) 都是右结合的。

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

[编辑]
2.5.6 - 表构造器
表构造器是创建表的表达式。

当计算构造器的时候,就会创建一个新的表。

构造器可以用来创建空的表,或者创建表并初始化一些字段。

一般的语法如下:
页脚内容19
tableconstructor ::= `{´[fieldlist] `}´fieldlist ::= field {fieldsep field} [fieldsep] field ::= `[´exp `]´`=´exp | Name `=´exp | exp fieldsep ::= `,´| `;´
[exp1] = exp2 形式的每一个添加到新表中的字段条目以exp1 为键并以exp2 为值。

name = exp 形式的字段,等同于["name"] = exp。

最后,exp 形式的字段等同于= exp 其中i 是连续的整数,从1开始。

其它格式的字段不会影响它的计数。

例如:
a = {[f(1)] = g; "x", "y"; x = 1, f(x), [30] = 23; 45}
等同于:
do
local temp = {}
temp[f(1)] = g
temp[1] = "x" -- 1st exp
temp[2] = "y" -- 2nd exp
temp.x = 1 -- temp["x"] = 1
temp[3] = f(x) -- 3rd exp
temp[30] = 23
temp[4] = 45 -- 4th exp
a = temp
end
如果列表中最后一个字段的形式是exp 同时表达式又是一个函数调用,那么调用返回的所有值会依次进入列表(见2.5.7)。

如果要避免这种情况,在函数调用两边加上括号(见2.5)。

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

页脚内容20
[编辑]
2.5.7 - 函数调用
Lua中的一个函数调用有如下语法:
functioncall ::= prefixexp args
在函数调用中,首先会计算prefixexp 和args 。

如果prefixexp 的值是function 类型,那么那个函数就会被调用,同时使用给出的参数。

否则,他的"call" 元方法就会被调用,第一个参数是prefixexp 的值,接下来是原来的调用参数(见2.8)。

形式
functioncall ::= prefixexp `:´Name args
可以用来调用“方法”("methods")。

调用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() 被调整成1个结果
g(x, f()) -- g 获得x 加上f()返回的所有值
页脚内容21
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语法自由格式的一个例外,你不能在函数调用的`(´前面加入一个换行。

这个限制可以避免语言中的一些二义性。

如果你写:
a = f
(g).x(a)
Lua会读作a = f(g).x(a)。

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

如果你实际上想调用f,你就必须删除(g) 前面的换行。

return functioncall 的调用格式称之为尾部调用(tail call)。

Lua实现了proper tail calls;在一个尾部调用中,被调用的函数将会重新使用调用程序的栈。

因此,程序执行对嵌套尾部调用的次数没有任何限制。

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

注意尾部调用只有在特殊的语法中才能出现,也就是return 只有一个函数调用作为参数,这种语法保证了调用函数确切返回被调用函数的返回值。

所以,下面的例子都不是尾部调用:
return (f(x)) -- results adjusted to 1
页脚内容22
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 funcname ::= Name {`.´Name} [`:´Name]
语句
function f () ... end
会被翻译为
f = function () ... end
语句
function t.a.b.c.f () ... end
会被翻译为
t.a.b.c.f = function () ... end
语句
local function f () ... end
页脚内容23
会被翻译为
local f; f = function () ... end
一个函数定义是一个可执行的表达式,他的类型为函数(function)。

当Lua预编译语句段的时候,他的函数体也会被预编译。

这样,当Lua执行函数定义的时候,函数被实例化(封装closed)。

这个函数实例(或闭包closure)是表达式的最终结果。

同一个函数的不同的实例可以引用不同的外部局部变量也可以有不同的环境表。

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

parlist1 ::= namelist [`,´`...´] parlist1 ::= `...´
当调用一个函数时,实参表会调整为和形参一样的长度,除非函数是variadic 或者变长参数函数(va rarg 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
页脚内容24。

相关文档
最新文档