2 – 基本概念

原文:https://www.lua.org/manual/5.4/manual.html

2 – Basic Concepts – 基本概念

This section describes the basic concepts of the language.

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

2.1 – Values and Types 值和类型

Lua is a dynamically typed language. This means that variables do not have types; only values do. There are no type definitions in the language. All values carry their own type.

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

All values in Lua are first-class values. This means that all values can be stored in variables, passed as arguments to other functions, and returned as results.

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

There are eight basic types in Lua: nil, boolean, number, string, function, userdata, thread, and table. The type nil has one single value, nil, whose main property is to be different from any other value; it often represents the absence of a useful value. The type boolean has two values, false and true. Both nil and false make a condition false; they are collectively called false values. Any other value makes a condition true. Despite its name, false is frequently used as an alternative to nil, with the key difference that false behaves like a regular value in a table, while a nil in a table represents an absent key.

​ Lua 中有八种基本类型:nil、布尔值、数字、字符串、函数、用户数据、线程和表。nil 类型只有一个值,即 nil,其主要属性是与任何其他值不同;它通常表示没有有用的值。布尔值类型有两个值,false 和 true。nil 和 false 都使条件为 false;它们统称为 false 值。任何其他值都会使条件为 true。尽管其名称为 false,但它经常用作 nil 的替代,关键区别在于 false 在表中表现得像一个常规值,而表中的 nil 表示一个缺失的键。

The type number represents both integer numbers and real (floating-point) numbers, using two subtypes: integer and float. Standard Lua uses 64-bit integers and double-precision (64-bit) floats, but you can also compile Lua so that it uses 32-bit integers and/or single-precision (32-bit) floats. The option with 32 bits for both integers and floats is particularly attractive for small machines and embedded systems. (See macro LUA_32BITS in file luaconf.h.)

​ 类型数字使用两个子类型(整数和浮点数)来表示整数和实数(浮点数)。标准 Lua 使用 64 位整数和双精度(64 位)浮点数,但您也可以编译 Lua,以便它使用 32 位整数和/或单精度(32 位)浮点数。对于小型机器和嵌入式系统,同时使用 32 位整数和浮点数的选项特别有吸引力。(请参阅文件 luaconf.h 中的宏 LUA_32BITS 。)

Unless stated otherwise, any overflow when manipulating integer values wrap around, according to the usual rules of two-complement arithmetic. (In other words, the actual result is the unique representable integer that is equal modulo 2n to the mathematical result, where n is the number of bits of the integer type.)

​ 除非另有说明,否则在操作整数值时发生的任何溢出都会根据二进制补码算术的常规规则进行环绕。(换句话说,实际结果是唯一的可表示整数,它模 2 n 等于数学结果,其中 n 是整数类型的位数。)

Lua has explicit rules about when each subtype is used, but it also converts between them automatically as needed (see §3.4.3). Therefore, the programmer may choose to mostly ignore the difference between integers and floats or to assume complete control over the representation of each number.

​ Lua 有明确的规则规定何时使用每个子类型,但它也会根据需要在它们之间自动转换(请参阅 §3.4.3)。因此,程序员可以选择基本上忽略整数和浮点数之间的差异,或者完全控制每个数字的表示。

The type string represents immutable sequences of bytes. Lua is 8-bit clean: strings can contain any 8-bit value, including embedded zeros (’\0’). Lua is also encoding-agnostic; it makes no assumptions about the contents of a string. The length of any string in Lua must fit in a Lua integer.

​ 类型字符串表示不可变的字节序列。Lua 是 8 位干净的:字符串可以包含任何 8 位值,包括嵌入的零(’ \0 ‘)。Lua 也是与编码无关的;它对字符串的内容不做任何假设。Lua 中任何字符串的长度都必须适合 Lua 整数。

Lua can call (and manipulate) functions written in Lua and functions written in C (see §3.4.10). Both are represented by the type function.

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

The type userdata is provided to allow arbitrary C data to be stored in Lua variables. A userdata value represents a block of raw memory. There are two kinds of userdata: full userdata, which is an object with a block of memory managed by Lua, and light userdata, which is simply a C pointer value. Userdata has no predefined operations in Lua, except assignment and identity test. By using metatables, the programmer can define operations for full userdata values (see §2.4). Userdata values cannot be created or modified in Lua, only through the C API. This guarantees the integrity of data owned by the host program and C libraries.

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

The type thread represents independent threads of execution and it is used to implement coroutines (see §2.6). Lua threads are not related to operating-system threads. Lua supports coroutines on all systems, even those that do not support threads natively.

​ 线程类型表示独立的执行线程,用于实现协程(参见 §2.6)。Lua 线程与操作系统线程无关。Lua 在所有系统上都支持协程,即使那些本机不支持线程的系统。

The type table implements associative arrays, that is, arrays that can have as indices not only numbers, but any Lua value except nil and NaN. (Not a Number is a special floating-point value used by the IEEE 754 standard to represent undefined numerical results, such as 0/0.) Tables can be heterogeneous; that is, they can contain values of all types (except nil). Any key associated to the value nil is not considered part of the table. Conversely, any key that is not part of a table has an associated value nil.

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

Tables are the sole data-structuring mechanism in Lua; they can be used to represent ordinary arrays, lists, symbol tables, sets, records, graphs, trees, etc. To represent records, Lua uses the field name as an index. The language supports this representation by providing a.name as syntactic sugar for a["name"]. There are several convenient ways to create tables in Lua (see §3.4.9).

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

