• 设为首页
  • 点击收藏
  • 手机版
    手机扫一扫访问
    迪恩网络手机版
  • 关注官方公众号
    微信扫一扫关注
    公众号

lua 5.3 英文手册 google机器翻译版

原作者: [db:作者] 来自: [db:来源] 收藏 邀请

LUA Lua 5.3参考手册
作者:Roberto Ierusalimschy,Luiz Henrique de Figueiredo,Waldemar Celes

版权所有©2015-2018 Lua.org,PUC-Rio。根据Lua许可条款免费提供 。

内容 · 索引 · 其他版本
1 - 简介
Lua是一种功能强大,高效,轻量级,可嵌入的脚本语言。它支持过程编程,面向对象编程,函数编程,数据驱动编程和数据描述。

Lua将简单的过程语法与基于关联数组和可扩展语义的强大数据描述结构相结合。Lua是动态类型的,通过使用基于寄存器的虚拟机解释字节码来运行,并具有增量垃圾收集的自动内存管理,使其成为配置,脚本和快速原型设计的理想选择。

Lua实现为一个库,用干净的C编写,这是标准C和C ++的通用子集。Lua发行版包括一个名为的主机程序lua,它使用Lua库提供完整的独立Lua解释器,用于交互式或批量使用。Lua旨在用作任何需要的程序的强大,轻量级,可嵌入的脚本语言,以及作为一种功能强大但轻量级且高效的独立语言。

作为一种扩展语言,Lua没有“主要”程序的概念:它嵌入在主机客户端中,称为嵌入程序或简称主机。(通常,这个主机是独立的lua程序。)主机程序可以调用函数来执行一段Lua代码,可以编写和读取Lua变量,并且可以注册要由Lua代码调用的C函数。通过使用C函数,可以扩展Lua以应对各种不同的域,从而创建共享语法框架的定制编程语言。

Lua是免费软件,按照许可证的规定,照常提供,不提供担保。Lua的官方网站上提供了本手册中描述的实现www.lua.org。

与任何其他参考手册一样,本文档在某些地方是干燥的。有关Lua设计背后的决策的讨论,请参阅Lua网站上提供的技术论文。有关Lua编程的详细介绍,请参阅Roberto的书“ Lua编程”。

2 - 基本概念
本节介绍该语言的基本概念。

2.1 - 价值观和类型
Lua是一种动态类型语言。这意味着变量没有类型; 只有价值观。该语言中没有类型定义。所有值都有自己的类型。

Lua中的所有值都是一等值。这意味着所有值都可以存储在变量中,作为参数传递给其他函数,并作为结果返回。

