文章目录
Lua
Lua是一种轻量小巧的脚本语言,用标准C语言编写并以源代码形式开放,其设计目的是为了嵌入应用程序中,从而为应用程序提供灵活的扩展和定制功能。
Lua的特性
- 轻量级:使用标准C语言编写并以源代码形式开放,编译之后仅为一百余K,可以很方便的嵌入到应用程序中
- 可扩展:Lua提供了非常易于使用的扩展接口和机制:由宿主语言(主要是C和C++)提供这些功能,Lua可以使用他们就像是本来就内置的功能一样
- 其他特性:
- 支持面向过程变成和函数式编程
- 自动的内存管理:只提供一种通用类型的表(table),用它可以实现数组,哈希表,集合,对象
- 语言内置模式匹配;闭包(closure);函数可以看做成一个值;提供多线程(协同进程,并非操作系统所支持的线程)支持
- 通过闭包和table可以很方便的支持面向对象编程所需要的一些关键机制,比如说数据抽象,虚函数,继承和重载
Lua的应用场景
- 游戏开发
- 独立开发脚本
- web应用脚本
- 扩展和数据库插件:MySql Proxy 和MySQL WorkBench
- 安全系统,如入侵检测系统
Lua基本语法
Lua编程
Lua编程可以支持两种形式的编程,交互式编程和脚本式编程
Lua交互式编程模式可以通过命令lua -i或者lua来启用:
$ lua -i
$ Lua 5.3.0 Copyright (C) 1994-2015 Lua.org, PUC-Rio
> print("Hello World!")
Hello World!
>
Lua脚本式编程是指将Lua程序代码保存到一个以lua结尾的文件,并执行。
#!/usr/local/bin/lua
print("Hello World!")
上述代码中指定了Lua的解释器/usr/local/bin/lua。加上#号标记解释器会忽略它。
注释
单行注释和多行注释
--print("你好世界")
--[[
print("上海市")
print("黄埔区")
--]]
print("hello,world".."你好世界")
执行代码为
hello,world你好世界
标识符
Lua标识符用于定义一个变量。表示符以一个字母A-Z或者a-z或者下划线_开头后面加上0个或者多个字母,下划线,数字(0-9)。
- 不能以数字开头
- 不能是关键字
- 避免“下划线+大写字母”组成的标识符(在lua中是保留用法,有特殊用法,一般约定用于Lua内部全局变量)
保留关键词
and | break | do | else |
---|---|---|---|
elseif | end | false | for |
function | if | in | local |
nil | not | or | repeat |
return | then | true | until |
while | goto |
全局变量
在Lua默认情况下,变量总是认为是全局的。全局变量不需要声明,给一个变量赋值之后即创建了这个全局变量,访问一个没有初始话的全局变量也不会错,只是会返回的结果是:nil。
如果你想删除一个全局变量,你只要将这个变量赋值为nil就行。
当且仅当一个变量不等于nil时,这个变量才存在。
--访问一个没有初始化的全局变量
print(b)
--创建一个全局变量C
c = 100
print(c)
--删除一个全局变量
c = nil
print(b)
执行代码结果为:
nil
100
nil
Lua数据类型
Lua是动态语言类型,变量不需要进行类型定义,只需要为变量进行赋值。值可以存储在变量中,作为参数传递或结果返回。
Lua中有个8个基本数据类型:nil、boolean、number、string、userdata、function、thread、table。
nil(空)
nil类型表示一种没有任何有效值,它只有一个值:nil。
对于全局变量和table, nil还有一个“删除”的作用。给全局变量或table表里的变量赋一个nil,就等同于删除这个变量。
tab1 = { "val1", "val2", "val3" }
for k, v in pairs(tab1) do
print(k .. " - " .. v)
end
print("************")
tab1[1] = nil
for k, v in pairs(tab1) do
print(k .. " - " .. v)
end
执行代码结果为:
1 - val1
2 - val2
3 - val3
************
2 - val2
3 - val3
tabl[1] = nil ,就等同于删除table tab1中的第一个变量
nil用作比较时应该加上双引号""
print(type(X))
print(type(X)==nil)
print(type(X)=="nil")
执行代码结果为:
nil
false
true
type()函数测试未初始化的全局变量X时返回的是一个"nil"的字符串。是一个string类型,则用nil比较时需要加上双引号""。
boolean(布尔)
boolean(布尔)类型只有两个可选值:true或false。Lua中把false和nil看作是false,其他都是true,数字0也是true。
print(type(true))
print(type(false))
print(type(nil))
if false or nil then
print("至少有一个是 true")
else
print("false 和 nil 都为 false")
end
if 0 then
print("数字 0 是 true")
else
print("数字 0 为 false")
end
执行代码结果为:
boolean
boolean
nil
false 和 nil 都为 false
数字 0 是 true
number(数字)
Lua中默认只有一种number类型:double(双精度类型),默认类型可以修改luaconf.h里的定义。
print(2e-1)
print(2e-2)
print(2e-3)
print(2e+0)
print(2e+1)
print(2e+2)
print(2e+3)
print("*************")
print(type(2))
print(type(2.2))
print(type(0.2))
print(type(2e+1))
执行结果为:
0.2
0.02
0.002
2.0
20.0
200.0
2000.0
*************
number
number
number
number
string(字符串)
字符串string由一对双引号或单引号来表示。也可以用两个方括号来表示一块字符串[[]]。
stringA = 'hello,world'
stringB = "hello,china"
stringC = [[www hello,hello www]]
print(stringA)
print(stringB)
print(stringC)
执行代码结果:
hello,world
hello,china
www hello,hello www
当对一个数字字符串进行算数运算时,会将这个数字字符串转成一个数字
strNumA = "5"
strNumB = "55"
print(type(strNumA))
print(type(strNumB))
resA = strNumA + 5
resB = strNumB + 5
resC = strNumA - 5
resD = strNumB - 5
resE = strNumA * 5
resF = strNumB / 5
resG = strNumB + strNumA
print("结果:"..resA.."类型为:"..type(resA))
print("结果:"..resB.."类型为:"..type(resB))
print("结果:"..resC.."类型为:"..type(resC))
print("结果:"..resD.."类型为:"..type(resD))
print("结果:"..resE.."类型为:"..type(resE))
print("结果:"..resF.."类型为:"..type(resF))
print("结果:"..resG.."类型为:"..type(resG))
执行结果为:
string
string
结果:10类型为:number
结果:60类型为:number
结果:0类型为:number
结果:50类型为:number
结果:25类型为:number
结果:11.0类型为:number
结果:60类型为:number
在lua中当一个数字字符串进行+、-、*、/算数运算时,会先将数字字符串转换成数字再进行算数运算。
字符串计算长度使用#
strA = "wwwhelloworldwww"
print("strA的长度是:"..#strA)
执行结果为:
strA的长度是:16
table(表)
在Lua中,表的创建时通过构造表达式来完后的,最简单的构造表达式{},用来创建一个空表。可以直接在表里面添加一些数据,直接初始化表。table(表)其实是一个关联数组,它的索引可以是数字或者字符串,它的默认初始索引下标是从1开始,区别于其他语言默认初始索引是从0开始。
table(表)不会固定长度大小,有添加新数据时table会自动变长。表中没有初始化都是nil。
tbl = {"apple", "pear", "orange", "grape"}
for key, val in pairs(tbl) do
print("Key", key,val)
end
--表中添加新数据
tbl[5] = "addNew"
for key, val in pairs(tbl) do
print("Key", key,val)
end
--表中未初始化数据
print(tbl[6])
执行代码结果为:
Key 1 apple
Key 2 pear
Key 3 orange
Key 4 grape
Key 1 apple
Key 2 pear
Key 3 orange
Key 4 grape
Key 5 addNew
nil
function(函数)
在Lua中,函数是被看作是"第一类值:First-Class Value",函数可以存在变量中。
function factorial1(n)
if n == 0 then
return 1
else
return n * factorial1(n - 1)
end
end
print("函数factorial1返回值:"..factorial1(5))
factorial2 = factorial1
print("变量factorial2的值:"..factorial2(5))
执行代码结果为:
函数factorial1返回值:120
变量factorial2的值:120
factorial2 = factorial1,
将factorial1函数的返回值赋值给变量factorial2 ,factorial1(n)函数的返回值就是返回n的阶乘:n!。
thread(线程)
在Lua中最主要的线程是协同程序(coroutine)。它跟线程差不多,拥有自己独立的栈、局部变量和指令指针,可以跟其他协同程序共享全局变量和其他大部分东西。
线程和协程的区别:线程可以同时运行多个,而协程任意时刻只能运行一个,并且处于运行状态的协程只有被挂起时(suspend)才能暂停。
userdata(自定义类型)
userdata是一种用户自定义数据,用于表示一种由应用程序或C/C++语言库所创建的类型,可以将任意C/C++的任意数据类型的数据(通常是struct和指针)存储到Lua变量中调用。
Lua变量
Lua 变量有三种类型:全局变量、局部变量、表中的域。
变量在使用前,需要在代码中进行声明,即创建该变量。Lua中变量全是全局变量,不管是语句块中或者是函数中都是全局变量。只有用local显示声明的变量为局部变量。局部变量的作用域从声明位置开始到变量所在语句块结束。变量的默认值都是nil。
a = 5 -- 全局变量
local b = 5 -- 局部变量
function joke()
c = 5 -- 全局变量
local d = 6 -- 局部变量d的作用域到end就结束
end
joke()
print(c,d) --> 5 nil
do
local a = 6 -- 局部变量
b = 6 -- 对局部变量重新赋值
print(a,b); --> 6 6
end
print(a,b) --> 5 6
执行代码结果:
5 nil
6 6
5 6
变量赋值
Lua可以对多个变量同时赋值。变量列表和值列表的各个元素用逗号分开,赋值语句会计算右边的值然后再进行赋值操作,将赋值给左边的变量。
如果要对多个变量进行赋值,必须依次对每个变量进行赋值。
当变量个数和值的个数不一致时,Lua会一直以变量个数为基础采取策略:
- 变量个数>值的个数,按变量个数对应的值补齐nil
- 变量个数<值的个数,多余的值会被忽略
多值赋值经常用来交换变量或将函数调用返回给变量
a,b = 1,2
print(a,b)
--变量的个数>值的个数,值不足就补nil
A,B,C = 3,4
print(A,B,C)
--变量的个数<值的个数,多余的值会被忽略
aa,bb = 11,22,33
print(aa,bb)
--函数返回值进行多值赋值
function func1(n)
return n,n-1
end
AA,BB = func1(6)
print(AA,BB)
--多值赋值必须对依次进行赋值
e,f,g = 0
print(e,f,g)
E,F,G = 0,0,0
print(E,F,G)
执行代码结果:
1 2
3 4 nil
11 22
6 5
0 nil nil
0 0 0
应尽可能使用局部变量
- 避免命名冲突
- 访问局部变量的速度比全局变量更快
Lua流程控制
Lua编程语言流程控制语句通过程序设定一个或多个条件语句来设定。在条件为true时执行指定程序代码,在false时执行其他代码。
控制结构的表达式结果可以是任何值,Lua中认为false和nil为假,true和非nil为真。
--条件表示结果值为false和nil则不执行该条件语句
if (false)
then
print("条件表达式结果:false为真")
else
print("条件表达式结果:false为假")
end
if (nil)
then
print("条件表达式结果:nil为真")
else
print("条件表达式结果:nil为假")
end
--0在表达式结果的值为true
if (0)
then
print("条件表达式结果:0为真")
else
print("条件表达式结果:0为假")
end
执行代码结果:
条件表达式结果:false为假
条件表达式结果:nil为假
条件表达式结果:0为真
Lua函数
在Lua语言中,函数是对语句和表达式进行抽象的主要方法。Lua函数的两种用途:
- 完成指定的任务,这种情况作为调用语句来使用执行指定的任务。
- 计算返回值,这种情况下函数作为赋值语句的表达式来使用。
Lua函数定义格式
optional_function_scope function function_name( argument1, argument2, argument3..., argumentn)
function_body
return result_params_comma_separated
end
optional_function_scope:是设置Lua函数是全局函数还是局部函数,未设置参数则默认为全局函数,若设置成局部函数,则需要设置参数local来指定该函数为局部函数
function_name:函数名称
( argument1, argument2, argument3…, argumentn):函数参数,若是多个参数则可以用逗号隔开,可以不带参数
function_body:函数体,函数中需要执行的代码块
result_params_comma_separated:函数的返回值,Lua函数可以返回多个值,多值之间使用逗号分开
Lua函数调用
--[[ Lua函数的调用max 获得最大值 --]]
function max(num1,num2)
if(num1 > num2)
then
result = num1
else
result = num2
end
return result
end
print("比较4、6的大小,最大的是:"..max(4,6))
print("比较4、2.51的大小,最大的是:"..max(4,2.51))
执行代码结果为:
比较4、6的大小,最大的是:6
比较4、2.51的大小,最大的是:4
Lua函数做函数参数
myprint = function(param)
print("这是打印函数 - ##",param,"##")
end
function add(num1,num2,functionPrint)
result = num1 + num2
-- 调用传递的函数参数
functionPrint(result)
end
myprint(10)
-- myprint 函数作为参数传递
add(2,5,myprint)
执行代码结果为:
这是打印函数 - ## 10 ##
这是打印函数 - ## 7 ##
Lua函数的多返回值
function maximum (a)
local mi = 1 -- 最大值索引
local m = a[mi] -- 最大值
for i,val in ipairs(a) do
if val > m then
mi = i
m = val
end
end
return m, mi
end
maxNum,maxIndex = maximum({8,10,23,12,5})
print("最大值为:"..maxNum,"最大值下标:"..maxIndex)
执行代码结果为:
最大值为:23 最大值下标:3
Lua函数的可变参数
Lua函数可以接受可变数量的参数,在参数列表中使用“…”表示。
Lua函数可以接受固定参数+可变参数。
function add(...)
local s = 0
for i, v in ipairs{...} do --> {...} 表示一个由所有变长参数构成的数组
s = s + v
end
return s
end
print("累加求和为:"..add(3,4,5,6,7)) --->25
执行代码结果为:
累加求和为:25
通常在遍历可变长度参数时只需要使用{…},然后而可变长参数中可能会含有一些nil,那么就可以使用select函数来访问可变长参数。
- select("#",…):返回可变参数的长度
- select(n,…):返回从起点n开始到结束位置所有参数列表
- select函数中传入的"#"或"n"的固定实参称为选择开关(selector),选择开关的不同则对应不同的select函数功能。
select(“n”,…):获得指定起点到结束位置的参数列表
function f(...)
a,b = select(3,...) -->从第三个位置开始,变量 a 对应右边变量列表的第一个参数
print ("变量列表的第一个值:",a)
print ("变量列表的前两个值:",a,b)
print ("变量列表的所有值:",select(3,...)) -->打印所有列表参数
end
f(0,1,2,3,4,5)
执行代码结果为:
变量列表的第一个值: 2
变量列表的前两个值: 2 3
变量列表的所有值: 2 3 4 5
select("#",…)获取可变参数长度
do
function foo(...)
for i = 1, select('#', ...) do -->获取参数总数
local arg = select(i, ...); -->读取参数,arg 对应的是右边变量列表的第一个参数
print("arg", arg);
end
end
foo(1, 2, 3, 4);
end
执行代码结果为:
arg 1
arg 2
arg 3
arg 4
其中do…end没有特殊含义,只是执行一个语句块,等同于C/C++中{},其中的局部变量只能在最近的do…end语句块中进行引用,在语句块外进行引用是失效的
Lua运算符
运算符是一个特殊的符号,用于告诉解释器执行特定的数学或者逻辑运算。Lua中的运算符类型可分为以下几种:算术运算符、关系运算符、逻辑运算符、其他运算符。
算术运算符
操作符分别为:+、-、*、/、%、^(乘幂)、-(减号)
a = 21
b = 10
c = a + b
print("Line 1 - c 的值为 ", c )
c = a - b
print("Line 2 - c 的值为 ", c )
c = a * b
print("Line 3 - c 的值为 ", c )
c = a / b
print("Line 4 - c 的值为 ", c )
c = a % b
print("Line 5 - c 的值为 ", c )
c = a^2
print("Line 6 - c 的值为 ", c )
c = -a
print("Line 7 - c 的值为 ", c )
执行代码结果:
Line 1 - c 的值为 31
Line 2 - c 的值为 11
Line 3 - c 的值为 210
Line 4 - c 的值为 2.1
Line 5 - c 的值为 1
Line 6 - c 的值为 441.0
Line 7 - c 的值为 -21
关系运算符
操作符分别为:==、~=(不等于)、>、<、>=、<=
a = 21
b = 10
print("a的值为:",a)
print("b的值为:",b)
if( a == b )
then
print("Line 1 - a 等于 b" )
else
print("Line 1 - a 不等于 b" )
end
if( a ~= b )
then
print("Line 2 - a 不等于 b" )
else
print("Line 2 - a 等于 b" )
end
if ( a < b )
then
print("Line 3 - a 小于 b" )
else
print("Line 3 - a 大于等于 b" )
end
if ( a > b )
then
print("Line 4 - a 大于 b" )
else
print("Line 5 - a 小于等于 b" )
end
-- 修改 a 和 b 的值
a = 5
b = 20
if ( a <= b )
then
print("Line 5 - a 小于等于 b" )
end
if ( b >= a )
then
print("Line 6 - b 大于等于 a" )
end
执行代码结果:
a的值为: 21
b的值为: 10
Line 1 - a 不等于 b
Line 2 - a 不等于 b
Line 3 - a 大于等于 b
Line 4 - a 大于 b
Line 5 - a 小于等于 b
Line 6 - b 大于等于 a
逻辑运算符
操作符分别为:and,or,not(逻辑非)
a = true
b = true
print("a为:",a)
print("b为:",b)
if ( a and b )
then
print("a and b - 条件为 true" )
end
if ( a or b )
then
print("a or b - 条件为 true" )
end
print("---------分割线---------" )
-- 修改 a 和 b 的值
a = false
b = true
if ( a and b )
then
print("a and b - 条件为 true" )
else
print("a and b - 条件为 false" )
end
if ( not( a and b) )
then
print("not( a and b) - 条件为 true" )
else
print("not( a and b) - 条件为 false" )
end
代码执行结果:
a为: true
b为: true
a and b - 条件为 true
a or b - 条件为 true
---------分割线---------
a and b - 条件为 false
not( a and b) - 条件为 true
其他运算符(#、…)
Lua语言中的连接运算符"…“或字符串长度的运算符”#"
a = "Hello "
b = "World"
c = 5
print("a的值:",a)
print("b的值:",b)
print("c的值:",c)
d = b..5 -->先将number转换成string,然后再进行..连接运算
print("d的值为:",d)
print("c的类型为:",type(c))
print("d的类型为:",type(d))
print("连接字符串 a 和 b ", a..b )
print("b 字符串长度 ",#b )
print("字符串 Test 长度 ",#"Test" )
代码执行结果为:
a的值: Hello
b的值: World
c的值: 5
d的值为: World5
c的类型为: number
d的类型为: string
连接字符串 a 和 b Hello World
b 字符串长度 5
字符串 Test 长度 4
…是连接两个字符串的操作符,当连接一方是number类型时会将先将其转换成string然后再进行连接
运算符优先级
^
not - (unary)
* / %
+ -
..
< > <= >= ~= ==
and
or
Lua字符串
字符串使用方式
字符串或者串是由数字、字母、下划线组成的一串字符。
字符串的表示方式:
单引号间的一串字符
双引号间的一串字符
[[与]]间的一串字符
转义字符用于不能直接显示的字符,在字符串中转换双引号“\”“。
string1 = '单引号表示方式'
print("string1:"..string1)
string2 = "双引号表示方式"
print("string2:"..string2)
string3 = [["括号表示方式"]]
print("string3:"..string3)
string4 = "\"转义双引号\""
print("string4:"..string4)
代码执行结果:
string1:单引号表示方式
string2:双引号表示方式
string3:"括号表示方式"
string4:"转义双引号"
字符串操作
Lua语言提供了很多支持字符串操作的方法。
序号 | 方法 & 用途 |
---|---|
1 | string.upper(argument): 字符串全部转为大写字母。 |
2 | string.lower(argument): 字符串全部转为小写字母。 |
3 | **string.gsub(mainString,findString,replaceString,num)**在字符串中替换。mainString 为要操作的字符串, findString 为被替换的字符,replaceString 要替换的字符,num 替换次数(可以忽略,则全部替换),如:> string.gsub("aaaa","a","z",3); zzza 3 |
4 | string.find (str, substr, [init, [end]]) 在一个指定的目标字符串中搜索指定的内容(第三个参数为索引),返回其具体位置。不存在则返回 nil。> string.find("Hello Lua user", "Lua", 1) 7 9 |
5 | string.reverse(arg) 字符串反转> string.reverse("Lua") auL |
6 | string.format(…) 返回一个类似printf的格式化字符串> string.format("the value is:%d",4) the value is:4 |
7 | string.char(arg) 和 string.byte(arg[,int]) char 将整型数字转成字符并连接, byte 转换字符为整数值(可以指定某个字符,默认第一个字符)。> string.char(97,98,99,100) abcd > string.byte("ABCD",4) 68 > string.byte("ABCD") 65 > |
8 | string.len(arg) 计算字符串长度。string.len("abc") 3 |
9 | string.rep(string, n) 返回字符串string的n个拷贝> string.rep("abcd",2) abcdabcd |
10 | … 链接两个字符串> print("www.runoob.".."com") www.runoob.com |
11 | string.gmatch(str, pattern) 回一个迭代器函数,每一次调用这个函数,返回一个在字符串 str 找到的下一个符合 pattern 描述的子串。如果参数 pattern 描述的字符串没有找到,迭代函数返回nil。> for word in string.gmatch("Hello Lua user", "%a+") do print(word) end Hello Lua user |
12 | string.match(str, pattern, init) string.match()只寻找源字串str中的第一个配对. 参数init可选, 指定搜寻过程的起点, 默认为1。 在成功配对时, 函数将返回配对表达式中的所有捕获结果; 如果没有设置捕获标记, 则返回整个配对字符串. 当没有成功的配对时, 返回nil。> = string.match("I have 2 questions for you.", "%d+ %a+") 2 questions > = string.format("%d, %q", string.match("I have 2 questions for you.", "(%d+) (%a+)")) 2, "questions" |
字符串截取
string.sub()是字符串的截取方法。原型为:string.sub(s, i [, j]
- s:要截取的字符串
- i:截取的位置
- j:截取的结束位置,默认为-1,最后一个字符
-- 字符串
local sourcestr = "prefix--runoobgoogletaobao--suffix"
print("\n原始字符串", string.format("%q", sourcestr))
-- 截取部分,第1个到第15个
local first_sub = string.sub(sourcestr, 4, 15)
print("\n第一次截取", string.format("%q", first_sub))
-- 取字符串前缀,第1个到第8个
local second_sub = string.sub(sourcestr, 1, 8)
print("\n第二次截取", string.format("%q", second_sub))
-- 截取最后10个
local third_sub = string.sub(sourcestr, -10)
print("\n第三次截取", string.format("%q", third_sub))
-- 索引越界,输出原始字符串
local fourth_sub = string.sub(sourcestr, -100)
print("\n第四次截取", string.format("%q", fourth_sub))
代码执行结果为:
原始字符串 "prefix--runoobgoogletaobao--suffix"
第一次截取 "fix--runoobg"
第二次截取 "prefix--"
第三次截取 "ao--suffix"
第四次截取 "prefix--runoobgoogletaobao--suffix"
字符串格式化
Lua提供了string.format()函数来生成具有特定格式的字符串,函数的一个参数是格式,之后是对应的格式的数据。与C语言中的printf类似,提高了字符串的可读性。格式字符串可能包含以下转义码。
%c - 接受一个数字, 并将其转化为ASCII码表中对应的字符
%d, %i - 接受一个数字并将其转化为有符号的整数格式
%o - 接受一个数字并将其转化为八进制数格式
%u - 接受一个数字并将其转化为无符号整数格式
%x - 接受一个数字并将其转化为十六进制数格式, 使用小写字母
%X - 接受一个数字并将其转化为十六进制数格式, 使用大写字母
%e - 接受一个数字并将其转化为科学记数法格式, 使用小写字母e
%E - 接受一个数字并将其转化为科学记数法格式, 使用大写字母E
%f - 接受一个数字并将其转化为浮点数格式
%g(%G) - 接受一个数字并将其转化为%e(%E, 对应%G)及%f中较短的一种格式
%q - 接受一个字符串并将其转化为可安全被Lua编译器读入的格式
%s - 接受一个字符串并按照给定的参数格式化该字符串
可在%后面添加参数进行格式的细化
(1) 符号: 一个+号表示其后的数字转义符将让正数显示正号. 默认情况下只有负数显示符号.
(2) 占位符: 一个0, 在后面指定了字串宽度时占位用. 不填时的默认占位符是空格.
(3) 对齐标识: 在指定了字串宽度时, 默认为右对齐, 增加-号可以改为左对齐.
(4) 宽度数值
(5) 小数位数/字串裁切: 在宽度数值后增加的小数部分n, 若后接f(浮点数转义符, 如%6.3f)则设定该浮点数的小数只保留n位, 若后接s(字符串转义符, 如%5.3s)则设定该字符串只显示前n位.
string1 = "Lua"
string2 = "Tutorial"
number1 = 10
number2 = 20
-- 基本字符串格式化
print(string.format("基本格式化 %s %s",string1,string2))
-- 日期格式化
date = 2; month = 1; year = 2014
print(string.format("日期格式化 %02d/%02d/%06d", date, month, year))
print(string.format("日期格式化 %02d/%02d/%04d", date, month, year))
-- 十进制格式化
print(string.format("%.4f",1/3))
代码执行结果:
基本格式化 Lua Tutorial
日期格式化 02/01/002014
日期格式化 02/01/2014
0.3333
字符与整数的转换
-- 字符转换
-- 转换第一个字符
print(string.byte("Lua"))
-- 转换第三个字符
print(string.byte("Lua",3))
-- 转换末尾第一个字符
print(string.byte("Lua",-1))
-- 第二个字符
print(string.byte("Lua",2))
-- 转换末尾第二个字符
print(string.byte("Lua",-2))
-- 整数 ASCII 码转换为字符
print(string.char(97))
代码执行结果为:
76
97
97
117
117
a
匹配模式
Lua中的匹配模式直接用常规的字符串来描述。用于模式匹配函数string.find,string.gmatch,string.gsub,string.match。
字符类指可以匹配一个特定字符集合内任何字符的模式项。比如字符类%d匹配任意数字。
Lua所支持的所有字符类:
-
.(点): 与任何字符配对
-
%a: 与任何字母配对
-
%c: 与任何控制符配对(例如\n)
-
%d: 与任何数字配对
-
%l: 与任何小写字母配对
-
%p: 与任何标点(punctuation)配对
-
%s: 与空白字符配对
-
%u: 与任何大写字母配对
-
%w: 与任何字母/数字配对
-
%x: 与任何十六进制数配对
-
%z: 与任何代表0的字符配对
-
%x(此处x是非字母非数字字符): 与字符x配对. 主要用来处理表达式中有功能的字符(^$()%.[]*±?)的配对问题, 例如%%与%配对
-
[数个字符类]: 与任何[]中包含的字符类配对. 例如[%w_]与任何字母/数字, 或下划线符号(_)配对
s = "Deadline is 30/05/1999, firm"
date = "%d%d/%d%d/%d%d%d%d"
print(string.sub(s, string.find(s, date))) --> 30/05/1999
-->'%A'非字母的字符
print(string.gsub("hello, up-down!", "%A", "."))
代码执行结果为:
30/05/1999
hello..up.down. 4
Lua数组
数组就是相同数据类型的元素按一定顺序排列,Lu数组的索引值可以使用整数表示。我们可以通过整数索引来访问数组元素,如果知道的索引没有值则返回nil。Lua索引值是以1开始,但你也可以指定0开始。除此之外还可以以负数作为索引值。
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
Lua迭代器
迭代器(Iterator)是一种对象,能够用来遍历标准模板库容器中的部分或者全部远古龙。每个迭代器对象代表容器中确切的地址。在Lua中迭代器是一种支持指针类型的结构,它可以遍历集合的每一个元素。
泛型for迭代器
泛型for在自己内部保存迭代函数,实际上它保存三个值:迭代函数、状态常量、控制变量。泛型for迭代器提供了集合的key/value对,语法格式是:
for k, v in pairs(t) do
print(k, v)
end
- k,v:为变量列表
- paris(t):为表达式列表
array = {"Google", "Runoob"}
for key,value in ipairs(array)
do
print(key, value)
end
代码执行结果:
1 Google
2 Runoob
上面例子使用的是Lua语言中默认提供的迭代函数ipairs()。
ipairs和pairs都是Lua中的内置迭代器,用于在循环中的数组迭代。但是两者还是有所区别,ipairs在遇到第一个值为nil的元素时就会停止迭代,仅适用于数组类型的table;但是如果需要使用字典式table或者需要遍历到空值,即pairs不管nil出不出现都不会影响迭代器一直迭代到整个数组的元素全部被遍历一遍。
泛型for的执行过程:
- 首先初始化,计算in后面表达式的值,该表达时应该返回泛型for需要的三个值:迭代函数、状态常量、控制常量;与多值赋值一样,如果表达式返回值个数不足三个会自动将值补上nil,多出个数值的则会被忽略。
- 将状态常量和控制常量作为参数调用迭代函数(注意:状态常量对于for结构来说没有任何用处,仅仅在初始化获取它的值并传递给迭代函数)
- 将迭代函数的返回值赋值给变量列表
- 如果返回值第一个为nil,则循环结束,否则执行循环体
- 再次回到第二步调用迭代函数
在Lua中常常使用函数来描述迭代器,每次调用该函数就返回集合的下一个元素。Lua中的迭代器主要分为两种类型。
- 无状态迭代器
- 多状态迭代器
无状态迭代器
无状态迭代器是指不保留任何状态的迭代器,因为在迭代中我们可以利用无状态迭代器避免创建闭包花费额外的代价。每一次迭代,迭代函数都是用两个变量(状态变量和控制变量)的值作为参数被调用,一个无状态的迭代器只利用这个两个值可以获取下一个元素。
这种无状态迭代器的典型的简单例子是iparis,它遍历数组的每一个元素,元素的索引需要时数值。
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
上述是一个简单的实现数字平方的迭代器函数,迭代器函数square将返回值赋值给变量列表。
多状态的迭代器
迭代器需要保存多个状态信息而不是简单的状态常量和控制变量,最简单的方式是使用闭包,还有一种方法是将所有状态信息封装到table内,将table作为迭代器的状态常量,因为这种情况下可以将所有的信息,所以迭代器通常就不需要第二个参数。
array = {"Google", "Runoob"}
function elementIterator (collection)
local index = 0
local count = #collection
-- 闭包函数
return function ()
index = index + 1
if index <= count
then
-- 返回迭代器的当前元素
return collection[index]
end
end
end
for element in elementIterator(array)
do
print(element)
end
代码执行结果:
Google
Runoob
上述例子elementIterator中使用闭包函数:计算集合大小并输出各种元素
Lua table(表)
table是Lua的一种数据结构用来帮我们创建不同的数据类型,如数组、字典等。
Lua table使用的是关联型数组,你可以根据任意类型的值来作数组的索引,但是索引值不能为nil。
Lua table是不固定大小的,你可以根据自己需要进行扩容。
Lua也是通过table来解决模块(module)、包(package)和对象(object)的
table的构造
构造器是表创建和初始化表的表达式。表是Lua特有功能强大的东西,最简单的是构造函数{},用来创建一个空表
--构造函数创建空表
mytable = {}
print("mytable的类型是:",type(mytable))
--空表添加元素
mytable[1] = "Lua Test"
mytable["hello"] = "world"
--mytable表赋值给altertable
altertable = mytable
print("altertable的类型是:",type(altertable))
print("altertable[1]:",altertable[1])
print("altertable[\"hello\"]:",altertable["hello"])
mytable = nil
print("将mytable设置为nil")
print("altertable[1]:",altertable[1])
print("altertable[\"hello\"]:",altertable["hello"])
altertable = nil
print("将altertable设置成nil")
print("altertable[1]:",altertable[1])
print("altertable[\"hello\"]:",altertable["hello"])
代码执行结果:
mytable的类型是: table
altertable的类型是: table
altertable[1]: Lua Test
altertable["hello"]: world
将mytable设置为nil
altertable[1]: Lua Test
altertable["hello"]: world
将altertable设置成nil
input:21: attempt to index a nil value (global 'altertable')
上述例子创建一个空表mytable,并设置元素"Lua Test"、“world”。然后将mytable赋值给altertable,则mytable和altertable都指向同一块内存,当mytable = nil时,则altertable同样能通过索引访问元素。若没有变量指向a,即mytable和altertable都为nil时,Lua的垃圾回收机制会清理相对应的内存。此时mytable和altertable为空值,所以解释器会报"attempt to index a nil value"的错误,空值是不能建立索引。
table的操作
Lua提供了很多方法来支持table的操作,列举几个常用的方法
序号 | 方法 & 用途 |
---|---|
1 | **table.concat (table [, sep [, start [, end]]])😗*concat是concatenate(连锁, 连接)的缩写. table.concat()函数列出参数中指定table的数组部分从start位置到end位置的所有元素, 元素间以指定的分隔符(sep)隔开。 |
2 | **table.insert (table, [pos,] value)😗*在table的数组部分指定位置(pos)插入值为value的一个元素. pos参数可选, 默认为数组部分末尾. |
3 | **table.maxn (table)**指定table中所有正数key值中最大的key值. 如果不存在key值为正数的元素, 则返回0。(Lua5.2之后该方法已经不存在了,本文使用了自定义函数实现) |
4 | **table.remove (table [, pos])**返回table数组部分位于pos位置的元素. 其后的元素会被前移. pos参数可选, 默认为table长度, 即从最后一个元素删起。 |
5 | **table.sort (table [, comp])**对给定的table进行升序排序。 |
table连接
fruits = {"banana","apple","pear","orange"}
print("fruits的类型是:",type(fruits))
--连接fruits
print("fruits.concat:",table.concat(fruits))
--指定连接的字符
print("指定\",\"连接的字符:",table.concat(fruits,","))
--通过索引指定连接
print("通过索引指定连接字符:",table.concat(fruits,",",2,4))
代码执行结果为:
fruits的类型是: table
fruits.concat: bananaapplepearorange
指定","连接的字符: banana,apple,pear,orange
通过索引指定连接字符: apple,pear,orange
table插入和移除
fruits = {"banana","apple","pear","orange"}
print("fruits的类型是:",type(fruits))
--没有指定位置,则默认在末尾插入
print("向fruits中insert mango:",table.insert(fruits,"mango"))
for k,v in ipairs(fruits) do
print(k,v)
end
--删除指定位置元素
print("删除最后一个元素:",table.remove(fruits,#(fruits)))
for k,v in ipairs(fruits) do
print(k,v)
end
--没有指定删除位置,则默认为table长度
print("删除最后一个元素:",table.remove(fruits))
for k,v in ipairs(fruits) do
print(k,v)
end
--指定位置插入元素strawberry
print("指定位置2插入元素:","strawberry",table.insert(fruits,2,"strawberry"))
for k,v in ipairs(fruits) do
print(k,v)
end
代码执行结果:
fruits的类型是: table
向fruits中insert mango:
1 banana
2 apple
3 pear
4 orange
5 mango
删除最后一个元素: mango
1 banana
2 apple
3 pear
4 orange
删除最后一个元素: orange
1 banana
2 apple
3 pear
指定位置2插入元素: strawberry
1 banana
2 strawberry
3 applea
4 pear
table排序
table排序的方法是table.sort(),默认的是升序排序
fruits = {"banana","orange","apple","grapes"}
print("排序前")
for k,v in ipairs(fruits) do
print(k,v)
end
table.sort(fruits)
print("排序后")
for k,v in ipairs(fruits) do
print(k,v)
end
代码执行结果为:
排序前
1 banana
2 orange
3 apple
4 grapes
排序后
1 apple
2 banana
3 grapes
4 orange
Lua模块与包
模块类似于一个封装库,Lua5.1版本之后,Lua加入了标准的模块管理机制,可以把一些公用的代码放到一个文件里,以Api接口的形式在其他地方调用,有利于代码的重用和降低代码的耦合度。
Lua模块是由变量、函数等已知元素所组成的table,因此创建一个模块就等同于创建一个table,然后把需要导出的常量和函数放在table里面,最后返回这个table就行了。
-- 文件名为 module.lua
-- 定义一个名为 module 的模块
module = {}
-- 定义一个常量
module.constant = "这是一个常量"
-- 定义一个函数
function module.func1()
io.write("这是一个公有函数!\n")
end
local function func2()
print("这是一个私有函数!")
end
function module.func3()
func2()
end
return module
上述例子是创建自定义模块module.lua,该文件的代码格式。从该文件的代码格式来看,模块的结构就是一个table的结构。因此可以像调用table里的元素那样调用模块里的常量或者函数。
func2是模块里面声明的local局部变量,表示的是一个私有函数,因此是不能从外部直接访问模块的这个私有函数,只能通过模块里的其他公有函数来调用此私有函数,即module.func3()。
模块加载函数
Lua提供了一个加载模块的函数:require。要加载一个模块了,只需要简单的调用该函数就可以了。
--调用格式
require("<模块名>") require "<模块名>"
-- test_module.lua 文件
-- module 模块为上文提到到 module.lua
require("module")
print(module.constant)
module.func3()
--给加载的模块定义一个别名变量
local m = require("module")
print(m.constant)
m.func3()
代码执行结果为:
这是一个常量
这是一个私有函数
这是一个常量
这是一个私有函数
模块加载机制
对于自定义的模块,模块文件不是放在哪个目录都行,加载函数require有自己特有的文件路径加载策略,它会尝试从Lua文件或者C程序库中加载模块。
require用于搜索Lua文件的路径是存放在全局变量package.path中,当Lua启动后,会以环境变量LUA_PATH的值来初始化这个环境变量,如果没有找到该环境变量,则使用一个编译时定义的默认路径来初始化。
如果没有LUA_PATH这个环境变量,可自定义设置,在当前用户的根目录下打开.profile文件,没有则创建该文件或者打开.bashrc可以,将"~/lua/"路径加入到LUA_PATH环境变量中。
#LUA_PATH
export LUA_PATH="~/lua/?.lua;;"
package.path实际上是在虚拟机启动时进行设置,如果LUA_PATH存在,则用该环境变量作为它的值,并把这个环境变量中的";;"替换为luaconf.h中定义的默认值,如果不存在该变量就直接使用luaconf.h定义的默认值
全局变量package.path:保存加载外部模块的搜索路径,这种路径是"模板式的路径",它里面可能会包含可替代符号"?",这个符号会被替换,然后查找这个文件是否存在,如果存在就会调用其特定的接口。假设package.path此时的值为:
/Users/dengjoe/lua/?.lua;./?.lua;/usr/local/share/lua/5.1/?.lua;/usr/local/share/lua/5.1/?/init.lua;/usr/local/lib/lua/5.1/?.lua;/usr/local/lib/lua/5.1/?/init.lua
如果lua代码中调用:require(“module”),则此时package.path中模板式的路径会被替换成
/Users/dengjoe/lua/module.lua;
./module.lua
/usr/local/share/lua/5.1/module.lua
/usr/local/share/lua/5.1/module/init.lua
/usr/local/lib/lua/5.1/module.lua
/usr/local/lib/lua/5.1/module/init.lua
当上面所说的模板式路径被替换时,就会尝试去打开文件目录去搜索目标文件module.lua。找到目标文件然后去调用模块require(“module”)。
调用模块步骤:
检查package.loaded表格是否已经加载过该模块,如果已经加载过就直接返回,所以重复加载同一个模块多次只会加载一次。
如果package.loaded没有过此模块的记录,则才会发生上面的替换packag.path的"模板式路径"去寻找目标文件,找到目标文件之后会调用loadfile加载,并且返回一个函数(loader)。如果找不到相应的目标文件,则会去package.cpath指定的路径下搜索动态库so文件,如果找到则会调用package.loadlib加载并寻找luaopen_modname函数(也就是loadfile的结果)
不管是在package.path还是在package.cpath路径下寻找对应的lua文件和so文件,现在都有拥有了一个loader加载器(函数)
此时require函数会调用加载器函数并传入两个参数(大部分模块会忽略参数),返回的任意值会存放在package.loaded表格,以便于下次加载时直接返回
Lua元表
Lua提供了元表(Metatable),允许我们该表table的行为,每个行为关联了对应的元方法。
当Lua试图对两个table进行相加操作时,首先先检查两者之一是否有元表,之后检查是否有一个叫"_add"的字段,若找到该字段,则调用对应的值(往往是一个函数或table)就是"元方法"。
两个处理元表的函数:
setmetatable(table,metatable):对指定table设置元表(metatable),如果元表(metatable)中存在_metatable键值,setmetatable会失败。
getmetatable(table):返回对象的元表(metatable)
Lua协同程序
Lua协同程序(coroutine)与线程类似:拥有独立的堆栈,独立的局部变量,独立的指令指针,同时与其他的协同程序共享全局变量和其他大部分东西
线程和协同程序的区别:
主要区别在于多个线程的程序可以同时运行几个线程,而协同程序需要彼此协作的运行。即在任意指定时刻只有一个协同程序在运行,并且这个正在运行的协同程序只有在明确的被要求挂起的时候才会被挂起。
协同程序有点类似同步的多线程,在等待同一个线程锁的几个线程有点类似协同。
基本语法
方法 | 描述 |
---|---|
coroutine.create() | 创建 coroutine,返回 coroutine, 参数是一个函数,当和 resume 配合使用的时候就唤醒函数调用 |
coroutine.resume() | 重启 coroutine,和 create 配合使用 |
coroutine.yield() | 挂起 coroutine,将 coroutine 设置为挂起状态,这个和 resume 配合使用能有很多有用的效果 |
coroutine.status() | 查看 coroutine 的状态 注:coroutine 的状态有三种:dead,suspended,running,具体什么时候有这样的状态请参考下面的程序 |
coroutine.wrap() | 创建 coroutine,返回一个函数,一旦你调用这个函数,就进入 coroutine,和 create 功能重复 |
coroutine.running() | 返回正在跑的 coroutine,一个 coroutine 就是一个线程,当使用running的时候,就是返回一个 corouting 的线程号 |
Lua文件I/O
Lua I/O库用于读取和处理文件。分为简单模式、完全模式。
简单模式:拥有一个当前输出文件和当前一个输入文件,并且提供针对这些文件相关的操作。
完全模式:使用外部的文件句柄来实现。它是一种面向对象的形式,将所有文件操作定义为文件句柄的方法
简单模式在做一些比较简单的文件操作时较为合适,但在进行一些高级文件操作时,比如说同时读取多个文件,使用完全模式比较合适。
--打开文件操作语句
file = io.open(filenaeme,[,mode])
文件操作模式
模式 | 描述 |
---|---|
r | 以只读方式打开文件,该文件必须存在。 |
w | 打开只写文件,若文件存在则文件长度清为0,即该文件内容会消失。若文件不存在则建立该文件。 |
a | 以附加的方式打开只写文件。若文件不存在,则会建立该文件,如果文件存在,写入的数据会被加到文件尾,即文件原先的内容会被保留。(EOF符保留) |
r+ | 以可读写方式打开文件,该文件必须存在。 |
w+ | 打开可读写文件,若文件存在则文件长度清为零,即该文件内容会消失。若文件不存在则建立该文件。 |
a+ | 与a类似,但此文件可读可写 |
b | 二进制模式,如果文件是二进制文件,可以加上b |
+ | 号表示对文件既可以读也可以写 |
简单模式
简单模式使用的是标准的I/O或者是使用一个当前输入文件和一个当前输出文件。
-- 以只读方式打开文件
file = io.open("test.lua", "r")
-- 设置默认输入文件为 test.lua
io.input(file)
-- 输出文件第一行
print(io.read())
-- 关闭打开的文件
io.close(file)
-- 以附加的方式打开只写文件
file = io.open("test.lua", "a")
-- 设置默认输出文件为 test.lua
io.output(file)
-- 在文件最后一行添加 Lua 注释
io.write("-- test.lua 文件末尾注释")
-- 关闭打开的文件
io.close(file)
io.read()读取参数列表
模式 | 描述 |
---|---|
“*n” | 读取一个数字并返回它。例:file.read("*n") |
“*a” | 从当前位置读取整个文件。例:file.read("*a") |
“*l”(默认) | 读取下一行,在文件尾 (EOF) 处返回 nil。例:file.read("*l") |
number | 返回一个指定字符个数的字符串,或在 EOF 时返回 nil。例:file.read(5) |
完全模式
通常我们需要在同一时间处理多个文件。需要使用file:function_name来代替io.function_name方法。
Lua错误处理
在任何一门语言的程序运行中错误处理是必要的,如果不注重错误处理就会造成信息泄露,程序无法运行等情况。
错误类型:语法错误、运行错误
语法错误
语法错误通常是由于对程序的组件(比如运算符、表达式)使用不当引起的
运行错误
运行错误是指程序可以正常运行,但是会输出报错信息
Lua调试
Lua提供了debug库用于创建我们自定义调试器的功能。Lua中debug库主要包含调试的一下函数。
序号 | 方法 & 用途 |
---|---|
1. | **debug()😗*进入一个用户交互模式,运行用户输入的每个字符串。 使用简单的命令以及其它调试设置,用户可以检阅全局变量和局部变量, 改变变量的值,计算一些表达式,等等。 输入一行仅包含 cont 的字符串将结束这个函数, 这样调用者就可以继续向下运行。 |
2. | **getfenv(object)😗*返回对象的环境变量。 |
3. | **gethook(optional thread)😗*返回三个表示线程钩子设置的值: 当前钩子函数,当前钩子掩码,当前钩子计数 |
4. | **getinfo ([thread,] f [, what])😗*返回关于一个函数信息的表。 你可以直接提供该函数, 也可以用一个数字 f 表示该函数。 数字 f 表示运行在指定线程的调用栈对应层次上的函数: 0 层表示当前函数(getinfo 自身); 1 层表示调用 getinfo 的函数 (除非是尾调用,这种情况不计入栈);等等。 如果 f 是一个比活动函数数量还大的数字, getinfo 返回 nil。 |
5. | **debug.getlocal ([thread,] f, local)😗*此函数返回在栈的 f 层处函数的索引为 local 的局部变量 的名字和值。 这个函数不仅用于访问显式定义的局部变量,也包括形参、临时变量等。 |
6. | **getmetatable(value)😗*把给定索引指向的值的元表压入堆栈。如果索引无效,或是这个值没有元表,函数将返回 0 并且不会向栈上压任何东西。 |
7. | **getregistry()😗*返回注册表表,这是一个预定义出来的表, 可以用来保存任何 C 代码想保存的 Lua 值。 |
8. | **getupvalue (f, up)**此函数返回函数 f 的第 up 个上值的名字和值。 如果该函数没有那个上值,返回 nil 。 以 ‘(’ (开括号)打头的变量名表示没有名字的变量 (去除了调试信息的代码块)。 |
10. | sethook ([thread,] hook, mask [, count]):将一个函数作为钩子函数设入。 字符串 mask 以及数字 count 决定了钩子将在何时调用。 掩码是由下列字符组合成的字符串,每个字符有其含义:’c ’: 每当 Lua 调用一个函数时,调用钩子;’r ’: 每当 Lua 从一个函数内返回时,调用钩子;’l ’: 每当 Lua 进入新的一行时,调用钩子。 |
11. | **setlocal ([thread,] level, local, value)😗*这个函数将 value 赋给 栈上第 level 层函数的第 local 个局部变量。 如果没有那个变量,函数返回 nil 。 如果 level 越界,抛出一个错误。 |
12. | **setmetatable (value, table)😗*将 value 的元表设为 table (可以是 nil)。 返回 value。 |
13. | **setupvalue (f, up, value)😗*这个函数将 value 设为函数 f 的第 up 个上值。 如果函数没有那个上值,返回 nil 否则,返回该上值的名字。 |
14. | **traceback ([thread,] [message [, level]])😗*如果 message 有,且不是字符串或 nil, 函数不做任何处理直接返回 message。 否则,它返回调用栈的栈回溯信息。 字符串可选项 message 被添加在栈回溯信息的开头。 数字可选项 level 指明从栈的哪一层开始回溯 (默认为 1 ,即调用 traceback 的那里)。 |
Lua垃圾回收
Lua采用了自动内存管理。不需要创建对象的内存如何分配,也不需要考虑对象不再使用的时候内存的释放。
Lua运行了一个垃圾收集器来收集所有死对象(即在Lua中不可能再访问得到的对象)来完成自动内存管理工作。Lua所用到的内存,字符串、表、用户数据、函数、线程、内部结构等,都服从自动管理。
Lua实现了一个增量标记-扫描收集器,它使用这两个数字来控制垃圾收集循环;垃圾收集器间歇率和垃圾收集器步进倍率。这两个数字是使用百分数为单位(即值为100,在内部表示为100的百分数为1)。
垃圾收集间歇率控制着收集器需要在开始新的循环前要等待多久。增大会减小收集器的积极性。当这个值小于100时,收集器在开始新的循环前不会有等待。设置这个值为200会让收集器等到总内存使用量达到之前两倍时才开始新的循环。
垃圾收集器步进倍率控制着收集器运作速度相对于内存分配速度的倍率。增大这个会让收集器更加积极,还是会增加每个增量步骤的长度。不要把值设得小于100,那样收集器的工作太慢了(收集器的运作速度小于内存分配速度),默认值是200,这表示收集器以内存分配的"两倍速"工作。
垃圾回收器函数
- Lua提供了控制自动内存管理的函数:
- collectgarbage(“collect”): 做一次完整的垃圾收集循环。通过参数 opt 它提供了一组不同的功能:
- collectgarbage(“count”): 以 K 字节数为单位返回 Lua 使用的总内存数。 这个值有小数部分,所以只需要乘上 1024 就能得到 Lua 使用的准确字节数(除非溢出)。
- collectgarbage(“restart”): 重启垃圾收集器的自动运行。
- collectgarbage(“setpause”): 将 arg 设为收集器的 间歇率。 返回 间歇率 的前一个值。
- collectgarbage(“setstepmul”): 返回 步进倍率 的前一个值。
- collectgarbage(“step”): 单步运行垃圾收集器。 步长"大小"由 arg 控制。 传入 0 时,收集器步进(不可分割的)一步。 传入非 0 值, 收集器收集相当于 Lua 分配这些多(K 字节)内存的工作。 如果收集器结束一个循环将返回 true 。
- collectgarbage(“stop”): 停止垃圾收集器的运行。 在调用重启前,收集器只会因显式的调用运行。
Lua面向对象
Lua中最基本的结构是table,可以用table来表示对象的属性。Lua中的function可以用来表示对象中方法。Lua中的类可以用过table+function的方式模拟出来。
| **setlocal ([thread,] level, local, value)😗*这个函数将 value 赋给 栈上第 level 层函数的第 local 个局部变量。 如果没有那个变量,函数返回 nil 。 如果 level 越界,抛出一个错误。 |
| 12. | **setmetatable (value, table)😗*将 value 的元表设为 table (可以是 nil)。 返回 value。 |
| 13. | **setupvalue (f, up, value)😗*这个函数将 value 设为函数 f 的第 up 个上值。 如果函数没有那个上值,返回 nil 否则,返回该上值的名字。 |
| 14. | **traceback ([thread,] [message [, level]])😗*如果 message 有,且不是字符串或 nil, 函数不做任何处理直接返回 message。 否则,它返回调用栈的栈回溯信息。 字符串可选项 message 被添加在栈回溯信息的开头。 数字可选项 level 指明从栈的哪一层开始回溯 (默认为 1 ,即调用 traceback 的那里)。 |
Lua垃圾回收
Lua采用了自动内存管理。不需要创建对象的内存如何分配,也不需要考虑对象不再使用的时候内存的释放。
Lua运行了一个垃圾收集器来收集所有死对象(即在Lua中不可能再访问得到的对象)来完成自动内存管理工作。Lua所用到的内存,字符串、表、用户数据、函数、线程、内部结构等,都服从自动管理。
Lua实现了一个增量标记-扫描收集器,它使用这两个数字来控制垃圾收集循环;垃圾收集器间歇率和垃圾收集器步进倍率。这两个数字是使用百分数为单位(即值为100,在内部表示为100的百分数为1)。
垃圾收集间歇率控制着收集器需要在开始新的循环前要等待多久。增大会减小收集器的积极性。当这个值小于100时,收集器在开始新的循环前不会有等待。设置这个值为200会让收集器等到总内存使用量达到之前两倍时才开始新的循环。
垃圾收集器步进倍率控制着收集器运作速度相对于内存分配速度的倍率。增大这个会让收集器更加积极,还是会增加每个增量步骤的长度。不要把值设得小于100,那样收集器的工作太慢了(收集器的运作速度小于内存分配速度),默认值是200,这表示收集器以内存分配的"两倍速"工作。
垃圾回收器函数
- Lua提供了控制自动内存管理的函数:
- collectgarbage(“collect”): 做一次完整的垃圾收集循环。通过参数 opt 它提供了一组不同的功能:
- collectgarbage(“count”): 以 K 字节数为单位返回 Lua 使用的总内存数。 这个值有小数部分,所以只需要乘上 1024 就能得到 Lua 使用的准确字节数(除非溢出)。
- collectgarbage(“restart”): 重启垃圾收集器的自动运行。
- collectgarbage(“setpause”): 将 arg 设为收集器的 间歇率。 返回 间歇率 的前一个值。
- collectgarbage(“setstepmul”): 返回 步进倍率 的前一个值。
- collectgarbage(“step”): 单步运行垃圾收集器。 步长"大小"由 arg 控制。 传入 0 时,收集器步进(不可分割的)一步。 传入非 0 值, 收集器收集相当于 Lua 分配这些多(K 字节)内存的工作。 如果收集器结束一个循环将返回 true 。
- collectgarbage(“stop”): 停止垃圾收集器的运行。 在调用重启前,收集器只会因显式的调用运行。
Lua面向对象
Lua中最基本的结构是table,可以用table来表示对象的属性。Lua中的function可以用来表示对象中方法。Lua中的类可以用过table+function的方式模拟出来。