Lua之参考
https://www.yiibai.com/lua/lua_basic_syntax.html
https://www.baiyu.tech/code/757
Lua注释
---- 注释
--这是 lua 的单行注释
--[[
这是 lua 的多行注释
第二行
第三行
--]]
--[[
这是 lua 的多行注释
第二行
第三行
]]
Lua交互模式(lua -i)
> io.write("Hello world, from ",_VERSION,"!\n")
Hello world, from Lua 5.4!
file (0x7fff889b2ee8)
> v=1
> print(type(v));
number
> print(type(vv));
nil
>
-- 20分配给变量g,30分配给变量l
> g,l = 20,30
-- type
> print(type("What is my type")) --> string
string
> t = 10
> print(type(5.8*t)) --> number
number
> print(type(true)) --> boolean
boolean
> print(type(print)) --> function
function
> print(type(nil)) --> nil
nil
> print(type(type(ABC))) --> string
string
Lua标识符
Lua标识符是用于标识变量,函数或任何其他用户定义项的名称。 标识符以字母A
到Z
或a
到z
或下划线_
开头,后跟零个或多个字母,下划线和数字(0
到9
)。
Lua不允许标识符中的标点符号,如@
,$
和%
。 Lua是一种区分大小写的编程语言。 因此,Yiibai
和yiibai
是Lua中的两个不同的标识符。 以下是可接受标识符的一些示例
mohd zara abc move_name a_123
myname50 _temp j a23b9 retVal
Lua关键字
以下列表显示了Lua中的一些保留字。 这些保留字不能用作常量或变量或任何其他标识符名称。
and break do else elseif end false for function if in
local nil not or repeat return then true until while
Lua变量
全局变量 - 所有变量都被视为全局变量,除非明确声明为局部变量。
局部变量 - 当为变量指定类型为
local
时,其范围受函数范围限制。表字段 - 这是一种特殊类型的变量,可以保存除
nil
之外的任何内容,包括函数。
Lua数据类型
编号 | 值类型 | 描述 |
---|---|---|
1 | nil |
用于区分值与某些数据或没有(nil )数据。 |
2 | boolean |
包括true 和false 作为值,通常用于条件检查。 |
3 | number |
表示实数(双精度浮点)数字。 |
4 | string |
表示字符数组。 |
5 | function |
表示用C语言或Lua编写的方法。 |
6 | userdata |
表示任意C语言数据。 |
7 | thread |
表示独立的执行线程,它用于实现协同程序。 |
8 | table |
表示普通数组,符号表,集合,记录,图形,树等,并实现关联数组。 它可以保存任何值(除了nil )。 |
Lua运算符
- 算术运算符
- 关系运算符
- 逻辑运算符
- 其它运算符
算术运算符
运算符 | 描述 | 示例 |
---|---|---|
+ |
相加两个操作数 | A + B = 30 |
- |
从第一个减去第二个操作数 | A - B = -10 |
* |
将两个操作数相乘 | A * B = 200 |
/ |
用除分子除以分子 | B / A = 2 |
% |
模数运算符,整数除法后的余数 | B % A = 0 |
^ |
指数运算符取指数幂值 | A^2 = 100 |
- |
一元,充当否定 | -A = -10 |
关系运算符
运算符 | 描述 | 示例 |
---|---|---|
== |
检查两个操作数的值是否相等,如果相等,则条件变为真。 |
(A == B) 结果为false
|
~= |
检查两个操作数的值是否相等,如果值不相等则条件变为true 。 |
(A ~= B) 结果为true
|
> |
检查左操作数的值是否大于右操作数的值,如果是,则条件变为true 。 |
(A > B) 结果为false
|
< |
检查左操作数的值是否小于右操作数的值,如果是,则条件变为true 。 |
(A < B) 结果为true
|
>= |
检查左操作数的值是否大于或等于右操作数的值,如果是,则条件变为true 。 |
(A >= B) 结果为false
|
<= |
检查左操作数的值是否小于或等于右操作数的值,如果是,则条件变为true 。 |
(A <= B) 结果为true
|
逻辑运算符
运算符 | 描述 | 示例 |
---|---|---|
and |
逻辑与运算符。如果两个操作数都不为零,则条件成立。 |
(A and B) 结果为false
|
or |
逻辑或运算符。 如果两个操作数中的任何一个不为零,则条件变为真。 |
(A or B) 结果为true
|
not |
逻辑非运算符。用于反转其操作数的逻辑状态。 如果条件为真,则逻辑非运算符将为false 。 |
!(A and B) 结果为true
|
其它运算符
编号 | 描述 | 示例 |
---|---|---|
.. |
连接两个字符串 | 如果a 为Hello ,b 为World ,a..b 将返回Hello World 。 |
# |
返回字符串或表长度的一元运算符。 |
#"Hello" 将返回 5
|
Lua运算符优先级
具有最高优先级的运算符显示在表的顶部,具有最低优先级的运算符显示在底部。 在表达式中,将首先评估更高优先级的运算符。
类别 | 操作符 | 关联性 |
---|---|---|
一元 |
not # -
|
右到左 |
连接 | .. |
右到左 |
乘法 |
* / %
|
左到右 |
加法 |
+ -
|
左到右 |
关系 |
< > <= >= == ~=
|
左到右 |
相等 |
== ~=
|
左到右 |
逻辑与 | and |
左到右 |
逻辑或 | or |
左到右 |
Lua循环
Lua提供以下类型的循环来处理循环需求。
编号 | 循环类型 | 描述 | |
---|---|---|---|
1 | while循环 | 在给定条件为真时重复语句或语句组,它在执行循环体之前测试条件。 | while(condition) <br />do <br /> statement(s) <br />end |
2 | for循环 | 多次执行一系列语句,并缩写管理循环变量的代码。 | for init,max/min value, increment <br />do <br /> statement(s) <br />end |
3 | repeat…unti循环 | 重复语句组的操作,直到满足until 条件。 |
repeat <br /> statement(s) <br />until( condition ) |
4 | 嵌套循环 | 可在任何循环中使用另一个或多个循环,如:while ,for 或do..while 循环。 |
循环控制声明
循环控制语句从正常顺序改变执行。 当执行离开作用域时,将销毁在该作用域中创建的所有自动对象。
Lua支持以下控制语句。
编号 | 控制语句 | 描述 |
---|---|---|
1 | break语句 | 终止循环并将执行转移到循环或switch之后的语句。 |
无限循环
如果条件永远不会变为假,则循环变为无限循环。 while
循环通常用于此目的。如果直接给出了一个条件为真,它就会永远执行。可以使用break
语句来打断这个无限循环。
while( true )
do
print("This loop will run forever.")
end
Lua决策结构
Lua编程语言假定布尔true
和non-nil
值的任意组合为true
,如果它是布尔false
或nil
,则假定为false
值。 需要注意的是,在Lua中,零将被视为true
。
Lua编程语言提供以下类型的决策制定语句 -
编号 | 决策语句 | 描述 |
---|---|---|
1 | if语句 |
if 语句由布尔表达式后跟一个或多个语句组成。 |
2 | if…else语句 |
if 语句后面可以跟一个可选的else 语句,该语句在布尔表达式为false 时执行。 |
3 | 嵌套if语句 | 在一个if 或else if 语句中使用另一个if 或else if 语句。 |
Lua函数
Lua编程语言中方法定义的一般形式如下 -
optional_function_scope function function_name( argument1, argument2, argument3........, argumentn)
function_body
return result_params_comma_separated
end
Lua编程语言中的方法定义由方法头和方法体组成。以下是方法的所有部分 -
-
可选函数范围 - 使用关键字
local
来限制函数的范围,或者忽略scope
部分,这会变成一个全局函数。 - 函数名称 - 这是函数的实际名称。 函数名称和参数列表一起构成函数签名。
- 参数 - 参数就像一个占位符。 调用函数时,将值传递给参数。 该值称为实际参数或参数。 参数列表指的是方法的参数的类型,顺序和数量。 参数是可选的; 也就是说,方法的参数可有可无。
- 函数主体 - 方法体包含一组语句,用于定义方法的作用。
-
返回 - 在Lua中,可通过使用
return
关键字,后加逗号分隔返回值列表来返回多个值。
Lua字符串
字符串是由一系列字符以及控制字符组成,如换页符。 字符串可以用三种形式初始化,包括 -
- 单引号之间的字符
- 双引号之间的字符
-
[[
和]]
之间的字符
string1 = "Lua"
print("\"String 1 is\"",string1)
string2 = 'Yiibai Tutorial'
print("String 2 is",string2)
string3 = [["Lua Tutorial"]]
print("String 3 is",string3)
字符串中使用转义序列字符来更改字符的正常解释。例如,要打印双引号(""
),在上面的示例中使用了"
。转义序列及其用法列在下表中。
转义序列 | 用法 |
---|---|
\a |
铃 |
\b |
退格 |
\f |
换页 |
\n |
新行 |
\r |
回车 |
\t |
制表符 |
\v |
垂直制表符 |
\\ |
反斜杠 |
" |
双引号 |
' |
单引号 |
\[ |
左方括号 |
\] |
右方括号 |
字符串操作
Lua支持字符串来操作字符串 -
编号 | 方法 | 作用 |
---|---|---|
1 | string.upper(argument) |
返回参数的大写表示。 |
2 | string.lower(argument) |
返回参数的小写表示。 |
3 | string.gsub(mainString,findString,replaceString) |
返回用replaceString 替换findString 后字符串。 |
4 | string.find(mainString,findString, optionalStartIndex,optionalEndIndex) |
返回mainString 中findString 的起始索引和结束索引找到字符串,如果未找到则返回nil 。 |
5 | string.reverse(arg) |
将传递的字符串arg 反转后的字符串。 |
6 | string.format(...) |
返回格式化字符串。 |
7 |
string.char(arg) 和 string.byte(arg)
|
返回输入参数的内部数字和字符表示。 |
8 | string.len(arg) |
返回传递字符串arg 的长度。 |
9 | string.rep(string, n)) |
通过重复相同的字符串n 次返回一个字符串。 |
10 | .. |
此运算符连接两个字符串。 |
Lua数组
数组是对象的有序排列,可以是包含行集合的一维数组或包含多个行和列的多维数组。
在Lua中,使用带整数的索引表实现数组。 数组的大小不固定,它可以根据要求增长,受内存限制。
一维数组
array = {"Lua", "Tutorial"}
for i = 0, 2 do
print(array[i])
end
多维数组
-- 初始化数组
array = {}
for i=1,3 do
array[i] = {}
for j=1,3 do
array[i][j] = i*j
end
end
-- 访问数组
for i=1,3 do
for j=1,3 do
print(array[i][j])
end
end
Lua迭代器
迭代器泛型
迭代器泛型提供集合中每个元素的键值对。 下面给出一个简单的例子。
array = {"Lua", "Tutorial"}
for key,value in ipairs(array)
do
print(key, value)
end
上面的例子使用了Lua提供的默认ipairs
迭代器函数。
在Lua中,使用函数来表示迭代器。 基于这些迭代器函数中的状态维护,有两种主要类型 -
- 无状态迭代器
- 有状态迭代器
无状态迭代器
通过名称,可以理解这种类型的迭代器函数不会保留任何状态。下面来看一个使用打印n
个数字的函数square
创建迭代器的示例。
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
稍微修改上面的代码,以模仿迭代器的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
有状态迭代器
上面使用函数的迭代示例不保留状态。 每次调用该函数时,它都会根据发送给函数的第二个变量返回集合的下一个元素。 要保持当前元素的状态,可使用闭包(Closure
)。 闭包在函数调用中保留变量值。 要创建一个新的闭包,这里首先创建两个函数,包括闭包本身和一个工厂,即创建闭包的函数。
现在来看看一个创建迭代器的例子,并将使用闭包。
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
来通过每次调用函数时递增索引来返回集合中的每个元素。
可使用闭包创建函数迭代器,如上所示,它可以为迭代整个集合的每个时间返回多个元素。
Lua表
表是Lua中唯一可用的数据结构,使用表可以创建不同的类型,如数组和字典。 Lua使用关联数组,不仅可使用数字编制索引,还可以使用除nil
之外的字符串编制索引。 表没有固定的大小,可以根据需要增长大小。
Lua在所有表示中使用表,包括包的表示。 当访问方法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
都指向相同的内存。 Lua不会单独为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("mytable 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
mytable 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
表操作
下面是用于表操作的内置函数,它们列在下表格中。
编号 | 方法 | 作用 |
---|---|---|
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]) |
根据可选的比较器参数对表进行排序。 |
下面来看看一些上述功能的示例。
1. 表连接
使用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
2. 插入和删除
在表操作中,最常见的是在表中插入和删除项目。如下解释。
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("Fruit at index 2 is ",table.concat(fruits,", ") )
-- banana, orange, apple, mango --> banana, grapes, orange, apple, mango
print("The maximum elements in table is",#fruits)
--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
3. 排序表
有时想要按特定顺序对表进行排序。 排序函数按字母顺序对表中的元素进行排序。 这方面的示例如下所示。
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
现在,为了在另一个文件(例如,moduletutorial.lua
)中访问此Lua模块,需要使用以下代码段。
package.path = '/Users/fei/work/lua_work/learn/mymath.lua'
mymathmodule = require("mymath")
mymathmodule.add(10,20)
mymathmodule.sub(30,20)
mymathmodule.mul(10,20)
mymathmodule.div(30,20)
要运行此代码,需要将两个Lua文件放在同一目录中,或者,可以将模块文件放在包路径(package.path
)中,它需要额外的设置。 当运行上面的程序时,将得到以下输出 -
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元表
元表(metatable
)是一个表,它是使用键集和相关元方法来修改附加到的表的行为。 这些元方法是强大的Lua功能,可实现如下功能 -
- 在表上更改/添加功能到操作符。
- 使用元表中的
__index
在表中没有键时查找元表。
在处理元表时有两种重要的方法,包括 -
-
setmetatable(table,metatable)
- 此方法用于为表设置元表。 -
getmetatable(table)
- 此方法用于获取表的元表。
首先来看看如何将一个表设置为另一个表的元表。 如下所示 -
mytable = {}
mymetatable = {}
setmetatable(mytable,mymetatable)
getmetatable(mytable)
上面的代码可以用一行表示,如下所示 -
mytable = setmetatable({},{})
getmetatable(mytable)
部分元方法介绍
__index -- 'table[key]',取下标操作,用于访问表中的域
__newindex -- 'table[key] = value',赋值操作,增改表中的域
__call -- 'func(args)',函数调用,(参见 《Lua 学习笔记(三)—— 表达式》中的函数部分介绍)
-- 数学运算操作符
__add -- '+'
__sub -- '-'
__mul -- '*'
__div -- '/'
__mod -- '%'
__pow -- '^'
__unm -- '-'
-- 连接操作符
__concat -- '..'
-- 取长操作符
__len -- '#'
-- 比较操作符
__eq -- '=='
__lt -- '<' -- a > b 等价于 b < a
__le -- '<=' -- a >= b 等价于 b <= a
## 参考: https://blog.csdn.net/yangzhenzhen/article/details/51233548
__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
添加到metatable
时,如果表中没有键,则新键的行为将由元方法定义。 下面给出了当主表中没有索引时设置metatable
索引的简单示例。
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
在上面的程序中看到,如果主表中存在一个键,它只会更新它。 当维护中的键不可用时,它会将该键添加到metatable
中。
使用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, (#newtable) do
table.insert(mytable, (#mytable)+1,newtable[i])
end
return mytable
end
})
secondtable = {4,5,6}
mytable = mytable + secondtable
print(table.concat(mytable," , "))
for k,v in ipairs(mytable) do
print(k,v)
end
当运行上面的程序时,将得到以下输出 -
1 , 2 , 3 , 4 , 5 , 6
1 1
2 2
3 3
4 4
5 5
6 6
__add
键包含在元表中以添加运算符 +
的行为。键表和相应的操作符如下所示。
编号 | 模式 | 描述 |
---|---|---|
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, (#mytable) do
sum = sum + mytable[i]
end
for i = 1, (#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
Shell
如果完全了解元表的功能,那么可以真正执行很多非常复杂的操作。 因此,尝试使用元表中可用的不同选项的元表更多地工作。
Lua协同程序 ??
协同程序本质上是协作的,它允许两种或多种方法以受控方式执行。 使用协同程序,在任何给定时间,只有一个协同程序运行,并且此运行协程仅在显式请求暂停时暂停执行。
上述定义可能看起来含糊不清。 假设有两种方法,<br />一种是主程序方法,另一种是协程。 当使用resume
函数调用一个协程时,它会开始执行,当调用yield
函数时,它会暂停执行。<br /> 同样的协同程序可以继续执行另一个恢复函数调用,协同程序就会暂停。 该过程可以持续到协程执行结束。
协同程序函数
下表列出了Lua中协程的所有可用函数及其相应的用法。
编号 | 方法 | 作用或目的 |
---|---|---|
1 | coroutine.create (f) |
使用函数f 创建一个新的协同程序,并返回thread 类型的对象。 |
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
Shell
上面的例子是实现什么功能?
如前所述,使用resume
函数来启动操作和yield
函数来停止操作。 此外,可以看到coroutine
的恢复功能接收到多个返回值。
- 首先,创建一个协同程序并分配给变量名称
co
,协同程序将两个变量作为参数。 - 当调用第一个恢复函数时,值
3
和2
保留在临时变量value1
和value2
中,直到协程结束。 - 使用了一个变量
tempvar3
,它最初值是10
,并且通过后续的协程调用更新为13
和16
,在整个协程的执行过程中value1
的值保持为3
。 - 第一个
coroutine.yield
将两个值4
和3
返回到resume
函数,通过更新yield
语句中的输入参数为3
和2
。 它还接收协程执行的true/false
状态。 - 关于协同程序的另一个问题是,在上面的例子中如何处理下一个
resume
调用的句子; 可以看到变量coroutine.yield
接收下一个调用参数,它提供了一种强大的方法,可以通过保留现有的参数值来进行新的操作。 - 最后,当协程中的所有语句都执行后,后续调用将返回
false
并且响应语句为:cannot resume dead coroutine。
另一个协同程序示例
下面来看一个简单的协同程序示例,它使用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
Lua
当运行上面的程序时,将得到以下输出。
1 1
2 2
3 3
4 4
5 5
6 1
7 2
8 3
9 4
10 5
通常会将协同程序与多路程序设计语言的线程进行比较,但需要了解协同程序具有类似的线程功能,但协同程序一次只执行一个程序,并且永远不会同时执行。
通过暂时保留某些信息来控制程序执行顺序以满足需求。 使用带有协同程序的全局变量为协同程序提供了更大的灵活性。
Lua文件操作
I/O库用于读取和操作Lua中的文件。 Lua中有两种文件操作,即隐式文件描述符和显式文件描述符。
对于以下示例,将使用示例文件 - test.lua,内容如下所示 -
-- sample test.lua
-- sample2 test.lua
文件打开操作使用以下语句 -
file = io.open (filename [, mode])
下表列出了各种文件模式 -
编号 | 模式 | 描述 |
---|---|---|
1 | r |
只读模式,是打开现有文件的默认模式。 |
2 | w |
写入启用模式,覆盖现有文件或创建新文件。 |
3 | a |
附加模式,用于打开现有文件或创建新文件以进行追加。 |
4 | r+ |
现有文件的读写模式。 |
5 | w+ |
如果文件存在或创建具有读写权限的新文件,则删除所有现有数据。 |
6 | a+ |
启用了读取模式的追加模式可打开现有文件或创建新文件。 |
1. 隐式文件描述符
隐式文件描述符使用标准输入/输出模式或使用单个输入和单个输出文件。 使用隐式文件描述符的示例如下所示。
-- 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\n")
-- 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()
方法。可选参数可以是以面中的任何一个。
编号 | 模式 | 描述 |
---|---|---|
1 | *n |
从当前文件位置读取并返回一个数字(如果存在于文件位置或返回nil )。 |
2 | *a |
从当前文件位置返回文件的所有内容。 |
3 | *l |
从当前文件位置读取行,并将文件位置移动到下一行。 |
4 | number |
读取函数中指定的字节数。 |
其他常见的I/O方法包括,
-
io.tmpfile()
- 返回一个临时文件,用于读取和写入,当程序退出,将删除该文件。 -
io.type(file)
- 根据输入文件返回文件,关闭文件或nil
。 -
io.flush()
- 清除默认输出缓冲区。 -
io.lines(可选文件名)
- 提供循环迭代器的通用循环迭代器,循环遍历文件并最终关闭文件,以防提供文件名或使用默认文件而不在循环结束时关闭。
2. 显式文件描述符
显式文件描述符经常使用,它允许一次操作多个文件。 这些函数与隐式文件描述符非常相似。 在这里,使用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(optional whence, optional offset)
- 参数是set
,cur
或end
。 使用文件开头的更新文件位置设置新文件指针。 在此函数中,偏移量为零。 如果第一个参数是set
,则从文件的开头测量偏移量; 从文件中的当前位置开始,如果它是cur
; 或者从文件的末尾开始,则它是end
。 默认参数值为cur
和0
,因此可以通过不带参数调用此函数来获取当前文件位置。 -
file:flush()
− 清除默认输出缓冲区。 -
io.lines(optional file name)
- 提供循环迭代器的通用循环迭代器,循环遍历文件并最终关闭文件,以防提供文件名或使用默认文件而不在循环结束时关闭。
使用搜索方法的示例如下所示。它将光标从文件结束前的25
个位置偏移。 read
函数从搜索位置打印文件的剩余部分。
-- 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服务调用。
在任何编程中,总是需要错误处理。 错误可以是两种类型,它们包括 -
- 语法错误
- 运行时错误
1. 语法错误
由于不正确地使用各种程序组件(如运算符和表达式),从而发生语法错误。 语法错误的一个简单示例如下所示-
a == 2
使用单个“单等于号”和“双等于号”之间存在差异。 使用不当可能导致错误。 一个“等于”指的是赋值,而两个“等于”指的是比较。
语法错误的另一个示例如下所示 -
for a= 1,10
print(a)
end
当运行上述程序时,将获得以下输出 -
lua: test2.lua:2: 'do' expected near 'print'
语法错误比运行时错误更容易处理,因为Lua解释器比运行时错误更清楚地定位错误。 从上面的错误中,可以很容易地知道根据Lua结构在print
语句之前添加do
语句。
2. 运行时错误
如果出现运行时错误,程序将成功执行,但由于输入错误或错误处理的函数,可能会导致运行时错误。 显示运行时错误的简单示例如下所示。
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
。 一个简单的例子如下所示。
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])
终止最后一个调用的受保护函数,并将消息作为错误消息返回。 此函数错误永远不会返回。 通常,错误会在消息开头添加有关错误位置的一些信息。 level
参数指定如何获取错误位置。 对于级别1
(默认值),错误位置是调用错误函数的位置。 级别2
将错误指向调用调用错误的函数的位置等等。 传递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调试库中可用的功能及其用法。
编号 | 方法 | 描述 |
---|---|---|
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 的函数;等等。 如果function 是一个大于活动函数数的数字,则getinfo 返回nil 。 |
5 | getlocal(optional thread, stack level, local index) |
|
6 | getmetatable(value) |
返回给定对象的元表,如果没有元表,则返回nil 。 |
7 | getregistry() |
返回注册表表,这是一个预定义的表,任何C语言代码都可以使用它来存储它需要存储的任何Lua值。 |
8 | getupvalue(function, upvalue index) |
此函数返回upvalue 的名称和值,索引为函数func 。 如果给定索引没有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) |
将给定函数设置为钩子。 字符串掩码和数字计数描述了何时调用挂钩。 这里,每次Lua调用,返回并分别输入函数中的每一行代码时,都会调用c ,r 和l 。 |
11 | setlocal(optional thread, stack level, local index, value) |
使用堆栈级别的函数的索引local 将值赋给局部变量。 如果没有具有给定索引的局部变量,则该函数返回nil ,并且当使用超出范围的级别调用时引发错误。 否则,它返回局部变量的名称。 |
12 | setmetatable(value, metatable) |
将给定对象的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中,调试类型主要有两种 -
- 命令行调试
- 图形化调试
1. 命令行调试
命令行调试是使用命令和打印语句在命令行进行调试的调试类型。 Lua有许多命令行调试器,下面列出了一些。
-
RemDebug - RemDebug是
Lua 5.0
和Lua 5.1
的远程调试器。 它允许远程控制另一个Lua程序的执行,设置断点并检查程序的当前状态。 RemDebug 还可以调试CGILua脚本。 - clidebugger - 它是Lua 5.1的简单命令行界面调试器,用纯Lua编写。 除了标准的Lua 5.1库之外,它不依赖于任何其他东西。 它的灵感来自RemDebug,但没有远程工具。
- ctrace - 用于跟踪Lua API调用的工具。
- xdbLua - 用于Windows平台的简单Lua命令行调试程序。
- LuaInterface Debugger - 它是LuaInterface的调试器扩展。 它将内置的Lua调试接口提升到更高的级别。与调试器的交互由事件和方法调用完成。
- Rldb - 它是一个通过套接字的远程Lua调试器,可在Windows和Linux上使用。 它可以为您提供比现有功能更多的功能。
- ModDebug - 这允许远程控制另一个Lua程序的执行,设置断点,并检查程序的当前状态。
2. 图形调试
在IDE下可以进行图形调试,在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使用自动内存管理,该管理使用基于Lua内置的某些算法的垃圾收集。自动内存管理收集,作为开发人员 -
- 无需担心为对象分配内存。
- 除非将其设置为零,否则无需在不再需要时释放它们。
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的自动内存管理功能,此结果会有所不同。
22.599609375
20.79296875
0
21.5537109375
在上面的程序中看到,完成垃圾收集,它就减少了使用的内存。 但是,这并不是强制要求。 即使不调用它们,它也会在预定义的时间段后由Lua解释器在稍后阶段自动执行。
显然,如果需要,可以使用这些函数来改变垃圾收集器的行为。 这些函数为开发人员提供了一些额外的功能来处理复杂的情况。 根据程序所需的内存类型,也可能不会使用此功能。 但了解应用程序中的内存使用情况并在编程期间检查它以避免在部署后出现意外结果非常有用。
Lua面向对象编程
面向对象编程(OOP)是现代编程中使用最多的编程技术之一。 有许多支持OOP的编程语言包括 -
- C++
- Java
- Objective-C
- Smalltalk
- C#
- Ruby
面向对象的特点
- 类 - 类是用于创建对象的可扩展模板,为状态(成员变量)和行为实现提供初始值。
- 对象 - 它是类的一个实例,并为自己分配了单独的内存。
- 继承 - 这是一个概念,通过该概念,一个类的变量和函数由另一个类继承。
- 封装 - 这是在类中组合数据和函数的过程。借助函数可以在类的外部访问数据。 它也被称为数据抽象。
Lua面向对象
可使用Lua的表和第一类函数在Lua中实现面向对象。 通过将函数和相关数据放入表中,形成对象。可使用元表实现继承,为父对象中不存在的函数(方法)和字段提供查找机制。
Lua中的表具有状态和标识等对象的特征,与其值无关。 具有相同值的两个对象(表)是不同的对象,而对象在不同的时间可以具有不同的值,但它始终是相同的对象。 与对象一样,表的生命周期与谁创建它们或创建位置无关。
真实世界的例子
面向对象的概念广泛使用,但需要清楚地理解面向对象以获得适当和最大的好处。
考虑一个简单的数学例子。 我们经常遇到处理不同形状的情况,如圆形,矩形和方形。
形状可以具有共同的属性area
。 因此,使用公共属性区域从基础对象形状扩展其他形状。 每个形状都可以有自己的属性,像矩形这样的函数可以具有属性length
,width
,area
作为属性,printArea
和calculateArea
作为它的函数。
创建一个简单的类
下面显示了具有三个属性length
,width
和area
的矩形的简单类实现。 它还有一个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 = Rectangle:new(nil,10,20)
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继承
继承是将简单的基础对象(如形状)扩展为矩形,正方形等的过程。 它经常在现实世界中用于共享和扩展基本属性和功能。
下面来看一个简单的类扩展。有一个如下所示的类,
-- 元类
Shape = {area = 0}
-- 基类方法
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
-- 基类方法 - 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
继承完整示例
在另一个new
方法的帮助下,使用metatables
,扩展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
。并在派生类中覆盖基类的函数。 在此示例中,派生类会覆盖函数printArea
。
Lua Web编程
Lua是一种高度灵活的语言,通常用于多种平台,包括Web应用程序。 开普勒(Kepler)社区成立于2004年,旨在为Lua提供开源Web组件。
尽管已经有很多Lua Web开发是使用Lua的Web框架,但这里主要介绍和使用Kepler社区提供的组件。
Lua常用应用程序和框架
- Orbit - Orbit是Lua的MVC Web框架,基于WSAPI。
- WSAPI - WSAPI是从Lua Web应用程序抽象Web主机服务器的API,是许多项目的基础。
- Xavante - 是一个提供WSAPI接口的Lua Web服务器。
- Sputnik - 是一个wiki/CMS,通过用于幽默和娱乐的Kepler项目的WSAPI开发。
- CGILua - 基于WSAPI提供LuaPages和LuaScripts网页创建,但不再受支持。 请改用Orbit,Sputnik或WSAPI。
在本教程中,将让您了解Lua可以执行的操作,并了解有关其安装和使用的更多信息,请参阅kepler网站。
Orbit
Orbit是Lua的MVC Web框架。 它完全放弃了CGILua“脚本”模型,转而支持应用程序,每个Orbit应用程序都可以放在一个文件中,但如果需要,可以将它分成多个文件。
所有Orbit应用程序都遵循WSAPI协议,因此它们目前与Xavante,CGI和Fastcgi一起使用。 它包含一个启动程序,可以轻松启动Xavante实例进行开发。
安装Orbit的最简单方法是使用LuaRocks
。 Luarocks是安装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
在配置文件的<Directory /var/www/>
部分下面添加以下部分。 如果此部分有AllowOverride None
,则需要将None
更改为All
,以便.htaccess
文件可以在本地覆盖配置。
<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浏览器了。 转到http://localhost: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 - by yiibai.com !</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是一个Lua HTTP 1.1 Web服务器,它使用基于URI映射处理程序的模块化体系结构。 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.yiibai.com"] = simplerules
}
}
Lua的Web组件
- Copas - 一个基于协同程序的调度程序,可以由TCP/IP服务器使用。
- Cosmo - 一个“安全模板”引擎,可以保护应用程序免受模板中任意代码的攻击。
- Coxpcall - 它将Lua native pcall和xpcall与coroutine兼容。
- LuaFileSystem - 一种访问底层目录结构和文件属性的可移植方式。
- Rings - 一个程序库,提供从Lua内部创建新的Lua状态的方法。
结束说明
有许多基于Lua的Web框架和组件可供使用,根据需要选择它。 还有其他可用的Web框架,包括以下内容 -
- Moonstalk可以高效地开发和托管使用Lua语言构建的动态生成的基于Web的项目; 从基本页面到复杂的应用程序。
- Lapis,这是一个使用MoonScript(或Lua)构建Web应用程序的框架,它在一个名为OpenResty的Nginx定制版本中运行。
- Lua Server Pages 是一个Lua脚本引擎插件,它打破了嵌入式Web开发的任何其他方法,为传统的C服务器页面提供了显着的捷径。
这些Web框架可在Web应用程序中充分利用,以执行强大的操作功能。
Lua MySQL操作
对于简单的数据操作,可使用文件处理。但有时,这些文件操作可能效率不高,可扩展且功能强大。 为此经常需要使用数据库。 LuaSQL是一个从Lua到许多数据库管理系统的简单接口。 LuaSQL是一个库,它为不同类型的SQL提供支持。 包括 -
- SQLite
- Mysql
- ODBC
在本教程中,将介绍在Lua中如何使用MySQL和SQLite数据库操作处理。它为两者使用通用接口,并且也可以将此实现移植到其他类型的数据库。 首先来看看如何在MySQL中进行操作。
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文件,并与新创建的文件建立连接。
执行函数
连接提供了一个简单的执行函数,可以帮助我们执行创建,插入,删除,更新等操作完成所有数据库操作。 语法如下所示 -
conn:execute([[ 'MySQLSTATEMENT' ]])
在上面的语法中,需要确保conn
是打开的,以及MySQL连接成功,并用正确的SQL语句替换'MySQLSTATEMENT'
。
创建表示例
下面显示了一个简单的create table
示例。 它创建一个表,其中包含两个字段参数: id
类型为integer
,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的insert
语句如下所示-
conn:execute([[INSERT INTO sample values('11','Maxsu')]])
更新语句示例
MySQL的更新语句如下所示 -
conn:execute([[UPDATE sample3 SET name='Yiibai' where id ='12']])
删除语句示例
MySQL的删除语句如下所示 -
conn:execute([[DELETE from sample3 where id ='12']])
选择语句示例
就select
语句而言,我们需要循环遍历每一行并提取所需的数据。一个简单的选择语句如下所示 -
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','Maxsu')]])
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: Maxsu
执行事务
事务是一种确保数据一致性的机制。事务具有以下四个属性 -
- 原子性 - 事务完成或根本没有任何事情发生。
- 一致性 - 事务必须以一致状态启动,并使系统保持一致状态。
- 隔离性 - 在当前事务之外不可见事务的中间结果。
- 持久性 - 提交事务后,即使系统出现故障,影响也会持续存在。
事务以START TRANSACTION
开始; 并以commit
或rollback
语句结束。
开始事务
要启动事务,需要在Lua中执行以下语句,假设conn
是一个打开的MySQL连接。
conn:execute([[START TRANSACTION;]])
回滚事务
需要执行以下语句来回滚执行启动事务后所做的更改。
conn:execute([[ROLLBACK;]])
提交事务
需要执行以下语句来提交启动事务执行后所做的更改。
conn:execute([[COMMIT;]])
在上面和后面的章节中已经了解了MySQL的基本SQL操作。 记住事务,虽然这里没有为SQLite3解释,但同样的语句也适用于SQLite3。
Lua游戏编程
Lua由于其简单的语言结构和语法,它广泛地用于游戏引擎开发中。 垃圾收集功能通常在由于使用丰富的图形而消耗大量内存的游戏中非常有用。 一些使用Lua的游戏引擎包括 -
- Corona SDK
- Gideros Mobile
- ShiVa3D
- Moai SDK
- LOVE
- CryEngine
这些游戏引擎中的每一个都基于Lua,并且每个引擎中都有一组丰富的API。 接下来将简要介绍每种引擎功能。
1. Corona SDK
Corona SDK是一款支持iPhone,iPad和Android平台的跨平台移动游戏引擎。 有一个免费版本的Corona SDK,可用于功能有限的小游戏。可以在需要时升级到其他版本。
Corona SDK提供了许多功能,包括以下功能 -
- 物理和碰撞处理API
- Web和网络API
- 游戏网络API
- 广告API
- 分析API
- 数据库和文件系统API
- 加密和数学API
- 音频和媒体API
使用上述API开发应用程序比使用iOS和Android单独使用本机API更容易,更快捷。
2. Gideros Mobile
Gideros提供跨平台SDK来为iOS和Android创建游戏,可以免费使用Gideros。 Gideoros的一些显着优势包括 -
- 开发IDE - 它提供了自己的IDE,可以更轻松地开发Gideros应用程序。
-
即时测试 - 在开发游戏时,只需
1
秒即可通过Wifi在真实设备上进行测试。无需在导出或部署过程中浪费时间。 - 插件 - 使用插件轻松扩展核心。 导入现有的(C,C++,Java或Obj-C)代码,绑定到Lua并直接解释它们。已经开发了许多开源插件并且可以随时使用。
- 干净OOP方法 - Gideros提供自己的类系统,包含所有基本的OOP标准,使您能够为未来的任何游戏编写干净且可重复使用的代码。
- 原生速度 - 基于C/C++和OpenGL开发,游戏以原生速度运行,并充分利用下面的CPU和GPU的强大功能。
3. ShiVa3D
ShiVa3D是3D游戏引擎之一,它提供了一个图形编辑器,旨在为Web,控制台和移动设备创建应用程序和视频游戏。 它支持多种平台,包括Windows,Mac,Linux,iOS,Android,BlackBerry,Palm OS,Wii和WebOS。
一些主要功能包括 -
- 标准插件
- 网格修改API
- IDE
- 内置地形,海洋和动画编辑器
- ODE物理引擎支持
- 完整的光照贴图控件
- 实时预览材料,粒子,轨迹和HUD
- Collada交换格式支持
Shiva3d的网络版是完全免费的,其他版本可能需要收费。
4. Moai SDK
Moai SDK是一款支持iPhone,iPad和Android平台的跨平台移动游戏引擎。 Moai平台最初由Moai SDK(一种开源游戏引擎)和Moai Cloud(一种用于托管和部署游戏服务的服务的云平台)组成。 现在Moai Cloud已关闭,只有游戏引擎可用。
Moai SDK可在多种平台上运行,包括iOS,Android,Chrome,Windows,Mac和Linux。
5. LOVE
LOVE是一个可用于制作2D游戏的框架,它是免费和开源的。 它支持Windows,Mac OS X和Linux平台。
它提供了多种功能,包括:
- 音频API
- 文件系统API
- 键盘和操纵杆API
- 数学API
- 窗口和鼠标API
- 物理API
- 系统和计时器API
6. CryEngine
CryEngine是由德国游戏开发商Crytek开发的游戏引擎。 它已从第1代发展到第4代,是一种先进的开发解决方案。 它支持PC,Xbox 360,PlayStation3和WiiU游戏。
它提供了多种功能,包括:
- 视觉效果,如自然光和动态软阴影,实时动态全局照明,光传播体积,粒子着色,曲面细分等。
- 人物动画系统与人物个性化系统。
- 参数化动画和独特的专用面部动画编辑器。
- AI系统,如多层导航网格和战术点系统。 还提供设计师友好的AI编辑系统。
- 在游戏混合和分析,数据驱动的声音系统动态声音和交互式音乐等。
- 物理特征如程序变形和高级绳索物理。
这些游戏SDK/框架中的每一个都有各自的优缺点。 在它们之间进行适当的选择可以让工作更轻松,并且可以更好地利用它。 因此,在使用它之前,需要了解游戏的要求,然后分析哪些能满足需求,然后才能决定使用哪一个。
Lua标准库
Lua标准库提供了一组丰富的函数,这些函数直接使用C语言API实现,并使用Lua编程语言构建。这些库提供Lua编程语言中的服务以及文件和数据库操作之外的服务。
这些在官方C语言API中构建的标准库作为单独的C模块提供。它包括以下内容 -
- 基本库,包括协程子库
- 模块库
- 字符串操作
- 表操作
- 数学库
- 文件输入和输出
- 操作系统设施
- 调试工具
1. 基础库
我们在整个教程中使用了各种主题下的基本库。 下表提供了相关页面的链接,并列出了本Lua教程各部分所涵盖的功能。
编号 | 库/方法 | 作用 |
---|---|---|
1 | 错误处理 | 包括错误处理函数,如断言, 错误,如Lua错误处理中所述。 |
2 | 内存管理 | 包括与垃圾收集相关的自动内存管理功能, 如Lua垃圾收集中所述。 |
3 | dofile ([filename]) |
它打开文件并以块的形式执行文件的内容。 |
4 | _G |
因此是保存全局环境的全局变量(即_G._G = _G )。 |
5 | getfenv ([f]) |
返回函数使用的当前环境。 |
6 | getmetatable (object) |
如果object 没有metatable , 则返回nil 。 否则,如果object 的metatable 具有__metatable 字段, |
7 | ipairs (t) |
此函数获取表的索引和值。 |
8 | load (func [, chunkname]) |
使用函数func 加载一个块来获取它的碎片。 |
9 | loadfile ([filename])) |
与load 类似,但是如果没有给出文件名,则从文件filename 或标准输入中获取块。 |
10 | loadstring (string [, chunkname]) |
与load 函数类似,但从给定的字符串中获取块。 |
11 | next (table [, index]) |
允许程序遍历表的所有字段。 |
12 | pairs (t) |
暂停正在运行的协同程序。 |
13 | print (...) |
打印给定的参数值。 |
14 | rawequal (v1, v2) |
检查v1 是否等于v2 ,而不调用任何无方法。 返回一个布尔值。 |
15 | rawget (table, index) |
获取table [index] 的值, 而不调用任何方法。table 必须是表; index 可以是任何值。 |
16 | rawset (table, index, value) |
将table [index] 的值设置为value ,而不调用任何方法。 |
17 | select (index, ...) |
如果index 是数字,则返回参数编号索引后的所有参数。 |
18 | setfenv (f, table) |
设置给定函数使用的环境。 |
19 | setmetatable (table, metatable) |
设置给定表的元表。 |
20 | tonumber (e [, base]) |
尝试将参数转换为数字。 |
21 | tostring (e) |
接收任何类型的参数并将其转换为合理格式的字符串。 |
22 | type (v) |
返回唯一参数的类型,编码为字符串。 |
23 | unpack (list [, i [, j]]) |
返回给定表中的元素。 |
24 | _VERSION |
包含当前解释器版本的字符串的全局变量(不是函数)。 |
25 | 协同程序 | 包括Lua协同程序中解释的协程操作功能。 |
2. 模块库
模块库提供了在Lua中加载模块的基本功能。 它直接在全局环境中导出一个函数:require
。 其他所有内容都在表包中导出。 有关模块库的详细信息,请参见前面的Lua模块教程。
3. 字符串操作
Lua提供了丰富的字符串操作功能。 前面的Lua字符串教程详细中介绍了这一点。
4. 表操作
Lua几乎在其所有操作中都依赖于表。 前面的Lua表教程详中细介绍了这一点。
5. 文件输入和输出
经常需要编程中的数据存储工具,这是由Lua中的文件I/O的标准库函数提供的。 它在前面的Lua 文件操作教程中讨论过。
6. 调试工具
Lua提供了一个调试库,它提供了创建自己的调试器的所有原始函数。 在前面的Lua调试教程中讨论过了。
Lua数学库
经常需要在科学和工程计算中进行数学运算,可以使用标准的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 |
pi 的值。 |
22 | math.pow(x,y) |
返回x 的y 方。(也可以使用表达式x ^ y 来计算此值。) |
23 | math.rad(x) |
以弧度为单位返回角度x (以度为单位)。 |
24 | math.random([m [, n]]) |
此函数是ANSI C提供的简单伪随机生成器函数rand的接口。 |
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
上面的例子只是一些常见的例子,可以根据需要使用数学库,所以尝试使用所有的函数以更熟悉运用。
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功能系统。 它传递要由操作系统shell执行的命令。 如果命令成功终止,则第一个结果为true ,否则为nil 。 |
5 | os.exit([code[, close]) |
调用ANSI C函数出口以终止宿主程序。 如果code 为true ,则返回状态为EXIT_SUCCESS ; 如果code 为false ,则返回状态为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 ,currency ,numeric 或time ; 默认类别(category )是"all" 。该函数返回新语言环境的名称,如果无法满足请求,则返回nil 。 |
10 | os.time([table]) |
返回不带参数调用的当前时间,或表示给定表指定的日期和时间的时间。 此表必须包含字段年,月和日,并且可能包含字段小时(默认值为12 ),分钟(默认值为0 ),秒(默认值为0 )和isdst (默认值为nil )。 有关这些字段的说明,请参见os.date 函数。 |
11 | os.tmpname() |
返回一个文件名,该文件名可用于临时文件。 文件必须在使用前显式打开,并在不再需要时显式删除。 |
常见的OS功能
使用常见数学函数的简单示例如下所示 -
-- 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/2018
The date and time is 01/25/18 07:38:40
The OS time is 1490615720
Lua started before 0.013
上面的例子只是一些常见的例子,可根据自己的需要使用OS库,建议尝试使用所有的功能以便更加熟悉。像remove
这样的函数有助于删除文件,执行有助于于执行OS命令。
ldoc
参考
http://stevedonovan.github.io/ldoc/manual/doc.md.html
https://www.cnblogs.com/ZhYQ-Note/articles/6022525.html
说明
LDoc遵循JavaDoc等文档生成工具的传统,即只有doc comment可以被解析,这种doc comment必须以三个-开头
安装方法
brew install luarocks -v
luarocks install Ldoc
使用方法
> vim config.ld
project='ldoc_demo'
title='一个用于测试LDoc的项目'
description='一个用于测试LDoc的项目'
file='.'
> ldoc -v ldoc_demo