Like indices, the values of table fields can be of any type. In particular, because functions are first-class values, table fields can contain functions. Thus tables can also carry methods (see §3.4.11).

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

The indexing of tables follows the definition of raw equality in the language. The expressions a[i] and a[j] denote the same table element if and only if i and j are raw equal (that is, equal without metamethods). In particular, floats with integral values are equal to their respective integers (e.g., 1.0 == 1). To avoid ambiguities, any float used as a key that is equal to an integer is converted to that integer. For instance, if you write a[2.0] = true, the actual key inserted into the table will be the integer 2.

​ 表的索引遵循语言中原始相等性的定义。表达式 a[i]a[j] 仅当 ij 原始相等(即,不带元方法的相等)时,才表示相同的表元素。特别是,具有整数值的浮点数等于它们各自的整数(例如, 1.0 == 1 )。为了避免歧义,任何用作键的浮点数(等于整数)都将转换为该整数。例如,如果您编写 a[2.0] = true ,则插入到表中的实际键将是整数 2

Tables, functions, threads, and (full) userdata values are objects: variables do not actually contain these values, only references to them. Assignment, parameter passing, and function returns always manipulate references to such values; these operations do not imply any kind of copy.

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

The library function type returns a string describing the type of a given value (see type).

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

2.2 – Environments and the Global Environment 环境和全局环境

As we will discuss further in §3.2 and §3.3.3, any reference to a free name (that is, a name not bound to any declaration) var is syntactically translated to _ENV.var. Moreover, every chunk is compiled in the scope of an external local variable named _ENV (see §3.3.2), so _ENV itself is never a free name in a chunk.

​ 如我们在 §3.2 和 §3.3.3 中进一步讨论的那样,对自由名称(即未绑定到任何声明的名称) var 的任何引用在语法上都翻译为 _ENV.var 。此外,每个块都在名为 _ENV 的外部局部变量的作用域中编译(参见 §3.3.2),因此 _ENV 本身在块中永远不是自由名称。

Despite the existence of this external _ENV variable and the translation of free names, _ENV is a completely regular name. In particular, you can define new variables and parameters with that name. Each reference to a free name uses the _ENV that is visible at that point in the program, following the usual visibility rules of Lua (see §3.5).

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

Any table used as the value of _ENV is called an environment.

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

Lua keeps a distinguished environment called the global environment. This value is kept at a special index in the C registry (see §4.3). In Lua, the global variable _G is initialized with this same value. (_G is never used internally, so changing its value will affect only your own code.)

​ Lua 保留一个称为全局环境的特殊环境。此值保存在 C 注册表中的特殊索引中(参见 §4.3)。在 Lua 中,全局变量 _G 使用此相同的值初始化。( _G 从未在内部使用,因此更改其值只会影响您自己的代码。)

When Lua loads a chunk, the default value for its _ENV variable is the global environment (see load). Therefore, by default, free names in Lua code refer to entries in the global environment and, therefore, they are also called global variables. Moreover, all standard libraries are loaded in the global environment and some functions there operate on that environment. You can use load (or loadfile) to load a chunk with a different environment. (In C, you have to load the chunk and then change the value of its first upvalue; see lua_setupvalue.)

​ 当 Lua 加载一个块时,其 _ENV 变量的默认值是全局环境(参见 load )。因此,默认情况下,Lua 代码中的自由名称引用全局环境中的条目,因此它们也被称为全局变量。此外,所有标准库都加载在全局环境中,并且那里的一些函数在该环境中运行。您可以使用 load (或 loadfile )来加载具有不同环境的块。(在 C 中,您必须加载块,然后更改其第一个上值的数值;参见 lua_setupvalue 。)

2.3 – Error Handling 错误处理

Several operations in Lua can raise an error. An error interrupts the normal flow of the program, which can continue by catching the error.

​ Lua 中的几个操作可能会引发错误。错误会中断程序的正常流程,可以通过捕获错误来继续执行程序。

Lua code can explicitly raise an error by calling the error function. (This function never returns.)

​ Lua 代码可以通过调用 error 函数来显式引发错误。(此函数从不返回。)

To catch errors in Lua, you can do a protected call, using pcall (or xpcall). The function pcall calls a given function in protected mode. Any error while running the function stops its execution, and control returns immediately to pcall, which returns a status code.

​ 要捕获 Lua 中的错误,您可以使用 pcall (或 xpcall )进行受保护的调用。函数 pcall 以受保护模式调用给定函数。运行函数时发生的任何错误都会停止其执行,并且控制权会立即返回到 pcall ,后者会返回一个状态代码。

Because Lua is an embedded extension language, Lua code starts running by a call from C code in the host program. (When you use Lua standalone, the lua application is the host program.) Usually, this call is protected; so, when an otherwise unprotected error occurs during the compilation or execution of a Lua chunk, control returns to the host, which can take appropriate measures, such as printing an error message.

​ 因为 Lua 是一种嵌入式扩展语言,Lua 代码通过宿主程序中的 C 代码调用开始运行。(当您独立使用 Lua 时, lua 应用程序就是宿主程序。)通常,此调用是受保护的;因此,当在 Lua 块的编译或执行过程中发生其他未受保护的错误时,控制权会返回给宿主,宿主可以采取适当的措施,例如打印错误消息。

Whenever there is an error, an error object is propagated with information about the error. Lua itself only generates errors whose error object is a string, but programs may generate errors with any value as the error object. It is up to the Lua program or its host to handle such error objects. For historical reasons, an error object is often called an error message, even though it does not have to be a string.

