- Lua 教程
- Lua - 首页
- Lua 基础
- Lua - 概述
- Lua - 环境
- Lua - 基本语法
- Lua - 注释
- Lua - 打印 Hello World
- Lua - 变量
- Lua - 数据类型
- Lua - 运算符
- Lua - 循环
- Lua - 决策
- Lua - 函数
- Lua - 日期和时间
- Lua 数组
- Lua - 数组
- Lua - 多维数组
- Lua - 数组长度
- Lua - 迭代数组
- Lua 迭代器
- Lua - 迭代器
- Lua 列表
- Lua - 在列表中搜索
- Lua 模块
- Lua - 模块
- Lua - 命名空间
- Lua 元表
- Lua - 元表
- Lua 协程
- Lua - 协程
- Lua 文件处理
- Lua - 文件 I/O
- Lua 库
- Lua - 标准库
- Lua - 数学库
- Lua - 操作系统功能
- Lua 有用资源
- Lua 快速指南
- Lua - 有用资源
- Lua - 讨论
Lua 快速指南
Lua - 概述
Lua 是一种用 C 语言编写的可扩展、轻量级编程语言。它始于 1993 年 Roberto Ierusalimschy、Luiz Henrique de Figueiredo 和 Waldemar Celes 的内部项目。
它从一开始就被设计成一种可以与用 C 语言和其他传统语言编写的代码集成的软件。这种集成带来了许多好处。它不试图做 C 语言已经能够做的事情,而是旨在提供 C 语言不擅长的事情:远离硬件,动态结构,没有冗余,易于测试和调试。为此,Lua 提供了安全的环境、自动内存管理以及处理动态大小的字符串和其他类型数据的好工具。
特性
Lua 提供了一组独特的特性,使其有别于其他语言。这些包括:
- 可扩展的
- 简单的
- 高效的
- 可移植的
- 免费和开放的
示例代码
print("Hello World!")
Lua是如何实现的?
Lua 由两部分组成——Lua 解释器部分和功能软件系统。功能软件系统是一个实际的计算机应用程序,可以解释用 Lua 编程语言编写的程序。Lua 解释器是用 ANSI C 编写的,因此它具有高度的可移植性,可以在从高端网络服务器到小型设备的各种设备上运行。
Lua 的语言及其解释器都成熟、小巧且快速。它从其他编程语言和顶级软件标准发展而来。体积小巧使其能够在内存较小的小型设备上运行。
学习 Lua
学习 Lua 最重要的一点是专注于概念,而不要迷失在它的技术细节中。
学习编程语言的目的是成为一名更好的程序员;也就是说,在设计和实现新系统以及维护旧系统方面变得更有效率。
Lua 的一些用途
游戏编程
独立应用程序中的脚本
Web 中的脚本
数据库(如 MySQL Proxy 和 MySQL WorkBench)的扩展和附加组件
安全系统,如入侵检测系统。
Lua - 环境
本地环境设置
如果您仍然希望为 Lua 编程语言设置环境,则您的计算机上需要以下软件:(a) 文本编辑器,(b) Lua 解释器和 (c) Lua 编译器。
文本编辑器
您需要一个文本编辑器来键入您的程序。一些编辑器的示例包括 Windows 记事本、OS Edit 命令、Brief、Epsilon、EMACS 和 vim 或 vi。
文本编辑器的名称和版本在不同的操作系统上可能有所不同。例如,Notepad 将在 Windows 上使用,而 vim 或 vi 可以在 Windows 以及 Linux 或 UNIX 上使用。
您使用编辑器创建的文件称为源文件,这些文件包含程序源代码。Lua 程序的源文件通常以扩展名 “.lua”命名。
Lua 解释器
它只是一个小型程序,使您能够键入 Lua 命令并立即执行它们。与执行完整的编译器不同,如果遇到错误,它会停止执行 Lua 文件。
Lua 编译器
当我们将 Lua 扩展到其他语言/应用程序时,我们需要一个与 Lua 应用程序编程接口兼容的编译器的软件开发工具包。
在 Windows 上安装
有一个名为“SciTE”的单独 IDE 为 Windows 环境开发,可以从 https://code.google.com/p/luaforwindows/ 下载部分下载。
运行下载的可执行文件以安装 Lua IDE。
因为它是一个 IDE,所以您可以使用相同的 IDE 创建和构建 Lua 代码。
如果您有兴趣以命令行模式安装 Lua,则需要安装 MinGW 或 Cygwin,然后在 Windows 中编译和安装 Lua。
在 Linux 上安装
要下载和构建 Lua,请使用以下命令:
$ wget https://lua.ac.cn/ftp/lua-5.2.3.tar.gz $ tar zxf lua-5.2.3.tar.gz $ cd lua-5.2.3 $ make linux test
为了在其他平台(如 aix、ansi、bsd、通用 Linux、mingw、posix、solaris)上安装,请用相应的平台名称替换 make Linux 中的 Linux,并进行测试。
我们有一个 Lua 中的 helloWorld.lua,如下所示:
print("Hello World!")
现在,我们可以通过使用 cd 切换到包含该文件的文件夹,然后使用以下命令来构建和运行 Lua 文件(例如 helloWorld.lua):
$ lua helloWorld
我们可以看到以下输出。
Hello World!
在 Mac OS X 上安装
要在 Mac OS X 中构建/测试 Lua,请使用以下命令:
$ curl -R -O https://lua.ac.cn/ftp/lua-5.2.3.tar.gz $ tar zxf lua-5.2.3.tar.gz $ cd lua-5.2.3 $ make macosx test
在某些情况下,您可能没有安装 Xcode 和命令行工具。在这种情况下,您将无法使用 make 命令。从 Mac App Store 安装 Xcode。然后转到 Xcode 的“偏好设置”,然后切换到“下载”并安装名为“命令行工具”的组件。该过程完成后,make 命令将可供您使用。
您不必执行“make macosx test”语句。即使不执行此命令,您仍然可以在 Mac OS X 中使用 Lua。
我们有一个 Lua 中的 helloWorld.lua,如下所示:
print("Hello World!")
现在,我们可以通过使用 cd 切换到包含该文件的文件夹,然后使用以下命令来构建和运行 Lua 文件(例如 helloWorld.lua):
$ lua helloWorld
我们可以看到以下输出:
Hello World!
Lua IDE
如前所述,对于 Windows SciTE,Lua IDE 是 Lua 创建团队提供的默认 IDE。另一个可用的 IDE 来自 ZeroBrane Studio,可在 Windows、Mac 和 Linux 等多个平台上使用。
还有一些 Eclipse 插件可以启用 Lua 开发。使用 IDE 可以使开发更容易,并具有代码完成等功能,因此强烈推荐使用。IDE 还提供与 Lua 命令行版本类似的交互式模式编程。
Lua - 基本语法
让我们开始创建我们的第一个 Lua 程序!
第一个 Lua 程序
交互式模式编程
Lua 提供了一种称为交互式模式的模式。在此模式下,您可以一个接一个地键入指令并获得即时结果。这可以通过在 shell 中使用 lua -i 或仅使用 lua 命令来调用。键入此命令后,按 Enter 键即可启动交互模式,如下所示。
$ lua -i $ Lua 5.1.4 Copyright (C) 1994-2008 Lua.org, PUC-Rio quit to end; cd, dir and edit also available
您可以使用以下语句打印内容:
print("test")
按 Enter 键后,您将获得以下输出:
test
默认模式编程
使用 Lua 文件名参数调用解释器将开始执行该文件,并持续到脚本完成。脚本完成后,解释器将不再活动。
让我们编写一个简单的 Lua 程序。所有 Lua 文件都将具有扩展名 .lua。因此,请将以下源代码放入 test.lua 文件中。
print("test")
假设 Lua 环境已正确设置,让我们使用以下代码运行程序:
$ lua test.lua
我们将获得以下输出:
test
让我们尝试另一种执行 Lua 程序的方法。以下是修改后的 test.lua 文件:
#!/usr/local/bin/lua print("test")
在这里,我们假设您的 Lua 解释器位于 /usr/local/bin 目录中。如果第一行以 # 符号开头,则解释器会忽略它。现在,尝试按如下方式运行此程序:
$ chmod a+rx test.lua $./test.lua
我们将获得以下输出。
test
现在让我们看看 Lua 程序的基本结构,这样您就可以轻松理解 Lua 编程语言的基本构建块。
Lua 中的标记
Lua 程序由各种标记组成,标记要么是关键字,要么是标识符,要么是常量,要么是字符串文字,要么是符号。例如,以下 Lua 语句包含三个标记:
io.write("Hello world, from ",_VERSION,"!\n")
各个标记是:
io.write ( "Hello world, from ",_VERSION,"!\n" )
注释
注释就像 Lua 程序中的帮助文本,解释器会忽略它们。它们以 --[[ 开头,以 --]] 结尾,如下所示:
--[[ my first program in Lua --]]
标识符
Lua 标识符是用于标识变量、函数或任何其他用户定义项的名称。标识符以字母“A 到 Z”或“a 到 z”或下划线“_”开头,后跟零个或多个字母、下划线和数字(0 到 9)。
Lua 不允许在标识符中使用标点符号,例如 @、$ 和 %。Lua 是一种**区分大小写**的编程语言。因此,Manpower 和 manpower 是 Lua 中的两个不同标识符。以下是一些可接受的标识符示例:
mohd zara abc move_name a_123 myname50 _temp j a23b9 retVal
关键字
以下列表显示了 Lua 中的一些保留字。这些保留字不能用作常量或变量或任何其他标识符名称。
and | break | do | else |
elseif | end | false | for |
function | if | in | local |
nil | not | or | repeat |
return | then | true | until |
while |
Lua 中的空格
仅包含空格(可能带有注释)的行称为空行,Lua 解释器会完全忽略它。
在Lua中,“空白”指的是空格、制表符、换行符和注释。空白字符用于分隔语句的不同部分,使解释器能够识别语句中一个元素(例如int)的结束位置和下一个元素的开始位置。因此,在下面的语句中:
local age
必须在local和age之间至少有一个空白字符(通常是空格),以便解释器能够区分它们。另一方面,在下面的语句中:
fruit = apples + oranges --get the total fruit
fruit和=之间,以及=和apples之间不需要空白字符,尽管为了可读性,您可以随意添加一些。
Lua - 变量
变量只不过是程序可以操作的存储区域的名称。它可以保存不同类型的数值,包括函数和表。
变量名可以由字母、数字和下划线组成。它必须以字母或下划线开头。Lua区分大小写,因此大小写字母是不同的。Lua中有八种基本类型的数值:
在Lua中,虽然我们没有变量数据类型,但我们根据变量的作用域有三种类型。
全局变量 - 除非明确声明为局部变量,否则所有变量都被视为全局变量。
局部变量 - 当变量的类型指定为local时,其作用域仅限于其作用域内的函数。
表字段 - 这是一种特殊的变量类型,可以保存除nil之外的任何值,包括函数。
Lua中的变量定义
变量定义是指告诉解释器在哪里以及如何为变量创建存储空间。变量定义可以有一个可选的类型,并包含一个或多个该类型的变量列表,如下所示:
type variable_list;
这里,type 可选地是local或指定的类型,使其成为全局变量,而variable_list 可以包含一个或多个以逗号分隔的标识符名称。这里显示了一些有效的声明:
local i, j local i local a,c
语句local i, j 同时声明和定义了变量i和j;它指示解释器创建名为i、j的变量,并将作用域限制为局部作用域。
可以在变量声明中初始化(赋值初始值)变量。初始化程序由一个等号后跟一个常量表达式组成,如下所示:
type variable_list = value_list;
一些例子:
local d , f = 5 ,10 --declaration of d and f as local variables. d , f = 5, 10; --declaration of d and f as global variables. d, f = 10 --[[declaration of d and f as global variables. Here value of f is nil --]]
对于没有初始化程序的定义:具有静态存储持续时间的变量会隐式初始化为nil。
Lua中的变量声明
正如你在上面的例子中看到的,多个变量的赋值遵循variable_list和value_list的格式。在上面的例子local d, f = 5,10中,我们有variable_list中的d和f,以及value_list中的5和10。
Lua中的值赋值方式是:将variable_list中的第一个变量与value_list中的第一个值关联,依次类推。因此,d的值为5,f的值为10。
示例
尝试以下示例,其中变量已在顶部声明,但在主函数内定义和初始化:
-- Variable definition: local a, b -- Initialization a = 10 b = 30 print("value of a:", a) print("value of b:", b) -- Swapping of variables b, a = a, b print("value of a:", a) print("value of b:", b) f = 70.0/3.0 print("value of f", f)
当编译并执行上述代码时,会产生以下结果:
value of a: 10 value of b: 30 value of a: 30 value of b: 10 value of f 23.333333333333
Lua中的左值和右值
Lua中有两种表达式:
左值 (lvalue) - 指向内存位置的表达式称为“左值”表达式。左值可以出现在赋值的左侧或右侧。
右值 (rvalue) - 右值指的是存储在内存某个地址的数据值。右值是一个不能被赋值的表达式,这意味着右值可以出现在赋值的右侧,但不能出现在左侧。
变量是左值,因此可以出现在赋值的左侧。数字字面量是右值,因此不能赋值,不能出现在左侧。以下是一个有效的语句:
g = 20
但以下语句无效,会生成编译时错误:
10 = 20
在Lua编程语言中,除了上述类型的赋值外,同一个语句中还可以有多个左值和右值。如下所示。
g,l = 20,30
在上述语句中,20被赋值给g,30被赋值给l。
Lua - 数据类型
Lua是一种动态类型语言,因此变量没有类型,只有值才有类型。值可以存储在变量中,作为参数传递,并作为结果返回。
在Lua中,虽然我们没有变量数据类型,但是我们有数值的类型。数值的数据类型列表如下所示。
序号 | 数值类型及描述 |
---|---|
1 | nil 用于区分数值是否有数据或无(nil)数据。 |
2 | boolean 包含true和false作为值。通常用于条件检查。 |
3 | number 表示实数(双精度浮点数)。 |
4 | string 表示字符数组。 |
5 | function function |
6 | 表示用C或Lua编写的函数。 userdata |
7 | 表示任意的C数据。 thread |
8 | 表示独立的执行线程,用于实现协程。 table |
表示普通数组、符号表、集合、记录、图、树等,并实现关联数组。它可以保存任何值(除了nil)。
type函数
print(type("What is my type")) --> string t = 10 print(type(5.8*t)) --> number print(type(true)) --> boolean print(type(print)) --> function print(type(nil)) --> nil print(type(type(ABC))) --> string
在Lua中,有一个名为“type”的函数,它使我们能够知道变量的类型。以下代码中给出了一些示例。
string number boolean function nil string
构建并执行上述程序后,它会在Linux上产生以下结果:
Lua - 运算符
默认情况下,所有变量在赋值或初始化之前都指向nil。在Lua中,零和空字符串在条件检查中被认为是true。因此,在使用布尔运算时必须小心。我们将在接下来的章节中更多地了解这些类型。
- 运算符是一个符号,它告诉解释器执行特定的数学或逻辑操作。Lua语言富含内置运算符,并提供以下类型的运算符:
- 算术运算符
- 关系运算符
- 逻辑运算符
其他运算符
运算符是一个符号,它告诉解释器执行特定的数学或逻辑操作。Lua语言富含内置运算符,并提供以下类型的运算符:
本教程将逐一解释算术、关系、逻辑和其他杂项运算符。
下表显示了Lua语言支持的所有算术运算符。假设变量A为10,变量B为20,则:
示例 | 运算符 | 示例 |
---|---|---|
+ | 描述 | 将两个操作数相加 |
- | A + B 将得到 30 | 从第一个操作数中减去第二个操作数 |
* | A - B 将得到 -10 | 将两个操作数相乘 |
/ | A * B 将得到 200 | 将分子除以分母 |
% | B / A 将得到 2 | 取模运算符,返回整数除法后的余数 |
^ | B % A 将得到 0 | 指数运算符,计算指数 |
- | A^2 将得到 100 | 一元 - 运算符作为否定运算符 |
算术运算符
-A 将得到 -10
下表显示了Lua语言支持的所有算术运算符。假设变量A为10,变量B为20,则:
示例 | 运算符 | 示例 |
---|---|---|
== | 下表显示了Lua语言支持的所有关系运算符。假设变量A为10,变量B为20,则: | 检查两个操作数的值是否相等,如果相等则条件为真。 |
~= | (A == B) 为假。 | 检查两个操作数的值是否不相等,如果不相等则条件为真。 |
> | (A ~= B) 为真。 | 检查左侧操作数的值是否大于右侧操作数的值,如果大于则条件为真。 |
< | (A > B) 为假。 | 检查左侧操作数的值是否小于右侧操作数的值,如果小于则条件为真。 |
>= | (A < B) 为真。 | 检查左侧操作数的值是否大于或等于右侧操作数的值,如果大于或等于则条件为真。 |
<= | (A >= B) 为假。 | 检查左侧操作数的值是否小于或等于右侧操作数的值,如果小于或等于则条件为真。 |
关系运算符
(A <= B) 为真。
下表显示了Lua语言支持的所有算术运算符。假设变量A为10,变量B为20,则:
示例 | 运算符 | 示例 |
---|---|---|
and | 下表显示了Lua语言支持的所有逻辑运算符。假设变量A为true,变量B为false,则: | 称为逻辑与运算符。如果两个操作数都不为零,则条件为真。 |
or | (A and B) 为假。 | 称为逻辑或运算符。如果两个操作数中的任何一个不为零,则条件为真。 |
not | (A or B) 为真。 | 称为逻辑非运算符。用于反转其操作数的逻辑状态。如果条件为真,则逻辑非运算符将使其为假。 |
逻辑运算符
!(A and B) 为真。
下表显示了Lua语言支持的所有算术运算符。假设变量A为10,变量B为20,则:
示例 | 运算符 | 示例 |
---|---|---|
.. | Lua语言支持的其他运算符包括连接和长度。 | 连接两个字符串。 |
# | a..b,其中a为"Hello ",b为"World",将返回"Hello World"。 | 一元运算符,返回字符串或表的长度。 |
#"Hello" 将返回 5
Lua中的运算符优先级
运算符优先级决定了表达式中项的分组方式。这会影响表达式的计算方式。某些运算符的优先级高于其他运算符;例如,乘法运算符的优先级高于加法运算符:
例如,x = 7 + 3 * 2;这里x赋值为13,而不是20,因为运算符*的优先级高于+,所以它先与3*2相乘,然后加到7中。
下表显示了Lua语言支持的所有算术运算符。假设变量A为10,变量B为20,则:
这里,优先级最高的运算符出现在表的最上面,优先级最低的出现在表的最下面。在一个表达式中,优先级高的运算符将首先被计算。 | 示例 | 类别 |
---|---|---|
结合性 | 一元 | not # - |
从右到左 | .. | not # - |
连接 | * / % | 从左到右 |
乘法 | + - | 从左到右 |
加法 | 关系 | 从左到右 |
< > <= >= == ~= | == ~= | 从左到右 |
等式 | and | 从左到右 |
逻辑与 | or | 从左到右 |
Lua - 循环
逻辑或
可能会有这样的情况:你需要多次执行一段代码。通常情况下,语句是顺序执行的:函数中的第一个语句首先执行,然后是第二个语句,依此类推。
编程语言提供各种控制结构,允许更复杂的执行路径。
循环语句允许我们多次执行一条语句或一组语句。以下是大多数编程语言中循环语句的一般形式:
Lua提供以下类型的循环来处理循环需求。 | 序号 |
---|---|
1 | 循环类型及描述
while循环 |
2 | 当给定条件为真时,重复执行一条语句或一组语句。它在执行循环体之前测试条件。
for循环 |
3 | repeat...until循环
重复执行一组语句,直到满足until条件。 |
4 | 嵌套循环
你可以在任何其他的while、for或do..while循环内部使用一个或多个循环。 |
循环控制语句
循环控制语句改变了执行的正常顺序。当执行离开一个作用域时,在该作用域中创建的所有自动对象都会被销毁。
Lua支持以下控制语句。
Lua提供以下类型的循环来处理循环需求。 | 控制语句及描述 |
---|---|
1 | break语句
终止循环并将执行转移到紧跟在循环或switch之后的语句。 |
无限循环
如果条件永远不会变为false,则循环将变成无限循环。while循环通常用于此目的。由于我们直接为条件提供true,因此它会永远执行下去。我们可以使用break语句来中断此循环。
while( true ) do print("This loop will run forever.") end
Lua - 决策
决策结构要求程序员指定一个或多个条件,由程序进行评估或测试,以及如果条件被确定为true,则要执行的语句,或者可选地,如果条件被确定为false,则要执行的其他语句。
以下是大多数编程语言中典型的决策结构的一般形式:
Lua编程语言将任何布尔true和非nil值的组合视为true,如果是布尔false或nil,则视为false值。需要注意的是,在Lua中,零将被视为true。
Lua编程语言提供以下类型的决策语句。
Lua提供以下类型的循环来处理循环需求。 | 语句及描述 |
---|---|
1 | if语句
if语句由一个布尔表达式和一个或多个语句组成。 |
2 | if...else语句
if语句后面可以跟一个可选的else语句,当布尔表达式为false时执行。 |
3 | 嵌套if语句
你可以在另一个if或else if语句中使用一个if或else if语句。 |
Lua - 函数
函数是一组共同执行一项任务的语句。你可以将代码分成单独的函数。你如何将代码划分到不同的函数中取决于你,但逻辑上划分通常是唯一的,因此每个函数执行一项特定任务。
Lua语言提供了许多内置方法,你的程序可以调用这些方法。例如,方法print()用于将作为输入传递的参数打印到控制台。
函数以各种名称而闻名,例如方法、子例程或过程等。
定义函数
Lua编程语言中方法定义的一般形式如下:
optional_function_scope function function_name( argument1, argument2, argument3........, argumentn) function_body return result_params_comma_separated end
Lua编程语言中的方法定义由方法头和方法体组成。以下是方法的所有部分:
可选函数作用域 - 你可以使用关键字local来限制函数的作用域或忽略作用域部分,这将使其成为全局函数。
函数名 - 这是函数的实际名称。函数名和参数列表共同构成函数签名。
参数 - 参数就像一个占位符。当调用函数时,你将值传递给参数。此值被称为实际参数或自变量。参数列表是指方法的参数类型、顺序和数量。参数是可选的;也就是说,方法可能不包含任何参数。
函数体 - 方法体包含定义方法执行内容的一组语句。
返回值 - 在Lua中,可以通过在return关键字后跟逗号分隔的返回值来返回多个值。
示例
以下是名为max()函数的源代码。此函数接受两个参数num1和num2,并返回两者之间的最大值:
--[[ function returning the max between two numbers --]] function max(num1, num2) if (num1 > num2) then result = num1; else result = num2; end return result; end
函数参数
如果函数要使用参数,则必须声明接受参数值的变量。这些变量称为函数的形式参数。
形式参数在函数内部的行为类似于其他局部变量,并在进入函数时创建,并在退出函数时销毁。
调用函数
在创建Lua函数时,你将定义函数必须执行的操作。要使用方法,你必须调用该函数来执行定义的任务。
当程序调用函数时,程序控制将转移到被调用的函数。被调用的函数执行定义的任务,当执行其return语句或达到其函数末尾时,它将程序控制返回到主程序。
要调用方法,你只需将所需参数与方法名称一起传递,如果方法返回值,则可以存储返回值。例如:
function max(num1, num2) if (num1 > num2) then result = num1; else result = num2; end return result; end -- calling a function print("The maximum of the two numbers is ",max(10,4)) print("The maximum of the two numbers is ",max(5,6))
运行上述代码后,我们将得到以下输出。
The maximum of the two numbers is 10 The maximum of the two numbers is 6
赋值和传递函数
在Lua中,我们可以将函数赋值给变量,也可以将它们作为另一个函数的参数传递。这是一个在Lua中赋值和传递函数作为参数的简单示例。
myprint = function(param) print("This is my print function - ##",param,"##") end function add(num1,num2,functionPrint) result = num1 + num2 functionPrint(result) end myprint(10) add(2,5,myprint)
运行上述代码后,我们将得到以下输出。
This is my print function - ## 10 ## This is my print function - ## 7 ##
带有可变参数的函数
可以使用'...'作为参数在Lua中创建具有可变参数的函数。我们可以通过查看一个示例来了解这一点,在这个示例中,函数将返回平均值,并且它可以接受可变参数。
function average(...) result = 0 local arg = {...} for i,v in ipairs(arg) do result = result + v end return result/#arg end print("The average is",average(10,5,3,4,5,6))
运行上述代码后,我们将得到以下输出。
The average is 5.5
Lua - 字符串
字符串是一系列字符以及控制字符,如换页符。字符串可以用三种形式初始化,包括:
- 单引号之间的字符
- 双引号之间的字符
- [[和]]之间的字符
下面显示了上述三种形式的示例。
string1 = "Lua" print("\"String 1 is\"",string1) string2 = 'Tutorial' print("String 2 is",string2) string3 = [["Lua Tutorial"]] print("String 3 is",string3)
运行上述程序后,我们将得到以下输出。
"String 1 is" Lua String 2 is Tutorial String 3 is "Lua Tutorial"
转义序列字符用于字符串中以更改字符的正常解释。例如,要打印双引号(""),我们在上述示例中使用了\"。转义序列及其用途在下面的表格中列出。
转义序列 | 用途 |
---|---|
\a | 响铃 |
\b | 退格 |
\f | 换页 |
\n | 换行 |
\r | 回车 |
\t | 制表符 |
\v | 垂直制表符 |
\\ | 反斜杠 |
\" | 双引号 |
\' | 单引号 |
\[ | 左方括号 |
\] | 右方括号 |
字符串操作
Lua支持用于操作字符串的字符串:
Lua提供以下类型的循环来处理循环需求。 | 方法及用途 |
---|---|
1 | string.upper(argument) 返回参数的大写表示形式。 |
2 | string.lower(argument) 返回参数的小写表示形式。 |
3 | string.gsub(mainString,findString,replaceString) 通过将findString的出现替换为replaceString来返回一个字符串。 |
4 | string.find(mainString,findString, optionalStartIndex,optionalEndIndex) 返回findString在主字符串中的起始索引和结束索引,如果未找到则返回nil。 |
5 | string.reverse(arg) 通过反转传递字符串的字符来返回一个字符串。 |
6 | string.format(...) 返回格式化的字符串。 |
7 | string.char(arg) 和 string.byte(arg) 返回输入参数的内部数值和字符表示。 |
8 | string.len(arg) 返回传递字符串的长度。 |
9 | string.rep(string, n)) 通过重复相同的字符串n次来返回一个字符串。 |
10 | .. 因此,操作符连接两个字符串。 |
现在,让我们深入了解一些示例,以准确了解这些字符串操作函数的行为。
大小写转换
下面给出了一个用于将字符串转换为大写和小写的示例代码。
string1 = "Lua"; print(string.upper(string1)) print(string.lower(string1))
运行上述程序后,我们将得到以下输出。
LUA lua
替换子字符串
下面给出了一个用另一个字符串替换一个字符串出现的示例代码。
string = "Lua Tutorial" -- replacing strings newstring = string.gsub(string,"Tutorial","Language") print("The new string is "..newstring)
运行上述程序后,我们将得到以下输出。
The new string is Lua Language
查找和反转
下面给出了一个用于查找子字符串索引和反转字符串的示例代码。
string = "Lua Tutorial" -- replacing strings print(string.find(string,"Tutorial")) reversedString = string.reverse(string) print("The new string is",reversedString)
运行上述程序后,我们将得到以下输出。
5 12 The new string is lairotuT auL
格式化字符串
在我们的编程中,很多时候可能需要以格式化的方式打印字符串。你可以使用string.format函数来格式化输出,如下所示。
string1 = "Lua" string2 = "Tutorial" number1 = 10 number2 = 20 -- Basic string formatting print(string.format("Basic formatting %s %s",string1,string2)) -- Date formatting date = 2; month = 1; year = 2014 print(string.format("Date formatting %02d/%02d/%03d", date, month, year)) -- Decimal formatting print(string.format("%.4f",1/3))
运行上述程序后,我们将得到以下输出。
Basic formatting Lua Tutorial Date formatting 02/01/2014 0.3333
字符和字节表示
字符和字节表示的示例代码,用于将字符串从字符串转换为内部表示,反之亦然。
-- Byte conversion -- First character print(string.byte("Lua")) -- Third character print(string.byte("Lua",3)) -- first character from last print(string.byte("Lua",-1)) -- Second character print(string.byte("Lua",2)) -- Second character from last print(string.byte("Lua",-2)) -- Internal Numeric ASCII Conversion print(string.char(97))
运行上述程序后,我们将得到以下输出。
76 97 97 117 117 a
其他常用函数
常见的字符串操作包括字符串连接、查找字符串长度,有时是重复相同的字符串多次。下面给出了这些操作的示例。
string1 = "Lua" string2 = "Tutorial" -- String Concatenations using .. print("Concatenated string",string1..string2) -- Length of string print("Length of string1 is ",string.len(string1)) -- Repeating strings repeatedString = string.rep(string1,3) print(repeatedString)
运行上述程序后,我们将得到以下输出。
Concatenated string LuaTutorial Length of string1 is 3 LuaLuaLua
Lua - 数组
数组是有序的对象排列,它可以是一维数组,包含一系列行,也可以是多维数组,包含多行多列。
在Lua中,数组使用带有整数的索引表实现。数组的大小不是固定的,它可以根据我们的需求增长,但受内存限制。
一维数组
一维数组可以使用简单的表结构表示,并可以使用简单的for循环进行初始化和读取。下面显示了一个示例。
array = {"Lua", "Tutorial"} for i = 0, 2 do print(array[i]) end
运行上述代码后,我们将得到以下输出。
nil Lua Tutorial
正如你在上述代码中看到的,当我们试图访问数组中不存在的索引中的元素时,它会返回nil。在Lua中,索引通常从索引1开始。但也可以在索引0和0以下创建对象。下面显示了使用负索引的数组,我们使用for循环初始化数组。
array = {} for i= -2, 2 do array[i] = i *2 end for i = -2,2 do print(array[i]) end
运行上述代码后,我们将得到以下输出。
-4 -2 0 2 4
多维数组
多维数组可以用两种方式实现。
- 数组的数组
- 通过操作索引的一维数组
下面使用数组的数组显示了3x3多维数组的示例。
-- Initializing the array array = {} for i=1,3 do array[i] = {} for j=1,3 do array[i][j] = i*j end end -- Accessing the array for i=1,3 do for j=1,3 do print(array[i][j]) end end
运行上述代码后,我们将得到以下输出。
1 2 3 2 4 6 3 6 9
下面显示了使用操作索引的多维数组的示例。
-- Initializing the array array = {} maxRows = 3 maxColumns = 3 for row=1,maxRows do for col=1,maxColumns do array[row*maxColumns +col] = row*col end end -- Accessing the array for row=1,maxRows do for col=1,maxColumns do print(array[row*maxColumns +col]) end end
运行上述代码后,我们将得到以下输出。
1 2 3 2 4 6 3 6 9
正如你在上面的示例中看到的,数据是基于索引存储的。可以以稀疏的方式放置元素,这就是Lua矩阵实现的工作方式。由于它不在Lua中存储nil值,因此与其他编程语言中使用的特殊技术相比,无需在Lua中使用任何特殊技术即可节省大量内存。
Lua - 迭代器
迭代器是一种构造,使你能够遍历所谓的集合或容器的元素。在Lua中,这些集合通常指表,用于创建各种数据结构,如数组。
泛型for迭代器
泛型for迭代器提供集合中每个元素的键值对。下面给出了一个简单的例子。
array = {"Lua", "Tutorial"} for key,value in ipairs(array) do print(key, value) end
运行上述代码后,我们将得到以下输出:
1 Lua 2 Tutorial
上述示例使用Lua提供的默认ipairs迭代器函数。
在Lua中,我们使用函数来表示迭代器。根据这些迭代器函数中的状态维护,我们主要有两種類型:
- 无状态迭代器
- 有状态迭代器
无状态迭代器
顾名思义,这种类型的迭代器函数不保留任何状态。
现在让我们来看一个使用简单函数创建我们自己的迭代器的例子,该函数打印n个数的平方。
function square(iteratorMaxCount,currentNumber) if currentNumber<iteratorMaxCount then currentNumber = currentNumber+1 return currentNumber, currentNumber*currentNumber end end for i,n in square,3,0 do print(i,n) end
运行上述程序后,我们将得到以下输出。
1 1 2 4 3 9
上面的代码可以稍微修改一下,以模拟迭代器的ipairs函数的工作方式。如下所示。
function square(iteratorMaxCount,currentNumber) if currentNumber<iteratorMaxCount then currentNumber = currentNumber+1 return currentNumber, currentNumber*currentNumber end end function squares(iteratorMaxCount) return square,iteratorMaxCount,0 end for i,n in squares(3) do print(i,n) end
运行上述程序后,我们将得到以下输出。
1 1 2 4 3 9
有状态迭代器
之前的使用函数的迭代示例不保留状态。每次调用函数时,它都会根据发送到函数的第二个变量返回集合的下一个元素。为了保存当前元素的状态,使用闭包。闭包跨函数调用保留变量值。要创建一个新的闭包,我们创建两个函数,包括闭包本身和一个工厂,即创建闭包的函数。
现在让我们来看一个创建我们自己的迭代器的例子,在这个例子中我们将使用闭包。
array = {"Lua", "Tutorial"} function elementIterator (collection) local index = 0 local count = #collection -- The closure function is returned return function () index = index + 1 if index <= count then -- return the current element of the iterator return collection[index] end end end for element in elementIterator(array) do print(element) end
运行上述程序后,我们将得到以下输出。
Lua Tutorial
在上面的例子中,我们可以看到elementIterator内部还有另一个方法,它使用局部外部变量index和count,通过在每次调用函数时递增index来返回集合中的每个元素。
我们可以像上面那样使用闭包创建我们自己的函数迭代器,并且它可以在每次迭代集合时返回多个元素。
Lua - 表
简介
表是Lua中唯一可用的数据结构,它帮助我们创建数组和字典等不同类型。Lua使用关联数组,可以用数字和字符串索引,除了nil。表没有固定大小,可以根据我们的需要增长。
Lua在所有表示中都使用表,包括包的表示。当我们访问方法string.format时,这意味着我们正在访问string包中可用的format函数。
表示和用法
表被称为对象,它们既不是值也不是变量。Lua使用构造表达式{}来创建一个空表。需要注意的是,持有表引用的变量和表本身之间没有固定的关系。
--sample table initialization mytable = {} --simple table value assignment mytable[1]= "Lua" --removing reference mytable = nil -- lua garbage collection will take care of releasing memory
当我们有一个包含一组元素的表a,并且我们将它赋值给b时,a和b都引用相同的内存。不会为b单独分配单独的内存。当a设置为nil时,表仍然可以通过b访问。当没有对表的引用时,Lua中的垃圾收集负责清理过程,使这些未引用的内存可以再次重用。
下面给出一个例子来解释上面提到的表的特性。
-- Simple empty table mytable = {} print("Type of mytable is ",type(mytable)) mytable[1]= "Lua" mytable["wow"] = "Tutorial" print("mytable Element at index 1 is ", mytable[1]) print("mytable Element at index wow is ", mytable["wow"]) -- alternatetable and mytable refers to same table alternatetable = mytable print("alternatetable Element at index 1 is ", alternatetable[1]) print("alternatetable Element at index wow is ", alternatetable["wow"]) alternatetable["wow"] = "I changed it" print("mytable Element at index wow is ", mytable["wow"]) -- only variable released and and not table alternatetable = nil print("alternatetable is ", alternatetable) -- mytable is still accessible print("mytable Element at index wow is ", mytable["wow"]) mytable = nil print("mytable is ", mytable)
运行上面的程序,我们将得到以下输出:
Type of mytable is table mytable Element at index 1 is Lua mytable Element at index wow is Tutorial alternatetable Element at index 1 is Lua alternatetable Element at index wow is Tutorial mytable Element at index wow is I changed it alternatetable is nil mytable Element at index wow is I changed it mytable is nil
表操作
有一些内置函数用于表操作,它们列在下面的表中。
Lua提供以下类型的循环来处理循环需求。 | 方法及用途 |
---|---|
1 | table.concat (table [, sep [, i [, j]]]) 根据给定的参数连接表中的字符串。详情请参见示例。 |
2 | table.insert (table, [pos,] value) 在指定位置将值插入到表中。 |
3 | table.maxn (table) 返回最大的数字索引。 |
4 | table.remove (table [, pos]) 从表中删除值。 |
5 | table.sort (table [, comp]) 根据可选的比较器参数对表进行排序。 |
让我们看看上面一些函数的示例。
表连接
我们可以使用concat函数连接两个表,如下所示:
fruits = {"banana","orange","apple"} -- returns concatenated string of table print("Concatenated string ",table.concat(fruits)) --concatenate with a character print("Concatenated string ",table.concat(fruits,", ")) --concatenate fruits based on index print("Concatenated string ",table.concat(fruits,", ", 2,3))
运行上面的程序,我们将得到以下输出:
Concatenated string bananaorangeapple Concatenated string banana, orange, apple Concatenated string orange, apple
插入和删除
在表操作中最常见的是表中项目的插入和删除。解释如下。
fruits = {"banana","orange","apple"} -- insert a fruit at the end table.insert(fruits,"mango") print("Fruit at index 4 is ",fruits[4]) --insert fruit at index 2 table.insert(fruits,2,"grapes") print("Fruit at index 2 is ",fruits[2]) print("The maximum elements in table is",table.maxn(fruits)) print("The last element is",fruits[5]) table.remove(fruits) print("The previous last element is",fruits[5])
运行上面的程序,我们将得到以下输出:
Fruit at index 4 is mango Fruit at index 2 is grapes The maximum elements in table is 5 The last element is mango The previous last element is nil
排序表
我们经常需要按特定顺序对表进行排序。sort函数按字母顺序对表中的元素进行排序。下面是一个示例。
fruits = {"banana","orange","apple","grapes"} for k,v in ipairs(fruits) do print(k,v) end table.sort(fruits) print("sorted table") for k,v in ipairs(fruits) do print(k,v) end
运行上面的程序,我们将得到以下输出:
1 banana 2 orange 3 apple 4 grapes sorted table 1 apple 2 banana 3 grapes 4 orange
Lua - 模块
什么是模块?
模块就像一个可以使用require加载的库,它包含一个包含表的单个全局名称。这个模块可以包含许多函数和变量。所有这些函数和变量都被包装到表中,该表充当命名空间。此外,一个表现良好的模块有必要的规定来在require时返回这个表。
Lua模块的特性
在模块中使用表以多种方式帮助我们,并使我们能够以与操作任何其他Lua表相同的方式操作模块。由于能够操作模块,它提供了其他语言需要特殊机制的额外功能。由于Lua中模块的这种自由机制,用户可以用多种方式调用Lua中的函数。下面列出其中一些。
-- Assuming we have a module printFormatter -- Also printFormatter has a funtion simpleFormat(arg) -- Method 1 require "printFormatter" printFormatter.simpleFormat("test") -- Method 2 local formatter = require "printFormatter" formatter.simpleFormat("test") -- Method 3 require "printFormatter" local formatterFunction = printFormatter.simpleFormat formatterFunction("test")
在上面的示例代码中,您可以看到Lua编程的灵活性,无需任何特殊的附加代码。
require函数
Lua提供了一个高级函数require来加载所有必要的模块。它尽可能保持简单,以避免加载模块时有太多关于模块的信息。require函数只是将模块视为定义某些值的代码块,实际上是函数或包含函数的表。
示例
让我们考虑一个简单的例子,其中一个函数具有数学函数。让我们将此模块称为mymath,文件名是mymath.lua。文件内容如下:
local mymath = {} function mymath.add(a,b) print(a+b) end function mymath.sub(a,b) print(a-b) end function mymath.mul(a,b) print(a*b) end function mymath.div(a,b) print(a/b) end return mymath
现在,为了在另一个文件中访问此Lua模块,例如moduletutorial.lua,您需要使用以下代码段。
mymathmodule = require("mymath") mymathmodule.add(10,20) mymathmodule.sub(30,20) mymathmodule.mul(10,20) mymathmodule.div(30,20)
为了运行此代码,我们需要将两个Lua文件放在同一个目录中,或者您可以将模块文件放在包路径中,这需要额外的设置。运行上面的程序,我们将得到以下输出。
30 10 200 1.5
需要注意的事项
将模块和运行的文件放在同一个目录中。
模块名称和文件名应相同。
最佳实践是为require函数返回模块,因此即使您可以在其他地方找到其他类型的实现,也最好如上所示实现模块。
旧的模块实现方式
现在让我以旧的方式重写相同的示例,该方式使用package.seeall类型的实现。这在Lua 5.1和5.0版本中使用。mymath模块如下所示。
module("mymath", package.seeall) function mymath.add(a,b) print(a+b) end function mymath.sub(a,b) print(a-b) end function mymath.mul(a,b) print(a*b) end function mymath.div(a,b) print(a/b) end
moduletutorial.lua中模块的使用如下所示。
require("mymath") mymath.add(10,20) mymath.sub(30,20) mymath.mul(10,20) mymath.div(30,20)
运行上面的程序,我们将得到相同的输出。但是建议不要使用旧版本的代码,因为它被认为安全性较低。许多使用Lua进行编程的SDK,如Corona SDK,已经弃用了这种用法。
Lua - 元表
元表是一个表,它可以帮助修改与其关联的表的行为,方法是使用密钥集和相关的元方法。这些元方法是强大的Lua功能,可以启用以下功能:
更改/添加表的运算符功能。
当表中没有键时,使用元表中的__index查找元表。
在处理元表时,使用两种重要的方法:
setmetatable(table,metatable) − 此方法用于为表设置元表。
getmetatable(table) − 此方法用于获取表的元表。
让我们首先看看如何将一个表设置为另一个表的元表。如下所示。
mytable = {} mymetatable = {} setmetatable(mytable,mymetatable)
上面的代码可以表示为一行,如下所示。
mytable = setmetatable({},{})
_index
下面是一个简单的元表示例,用于在表中找不到元表时查找元表。
mytable = setmetatable({key1 = "value1"}, { __index = function(mytable, key) if key == "key2" then return "metatablevalue" else return mytable[key] end end }) print(mytable.key1,mytable.key2)
运行上述程序后,我们将得到以下输出。
value1 metatablevalue
让我们逐步解释上面示例中发生的情况。
这里的mytable表为{key1 = "value1"}。
为mytable设置元表,其中包含__index函数,我们将其称为元方法。
元方法的工作很简单,查找索引“key2”,如果找到,则返回“metatablevalue”,否则返回mytable中对应索引的值。
我们可以简化上面的程序,如下所示。
mytable = setmetatable({key1 = "value1"}, { __index = { key2 = "metatablevalue" } }) print(mytable.key1,mytable.key2)
__newindex
当我们将__newindex添加到元表时,如果表中没有键,则新键的行为将由元方法定义。下面是一个简单的示例,当主表中没有索引时,设置元表的索引。
mymetatable = {} mytable = setmetatable({key1 = "value1"}, { __newindex = mymetatable }) print(mytable.key1) mytable.newkey = "new value 2" print(mytable.newkey,mymetatable.newkey) mytable.key1 = "new value 1" print(mytable.key1,mymetatable.newkey1)
运行上面的程序,您将得到以下输出。
value1 nil new value 2 new value 1 nil
您可以在上面的程序中看到,如果键存在于主表中,它只会更新它。当主表中没有键时,它会将该键添加到元表。
另一个使用rawset函数更新同一个表的示例如下所示。
mytable = setmetatable({key1 = "value1"}, { __newindex = function(mytable, key, value) rawset(mytable, key, "\""..value.."\"") end }) mytable.key1 = "new value" mytable.key2 = 4 print(mytable.key1,mytable.key2)
运行上面的程序,我们将得到以下输出。
new value "4"
rawset在不使用元表的__newindex的情况下设置值。类似地,rawget在不使用__index的情况下获取值。
向表添加运算符行为
下面是一个使用+运算符组合两个表的简单示例:
mytable = setmetatable({ 1, 2, 3 }, { __add = function(mytable, newtable) for i = 1, table.maxn(newtable) do table.insert(mytable, table.maxn(mytable)+1,newtable[i]) end return mytable end }) secondtable = {4,5,6} mytable = mytable + secondtable for k,v in ipairs(mytable) do print(k,v) end
运行上述程序后,我们将得到以下输出。
1 1 2 2 3 3 4 4 5 5 6 6
__add键包含在元表中以添加+运算符的行为。键和相应运算符的表如下所示。
Lua提供以下类型的循环来处理循环需求。 | 模式和描述 |
---|---|
1 | __add 更改'+'运算符的行为。 |
2 | __sub 更改'-'运算符的行为。 |
3 | __mul 更改'*'运算符的行为。 |
4 | __div 更改'/'运算符的行为。 |
5 | __mod 更改'%'运算符的行为。 |
6 | __unm 更改'-'运算符的行为。 |
7 | __concat 更改'..'运算符的行为。 |
8 | __eq 更改'=='运算符的行为。 |
9 | __lt 更改'<'运算符的行为。 |
10 | __le 更改'<='运算符的行为。 |
__call
使用__call语句添加方法调用的行为。一个简单的示例,返回主表中值与传递表的总和。
mytable = setmetatable({10}, { __call = function(mytable, newtable) sum = 0 for i = 1, table.maxn(mytable) do sum = sum + mytable[i] end for i = 1, table.maxn(newtable) do sum = sum + newtable[i] end return sum end }) newtable = {10,20,30} print(mytable(newtable))
运行上述程序后,我们将得到以下输出。
70
__tostring
要更改print语句的行为,我们可以使用__tostring元方法。下面是一个简单的示例。
mytable = setmetatable({ 10, 20, 30 }, { __tostring = function(mytable) sum = 0 for k, v in pairs(mytable) do sum = sum + v end return "The sum of values in the table is " .. sum end }) print(mytable)
运行上述程序后,我们将得到以下输出。
The sum of values in the table is 60
如果您充分了解元表的强大功能,您可以轻松执行许多如果没有元表则非常复杂的操作。因此,请尝试更多地使用元表及其提供的各种选项(如示例中所述),并创建您自己的示例。
Lua - 协程
简介
协程本质上是协作式的,允许两种或多种方法以受控方式执行。使用协程时,在任何给定时间,只有一个协程运行,并且此运行协程只有在其明确请求挂起时才会挂起其执行。
上述定义可能看起来比较模糊。让我们假设我们有两个方法,一个是主程序方法,另一个是协程。当我们使用 resume 函数调用协程时,它开始执行;当我们调用 yield 函数时,它挂起执行。同一个协程可以使用另一个 resume 函数调用从其挂起的地方继续执行。此过程可以一直持续到协程执行结束。
协程中可用的函数
下表列出了 Lua 中所有可用的协程函数及其对应的用途。
Lua提供以下类型的循环来处理循环需求。 | 方法及用途 |
---|---|
1 | coroutine.create (f) 使用函数 f 创建一个新的协程,并返回一个“线程”类型的对象。 |
2 | coroutine.resume (co [, val1, ...]) 恢复协程 co 并传递任何参数(如有)。它返回操作的状态和可选的其他返回值。 |
3 | coroutine.running () 返回正在运行的协程,如果在主线程中调用则返回 nil。 |
4 | coroutine.status (co) 根据协程的状态,返回 running、normal、suspended 或 dead 中的一个值。 |
5 | coroutine.wrap (f) 与 coroutine.create 类似,coroutine.wrap 函数也创建一个协程,但它不返回协程本身,而是返回一个函数,当调用该函数时,它会恢复协程。 |
6 | coroutine.yield (...) 挂起正在运行的协程。传递给此方法的参数充当 resume 函数的附加返回值。 |
示例
让我们来看一个例子来理解协程的概念。
co = coroutine.create(function (value1,value2) local tempvar3 = 10 print("coroutine section 1", value1, value2, tempvar3) local tempvar1 = coroutine.yield(value1+1,value2+1) tempvar3 = tempvar3 + value1 print("coroutine section 2",tempvar1 ,tempvar2, tempvar3) local tempvar1, tempvar2= coroutine.yield(value1+value2, value1-value2) tempvar3 = tempvar3 + value1 print("coroutine section 3",tempvar1,tempvar2, tempvar3) return value2, "end" end) print("main", coroutine.resume(co, 3, 2)) print("main", coroutine.resume(co, 12,14)) print("main", coroutine.resume(co, 5, 6)) print("main", coroutine.resume(co, 10, 20))
运行上述程序后,我们将得到以下输出。
coroutine section 1 3 2 10 main true 4 3 coroutine section 2 12 nil 13 main true 5 1 coroutine section 3 5 6 16 main true 2 end main false cannot resume dead coroutine
上述示例做了什么?
如前所述,我们使用 resume 函数启动操作,使用 yield 函数停止操作。此外,您可以看到协程的 resume 函数接收多个返回值。
首先,我们创建一个协程并将其赋值给变量名 co,协程接收两个变量作为其参数。
当我们调用第一个 resume 函数时,值 3 和 2 会保留在临时变量 value1 和 value2 中,直到协程结束。
为了让您理解这一点,我们使用了 tempvar3,它最初为 10,由于 value1 在协程执行过程中始终保持为 3,因此它通过后续的协程调用更新为 13 和 16。
第一个 coroutine.yield 将两个值 4 和 3 返回给 resume 函数,我们通过更新 yield 语句中的输入参数 3 和 2 来获得这些值。它还接收协程执行的真/假状态。
关于协程的另一个方面是如何处理 resume 调用的下一个参数,在上面的示例中;您可以看到变量 coroutine.yield 接收下一个调用的参数,这提供了一种强大的方法来使用现有参数值的关系执行新操作。
最后,一旦协程中的所有语句都执行完毕,后续调用将返回 false 和“无法恢复已终止的协程”语句作为响应。
另一个协程示例
让我们来看一个简单的协程,它使用 yield 函数和 resume 函数返回 1 到 5 的数字。如果不存在协程,则创建协程;否则恢复现有协程。
function getNumber() local function getNumberHelper() co = coroutine.create(function () coroutine.yield(1) coroutine.yield(2) coroutine.yield(3) coroutine.yield(4) coroutine.yield(5) end) return co end if(numberHelper) then status, number = coroutine.resume(numberHelper); if coroutine.status(numberHelper) == "dead" then numberHelper = getNumberHelper() status, number = coroutine.resume(numberHelper); end return number else numberHelper = getNumberHelper() status, number = coroutine.resume(numberHelper); return number end end for index = 1, 10 do print(index, getNumber()) end
运行上述程序后,我们将得到以下输出。
1 1 2 2 3 3 4 4 5 5 6 1 7 2 8 3 9 4 10 5
协程经常与多编程语言的线程进行比较,但我们需要理解,协程具有与线程类似的功能,但它们一次只执行一个,并且永远不会并发执行。
我们控制程序执行顺序以满足需求,并暂时保留某些信息。将全局变量与协程一起使用可以为协程提供更大的灵活性。
Lua - 文件 I/O
I/O 库用于在 Lua 中读取和操作文件。Lua 中有两种文件操作,即隐式文件描述符和显式文件描述符。
对于以下示例,我们将使用如下所示的示例文件 test.lua。
-- sample test.lua -- sample2 test.lua
简单的文件打开操作使用以下语句。
file = io.open (filename [, mode])
下表列出了各种文件模式。
Lua提供以下类型的循环来处理循环需求。 | 模式和描述 |
---|---|
1 | "r" 只读模式,也是默认模式,其中打开现有文件。 |
2 | "w" 启用写入模式,该模式会覆盖现有文件或创建新文件。 |
3 | "a" 追加模式,打开现有文件或创建新文件以进行追加。 |
4 | "r+" 现有文件的读写模式。 |
5 | "w+" 如果文件存在,则删除所有现有数据;或者创建具有读写权限的新文件。 |
6 | "a+" 启用读模式的追加模式,打开现有文件或创建新文件。 |
隐式文件描述符
隐式文件描述符使用标准输入/输出模式,或者使用单个输入和单个输出文件。使用隐式文件描述符的示例如下所示。
-- Opens a file in read file = io.open("test.lua", "r") -- sets the default input file as test.lua io.input(file) -- prints the first line of the file print(io.read()) -- closes the open file io.close(file) -- Opens a file in append mode file = io.open("test.lua", "a") -- sets the default output file as test.lua io.output(file) -- appends a word test to the last line of the file io.write("-- End of the test.lua file") -- closes the open file io.close(file)
运行程序时,您将获得 test.lua 文件第一行的输出。对于我们的程序,我们获得了以下输出。
-- Sample test.lua
这是 test.lua 文件中我们语句的第一行。此外,"-- End of the test.lua file" 将被追加到 test.lua 代码的最后一行。
在上面的示例中,您可以看到隐式描述符如何使用 io."x" 方法与文件系统一起工作。上面的示例使用 io.read() 没有可选参数。可选参数可以是以下任何一个。
Lua提供以下类型的循环来处理循环需求。 | 模式和描述 |
---|---|
1 | "*n" 从当前文件位置读取,如果文件位置存在数字则返回该数字,否则返回 nil。 |
2 | "*a" 从当前文件位置返回文件的全部内容。 |
3 | "*l" 从当前文件位置读取行,并将文件位置移动到下一行。 |
4 | number 读取函数中指定的字节数。 |
其他常见的 I/O 方法包括:
io.tmpfile() − 返回一个用于读取和写入的临时文件,该文件将在程序退出时删除。
io.type(file) − 根据输入文件返回文件、已关闭的文件或 nil。
io.flush() − 清空默认输出缓冲区。
io.lines(可选文件名) − 提供一个通用的for循环迭代器,该迭代器遍历文件并在最后关闭文件(如果提供了文件名或使用了默认文件,并且在循环结束时未关闭)。
显式文件描述符
我们经常使用显式文件描述符,它允许我们一次操作多个文件。这些函数与隐式文件描述符非常相似。在这里,我们使用 file:function_name 代替 io.function_name。以下示例显示了相同隐式文件描述符示例的文件版本。
-- Opens a file in read mode file = io.open("test.lua", "r") -- prints the first line of the file print(file:read()) -- closes the opened file file:close() -- Opens a file in append mode file = io.open("test.lua", "a") -- appends a word test to the last line of the file file:write("--test") -- closes the open file file:close()
运行程序时,您将获得与隐式描述符示例类似的输出。
-- Sample test.lua
外部描述符的文件打开的所有模式和读取参数与隐式文件描述符相同。
其他常见的文件方法包括:
file:seek(可选 whence,可选 offset) − Whence 参数为“set”、“cur”或“end”。使用从文件开头更新的文件位置设置新的文件指针。在此函数中,偏移量为基于零的。如果第一个参数为“set”,则偏移量是从文件开头测量的;如果为“cur”,则从文件中的当前位置测量;如果为“end”,则从文件结尾测量。默认参数值为“cur”和 0,因此可以通过不带参数调用此函数来获得当前文件位置。
file:flush() − 清空默认输出缓冲区。
io.lines(可选文件名) − 提供一个通用的for循环迭代器,该迭代器遍历文件并在最后关闭文件(如果提供了文件名或使用了默认文件,并且在循环结束时未关闭)。
使用 seek 方法的示例如下所示。它将光标偏移到文件结尾之前的 25 个位置。read 函数打印从 seek 位置开始的文件其余部分。
-- Opens a file in read file = io.open("test.lua", "r") file:seek("end",-25) print(file:read("*a")) -- closes the opened file file:close()
您将获得一些类似于以下内容的输出。
sample2 test.lua --test
您可以尝试所有不同的模式和参数,以了解 Lua 文件操作的全部功能。
Lua - 错误处理
错误处理的必要性
错误处理非常关键,因为现实世界中的操作通常需要使用复杂的操作,包括文件操作、数据库事务和 Web 服务调用。
在任何编程中,都需要进行错误处理。错误可以分为两种类型,包括:
- 语法错误
- 运行时错误
语法错误
语法错误是由于不正确地使用各种程序组件(如运算符和表达式)造成的。语法错误的一个简单示例如下所示。
a == 2
如您所知,“等于”和“等于等于”的使用之间存在差异。使用一个代替另一个可能会导致错误。“等于”是指赋值,“等于等于”是指比较。类似地,表达式和函数具有其预定义的实现方式。
另一个语法错误示例如下所示:
for a= 1,10 print(a) end
运行上面的程序,我们将得到以下输出:
lua: test2.lua:2: 'do' expected near 'print'
语法错误比运行时错误更容易处理,因为 Lua 解释器比运行时错误更清晰地定位错误。从上述错误中,我们可以很容易地知道,根据 Lua 结构,需要在 print 语句之前添加一个do语句。
运行时错误
对于运行时错误,程序可以成功执行,但由于输入错误或函数处理不当,可能会导致运行时错误。一个简单的运行时错误示例如下所示。
function add(a,b) return a+b end add(10)
当我们构建程序时,它将成功构建并运行。一旦运行,就会显示运行时错误。
lua: test2.lua:2: attempt to perform arithmetic on local 'b' (a nil value) stack traceback: test2.lua:2: in function 'add' test2.lua:5: in main chunk [C]: ?
这是一个运行时错误,这是由于没有传递两个变量造成的。期望参数b,但此处为 nil,从而产生错误。
Assert 和 Error 函数
为了处理错误,我们经常使用两个函数:assert 和 error。一个简单的示例如下所示。
local function add(a,b) assert(type(a) == "number", "a is not a number") assert(type(b) == "number", "b is not a number") return a+b end add(10)
运行上述程序时,我们将获得以下错误输出。
lua: test2.lua:3: b is not a number stack traceback: [C]: in function 'assert' test2.lua:3: in function 'add' test2.lua:6: in main chunk [C]: ?
error (message [, level]) 终止最后一次调用的受保护函数,并将 message 作为错误消息返回。此 error 函数永远不会返回。通常,error 会在消息开头添加一些关于错误位置的信息。level 参数指定如何获取错误位置。使用 level 1(默认值),错误位置是调用 error 函数的位置。level 2 将错误指向调用 error 的函数的调用位置;依此类推。传递 level 0 将避免将错误位置信息添加到消息中。
pcall 和 xpcall
在 Lua 编程中,为了避免抛出这些错误并进行错误处理,我们需要使用 pcall 或 xpcall 函数。
pcall (f, arg1, ...) 函数以保护模式调用请求的函数。如果函数 f 中发生某些错误,它不会抛出错误。它只返回错误的状态。下面显示了一个使用 pcall 的简单示例。
function myfunction () n = n/nil end if pcall(myfunction) then print("Success") else print("Failure") end
运行上述程序后,我们将得到以下输出。
Failure
xpcall (f, err) 函数调用请求的函数并设置错误处理程序。f 内部的任何错误都不会传播;相反,xpcall 会捕获错误,使用原始错误对象调用 err 函数,并返回状态代码。
下面显示了 xpcall 的一个简单示例。
function myfunction () n = n/nil end function myerrorhandler( err ) print( "ERROR:", err ) end status = xpcall( myfunction, myerrorhandler ) print( status)
运行上述程序后,我们将得到以下输出。
ERROR: test2.lua:2: attempt to perform arithmetic on global 'n' (a nil value) false
作为程序员,最重要的是确保您在编写的程序中处理好适当的错误处理。使用错误处理可以确保处理超出边界条件的意外情况,而不会干扰程序的用户。
Lua - 调试
Lua 提供了一个调试库,该库为我们提供了创建自己的调试器所需的所有基本函数。尽管没有内置的 Lua 调试器,但我们有许多由不同开发者创建的 Lua 调试器,其中许多是开源的。
Lua 调试库中可用的函数及其用途列在下表中。
Lua提供以下类型的循环来处理循环需求。 | 方法及用途 |
---|---|
1 | debug() 进入交互式调试模式,该模式一直处于活动状态,直到我们在某一行键入 cont 并按 Enter 键为止。用户可以在此模式下使用其他函数检查变量。 |
2 | getfenv(object) 返回对象的运行环境。 |
3 | gethook(optional thread) 返回线程的当前钩子设置,作为三个值:当前钩子函数、当前钩子掩码和当前钩子计数。 |
4 | getinfo(optional thread, function or stack level, optional flag) 返回一个包含函数信息的表。您可以直接提供函数,也可以提供一个数字作为函数的值,这意味着在给定线程的调用堆栈的函数级别运行的函数——级别 0 是当前函数(getinfo 本身);级别 1 是调用 getinfo 的函数;以此类推。如果函数的数字大于活动函数的数量,则 getinfo 返回 nil。 |
5 | getlocal(optional thread, stack level, local index) 返回堆栈级别 level 的函数中索引为 local 的局部变量的名称和值。如果不存在具有给定索引的局部变量,则返回 nil;如果使用超出范围的级别调用,则会引发错误。 |
6 | getmetatable(value) 返回给定对象的元表,如果它没有元表则返回 nil。 |
7 | getregistry() 返回注册表,一个预定义的表,任何 C 代码都可以使用它来存储任何 Lua 值。 |
8 | getupvalue(function, upvalue index) 此函数返回函数 func 中索引为 up 的 upvalue 的名称和值。如果不存在具有给定索引的 upvalue,则该函数返回 nil。 |
9 | setfenv(function or thread or userdata, environment table) 将给定对象的运行环境设置为给定的表。返回对象。 |
10 | sethook(optional thread, hook function, hook mask string with "c" and/or "r" and/or "l", optional instruction count) 将给定函数设置为钩子。字符串掩码和计数数字描述了何时调用钩子。这里,c、r 和 l 分别在 Lua 调用、返回和进入函数的每一行代码时被调用。 |
11 | setlocal(optional thread, stack level, local index, value) 将值赋给堆栈级别 level 的函数中索引为 local 的局部变量。如果不存在具有给定索引的局部变量,则该函数返回 nil;如果使用超出范围的级别调用,则会引发错误。否则,它返回局部变量的名称。 |
12 | setmetatable(value, metatable) 将给定对象的元表设置为给定的表(可以为 nil)。 |
13 | setupvalue(function, upvalue index, value) 此函数将值赋给函数 func 中索引为 up 的 upvalue。如果不存在具有给定索引的 upvalue,则该函数返回 nil。否则,它返回 upvalue 的名称。 |
14 | traceback(optional thread, optional message string, optional level argument) 构建包含回溯的扩展错误消息。 |
以上列表是 Lua 中调试函数的完整列表,我们经常使用一个使用上述函数并提供更轻松调试的库。使用这些函数并创建我们自己的调试器相当复杂,并不推荐。无论如何,我们将看到一个简单使用调试函数的示例。
function myfunction () print(debug.traceback("Stack trace")) print(debug.getinfo(1)) print("Stack trace end") return 10 end myfunction () print(debug.getinfo(1))
运行上述程序时,我们将得到如下所示的堆栈跟踪。
Stack trace stack traceback: test2.lua:2: in function 'myfunction' test2.lua:8: in main chunk [C]: ? table: 0054C6C8 Stack trace end
在上面的示例程序中,堆栈跟踪是通过使用调试库中提供的 debug.trace 函数打印的。debug.getinfo 获取函数的当前表。
调试 - 示例
我们经常需要知道函数的局部变量才能进行调试。为此,我们可以使用 getupvalue,要设置这些局部变量,我们使用 setupvalue。下面显示了一个简单的示例。
function newCounter () local n = 0 local k = 0 return function () k = n n = n + 1 return n end end counter = newCounter () print(counter()) print(counter()) local i = 1 repeat name, val = debug.getupvalue(counter, i) if name then print ("index", i, name, "=", val) if(name == "n") then debug.setupvalue (counter,2,10) end i = i + 1 end -- if until not name print(counter())
运行上述程序后,我们将得到以下输出。
1 2 index 1 k = 1 index 2 n = 2 11
在这个例子中,计数器每次调用时都会增加一。我们可以使用 getupvalue 函数查看局部变量的当前状态。然后我们将局部变量设置为一个新值。这里,在调用 set 操作之前,n 为 2。使用 setupvalue 函数,它被更新为 10。现在当我们调用计数器函数时,它将返回 11 而不是 3。
调试类型
- 命令行调试
- 图形化调试
命令行调试
命令行调试是一种使用命令行和打印语句进行调试的调试类型。Lua 有许多可用的命令行调试器,其中一些列在下面。
RemDebug - RemDebug 是 Lua 5.0 和 5.1 的远程调试器。它允许您远程控制另一个 Lua 程序的执行,设置断点并检查程序的当前状态。RemDebug 还可以调试 CGILua 脚本。
clidebugger - 一个用纯 Lua 编写的 Lua 5.1 的简单命令行界面调试器。它不依赖于标准 Lua 5.1 库以外的任何东西。它受到 RemDebug 的启发,但没有它的远程功能。
ctrace - 用于跟踪 Lua API 调用的工具。
xdbLua - Windows 平台的简单 Lua 命令行调试器。
LuaInterface - Debugger - 此项目是 LuaInterface 的调试器扩展。它将内置的 Lua 调试接口提升到更高的级别。与调试器的交互是通过事件和方法调用完成的。
Rldb - 这是一个通过套接字进行远程 Lua 调试的工具,可在 Windows 和 Linux 上使用。它可以提供比任何现有工具都多的功能。
ModDebug - 这允许远程控制另一个 Lua 程序的执行,设置断点并检查程序的当前状态。
图形化调试
借助 IDE 可以进行图形化调试,其中您可以对各种状态(如变量值、堆栈跟踪和其他相关信息)进行可视化调试。借助 IDE 中的断点、单步进入、单步跳过和其他按钮,可以对执行进行可视化表示和逐步控制。
Lua 有许多图形化调试器,包括以下这些。
SciTE - Lua 的默认 Windows IDE 提供多个调试功能,例如断点、单步、单步进入、单步跳过、监视变量等等。
Decoda - 这是一个具有远程调试支持的图形化调试器。
ZeroBrane Studio - 集成了远程调试器、堆栈视图、监视视图、远程控制台、静态分析器等的 Lua IDE。适用于 LuaJIT、Love2d、Moai 和其他 Lua 引擎;Windows、OSX 和 Linux。开源。
akdebugger - Eclipse 的调试器和编辑器 Lua 插件。
luaedit - 它具有远程调试、本地调试、语法高亮、代码补全列表、参数建议引擎、高级断点管理(包括断点上的条件系统和命中计数)、函数列表、全局和局部变量列表、监视、面向解决方案的管理等功能。
Lua - 垃圾回收
Lua 使用基于某些内置算法的垃圾收集的自动内存管理。由于自动内存管理,作为开发者:
- 无需担心为对象分配内存。
- 无需在不再需要时释放它们,除非将其设置为 nil。
Lua 使用一个垃圾收集器,该收集器会不时运行以收集不再可从 Lua 程序访问的死对象。
所有对象(包括表、用户数据、函数、线程、字符串等)都受自动内存管理。Lua 使用增量标记和清除收集器,它使用两个数字来控制其垃圾收集周期,即垃圾收集器暂停和垃圾收集器步长乘数。这些值以百分比表示,值 100 通常在内部等于 1。
垃圾收集器暂停
垃圾收集器暂停用于控制垃圾收集器需要等待多长时间,然后 Lua 的自动内存管理再次调用它。小于 100 的值意味着 Lua 将不会等待下一个周期。同样,此值的较高值会导致垃圾收集器速度较慢且性质不那么积极。值为 200 表示收集器等待使用中的总内存加倍后再启动新周期。因此,根据应用程序的性质和速度,可能需要更改此值才能在 Lua 应用程序中获得最佳性能。
垃圾收集器步长乘数
此步长乘数控制 Lua 程序中垃圾收集器与内存分配的相对速度。较大的步长值将导致垃圾收集器更积极,并且还会增加垃圾收集每个增量步骤的步长。小于 100 的值通常会导致垃圾收集器无法完成其周期,通常不推荐。默认值为 200,这意味着垃圾收集器的运行速度是内存分配速度的两倍。
垃圾收集器函数
作为开发者,我们确实可以对 Lua 中的自动内存管理进行一些控制。为此,我们有以下方法。
collectgarbage("collect") - 运行一次完整的垃圾收集周期。
collectgarbage("count") - 返回程序当前使用的内存量(以千字节为单位)。
collectgarbage("restart") - 如果垃圾收集器已停止,则重新启动它。
collectgarbage("setpause") - 将作为第二个参数给出的值除以 100 设置为垃圾收集器暂停变量。其用途如上所述。
collectgarbage("setstepmul") - 将作为第二个参数给出的值除以 100 设置为垃圾收集器步长乘数变量。其用途如上所述。
collectgarbage("step") - 运行一步垃圾收集。第二个参数越大,这一步就越大。如果触发的步骤是垃圾收集周期的最后一步,则 collectgarbage 将返回 true。
collectgarbage("stop") - 如果垃圾收集器正在运行,则停止它。
下面显示了一个使用垃圾收集器示例的简单示例。
mytable = {"apple", "orange", "banana"} print(collectgarbage("count")) mytable = nil print(collectgarbage("count")) print(collectgarbage("collect")) print(collectgarbage("count"))
运行以上程序后,我们将得到以下输出。请注意,由于操作系统类型的不同以及 Lua 的自动内存管理功能,此结果会有所不同。
23.1455078125 149 23.2880859375 295 0 22.37109375 380
您可以在上述程序中看到,一旦垃圾回收完成,它就会减少使用的内存。但是,这并非强制性的。即使我们不调用它们,Lua 解释器也会在预定义的时间段后自动在后期执行它们。
显然,如果需要,我们可以使用这些函数来更改垃圾收集器的行为。这些函数为开发人员提供了处理复杂情况的额外能力。根据程序的内存需求类型,您可以使用或不使用此功能。但是,了解应用程序中的内存使用情况并在编程过程中检查它以避免部署后出现意外结果非常有用。
Lua - 面向对象
面向对象编程简介
面向对象编程 (OOP) 是现代编程时代最常用的编程技术之一。许多编程语言都支持 OOP,包括:
- C++
- Java
- Objective-C
- Smalltalk
- C#
- Ruby
OOP 的特性
类 − 类是创建对象的可扩展模板,提供状态(成员变量)的初始值和行为的实现。
对象 − 它是类的实例,并为此自身分配了单独的内存。
继承 − 这是一种概念,一个类的变量和函数可以被另一个类继承。
封装 − 它是将数据和函数组合到一个类中的过程。可以使用函数从类外部访问数据。它也称为数据抽象。
Lua 中的 OOP
您可以借助 Lua 的表和一等函数在 Lua 中实现面向对象。通过将函数和相关数据放入表中,形成一个对象。继承可以借助元表来实现,为父对象中不存在的函数(方法)和字段提供查找机制。
Lua 中的表具有类似于对象的状态和标识的特性,这些特性与其值无关。具有相同值的两个对象(表)是不同的对象,而一个对象可以在不同时间具有不同的值,但它始终是同一个对象。与对象一样,表的生命周期与其创建者或创建位置无关。
一个真实的例子
面向对象的概念被广泛使用,但您需要清楚地理解它才能获得最佳益处。
让我们考虑一个简单的数学例子。我们经常遇到处理不同形状(如圆形、矩形和正方形)的情况。
这些形状可以具有共同的属性“面积”。因此,我们可以从具有公共属性“面积”的基对象“形状”扩展其他形状。每个形状都可以有其自身的属性和函数,例如矩形可以具有属性长度、宽度、面积以及函数 printArea 和 calculateArea。
创建简单的类
下面显示了一个具有三个属性(面积、长度和宽度)的矩形的简单类实现。它还有一个 printArea 函数来打印计算出的面积。
-- Meta class Rectangle = {area = 0, length = 0, breadth = 0} -- Derived class method new function Rectangle:new (o,length,breadth) o = o or {} setmetatable(o, self) self.__index = self self.length = length or 0 self.breadth = breadth or 0 self.area = length*breadth; return o end -- Derived class method printArea function Rectangle:printArea () print("The area of Rectangle is ",self.area) end
创建对象
创建对象是为类实例分配内存的过程。每个对象都有自己的内存,并共享公共类数据。
r = Rectangle:new(nil,10,20)
访问属性
我们可以使用点运算符访问类中的属性,如下所示:
print(r.length)
访问成员函数
您可以使用冒号运算符和对象一起访问成员函数,如下所示:
r:printArea()
内存被分配,并且设置初始值。初始化过程可以与其他面向对象语言中的构造函数进行比较。它只不过是一个允许设置值的函数,如上所示。
完整示例
让我们看一个在 Lua 中使用面向对象的完整示例。
-- Meta class Shape = {area = 0} -- Base class method new function Shape:new (o,side) o = o or {} setmetatable(o, self) self.__index = self side = side or 0 self.area = side*side; return o end -- Base class method printArea function Shape:printArea () print("The area is ",self.area) end -- Creating an object myshape = Shape:new(nil,10) myshape:printArea()
运行上述程序后,您将得到以下输出。
The area is 100
Lua 中的继承
继承是从简单基对象(如形状)扩展到矩形、正方形等的过程。它经常在现实世界中用于共享和扩展基本属性和函数。
让我们来看一个简单的类扩展。我们有一个如下所示的类。
-- Meta class Shape = {area = 0} -- Base class method new function Shape:new (o,side) o = o or {} setmetatable(o, self) self.__index = self side = side or 0 self.area = side*side; return o end -- Base class method printArea function Shape:printArea () print("The area is ",self.area) end
我们可以将形状扩展到正方形类,如下所示。
Square = Shape:new() -- Derived class method new function Square:new (o,side) o = o or Shape:new(o,side) setmetatable(o, self) self.__index = self return o end
重写基函数
我们可以重写基类函数,即派生类可以使用其自身的实现来代替基类中的函数,如下所示:
-- Derived class method printArea function Square:printArea () print("The area of square is ",self.area) end
继承完整示例
我们可以使用元表通过另一种新方法扩展如上所示的 Lua 中的简单类实现。基类中的所有成员变量和函数都保留在派生类中。
-- Meta class Shape = {area = 0} -- Base class method new function Shape:new (o,side) o = o or {} setmetatable(o, self) self.__index = self side = side or 0 self.area = side*side; return o end -- Base class method printArea function Shape:printArea () print("The area is ",self.area) end -- Creating an object myshape = Shape:new(nil,10) myshape:printArea() Square = Shape:new() -- Derived class method new function Square:new (o,side) o = o or Shape:new(o,side) setmetatable(o, self) self.__index = self return o end -- Derived class method printArea function Square:printArea () print("The area of square is ",self.area) end -- Creating an object mysquare = Square:new(nil,10) mysquare:printArea() Rectangle = Shape:new() -- Derived class method new function Rectangle:new (o,length,breadth) o = o or Shape:new(o) setmetatable(o, self) self.__index = self self.area = length * breadth return o end -- Derived class method printArea function Rectangle:printArea () print("The area of Rectangle is ",self.area) end -- Creating an object myrectangle = Rectangle:new(nil,10,20) myrectangle:printArea()
运行上面的程序,我们将得到以下输出:
The area is 100 The area of square is 100 The area of Rectangle is 200
在上面的例子中,我们从基类 Square 创建了两个派生类——Rectangle 和 Square。可以在派生类中重写基类的函数。在这个例子中,派生类重写了函数 printArea。
Lua - Web编程
Lua 是一种高度灵活的语言,它经常用于多个平台,包括 Web 应用程序。2004 年成立的 Kepler 社区致力于提供 Lua 中的开源 Web 组件。
尽管已经开发出其他使用 Lua 的 Web 框架,但我们将主要关注 Kepler 社区提供的组件。
应用程序和框架
Orbit 是一个基于 WSAPI 的 Lua MVC Web 框架。
WSAPI 是一个将 Web 宿主服务器与 Lua Web 应用程序抽象出来的 API,它是许多项目的基石。
Xavante 是一个提供 WSAPI 接口的 Lua Web 服务器。
Sputnik 是一个基于 Kepler 项目的 WSAPI 上开发的 wiki/CMS,用于幽默和娱乐。
CGILua 提供 LuaPages 和 LuaScripts 网页创建,基于 WSAPI,但不再支持。请改用 Orbit、Sputnik 或 WSAPI。
在本教程中,我们将尝试让您了解 Lua 的功能,并了解有关其安装和使用的更多信息,请参考 Kepler 网站
Orbit
Orbit 是一个用于 Lua 的 MVC Web 框架。它完全放弃了 CGILua 的“脚本”模型,转而采用应用程序,其中每个 Orbit 应用程序都可以放在单个文件中,但如果需要,您也可以将其拆分为多个文件。
所有 Orbit 应用程序都遵循 WSAPI 协议,因此它们目前可与 Xavante、CGI 和 Fastcgi 一起使用。它包含一个启动器,可以轻松启动 Xavante 实例以进行开发。
安装 Orbit 最简单的方法是使用 LuaRocks。`luarocks install orbit` 是安装命令。为此,您需要先安装 LuaRocks。
如果您尚未安装所有依赖项,以下是设置 Unix/Linux 环境中 Orbit 的步骤。
安装 Apache
连接到您的服务器。安装 Apache2、其支持模块并启用所需的 Apache2 模块:
$ sudo apt-get install apache2 libapache2-mod-fcgid libfcgi-dev build-essential $ sudo a2enmod rewrite $ sudo a2enmod fcgid $ sudo /etc/init.d/apache2 force-reload
安装 LuaRocks
$ sudo apt-get install luarocks
安装 WSAPI、FCGI、Orbit 和 Xavante
$ sudo luarocks install orbit $ sudo luarocks install wsapi-xavante $ sudo luarocks install wsapi-fcgi
设置 Apache2
$ sudo raj /etc/apache2/sites-available/default
在配置文件的 `
<IfModule mod_fcgid.c> AddHandler fcgid-script .lua AddHandler fcgid-script .ws AddHandler fcgid-script .op FCGIWrapper "/usr/local/bin/wsapi.fcgi" .ws FCGIWrapper "/usr/local/bin/wsapi.fcgi" .lua FCGIWrapper "/usr/local/bin/op.fcgi" .op #FCGIServer "/usr/local/bin/wsapi.fcgi" -idle-timeout 60 -processes 1 #IdleTimeout 60 #ProcessLifeTime 60 </IfModule>
重新启动服务器以确保所做的更改生效。
要启用您的应用程序,您需要将 `+ExecCGI` 添加到 Orbit 应用程序根目录中的 .htaccess 文件中——在本例中为 `/var/www`。
Options +ExecCGI DirectoryIndex index.ws
简单示例 - Orbit
#!/usr/bin/env index.lua -- index.lua require"orbit" -- declaration module("myorbit", package.seeall, orbit.new) -- handler function index(web) return my_home_page() end -- dispatch myorbit:dispatch_get(index, "/", "/index") -- Sample page function my_home_page() return [[ <head></head> <html> <h2>First Page</h2> </html> ]] end
现在,您应该能够启动您的 Web 浏览器。访问 https://127.0.0.1:8080/,您应该看到以下输出:
First Page
Orbit 提供了另一种选择,即 Lua 代码可以生成 html。
#!/usr/bin/env index.lua -- index.lua require"orbit" function generate() return html { head{title "HTML Example"}, body{ h2{"Here we go again!"} } } end orbit.htmlify(generate) print(generate())
创建表单
下面显示了一个简单的表单示例:
#!/usr/bin/env index.lua require"orbit" function wrap (inner) return html{ head(), body(inner) } end function test () return wrap(form (H'table' { tr{td"First name",td( input{type = 'text', name='first'})}, tr{td"Second name",td(input{type = 'text', name='second'})}, tr{ td(input{type = 'submit', value = 'Submit!'}), td(input{type = 'submit',value = 'Cancel'}) }, })) end orbit.htmlify(wrap,test) print(test())
WSAPI
如前所述,WSAPI 充当许多项目的基石,并嵌入了多个功能。您可以使用 WSAPI 并支持以下平台:
- Windows
- 基于 UNIX 的系统
WSAPI 支持的服务器和接口包括:
- CGI
- FastCGI
- Xavante
WSAPI 提供了许多库,这使得我们使用 Lua 进行 Web 编程更加容易。Lua 中支持的一些功能包括:
- 请求处理
- 输出缓冲
- 身份验证
- 文件上传
- 请求隔离
- 多路复用
下面显示了 WSAPI 的一个简单示例:
#!/usr/bin/env wsapi.cgi module(..., package.seeall) function run(wsapi_env) local headers = { ["Content-type"] = "text/html" } local function hello_text() coroutine.yield("<html><body>") coroutine.yield("<p>Hello Wsapi!</p>") coroutine.yield("<p>PATH_INFO: " .. wsapi_env.PATH_INFO .. "</p>") coroutine.yield("<p>SCRIPT_NAME: " .. wsapi_env.SCRIPT_NAME .. "</p>") coroutine.yield("</body></html>") end return 200, headers, coroutine.wrap(hello_text) end
您可以在上面的代码中看到一个简单的 html 页面是如何形成并返回的。您可以看到协程的使用,它使得逐句将语句返回给调用函数成为可能。最后,返回 html 状态代码 (200)、标题和 html 页面。
Xavante
Xavante 是一个使用基于 URI 映射处理程序的模块化架构的 Lua HTTP 1.1 Web 服务器。Xavante 目前提供:
- 文件处理程序
- 重定向处理程序
- WSAPI 处理程序
文件处理程序用于通用文件。重定向处理程序启用 URI 重新映射,WSAPI 处理程序用于处理 WSAPI 应用程序。
下面显示了一个简单的示例。
require "xavante.filehandler" require "xavante.cgiluahandler" require "xavante.redirecthandler" -- Define here where Xavante HTTP documents scripts are located local webDir = XAVANTE_WEB local simplerules = { { -- URI remapping example match = "^[^%./]*/$", with = xavante.redirecthandler, params = {"index.lp"} }, { -- cgiluahandler example match = {"%.lp$", "%.lp/.*$", "%.lua$", "%.lua/.*$" }, with = xavante.cgiluahandler.makeHandler (webDir) }, { -- filehandler example match = ".", with = xavante.filehandler, params = {baseDir = webDir} }, } xavante.HTTP{ server = {host = "*", port = 8080}, defaultHost = { rules = simplerules }, }
要将虚拟主机与 Xavante 一起使用,对 xavante.HTTP 的调用将更改为类似以下内容:
xavante.HTTP{ server = {host = "*", port = 8080}, defaultHost = {}, virtualhosts = { ["www.sitename.com"] = simplerules } }
Lua Web 组件
Copas,一个基于协程的调度器,可由 TCP/IP 服务器使用。
Cosmo,一个“安全模板”引擎,可以保护您的应用程序免受模板中任意代码的攻击。
Coxpcall 使用与协程兼容的 Lua 原生 pcall 和 xpcall 进行封装。
LuaFileSystem,一种访问底层目录结构和文件属性的可移植方式。
Rings,一个提供从 Lua 内部创建新 Lua 状态的方法的库。
结束语
有很多基于 Lua 的 Web 框架和组件可供我们使用,可以根据需要进行选择。还有其他可用的 Web 框架,包括以下:
Moonstalk 能够高效地开发和托管使用 Lua 语言构建的动态生成的基于 Web 的项目;从基本的页面到复杂的应用程序。
Lapis 是一个用于使用 MoonScript(或 Lua)构建 Web 应用程序的框架,它运行在一个名为 OpenResty 的定制版 Nginx 中。
Lua 服务器页面 (Lua Server Pages),一个 Lua 脚本引擎插件,它优于任何其他嵌入式 Web 开发方法,为传统的 C 服务器页面提供了巨大的捷径。
这些 Web 框架可以利用您的 Web 应用程序,并帮助您执行强大的操作。
Lua - 数据库访问
对于简单的數據操作,我们可以使用文件,但是,有时这些文件操作可能效率不高,不可扩展且不够强大。为此,我们通常会切换到使用数据库。LuaSQL 是 Lua 与多个数据库管理系统之间的一个简单接口。LuaSQL 是一个库,它提供对不同类型 SQL 的支持,包括:
- SQLite
- MySQL
- ODBC
在本教程中,我们将介绍在 Lua 中处理 MySQL 和 SQLite 数据库的方法。这两种数据库都使用通用的接口,因此可以将此实现移植到其他类型的数据库。
MySQL 数据库设置
为了使以下示例按预期工作,我们需要进行初始数据库设置。假设条件如下:
您已安装并设置了 MySQL,默认用户为 root,密码为“123456”。
您已创建了一个名为 test 的数据库。
您已学习过 MySQL 教程,了解MySQL 基础知识。
导入 MySQL
假设您的 Lua 实现正确,我们可以使用简单的require语句导入 sqlite 库。
mysql = require "luasql.mysql"
变量 mysql 将通过引用主 mysql 表来提供对函数的访问。
设置连接
我们可以通过初始化 MySQL 环境,然后为该环境创建连接来设置连接。如下所示:
local env = mysql.mysql() local conn = env:connect('test','root','123456')
上述连接将连接到现有的 MySQL 文件,并与新创建的文件建立连接。
执行函数
连接提供了一个简单的 execute 函数,它可以帮助我们执行所有数据库操作,例如创建、插入、删除和更新等。语法如下:
conn:execute([[ 'MySQLSTATEMENT' ]])
在上述语法中,我们需要确保 conn 是打开的且存在的 MySQL 连接,并将“MySQLSTATEMENT”替换为正确的语句。
创建表示例
下面是一个简单的创建表示例。它创建一个包含两个参数的表:id(整数类型)和 name(varchar 类型)。
mysql = require "luasql.mysql" local env = mysql.mysql() local conn = env:connect('test','root','123456') print(env,conn) status,errorString = conn:execute([[CREATE TABLE sample2 (id INTEGER, name TEXT);]]) print(status,errorString )
运行上述程序后,将创建一个名为 sample 的表,其中包含名为 id 和 name 的两列。
MySQL environment (004BB178) MySQL connection (004BE3C8) 0 nil
如果出现任何错误,系统将返回错误语句而不是 nil。下面是一个简单的错误语句示例。
LuaSQL: Error executing query. MySQL: You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near '"id INTEGER, name TEXT)' at line 1
插入语句示例
下面是 MySQL 的插入语句示例。
conn:execute([[INSERT INTO sample values('11','Raj')]])
更新语句示例
下面是 MySQL 的更新语句示例。
conn:execute([[UPDATE sample3 SET name='John' where id ='12']])
删除语句示例
下面是 MySQL 的删除语句示例。
conn:execute([[DELETE from sample3 where id ='12']])
选择语句示例
至于选择语句,我们需要遍历每一行并提取所需的数据。下面是一个简单的选择语句示例。
cursor,errorString = conn:execute([[select * from sample]]) row = cursor:fetch ({}, "a") while row do print(string.format("Id: %s, Name: %s", row.id, row.name)) -- reusing the table of results row = cursor:fetch (row, "a") end
在上述代码中,conn 是一个打开的 MySQL 连接。借助 execute 语句返回的游标,您可以遍历表响应并获取所需的选择数据。
完整示例
下面是一个包含所有上述语句的完整示例。
mysql = require "luasql.mysql" local env = mysql.mysql() local conn = env:connect('test','root','123456') print(env,conn) status,errorString = conn:execute([[CREATE TABLE sample3 (id INTEGER, name TEXT)]]) print(status,errorString ) status,errorString = conn:execute([[INSERT INTO sample3 values('12','Raj')]]) print(status,errorString ) cursor,errorString = conn:execute([[select * from sample3]]) print(cursor,errorString) row = cursor:fetch ({}, "a") while row do print(string.format("Id: %s, Name: %s", row.id, row.name)) row = cursor:fetch (row, "a") end -- close everything cursor:close() conn:close() env:close()
运行上述程序后,您将得到以下输出。
MySQL environment (0037B178) MySQL connection (0037EBA8) 0 nil 1 nil MySQL cursor (003778A8) nil Id: 12, Name: Raj
执行事务
事务是一种确保数据一致性的机制。事务应该具有以下四个属性:
原子性 - 事务要么完成,要么什么也不发生。
一致性 - 事务必须从一致状态开始,并使系统保持一致状态。
隔离性 - 事务的中间结果在当前事务之外不可见。
持久性 - 一旦事务提交,其效果将是持久的,即使在系统故障后也是如此。
事务以 START TRANSACTION; 开始,并以 commit 或 rollback 语句结束。
启动事务
为了启动事务,我们需要在 Lua 中执行以下语句,假设 conn 是一个打开的 MySQL 连接。
conn:execute([[START TRANSACTION;]])
回滚事务
我们需要执行以下语句来回滚在启动事务后所做的更改。
conn:execute([[ROLLBACK;]])
提交事务
我们需要执行以下语句来提交在启动事务后所做的更改。
conn:execute([[COMMIT;]])
我们已经了解了上述 MySQL,以下部分将介绍基本的 SQL 操作。请记住,虽然没有再次解释 SQLite3 的事务,但相同的语句也应该适用于 SQLite3。
导入 SQLite
假设您的 Lua 实现正确,我们可以使用简单的 require 语句导入 SQLite 库。安装过程中,会创建一个包含数据库相关文件的 libsql 文件夹。
sqlite3 = require "luasql.sqlite3"
变量 sqlite3 将通过引用主 sqlite3 表来提供对函数的访问。
设置连接
我们可以通过初始化 SQLite 环境,然后为该环境创建连接来设置连接。如下所示:
local env = sqlite3.sqlite3() local conn = env:connect('mydb.sqlite')
上述连接将连接到现有的 SQLite 文件,或者创建一个新的 SQLite 文件,并与新创建的文件建立连接。
执行函数
连接提供了一个简单的 execute 函数,它可以帮助我们执行所有数据库操作,例如创建、插入、删除和更新等。语法如下:
conn:execute([[ 'SQLite3STATEMENT' ]])
在上述语法中,我们需要确保 conn 是打开的且存在的 sqlite3 连接,并将“SQLite3STATEMENT”替换为正确的语句。
创建表示例
下面是一个简单的创建表示例。它创建一个包含两个参数的表:id(整数类型)和 name(varchar 类型)。
sqlite3 = require "luasql.sqlite3" local env = sqlite3.sqlite3() local conn = env:connect('mydb.sqlite') print(env,conn) status,errorString = conn:execute([[CREATE TABLE sample ('id' INTEGER, 'name' TEXT)]]) print(status,errorString )
运行上述程序后,将创建一个名为 sample 的表,其中包含名为 id 和 name 的两列。
SQLite3 environment (003EC918) SQLite3 connection (00421F08) 0 nil
如果出现错误,系统将返回错误语句而不是 nil。下面是一个简单的错误语句示例。
LuaSQL: unrecognized token: ""'id' INTEGER, 'name' TEXT)"
插入语句示例
下面是 SQLite 的插入语句示例。
conn:execute([[INSERT INTO sample values('11','Raj')]])
选择语句示例
至于选择语句,我们需要遍历每一行并提取所需的数据。下面是一个简单的选择语句示例。
cursor,errorString = conn:execute([[select * from sample]]) row = cursor:fetch ({}, "a") while row do print(string.format("Id: %s, Name: %s", row.id, row.name)) -- reusing the table of results row = cursor:fetch (row, "a") end
在上述代码中,conn 是一个打开的 sqlite3 连接。借助 execute 语句返回的游标,您可以遍历表响应并获取所需的选择数据。
完整示例
下面是一个包含所有上述语句的完整示例。
sqlite3 = require "luasql.sqlite3" local env = sqlite3.sqlite3() local conn = env:connect('mydb.sqlite') print(env,conn) status,errorString = conn:execute([[CREATE TABLE sample ('id' INTEGER, 'name' TEXT)]]) print(status,errorString ) status,errorString = conn:execute([[INSERT INTO sample values('1','Raj')]]) print(status,errorString ) cursor,errorString = conn:execute([[select * from sample]]) print(cursor,errorString) row = cursor:fetch ({}, "a") while row do print(string.format("Id: %s, Name: %s", row.id, row.name)) row = cursor:fetch (row, "a") end -- close everything cursor:close() conn:close() env:close()
运行上述程序后,您将得到以下输出。
SQLite3 environment (005EC918) SQLite3 connection (005E77B0) 0 nil 1 nil SQLite3 cursor (005E9200) nil Id: 1, Name: Raj
我们可以使用此 libsql 库执行所有可用的查询。因此,请不要局限于这些示例。尝试在 Lua 中使用 MySQL、SQLite3 和其他受支持的数据库中可用的各种查询语句。
Lua - 游戏编程
Lua 因其简单的语言结构和语法而被广泛用于许多游戏引擎。垃圾收集功能在游戏中经常非常有用,因为游戏由于使用了丰富的图形而会消耗大量的内存。一些使用 Lua 的游戏引擎包括:
- Corona SDK
- Gideros Mobile
- ShiVa3D
- Moai SDK
- LOVE
- CryEngine
这些游戏引擎都是基于 Lua 的,并且每个引擎都提供丰富的 API。
Corona SDK
Corona SDK 是一个跨平台移动游戏引擎,支持 iPhone、iPad 和 Android 平台。Corona SDK 有一个免费版本,可用于具有有限功能的小型游戏。您可以在需要时升级到其他版本。
Corona SDK 提供了许多功能,包括:
- 物理和碰撞处理 API
- Web 和网络 API
- 游戏网络 API
- 广告 API
- 分析 API
- 数据库和文件系统 API
- 加密和数学 API
- 音频和媒体 API
使用上述 API 开发应用程序比分别为 iOS 和 Android 使用原生 API 更容易、更快。
Gideros Mobile
Gideros 提供跨平台 SDK,用于为 iOS 和 Android 创建游戏。它是免费使用的,带有“由 Gideros 制作”的启动画面。Gideros 的一些显著优势包括:
开发 IDE - 它提供自己的 IDE,使开发 Gideros 应用程序更容易。
即时测试 - 在开发游戏时,只需 1 秒钟即可通过 Wifi 在真实设备上进行测试。您无需浪费时间进行导出或部署过程。
插件 - 您可以轻松地使用插件扩展核心功能。导入现有的 (C、C++、Java 或 Obj-C) 代码,绑定到 Lua 并直接解释它们。已经有数十个开源插件可以立即使用。
简洁的面向对象方法 - Gideros 提供了自己的类系统,包含所有基本的面向对象标准,使您可以为将来的任何游戏编写简洁且可重用的代码。
原生速度 - 基于 C/C++ 和 OpenGL 构建,您的游戏以原生速度运行,并充分利用底层 CPU 和 GPU 的强大功能。
ShiVa3D
ShiVa3D 是一个 3D 游戏引擎,它提供了一个图形编辑器,用于为 Web、游戏机和移动设备创建应用程序和视频游戏。它支持多个平台,包括 Windows、Mac、Linux、iOS、Android、BlackBerry、Palm OS、Wii 和 WebOS。
一些主要功能包括:
- 标准插件
- 网格修改 API
- IDE
- 内置地形、海洋和动画编辑器
- ODE 物理引擎支持
- 完全的光照贴图控制
- 材质、粒子、轨迹和 HUD 的实时预览
- Collada 交换格式支持
Shiva3d 的 Web 版本是完全免费的,其他版本需要订阅。
Moai SDK
Moai SDK 是一个跨平台移动游戏引擎,支持 iPhone、iPad 和 Android 平台。Moai 平台最初由 Moai SDK(一个开源游戏引擎)和 Moai Cloud(一个用于托管和部署游戏服务的云平台即服务)组成。现在 Moai Cloud 已关闭,只有游戏引擎可用。
Moai SDK 运行在多个平台上,包括 iOS、Android、Chrome、Windows、Mac 和 Linux。
LOVE
LOVE 是一个可用于制作 2D 游戏的框架。它是免费且开源的。它支持 Windows、Mac OS X 和 Linux 平台。
它提供了多个功能,包括:
- 音频 API
- 文件系统 API
- 键盘和操纵杆 API
- 数学 API
- 窗口和鼠标API
- 物理API
- 系统和计时器API
CryEngine
CryEngine是由德国游戏开发商Crytek开发的游戏引擎。它已经从第一代发展到第四代,是一款先进的开发解决方案。它支持PC、Xbox 360、PlayStation 3和Wii U游戏。
它提供了多个功能,包括:
视觉效果,例如自然光照和动态软阴影、实时动态全局光照、光传播体积、粒子着色、细分等等。
角色动画系统和角色个性化系统。
参数化骨骼动画和独特的专用面部动画编辑器
AI系统,例如多层导航网格和战术点系统。还提供用户友好的AI编辑系统。
游戏内混音和分析,数据驱动的音效系统动态音效和互动音乐等等。
物理特性,例如程序变形和高级绳索物理。
结束语
这些游戏SDK/框架各有优缺点。在它们之间做出正确的选择会让你的任务更容易,你也能更好地完成它。因此,在使用它之前,你需要了解你游戏的要求,然后分析哪个满足你所有的需求,然后再使用它们。
Lua - 标准库
Lua标准库提供了一套丰富的函数,这些函数是直接用C API实现的,并且内置于Lua编程语言中。这些库提供Lua编程语言内部的服务,以及外部服务,如文件和数据库操作。
这些内置于官方C API的标准库作为单独的C模块提供。它包括以下内容:
- 基本库,包括协程子库
- 模块库
- 字符串操作
- 表操作
- 数学库
- 文件输入和输出
- 操作系统功能
- 调试功能
基本库
在本教程的各个主题中,我们都使用了基本库。下表提供了相关页面的链接,并列出了本Lua教程各个部分中涵盖的函数。
Lua提供以下类型的循环来处理循环需求。 | 库/方法和用途 |
---|---|
1 | 错误处理 包括错误处理函数,如assert、error,如Lua - 错误处理中所述。 |
2 | 内存管理 包括与垃圾收集相关的自动内存管理函数,如Lua - 垃圾收集中所述。 |
3 | dofile ([filename]) 它打开文件并执行文件内容作为代码块。如果没有传递参数,则此函数执行标准输入的内容。错误将传播到调用者。 |
4 | _G 这是一个全局变量,它保存全局环境(即,_G._G = _G)。Lua本身不使用此变量。 |
5 | getfenv ([f]) 返回函数正在使用的当前环境。f可以是Lua函数或一个数字,指定该栈级别上的函数——级别1是调用getfenv的函数。如果给定的函数不是Lua函数,或者f为0,则getfenv返回全局环境。f的默认值为1。 |
6 | getmetatable (object) 如果对象没有元表,则返回nil。否则,如果对象的元表具有“__metatable”字段,则返回关联的值。否则,返回给定对象的元表。 |
7 | ipairs (t) 此函数获取表的索引和值。 |
8 | load (func [, chunkname]) 使用函数func加载代码块以获取其片段。对func的每次调用都必须返回一个与先前结果连接的字符串。 |
9 | loadfile ([filename])) 类似于load,但从文件filename或标准输入(如果未给出文件名)获取代码块。 |
10 | loadstring (string [, chunkname]) 类似于load,但从给定的字符串获取代码块。 |
11 | next (table [, index]) 允许程序遍历表的全部字段。它的第一个参数是一个表,第二个参数是该表中的一个索引。next返回表的下一个索引及其关联的值。 |
12 | pairs (t) 挂起正在运行的协程。传递给此方法的参数充当 resume 函数的附加返回值。 |
13 | print (...) 挂起正在运行的协程。传递给此方法的参数充当 resume 函数的附加返回值。 |
14 | rawequal (v1, v2) 检查v1是否等于v2,不调用任何元方法。返回布尔值。 |
15 | rawget (table, index) 获取table[index]的真实值,不调用任何元方法。table必须是一个表;index可以是任何值。 |
16 | rawset (table, index, value) 将table[index]的真实值设置为value,不调用任何元方法。table必须是一个表,index为非nil的任何值,value为任何Lua值。此函数返回table。 |
17 | select (index, ...) 如果index是数字,则返回索引号index之后的全部参数。否则,index必须是字符串“#”,select返回它接收的额外参数的总数。 |
18 | setfenv (f, table) 设置要由给定函数使用的环境。f可以是Lua函数或一个数字,指定该栈级别上的函数——级别1是调用setfenv的函数。setfenv返回给定的函数。作为特例,当f为0时,setfenv更改运行线程的环境。在这种情况下,setfenv不返回值。 |
19 | setmetatable (table, metatable) 为给定表设置元表。(你不能从Lua更改其他类型的元表,只能从C更改。)如果metatable为nil,则删除给定表的元表。如果原始元表具有“__metatable”字段,则引发错误。此函数返回table。 |
20 | tonumber (e [, base]) 尝试将它的参数转换为数字。如果参数已经是数字或可转换为数字的字符串,则tonumber返回此数字;否则,它返回nil。 |
21 | tostring (e) 接收任何类型的参数并将其转换为合理格式的字符串。要完全控制数字的转换方式,请使用string.format。 |
22 | type (v) 返回其唯一参数的类型,编码为字符串。此函数的可能结果是“nil”(一个字符串,而不是值nil)、“number”、“string”、“boolean”、“table”、“function”、“thread”和“userdata”。 |
23 | unpack (list [, i [, j]]) 返回给定表中的元素。 |
24 | _VERSION 一个全局变量(不是函数),它保存一个包含当前解释器版本的字符串。此变量的当前内容是“Lua 5.1”。 |
25 | 协程 包括协程操作函数,如Lua - 协程中所述。 |
模块库
模块库提供在Lua中加载模块的基本函数。它直接在全局环境中导出一个函数:require。其他所有内容都在表package中导出。关于模块库的详细信息在前面章节Lua - 模块教程中进行了说明。
字符串操作
Lua提供了一套丰富的字符串操作函数。之前的Lua - 字符串教程详细介绍了这一点。
表操作
Lua几乎在其所有操作中都依赖于表。之前的Lua - 表教程详细介绍了这一点。
文件输入和输出
在编程中,我们经常需要数据存储功能,Lua中的标准库函数提供了文件I/O功能。这在之前的Lua - 文件I/O教程中进行了讨论。
调试功能
Lua提供了一个调试库,该库为我们提供了创建自己的调试器所需的所有基本函数。这在之前的Lua - 调试教程中进行了讨论。
Lua - 数学库
在科学和工程计算中,我们经常需要数学运算,我们可以使用标准Lua库math来实现这一点。math库中可用的函数列表如下表所示。
Lua提供以下类型的循环来处理循环需求。 | 库/方法和用途 |
---|---|
1 | math.abs (x) 返回x的绝对值。 |
2 | math.acos (x) 返回x的反余弦(以弧度为单位)。 |
3 | math.asin (x) 返回x的反正弦(以弧度为单位)。 |
4 | math.atan (x) 返回x的反正切(以弧度为单位)。 |
5 | math.atan2 (y, x) 返回y/x的反正切(以弧度为单位),但使用两个参数的符号来查找结果的象限。(它也正确处理x为零的情况。) |
6 | math.ceil (x) 返回大于或等于x的最小整数。 |
7 | math.cos (x) 返回x的余弦(假定为弧度)。 |
8 | math.cosh (x) 返回x的双曲余弦。 |
9 | math.deg (x) 返回以弧度表示的角度x(以度为单位)。 |
10 | math.exp (x) 返回e的x次幂。 |
11 | math.floor (x) 返回小于或等于x的最大整数。 |
12 | math.fmod (x, y) 返回x除以y的余数,将商四舍五入到零。 |
13 | math.frexp (x) 返回m和e,使得x = m2e,e是整数,m的绝对值在[0.5, 1)范围内(或当x为零时为零)。 |
14 | math.huge HUGE_VAL的值,大于或等于任何其他数值的值。 |
15 | math.ldexp (m, e) 返回m2e(e应为整数)。 |
16 | math.log (x) 返回x的自然对数。 |
17 | math.log10 (x) 返回x的以10为底的对数。 |
18 | math.max (x, ...) 返回其参数中的最大值。 |
19 | math.min (x, ...) 返回其参数中的最小值。 |
20 | math.modf (x) 返回两个数字,x的整数部分和x的小数部分。 |
21 | math.pi π的值。 |
22 | math.pow (x, y) 返回xy。(你也可以使用表达式x^y来计算此值。) |
23 | math.rad (x) 返回以度表示的角度x(以弧度为单位)。 |
24 | math.random ([m [, n]]) 此函数是ANSI C提供的简单伪随机生成器函数rand的接口。在不带参数的情况下调用时,返回范围[0,1)内的均匀伪随机实数。当用整数m调用时,math.random返回范围[1, m]内的均匀伪随机整数。当用两个整数m和n调用时,math.random返回范围[m, n]内的均匀伪随机整数。 |
25 | math.randomseed (x) 将x设置为伪随机生成器的“种子”:相同的种子产生相同的数字序列。 |
26 | math.sin (x) 返回x的正弦(假定为弧度)。 |
27 | math.sinh (x) 返回x的双曲正弦。 |
28 | math.sqrt (x) 返回x的平方根。(你也可以使用表达式x^0.5来计算此值。) |
29 | math.tan (x) 返回x的正切(假定为弧度)。 |
30 | math.tanh (x) 返回x的双曲正切。 |
三角函数
下面显示了一个使用三角函数的简单示例。
radianVal = math.rad(math.pi / 2) io.write(radianVal,"\n") -- Sin value of 90(math.pi / 2) degrees io.write(string.format("%.1f ", math.sin(radianVal)),"\n") -- Cos value of 90(math.pi / 2) degrees io.write(string.format("%.1f ", math.cos(radianVal)),"\n") -- Tan value of 90(math.pi / 2) degrees io.write(string.format("%.1f ", math.tan(radianVal)),"\n") -- Cosh value of 90(math.pi / 2) degrees io.write(string.format("%.1f ", math.cosh(radianVal)),"\n") -- Pi Value in degrees io.write(math.deg(math.pi),"\n")
运行上述程序后,我们将得到以下输出。
0.027415567780804 0.0 1.0 0.0 1.0 180
其他常用数学函数
下面显示了一个使用常用数学函数的简单示例。
-- Floor io.write("Floor of 10.5055 is ", math.floor(10.5055),"\n") -- Ceil io.write("Ceil of 10.5055 is ", math.ceil(10.5055),"\n") -- Square root io.write("Square root of 16 is ",math.sqrt(16),"\n") -- Power io.write("10 power 2 is ",math.pow(10,2),"\n") io.write("100 power 0.5 is ",math.pow(100,0.5),"\n") -- Absolute io.write("Absolute value of -10 is ",math.abs(-10),"\n") --Random math.randomseed(os.time()) io.write("Random number between 1 and 100 is ",math.random(),"\n") --Random between 1 to 100 io.write("Random number between 1 and 100 is ",math.random(1,100),"\n") --Max io.write("Maximum in the input array is ",math.max(1,100,101,99,999),"\n") --Min io.write("Minimum in the input array is ",math.min(1,100,101,99,999),"\n")
运行上述程序后,我们将得到以下输出。
Floor of 10.5055 is 10 Ceil of 10.5055 is 11 Square root of 16 is 4 10 power 2 is 100 100 power 0.5 is 10 Absolute value of -10 is 10 Random number between 1 and 100 is 0.22876674703207 Random number between 1 and 100 is 7 Maximum in the input array is 999 Minimum in the input array is 1
以上示例只是一些常用示例,我们可以根据需要使用math库,因此请尝试使用所有函数以更熟悉它们。
Lua - 操作系统功能
在任何应用程序中,通常都需要访问操作系统级函数,并且可以使用操作系统库来实现。可用的函数列表如下表所示。
Lua提供以下类型的循环来处理循环需求。 | 库/方法和用途 |
---|---|
1 | os.clock () 返回程序使用的CPU时间量(以秒为单位)的近似值。 |
2 | os.date ([format [, time]]) 返回一个包含日期和时间的字符串或表,其格式根据给定的字符串格式。 |
3 | os.difftime (t2, t1) 返回从时间t1到时间t2的秒数。在POSIX、Windows和一些其他系统中,此值正好是t2-t1。 |
4 | os.execute ([command]) 此函数等效于ANSI C函数system。它传递要由操作系统shell执行的命令。它的第一个结果是如果命令成功终止则为true,否则为nil。 |
5 | os.exit ([code [, close]]) 调用 ANSI C 函数 exit 来终止主机程序。如果 code 为真,则返回状态为 EXIT_SUCCESS;如果 code 为假,则返回状态为 EXIT_FAILURE;如果 code 为数字,则返回状态为此数字。 |
6 | os.getenv (varname) 返回进程环境变量 varname 的值,如果变量未定义则返回 nil。 |
7 | os.remove (filename) 删除具有给定名称的文件(或 POSIX 系统上的空目录)。如果此函数失败,它将返回 nil,加上描述错误的字符串和错误代码。 |
8 | os.rename (oldname, newname) 将名为 oldname 的文件或目录重命名为 newname。如果此函数失败,它将返回 nil,加上描述错误的字符串和错误代码。 |
9 | os.setlocale (locale [, category]) 设置程序的当前区域设置。locale 是一个系统相关的字符串,指定一个区域设置;category 是一个可选字符串,描述要更改的类别:“all”、“collate”、“ctype”、“monetary”、“numeric”或“time”;默认类别为“all”。该函数返回新区域设置的名称,如果请求无法满足则返回 nil。 |
10 | os.time ([table]) 在不带参数调用时返回当前时间,或表示由给定表指定的日期和时间的 time。此表必须具有 year、month 和 day 字段,并且可能具有 hour(默认为 12)、min(默认为 0)、sec(默认为 0)和 isdst(默认为 nil)字段。有关这些字段的描述,请参阅 os.date 函数。 |
11 | os.tmpname () 返回一个字符串,其中包含可用于临时文件的文件名。必须在使用临时文件之前显式打开它,并在不再需要时显式删除它。 |
常用操作系统函数
下面显示了一个使用常用数学函数的简单示例。
-- Date with format io.write("The date is ", os.date("%m/%d/%Y"),"\n") -- Date and time io.write("The date and time is ", os.date(),"\n") -- Time io.write("The OS time is ", os.time(),"\n") -- Wait for some time for i=1,1000000 do end -- Time since Lua started io.write("Lua started before ", os.clock(),"\n")
运行上述程序后,我们将得到类似于以下的输出。
The date is 01/25/2014 The date and time is 01/25/14 07:38:40 The OS time is 1390615720 Lua started before 0.013
以上示例只是一些常用的示例,我们可以根据需要使用 OS 库,因此请尝试使用所有函数以更熟悉它们。例如,remove 函数可以帮助删除文件,execute 函数可以帮助我们执行上述解释的操作系统命令。