> > 技术-技术专区 > Lua 5.0 参考手册
 

Lua 5.0 参考手册

2011-01-26 18:24:27
 

1 - 绪论
Lua是一种为支持有数据描述机制的一般过程式编程语言而设计的扩展编程语言。它同样可以对面向对象语言、函数式程序设计(Functional Programming,如Lisp)以及数据驱动编程(data-driven programming)提供很好的支持。它的目标是被用作一种强大的、轻型的配置语言。Lua目前已经被实现为一个扩展库,是用clean C (ANSI C/C++的一个通用子集)编写的。

作为一个扩展语言,Lua没有"Main"函数的概念:它仅仅是嵌入一个宿主程序进行工作,可以称之为 嵌入式编程 或者简单的说是 宿主编程。这个宿主程序可以调用函数来执行Lua的代码片断,可以设置和读取Lua的变量,可以注册C函数让Lua代码调用。Lua的能力可以扩展到更大范围,在不同的领域内,这样就在同样的语法框架下创建了你自定义的编程语言。

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

Lua是自由软件,通常不提供任何担保,如它的版权说明中叙述的那样。 手册中描述的实现在Lua的官方网站可以找到,www.lua.org

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

R. Ierusalimschy, L. H. de Figueiredo, and W. Celes. Lua---an extensible extension language. Software: Practice & Experience26 #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 (1994) 273-283.
L. H. de Figueiredo, R. Ierusalimschy, and W. Celes. Lua: an extensible embedded language. Dr. Dobb's Journal21 #12 (Dec 1996) 26-33.
R. Ierusalimschy, L. H. de Figueiredo, and W. Celes. The evolution of an extension language: 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] 表示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内部变量所保留的。

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

       +     -     *     /     ^     =
       ~=    <=    >=    <     >     ==
       (     )     {     }     [     ]
       ;     :     ,     .     ..    ...
