Lua 是一种扩展编程语言,旨在支持通用的过程式编程和数据描述功能。Lua 的目标是成为一种功能强大、轻量级的脚本语言,供任何需要它的程序使用。
Corona 使用 Lua 5.1.3 版本。更多详细信息,请参阅 Lua 5.1 参考手册。
Lua 中的**名称**(也称为**标识符**)可以是任何由字母、数字和下划线组成的字符串,但不能以数字开头。这与大多数语言中名称的定义一致。“字母”的定义取决于当前的区域设置:当前区域设置中被认为是字母的任何字符都可以在标识符中使用。标识符用于命名变量和表字段。
以下关键字是**保留字**,不能用作名称:
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
)
**注释**以双横线 (--
) 开头,可以出现在字符串之外的任何位置。它们会一直运行到行尾。您可以通过将一段代码用 --[[
和 --]]
包围来将其注释掉。要取消注释同一代码块,只需在第一个包围符中添加另一个连字符,如 ---[[
。
-- Single line commented out --[[ Entire block commented out print( 10 ) print( 15 ) --]]
**数值常量**可以用可选的小数部分和可选的十进制指数来表示。Lua 也接受整数十六进制常量,方法是在它们前面加上 0x
。有效的数值常量的示例如下:
3 3.0 3.1416 314.16e-2 0.31416E1 0xff 0x56
Lua 是一种**动态类型语言**。这意味着变量没有类型;只有值才有类型。语言中没有类型定义。所有值都带有自己的类型。
Lua 中的所有值都是**一等值**。这意味着所有值都可以存储在变量中,作为参数传递给其他函数,并作为结果返回。
您应该关注的基本类型是:
**nil** — 值 nil
的类型,其主要属性是与任何其他值不同;它通常表示缺少有用的值。
**boolean** — 值 false
和 true
的类型。nil
和 false
都会使条件为假;任何其他值都使其为真。
**number** — 表示实数(双精度浮点数)。
**string** — 表示字符数组。Lua 是
**function** — 更多信息请参阅函数。
**table** — Lua 中唯一的数据结构机制。更多信息请参阅表。
Lua 在运行时提供字符串和数字值之间的自动转换。应用于字符串的任何算术运算都会尝试按照通常的转换规则将此字符串转换为数字。相反,只要在预期字符串的地方使用数字,该数字就会以合理的格式转换为字符串。要完全控制数字如何转换为字符串,请使用字符串库中的 string.format 函数。
表是 Lua 中唯一的数据结构机制。它们实现了**关联数组**,这意味着数组不仅可以用数字索引,还可以用除 nil
之外的任何值索引。表可以是异构的,它们可以包含除 nil
之外的所有类型的值。
为了表示记录(属性),Lua 使用字段名称作为**索引**。该语言通过提供 a.name
作为 a["name"]
的语法糖来支持这种表示形式。
与索引一样,表字段的值可以是除 nil
之外的任何类型。特别是,由于函数是
表是**对象**:变量实际上并不包含这些值,只包含对它们的**引用**。赋值、参数传递和函数返回始终操作对此类值的引用;这些操作**不**暗示任何类型的复制。
表构造函数使用**大括号**(花括号)编写,如 {}
。
t = {} -- Create a table k = "x" t[k] = 3.14 -- New table entry with key = "x" and value = 3.14 print( t[k] ) --> 3.14 print( t["x"] ) --> 3.14 print( t.x ) --> 3.14 t[2] = "foo" -- New table entry with key = 2 and value = "foo" print( t[2] ) --> "foo"
在上面的示例中,以两种方式访问了字段名为 "x"
的条目:使用点运算符 t.x
作为**属性**,以及使用 t["x"]
作为**数组索引**。
一个常见的错误是将 t.x
与 t[x]
混淆。第一个等价于 t["x"]
:由字符串 "x"
索引的表。第二个是由变量 x
的值索引的表。
许多 Corona API 返回对象。您可以像操作表属性一样操作这些对象的记录属性。您甚至可以添加自己的属性,前提是您**不要**以下划线作为任何自定义属性的前缀,例如t._custom = 10
变量是存储值的地方。Lua 中有三种变量:**全局变量**、**局部变量**和**表字段**(属性)。任何nil
。
全局变量不需要声明。您只需为其赋值即可创建它
print( s ) --> nil s = "One million dollars" print( s ) --> One million dollars
全局变量在应用程序运行时一直存在。您可以通过为全局变量赋值 nil
来删除它。此时,全局变量的行为就像从未初始化过一样。
s = nil print( s ) --> nil
局部变量使用 local 语句声明
x = 1 -- Global variable local y = 10 -- Local variable
与全局变量不同,局部变量仅在声明它们的块中可见。局部变量的**作用域**从声明之后开始,到块结束为止。
a = 10 local i = 1 while ( i <= 10 ) do local a = i*i -- Different variable "a", local to this "while" block print( a ) --> 1, 4, 9, 16, 25, ... i = i + 1 end print( a ) --> 10 (the global "a")
表字段只是表本身的元素。您索引到数组以将值赋给字段。
t = { foo="hello" } -- Create table with a single property "foo" print( t.foo ) --> "hello" t.foo = "bye" -- Assign a new value to property "foo" print( t.foo ) --> "bye" t.bar = 10 -- Create a new property named "bar" print( t.bar ) --> 10 print( t["bar"] ) --> 10
Lua 支持常见的二元算术运算符:
运算符 | 用途 |
---|---|
+ |
+ 加法 |
- |
- 减法 |
* |
* 乘法 |
/ |
/ 除法 |
% |
% 取模 |
^ |
^ 幂运算 |
它也支持一元运算符 -
(取反)。
如果操作数是数字或可以转换为数字的字符串,则所有运算都具有通常的含义。幂运算适用于任何指数。例如,x^(-0.5)
计算 x
平方根的倒数。
取模定义为a % b == a - math.floor(a/b)*b
Lua 中的关系运算符是:
运算符 | 用途 |
---|---|
== |
== 等于 |
~= |
~= 不等于 |
< |
< 小于 |
> |
> 大于 |
<= |
<= 小于等于 |
>= |
>= 大于等于 |
这些运算符的结果始终为 true
或 false
。
等式 (==
) 首先比较其操作数的类型。如果类型不同,则结果为 false
。否则,将比较操作数的值。
数字和字符串以通常的方式进行比较。对象按引用进行比较:只有当两个对象是同一个对象时,它们才被认为是相等的。每次创建新对象时,该对象都与先前存在的任何对象不同。
自动转换规则**不**适用于相等性比较。因此,"0" == 0
false
。
Lua 中的逻辑运算符是 and
、or
和 not
。所有逻辑运算符都将 false
和 nil
视为 false,将其他任何内容视为 true。
**and** — 合取运算符 and
如果其第一个参数的值为 false
或 nil
,则返回其第一个参数;否则,and
返回其第二个参数。
**or** — 析取运算符 or
如果其第一个参数的值不同于 nil
和 false
,则返回其第一个参数;否则,or
返回其第二个参数。
**not** — 否定运算符 not
始终返回 false
或 true
。
and
和 or
都使用短路求值 — 仅在必要时才对第二个操作数求值。
10 or 20 --> 10 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
Lua 中的字符串连接运算符由两个点 (..
) 表示。如果两个操作数都是字符串或数字,则根据上面提到的转换规则将它们转换为字符串。
local s = "foo".."10"
长度运算符由一元运算符 #
表示。**字符串**的长度是它的字节数 — 当每个字符是一个字节时,字符串长度的通常含义。
**表** t
的长度定义为任何整数索引 n
,使得 t[n]
不为 nil
且 t[n+1]
为 nil
;此外,如果 t[1]
为 nil
,则 n
可以为零。对于常规数组,其nil
n
,其长度恰好是 n
,即其最后一个值的索引。如果数组有“孔”nil
值nil
值)#t
可以是紧邻 nil
值之前的任何索引。因此,它可以将任何此类 nil
值视为数组的结尾。
Lua 中的运算符优先级遵循以下列表,从低到高优先级:
or
and
<
, >
, <=
, >=
, ~=
, ==
..
+
, -
*
, /
, %
not
、#
、-
(一元)^
像往常一样,您可以使用括号来更改表达式的优先级。连接 (..
) 和幂运算 (^
) 运算符是右结合的。所有其他二元运算符都是左结合的。
普通函数正如预期:您提供参数作为输入
以下是声明函数的常用方法
local function f () --body end local f = function() --body end function f () --body end f = function () --body end
函数可以是变量,因此表可以将它们存储为属性。这允许非常灵活地使用表。它可以用于将一系列函数逻辑地分组到一个表中,例如 math
库。在这种情况下,要计算 100 的正弦值,您可以编写 math.sin(100)
。这里,math
只是一个表,属性 sin
是实际的函数。
Lua 中的对象由表表示。显示对象和全局 Runtime 对象都是**对象**。与 math
库类似,这些对象同样将对象方法(实例方法)存储为属性。然而,一个关键的区别是语法。您需要告诉 Lua 您打算将此函数作为**对象方法**调用,而不仅仅是一个普通函数。为此,您需要使用冒号 (:
) 运算符而不是点运算符。这可能与其他语言不同。比较 Javascript 和 Lua 的语法
Javascript | Lua |
---|---|
object.translate( 10, 10 ); |
object:translate( 10, 10 ) |
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
语句的每次执行都会定义新的局部变量
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
。
移动设备尤其具有有限的可用内存,因此必须注意确保应用程序的总内存占用量保持在最低限度。
Lua 执行自动内存管理。这意味着您不必担心为新对象分配内存。当对象不再需要时,您也不需要显式释放内存。Lua 通过不时运行**垃圾收集器**来自动管理内存,以收集所有“死”对象(Lua 不再可访问的对象)。Lua 使用的所有内存都受自动管理。但是,由您来告诉 Lua 什么是垃圾。例如,存储在全局变量中的任何内容都**不**被视为垃圾,即使您的应用程序再也不会使用它。类似地,如果垃圾收集器无法删除表或数组,则存储在表或数组中的任何内容都不会被视为垃圾,即使存储的变量/对象最初是在局部作用域中声明的。在这两种情况下,都由您将 nil
分配给这些位置。这确保了它们相应的内存不会被锁定,并且可以被垃圾收集器释放。
显示对象需要额外的步骤。您必须首先使用 object:removeSelf()
或display.remove( object )
nil
。
myObject:removeSelf() -- OR... display.remove( myObject ) myObject = nil
Lua 与其他语言之间的一些语法差异值得注意,因为它们可以帮助防止编译器错误
**分号** — Lua 中每个语句(实际上是一行代码)末尾的尾随分号是**可选的**。
**大括号** — 您可能习惯于使用{ }
do
和 end
将代码括起来来实现这一点。Lua 中的大括号被解释为**表构造器**。
**if - then - else** — 如果您来自 C、Java、Javascript 等,那么在编写 if
和 elseif
语句时,您会犯的一个常见错误是忘记将 then
附加到 if/elseif
测试条件的末尾。另一个常见错误是在 Lua 需要 elseif
时无意中使用了 else if
(带空格)。
**数组** — 在 Lua 中,数组是**从 1 开始**的。从技术上讲,您可以从 0
开始索引数组。但是,Lua 和 Corona API 假定表 t
的第一个元素是 t[1]
,而不是 t[0]
。
**多个返回值** — Lua 中一个非常规但有用的特性是函数能够返回**多个结果**。
**多重赋值** — 多重赋值提供了一种交换值的便捷方法。语句x,y = y,x
x
和 y
的值。
**三元运算符** (? :
) — Lua 不提供等效于 C 三元运算符 a?b:c
的运算符。Lua 习语(a and b) or c
b
不为假。例如,等效于max = (x>y?x:y)
max = ( x>y and x or y)
基本的 Lua 库提供了几个核心函数。出于安全原因,Corona 中不提供 dofile()
、load()
和 loadfile()
函数。支持 loadstring()
。
有关您可以使用的完整列表,请参阅(全局)API 参考。
本指南中的内容引用自 Lua 5.1 参考手册,该手册根据 Lua 许可证 的条款免费提供。