关闭

LUA语言参考

787人阅读 评论(0) 收藏 举报
分类:

LUA语言参考

Lua 的语法比较简单,学习起来也比较省力,但功能却并不弱。
  在Lua中,一切都是变量,除了关键字。请记住这句话。

I. 首先是注释
  写一个程序,总是少不了注释的。
  在Lua中,你可以使用单行注释和多行注释。
  单行注释中,连续两个减号"--"表示注释的开始,一直延续到行末为止。相当于C++语言中的"//"。
  多行注释中,由"--[["表示注释开始,并且一直延续到"]]"为止。这种注释相当于C语言中的"/…/"。在注释当中,"[["和"]]"是可以嵌套的。
II. Lua编程
  经典的"Hello world"的程序总是被用来开始介绍一种语言。在Lua中,写一个这样的程序很简单:
  print("Hello world")
  在Lua中,语句之间可以用分号";"隔开,也可以用空白隔开。一般来说,如果多个语句写在同一行的话,建议总是用分号隔开。
  Lua 有好几种程序控制语句,如:

  条件控制:if 条件 then … elseif 条件 then … else … end
  While循环:while 条件 do … end
  Repeat循环:repeat … until 条件
  For循环:for 变量 = 初值,终点值,步进 do … end
  For循环:for 变量1,变量2,… ,变量N in表或枚举函数 do … end

  注意一下,for的循环变量总是只作用于for的局部变量,你也可以省略步进值,这时候,for循环会使用1作为步进值。
  你可以用break来中止一个循环。
  如果你有程序设计的基础,比如你学过Basic,C之类的,你会觉得Lua也不难。但Lua有几个地方是明显不同于这些程序设计语言的,所以请特别注意。

  .语句块
    语句块在C++中是用"{"和"}"括起来的,在Lua中,它是用do 和 end 括起来的。比如: 
    do print("Hello") end
    你可以在 函数 中和 语句块 中定局部变量。

  .赋值语句
    赋值语句在Lua被强化了。它可以同时给多个变量赋值。
    例如:
    a,b,c,d=1,2,3,4
    甚至是:
    a,b=b,a -- 多么方便的交换变量功能啊。
    在默认情况下,变量总是认为是全局的。假如你要定义局部变量,则在第一次赋值的时候,需要用local说明。比如:
    local a,b,c = 1,2,3 -- a,b,c都是局部变量

  .数值运算
    和C语言一样,支持 +, -, , /。但Lua还多了一个"^"。这表示指数乘方运算。比如2^3 结果为8, 2^4结果为16。
    连接两个字符串,可以用".."运处符。如:
    "This a " .. "string." -- 等于 "this a string"

  .比较运算
    < > <= >= == ~=
    分别表示 小于,大于,不大于,不小于,相等,不相等
    所有这些操作符总是返回true或false。
    对于Table,Function和Userdata类型的数据,只有 == 和 ~=可以用。相等表示两个变量引用的是同一个数据。比如:
    a={1,2}
    b=a
    print(a==b, a~=b) -- true, false
    a={1,2}
    b={1,2}
    print(a==b, a~=b) -- false, true

  .逻辑运算
    and, or, not
    其中,and 和 or 与C语言区别特别大。
    在这里,请先记住,在Lua中,只有false和nil才计算为false,其它任何数据都计算为true,0也是true!
    and 和 or的运算结果不是true和false,而是和它的两个操作数相关。
    a and b:如果a为false,则返回a;否则返回b
    a or b:如果 a 为true,则返回a;否则返回b

    举几个例子:
     print(4 and 5) --> 5
     print(nil and 13) --> nil
     print(false and 13) --> false
     print(4 or 5) --> 4
     print(false or 5) --> 5

    在Lua中这是很有用的特性,也是比较令人混洧的特性。
    我们可以模拟C语言中的语句:x = a? b : c,在Lua中,可以写成:x = a and b or c。
    最有用的语句是: x = x or v,它相当于:if not x then x = v end 。

  .运算符优先级,从高到低顺序如下:
    ^
    not - (一元运算)
     /
     + -
     ..(字符串连接)
     < > <= >= ~= ==
     and
     or

III. 关键字
  关键字是不能做为变量的。Lua的关键字不多,就以下几个:
  and break do else elseif
  end false for function if
  in local nil not or
  repeat return then true until while

IV. 变量类型
  怎么确定一个变量是什么类型的呢?大家可以用type()函数来检查。Lua支持的类型有以下几种:

  Nil 空值,所有没有使用过的变量,都是nil。nil既是值,又是类型。
  Boolean 布尔值
  Number 数值,在Lua里,数值相当于C语言的double
  String 字符串,如果你愿意的话,字符串是可以包含'\0'字符的
  Table 关系表类型,这个类型功能比较强大,我们在后面慢慢说。
  Function 函数类型,不要怀疑,函数也是一种类型,也就是说,所有的函数,它本身就是一个变量。
  Userdata 嗯,这个类型专门用来和Lua的宿主打交道的。宿主通常是用C和C++来编写的,在这种情况下,Userdata可以是宿主的任意数据类型,常用的有Struct和指针。
  Thread   线程类型,在Lua中没有真正的线程。Lua中可以将一个函数分成几部份运行。如果感兴趣的话,可以去看看Lua的文档。