字符串(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进制数字,包括嵌入零,它可以表示为 `´。

字符串也可以用双方括号来定界[[ · · · ]]。这种括号方式的语法,字符串可以跨越多行,也可以包含嵌套的,同时不会转义任何序列。方便起见,当开始的 `[[´ 后面紧跟着一个换行符的话,这个换行符不会包括在字符串内。举个例子:在一个使用ASCII编码(其中`a´ 的编码是 97,换行符是 10,字符`1´ 是 49)的系统中,以下四种格式得到的都是同一个字符串:

      (1)   "alon123""
      (2)   '97lo104923"'
      (3)   [[alo
            123"]]
      (4)   [[
            alo
            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)。这意味着变量是没有类型的;只有值才有。语言中没有类型定义。所有的值都包含他自身的类型。

Lua中有八种基本类型:nil, boolean, number, string, function, userdata, thread 和 table。 Nil 空类型只对应 nil值,他的属性和其他任何值都有区别;通常它代表没有有效的值。 Boolean 布尔类型有两种不同的值 false and true。在Lua中, nil and false 代表成假条件;其他任何值都代表成真条件。 Number 数字类型表示实数(双精度浮点数)。(构建Lua解释器时也可以很容易地用其他内部的表示方式表示数字,如单精度浮点数或者长整型)。 String 字符串类型表示一个字符的序列。Lua 字符串可以包含8位字符,包括嵌入的 ('') (见 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 是Lua中唯一的数据结构机制;他们可以用来表示一般数组,特征表,集合,记录,图,树等等。如果要表示记录,Lua使用字段名作为索引。语言支持 a.name 这种比较优美的表示方式,还有 a["name"]。在Lua中有几种建立表的简便方法 (见 2.5.6)。

就像索引一样,表字段的值也可以是任何类型(除了 nil)。特别需要注意地是,由于函数是第一型的值,表字段也可以包含函数。这样表也可以支持 方法(methods) (见 2.5.8)。

表,函数,和用户数据类型的值都是 对象(objects):变量不会包含他们的实际值,只是一个他们的引用(references)。 赋值,参数传递和函数返回只是操作这些值的引用,这些操作不会暗含任何拷贝。

库函数 type 返回一个字符串描述给出值所表示的类型 (见 5.1)。
2.2.1 - 类型转换
Lua提供运行时的数字和字符串值得自动转换。任何对字符串的算术操作都会现尝试把字符串转换成数字,使用一般规则转换。反过来,当一个数值用在需要字符串的地方时,数字会自动转换成字符串,遵循一种合理的格式。如果要指定数值如何转换成字符串,请使用字符串库中的 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["NAME"] 的较好形式:

 var ::= prefixexp `.´ Name

访问全局变量和表字段的实质可以通过元表进行改变。对索引变量 t[i] 的访问等同于调用 gettable_event(t,i)。(关于 gettable_event 的完整描述见 2.8。这个函数并没有在Lua中定义,也无法调用。我们在这里仅仅用来解释原理)。

所有的全局变量存在一个普?ǖ腖ua表中,称之为 环境变量表(environment tables) 或简称 环境(environments)。由C写的并导入到Lua中的函数 (C 函数) 全部共享一个通用 全局环境(global environment)。Lua写的每个函数 (a Lua 函数) 都有一个它自己的环境的引用,这样这个函数中的所有的全局变量都会指向这个环境变量表。当新创建一个函数时,它会继承创建它的函数的环境。要改变或者获得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 - 语句块
一个语句块是一系列语句;从语句构成上来看,语句块等同于语句段:

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

 stat ::= do block end显式语句块可以很好地控制变量的声明范围。显示语句块有时也常会在另一个语句块的中间添加 return 或 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] = i+1, 20
设置 a[3] 为 20,但不影响 a[4]。因为在 a[i] 中的 i 在赋值为4之前是等于3。同样的,下面这行:

       x, y = y, x

可以交换 x 和 y 的值。

对全局变量和表字段的赋值可以看作是通过元表进行的。对一个索引变量的赋值 t[i] = val 等同于 settable_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 block} [else block] endLua也有 for 语句,有两种格式 (见 2.4.5)。

控制结构的条件表达式 exp 可以返回任意值。false 和 nil 都表示假。所有其他的值都认为是真(特别要说明的:数字0和空字符串也表示真)。

语句 return 用来从函数或者是语句段中返回一个值。函数和语句段都可以返回多个值,所以 return 语句的语法为:

 stat ::= return [explist1]
break 语句可以用来终止while, repeat 或者 for 循环的执行,直接跳到循环后面的语句。

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

由于语法的原因, 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 endblock 语句块根据 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 语句

       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 - 局部变量声明
局部变量可以在语句块中任何地方声明。声明时也可以添加一个初始赋值:

 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 `)´
数字和字符串已经在 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)。
2.5.2 - 关系运算符
Lua中的关系运算符有

       ==    ~=    <     >     <=    >=
这些运算只会产生 false 或 true值。

等于 (==) 先比较操作数的类型。如果类型不一样,结果便是 false。否则,再比较操作数的值。对象(表,用户数据,线程,和函数)是按照引用进行比较:只有两个对象是同一个对象的时候,才认为是相等。每次你创建一个新的对象(表,用户数据,或者是函数)。这个新的对象将不同于前面存在的任何对象。

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

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


2.5.4 - 串联接
在Lua中字符串连接操作符是两个点 (`..´)。如果两边的操作数都是字符或者数字,他们就都会按照 2.2.1的规则被转换成字符串。否则,将调用 "concat" 元方法(见 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 为值。name = exp 形式的字段,等同于 ["name"] = exp。最后,exp 形式的字段等同于 [i] = 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)。

字段列表可以有一个结尾的分隔符,这个对由机器生成的列表十分方便。
2.5.7 - 函数调用
Lua中的一个函数调用有如下语法:

 functioncall ::= prefixexp args
在函数调用中,首先会计算 prefixexp 和 args 。如果 prefixexp 的值是 function 类型,那么那个函数就会被调用,同时使用给出的参数。否则,他的 "call" 元方法就会被调用,第一个参数是 prefixexp 的值,接下来是原来的调用参数(见 2.8)。

形式

 functioncall ::= prefixexp `:´ Name args
可以用来调用“方法”("methods")。调用 v:name(...) 语法上比 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()返回的所有值
       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
  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 ::= localfunction 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
会被翻译为

       local f; f = function () ... end
一个函数定义是一个可执行的表达式,他的类型为 函数(function) 。当Lua预编译语句段的时候,他的函数体也会被预编译。这样,当Lua执行函数定义的时候,函数被 实例化 (封装 closed)。这个函数实例(或闭包 closure)是表达式的最终结果。同一个函数的不同的实例可以引用不同的外部局部变量也可以有不同的环境表。

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

 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. 。这样,语句:

       function t.a.b.c:f (...) ... end
相对以下是较好的形式:

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


2.6 - 可见性规则
Lua是一个有词法范围的语言。变量的范围从声明语句后的第一个语句开始到包含声明的最内部的语句块为止。例如:

  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
内部函数使用的局部变量在函数内部称之为 上值(upvalue),或者 外局部变量(external local variable)。

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

  a = {}
  local x = 20
  for i=1,10 do
    local y = 0
    a[i] = function () y=y+1; return x+y end
  end
循环产生了十个闭包(也就是,十个匿名函数的实例)。每个闭包使用不同的 y 变量,但他们共享同一个 x 变量。
2.7 - 错误处理
因为Lua是一个扩展语言,所有的Lua动作都是从宿主程序中调用Lua库中函数的C代码开始的(见 3.15)。无论错误发生在Lua编译过程时或执行时,控制返回C,然后可以做相应的处理(比如打印一个错误)。

Lua代码可以通过调用error函数来产生一个错误(见 5.1)。如果你要在Lua中捕获错误,你可以使用 pcall 函数(见 5.1)。
2.8 - 元表 (Metatables)
Lua中的每一个表和用户数据都可以拥有一个 元表(metatable)。这个 元表 是一个普通的Lua表,定义了在特定操作下原始表和用户数据的行为。你可以通过设置一个对象的元表中的特定字段来更改它某些方面的行为。例如,当一个对象是一个加法的操作数时,Lua检查它的元表中的 "__add" 字段是不是一个函数。如果是,Lua调用它来执行加法。

我们称元表中的键(字段名,key)为 事件(events) ,值为 元方法(metamethods)。在上一个例子中, "add" 是事件,执行加法的函数是元方法。

你可以通过 set/getmetatable 函数来查询和更改一个对象的元表(见 5.1)。

元表可以控制对象在算术操作、比较、串连接、索引取值中如何运行。元表也可以定义一个函数当收集内存垃圾时调用。每一个操作这里Lua都用一个特定的键关联,称之为事件。当Lua对一个表或是一个用户数据执行上面中的一个操作时,它先检查元表控制的操作已经罗列在下面。每个操作有一个相应的名称,代表了他的含义。他们在元表中的键是由名称前加上两条下划线;如,操作 "add" 的键是 "__add"。这些操作的语义

这里给出的Lua代码仅仅是说明性的;真正的行为是硬编码在解释器中的,比下面的的模拟的效率要高很多。描述中用到的函数 (rawget, tonumber, 等等) 在 5.1 中会对他们进行描述。特别地,要获得一个给定对象的元方法,我们使用这个表达式:

  metatable(obj)[event]
这个要读作:

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

也就是,访问元方法时不会调用其它元方法,同时调用没有元表的对象不会出错(它返回一个 nil值)。

"add": + 加法操作。

下面的 getbinhandler 函数定义了Lua如何给一个二元操作选择一个处理器。首先,Lua尝试第一个操作数。如果它的类型没有定义这个操作的处理器,那么然后Lua尝试第二个操作数。

 function getbinhandler (op1, op2, event)
   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, "__add")
     if h then
       -- call the handler with both operands
       return h(op1, op2)
     else  -- no handler available: default behavior
       error("...")
     end
   end
 end
"sub": - 操作。行为方式类似 "add" 操作。
"mul": * 操作。行为方式类似 "add" 操作。
"div": / 操作。行为方式类似 "add" 操作。
"pow": ^ (指数) 操作

 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, "__pow")
     if h then
       -- call the handler with both operands
       return h(op1, op2)
     else  -- no handler available: default behavior
       error("...")
     end
   end
  end
"unm": 一元取负 - 操作。

 function unm_event (op)
   local o = tonumber(op)
   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
"concat": .. (串连接)操作。

 function concat_event (op1, op2)
   if (type(op1) == "string" or type(op1) == "number") and
      (type(op2) == "string" or type(op2) == "number") then
     return op1 .. op2  -- primitive string concatenation
   else
     local h = getbinhandler(op1, op2, "__concat")
     if h then
       return h(op1, op2)
     else
       error("...")
     end
   end
 end
"eq": == 操作。函数 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)
   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, "__eq")
   if h then
     return h(op1, op2)
   else
     return false
   end
 end
a ~= b is equivalent to not (a == b).

"lt": < 操作。

 function lt_event (op1, op2)
   if type(op1) == "number" and type(op2) == "number" then
     return op1 < op2   -- numeric comparison
   elseif type(op1) == "string" and type(op2) == "string" then
     return op1 < op2   -- lexicographic comparison
   else
     local h = getcomphandler(op1, op2, "__lt")
     if h then
       return h(op1, op2)
     else
       error("...");
     end
   end
 end

a > b is equivalent to b < a.

"le": <= 操作。

 function le_event (op1, op2)
   if type(op1) == "number" and type(op2) == "number" then
     return op1 <= op2   -- numeric comparison
   elseif type(op1) == "string" and type(op2) == "string" then
     return op1 <= op2   -- lexicographic comparison
   else
     local h = getcomphandler(op1, op2, "__le")
     if h then
       return h(op1, op2)
     else
       h = getcomphandler(op1, op2, "__lt")
       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 "le" metamethod, Lua tries the "lt", assuming that a <= b is equivalent to not (b < a).

"index": 通过索引访问 table[key]。

 function gettable_event (table, key)
   local h
   if type(table) == "table" then
     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) == "function" then
     return h(table, key)      -- call the handler
   else return h[key]          -- or repeat operation on it
 end
"newindex": 给表的索引赋值 table[key] = value。

 function settable_event (table, key, value)
   local h
   if type(table) == "table" then
     local v = rawget(table, key)
     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) == "function" then
     return h(table, key,value)    -- call the handler
   else h[key] = value             -- or repeat operation on it
 end