​ 每当发生错误时,都会传播一个错误对象,其中包含有关错误的信息。Lua 本身只生成错误对象为字符串的错误,但程序可能会生成错误对象为任何值的错误。由 Lua 程序或其宿主处理此类错误对象。出于历史原因,错误对象通常称为错误消息,即使它不必是字符串。

When you use xpcall (or lua_pcall, in C) you may give a message handler to be called in case of errors. This function is called with the original error object and returns a new error object. It is called before the error unwinds the stack, so that it can gather more information about the error, for instance by inspecting the stack and creating a stack traceback. This message handler is still protected by the protected call; so, an error inside the message handler will call the message handler again. If this loop goes on for too long, Lua breaks it and returns an appropriate message. The message handler is called only for regular runtime errors. It is not called for memory-allocation errors nor for errors while running finalizers or other message handlers.

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

Lua also offers a system of warnings (see warn). Unlike errors, warnings do not interfere in any way with program execution. They typically only generate a message to the user, although this behavior can be adapted from C (see lua_setwarnf).

​ Lua 还提供了一个警告系统(请参阅 warn )。与错误不同,警告不会以任何方式干扰程序执行。它们通常只向用户生成一条消息,尽管这种行为可以从 C 中调整(请参阅 lua_setwarnf )。

2.4 – Metatables and Metamethods 元表和元方法

Every value in Lua can have a metatable. This metatable is an ordinary Lua table that defines the behavior of the original value under certain events. You can change several aspects of the behavior of a value by setting specific fields in its metatable. For instance, when a non-numeric value is the operand of an addition, Lua checks for a function in the field __add of the value’s metatable. If it finds one, Lua calls this function to perform the addition.

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

The key for each event in a metatable is a string with the event name prefixed by two underscores; the corresponding value is called a metavalue. For most events, the metavalue must be a function, which is then called a metamethod. In the previous example, the key is the string “__add” and the metamethod is the function that performs the addition. Unless stated otherwise, a metamethod may in fact be any callable value, which is either a function or a value with a __call metamethod.

​ 元表中每个事件的键都是一个字符串,其事件名称前缀为两个下划线;相应的值称为元值。对于大多数事件,元值必须是一个函数,然后称为元方法。在前面的示例中,键是字符串“ __add ”,元方法是执行加法的函数。除非另有说明,否则元方法实际上可以是任何可调用值,它可以是函数或具有 __call 元方法的值。

You can query the metatable of any value using the getmetatable function. Lua queries metamethods in metatables using a raw access (see rawget).

​ 您可以使用 getmetatable 函数查询任何值的元表。Lua 使用原始访问查询元表中的元方法(请参见 rawget )。

You can replace the metatable of tables using the setmetatable function. You cannot change the metatable of other types from Lua code, except by using the debug library (§6.10).

​ 您可以使用 setmetatable 函数替换表的元表。除了使用 debug 库(§6.10)之外,您无法通过 Lua 代码更改其他类型的元表。

Tables and full userdata have individual metatables, although multiple tables and userdata can share their metatables. Values of all other types share one single metatable per type; that is, there is one single metatable for all numbers, one for all strings, etc. By default, a value has no metatable, but the string library sets a metatable for the string type (see §6.4).

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

A detailed list of operations controlled by metatables is given next. Each event is identified by its corresponding key. By convention, all metatable keys used by Lua are composed by two underscores followed by lowercase Latin letters.