V. 变量的定义
  所有的语言,都要用到变量。在Lua中,不管你在什么地方使用变量,都不需要声明,并且所有的这些变量总是全局变量,除非,你在前面加上"local"。
  这一点要特别注意,因为你可能想在函数里使用局部变量,却忘了用local来说明。
  至于变量名字,它是大小写相关的。也就是说,A和a是两个不同的变量。
  定义一个变量的方法就是赋值。"="操作就是用来赋值的
  我们一起来定义几种常用类型的变量吧。
  A. Nil
    正如前面所说的,没有使用过的变量的值,都是Nil。有时候我们也需要将一个变量清除,这时候,我们可以直接给变量赋以nil值。如:
    var1=nil -- 请注意 nil 一定要小写

  B. Boolean
    布尔值通常是用在进行条件判断的时候。布尔值有两种:true 和 false。在Lua中,只有false和nil才被计算为false,而所有任何其它类型的值,都是true。比如0,空串等等,都是true。不要被C语言的习惯所误导,0在Lua中的的确确是true。你也可以直接给一个变量赋以Boolean类型的值,如:
    varboolean = true

  C. Number
    在Lua中,是没有整数类型的,也不需要。一般情况下,只要数值不是很大(比如不超过100,000,000,000,000),是不会产生舍入误差的。在很多CPU上,实数的运算并不比整数慢。
    实数的表示方法,同C语言类似,如:
    4 0.4 4.57e-3 0.3e12 5e+20

  D. String
    字符串,总是一种非常常用的高级类型。在Lua中,你可以非常方便的定义很长很长的字符串。
    字符串在Lua中有几种方法来表示,最通用的方法,是用双引号或单引号来括起一个字符串的,如:
    "This is a string."
    和C语言相同的,它支持一些转义字符,列表如下:
    \a bell
    \b back space
    \f form feed
    \n newline
    \r carriage return
    \t horizontal tab
    \v vertical tab
    \\ backslash
    \" double quote
    \' single quote
    \[ left square bracket
    \] right square bracket

    由于这种字符串只能写在一行中,因此,不可避免的要用到转义字符。加入了转义字符的串,看起来实在是不敢恭维,比如:
    "one line\nnext line\n\"in quotes\", 'in quotes'"
    一大堆的"\"符号让人看起来很倒胃口。如果你与我有同感,那么,我们在Lua中,可以用另一种表示方法:用"[["和"]]"将多行的字符串括起来,如:
    page = [[
    <HTML>
      <HEAD>
        <TITLE>An HTML Page</TITLE>
      </HEAD>
      <BODY>
        <A HREF="[http://www.lua.org]">Lua</A>
        [[a text between double brackets]]
      </BODY>
    </HTML>
    ]]

    值得注意的是,在这种字符串中,如果含有单独使用的"[["或"]]"就仍然得用"\["或"\]"来避免歧义。当然,这种情况是极少会发生的。

  E. Table
    关系表类型,这是一个很强大的类型。我们可以把这个类型看作是一个数组。只是C语言的数组,只能用正整数来作索引;在Lua中,你可以用任意类型来作数组的索引,除了nil。同样,在C语言中,数组的内容只允许一种类型;在Lua中,你也可以用任意类型的值来作数组的内容,除了nil。
    Table的定义很简单,它的主要特征是用"{"和"}"来括起一系列数据元素的。比如:

    T1 = {} -- 定义一个空表
    T1[1]=10 -- 然后我们就可以象C语言一样来使用它了。
    T1["John"]={Age=27, Gender="Male"}
    这一句相当于:
    T1["John"]={} -- 必须先定义成一个表,还记得未定义的变量是nil类型吗
    T1["John"]["Age"]=27
    T1["John"]["Gender"]="Male"
    当表的索引是字符串的时候,我们可以简写成:
    T1.John={}
    T1.John.Age=27
    T1.John.Gender="Male"
    或
    T1.John{Age=27, Gender="Male"}
    这是一个很强的特性。

    在定义表的时候,我们可以把所有的数据内容一起写在"{"和"}"之间,这样子是非常方便,而且很好看。比如,前面的T1的定义,我们可以这么写:

    T1=
    {
      10, -- 相当于 [1] = 10
      [100] = 40,
      John= -- 如果你原意,你还可以写成:["John"] =
      {
        Age=27,   -- 如果你原意,你还可以写成:["Age"] =27
        Gender=Male   -- 如果你原意,你还可以写成:["Gender"] =Male
      },
      20 -- 相当于 [2] = 20
    }

    看起来很漂亮,不是吗?我们在写的时候,需要注意三点:
    第一,所有元素之间,总是用逗号","隔开;
    第二,所有索引值都需要用"["和"]"括起来;如果是字符串,还可以去掉引号和中括号;
    第三,如果不写索引,则索引就会被认为是数字,并按顺序自动从1往后编;

    表类型的构造是如此的方便,以致于常常被人用来代替配置文件。是的,不用怀疑,它比ini文件要漂亮,并且强大的多。

  F. Function
    函数,在Lua中,函数的定义也很简单。典型的定义如下:
    function add(a,b) -- add 是函数名字,a和b是参数名字
     return a+b -- return 用来返回函数的运行结果
    end

    请注意,return语言一定要写在end之前。假如你非要在中间放上一句return,那么请写成:do return end。
    还记得前面说过,函数也是变量类型吗?上面的函数定义,其实相当于:
    add = function (a,b) return a+b end
    当你重新给add赋值时,它就不再表示这个函数了。你甚至可以赋给add任意数据,包括nil (这样,你就清除了add变量)。Function是不是很象C语言的函数指针呢?

    和C语言一样,Lua的函数可以接受可变参数个数,它同样是用"…"来定义的,比如:
    function sum (a,b,…)
    如果想取得…所代表的参数,可以在函数中访问arg局部变量(表类型)得到。
    如 sum(1,2,3,4)
    则,在函数中,a = 1, b = 2, arg = {3, 4}
    更可贵的是,它可以同时返回多个结果,比如:
    function s()
      return 1,2,3,4
    end
    a,b,c,d = s() -- 此时,a = 1, b = 2, c = 3, d = 4

    前面说过,表类型可以拥有任意类型的值,包括函数!因此,有一个很强大的特性是,拥有函数的表,哦,我想更恰当的应该说是对象吧。Lua可以使用面向对象编程了。不信?那我举例如下:

    t =
    {
     Age = 27
     add = function(self, n) self.Age = self.Age+n end
    }
    print(t.Age) -- 27
    t.add(t, 10)
    print(t.Age) -- 37

    不过,t.add(t,10) 这一句实在是有点土对吧?没关系,在Lua中,你可以简写成:
    t:add(10)   -- 相当于 t.add(t,10)

  G. Userdata 和 Thread
    这两个类型的话题,超出了本文的内容,就不打算细说了。
 




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的官方网站可以找到,[http://www.lua.org]。 

如果需要知道Lua设计背后的一些决定和讨论,可以参考以下论文,它们都可以在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 (1994) 273-283. 
L. H. de Figueiredo, R. Ierusalimschy, and W. Celes. Lua: an extensible embedded language. Dr. Dobb's Journal 21 #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进制数字,包括嵌入零,它可以表示为 `\0´。 

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

    (1)   "alo\n123\""
    (2)   '\97lo\10\04923"'
    (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位字符,包括嵌入的 ('\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 是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中定义,也无法调用。我们在这里仅仅用来解释原理)。 

所有的全局变量存在一个普通的Lua表中,称之为 环境变量表(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] end

Lua也有 for 语句,有两种格式 (见 2.4.5)。 

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

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

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

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

由于语法的原因, 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
注意: 

三种控制表达式只会被计算一次,在循环开始之前。他们的结果必须是数值。 
_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 ::= 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
会被翻译为 

    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(2a)
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('\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 将栈中的值转换为通用的C 语言指针(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' 结尾,并且不能包含内嵌的 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)。 

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

一个轻量用户数据代表一个指针。它是一个值(像一个数字):你并没有创建它,它也没有元表、,它不能被回收(因为它从未被创建)。轻量用户数据相等的条件是指针指向的地址相同。 

在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回收一个完整用户数据,它调用该用户数据的 gc 元方法,然后释放该用户数据相应的内存。 

3.9 - 元表 (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 函数读取语句段的内容。当需要调用其它段时,lua_load 调用 reader,传递其 data 参数。必须返回指向语句段所在的新内存块的指针,并将段大小设置为 0。为了标志块尾,reader 必须返回 NULL。reader 函数可以返回任何大于零的值。 

在当前的实现中,reader 函数不能调用任何Lua 函数;为了保证这一点,它总是会得到为 NULL 的Lua状态。 

语句段名(chunkname) 用于错误信息和调试信息(见 4)。 

参考辅助库 (lauxlib.c) 了解如何使用 lua_load 以及如何使用现成的函数从文件和字符串中加载语句段。 

3.11 - 表操作
通过调用以下函数可以创建表: 

    void lua_newtable (lua_State L);
这个函数创建一张新的空表,并将其压入栈。 

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

    void lua_gettable (lua_State L, int index);
index 代表表的位置。lua_gettable 从栈中弹出一个键,并且返回该键对应的值,表仍然留在堆栈中。在Lua中,这个函数可能触发一个针对 index 事件的元方法(见 2.8)。想要在不调用任何元方法的情况下得到表主键所对应的真实值,使用这个原始(raw)版本: 

    void lua_rawget (lua_State L, int index);
要将一个值储存到栈中的一张表中,你需要将键压入栈,再将值压入栈,调用: 

    void lua_settable (lua_State L, int index);

index 代表表的位置。lua_settable 从栈中弹出主键和值。表仍然留在栈中。在Lua中,这个操作可能触发针对 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("%s - %s\n",
      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_tostring,除非你知道这个键确实是一个字符串。再次调用 lua_tostring 改变了所给索引指向的值;这使 lua_next 的调用发生混乱。 

3.12 - 环境变量操作 (Manipulating Environments)
所有的全局变量保存在普通的Lua 表中,叫做环境变量。初始的环境变量被称作全局环境变量。这张表总是在 LUA_GLOBALSINDEX 这个伪索引处。 

要访问或改变全局变量的值,你可以对环境变量表使用常规的表操作。举个例子,存取一个全局变量的值: 

    lua_pushstring(L, varname);
    lua_gettable(L, LUA_GLOBALSINDEX);
你可以改变一个Lua 线程的全局环境变量通过 lua_replace 函数。 

以下的函数提供获取、设置Lua函数的环境变量的功能: 

    void lua_getfenv (lua_State L, int index);
    int lua_setfenv (lua_State L, int index);
lua_getfenv 将堆栈中 index 索引指向的函数的环境变量表压入栈。如果函数是一个C 函数,lua_getfenv 将全局环境变量压入栈。lua_setfenv 从栈中弹出一张表并且将其设置为栈中 index 索引处的函数的新环境变量。如果给定索引处的对象不是一个Lua 函数,lua_setfenv 返回 0。 

3.13 - 将表作为数组使用 Using Tables as Arrays
有一些 API 能够帮助我们将Lua 表作为数组使用,也就是说,表只由数字作为索引: 

    void lua_rawgeti (lua_State L, int index, int n);
    void lua_rawseti (lua_State L, int index, int n);
lua_rawgeti 将表中的第 n 个元素放入堆栈中的指定位置 index。lua_rawseti 将堆栈中指定位置 index 处的表中的第 n 个元素的值设定为栈顶的值,并将原来的值从栈中删除。 

3.14 - 调用函数
定义在Lua 中的函数和C语言函数经过注册就可以被宿主程序调用。这些调用必须遵循以下协议:首先,被调用的函数被压入栈;然后,函数的参数必须顺序(direct order)输入,也就是说,第一个参数需要被第一个输入。最后,函数通过下面的方法调用: 

    void lua_call (lua_State L, int nargs, int nresults);
nargs 是你压入栈的参数的数量。所有参数和函数值从堆栈中弹出,并且函数结果被压入栈。返回值的数量被调整为 nresults,除非 nresults 是 LUA_MULTRET。在那种情况下,所有函数结果都被压入栈。Lua 会检测返回值是否适合栈空间。函数返回值按顺序被压入栈(第一个返回值首先入栈),所以调用结束后最后一个返回值在栈顶。 

下面的例子展示宿主程序如何可以和这个Lua 代码等效: 

    a = f("how", t.x, 14)
这里是C 语言里的做法: 

  lua_pushstring(L, "t");
  lua_gettable(L, LUA_GLOBALSINDEX);       / global `t' (for later use) /
  lua_pushstring(L, "a");                           / var name /
  lua_pushstring(L, "f");                       / function name /
  lua_gettable(L, LUA_GLOBALSINDEX);           / function to be called /
  lua_pushstring(L, "how");                       / 1st argument /
  lua_pushstring(L, "x");                   / push the string "x" /
  lua_gettable(L, -5);               / push result of t.x (2nd arg) /
  lua_pushnumber(L, 14);                         / 3rd argument /
  lua_call(L, 3, 1);       / call function with 3 arguments and 1 result /
  lua_settable(L, LUA_GLOBALSINDEX);         / set global variable `a' /
  lua_pop(L, 1);                     / remove `t' from the stack /
注意上面的代码是“平衡的”:在它结束时,堆栈返回原来的配置。这个被认为是良好的编程实践。 

(为了展示细节,我们只用Lua 提供的原始 API 完成这个例子。通常程序员定义并使用几个宏和辅助库函数在Lua 中提供高级存取功能。请参考例子中标准库函数的源代码。) 

3.15 - 受保护调用 Protected Calls
当你通过 lua_call 调用一个函数,所调用函数内部产生的错误将向上传递(通过一个 longjmp)。如果你需要处理错误,你应该使用 lua_pcall: 

    int lua_pcall (lua_State L, int nargs, int nresults, int errfunc);
nargs 和 nresults 在 lua_call 中有相同的意义。如果调用过程中没有错误,lua_pcall 的行为非常像 lua_call 。然而,如果有错误,lua_call 会捕获它,将一个单一值(错误信息)压入栈,并且返回错误代码。像 lua_call ,lua_pcall 总是从栈中删除函数和它的参数。 

如果 errfunc 是 0,所返回的错误信息就是原始的错误信息。另外,errfunc 给出一个指向错误处理函数(error handler function)的栈索引。(在当前的实现中,索引不能为伪索引。)假设运行时错误,函数将和错误信息一起被调用,并且他的返回值将是 lua_pcall 返回的信息。 

错误处理函数被用来为错误信息增加更多的调试信息,例如栈的记录。这样的信息在 lua_pcall 调用返回后将不能被收集,因此栈已经被解开了。 

如果 lua_pcall 函数调用成功返回 0,否则返回以下的一个错误代码(定义在 lua.h): 

LUA_ERRRUN --- 运行时错误 
LUA_ERRMEM --- 内存分配错误。这样的错误下,Lua 不调用错误处理函数 
LUA_ERRERR --- 运行错误处理函数时发生的错误 
3.16 - 定义C 函数
Lua可以通过C 语言写的函数进行扩展,这些函数必须是 lua_CFunction 类型的,作为以下定义: 

    typedef int (lua_CFunction) (lua_State L);
一个C 函数接收一个Lua 状态并且返回一个整数,数值需要返回给Lua。 

为了正确的和Lua 通讯,C 函数必须遵循以下协议,它定义了参数和返回值传递的方法:一个C 函数在它的堆栈中从Lua获取顺序(第一个参数首先入栈)参数。所以,当函数开始时,第一个参数在索引位置 1。为了将返回值传递给Lua,一个C 函数将它们顺序压入栈,并且返回它们的数量。任何在堆栈中位于返回值以下的值都将被Lua 适当的解除。就像Lua 函数一样,一个C 函数被Lua 调用也可以返回很多结果。 

作为一个例子,下面的函数接收一个任意数量的数字参数并且返回它们的平均值和总合: 

    static int foo (lua_State L) {
      int n = lua_gettop(L);   / number of arguments /
      lua_Number sum = 0;
      int i;
      for (i = 1; i <= n; i++) {
      if (!lua_isnumber(L, i)) {
        lua_pushstring(L, "incorrect argument to function `average'");
        lua_error(L);
      }
      sum += lua_tonumber(L, i);
      }
      lua_pushnumber(L, sum/n);     / first result /
      lua_pushnumber(L, sum);       / second result /
      return 2;             / number of results /
    }
下面是一些便利的宏用来在Lua中注册一个C 函数: 

    #define lua_register(L,n,f) \
          (lua_pushstring(L, n), \
          lua_pushcfunction(L, f), \
          lua_settable(L, LUA_GLOBALSINDEX))
  / lua_State L;   /
  / const char n;   /
  / lua_CFunction f; /
它接收Lua 中的函数名和一个指向函数的指针。这样,上面的C 函数foo可以在Lua中被注册为 average 并被调用。 

    lua_register(L, "average", foo);

3.17 - 定义C 函数关闭 Defining C Closures
当一个C 函数被创建后,它可以与一些值关联,这样创建了一个 C 闭包(C closure);这些值可以被随时被函数访问。为了使值和C 函数关联,首先这些值要被压入栈(有多个值时,第一个值先入),然后这个函数 

    void lua_pushcclosure (lua_State L, lua_CFunction fn, int n);

被用来将C 函数压入栈,通过参数 n 告知应该有多少个值和该函数关联(lua_pushcclosure 将这些值从堆栈中弹出);事实上,这个宏 lua_pushcfunction 被定义作为 lua_pushcfunction 将 n 设置为 0。 

然后,无论何时C 函数被调用,那些值被定为于指定的伪索引处。那些伪索引有一个宏 lua_upvalueindex 产生。第一个和函数关联的值在 lua_upvalueindex(1) 处,其他的以此类推。当 n 比当前函数的上值大时,lua_upvalueindex(n) 会产生一个可接受的索引(但是无效)。 

C语言函数和关闭的例子,请参考Lua官方发行版中的标准库(src/lib/.c)。 

3.18 - 注册表 Registry
Lua提供了一个注册表,一张可以被所有C 代码用来储存任何需要储存的Lua值的预定义表,特别是如果C 代码需要维护C 函数以外存活的Lua值。这张表总是位于 LUA_REGISTRYINDEX 这个为索引处。任何C 语言库可以将数据储存在这张表中,只要它选择的键和其他库不同。典型的做法是你应该使用字符串作为主键包含你的库名或者在你的代码中使用一个包含C 对象地址的轻量用户数据。 

在注册表中的整数键被引用机制所使用,由辅助库实现,因此不应该被用作其它用途。 

3.19 - C 中的错误处理 Error Handling in C
总的来说,Lua使用C longjmp 机制来处理错误。当Lua面对任何错误(例如内存分配错误,类型错误,句法错误)它 升起(raises) 一个错误,也就是说,它做了一个长跳跃。一个受保护的环境使用 setjmp 设置一个恢复点;任何错误跳至最近最活跃的恢复点。 

如果错误发生在任何受保护的环境,Lua调用 panic 函数 并且随后调用exit(EXIT_FAILURE)。你可以将panic 函数变为以下内容。 

    lua_CFunction lua_atpanic (lua_State L, lua_CFunction panicf);
你的新panic 函数可以避免程序因为没有返回(例如通过一个长跳跃)而退出。否则,相应的Lua 状态将不一致;唯一安全的操作就是关闭它。 

几乎所有的API 函数都可能引起错误,例如导致一个内存分配错误。:lua_open, lua_close, lua_load 和 lua_pcall 这些的函数运行在保护模式下(也就是说,它们创建了一个受保护的环境并在其中运行),所以它们从不会引起错误。 

有另外一个函数将所给的C 函数运行在保护模式下: 

    int lua_cpcall (lua_State L, lua_CFunction func, void ud);
lua_cpcall 在保护模式下调用 func。func 由一个包含 ud 的轻量用户数据开始。在错误问题上,lua_cpcall 像 lua_pcall 一样返回相同的错误代码(见 3.15),加上在栈顶的一个错误对象;否则,返回 0,并且不对堆栈进行任何操作。任何由 func 返回的值都被丢弃。 

C 代码可以通过调用下面的函数产生一个Lua错误: 

    void lua_error (lua_State L);
错误信息(实际上可以是任何类型的对象)必须在栈顶。这个函数进行一个长跳跃,因此从来不会返回。 

3.20 - 线程
Lua 提供了操作线程的部分支持。如果你有多线程操作的C 语言库,那么Lua能够与其协作并且在Lua中实现相同的机制。同样,Lua在线程之上实现自己的协同程序系统。以下函数用来在Lua中创建一个线程: 

    lua_State lua_newthread (lua_State L);
这个函数将线程压入栈并且返回代表新线程的 lua_State 指针。这个返回的新状态与所有全局对象(例如表)共享初始状态,但是有一个独立的运行时堆栈。 

每个线程都有自己独立的全局环境表。当你创建一个线程,这张表就和所给状态一样,但是你可以独自更改它们。 

没有明确的函数可以关闭或者销毁一个线程。线程和其他Lua对象一样受垃圾收集程序的支配: 

要像协同程序一样操作线程,Lua提供了以下函数: 

    int lua_resume (lua_State L, int narg);
    int lua_yield (lua_State L, int nresults);
你需要创建一个线程以便启动协同程序;然后你将函数体和事件参数压入堆栈;然后调用 lua_resume,narg 的值代表参数的数量。当同步程序暂停或者结束执行,函数将返回。当它返回后,栈中包含的所有值传递给 lua_yield,或者有主体函数返回。如果同步程序运行无误,lua_resume 返回 0,否则返回一个错误代码(见 3.15)。对于错误,堆栈只包含错误信息。要重起同步程序,将作为结果传递给 yield 的值压入堆栈,并且调用 lua_resume。 

lua_yield 函数只能像C 函数的返回表达式一样被调用,就像下面所展示的: 

    return lua_yield (L, nresults);
如果C 函数像这样调用 lua_yield,正在运行的同步程序暂停它的执行,并且调用 lua_resume 开始让这个协同程序返回。nresults 这个参数代表了在堆栈中作为结果传递给 lua_resume 的值的数量。 

要在不同线程中交换值,你可以使用 lua_xmove: 

    void lua_xmove (lua_State from, lua_State to, int n);
它从堆栈 from 中弹出 n 个值,并将其压入堆栈 to。 

4 - 调试接口 The Debug Interface
Lua 没有内置的调试设施。它使用一种特殊的接口,这种接口依赖函数和 钩子(hooks)。该接口允许构造不同种类的调试器,分析器以及其他工具用以从解释器得到所需的信息。 

4.1 - 堆栈及函数信息 Stack and Function Information
得到解释程序运行时堆栈信息的主要函数是: 

    int lua_getstack (lua_State L, int level, lua_Debug ar);
这个函数用一个指定等级的函数的 activation record 的标示符填充一个 lua_Debug 结构,等级 0 是当前运行函数,然而等级 n+1 是在等级 n 上调用的函数。当没有错误发生时,lua_getstack 返回 1;当在比栈更深的等级上调用的时候,它返回 0; 

lua_Debug 结构被用来携带一个处于活动状态的函数的各种信息: 

    typedef struct lua_Debug {
    int event;
    const char name;     / (n) /
    const char namewhat; / (n) `global', `local', `field', `method' /
    const char what;     / (S) `Lua' function, `C' function, Lua `main' /
    const char source;   / (S) /
    int currentline;     / (l) /
    int nups;         / (u) number of upvalues /
    int linedefined;     / (S) /
    char short_src[LUA_IDSIZE]; / (S) /

    / private part /
    ...
    } lua_Debug;
lua_getstack 只填充结构的私有部分以备之后使用。要填充 lua_Debug 其他有用信息,调用 

    int lua_getinfo (lua_State L, const char what, lua_Debug ar);

这个函数发生错误是返回 0 (举个例子,一个无效的 what 选项)。what 字符串中的每个字符选择填充一些 ar 结构的字段,把上面在 lua_Debug 定义中用圆括号括起来的字母作为指示: `S´ 填充在 source, linedefined 和 what 字段中;`l´ 填充在 currentline 字段中,等等...。而且,`f´ 将正在运行在所给等级上的函数压入堆栈。 

想要从一个不处于活动状态的函数那得到信息(就是不在栈上的函数),你需要将其压入栈并且用 >´ 作为 what 字符串的开始。举个例子,要知道函数 f 定义在第几行,你需要这样写 

    lua_Debug ar;
    lua_pushstring(L, "f");
    lua_gettable(L, LUA_GLOBALSINDEX); / get global `f' /
    lua_getinfo(L, ">S", &ar);
    printf("%d\n", ar.linedefined);
lua_Debug 的字段有如下的含义:

source 如果函数在一个字符串中定义,那么 source 就是那个字符串。如果函数定义在一个文件中,source 开始于一个 `@´ 后面跟随文件名。 
short_src 一个可打印版的 source,用于错误信息。 
linedefined 函数定义起始的行号。 
what 如果这是一个Lua函数,显示 "Lua" 字符串, "C" 为C 函数,"main" 如果这是一个语句段的main部分,"tail" 如果这是一个做了尾部调用的函数。在后面的情况里,Lua 没有其他关于这个函部的信息。 
currentline 代表当前函数执行到的行数。如果没有行信息可用,currentline 被设置为 -1。 
name 一个所给函数合理的函数名。因为函数在Lua中属于第一类值,它们没有固定的名字:一些函数可能是多个全局变量的值,其他的可能只储存在一个表字段里。lua_getinfo 函数检测函数如何被调用或者是否为一个全局变量的值以寻找一个合适的名字。如果找不到合适的名字,name 被设置为 NULL。 
namewhat name 字段的解释。根据函数如何被调用,namewhat 的值可以是 "global", "local", "method", "field" 或者 "" (空字符串)。(当没有其他可选项的时候Lua使用空字符串代替) 
nups 函数上值的数量。 
4.2 - 操作局部变量和上值 Manipulating Local Variables and Upvalues
为了更多的操作局部变量和上值,调试接口使用索引:第一个参数或者局部变量索引为 1,以此类推,直到最后一个活动的局部变量。整个函数中的活动的上值没有特定的顺序。 

下面的函数允许操作一个所给激活记录的局部变量: 

    const char lua_getlocal (lua_State L, const lua_Debug ar, int n);
    const char lua_setlocal (lua_State L, const lua_Debug ar, int n);
参数 ar 必须是一个被前一个 lua_getstack 调用填充的有效的激活记录或者作为一个钩子函数的参数(见 4.3)。lua_getlocal 获得一个局部变量的索引 n,将变量的值压入栈,并且返回变量名。lua_setlocal 从栈顶分配一个值给变量并且返回变量名。当索引超过活动的局部变量的数量时,两个函数都返回 NULL。 

以下的函部可以操作所给函数的上值(不像局部变量,函数的上值即使在函数不处于活动状态的时候都可以被访问): 

    const char lua_getupvalue (lua_State L, int funcindex, int n);
    const char lua_setupvalue (lua_State L, int funcindex, int n);
这些函数可以作为Lua 函数使用也可以作为C 函数使用。(作为Lua 函数,上值是函数外部使用的局部变量,因此它被包含在函数闭包中。)funcindex 指向栈中的一个函数。lua_getupvalue 得到一个上值的索引 n,将上值的值压入栈,并返回其变量名。lua_setupvalue 从栈顶分配一个值给上值并返回变量名。当索引大于上值数量时,两个函数都返回 NULL。对于C 函数来说,这些函数使用空字符串作为所有上值的变量名。 

作为一个例子,下面的函数列举了所给等级的栈中的函数的所有局部变量名和上值变量名: 

    int listvars (lua_State L, int level) {
      lua_Debug ar;
      int i;
      const char name;
      if (lua_getstack(L, level, &ar) == 0)
      return 0; / failure: no such level in the stack /
      i = 1;
      while ((name = lua_getlocal(L, &ar, i++)) != NULL) {
      printf("local %d %s\n", i-1, name);
      lua_pop(L, 1); / remove variable value /
      }
      lua_getinfo(L, "f", &ar); / retrieves function /
      i = 1;
      while ((name = lua_getupvalue(L, -1, i++)) != NULL) {
      printf("upvalue %d %s\n", i-1, name);
      lua_pop(L, 1); / remove upvalue value /
      }
      return 1;
    }
4.3 - 钩子 Hooks
Lua offers a mechanism of hooks, which are user-defined C functions that are called during the program execution. A hook may be called in four different events: a call event, when Lua calls a function; a return event, when Lua returns from a function; a line event, when Lua starts executing a new line of code; and a count event, which happens every "count" instructions. Lua identifies these events with the following constants: LUA_HOOKCALL, LUA_HOOKRET (or LUA_HOOKTAILRET, see below), LUA_HOOKLINE, and LUA_HOOKCOUNT. 

A hook has type lua_Hook, defined as follows: 

    typedef void (lua_Hook) (lua_State L, lua_Debug ar);
You can set the hook with the following function: 

    int lua_sethook (lua_State L, lua_Hook func, int mask, int count);
func is the hook. mask specifies on which events the hook will be called: It is formed by a disjunction of the constants LUA_MASKCALL, LUA_MASKRET, LUA_MASKLINE, and LUA_MASKCOUNT. The count argument is only meaningful when the mask includes LUA_MASKCOUNT. For each event, the hook is called as explained below: 

The call hook is called when the interpreter calls a function. The hook is called just after Lua enters the new function. 
The return hook is called when the interpreter returns from a function. The hook is called just before Lua leaves the function. 
The line hook is called when the interpreter is about to start the execution of a new line of code, or when it jumps back in the code (even to the same line). (This event only happens while Lua is executing a Lua function.) 
The count hook is called after the interpreter executes every count instructions. (This event only happens while Lua is executing a Lua function.) 
A hook is disabled by setting mask to zero. 

You can get the current hook, the current mask, and the current count with the following functions: 

    lua_Hook lua_gethook     (lua_State L);
    int     lua_gethookmask (lua_State L);
    int     lua_gethookcount (lua_State L);
Whenever a hook is called, its ar argument has its field event set to the specific event that triggered the hook. Moreover, for line events, the field currentline is also set. To get the value of any other field in ar, the hook must call lua_getinfo. For return events, event may be LUA_HOOKRET, the normal value, or LUA_HOOKTAILRET. In the latter case, Lua is simulating a return from a function that did a tail call; in this case, it is useless to call lua_getinfo. 

While Lua is running a hook, it disables other calls to hooks. Therefore, if a hook calls back Lua to execute a function or a chunk, that execution occurs without any calls to hooks. 

5 - 标准库
The standard libraries provide useful functions that are implemented directly through the C API. Some of these functions provide essential services to the language (e.g., type and getmetatable); others provide access to "outside" services (e.g., I/O); and others could be implemented in Lua itself, but are quite useful or have critical performance to deserve an implementation in C (e.g., sort). 

All libraries are implemented through the official C API and are provided as separate C modules. Currently, Lua has the following standard libraries: 

基本库 basic library; 
字符串操作 string manipulation; 
表操作 table manipulation; 
数学函数 (sin, log 等等)mathematical functions (sin, log, etc.); 
输入输出 input and output; 
操作系统机制 operating system facilities; 
调试机制 debug facilities. 
Except for the basic library, each library provides all its functions as fields of a global table or as methods of its objects. 

To have access to these libraries, the C host program must first call the functions luaopen_base (for the basic library), luaopen_string (for the string library), luaopen_table (for the table library), luaopen_math (for the mathematical library), luaopen_io (for the I/O and the Operating System libraries), and luaopen_debug (for the debug library). These functions are declared in lualib.h.
 

5.1 - 基本函数 Basic Functions
The basic library provides some core functions to Lua. If you do not include this library in your application, you should check carefully whether you need to provide some alternative implementation for some of its facilities. 

assert (v [, message])
Issues an error when the value of its argument v is nil or false; otherwise, returns this value. message is an error message; when absent, it defaults to "assertion failed!" 

collectgarbage ([limit])
Sets the garbage-collection threshold to the given limit (in Kbytes) and checks it against the byte counter. If the new threshold is smaller than the byte counter, then Lua immediately runs the garbage collector (see 2.9). If limit is absent, it defaults to zero (thus forcing a garbage-collection cycle). 

dofile (filename)
Opens the named file and executes its contents as a Lua chunk. When called without arguments, dofile executes the contents of the standard input (stdin). Returns any value returned by the chunk. In case of errors, dofile propagates the error to its caller (that is, it does not run in protected mode). 

error (message [, level])
Terminates the last protected function called and returns message as the error message. Function error never returns. 

The level argument specifies where the error message points the error. With level 1 (the default), the error position is where the error function was called. Level 2 points the error to where the function that called error was called; and so on. 

_G
A global variable (not a function) that holds the global environment (that is, _G._G = _G). Lua itself does not use this variable; changing its value does not affect any environment. (Use setfenv to change environments.) 

getfenv (f)
Returns the current environment in use by the function. f can be a Lua function or a number, which specifies the function at that stack level: Level 1 is the function calling getfenv. If the given function is not a Lua function, or if f is 0, getfenv returns the global environment. The default for f is 1. 

If the environment has a "__fenv" field, returns the associated value, instead of the environment. 

getmetatable (object)
If the object does not have a metatable, returns nil. Otherwise, if the object's metatable has a "__metatable" field, returns the associated value. Otherwise, returns the metatable of the given object. 

gcinfo ()
Returns two results: the number of Kbytes of dynamic memory that Lua is using and the current garbage collector threshold (also in Kbytes). 

ipairs (t)
Returns an iterator function, the table t, and 0, so that the construction 

    for i,v in ipairs(t) do ... end
will iterate over the pairs (1,t[1]), (2,t[2]), ..., up to the first integer key with a nil value in the table. 

loadfile (filename)
Loads a file as a Lua chunk (without running it). If there are no errors, returns the compiled chunk as a function; otherwise, returns nil plus the error message. The environment of the returned function is the global environment. 

loadlib (libname, funcname)
Links the program with the dynamic C library libname. Inside this library, looks for a function funcname and returns this function as a C function. 

libname must be the complete file name of the C library, including any eventual path and extension. 

This function is not supported by ANSI C. As such, it is only available on some platforms (Windows, Linux, Solaris, BSD, plus other Unix systems that support the dlfcn standard). 

loadstring (string [, chunkname])
Loads a string as a Lua chunk (without running it). If there are no errors, returns the compiled chunk as a function; otherwise, returns nil plus the error message. The environment of the returned function is the global environment. 

The optional parameter chunkname is the name to be used in error messages and debug information. 

To load and run a given string, use the idiom 

    assert(loadstring(s))()
next (table [, index])
Allows a program to traverse all fields of a table. Its first argument is a table and its second argument is an index in this table. next returns the next index of the table and the value associated with the index. When called with nil as its second argument, next returns the first index of the table and its associated value. When called with the last index, or with nil in an empty table, next returns nil. If the second argument is absent, then it is interpreted as nil. 

Lua has no declaration of fields; There is no difference between a field not present in a table or a field with value nil. Therefore, next only considers fields with non-nil values. The order in which the indices are enumerated is not specified, even for numeric indices. (To traverse a table in numeric order, use a numerical for or the ipairs function.) 

The behavior of next is undefined if, during the traversal, you assign any value to a non-existent field in the table. 

pairs (t)
Returns the next function and the table t (plus a nil), so that the construction 

    for k,v in pairs(t) do ... end
will iterate over all key-value pairs of table t. 

pcall (f, arg1, arg2, ...)
Calls function f with the given arguments in protected mode. That means that any error inside f is not propagated; instead, pcall catches the error and returns a status code. Its first result is the status code (a boolean), which is true if the call succeeds without errors. In such case, pcall also returns all results from the call, after this first result. In case of any error, pcall returns false plus the error message. 

print (e1, e2, ...)
Receives any number of arguments, and prints their values in stdout, using the tostring function to convert them to strings. This function is not intended for formatted output, but only as a quick way to show a value, typically for debugging. For formatted output, use format (see 5.3). 

rawequal (v1, v2)
Checks whether v1 is equal to v2, without invoking any metamethod. Returns a boolean. 

rawget (table, index)
Gets the real value of table[index], without invoking any metamethod. table must be a table; index is any value different from nil. 

rawset (table, index, value)
Sets the real value of table[index] to value, without invoking any metamethod. table must be a table, index is any value different from nil, and value is any Lua value.

require (packagename)
Loads the given package. The function starts by looking into the table _LOADED to determine whether packagename is already loaded. If it is, then require returns the value that the package returned when it was first loaded. Otherwise, it searches a path looking for a file to load. 

If the global variable LUA_PATH is a string, this string is the path. Otherwise, require tries the environment variable LUA_PATH. As a last resort, it uses the predefined path "?;?.lua". 

The path is a sequence of templates separated by semicolons. For each template, require will change each interrogation mark in the template to packagename, and then will try to load the resulting file name. So, for instance, if the path is 

"./?.lua;./?.lc;/usr/local/?/?.lua;/lasttry"

a require "mod" will try to load the files ./mod.lua, ./mod.lc, /usr/local/mod/mod.lua, and /lasttry, in that order. 

The function stops the search as soon as it can load a file, and then it runs the file. After that, it associates, in table _LOADED, the package name with the value that the package returned, and returns that value. If the package returns nil (or no value), require converts this value to true. If the package returns false, require also returns false. However, as the mark in table _LOADED is false, any new attempt to reload the file will happen as if the package was not loaded (that is, the package will be loaded again). 

If there is any error loading or running the file, or if it cannot find any file in the path, then require signals an error. 

While running a file, require defines the global variable _REQUIREDNAME with the package name. The package being loaded always runs within the global environment.

setfenv (f, table)
Sets the current environment to be used by the given function. f can be a Lua function or a number, which specifies the function at that stack level: Level 1 is the function calling setfenv. 

As a special case, when f is 0 setfenv changes the global environment of the running thread. 

If the original environment has a "__fenv" field, setfenv raises an error. 

setmetatable (table, metatable)
Sets the metatable for the given table. (You cannot change the metatable of a userdata from Lua.) If metatable is nil, removes the metatable of the given table. If the original metatable has a "__metatable" field, raises an error. 

tonumber (e [, base])
Tries to convert its argument to a number. If the argument is already a number or a string convertible to a number, then tonumber returns that number; otherwise, it returns nil. 

An optional argument specifies the base to interpret the numeral. The base may be any integer between 2 and 36, inclusive. In bases above 10, the letter `A´ (in either upper or lower case) represents 10, `B´ represents 11, and so forth, with `Z´ representing 35. In base 10 (the default), the number may have a decimal part, as well as an optional exponent part (see 2.2.1). In other bases, only unsigned integers are accepted. 

tostring (e)
Receives an argument of any type and converts it to a string in a reasonable format. For complete control of how numbers are converted, use format (see 5.3). 

If the metatable of e has a "__tostring" field, tostring calls the corresponding value with e as argument, and uses the result of the call as its result. 

type (v)
Returns the type of its only argument, coded as a string. The possible results of this function are "nil" (a string, not the value nil), "number", "string", "boolean, "table", "function", "thread", and "userdata". 

unpack (list)
Returns all elements from the given list. This function is equivalent to 

return list[1], list[2], ..., list[n]

except that the above code can be written only for a fixed n. The number n is the size of the list, as defined for the table.getn function. 

_VERSION
A global variable (not a function) that holds a string containing the current interpreter version. The current content of this string is "Lua 5.0". 

xpcall (f, err)
This function is similar to pcall, except that you can set a new error handler. 

xpcall calls function f in protected mode, using err as the error handler. Any error inside f is not propagated; instead, xpcall catches the error, calls the err function with the original error object, and returns a status code. Its first result is the status code (a boolean), which is true if the call succeeds without errors. In such case, xpcall also returns all results from the call, after this first result. In case of any error, xpcall returns false plus the result from err.
 

6 - Lua 独立程序 Lua Stand-alone
尽管Lua被设计为一种内嵌于C 语言宿主程序中的扩展语言,它还是经常被用作一个独立程序语言。一个Lua的解释程序将Lua作为一个独立的语言,我们称之为简化的 lua,它提供了标准的发行版本。独立的解释器包含了所有标准库加上反射性的调试接口。它的使用方式如下:

    lua [options] [script [args]]
options 可以是以下内容: 

- 将 标准输入(stdin) 当作文件执行; 
-e stat 执行字符串 stat; 
-l file “需要”file 文件; 
-i 在运行脚本后进入交互模式; 
-v 打印版本信息; 
-- 停止处理选项。
在停止处理选项后,lua 运行所给的脚本,传递所给参数 args。当无参数调用时,lua 就像 stdin 是程序的终结时 lua -v -i 所表现的一样,而且还与 lua- 一样。 

Before running any argument, the interpreter checks for an environment variable LUA_INIT. If its format is @filename, then lua executes the file. Otherwise, lua executes the string itself. 

All options are handled in order, except -i. For instance, an invocation like

    $ lua -e'a=1' -e 'print(a)' script.lua
will first set a to 1, then print a, and finally run the file script.lua. (Here, $ is the shell prompt. Your prompt may be different.) 

Before starting to run the script, lua collects all arguments in the command line in a global table called arg. The script name is stored in index 0, the first argument after the script name goes to index 1, and so on. The field n gets the number of arguments after the script name. Any arguments before the script name (that is, the interpreter name plus the options) go to negative indices. For instance, in the call 

    $ lua -la.lua b.lua t1 t2
the interpreter first runs the file a.lua, then creates a table 

    arg = { [-2] = "lua", [-1] = "-la.lua", [0] = "b.lua",
          [1] = "t1", [2] = "t2"; n = 2 }
and finally runs the file b.lua. 

在交互模式中,如果你写入了一个不完整的语句,解释器将等待你的完成。

If the global variable _PROMPT is defined as a string, then its value is used as the prompt. Therefore, the prompt can be changed directly on the command line: 

    $ lua -e"_PROMPT='myprompt> '" -i

(the outer pair of quotes is for the shell, the inner is for Lua), or in any Lua programs by assigning to _PROMPT. Note the use of -i to enter interactive mode; otherwise, the program would end just after the assignment to _PROMPT. 

在Unix系统中,Lua脚本可以用 chmod +x 将其变成可执行程序,并且通过 #! 形式,例如

#!/usr/local/bin/lua

(当然,Lua解释器的位置可能有所不同,如果 lua 在你的 PATH 中,那么

#!/usr/bin/env lua
就是一个更通用的解决方案。) 

致谢
The Lua team is grateful to Tecgraf for its continued support to Lua. We thank everyone at Tecgraf, specially the head of the group, Marcelo Gattass. At the risk of omitting several names, we also thank the following individuals for supporting, contributing to, and spreading the word about Lua: Alan Watson. André Clinio, André Costa, Antonio Scuri, Asko Kauppi, Bret Mogilefsky, Cameron Laird, Carlos Cassino, Carlos Henrique Levy, Claudio Terra, David Jeske, Ed Ferguson, Edgar Toernig, Erik Hougaard, Jim Mathies, John Belmonte, John Passaniti, John Roll, Jon Erickson, Jon Kleiser, Mark Ian Barlow, Nick Trout, Noemi Rodriguez, Norman Ramsey, Philippe Lhoste, Renata Ratton, Renato Borges, Renato Cerqueira, Reuben Thomas, Stephan Herrmann, Steve Dekorte, Thatcher Ulrich, Tomás Gorham, Vincent Penquerc'h. Thank you! 


--------------------------------------------------------------------------------

与以前版本的不兼容性 Incompatibilities with Previous Versions
Lua 5.0 是一个主版本,所有与 Lua 4.0 有一些地方不兼容。 

与 v4.0 的不兼容性 Incompatibilities with version 4.0
语言上的变动 
整个标签方法模式被元表所替代。The whole tag-method scheme was replaced by metatables. 
Function calls written between parentheses result in exactly one value. 
A function call as the last expression in a list constructor (like {a,b,f()}) has all its return values inserted in the list. 
The precedence of or is smaller than the precedence of and. 
in, false, and true are reserved words. 
The old construction for k,v in t, where t is a table, is deprecated (although it is still supported). Use for k,v in pairs(t) instead. 
When a literal string of the form [[...]] starts with a newline, this newline is ignored. 
Upvalues in the form %var are obsolete; use external local variables instead. 
库的变更
Most library functions now are defined inside tables. There is a compatibility script (compat.lua) that redefines most of them as global names. 
In the math library, angles are expressed in radians. With the compatibility script (compat.lua), functions still work in degrees. 
The call function is deprecated. Use f(unpack(tab)) instead of call(f, tab) for unprotected calls, or the new pcall function for protected calls. 
dofile does not handle errors, but simply propagates them. 
dostring is deprecated. Use loadstring instead. 
The read option w is obsolete. 
The format option %n$ is obsolete. 
API 上的改动
lua_open 不再需要堆栈大小作为参数(堆栈是动态的)。 
lua_pushuserdata 已经被废除了。使用 lua_newuserdata 或 lua_pushlightuserdata 来代替它。 
Lua 完整语法参考
     chunk ::= {stat [`;´]}

     block ::= chunk

     stat ::= varlist1 `=´ explist1 | functioncall | do block end | while exp do block end | repeat block until exp | if exp then block {elseif exp then block} [else block] end | return [explist1] | break | for Name `=´ exp `,´ exp [`,´ exp] do block end | for Name {`,´ Name} in explist1 do block end | function funcname funcbody | local function Name funcbody | local namelist [init] 

     funcname ::= Name {`.´ Name} [`:´ Name]

     varlist1 ::= var {`,´ var}

     var ::= Name | prefixexp `[´ exp `]´ | prefixexp `.´ Name 

     namelist ::= Name {`,´ Name}

     init ::= `=´ explist1

     explist1 ::= {exp `,´} exp

     exp ::= nil | false | true | Number | Literal | function | prefixexp | tableconstructor | exp binop exp | unop exp 

     prefixexp ::= var | functioncall | `(´ exp `)´

     functioncall ::= prefixexp args | prefixexp `:´ Name args 

     args ::= `(´ [explist1] `)´ | tableconstructor | Literal 

     function ::= function funcbody

     funcbody ::= `(´ [parlist1] `)´ block end

     parlist1 ::= Name {`,´ Name} [`,´ `...´] | `...´ 

     tableconstructor ::= `{´ [fieldlist] `}´

     fieldlist ::= field {fieldsep field} [fieldsep]
     field ::= `[´ exp `]´ `=´ exp | name `=´ exp | exp
     fieldsep ::= `,´ | `;´

     binop ::= `+´ | `-´ | `´ | `/´ | `^´ | `..´ | `<´ | `<=´ | `>´ | `>=´ | `==´ | `~=´ | and | or

     unop ::= `-´ | not
 

0
0

查看评论
* 以上用户言论只代表其个人观点,不代表CSDN网站的观点或立场
    个人资料
    • 访问:226896次
    • 积分:3204
    • 等级:
    • 排名:第10714名
    • 原创:73篇
    • 转载:154篇
    • 译文:0篇
    • 评论:34条
    博客专栏
    最新评论