"call": 当Lua调用某个值时调用。

 function function_event (func, ...)
   if type(func) == "function" then
     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中所有的对象都是自动管理的目标:表,用户数据,函数,线程,和字符串。Lua使用两个数字控制垃圾收集循环。一个数字表示Lua使用的动态内存的字节数,另一个是阀值。当内存字节数到达阀值时,Lua就运行垃圾收集器,来释放死对象的空间。一旦字节计数器被调整,那么阀值就会被设为字节计数器新值的两倍。

通过C API,你可以查询和更改阀值(见 3.7)。将阀值设为零时会强制立刻进行垃圾收集,同时把他设为足够大就可以停止垃圾收集。仅使用Lua代码中的 gcinfo 和 collectgarbage 函数 (见 5.1)可以获得一定程度上对垃圾收集循环的控制。
2.9.1 - 垃圾收集元方法 (Garbage-Collection Metamethods)
使用 C API,你可以对用户数据设置一个垃圾收集元方法(见 2.8)。这些元方法也称为 终结器(finalizers)。终结器允许你用外部的资源管理来调整Lua的垃圾收集(如关闭文件,网络或数据库连接,或者释放你自己的内存。

用元表中包含 __gc 字段的自由用户数据不会立即被垃圾收集器回收。而是,Lua把它们放在一个列表中。收集完毕之后,Lua会对这个列表中的用户数据执行和以下函数相等的操作:

 function gc_event (udata)
   local h = metatable(udata).__gc
   if h then
     h(udata)
   end
 end
在每个垃圾收集过程最后,调用用户数据的终结器的顺序,将按照他们在收集过程中添加到列表中的相反顺序进行。也就是,第一个被调用的终结器是和在程序中创建的最后一个用户数据相关的那个终结器。
2.9.2 - 弱表
一个 弱表(weak table) 是一个包含的元素是 弱引用(weak references)的表。垃圾收集器会忽略弱引用。换句话说,如果指向一个对象的引用只有弱引用,那么这个对象还是要被垃圾收集器回收。

弱表可以包含弱的键,弱的值,或者两者皆有。一个包含弱键的表允许它的键被回收,但值不可以。一个同时包含弱键和弱值的表允许键和值的回收。无论哪种情况,只要键或者值中的一个被回收了,那么这一对键值将会从表中删除。这个表的弱属性是由它的元表的 __mode 字段控制的。如果 __mode 字段是一个包含字符 `k´的字符串,那么表中的键是弱键。如果 __mode 字段是一个包含字符 `v´ 的字符串,那么表中的值是弱值。

在你将表用作元表之后,你不应该更改 __mode 字段的值。否则,这个元表控制的表的弱表行为将会不确定。
2.10 - 同步程序
Lua支持同步程序,也称为 半同步程序(semi-coroutines) 或 协同多线程(collaborative multithreading)。Lua中的一个同步程序代表了一个独立的执行线程。然而,不像在多线程系统中的线程那样,一个同步程序只有在调用了一个yield(产生结果)函数才能挂起它的执行。

你可以调用 coroutine.create 来创建一个同步程序。它唯一的一个参数是一个函数,代表同步程序的主函数。create 函数仅仅建立一个新的同步程序然后返回一个它的句柄 (一个线程 thread 类型的对象);它不会启动该同步程序。

当你第一次调用 coroutine.resume,将 coroutine.create 返回的线程对象作为第一个参数传递给它,然后同步程序就启动了,从它的主函数的第一行开始。传给 coroutine.resume 的额外的参数会作为同步程序主函数的参数传递过去。在同步程序开始执行之后,它一直运行到它结束或产生结果。

一个同步程序通过两种方式结束它的运行:正常情况下,当它的主函数返回(显式地或隐式的,在最后一个指令之后)时结束;异常地,如果有未保护的错误。第一各情况下,coroutine.resume 返回 true,加上同步程序主函数返回的其它值。在有错误的情况下,coroutine.resume 返回 false ,并附上错误信息。

一个同步程序通过调用 coroutine.yield 来产生结果。当一个同步程序产生结果,相应的 coroutine.resume 就立刻返回,即使操作发生在嵌套函数调用中(也就是,不在主函数中,而在被主函数直接或间接调用的函数中)。在这种情况下, coroutine.resume 也返回 true,以及传给 coroutine.yield。的所有参数。下次你继续同一个同步程序时,它会从它原来yield的地方继续执行,而 coroutine.yield 将返回给主程序传给 coroutine.resume 的额外参数。

coroutine.wrap 函数创建一个和 coroutine.create 一样的同步程序,但它不返回同步程序本身,而是返回一个继续同步程序的函数(当调用的时候)。传递给这个函数的参数作为继续resume的额外参数。函数将返回resume返回的所有值,出除了第一个(布尔值的错误代码)。不像 coroutine.resume,这个函数不捕获错误;出现任何错误都传回给调用者。

请考虑以下例子:

function foo1 (a)
  print("foo", a)
  return coroutine.yield(2*a)
end

co = coroutine.create(function (a,b)
      print("co-body", a, b)
      local r = foo1(a+1)
      print("co-body", r)
      local r, s = coroutine.yield(a+b, a-b)
      print("co-body", r, s)
      return b, "end"
end)
      
a, b = coroutine.resume(co, 1, 10)
print("main", a, b)
a, b, c = coroutine.resume(co, "r")
print("main", a, b, c)
a, b, c = coroutine.resume(co, "x", "y")
print("main", a, b, c)
a, b = coroutine.resume(co, "x", "y")
print("main", a, b)
当你运行它的时候,它会产生以下输出结果:

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,这是对于宿主程序可用的C函数集合,用以和Lua通讯。所有的API函数及其相关类型和常量都声明在头文件lua.h中。

即便每次我都使用“函数”这个词,任何设施在API里面都可能被一个宏所替代。所有这些宏(macro)都只使用一次它的参数(除了第一个参数、这个每次总是一个Lua状态),所以不会产生隐藏的副作用。
3.1 - 状态
Lua库是可重入的(reentrant)的:它没有全局变量。整个Lua解释器的状态(全局变量、栈、等等)储存在一个动态分配的 lua_State 结构类型中。一个指向这个状态的指针必须作为库中每一个函数的第一个参数,除了 lua_open 这个函数。该函数从最开始创建一个Lua状态。

在调用任何API函数之前,你必须通过调用 lua_open 创建一个状态:

       lua_State *lua_open (void);
调用 lua_close 去释放这个由 lua_open 创建的状态:

       void lua_close (lua_State *L);
这个函数销毁所有被给予Lua状态的对象(调用相应的垃圾收集元方法)并且释放那个状态使用的所有动态内存。在个别的平台上,你或许不需要调用这个函数,因为当宿主程序结束的时候会自然的释放所有的资源。另一方面,长时间运行的程序,像一些守护进程或者Web服务器,可能需要立即释放那些不需要的状态资源,以避免占用太多内存。
3.2 - 堆栈和索引
Lua使用一个来自于C语言的 虚拟栈(virtual stack) 传递值。栈里面的每一个元素都代表一个Lua值 (nil, number, string, etc.)。

只要Lua调用C语言函数,这个所调用的函数将得到一个新的栈,这个栈将独立于先前的栈以及那些仍然活跃的C函数的栈。这个栈最初包含了C函数的所有参数,并且这也会存放C函数的返回值(见 3.16)。

为了方便起见,大多数查询操作的API不需要遵守一个严格的栈定义(注:即不需要遵循FILO)。他们可以使用 索引(index) 引用任何栈中元素:一个正数索引代表了栈中的绝对位置(从1开始);一个负数索引代表了从栈顶的偏移量。更特别的是,如果栈有 n 个元素,那么索引 1 代表第一个元素(这就是说,这个元素首先入栈)并且索引 n 代表了最后一个元素;索引 -1 也代表了最后一个元素(也就是栈顶)并且索引 -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_MINSTACK 定义在 lua.h 中,它的值是 20,所以你不需要总担心栈空间除非你的代码通过循环将元素压入栈。

大多数插叙函数接受指向有效栈空间的索引,那就是说,索引达到栈空间的最大值是你需要使用 lua_checkstack。这样的索引称为可接受索引(acceptable indices)。更正规的说法,我们给出一个严格的定义如下:

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

注意,0永远不是一个可接受索引。

除非另外说明,任何函数接受有效索引可以被称为是 伪索引(pseudo-indices),这些索引代表一些Lua值可以被C 代码访问但是却不存在于栈中。假索引通常用于访问全局环境变量,注册表,和一个C 函数的上值(见 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 个元素。

lua_pushvalue 将一个索引指向的元素的拷贝压入栈。 lua_remove 删除指定位置的元素,将该元素上方的所有元素下移以填满空缺。lua_insert 将栈顶元素移动到指定位置,将该位置以上的元素上移。lua_replace 将栈顶元素移动到指定位置而不移动其他任何其他元素(因此替代了给定位置的元素的值)。所有这些函数只接受有效的索引。(你不能使用伪索引调用 lua_remove 或 lua_insert,因为他们不代表栈中的位置。)

举个例子,如果栈开始于 10 20 30 40 50*(自底向上;`*´ 标记了栈顶),那么:

       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_TNONE(就是说如果栈为空)。这些lua_type 代表的返回值作为常量定义在 lua.h 中:LUA_TNIL, LUA_TNUMBER, LUA_TBOOLEAN, LUA_TSTRING, LUA_TTABLE, LUA_TFUNCTION, LUA_TUSERDATA, LUA_TTHREAD, LUA_TLIGHTUSERDATA。下面的函数将这些常量转换成字符串:

       const char *lua_typename  (lua_State *L, int type);
lua_is* 函数返回 1 当对象与所给类型兼容的时候,其他情况返回 0。 lua_isboolean 是一个例外:它只针对布尔值时才会成功(否则将是无用的,因为任何值都是一个布尔值)。这些函数对于无效引用返回 0。 lua_isnumber 接受数字和用数字表示的字符串;lua_isstring 接受字符串和数字(见 2.2.1);lua_isfunction 接受Lua函数和C函数; lua_isuserdata 接受完整的和轻量的用户数据。要区分C 函数和Lua 函数,你可以使用 lua_iscfunction。要区分用户数据,你可以使用 lua_islightuserdata。要区分数字还是用数字表示的字符串,你可以使用 lua_type。

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

       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_rawequal 用于比较基本类型但不包括元方法。如果有任何形式的无效索引,这些函数都返回 0(false)。
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 将索引指向的Lua值转换为C语言类型的布尔值(0 或 1)。就像所有Lua中的测试一样,任何不等于 false 或者 nil 的Lua值通过 lua_toboolean 都将返回 1;否则将返回 0。当然,如果是一个无效索引,也将返回 0。(如果你只想接受真实的布尔值,使用 lua_isboolean 去测试值的类型。)

lua_tonumber 将索引指向的Lua值转换成一个数字(默认情况下,lua_Number 是 double类型)。Lua值必须是一个数字或者可转化为数字的字符串(见 2.2.1);否则,lua_tonumber 返回 0。

lua_tostring 将索引指向的Lua值转换成字符串(const char*)。Lua值必须是一个字符串或者数字;否则,函数返回 NULL。如果值是一个数字,lua_tostring 会将栈中的真实值变成一个字符串类型。(当 lua_tostring 应用于键时这个改变将引起 lua_next 的混乱。)lua_tostring 在Lua 状态内部返回一个字符串的指针。这个字符串总是以 0('')结尾,就像C 语言里的一样,但是也可能包含其他 0 在其中。如果你不知道一个字符串中是否存在 0 ,你可以使用 lua_strlen 得到它的实际长度。因为Lua具有垃圾收集机制,所以不能保证 lua_tostring 返回的指针仍然有效,当相应的值从栈中删除之后。如果你在当前函数返回之后还需要这个字符串,你需要复制它并且将它存入注册表(见 3.18)。

lua_tocfunction 将栈中的值转换为C 函数。这个值必须是一个C 函数;否则,lua_tocfunction 返回 NULL。类型 lua_CFunction 在 3.16 中有详细解释。

lua_tothread 将栈中的值转换为Lua线程(被描绘成 lua_State *)。这个值必须是一个线程;否则;lua_tothread 返回 NULL。

lua_topointer 将栈中?闹底?晃?ㄓ玫腃 语言指针(void *)。这个值可能是一个用户数据、表、线程、或者函数;否则,lua_topointer 返回 NULL。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_pushlstring 和 lua_pushstring 将对所给的字符串做一个内部拷贝。lua_pushstring 只能压入合适的C 语言字符串(也就是说,字符串要以 '' 结尾,并且不能包含内嵌的 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、vsprintf 类似,但是有一些重要的不同之处:

你不需要为结果分配空间:结果是Lua字符串并且Lua会关心内存分配问题(和内存释放问题,通过垃圾收集机制)。
转换受到限制。这里没有标志、宽度或精度。转换操作的修饰符可以是简单的`%%´(在字符串中插入一个`%´),`%s´(插入一个没有大小限制的以 0 结尾的字符串),`%f´(插入一个 lua_Number),`%d´(插入一个 int),`%c´(插入一个 int 作为一个字符)。
这个函数

       void lua_concat (lua_State *L, int n);
连接栈顶的 n 个值,将它们弹出,并且将结果留在栈顶。如果 n 为 1,结果是单个字符串(也就是说,函数什么也不做);如果 n 是 0,结果是空字符串。连接的完成依据Lua的语义(见 2.5.4)。
3.7 - 控制垃圾收集
Lua使用两个数字控制垃圾收集循环。一个数字表示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_setgcthreshold(L,0) 强迫进行垃圾收集。在这之后,一个新值根据先前的规则被设置。
3.8 - 用户数据类型 (Userdata)
用户数据代表了Lua中使用的C语言值。Lua支持两种用户数据:完整用户数据(full userdata) 和 轻量用户数据(light userdata)。

一个完整用户数据代表了一块内存。它是一个对象(像一个表):你必须创建它,它有自己的元表,当它被回收的时候你可以检测到。一个完整用户数据只能与自己相等(基于原始的相等规则)。

一个轻量用户数据代表一个指针。它是?桓鲋担ㄏ褚桓鍪?郑?耗悴⒚挥写唇ㄋ???裁挥性?怼ⅲ??荒鼙换厥眨ㄒ蛭??游幢淮唇ǎ?G崃坑没??菹嗟鹊奶跫?侵刚胫赶虻牡刂废嗤??

在Lua 代码里,没办法测试用户数据类型是完整的还是轻量的;两者都是 用户数据类型。在C 代码里,如果是完整用户数据,lua_type 返回 LUA_TUSERDATA,反之,返回 LUA_TLIGHTUSERDATA。

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

       void *lua_newuserdata (lua_State *L, size_t size);
这个函数根据指定大小分配一个内存块,将用户数据的地址压入栈并且返回这个地址。

要将轻量用户数据压入栈,你需要使用 lua_pushlightuserdata(见 3.6)。

lua_touserdata (见 3.5)用来取回用户数据的值。当你用在完整用户数据的时候,它返回这个块的地址,当你用在轻量用户数据的时候,它返回它的指针,当你用在非用数据的时候,返回 NULL。

当Lua回收一

 
相关话题推荐
 
相关新闻:
发贴区
  评论仅表达个人看法,并不表明爱酷游同意其观点或证实其描述。
   
icoou资讯76小时热门排行
icoou人气图片推荐
酷友推荐
焦点推荐
24小时前沿动态
标题  
点击数 
主题推荐

话题推荐
视频专题

娱乐专题