Lua中有八种基本类型: nil,boolean,number, string,function,userdata, thread和table。nil类型只有一个值nil,其主要属性与任何其他值不同; 它通常代表缺乏有用的价值。boolean类型有两个值,false和true。两个零和假赚了条件为假; 任何其他价值都是真的。该类型的号码表示整数和实数(浮点)数。类型字符串表示不可变的字节序列。Lua是8位干净的:字符串可以包含任何8位值,包括嵌入的零(\' \0\')。Lua也是编码不可知的; 它没有假设字符串的内容。

类型号使用两个内部表示,或两个子类型,一个称为整数,另一个称为float。Lua有关于何时使用每个表示的明确规则,但它也会根据需要自动转换它们(参见§3.4.3)。因此,程序员可以选择主要忽略整数和浮点数之间的差异,或者假设完全控制每个数字的表示。标准Lua使用64位整数和双精度(64位)浮点数,但您也可以编译Lua,以便它使用32位整数和/或单精度(32位)浮点数。对于小型机器和嵌入式系统,整数和浮点数的32位选项特别有吸引力。(见宏LUA_32BITS在文件中luaconf.h。)

Lua可以调用(和操作)用Lua编写的函数和用C编写的函数(参见§3.4.10)。两者都由类型函数表示。

提供 类型userdata以允许任意C数据存储在Lua变量中。userdata值表示原始内存块。有两种用户数据: 完整用户数据,一个由Lua管理的内存块的对象,以及轻用户数据,它只是一个C指针值。除了赋值和身份测试之外,Userdata在Lua中没有预定义的操作。通过使用元表,程序员可以定义完整用户数据值的操作(参见§2.4)。只能通过C API在Lua中创建或修改Userdata值。这保证了主机程序拥有的数据的完整性。

类型线程表示独立的执行线程,它用于实现协同程序(参见§2.6)。Lua线程与操作系统线程无关。Lua支持所有系统上的协同程序,甚至是那些本身不支持线程的协同程序。

类型表实现关联数组,即不仅可以包含数字作为索引的数组,还可以包含除nil和NaN 之外的任何Lua值。(非数字是用于表示未定义或不可表示的数值结果的特殊值,例如0/0。)表可以是异构的 ; 也就是说,它们可以包含所有类型的值(除了nil)。值为nil的任何键都不被视为表的一部分。相反,任何不属于表的键都具有关联值nil。

表是Lua中唯一的数据结构机制; 它们可用于表示普通数组,列表,符号表,集合,记录,图形,树等。为了表示记录,Lua使用字段名称作为索引。该语言通过提供a.name语法糖来支持这种表示a["name"]。有几种方便的方法可以在Lua中创建表(参见§3.4.9)。

与索引一样,表字段的值可以是任何类型。特别是,因为函数是第一类值,所以表字段可以包含函数。因此,表格也可以带有方法(见§3.4.11)。

表的索引遵循语言中原始相等的定义。表达式a[i]和a[j] 表示相同的表元素当且仅当i和j原始相等(即,没有元方法时相等)。特别是,具有整数值的浮点数等于它们各自的整数(例如1.0 == 1)。为避免歧义,任何具有用作键的整数值的浮点数都将转换为其各自的整数。例如,如果你写a[2.0] = true,插入表中的实际键将是整数2。(另一方面,2和“ 2”是不同的Lua值,因此表示不同的表条目。)

表,函数,线程和(完整)用户数据值是对象:变量实际上不包含这些值,只是对它们的引用。赋值,参数传递和函数返回总是操纵对这些值的引用; 这些操作并不意味着任何形式的复制。

库函数type返回一个描述给定值类型的字符串(参见§6.1)。

2.2 - 环境与全球环境
如将在要讨论§3.2和§3.3.3,到自由名称的任何引用(即,不束缚于任何声明的名称)var 在语法翻译成_ENV.var。而且,每个块都在一个名为_ENV(参见§3.3.2)的外部局部变量的范围内编译,因此_ENV它本身永远不是块中的自由名称。

尽管存在这个外部_ENV变量和自由名称的翻译 ,但这_ENV是一个完全规则的名称。特别是,您可以使用该名称定义新变量和参数。每个对自由名称的引用都使用_ENV在程序中该点可见的,遵循Lua的常见可见性规则(参见§3.5)。

用作值的任何表_ENV称为环境。

Lua保持着一个称为全球环境的杰出环境。该值保存在C注册表中的特殊索引处(参见§4.5)。在Lua中,全局变量_G用相同的值初始化。(_G从不在内部使用。)

当Lua加载块时,其_ENVupvalue 的默认值是全局环境(请参阅参考资料load)。因此,默认情况下,Lua代码中的自由名称是指全局环境中的条目(因此,它们也称为全局变量)。此外,所有标准库都加载到全局环境中,并且那里的一些功能在该环境中运行。您可以使用load(或loadfile)加载具有不同环境的块。(在C中,您必须加载块,然后更改其第一个upvalue的值。)

2.3 - 错误处理
因为Lua是一种嵌入式扩展语言,所以所有Lua操作都从宿主程序中的C代码开始,从Lua库调用一个函数。(当您使用Lua standalone时,lua应用程序是主程序。)每当编译或执行Lua块时发生错误,控制权将返回主机,主机可以采取适当的措施(例如打印错误消息)。

Lua代码可以通过调用error函数显式生成错误 。如果需要捕获Lua中的错误,可以使用pcall或xpcall 以受保护模式调用给定函数。

每当出现错误时,都会传播错误对象(也称为错误消息),其中包含有关错误的信息。Lua本身只生成错误对象是字符串的错误,但程序可能会生成任何值的错误作为错误对象。由Lua程序或其主机来处理这样的错误对象。

当您使用xpcall或时lua_pcall,您可以 在出现错误时调用消息处理程序。使用原始错误对象调用此函数并返回新的错误对象。它在错误展开堆栈之前调用,以便它可以收集有关错误的更多信息,例如通过检查堆栈并创建堆栈回溯。此消息处理程序仍受受保护的调用保护; 所以,消息处理程序中的错误将再次调用消息处理程序。如果这个循环持续太长时间,Lua会打破它并返回一个适当的消息。(仅针对常规运行时错误调用消息处理程序。在运行终结器时,不会调用内存分配错误或错误。)

2.4 - Metatables和Metamethods
Lua中的每个值都可以有一个metatable。此metatable是一个普通的Lua表,它定义了某些特殊操作下原始值的行为。您可以通过在其元表中设置特定字段来更改操作行为的几个方面。例如,当非数字值是加法的操作数时,Lua检查__add值的metatable 字段“ ”中的函数。如果找到一个,Lua会调用此函数来执行添加。

metatable中每个事件的键是一个字符串,其事件名称前缀为两个下划线; 相应的值称为metamethods。在前面的示例中,键是“ __add”,而metamethod是执行添加的函数。除非另有说明,否则metamethods应该是函数值。

您可以使用该getmetatable函数查询任何值的元表。Lua使用原始访问查询元数据中的元方法(请参阅参考资料rawget)。因此,为了检索ev对象中事件的元方法o,Lua完全等效于以下代码:

rawget(getmetatable(o)或{},“__ ev ”)
您可以使用该setmetatable函数替换表的元表。您无法从Lua代码更改其他类型的元表(除非使用调试库(第6.10节)); 你应该使用C API。

表和完整的userdata具有单独的元表(尽管多个表和userdata可以共享其元表)。所有其他类型的值每种类型共享一个metatable; 也就是说,所有数字都有一个metatable,一个用于所有字符串,等等。默认情况下,一个值没有metatable,但是字符串库为字符串类型设置了metatable(参见§6.4)。

metatable控制对象在算术运算,按位运算,顺序比较,串联,长度运算,调用和索引中的行为方式。当用户数据或表被垃圾收集时,metatable也可以定义要调用的函数(第2.5节)。

对于一元运算符(否定,长度和按位NOT),计算元方法并使用虚拟的第二个操作数调用,等于第一个操作数。这个额外的操作数只是为了简化Lua的内部(通过使这些操作符像二进制操作一样),并且可能在将来的版本中被删除。(对于大多数用途,这个额外的操作数是无关紧要的。)

接下来给出由元表控制的事件的详细列表。每个操作由其相应的键标识。

__add: addition(+)操作。如果添加的任何操作数不是数字(也不是对数字强制的字符串),Lua将尝试调用metamethod。首先,Lua将检查第一个操作数(即使它是有效的)。如果该操作数没有定义元方法__add,那么Lua将检查第二个操作数。如果Lua可以找到一个metamethod,它会将两个操作数作为参数调用metamethod,并且调用的结果(调整为一个值)是操作的结果。否则,它会引发错误。
__sub: 减法(-)操作。行为类似于添加操作。
__mul: multiplication(*)操作。行为类似于添加操作。
__div: division(/)操作。行为类似于添加操作。
__mod: modulo(%)操作。行为类似于添加操作。
__pow: exponentiation(^)操作。行为类似于添加操作。
__unm: 否定(一元-)操作。行为类似于添加操作。
__idiv: floor division(//)操作。行为类似于添加操作。
__band: 按位AND(&)操作。类似于加法运算的行为,除了如果任何操作数既不是整数也不是对整数可强制的值,Lua将尝试元方法(参见§3.4.3)。
__bor: 按位OR(|)操作。行为类似于按位AND运算。
__bxor: 按位异或(二进制~)运算。行为类似于按位AND运算。
__bnot: 按位NOT(一元~)运算。行为类似于按位AND运算。
__shl: 按位左移(<<)运算。行为类似于按位AND运算。
__shr: 按位右移(>>)运算。行为类似于按位AND运算。
__concat: concatenation(..)操作。类似于加法运算的行为,除了如果任何操作数既不是字符串也不是数字(对于字符串总是可强制的),Lua将尝试元方法。
__len: length(#)操作。如果对象不是字符串,Lua将尝试其metamethod。如果存在metame方法,Lua将该对象作为参数调用,并且调用的结果(始终调整为一个值)是操作的结果。如果没有metamethod但对象是表,则Lua使用表长度操作(参见§3.4.7)。否则,Lua会引发错误。
__eq: equal(==)操作。类似于加法运算的行为,除了Lua将仅在被比较的值既是表或两个完整用户数据并且它们不是原始相等时尝试元方法。调用的结果总是转换为布尔值。
__lt: 小于(<)操作。行为类似于加法运算,除了Lua只在被比较的值既不是数字也不是两个字符串时都会尝试元方法。调用的结果总是转换为布尔值。
__le: 较少的equal(<=)操作。与其他操作不同,不太相等的操作可以使用两个不同的事件。首先,Lua __le在两个操作数中寻找元方法,就像在少于操作中一样。如果它找不到这样的元方法,那么它将尝试__ltmetame方法,假设a <= b相当于not (b < a)。与其他比较运算符一样,结果始终是布尔值。(__lt在将来的版本中可以删除此事件的使用;它也比真正的__le元方法慢。)
__index: 索引访问操作table[key]。当事件table不是表或key不存在时,会发生此事件table。查找了metamethod table。
尽管有这个名字,这个事件的元方法可以是函数或表。如果它是一个函数,则使用table和key作为参数调用它,并且调用的结果(调整为一个值)是操作的结果。如果它是一个表,则最终结果是将该表索引的结果key。(这种索引是常规的,而不是原始的,因此可以触发另一种元方法。)

__newindex: 索引分配table[key] = value。与索引事件一样,此事件发生在table不是表或key不存在时table。查找了metamethod table。
与索引一样,此事件的元方法可以是函数或表。如果它是一个功能,它被称为用table,key以及value作为参数。如果它是一个表,Lua会使用相同的键和值对此表进行索引分配。(这项任务是有规律的,不是原始的,因此可以触发另一种元方法。)

每当有一个__newindex元方法时,Lua就不会执行原始赋值。(如果有必要,metamethod本身可以调用rawset 来进行赋值。)

__call: 通话操作func(args)。当Lua尝试调用非函数值(即,func不是函数)时,会发生此事件。查找了metamethod func。如果存在,则使用func第一个参数调用元方法,然后调用原始调用(args)的参数。调用的所有结果都是操作的结果。(这是唯一允许多个结果的元方法。)
在将其设置为某个对象的元表之前,将所有需要的元方法添加到表中是一种很好的做法。特别是,元__gc方法仅在遵循此顺序时才起作用(参见§2.5.1)。

因为元表是常规表,所以它们可以包含任意字段,而不仅仅是上面定义的事件名称。标准库中的某些函数(例如tostring)使用元表中的其他字段用于它们自己的目的。

2.5 - 垃圾收集
Lua执行自动内存管理。这意味着您不必担心为新对象分配内存或在不再需要对象时释放它。Lua通过运行垃圾收集器来自动管理内存,以收集所有死对象 (即不再可从Lua访问的对象)。Lua使用的所有内存都受自动管理:字符串,表,用户数据,函数,线程,内部结构等。

Lua实现了一个增量的标记和扫描收集器。它使用两个数字来控制其垃圾收集周期:垃圾收集器暂停和垃圾收集器步骤倍增器。两者都使用百分点作为单位(例如,值100表示​​内部值为1)。

垃圾收集器暂停控制收集器在开始新循环之前等待的时间。较大的值使收集器不那么具有攻击性。小于100的值意味着收集器不会等待开始新的循环。值200表示收集器在开始新循环之前等待使用的总内存加倍。

垃圾收集器步骤乘法器控制收集器相对于内存分配的相对速度。较大的值会使收集器更具侵略性,但也会增加每个增量步骤的大小。您不应该使用小于100的值,因为它们会使收集器太慢并且可能导致收集器永远不会完成一个循环。默认值为200,这意味着收集器以“两倍”的内存分配速度运行。

如果将步长乘数设置为一个非常大的数字(大于程序可能使用的最大字节数的10%),则收集器的行为就像一个世界上的停止收集器。如果然后将暂停设置为200,则收集器的行为与旧的Lua版本相同,每次Lua将其内存使用量增加一倍时执行完整集合。

您可以通过lua_gc在C或collectgarbageLua中调用来更改这些数字。您也可以使用这些函数直接控制收集器(例如,停止并重新启动它)。

2.5.1 - 垃圾收集Metamethods
您可以为表设置垃圾收集器元方法,并使用C API为完整用户数据设置(参见§2.4)。这些元方法也称为终结器。终结器允许您协调Lua的垃圾收集与外部资源管理(例如关闭文件,网络或数据库连接,或释放您自己的内存)。

对于要在收集时完成的对象(表或用户数据),必须将其标记为最终确定。在设置metatable时标记对象以进行最终化,并且metatable具有由字符串“ __gc” 索引的字段。请注意,如果在没有__gc字段的情况下设置元表,然后在元表中创建该字段,则不会标记该对象以进行最终化。

当标记的对象变为垃圾时,垃圾收集器不会立即收集它。相反,Lua把它放在一个列表中。收集完成后,Lua会查看该列表。对于列表中的每个对象,它检查对象的__gcmetamethod:如果它是一个函数,Lua将该对象作为其单个参数调用它; 如果metame方法不是函数,Lua就会忽略它。

在每个垃圾收集周期结束时,对象的终结器按照与该周期中收集的对象被标记为完成的相反顺序进行调用; 也就是说,要调用的第一个终结器是与程序中最后标记的对象相关联的终结器。每个终结器的执行可以在执行常规代码期间的任何时刻发生。

因为正在收集的对象仍然必须由终结器使用,所以该对象(以及只能通过它访问的其他对象)必须由Lua 复活。通常,这种复活是暂时的,并且在下一个垃圾收集周期中释放对象存储器。但是,如果终结器将对象存储在某个全局位置(例如,全局变量),则复活是永久性的。此外,如果终结器再次标记最终化的终结对象,则将在下一个无法访问对象的循环中再次调用其终结器。在任何情况下,仅在GC循环中释放对象存储器,其中对象不可到达且未标记为完成。

当你关闭一个状态(参见lua_close参考资料)时,Lua按照标记的相反顺序调用标记为完成的所有对象的终结符。如果任何终结器在该阶段标记要收集的对象,则这些标记无效。

2.5.2 - 弱表
一个弱表是一个表,它的元素是 弱引用。垃圾收集器忽略弱引用。换句话说,如果对对象的唯一引用是弱引用,则垃圾收集器将收集该对象。

弱表可以具有弱键,弱值或两者。具有弱值的表允许收集其值,但会阻止其键的收集。具有弱键和弱值的表允许收集键和值。在任何情况下,如果收集了密钥或值,则从表中删除整个对。表的弱点__mode由其元表的字段控制 。如果该__mode字段是包含字符\' k\' 的字符串,则表中的键很弱。如果__mode包含\' v\',则表中的值很弱。

具有弱键和强值的表也称为ephemeron表。在ephemeron表中,只有在其密钥可访问时,才认为该值是可到达的。特别是,如果对键的唯一引用通过其值,则删除该对。

表的弱点的任何变化可能仅在下一个收集周期生效。特别是,如果您将弱点更改为更强的模式,Lua可能会在更改生效之前从该表中收集一些项目。

仅从弱表中删除具有显式构造的对象。诸如数字和轻C函数之类的值不受垃圾收集的影响,因此不会从弱表中删除(除非收集它们的相关值)。虽然字符串可以进行垃圾回收,但它们没有明确的构造,因此不会从弱表中删除。

复活的对象(即,最终确定的对象和仅通过最终确定的对象可访问的对象)在弱表中具有特殊行为。它们在运行终结器之前从弱值中删除,但在运行终结器后,实际释放这些对象时,仅在下一个集合中从弱键中删除它们。此行为允许终结器通过弱表访问与对象关联的属性。

如果在收集周期中复活的对象中存在弱表,则在下一个周期之前可能无法正确清除它。

2.6 - 协同程序
Lua支持协程,也称为协作多线程。Lua中的协程代表一个独立的执行线程。但是,与多线程系统中的线程不同,协程只通过显式调用yield函数来暂停其执行。

你通过调用创建一个协同程序coroutine.create。它唯一的论点是一个函数,它是协程的主要功能。该create函数只创建一个新的协同程序并返回一个句柄(一个类型为thread的对象); 它没有启动协程。

你通过调用执行一个协同程序coroutine.resume。当你第一次调用时coroutine.resume,作为第一个参数传递一个返回的线程coroutine.create,协同程序通过调用它的main函数来启动它的执行。传递给的额外参数coroutine.resume作为参数传递给该函数。协程开始运行后,它会一直运行直到它终止或产生。

协程可以通过两种方式终止其执行:通常,当其主函数返回时(显式或隐式地,在最后一条指令之后); 如果出现无保护错误,则异常。在正常终止的情况下, coroutine.resume返回true,以及coroutine main函数返回的任何值。如果出现错误,则coroutine.resume返回false 加上错误对象。

一个协程通过调用产生coroutine.yield。当协程生成时,相应的coroutine.resume返回立即返回,即使在嵌套函数调用内发生了收益(也就是说,不在main函数中,而是在main函数直接或间接调用的函数中)。在yield的情况下,coroutine.resume也返回true,加上传递给的任何值coroutine.yield。下次你恢复相同的协程时,它会从它产生的点继续执行,并调用coroutine.yield返回传递给它的任何额外参数coroutine.resume。

就像coroutine.create,该coroutine.wrap函数还创建了一个协程,但它不是返回协程本身,而是返回一个函数,当被调用时,它恢复协程。传递给此函数的任何参数都作为额外的参数coroutine.resume。 coroutine.wrap返回coroutine.resume除第一个(布尔错误代码)之外的所有返回值。不同coroutine.resume, coroutine.wrap不会发现错误; 任何错误都会传播给调用者。

作为协同程序如何工作的示例,请考虑以下代码:

功能foo(a)
print(“foo”,a)
return coroutine.yield(2 * a)
结束

co = coroutine.create(function(a,b)
print(“co-body”,a,b)
本地r = foo(a + 1)
print(“co-body”,r)
本地r,s = coroutine.yield(a + b,ab)
print(“co-body”,r,s)
返回b,“结束”
结束)

print(“main”,coroutine.resume(co,1,10))
print(“main”,coroutine.resume(co,“r”))
print(“main”,coroutine.resume(co,“x”,“y”))
print(“main”,coroutine.resume(co,“x”,“y”))
运行它时,它会产生以下输出:

共同体1 10
foo 2
主要真实4
共同体
主要是真实的11-9
共同体xy
主要真正的10结束
主要的假不能恢复死coroutine
您还可以创建和操作通过C API协同程序:看功能lua_newthread,lua_resume和lua_yield。

3 - 语言
本节描述了Lua的lexis,语法和语义。换句话说,本节描述了哪些令牌有效,它们如何组合以及它们的组合意味着什么。

语言结构将使用通常的扩展BNF表示法来解释,其中{ a }表示0或更多a,而[ a ]表示可选的a。非终端显示为非终端,关键字显示为kword,其他终端符号显示为\' = \'。Lua的完整语法可以在 本手册末尾的§9中找到。

3.1 - 词汇约定
Lua是一种自由形式的语言。它忽略了空格(包括新行)和词法元素(标记)之间的注释,除了名称和关键字之间的分隔符。

Lua中的名称 (也称为标识符)可以是任何字母,数字和下划线字符串,不以数字开头而不是保留字。标识符用于命名变量,表字段和标签。

以下关键字是保留的,不能用作名称:

并打破其他的结果
函数goto为false,如果在
当地没有或重复返回
然后真实,直到
Lua是一种区分大小写的语言: and是一个保留字,但And它AND 是两个不同的有效名称。作为惯例,程序应避免创建以下划线开头的名称,后跟一个或多个大写字母(例如_VERSION)。

以下字符串表示其他标记:

+ - * /%^#
&〜| << >> //
==〜= <=> = <> =
(){} [] ::
; :,。......
甲短文本字符串 可以通过匹配单引号或双引号进行分隔,并且可以包含下面的C状的转义序列:“ \a”(钟形),“ \b”(退格),“ \f”(形式进料),“ \n”(换行), \' \r\'(回车),\' \t\'(水平标签),\' \v\'(垂直标签),\' \\\'(反斜杠),\' \"\'(引号[双引号])和\' \\'\'(撇号[单引号])。反斜杠后跟换行符会在字符串中生成换行符。逃脱序列\'\z\'跳过以下白色空格字符,包括换行符; 将长文字字符串分解并缩进多行而不将新行和空格添加到字符串内容中特别有用。短文字字符串不能包含未转义的换行符,也不能包含未形成有效转义序列的转义符。

我们可以通过其数值(包括嵌入的零)指定短文字字符串中的任何字节。这可以通过转义序列来完成,其中XX是正好两个十六进制数字的序列,或者使用转义序列,其中ddd是最多三个十进制数字的序列。(请注意,如果一个十进制转义序列后跟一个数字,则必须使用正好三位数表示。) \xXX\ddd

可以将Unicode字符的UTF-8编码插入带有转义序列的文字字符串中 (请注意必需的括号),其中XXX是一个表示字符代码点的一个或多个十六进制数字的序列。 \u{XXX}

也可以使用长括号括起来的长格式来定义文字字符串。我们将n级的开口长支架定义为开口方括号,后跟n个等号后跟另一个开口方括号。因此,写入级别0 [[的开始长括号,将级别1的开始长括号写为[=[,依此类推。甲长括号被类似地定义; 例如,级别4的结束长括号写为 ]====]。一个长的文字从任何级别的开口长支架开始,并在同一级别的第一个关闭长支架处结束。它可以包含除同一级别的右括号之外的任何文本。这种括号中的文字可以运行多行,不解释任何转义序列,并忽略任何其他级别的长括号。任何类型的行结束序列(回车符,换行符,回车后跟换行符,换行符后换行符)都会转换为简单的换行符。

为方便起见,当开头长括号后面紧跟换行符时,换行符不包含在字符串中。例如,在使用ASCII的系统中(其中\' a\'编码为97,换行符编码为10,\' 1\'编码为49),下面的五个文字字符串表示相同的字符串:

a =\'alo \ n123“\'
a =“alo \ n123 \”“
a =\'\ 97lo \ 10 \ 04923“\'
a = [[alo
123" ]]
a = [== [
ALO
123" ] ==]
未受先前规则明确影响的文字字符串中的任何字节都表示自身。但是,Lua打开文件以在文本模式下进行解析,并且系统文件函数可能存在某些控制字符的问题。因此,将非文本数据表示为带有非文本字符的显式转义序列的带引号的文字更安全。

甲数字常数(或标记)可以与一个可选的小数部分和可选的小数指数被写入,由字母“标记e”或“ E”。Lua也接受十六进制常量,以0x或开头0X。十六进制常量也接受可选的小数部分加上可选的二进制指数,用字母\' p\'或\' P\' 标记。带小数点或指数的数字常量表示浮点数; 否则,如果其值适合整数,则表示整数。有效整数常量的示例是

3 345 0xff 0xBEBADA
有效浮点常量的示例是

3.0 3.1416 314.16e-2 0.31416E1 34e1
0x0.1E 0xA23p-4 0X1.921FB54442D18P + 1
一个注释开始用双连字符(--)的字符串以外的任何地方。如果紧接着的文本--不是开头长括号,则注释是一个简短的注释,一直运行到行的结尾。否则,它是一个长注释,直到相应的结束长括号。长注释经常用于临时禁用代码。

3.2 - 变量
变量是存储值的地方。Lua中有三种变量:全局变量,局部变量和表字段。

单个名称可以表示全局变量或局部变量(或函数的形式参数,它是一种特殊的局部变量):

var :: = Name
名称表示标识符,如§3.1中所定义。

除非明确声明为本地变量名称,否则任何变量名称都被假定为全局变量名称(参见§3.3.7)。局部变量是词法范围的:局部变量可以通过其范围内定义的函数自由访问(参见§3.5)。

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

方括号用于索引表:

var :: = prefixexp\' [ \'exp\' ] \'
访问表字段的含义可以通过元表更改(参见§2.4)。

语法var.Name只是语法糖 var["Name"]:

var :: = prefixexp\' 。\' 名称
对全局变量的访问x 等同于_ENV.x。由于编译块的方式, _ENV永远不是全局名称(参见§2.2)。

3.3 - 陈述
Lua支持几乎常规的语句集,类似于Pascal或C中的语句。该集包括赋值,控制结构,函数调用和变量声明。

3.3.1 - 块
块是一个语句列表,按顺序执行:

block :: = {stat}
Lua 有空语句 ,允许您用分号分隔语句,用分号开始一个块或按顺序写两个分号:

stat :: =\' ; “
函数调用和赋值可以以左括号开头。这种可能性导致了Lua语法的模糊性。请考虑以下片段:

a = b + c
(打印或io.write)(\'完成\')
语法可以通过两种方式看到它:

a = b + c(print或io.write)(\'done\')

a = b + c; (打印或io.write)(\'完成\')
当前解析器总是以第一种方式看到这样的结构,将开括号解释为调用参数的开始。为避免这种歧义,最好始终在以括号开头的分号语句之前:

;(打印或io.write)(\'完成\')
可以明确分隔块以生成单个语句:

stat :: = do block end
显式块对于控制变量声明的范围很有用。显式块有时也用于在另一个块的中间添加一个return语句(参见§3.3.4)。

3.3.2 - 大块
Lua的编译单元称为块。从语法上讲,块只是一个块:

chunk :: = block
Lua将一个块作为具有可变数量参数的匿名函数的主体处理(参见§3.4.11)。因此,块可以定义局部变量,接收参数和返回值。此外,这种匿名函数被编译为外部局部变量的范围_ENV(见§2.2)。结果函数始终具有_ENV唯一的up值,即使它不使用该变量。

块可以存储在宿主程序内的文件或字符串中。为了执行一个块,Lua首先加载它,将块的代码预编译为虚拟机的指令,然后Lua用虚拟机的解释器执行编译的代码。

块也可以预编译成二进制形式; 详情请参阅程序luac和功能string.dump。源代码和编译形式的程序是可以互换的; Lua会自动检测文件类型并采取相应措施(请参阅参考资料load)。

3.3.3 - 分配
Lua允许多项任务。因此,赋值语法定义左侧的变量列表和右侧的表达式列表。两个列表中的元素用逗号分隔:

stat :: = varlist\' = \'explist
varlist :: = var {\' , \'var}
explist :: = exp {\' , \'exp}
表达式在§3.4中讨论。

在赋值之前,将值列表调整为变量列表的长度。如果值多于所需值,则会丢弃多余的值。如果值少于所需值,则列表将根据需要扩展为 nil。如果表达式列表以函数调用结束,则该调用返回的所有值在调整之前输入值列表(除非将调用括在括号中;请参阅§3.4)。

赋值语句首先计算其所有表达式,然后才执行赋值。因此代码

我= 3
我,a [i] = i + 1,20
设置a[3]为20,不会影响,a[4] 因为i在a[i]分配之前评估(到3)。同样,该行

x,y = y,x
的交流值x和y,和

x,y,z = y,z,x
周期性的置换的值x,y和z。

对全局名称x = val 的赋值等同于赋值 _ENV.x = val(参见§2.2)。

表字段和全局变量(实际上也是表字段)的赋值的含义可以通过元表更改(参见§2.4)。

3.3.4 - 控制结构
控制结构 if,while和repeat具有通常的含义和熟悉的语法:

stat :: = while exp do block end
stat :: = 重复阻止直到 exp
stat :: = if exp then block { elseif exp then block} [ else block] end
Lua还有一种for语句,有两种形式(见§3.3.5)。

控制结构的条件表达式可以返回任何值。这两种假和零被认为是假的。不同于nil和false的所有值都被认为是真的(特别是,数字0和空字符串也是真的)。

在repeat - until循环中,内部块不以until关键字结束,而是仅在条件之后。因此,条件可以引用循环块内声明的局部变量。

将跳转语句将程序控制的标签。出于语法原因,Lua中的标签也被视为语句:

stat :: = goto名称
stat :: =标签
label :: =\' :: \'Name\' :: \'
标签在定义它的整个块中是可见的,除了嵌套块内,其中定义了具有相同名称的标签并在嵌套函数内。只要goto没有进入局部变量的范围,它就可以跳转到任何可见标签。

标签和空语句称为void语句,因为它们不执行任何操作。

在休息的语句终止执行 ,同时,重复,或用于循环,跳到循环之后的下一条语句:

stat :: = break
甲休息结束该最内层循环。

该返回语句用来从一功能或一组块(这是一个匿名功能)返回值。函数可以返回多个值,因此return语句的语法是

stat :: = return [explist] [\' ; “]
在返回语句只能写成块的最后陈述。如果确实需要在块的中间返回,则可以使用显式内部块,如在习语中do return end,因为现在返回是其(内部)块中的最后一个语句。

3.3.5 - 声明
该对语句有两种形式:一个数字和一个通用的。

数值为同时控制变量通过算术级数运行循环重复的代码块。它具有以下语法:

stat :: = for Name\' = \'exp\' , \'exp [\' , \'exp] do block end
从第一个exp的值开始重复 该块的名称,直到它通过第三个exp的步骤通过第二个exp。更确切地说,一个for语句就像

对于v = e1,e2,e3做阻止结束
相当于代码:


本地变种,极限,步骤 = tonumber(E1),tonumber(E2),tonumber(E3)
如果不是(var和limit和step)则error()end
var = var - step
虽然如此
var = var + step
if(step > = 0且var > limit)或(step <0和var < limit)然后
打破
结束
本地v = var

结束
结束
请注意以下事项:

在循环开始之前,所有三个控制表达式仅被评估一次。它们都必须导致数字。
var,, limit和step是不可见的变量。此处显示的名称仅用于说明目的。
如果不存在第三表达式(步骤),则使用步骤1。
您可以使用break和goto退出for循环。
循环变量v是循环体的局部变量。如果在循环后需要其值,请在退出循环之前将其分配给另一个变量。
泛型for语句适用于函数,称为迭代器。在每次迭代时,调用迭代器函数以生成新值,在此新值为nil时停止。泛型for循环具有以下语法:

STAT :: = 对于名称列表中 explist 不阻止端
名称列表:: = {名称“ ”名称}
一个for语句就像

对于var_1,...,explist中的var_n执行块结束
相当于代码:


local f,s,var = explist
虽然如此
local var_1,...,var_n = f(s,var)
如果var_1 == nil则断开结束
var = var_1

结束
结束
请注意以下事项:

explist仅评估一次。其结果是迭代器函数,状态和第一个迭代器变量的初始值。
f,, s和var是不可见的变量。这些名称仅供参考。
您可以使用break来退出for循环。
循环变量var_i是循环的局部变量; 之后,你不能用自己的价值观为目的。如果需要这些值,则在断开或退出循环之前将它们分配给其他变量。
3.3.6 - 函数调用作为语句
为了允许可能的副作用,函数调用可以作为语句执行:

stat :: = functioncall
在这种情况下,所有返回的值都将被丢弃。函数调用在§3.4.10中解释。

3.3.7 - 本地声明
局部变量可以在块内的任何位置声明。声明可以包括初始分配:

stat :: = local namelist [\' = \'explist]
如果存在,初始赋值具有与多赋值相同的语义(参见§3.3.3)。否则,所有变量都用nil初始化。

块也是一个块(参见§3.3.2),因此局部变量可以在任何显式块之外的块中声明。

局部变量的可见性规则在§3.5中解释。

3.4 - 表达式
Lua中的基本表达式如下:

exp :: = prefixexp
exp :: = nil | 假 | 真正
exp :: =数字
exp :: = LiteralString
exp :: = functiondef
exp :: = tableconstructor
exp :: =\' ... \'
exp :: = exp binop exp
exp :: = unop exp
prefixexp :: = var | functioncall | \' ( \'exp\' ) \'
数字和文字字符串在§3.1中解释; 变量在§3.2中解释; 函数定义在§3.4.11中解释; 函数调用在§3.4.10中解释; 表构造函数在§3.4.9中解释。Vararg表达式由三个点(\' ...\')表示,只能在直接在vararg函数内部时使用; 它们在§3.4.11中解释。

二元运算符包括算术运算符(参见§3.4.1),按位运算符(参见§3.4.2),关系运算符(参见§3.4.4),逻辑运算符(参见§3.4.5)和连接运算符(参见§) 3.4.6)。一元运算符包括一元减号(见§3.4.1),一元按位NOT(见§3.4.2),一元逻辑非(见§3.4.5)和一元长度运算符(见§3.4.7) 。

函数调用和vararg表达式都可能导致多个值。如果函数调用用作语句(参见§3.3.6),则其返回列表将调整为零元素,从而丢弃所有返回的值。如果表达式用作表达式列表的最后一个(或唯一)元素,则不进行任何调整(除非表达式括在括号中)。在所有其他上下文中,Lua将结果列表调整为一个元素,要么丢弃除第一个之外的所有值,要么在没有值时添加单个nil。

这里有些例子:

f() - 调整为0结果
g(f(),x) - f()调整为1
g(x,f()) - g得到x加上f()的所有结果
a,b,c = f(),x - f()调整为1结果(c变为零)
a,b = ... - a得到第一个vararg参数,b得到
- 第二个(如果有的话,a和b都可以为零)
- 没有相应的vararg论点)

a,b,c = x,f() - f()调整为2个结果
a,b,c = f() - f()调整为3个结果
return f() - 返回f()的所有结果
return ... - 返回所有收到的vararg参数
return x,y,f() - 返回x,y和f()的所有结果
{f()} - 创建一个包含f()所有结果的列表
{...} - 创建一个包含所有vararg参数的列表
{f(),nil} - f()调整为1
括在括号中的任何表达式始终只生成一个值。因此,(f(x,y,z))即使f返回多个值, 也始终是单个值。(的值(f(x,y,z))是由所返回的第一值f 或者零,如果f没有返回任何值。)

3.4.1 - 算术运算符
Lua支持以下算术运算符:

+:另外
-:减法
*:乘法
/:浮动师
//:地板师
%: modulo
^:取幂
-:一元减去
除了取幂和浮点除法之外,算术运算符的工作方式如下:如果两个操作数都是整数,则操作在整数上执行,结果为整数。否则,如果两个操作数都是可以转换为数字的数字或字符串(参见§3.4.3),那么它们将转换为浮点数,操作按照通常的浮点运算规则(通常是IEEE 754标准)执行,结果是浮动。

Exponentiation和float division(/)总是将它们的操作数转换为浮点数,结果总是浮点数。Exponentiation使用ISO C函数pow,因此它也适用于非整数指数。

Floor division(//)是一个将商舍入负无穷大的分区,即其操作数的分区。

模数被定义为将商舍入负无穷大(分层)的除法的余数。

在整数运算中出现溢出的情况下,根据二补码算法的通常规则,所有运算都会回绕。(换句话说,它们将与模数为2 64的唯一可表示整数返回到数学结果。)

3.4.2 - 按位运算符
Lua支持以下按位运算符:

&:按位AND
|:按位OR
~:按位异或
>>:右转
<<:左移
~:一元按位NOT
所有按位运算都将其操作数转换为整数(参见§3.4.3),对这些整数的所有位进行操作,并产生整数。

右移和左移都用零填充空位。负位移向另一个方向移动; 绝对值等于或高于整数中的位数的位移导致零(因为所有位都被移出)。

3.4.3 - 强制和转换
Lua在运行时提供某些类型和表示之间的一些自动转换。按位运算符总是将浮点操作数转换为整数。Exponentiation和float除法总是将整数操作数转换为浮点数。应用于混合数字(整数和浮点数)的所有其他算术运算将整数操作数转换为浮点数; 这被称为通常的规则。根据需要,C API还将整数转换为浮点数并将浮点数转换为整数。此外,字符串连接除了字符串之外还接受数字作为参数。

每当有数字时,Lua也会将字符串转换为数字。

在从整数到浮点数的转换中,如果整数值具有精确的表示形式,那么就是结果。否则,转换得到最接近的较高或最接近的较低可表示值。这种转换永远不会失败。

从float到integer的转换检查float是否具有整数的精确表示(即,float具有整数值,并且它在整数表示的范围内)。如果是,则表示结果。否则,转换失败。

从字符串到数字的转换如下:首先,按照语法和Lua词法分析器的规则将字符串转换为整数或浮点数。(该字符串可能还有前导和尾随空格以及符号。)然后,结果数(浮点数或整数)将转换为上下文所需的类型(浮点数或整数)(例如,强制转换的操作)。

从字符串到数字的所有转换都接受点和当前区域设置标记作为基数字符。(然而,Lua lexer只接受一个点。)

从数字到字符串的转换使用未指定的人类可读格式。要完全控制数字如何转换为字符串,请使用format字符串库中的函数(请参阅参考资料string.format)。

3.4.4 - 关系运算符
Lua支持以下关系运算符:

==:平等
~=:不平等
<:不到
>:大于
<=:更少或相等
>=:更大或更平等
这些运算符总是导致错误或真实。

Equality(==)首先比较其操作数的类型。如果类型不同,则结果为false。否则,比较操作数的值。字符串以明显的方式进行比较。如果它们表示相同的数学值,则数字相等。

通过引用比较表,userdata和线程:只有当两个对象是同一个对象时才认为它们是相等的。每次创建新对象(表,用户数据或线程)时,此新对象都不同于以前存在的任何对象。闭包总是等于它自己。具有任何可检测差异(不同行为,不同定义)的闭包总是不同的。在不同时间创建但没有可检测到的差异的闭包可以被分类为相同或不同(取决于内部缓存细节)。

您可以使用“eq”元方法更改Lua比较表和userdata的方式(参见§2.4)。

等式比较不会将字符串转换为数字,反之亦然。因此,"0"==0计算结果为假,并t[0]和t["0"]在表中表示不同的条目。

运算符~=恰好是对equality(==)的否定。

订单操作符的工作方式如下。如果两个参数都是数字,则根据它们的数学值(不管它们的子类型)进行比较。否则,如果两个参数都是字符串,则根据当前区域设置比较它们的值。否则,Lua会尝试调用“lt”或“le”元方法(参见§2.4)。比较a > b被翻译成b < a 并被a >= b翻译成b <= a。

遵循IEEE 754标准,NaN被认为既不小于,也不等于,也不大于任何值(包括其本身)。

3.4.5 - 逻辑运算符
Lua中的逻辑运算符是 和,或者是,而不是。与控制结构一样(参见§3.3.4),所有逻辑运算符都将false和nil视为false,将其他任何内容视为true。

否定运算符并不总是返回false或true。如果此值为false或nil,则为连接运算符并返回其第一个参数; 否则,并且返回第二个参数。如果此值与nil和false不同,则析取运算符或返回其第一个参数; 否则,或返回其第二个参数。既与和或使用短路评价; 也就是说,仅在必要时评估第二个操作数。这里有些例子:

10或20 - > 10
10或错误() - > 10
零或“a” - >“a”
零和10 - >零
false和error() - > false
假和零 - >假
false或nil - > nil
10和20 - > 20
(在本手册中, -->表示前面表达式的结果。)

3.4.6 - 连接
Lua中的字符串连接运算符由两个点(\' ..\')表示。如果两个操作数都是字符串或数字,则它们将根据§3.4.3中描述的规则转换为字符串。否则,__concat调用metamethod(参见§2.4)。

3.4.7 - 长度算子
长度运算符由一元前缀运算符表示#。

字符串的长度是它的字节数(也就是每个字符是一个字节时字符串长度的通常含义)。

应用于表的长度运算符返回该表中的边框。表中的边框t是满足以下条件的任何自然数:

(border == 0或t [border]〜= nil)和t [border + 1] == nil
换句话说,边框是表中的任何(自然)索引,其中非零值后跟零值(或者当索引1为零时为零)。

只有一个边框的表称为序列。例如,该表{10, 20, 30, 40, 50}是一个序列,因为它只有一个边框(5)。该表{10, 20, 30, nil, 50}有两个边框(3和5),因此它不是一个序列。该表{nil, 20, 30, nil, nil, 60, nil} 有三个边框(0,3和6),所以它也不是一个序列。该表{}是带边框0的序列。请注意,非自然键不会干扰表是否为序列。

何时t是序列, #t返回其唯一的边界,这对应于序列长度的直观概念。当t不是序列时, #t可以返回其任何边界。(确切的一个取决于表的内部表示的细节,而这又取决于表的填充方式和非数字键的内存地址。)

表的长度的计算具有保证的最差时间O(log n),其中n是表中最大的自然键。

程序可以通过元方法修改长度运算符对任何值的行为,但是字符串可以通过元方法__len修改(参见§2.4)。

3.4.8 - 优先顺序
Lua中的运算符优先级遵循下表,从较低优先级到较高优先级:

要么

<> <=> =〜= ==
|


<< >>
..
+ -
* / //%
一元运算符(不是# - 〜)
^
像往常一样,您可以使用括号来更改表达式的优先级。连接(\' ..\')和取幂(\' ^\')运算符是右关联的。所有其他二元运算符都是左关联的。

3.4.9 - 表构造函数
表构造函数是创建表的表达式。每次评估构造函数时,都会创建一个新表。构造函数可用于创建空表或创建表并初始化其某些字段。构造函数的一般语法是

tableconstructor :: =\' { \'[fieldlist]\' } \'
fieldlist :: = field {fieldsep field} [fieldsep]
field :: =\' [ \'exp\' ] \'\' = \'exp | 名称\' = \'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}
相当于


本地t = {}
t [f(1)] = g
t [1] =“x” - 第1次曝光
t [2] =“y” - 第二次曝光
tx = 1 - t [“x”] = 1
t [3] = f(x) - 第3个exp
t [30] = 23
t [4] = 45 - 第4次exp
a = t
结束
构造函数中赋值的顺序未定义。(此顺序仅在有重复键时才有意义。)

如果列表中的最后一个字段具有表单exp ,并且表达式是函数调用或vararg表达式,则此表达式返回的所有值将连续输入列表(请参见§3.4.10)。

字段列表可以有一个可选的尾随分隔符,以方便机器生成的代码。

3.4.10 - 函数调用
Lua中的函数调用具有以下语法:

functioncall :: = prefixexp args
在函数调用中,首先计算prefixexp和args。如果prefixexp的值具有type 函数,则使用给定的参数调用此函数。否则,调用prefixexp“call”元方法,将prefixexp的值作为第一个参数,后跟原始调用参数(参见§2.4)。

表格

functioncall :: = prefixexp\' : \'名称args
可以用来称呼“方法”。一个调用 是语法糖,除了只评估一次。 v:name(args)v.name(v,args)v

参数具有以下语法:

args :: =\' ( \'[explist]\' ) \'
args :: = tableconstructor
args :: = LiteralString
在调用之前计算所有参数表达式。形式的调用是语法糖; 也就是说,参数列表是一个新表。形式 (或或)的调用是语法糖; 也就是说,参数列表是单个文字字符串。 f{fields}f({fields})f\'string\'f"string"f[[string]]f(\'string\')

表单调用称为尾调用。Lua实现了正确的尾调用 (或正确的尾递归):在尾调用中,被调用函数重用调用函数的堆栈条目。因此,程序可以执行的嵌套尾调用的数量没有限制。但是,尾调用会删除有关调用函数的所有调试信息。请注意,尾调用只发生在特定的语法中,其中返回只有一个函数调用作为参数; 这种语法使调用函数完全返回被调用函数的返回值。因此,以下示例都不是尾调用: return functioncall

return(f(x)) - 结果调整为1
返回2 * f(x)
return x,f(x) - 其他结果
F(X); 返回 - 结果丢弃
返回x或f(x) - 结果调整为1
3.4.11 - 功能定义
函数定义的语法是

functiondef :: = function funcbody
funcbody :: =\' ( \'[parlist]\' ) \'阻止结束
以下语法糖简化了函数定义:

stat :: = function funcname funcbody
stat :: = local 函数名称funcbody
funcname :: = Name {\' 。\'姓名} [\' : \'姓名]
该声明

函数f()正文结束
翻译成

f = function()正文结束
该声明

函数tabcf()正文结束
翻译成

tabcf = function()正文结束
该声明

局部函数f()体端
翻译成

当地的f; f = function()正文结束
不要

局部f = function()体端
(这仅在函数体包含对的引用时才有所不同f。)

函数定义是可执行表达式,其值具有类型函数。当Lua预编译一个块时,它的所有函数体也都被预编译。然后,每当Lua执行函数定义时,该函数被实例化(或关闭)。此函数实例(或闭包)是表达式的最终值。

参数充当使用参数值初始化的局部变量:

parlist :: = namelist [\' , \'\' ... \'] | \' ... \'
调用函数时,参数列表将调整为参数列表的长度,除非该函数是vararg函数,该...参数由参数列表末尾的三个点(\' \')表示。vararg函数不调整其参数列表; 相反,它收集所有额外的参数,并通过vararg表达式将它们提供给函数,也写成三个点。此表达式的值是所有实际额外参数的列表,类似于具有多个结果的函数。如果在另一个表达式内或表达式列表的中间使用vararg表达式,则将其返回列表调整为一个元素。如果表达式用作表达式列表的最后一个元素,则不进行任何调整(除非最后一个表达式括在括号中)。

例如,请考虑以下定义:

函数f(a,b)结束
函数g(a,b,...)结束
函数r()返回1,2,3结束
然后,我们从参数到参数以及vararg表达式进行以下映射:

调用参数

f(3)a = 3,b =零
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,... - >(没有)
g(3,4)a = 3,b = 4,... - >(无)
g(3,4,5,8)a = 3,b = 4,...... - > 5 8
g(5,r())a = 5,b = 1,...... - > 2 3
使用return语句返回结果(参见§3.3.4)。如果控件到达函数的末尾而没有遇到return语句,则函数返回时没有结果。

函数可能返回的值的数量存在系统相关限制。此限制保证大于1000。

在结肠语法用于定义方法,即,具有一个隐含的额外的参数的功能self。因此,声明

功能tabc:f(params)正文结束
是语法糖

tabcf = function(self,params)body end
3.5 - 可见性规则
Lua是一种词汇范围的语言。局部变量的范围从其声明后的第一个语句开始,一直持续到包含声明的最内层块的最后一个非void语句。请考虑以下示例:

x = 10 - 全局变量
做 - 新块
local x = x - new\'x\',值为10
print(x) - > 10
x = x + 1
做 - 另一个块
局部x = x + 1 - 另一个\'x\'
print(x) - > 12
结束
print(x) - > 11
结束
print(x) - > 10(全球一个)
请注意,在声明中local x = x,声明的新x声明尚未在范围内,因此第二个x引用外部变量。

由于词法范围规则,局部变量可以由其范围内定义的函数自由访问。内部函数使用的局部变量在内部函数内称为upvalue或外部局部变量。

请注意,每次执行本地语句都会定义新的局部变量。请考虑以下示例:

a = {}
本地x = 20
对于i = 1,10做
本地y = 0
a [i] = function()y = y + 1; 返回x + y结束
结束
循环创建十个闭包(即匿名函数的十个实例)。这些闭包中的每一个都使用不同的y变量,而它们都共享相同的变量x。

4 - 应用程序接口
本节介绍Lua的C API,即主机程序可用于与Lua通信的C函数集。所有API函数以及相关类型和常量都在头文件中声明lua.h。

即使我们使用术语“函数”,API中的任何工具也可以作为宏提供。除非另有说明,否则所有这些宏都只使用它们的每个参数一次(除了第一个参数,它总是一个Lua状态),因此不会产生任何隐藏的副作用。

与大多数C库一样,Lua API函数不检查其参数的有效性或一致性。但是,您可以通过使用LUA_USE_APICHECK定义的宏编译Lua来更改此行为。

Lua库是完全可重入的:它没有全局变量。它将所需的所有信息保存在称为Lua状态的动态结构中。

每个Lua状态具有一个或多个线程,其对应于独立的协作执行线。类型lua_State(尽管名称)指的是一个线程。(间接地,通过线程,它还指向与线程关联的Lua状态。)

必须将指向线程的指针作为第一个参数传递给库中的每个函数,除了lua_newstate,它从头创建一个Lua状态并返回一个指向新状态的主线程的指针。

4.1 - 堆栈
Lua使用虚拟堆栈向C传递值。此堆栈中的每个元素表示Lua值(nil,number,string等)。API中的函数可以通过它们接收的Lua状态参数访问此堆栈。

每当Lua调用C时,被调用的函数将获得一个新的堆栈,该堆栈独立于先前的堆栈和仍处于活动状态的C函数堆栈。该堆栈最初包含C函数的任何参数,它是C函数可以存储临时Lua值的地方,并且必须将其结果推送回调用者(请参阅参考资料lua_CFunction)。

为方便起见,API中的大多数查询操作都不遵循严格的堆栈规则。相反,它们可以通过使用索引引用堆栈中的任何元素:正索引表示绝对堆栈位置(从1开始); 负索引表示相对于堆栈顶部的偏移量。更具体地说,如果堆栈有n个元素,则索引1表示第一个元素(即,首先被压入堆栈的元素),索引 n表示最后一个元素; index -1也表示最后一个元素(即顶部的元素),index -n表示第一个元素。

4.2 - 堆栈大小
当您与Lua API交互时,您有责任确保一致性。特别是, 您负责控制堆栈溢出。您可以使用该函数lua_checkstack 确保堆栈有足够的空间来推送新元素。

每当Lua调用C时,它确保堆栈具有至少LUA_MINSTACK额外插槽的空间。 LUA_MINSTACK定义为20,因此通常您不必担心堆栈空间,除非您的代码具有将元素推送到堆栈的循环。

当你在没有固定数量结果的情况下调用Lua函数时(参见lua_call参考资料),Lua确保堆栈有足够的空间容纳所有结果,但它不能确保任何额外的空间。所以,在这样的调用之后推送堆栈中的任何东西之前你应该使用lua_checkstack。

4.3 - 有效和可接受的指数
接收堆栈索引的API中的任何函数仅适用于有效索引或可接受的索引。

甲有效的索引是指存储可修改的Lua值的位置的索引。它包含1和堆栈顶部(1 ≤ abs(index) ≤ top)加上伪索引之间的堆栈索引,它们表示C代码可访问但不在堆栈中的某些位置。伪索引用于访问注册表(参见§4.5)和C函数的upvalues(参见§4.4)。

可以使用可接受的索引调用不需要特定可变位置但仅需要值(例如,查询函数)的函数。一个可以接受的指数可以是任何有效指标,但它也可以分配给栈的空间内栈顶之后的任何积极的指标,也就是指数高达堆栈大小。(注意0绝不是可接受的索引。)除非另有说明,否则A​​PI中的函数使用可接受的索引。

可接受的索引用于在查询堆栈时避免对堆栈顶部进行额外测试。例如,C函数可以查询其第三个参数,而无需首先检查是否存在第三个参数,即无需检查3是否为有效索引。

对于可以使用可接受索引调用的函数,任何无效索引都被视为包含虚拟类型的值LUA_TNONE,其行为类似于nil值。

4.4 - C闭包
当创建C函数时,可以将一些值与它相关联,从而创建一个C闭包 (参见参考资料lua_pushcclosure); 这些值称为upvalues,只要调用它,函数就可以访问它们。

每当调用C函数时,其upvalues都位于特定的伪索引处。这些伪指数由宏产生 lua_upvalueindex。与函数关联的第一个upvalue是索引 lua_upvalueindex(1),依此类推。任何访问,其中n大于当前函数的upvalues的数量(但不大于256,这是一个加上闭包中upvalues的最大数量),产生一个可接受但无效的索引。 lua_upvalueindex(n)

4.5 - 注册表
Lua提供了一个注册表,一个预定义的表,任何C代码都可以使用它来存储它需要存储的任何Lua值。注册表始终位于伪索引处 LUA_REGISTRYINDEX。任何C库都可以将数据存储到此表中,但必须注意选择与其他库使用的键不同的键,以避免冲突。通常,您应该使用包含您的库名称的字符串作为键,或者使用代码中具有C对象地址的轻用户数据,或者您的代码创建的任何Lua对象。与变量名一样,以下划线后跟大写字母开头的字符串键保留给Lua。

注册表中的整数键由引用机制(请参阅参考资料luaL_ref)和某些预定义值使用。因此,整数键不得用于其他目的。

当您创建新的Lua状态时,其注册表会附带一些预定义的值。这些预定义值使用定


鲜花

握手

雷人

路过

鸡蛋
该文章已有0人参与评论

请发表评论

全部评论

专题导读
上一篇:
lua去掉字符串中的UTF-8的BOM三个字节发布时间:2022-07-22
下一篇:
php+redis+lua实现分布式锁(转载)发布时间:2022-07-22
热门推荐
热门话题
阅读排行榜

扫描微信二维码

查看手机版网站

随时了解更新最新资讯

139-2527-9053

在线客服(服务时间 9:00~18:00)

在线QQ客服
地址:深圳市南山区西丽大学城创智工业园
电邮:jeky_zhao#qq.com
移动电话:139-2527-9053

Powered by 互联科技 X3.4© 2001-2213 极客世界.|Sitemap