Lua 简介

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 是一个保留字,但 AndAND 是两个不同的有效名称。按照惯例,以下划线开头后跟大写字母的名称(例如 _VERSION保留给 Lua 使用的内部全局变量。

**注释**以双横线 (--) 开头,可以出现在字符串之外的任何位置。它们会一直运行到行尾。您可以通过将一段代码用 --[[--]] 包围来将其注释掉。要取消注释同一代码块,只需在第一个包围符中添加另一个连字符,如 ---[[

-- 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 中的所有值都是**一等值**。这意味着所有值都可以存储在变量中,作为参数传递给其他函数,并作为结果返回。

您应该关注的基本类型是:

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.xt[x] 混淆。第一个等价于 t["x"]:由字符串 "x" 索引的表。第二个是由变量 x 的值索引的表。

重要

许多 Corona API 返回对象。您可以像操作表属性一样操作这些对象的记录属性。您甚至可以添加自己的属性,前提是您**不要**以下划线作为任何自定义属性的前缀,例如t._custom = 10。Corona 保留使用任何以下划线开头的属性名称的权利。

变量

变量是存储值的地方。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 中的关系运算符是:

运算符 用途
== ==

等于

~= ~=

不等于

< <

小于

> >

大于

<= <=

小于等于

>= >=

大于等于

这些运算符的结果始终为 truefalse

注意
  • 等式 (==) 首先比较其操作数的类型。如果类型不同,则结果为 false。否则,将比较操作数的值。

  • 数字和字符串以通常的方式进行比较。对象按引用进行比较:只有当两个对象是同一个对象时,它们才被认为是相等的。每次创建新对象时,该对象都与先前存在的任何对象不同。

  • 自动转换规则**不**适用于相等性比较。因此,"0" == 0的计算结果为 false

逻辑运算符

Lua 中的逻辑运算符是 andornot。所有逻辑运算符都将 falsenil 视为 false,将其他任何内容视为 true。

  • **and** — 合取运算符 and 如果其第一个参数的值为 falsenil,则返回其第一个参数;否则,and 返回其第二个参数。

  • **or** — 析取运算符 or 如果其第一个参数的值不同于 nilfalse,则返回其第一个参数;否则,or 返回其第二个参数。

  • **not** — 否定运算符 not 始终返回 falsetrue

andor 都使用短路求值 — 仅在必要时才对第二个操作数求值。

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] 不为 nilt[n+1]nil;此外,如果 t[1]nil,则 n 可以为零。对于常规数组,其nil值从 1 到给定的 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 函数

基本的 Lua 库提供了几个核心函数。出于安全原因,Corona 中不提供 dofile()load()loadfile() 函数。支持 loadstring()

有关您可以使用的完整列表,请参阅(全局)API 参考。


本指南中的内容引用自 Lua 5.1 参考手册,该手册根据 Lua 许可证 的条款免费提供。