​ 接下来给出了受元表控制的操作的详细列表。每个事件由其对应的键标识。按照惯例,Lua 使用的所有元表键都由两个下划线后跟小写拉丁字母组成。

  • __add: the addition (+) operation. If any operand for an addition is not a number, Lua will try to call a metamethod. It starts by checking the first operand (even if it is a number); if that operand does not define a metamethod for __add, then Lua will check the second operand. If Lua can find a metamethod, it calls the metamethod with the two operands as arguments, and the result of the call (adjusted to one value) is the result of the operation. Otherwise, if no metamethod is found, Lua raises an error. __add :加法( + )运算。如果加数中任何一个不是数字,Lua 将尝试调用元方法。它首先检查第一个操作数(即使它是一个数字);如果该操作数未定义 __add 的元方法,则 Lua 将检查第二个操作数。如果 Lua 找到元方法,它将使用两个操作数作为参数调用元方法,并且调用的结果(调整为一个值)是运算的结果。否则,如果未找到元方法,Lua 将引发错误。

  • __sub: the subtraction (-) operation. Behavior similar to the addition operation. __sub :减法( - )运算。行为类似于加法运算。

  • __mul: the multiplication (*) operation. Behavior similar to the addition operation. __mul :乘法( * )运算。行为类似于加法运算。

  • __div: the division (/) operation. Behavior similar to the addition operation. __div :除法( / )运算。行为类似于加法运算。

  • __mod: the modulo (%) operation. Behavior similar to the addition operation. __mod :模运算( % )运算。行为类似于加法运算。

  • __pow: the exponentiation (^) operation. Behavior similar to the addition operation. __pow :幂运算( ^ )运算。行为类似于加法运算。

  • __unm: the negation (unary -) operation. Behavior similar to the addition operation. __unm :取反(一元 - )运算。行为类似于加法运算。

  • __idiv: the floor division (//) operation. Behavior similar to the addition operation. __idiv :地板除法( // )运算。行为类似于加法运算。

  • __band: the bitwise AND (&) operation. Behavior similar to the addition operation, except that Lua will try a metamethod if any operand is neither an integer nor a float coercible to an integer (see §3.4.3). __band :按位AND( & )运算。行为类似于加法运算,但如果任何操作数既不是整数也不是可强制转换为整数的浮点数,Lua将尝试使用元方法(参见§3.4.3)。

  • __bor: the bitwise OR (|) operation. Behavior similar to the bitwise AND operation. __bor :按位OR( | )运算。行为类似于按位AND运算。

  • __bxor: the bitwise exclusive OR (binary ~) operation. Behavior similar to the bitwise AND operation. __bxor :按位异或(二进制 ~ )运算。行为类似于按位AND运算。

  • __bnot: the bitwise NOT (unary ~) operation. Behavior similar to the bitwise AND operation. __bnot :按位NOT(一元 ~ )运算。行为类似于按位AND运算。

  • __shl: the bitwise left shift (<<) operation. Behavior similar to the bitwise AND operation. __shl :按位左移( << )运算。行为类似于按位AND运算。

  • __shr: the bitwise right shift (>>) operation. Behavior similar to the bitwise AND operation. __shr :按位右移( >> )运算。行为类似于按位AND运算。

  • __concat: the concatenation (..) operation. Behavior similar to the addition operation, except that Lua will try a metamethod if any operand is neither a string nor a number (which is always coercible to a string). __concat :连接( .. )运算。行为类似于加法运算,但如果任何操作数既不是字符串也不是数字(始终可强制转换为字符串),Lua将尝试使用元方法。

  • __len: the length (#) operation. If the object is not a string, Lua will try its metamethod. If there is a metamethod, Lua calls it with the object as argument, and the result of the call (always adjusted to one value) is the result of the operation. If there is no metamethod but the object is a table, then Lua uses the table length operation (see §3.4.7). Otherwise, Lua raises an error. __len :长度( # )运算。如果对象不是字符串,Lua 将尝试其元方法。如果存在元方法,Lua 会将其作为参数调用该元方法,并且调用的结果(始终调整为一个值)是运算的结果。如果不存在元方法,但对象是一个表,那么 Lua 会使用表长度运算(参见 §3.4.7)。否则,Lua 会引发错误。

  • __eq: the equal (==) operation. Behavior similar to the addition operation, except that Lua will try a metamethod only when the values being compared are either both tables or both full userdata and they are not primitively equal. The result of the call is always converted to a boolean. __eq :相等( == )运算。行为类似于加法运算,不同之处在于 Lua 仅在要比较的值都是表或都是完整用户数据且它们不是原始相等时才尝试元方法。调用的结果始终转换为布尔值。

  • __lt: the less than (<) operation. Behavior similar to the addition operation, except that Lua will try a metamethod only when the values being compared are neither both numbers nor both strings. Moreover, the result of the call is always converted to a boolean. __lt :小于( < )运算。行为类似于加法运算,不同之处在于 Lua 仅在要比较的值既不是数字也不是字符串时才尝试元方法。此外,调用的结果始终转换为布尔值。

  • __le: the less equal (<=) operation. Behavior similar to the less than operation. __le :小于等于( <= )运算。行为类似于小于运算。

  • __index: The indexing access operation table[key]. This event happens when table is not a table or when key is not present in table . The metavalue is looked up in the metatable of table .

    __index :索引访问操作 table[key] 。当 table 不是表或 key 不存在于 table 中时,会发生此事件。在 table 的元表中查找元值。

    The metavalue for this event can be either a function, a table, or any value with an __index metavalue. If it is a function, it is called with table and key as arguments, and the result of the call (adjusted to one value) is the result of the operation. Otherwise, the final result is the result of indexing this metavalue with key. This indexing is regular, not raw, and therefore can trigger another __index metavalue.

    ​ 此事件的元值可以是函数、表或具有 __index 元值的任何值。如果它是一个函数,则以 tablekey 作为参数调用它,并且调用的结果(调整为一个值)是操作的结果。否则,最终结果是使用 key 索引此元值的结果。此索引是常规的,不是原始的,因此可以触发另一个 __index 元值。

  • __newindex: The indexing assignment table[key] = value . Like the index event, this event happens when table is not a table or when key is not present in table . The metavalue is looked up in the metatable of table.

    __newindex :索引赋值 table[key] = value 。与索引事件一样,当 table 不是表或 key 不存在于 table 中时,会发生此事件。在 table 的元表中查找元值。

    Like with indexing, the metavalue for this event can be either a function, a table, or any value with an __newindex metavalue. If it is a function, it is called with table, key, and value as arguments. Otherwise, Lua repeats the indexing assignment over this metavalue with the same key and value. This assignment is regular, not raw, and therefore can trigger another __newindex metavalue.

    ​ 与索引一样,此事件的元值可以是函数、表或具有 __newindex 元值的任何值。如果它是一个函数,则以 tablekeyvalue 作为参数调用它。否则,Lua 会使用相同的键和值对该元值重复索引赋值。此赋值是常规的,不是原始的,因此可以触发另一个 __newindex 元值。

    Whenever a __newindex metavalue is invoked, Lua does not perform the primitive assignment. If needed, the metamethod itself can call rawset to do the assignment.

    ​ 每当调用 __newindex 元值时,Lua 不会执行原始赋值。如果需要,元方法本身可以调用 rawset 来执行赋值。

  • __call: The call operation func(args). This event happens when Lua tries to call a non-function value (that is, func is not a function). The metamethod is looked up in func. If present, the metamethod is called with func as its first argument, followed by the arguments of the original call (args). All results of the call are the results of the operation. This is the only metamethod that allows multiple results. __call :调用操作 func(args) 。当 Lua 尝试调用非函数值(即 func 不是函数)时,会发生此事件。在 func 中查找元方法。如果存在,则使用 func 作为其第一个参数调用元方法,后跟原始调用的参数( args )。调用的所有结果都是操作的结果。这是唯一允许多个结果的元方法。

In addition to the previous list, the interpreter also respects the following keys in metatables: __gc (see §2.5.3), __close (see §3.3.8), __mode (see §2.5.4), and __name. (The entry __name, when it contains a string, may be used by tostring and in error messages.)

​ 除了之前的列表,解释器还尊重元表中的以下键: __gc (参见 §2.5.3)、 __close (参见 §3.3.8)、 __mode (参见 §2.5.4)和 __name 。(当 __name 包含字符串时, tostring 和错误消息中可以使用该字符串。)

For the unary operators (negation, length, and bitwise NOT), the metamethod is computed and called with a dummy second operand, equal to the first one. This extra operand is only to simplify Lua’s internals (by making these operators behave like a binary operation) and may be removed in future versions. For most uses this extra operand is irrelevant.

​ 对于一元运算符(否定、长度和按位非),使用虚拟第二个操作数(等于第一个操作数)计算并调用元方法。此额外操作数仅用于简化 Lua 内部(通过使这些运算符的行为类似于二元运算)并且可能会在未来版本中删除。对于大多数用途,此额外操作数无关紧要。

Because metatables are regular tables, they can contain arbitrary fields, not only the event names defined above. Some functions in the standard library (e.g., tostring) use other fields in metatables for their own purposes.

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

It is a good practice to add all needed metamethods to a table before setting it as a metatable of some object. In particular, the __gc metamethod works only when this order is followed (see §2.5.3). It is also a good practice to set the metatable of an object right after its creation.

​ 在将表设置为某个对象的元表之前,最好向表中添加所有需要的元方法。特别是,只有按照此顺序, __gc 元方法才能工作(参见 §2.5.3)。最好在创建对象后立即设置该对象的元表。

2.5 – Garbage Collection 垃圾回收

Lua performs automatic memory management. This means that you do not have to worry about allocating memory for new objects or freeing it when the objects are no longer needed. Lua manages memory automatically by running a garbage collector to collect all dead objects. All memory used by Lua is subject to automatic management: strings, tables, userdata, functions, threads, internal structures, etc.

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

An object is considered dead as soon as the collector can be sure the object will not be accessed again in the normal execution of the program. (“Normal execution” here excludes finalizers, which can resurrect dead objects (see §2.5.3), and excludes also operations using the debug library.) Note that the time when the collector can be sure that an object is dead may not coincide with the programmer’s expectations. The only guarantees are that Lua will not collect an object that may still be accessed in the normal execution of the program, and it will eventually collect an object that is inaccessible from Lua. (Here, inaccessible from Lua means that neither a variable nor another live object refer to the object.) Because Lua has no knowledge about C code, it never collects objects accessible through the registry (see §4.3), which includes the global environment (see §2.2).

​ 当收集器可以确定在程序的正常执行中不会再次访问对象时,该对象就被认为已死亡。(此处的“正常执行”不包括可以使已死亡对象起死回生的终结器(请参阅 §2.5.3),也不包括使用 debug 库进行操作。)请注意,收集器可以确定对象已死亡的时间可能与程序员的预期不一致。唯一的保证是,Lua 不会收集在程序的正常执行中可能仍会访问的对象,并且最终会收集从 Lua 无法访问的对象。(在此,从 Lua 无法访问意味着既没有变量也没有其他活动对象引用该对象。)因为 Lua 不了解 C 代码,所以它绝不会收集可通过注册表(请参阅 §4.3)访问的对象,其中包括全局环境(请参阅 §2.2)。

The garbage collector (GC) in Lua can work in two modes: incremental and generational.

​ Lua 中的垃圾回收器 (GC) 可以采用两种模式工作:增量式和代际式。

The default GC mode with the default parameters are adequate for most uses. However, programs that waste a large proportion of their time allocating and freeing memory can benefit from other settings. Keep in mind that the GC behavior is non-portable both across platforms and across different Lua releases; therefore, optimal settings are also non-portable.

​ 对于大多数用途,具有默认参数的默认 GC 模式是足够的。但是,将大量时间浪费在分配和释放内存上的程序可以从其他设置中受益。请记住,GC 行为在不同平台和不同 Lua 版本之间是不可移植的;因此,最佳设置也是不可移植的。

You can change the GC mode and parameters by calling lua_gc in C or collectgarbage in Lua. You can also use these functions to control the collector directly (e.g., to stop and restart it).

​ 您可以在 C 中调用 lua_gc 或在 Lua 中调用 collectgarbage 来更改 GC 模式和参数。您还可以使用这些函数直接控制收集器(例如,停止和重新启动它)。

2.5.1 – Incremental Garbage Collection 增量垃圾回收

In incremental mode, each GC cycle performs a mark-and-sweep collection in small steps interleaved with the program’s execution. In this mode, the collector uses three numbers to control its garbage-collection cycles: the garbage-collector pause, the garbage-collector step multiplier, and the garbage-collector step size.

​ 在增量模式下,每个 GC 周期都会以小步骤执行标记和清除收集,这些步骤与程序的执行交织在一起。在此模式下,收集器使用三个数字来控制其垃圾回收周期:垃圾收集器暂停、垃圾收集器步骤乘数和垃圾收集器步骤大小。

The garbage-collector pause controls how long the collector waits before starting a new cycle. The collector starts a new cycle when the use of memory hits n% of the use after the previous collection. Larger values make the collector less aggressive. Values equal to or less than 100 mean the collector will not wait to start a new cycle. A value of 200 means that the collector waits for the total memory in use to double before starting a new cycle. The default value is 200; the maximum value is 1000.

​ 垃圾回收器暂停控制回收器在开始新周期之前等待多长时间。当内存使用量达到上次回收后使用量的 n% 时,回收器会开始一个新周期。较大的值使回收器不那么激进。等于或小于 100 的值意味着回收器不会等待开始新周期。200 的值意味着回收器等待正在使用的总内存加倍,然后才开始新周期。默认值为 200;最大值为 1000。

The garbage-collector step multiplier controls the speed of the collector relative to memory allocation, that is, how many elements it marks or sweeps for each kilobyte of memory allocated. Larger values make the collector more aggressive but also increase the size of each incremental step. You should not use values less than 100, because they make the collector too slow and can result in the collector never finishing a cycle. The default value is 100; the maximum value is 1000.

​ 垃圾回收器步骤乘数控制回收器相对于内存分配的速度,即它为每千字节分配的内存标记或清除多少个元素。较大的值使回收器更激进,但也增加了每个增量步骤的大小。您不应使用小于 100 的值,因为它们会使回收器太慢,并可能导致回收器永远无法完成一个周期。默认值为 100;最大值为 1000。

The garbage-collector step size controls the size of each incremental step, specifically how many bytes the interpreter allocates before performing a step. This parameter is logarithmic: A value of n means the interpreter will allocate 2n bytes between steps and perform equivalent work during the step. A large value (e.g., 60) makes the collector a stop-the-world (non-incremental) collector. The default value is 13, which means steps of approximately 8 Kbytes.

​ 垃圾回收器步骤大小控制每个增量步骤的大小,具体来说就是解释器在执行步骤之前分配多少字节。此参数是采用对数形式:值 n 表示解释器将在步骤之间分配 2 n 字节,并在步骤期间执行等效的工作。较大的值(例如 60)会使收集器成为停止世界(非增量)收集器。默认值为 13,表示大约 8 KB 的步骤。

2.5.2 – Generational Garbage Collection 代垃圾回收

In generational mode, the collector does frequent minor collections, which traverses only objects recently created. If after a minor collection the use of memory is still above a limit, the collector does a stop-the-world major collection, which traverses all objects. The generational mode uses two parameters: the minor multiplier and the the major multiplier.

​ 在代模式中,收集器会执行频繁的次要回收,仅遍历最近创建的对象。如果在次要回收后内存使用量仍高于限制,收集器会执行停止世界的次要回收,遍历所有对象。代模式使用两个参数:次要乘数和主要乘数。

The minor multiplier controls the frequency of minor collections. For a minor multiplier x, a new minor collection will be done when memory grows x% larger than the memory in use after the previous major collection. For instance, for a multiplier of 20, the collector will do a minor collection when the use of memory gets 20% larger than the use after the previous major collection. The default value is 20; the maximum value is 200.

​ 次要乘数控制次要收集的频率。对于次要乘数 x,当内存增长 x% 大于上次主要收集后的使用内存时,将执行新的次要收集。例如,对于乘数 20,当内存使用量比上次主要收集后的使用量增加 20% 时,收集器将执行次要收集。默认值为 20;最大值为 200。

The major multiplier controls the frequency of major collections. For a major multiplier x, a new major collection will be done when memory grows x% larger than the memory in use after the previous major collection. For instance, for a multiplier of 100, the collector will do a major collection when the use of memory gets larger than twice the use after the previous collection. The default value is 100; the maximum value is 1000.

​ 主要乘数控制主要收集的频率。对于主要乘数 x,当内存增长 x% 大于上次主要收集后的使用内存时,将执行新的主要收集。例如,对于乘数 100,当内存使用量比上次收集后的使用量增加一倍以上时,收集器将执行主要收集。默认值为 100;最大值为 1000。

2.5.3 – Garbage-Collection Metamethods 2.5.3 – 垃圾回收元方法

You can set garbage-collector metamethods for tables and, using the C API, for full userdata (see §2.4). These metamethods, called finalizers, are called when the garbage collector detects that the corresponding table or userdata is dead. Finalizers allow you to coordinate Lua’s garbage collection with external resource management such as closing files, network or database connections, or freeing your own memory.

​ 您可以为表设置垃圾回收器元方法,并使用 C API 为完整用户数据设置(参见 §2.4)。当垃圾回收器检测到相应的表或用户数据已死时,将调用这些称为终结器的元方法。终结器允许您将 Lua 的垃圾回收与外部资源管理协调起来,例如关闭文件、网络或数据库连接,或释放您自己的内存。

For an object (table or userdata) to be finalized when collected, you must mark it for finalization. You mark an object for finalization when you set its metatable and the metatable has a __gc metamethod. Note that if you set a metatable without a __gc field and later create that field in the metatable, the object will not be marked for finalization.

​ 要在收集时对对象(表或用户数据)进行终结,您必须将其标记为要进行终结。当您设置其元表且元表具有 __gc 元方法时,您会将对象标记为要进行终结。请注意,如果您设置了一个没有 __gc 字段的元表,然后在元表中创建该字段,则不会将对象标记为要进行终结。

When a marked object becomes dead, it is not collected immediately by the garbage collector. Instead, Lua puts it in a list. After the collection, Lua goes through that list. For each object in the list, it checks the object’s __gc metamethod: If it is present, Lua calls it with the object as its single argument.

​ 当标记的对象变为死对象时,垃圾回收器不会立即收集它。相反,Lua 会将其放入列表中。在收集之后,Lua 会遍历该列表。对于列表中的每个对象,它都会检查该对象的 __gc 元方法:如果存在,Lua 会将其作为其唯一参数调用该对象。

At the end of each garbage-collection cycle, the finalizers are called in the reverse order that the objects were marked for finalization, among those collected in that cycle; that is, the first finalizer to be called is the one associated with the object marked last in the program. The execution of each finalizer may occur at any point during the execution of the regular code.

​ 在每个垃圾回收周期结束时,按对象被标记为要执行终结操作的相反顺序调用终结器,其中包括在该周期中回收的对象;也就是说,要调用的第一个终结器是与程序中最后标记的对象关联的终结器。每个终结器的执行可能发生在常规代码执行期间的任何时间。

Because the object being collected must still be used by the finalizer, that object (and other objects accessible only through it) must be resurrected by Lua. Usually, this resurrection is transient, and the object memory is freed in the next garbage-collection cycle. However, if the finalizer stores the object in some global place (e.g., a global variable), then the resurrection is permanent. Moreover, if the finalizer marks a finalizing object for finalization again, its finalizer will be called again in the next cycle where the object is dead. In any case, the object memory is freed only in a GC cycle where the object is dead and not marked for finalization.

​ 由于要回收的对象仍必须由终结器使用,因此该对象(以及只能通过它访问的其他对象)必须由 Lua 复活。通常,这种复活是暂时的,并且对象内存将在下一个垃圾回收周期中释放。但是,如果终结器将对象存储在某个全局位置(例如,全局变量),则复活是永久的。此外,如果终结器再次将要执行终结操作的对象标记为要执行终结操作,则其终结器将在对象死亡的下一个周期中再次被调用。在任何情况下,对象内存仅在对象死亡且未标记为要执行终结操作的 GC 周期中释放。

When you close a state (see lua_close), Lua calls the finalizers of all objects marked for finalization, following the reverse order that they were marked. If any finalizer marks objects for collection during that phase, these marks have no effect.

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

Finalizers cannot yield nor run the garbage collector. Because they can run in unpredictable times, it is good practice to restrict each finalizer to the minimum necessary to properly release its associated resource.

​ 终结器不能产生或运行垃圾回收器。因为它们可以在不可预测的时间运行,所以最好将每个终结器限制为正确释放其关联资源所需的最小值。

Any error while running a finalizer generates a warning; the error is not propagated.

​ 运行终结器时发生的任何错误都会生成警告;错误不会传播。

2.5.4 – Weak Tables 弱表

A weak table is a table whose elements are weak references. A weak reference is ignored by the garbage collector. In other words, if the only references to an object are weak references, then the garbage collector will collect that object.

​ 弱表是一个元素为弱引用的表。垃圾回收器会忽略弱引用。换句话说,如果对对象的唯一引用是弱引用,那么垃圾回收器将收集该对象。

A weak table can have weak keys, weak values, or both. A table with weak values allows the collection of its values, but prevents the collection of its keys. A table with both weak keys and weak values allows the collection of both keys and values. In any case, if either the key or the value is collected, the whole pair is removed from the table. The weakness of a table is controlled by the __mode field of its metatable. This metavalue, if present, must be one of the following strings: “k”, for a table with weak keys; “v”, for a table with weak values; or “kv”, for a table with both weak keys and values.

​ 弱表可以具有弱键、弱值或两者兼有。具有弱值的表允许收集其值,但阻止收集其键。具有弱键和弱值的表允许收集键和值。在任何情况下,如果键或值被收集,则整个键值对将从表中删除。表的弱性由其元表的 __mode 字段控制。如果存在此元值,则它必须是以下字符串之一:“ k ”,表示具有弱键的表;“ v ”,表示具有弱值的表;或“ kv ”,表示具有弱键和弱值的表。

A table with weak keys and strong values is also called an ephemeron table. In an ephemeron table, a value is considered reachable only if its key is reachable. In particular, if the only reference to a key comes through its value, the pair is removed.

​ 具有弱键和强值的表也称为瞬态表。在瞬态表中,仅当键可访问时,值才被视为可访问。特别是,如果对键的唯一引用通过其值而来,则删除该键值对。

Any change in the weakness of a table may take effect only at the next collect cycle. In particular, if you change the weakness to a stronger mode, Lua may still collect some items from that table before the change takes effect.

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

Only objects that have an explicit construction are removed from weak tables. Values, such as numbers and light C functions, are not subject to garbage collection, and therefore are not removed from weak tables (unless their associated values are collected). Although strings are subject to garbage collection, they do not have an explicit construction and their equality is by value; they behave more like values than like objects. Therefore, they are not removed from weak tables.

​ 只有具有显式构造的对象才会从弱表中移除。值(例如数字和轻量级 C 函数)不受垃圾回收的影响,因此不会从弱表中移除(除非其关联的值被回收)。尽管字符串会受到垃圾回收的影响,但它们没有显式构造,并且它们的相等性是按值计算的;它们的行为更像值,而不是对象。因此,它们不会从弱表中移除。

Resurrected objects (that is, objects being finalized and objects accessible only through objects being finalized) have a special behavior in weak tables. They are removed from weak values before running their finalizers, but are removed from weak keys only in the next collection after running their finalizers, when such objects are actually freed. This behavior allows the finalizer to access properties associated with the object through weak tables.

​ 复活的对象(即正在完成处理的对象和仅可通过正在完成处理的对象访问的对象)在弱表中具有特殊行为。它们在运行其完成处理程序之前从弱值中移除,但仅在运行其完成处理程序之后的下一次回收中才从弱键中移除,此时这些对象实际上已释放。此行为允许完成处理程序通过弱表访问与对象关联的属性。

If a weak table is among the resurrected objects in a collection cycle, it may not be properly cleared until the next cycle.

​ 如果弱表是回收周期中复活的对象之一,则可能直到下一次周期才正确清除它。

2.6 – Coroutines 协程

Lua supports coroutines, also called collaborative multithreading. A coroutine in Lua represents an independent thread of execution. Unlike threads in multithread systems, however, a coroutine only suspends its execution by explicitly calling a yield function.

​ Lua 支持协程,也称为协作式多线程。Lua 中的协程表示一个独立的执行线程。然而,与多线程系统中的线程不同,协程仅通过显式调用 yield 函数来挂起其执行。

You create a coroutine by calling coroutine.create. Its sole argument is a function that is the main function of the coroutine. The create function only creates a new coroutine and returns a handle to it (an object of type thread); it does not start the coroutine.

​ 您可以通过调用 coroutine.create 来创建协程。它的唯一参数是一个函数,该函数是协程的主函数。 create 函数仅创建一个新的协程并返回对其的句柄(类型为 thread 的对象);它不会启动协程。

You execute a coroutine by calling coroutine.resume. When you first call coroutine.resume, passing as its first argument a thread returned by coroutine.create, the coroutine starts its execution by calling its main function. Extra arguments passed to coroutine.resume are passed as arguments to that function. After the coroutine starts running, it runs until it terminates or yields.

​ 您可以通过调用 coroutine.resume 来执行协程。当您首次调用 coroutine.resume 时,将 coroutine.create 返回的线程作为其第一个参数传递,协程通过调用其主函数来启动其执行。传递给 coroutine.resume 的额外参数作为参数传递给该函数。协程开始运行后,它将运行直到终止或产生。

A coroutine can terminate its execution in two ways: normally, when its main function returns (explicitly or implicitly, after the last instruction); and abnormally, if there is an unprotected error. In case of normal termination, coroutine.resume returns true, plus any values returned by the coroutine main function. In case of errors, coroutine.resume returns false plus the error object. In this case, the coroutine does not unwind its stack, so that it is possible to inspect it after the error with the debug API.

​ 协程可以通过两种方式终止其执行:正常终止,当其主函数返回时(显式或隐式,在最后一条指令之后);异常终止,如果存在未受保护的错误。在正常终止的情况下, coroutine.resume 返回 true,加上协程主函数返回的任何值。在发生错误的情况下, coroutine.resume 返回 false 加上错误对象。在这种情况下,协程不会展开其栈,因此可以在错误发生后使用调试 API 检查它。

A coroutine yields by calling coroutine.yield. When a coroutine yields, the corresponding coroutine.resume returns immediately, even if the yield happens inside nested function calls (that is, not in the main function, but in a function directly or indirectly called by the main function). In the case of a yield, coroutine.resume also returns true, plus any values passed to coroutine.yield. The next time you resume the same coroutine, it continues its execution from the point where it yielded, with the call to coroutine.yield returning any extra arguments passed to coroutine.resume.

​ 协程通过调用 coroutine.yield 来生成。当协程生成时,相应的 coroutine.resume 立即返回,即使生成发生在嵌套函数调用中(即,不在主函数中,而是在主函数直接或间接调用的函数中)。在生成的情况下, coroutine.resume 也返回 true,加上传递给 coroutine.yield 的任何值。下次恢复同一协程时,它将从生成的位置继续执行,调用 coroutine.yield 返回传递给 coroutine.resume 的任何额外参数。

Like coroutine.create, the coroutine.wrap function also creates a coroutine, but instead of returning the coroutine itself, it returns a function that, when called, resumes the coroutine. Any arguments passed to this function go as extra arguments to coroutine.resume. coroutine.wrap returns all the values returned by coroutine.resume, except the first one (the boolean error code). Unlike coroutine.resume, the function created by coroutine.wrap propagates any error to the caller. In this case, the function also closes the coroutine (see coroutine.close).

​ 与 coroutine.create 一样, coroutine.wrap 函数也会创建一个协程,但它不会返回协程本身,而是返回一个函数,当调用时,会恢复协程。传递给此函数的任何参数都会作为 coroutine.resume 的额外参数。 coroutine.wrap 返回 coroutine.resume 返回的所有值,除了第一个值(布尔错误代码)。与 coroutine.resume 不同,由 coroutine.wrap 创建的函数会将任何错误传播给调用者。在这种情况下,该函数还会关闭协程(请参阅 coroutine.close )。

As an example of how coroutines work, consider the following code:

​ 作为协程工作原理的一个示例,请考虑以下代码:

     function foo (a)
       print("foo", a)
       return coroutine.yield(2*a)
     end
     
     co = coroutine.create(function (a,b)
           print("co-body", a, b)
           local r = foo(a+1)
           print("co-body", r)
           local r, s = coroutine.yield(a+b, a-b)
           print("co-body", r, s)
           return b, "end"
     end)
     
     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"))

When you run it, it produces the following output:

​ 当您运行它时,它会产生以下输出:

     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

You can also create and manipulate coroutines through the C API: see functions lua_newthread, lua_resume, and lua_yield.

​ 您还可以通过 C API 创建和操作协程:请参阅函数 lua_newthreadlua_resumelua_yield

最后修改 January 31, 2024: 更新 (e0896df)