logo资料库

Lua5.3全中文参考手册 云风翻译.pdf

第1页 / 共104页
第2页 / 共104页
第3页 / 共104页
第4页 / 共104页
第5页 / 共104页
第6页 / 共104页
第7页 / 共104页
第8页 / 共104页
资料共104页,剩余部分请下载后查看
2015/3/5 Lua 5.3 参考手册  Lua 5.3 参考手册 作者 Roberto Ierusalimschy, Luiz Henrique de Figueiredo, Waldemar Celes 译者 云风 Lua.org, PUC-Rio 版权所有 © 2015 , 在遵循 Lua license 条款下,可自由使用。 目录 ∙ 索引 ∙ 中英术语对照表 1 – 简介 Lua 是一门扩展式程序设计语言,被设计成支持通用过程式编程,并有相关数据描述 设施。  同时对面向对象编程、函数式编程和数据驱动式编程也提供了良好的支持。 它作为一个强大、轻量的嵌入式脚本语言,可供任何需要的程序使用。  Lua  由 clean C(标准 C 和 C++ 间共通的子集) 实现成一个库。 作为一门扩展式语言,Lua 没有 "main" 程序的概念: 它只能 嵌入 一个宿主程序中 工作, 该宿主程序被称为 被嵌入程序 或者简称 宿主 。 宿主程序可以调用函数执行 一小段 Lua 代码,可以读写 Lua 变量,可以注册 C 函数让 Lua 代码调用。 依靠 C 函数,Lua  可以共享相同的语法框架来定制编程语言,从而适用不同的领域。  Lua 的官方发布版包含一个叫做 lua 的宿主程序示例, 它是一个利用 Lua 库实现的完整 独立的 Lua 解释器,可用于交互式应用或批处理。 Lua  是一个自由软件,其使用许可证决定了它的使用过程无需任何担保。  本手册所 描述的实现可以在 Lua 的官方网站 www.lua.org 找到。 与其它的许多参考手册一样,这份文档有些地方比较枯燥。 关于 Lua 背后的设计思 想, 可以看看 Lua 网站上提供的技术论文。 至于用 Lua 编程的细节介绍, 请参阅 Roberto 的书,Programming in Lua。 2 – 基本概念 本章描述了语言的基本概念。 http://cloudwu.github.io/lua53doc/manual.html 1/104
2015/3/5 Lua 5.3 参考手册 2.1 – 值与类型 Lua 是一门动态类型语言。 这意味着变量没有类型;只有值才有类型。 语言中不设 类型定义。 所有的值携带自己的类型。 Lua 中所有的值都是 一等公民。 这意味着所有的值均可保存在变量中、 当作参数传 递给其它函数、以及作为返回值。 Lua 中有八种基本类型: nil、boolean、number、string、function、userdata、 thread 和 table。 Nil 是值 nil 的类型, 其主要特征就是和其它值区别开;通常用 来表示一个有意义的值不存在时的状态。  Boolean  是  false  与  true  两个值的类 型。 nil 和 false 都会导致条件判断为假; 而其它任何值都表示为真。 Number 代 表了整数和实数(浮点数)。 String 表示一个不可变的字节序列。 Lua 对 8 位是友 好的: 字符串可以容纳任意 8 位值, 其中包含零 ('\0')  。  Lua  的字符串与编码无 关; 它不关心字符串中具体内容。 number  类型有两种内部表现方式,  整数  和  浮点数。  对于何时使用哪种内部形 式,Lua 有明确的规则, 但它也按需(参见 §3.4.3)作自动转换。 因此,程序员多 数情况下可以选择忽略整数与浮点数之间的差异或者假设完全控制每个数字的内部表 现方式。 标准 Lua 使用 64 位整数和双精度(64 位)浮点数, 但你也可以把 Lua 编译成使用 32 位整数和单精度(32 位)浮点数。 以 32 位表示数字对小型机器以 及嵌入式系统特别合适。 (参见 luaconf.h 文件中的宏 LUA_32BITS 。) Lua 可以调用(以及操作)用 Lua 或 C (参见 §3.4.10)编写的函数。 这两种函数 有统一类型 function。 userdata 类型允许将 C 中的数据保存在 Lua 变量中。 用户数据类型的值是一个内 存块,  有两种用户数据:  完全用户数据  ,指一块由  Lua  管理的内存对应的对象; 轻量用户数据  ,则指一个简单的  C  指针。  用户数据在  Lua  中除了赋值与相等性判 断之外没有其他预定义的操作。 通过使用 元表 ,程序员可以给完全用户数据定义一 系列的操作 (参见 §2.4)。 你只能通过 C API 而无法在 Lua 代码中创建或者修改 用户数据的值, 这保证了数据仅被宿主程序所控制。 thread  类型表示了一个独立的执行序列,被用于实现协程  (参见  §2.6)。  Lua  的 线程与操作系统的线程毫无关系。  Lua  为所有的系统,包括那些不支持原生线程的 系统,提供了协程支持。 table  是一个关联数组,  也就是说,这个数组不仅仅以数字做索引,除了  nil  和 NaN 之外的所有 Lua 值 都可以做索引。 (Not a Number 是一个特殊的数字,它 用于表示未定义或表示不了的运算结果,比如 0/0。)  表可以是  异构  的;  也就是 说,表内可以包含任何类型的值( nil 除外)。 任何键的值若为 nil 就不会被记入表 结构内部。 换言之,对于表内不存在的键,都对应着值 nil 。 表是 Lua 中唯一的数据结构, 它可被用于表示普通数组、序列、符号表、集合、记 录、图、树等等。 对于记录,Lua 使用域名作为索引。 语言提供了 a.name 这样的 语法糖来替代 a["name"] 这种写法以方便记录这种结构的使用。 在 Lua 中有多种 便利的方式创建表(参见 §3.4.9)。 http://cloudwu.github.io/lua53doc/manual.html 2/104
2015/3/5 Lua 5.3 参考手册 我们使用 序列 这个术语来表示一个用 {1..n} 的正整数集做索引的表。 这里的非负整 数 n 被称为该序列的长度(参见 §3.4.7)。 和索引一样,表中每个域的值也可以是任何类型。  需要特别指出的是:既然函数是 一等公民,那么表的域也可以是函数。  这样,表就可以携带  方法  了。  (参见 §3.4.11)。 索引一张表的原则遵循语言中的直接比较规则。  当且仅当  i  与  j直接比较相等时 (即不通过元方法的比较), 表达式 a[i] 与 a[j] 表示了表中相同的元素。 特别指 出:一个可以完全表示为整数的浮点数和对应的整数相等  (例如:1.0  ==  1)。 为了消除歧义,当一个可以完全表示为整数的浮点数做为键值时,  都会被转换为对 应的整数储存。  例如,当你写  a[2.0]  =  true  时,  实际被插入表中的键是整数  2 。 (另一方面,2 与 "2" 是两个不同的 Lua 值, 故而它们可以是同一张表中的不同 项。) 表、函数、线程、以及完全用户数据在 Lua 中被称为 对象: 变量并不真的 持有 它 们的值,而仅保存了对这些对象的  引用。  赋值、参数传递、函数返回,都是针对引 用而不是针对值的操作, 这些操作均不会做任何形式的隐式拷贝。 库函数 type 用于以字符串形式返回给定值的类型。 (参见 §6.1)。 2.2 – 环境与全局环境 后面在 §3.2 以及 §3.3.3 会讨论, 引用一个叫 var 的自由名字(指在任何层级都未 被声明的名字) 在句法上都被翻译为 _ENV.var  。  此外,每个被编译的  Lua  代码 块都会有一个外部的局部变量叫 _ENV (参见 §3.3.2), 因此,_ENV 这个名字永 远都不会成为一个代码块中的自由名字。 在转译那些自由名字时,_ENV  是否是那个外部的局部变量无所谓。  _ENV  和其它 你可以使用的变量名没有区别。  这里特别指出,你可以定义一个新变量或指定一个 参数叫这个名字。 当编译器在转译自由名字时所用到的 _ENV , 指的是你的程序在 那个点上可见的那个名为 _ENV 的变量。 (Lua 的可见性规则参见 §3.5) 被 _ENV 用于值的那张表被称为 环境。 Lua 保有一个被称为 全局环境 特别环境。它被保存在 C 注册表 (参见 §4.5)的一 个特别索引下。 在 Lua 中,全局变量 _G 被初始化为这个值。 (_G 不被内部任何 地方使用。) 当  Lua  加载一个代码块,_ENV  这个上值的默认值就是这个全局环境  (参见 load)。  因此,在默认情况下,Lua  代码中提及的自由名字都指的全局环境中的相 关项  (因此,它们也被称为  全局变量  )。  此外,所有的标准库都被加载入全局环 境,一些函数也针对这个环境做操作。 你可以用 load (或 loadfile)加载代码块, 并赋予它们不同的环境。  (在  C  里,当你加载一个代码块后,可以通过改变它的第 一个上值来改变它的环境。) http://cloudwu.github.io/lua53doc/manual.html 3/104
2015/3/5 Lua 5.3 参考手册 2.3 – 错误处理 由于 Lua 是一门嵌入式扩展语言,其所有行为均源于宿主程序中 C 代码对某个 Lua 库函数的调用。 (单独使用 Lua 时,lua 程序就是宿主程序。) 所以,在编译或运 行 Lua 代码块的过程中,无论何时发生错误, 控制权都返回给宿主,由宿主负责采 取恰当的措施(比如打印错误消息)。 可以在 Lua 代码中调用 error 函数来显式地抛出一个错误。 如果你需要在 Lua 中捕 获这些错误, 可以使用 pcall 或 xpcall 在 保护模式 下调用一个函数。 无论何时出现错误,都会抛出一个携带错误信息的 错误对象 (错误消息)。 Lua 本 身只会为错误生成字符串类型的错误对象,  但你的程序可以为错误生成任何类型的 错误对象, 这就看你的 Lua 程序或宿主程序如何处理这些错误对象。 使用  xpcall  或  lua_pcall  时,  你应该提供一个  消息处理函数  用于错误抛出时调 用。  该函数需接收原始的错误消息,并返回一个新的错误消息。  它在错误发生后栈 尚未展开时调用,  因此可以利用栈来收集更多的信息,  比如通过探知栈来创建一组 栈回溯信息。  同时,该处理函数也处于保护模式下,所以该函数内发生的错误会再 次触发它(递归)。 如果递归太深,Lua 会终止调用并返回一个合适的消息。 2.4 – 元表及元方法 Lua 中的每个值都可以有一个 元表。 这个 元表 就是一个普通的 Lua 表, 它用于定 义原始值在特定操作下的行为。  如果你想改变一个值在特定操作下的行为,你可以 在它的元表中设置对应域。 例如,当你对非数字值做加操作时, Lua 会检查该值的 元表中的 "__add" 域下的函数。 如果能找到,Lua 则调用这个函数来完成加这个操 作。 元表中的键对应着不同的 事件 名; 键关联的那些值被称为 元方法。 在上面那个例 子中引用的事件为 "add" , 完成加操作的那个函数就是元方法。 你可以用 getmetatable 函数 来获取任何值的元表。 使用 setmetatable 来替换一张表的元表。在  Lua  中,你不可以改变表以外其它类 型的值的元表  (除非你使用调试库(参见§6.10));  若想改变这些非表类型的值 的元表,请使用 C API。 表和完全用户数据有独立的元表  (当然,多个表和用户数据可以共享同一个元 表)。 其它类型的值按类型共享元表; 也就是说所有的数字都共享同一个元表, 所 有的字符串共享另一个元表等等。  默认情况下,值是没有元表的,  但字符串库在初 始化的时候为字符串类型设置了元表 (参见 §6.4)。 元表决定了一个对象在数学运算、位运算、比较、连接、  取长度、调用、索引时的 行为。  元表还可以定义一个函数,当表对象或用户数据对象在垃圾回收  (参见 §2.5)时调用它。 http://cloudwu.github.io/lua53doc/manual.html 4/104
2015/3/5 Lua 5.3 参考手册 接下来会给出一张元表可以控制的事件的完整列表。  每个操作都用对应的事件名来 区分。 每个事件的键名用加有 '__' 前缀的字符串来表示; 例如 "add" 操作的键名 为字符串 "__add"。 注意、Lua 从元表中直接获取元方法; 访问元表中的元方法永 远不会触发另一次元方法。 下面的代码模拟了 Lua 从一个对象 obj 中获取一个元方 法的过程:      rawget(getmetatable(obj) or {}, "__" .. event_name) 对于一元操作符(取负、求长度、位反),  元方法调用的时候,第二个参数是个哑 元,其值等于第一个参数。 这样处理仅仅是为了简化 Lua 的内部实现 (这样处理可 以让所有的操作都和二元操作一致),  这个行为有可能在将来的版本中移除。  (使 用这个额外参数的行为都是不确定的。) "add": +  操作。  如果任何不是数字的值(包括不能转换为数字的字符串)做 加法,  Lua  就会尝试调用元方法。  首先、Lua  检查第一个操作数(即使它是 合法的), 如果这个操作数没有为 "__add" 事件定义元方法, Lua 就会接着 检查第二个操作数。  一旦  Lua  找到了元方法,  它将把两个操作数作为参数传 入元方法,  元方法的结果(调整为单个值)作为这个操作的结果。  如果找不 到元方法,将抛出一个错误。 "sub": ­ 操作。 行为和 "add" 操作类似。 "mul": * 操作。 行为和 "add" 操作类似。 "div": / 操作。 行为和 "add" 操作类似。 "mod": % 操作。 行为和 "add" 操作类似。 "pow": ^ (次方)操作。 行为和 "add" 操作类似。 "unm": ­ (取负)操作。 行为和 "add" 操作类似。 "idiv": // (向下取整除法)操作。 行为和 "add" 操作类似。 "band": & (按位与)操作。 行为和 "add" 操作类似, 不同的是 Lua 会在任 何一个操作数无法转换为整数时 (参见 §3.4.3)尝试取元方法。 "bor": | (按位或)操作。 行为和 "band" 操作类似。 "bxor": ~ (按位异或)操作。 行为和 "band" 操作类似。 "bnot": ~ (按位非)操作。 行为和 "band" 操作类似。 "shl": << (左移)操作。 行为和 "band" 操作类似。 "shr": >> (右移)操作。 行为和 "band" 操作类似。 "concat": .. (连接)操作。 行为和 "add" 操作类似, 不同的是 Lua 在任何 操作数即不是一个字符串  也不是数字(数字总能转换为对应的字符串)的情况 下尝试元方法。 "len":  #  (取长度)操作。  如果对象不是字符串,Lua  会尝试它的元方法。 如果有元方法,则调用它并将对象以参数形式传入,  而返回值(被调整为单 个)则作为结果。  如果对象是一张表且没有元方法,  Lua  使用表的取长度操 作(参见 §3.4.7)。 其它情况,均抛出错误。 "eq": == (等于)操作。 和 "add" 操作行为类似, 不同的是 Lua 仅在两个 值都是表或都是完全用户数据  且它们不是同一个对象时才尝试元方法。  调用 的结果总会被转换为布尔量。 "lt": < (小于)操作。 和 "add" 操作行为类似, 不同的是 Lua 仅在两个值 不全为整数也不全为字符串时才尝试元方法。  调用的结果总会被转换为布尔 量。 "le": <= (小于等于)操作。 和其它操作不同, 小于等于操作可能用到两个 http://cloudwu.github.io/lua53doc/manual.html 5/104
2015/3/5 Lua 5.3 参考手册 不同的事件。  首先,像  "lt"  操作的行为那样,Lua  在两个操作数中查找 "__le" 元方法。 如果一个元方法都找不到,就会再次查找 "__lt" 事件, 它会 假设 a <= b 等价于 not (b < a)。 而其它比较操作符类似,其结果会被转换 为布尔量。 "index": 索引 table[key]。 当 table 不是表或是表 table 中不存在 key 这 个键时,这个事件被触发。 此时,会读出 table 相应的元方法。 尽管名字取成这样,  这个事件的元方法其实可以是一个函数也可以是一张表。 如果它是一个函数,则以 table  和  key  作为参数调用它。  如果它是一张表, 最终的结果就是以  key  取索引这张表的结果。  (这个索引过程是走常规的流 程,而不是直接索引, 所以这次索引有可能引发另一次元方法。) "newindex": 索引赋值 table[key] = value 。 和索引事件类似,它发生在 table 不是表或是表 table 中不存在 key 这个键的时候。 此时,会读出 table 相应的元方法。 同索引过程那样,  这个事件的元方法即可以是函数,也可以是一张表。  如果 是一个函数, 则以 table、 key、以及 value 为参数传入。 如果是一张表, Lua 对这张表做索引赋值操作。 (这个索引过程是走常规的流程,而不是直接 索引赋值, 所以这次索引赋值有可能引发另一次元方法。) 一旦有了  "newindex"  元方法,  Lua  就不再做最初的赋值操作。  (如果有必 要,在元方法内部可以调用 rawset 来做赋值。) "call": 函数调用操作 func(args)。 当 Lua 尝试调用一个非函数的值的时候会 触发这个事件  (即  func  不是一个函数)。  查找  func  的元方法,  如果找得 func  作为第一个参数传入,原来调用的参数 到,就调用这个元方法,  (args)后依次排在后面。 2.5 – 垃圾收集 Lua  采用了自动内存管理。  这意味着你不用操心新创建的对象需要的内存如何分配 出来, 也不用考虑在对象不再被使用后怎样释放它们所占用的内存。 Lua 运行了一 个 垃圾收集器 来收集所有 死对象 (即在 Lua 中不可能再访问到的对象)来完成自 动内存管理的工作。  Lua  中所有用到的内存,如:字符串、表、用户数据、函数、 线程、 内部结构等,都服从自动管理。 Lua  实现了一个增量标记-扫描收集器。  它使用这两个数字来控制垃圾收集循环: 垃圾收集器间歇率  和  垃圾收集器步进倍率。  这两个数字都使用百分数为单位  (例 如:值 100 在内部表示 1 )。 垃圾收集器间歇率控制着收集器需要在开启新的循环前要等待多久。  增大这个值会 减少收集器的积极性。 当这个值比 100 小的时候,收集器在开启新的循环前不会有 等待。 设置这个值为 200 就会让收集器等到总内存使用量达到 之前的两倍时才开始 新的循环。 垃圾收集器步进倍率控制着收集器运作速度相对于内存分配速度的倍率。  增大这个 http://cloudwu.github.io/lua53doc/manual.html 6/104
2015/3/5 Lua 5.3 参考手册 值不仅会让收集器更加积极,还会增加每个增量步骤的长度。  不要把这个值设得小 于 100 , 那样的话收集器就工作的太慢了以至于永远都干不完一个循环。 默认值是 200 ,这表示收集器以内存分配的“两倍”速工作。 如果你把步进倍率设为一个非常大的数字  (比你的程序可能用到的字节数还大  10% ), 收集器的行为就像一个 stop-the-world 收集器。 接着你若把间歇率设为 200 ,  收集器的行为就和过去的  Lua  版本一样了:  每次  Lua  使用的内存翻倍时,就做 一次完整的收集。 你可以通过在  C  中调用  lua_gc  或在  Lua  中调用  collectgarbage  来改变这俩数 字。 这两个函数也可以用来直接控制收集器(例如停止它或重启它)。 2.5.1 – 垃圾收集元方法 你可以为表设定垃圾收集的元方法, 对于完全用户数据(参见 §2.4), 则需要使用 C API 。 该元方法被称为 终结器。  终结器允许你配合  Lua  的垃圾收集器做一些额 外的资源管理工作  (例如关闭文件、网络或数据库连接,或是释放一些你自己的内 存)。 如果要让一个对象(表或用户数据)在收集过程中进入终结流程, 你必须 标记 它需 要触发终结器。  当你为一个对象设置元表时,若此刻这张元表中用一个以字符串 "__gc" 为索引的域,那么就标记了这个对象需要触发终结器。 注意:如果你给对象 设置了一个没有  __gc  域的元表,之后才给元表加上这个域,  那么这个对象是没有 被标记成需要触发终结器的。  然而,一旦对象被标记,  你还是可以自由的改变其元 表中的 __gc 域的。 当一个被标记的对象成为了垃圾后,  垃圾收集器并不会立刻回收它。  取而代之的 是,Lua 会将其置入一个链表。 在收集完成后,Lua 将遍历这个链表。 Lua 会检查 每个链表中的对象的  __gc  元方法:如果是一个函数,那么就以对象为唯一参数调 用它; 否则直接忽略它。 在每次垃圾收集循环的最后阶段,  本次循环中检测到的需要被回收之对象,  其终结 器的触发次序按当初给对象作需要触发终结器的标记之次序的逆序进行;  这就是 说,第一个被调用的终结器是程序中最后一个被标记的对象所携的那个。  每个终结 器的运行可能发生在执行常规代码过程中的任意一刻。 由于被回收的对象还需要被终结器使用,  该对象(以及仅能通过它访问到的其它对 象)一定会被 Lua 复活。 通常,复活是短暂的,对象所属内存会在下一个垃圾收集 循环释放。  然后,若终结器又将对象保存去一些全局的地方  (例如:放在一个全局 变量里),这次复活就持续生效了。  此外,如果在终结器中对一个正进入终结流程 的对象再次做一次标记让它触发终结器,  只要这个对象在下个循环中依旧不可达, 它的终结函数还会再调用一次。  无论是哪种情况,  对象所属内存仅在垃圾收集循环 中该对象不可达且 没有被标记成需要触发终结器才会被释放。 当你关闭一个状态机(参见  lua_close),  Lua  将调用所有被标记了需要触发终结 器对象的终结过程,  其次序为标记次序的逆序。  在这个过程中,任何终结器再次标 记对象的行为都不会生效。 http://cloudwu.github.io/lua53doc/manual.html 7/104
2015/3/5 2.5.2 – 弱表 Lua 5.3 参考手册 弱表 指内部元素为 弱引用 的表。 垃圾收集器会忽略掉弱引用。 换句话说,如果一 个对象只被弱引用引用到, 垃圾收集器就会回收这个对象。 一张弱表可以有弱键或是弱值,也可以键值都是弱引用。  仅含有弱键的表允许收集 器回收它的键,但会阻止对值所指的对象被回收。  若一张表的键值均为弱引用,  那 么收集器可以回收其中的任意键和值。  任何情况下,只要键或值的任意一项被回 收,  相关联的键值对都会从表中移除。  一张表的元表中的  __mode  域控制着这张 表的弱属性。 当 __mode 域是一个包含字符 'k' 的字符串时,这张表的所有键皆为 弱引用。 当 __mode 域是一个包含字符 'v' 的字符串时,这张表的所有值皆为弱引 用。 属性为弱键强值的表也被称为 暂时表。 对于一张暂时表, 它的值是否可达仅取决于 其对应键是否可达。  特别注意,如果表内的一个键仅仅被其值所关联引用,  这个键 值对将被表内移除。 对一张表的弱属性的修改仅在下次收集循环才生效。 尤其是当你把表由弱改强,Lua 还是有可能在修改生效前回收表内一些项目。 只有那些有显式构造过程的对象才会从弱表中移除。  值,例如数字和轻量  C  函数, 不受垃圾收集器管辖, 因此不会从弱表中移除 (除非它们的关联项被回收)。 虽然 字符串受垃圾回收器管辖,  但它们没有显式的构造过程,所以也不会从弱表中移 除。 弱表针对复活的对象  (指那些正在走终结流程,仅能被终结器访问的对象)  有着特 殊的行为。  弱值引用的对象,在运行它们的终结器前就被移除了,  而弱键引用的对 象则要等到终结器运行完毕后,到下次收集当对象真的被释放时才被移除。  这个行 为使得终结器运行时得以访问到由该对象在弱表中所关联的属性。 如果一张弱表在当次收集循环内的复活对象中,  那么在下个循环前这张表有可能未 被正确地清理。 2.6 – 协程 Lua 支持协程,同时它也被称为 协同式多线程。 Lua 为每个协程提供一个独立的运 行序。  然而和多线程系统中的线程不同,  协程仅在显式地调用一个让出函数时才挂 起当前的执行状态。 通过调用  coroutine.create  可创建一个协程。  它唯一的参数是一个函数,这个函 数将作为这个协程的主函数。  create  函数仅仅创建出这个协程然后返回它的句柄 (一个类型为 thread 的对象); 它并不运行该协程。 通过调用  coroutine.resume  可执行一个协程。  第一次调用  coroutine.resume 时,第一个参数应传入 coroutine.create 返回的线程对象,这样协程就会从其主函 数的第一行开始执行。  coroutine.resume  后面的参数将作为主函数的参数传入。 协程将一直运行到它结束或 让出。 http://cloudwu.github.io/lua53doc/manual.html 8/104
分享到:
收藏