赞
踩
Lua是一种轻量小巧的脚本语言,用标准C语言编写并以源代码形式开放,其设计目的是为了嵌入应用程序中,从而为应用程序提供灵活的拓展和定制功能
Lua可以方便的和其他进程进行集成(C++,c#,java,)
Lua可以在几乎所有的操作系统和平台进行编译运行
可以很方便的更新代码
更新了代码后,可以直接在手机上运行,不需要重新安装(后续的热更新方案)
C#只能在特定的操作系统中进行编译成dll文件,然后打包进安装包在其他平台(Android,IOS)运行
在移动平台上不能更新替换已有的dll文件,除非重新下载安装包
print("hello world")
print("Aubyn")
-- 这是一条注释
--[[
这是多行注释
这是多行注释
--]]
Lua 标示符用于定义一个变量,函数获取其他用户定义的项。标示符以一个字母 A 到 Z 或 a 到 z 或下划线 _ 开头后加上 0 个或多个字母,下划线,数字(0 到 9)。
最好不要使用下划线加大写字母的标示符,因为Lua的保留字也是这样的。
Lua 不允许使用特殊字符如 @, $, 和 % 来定义标示符。 Lua 是一个区分大小写的编程语言。
以下列出了 Lua 的保留关键词。保留关键字不能作为常量或变量或其他用户自定义标示符:
and | break | do | else |
---|---|---|---|
elseif | end | false | for |
function | if | in | local |
nil | not | or | repeat |
return | then | true | until |
while | goto |
一般约定,以下划线开头连接一串大写字母的名字(比如 _VERSION)被保留用于 Lua 内部全局变量。
在默认情况下,变量总是认为是全局的。
全局变量不需要声明,给一个变量赋值后即创建了这个全局变量,访问一个没有初始化的全局变量也不会出错,只不过得到的结果是:nil。
print(b)
b = 10
print(b)
如果想删除一个全局变量,只需要将变量赋值为nil。
b = nil
print(b)
当且仅当一个变量不等于nil时,这个变量即存在。
Lua 是动态类型语言,变量不要类型定义,只需要为变量赋值。 值可以存储在变量中,作为参数传递或结果返回。
数据类型 | 描述 |
---|---|
nil | 这个最简单,只有值nil属于该类,表示一个无效值(在条件表达式中相当于false)。 |
boolean | 包含两个值:false和true。 |
number | 表示双精度类型的实浮点数 |
string | 字符串由一对双引号或单引号来表示 |
function | 由 C 或 Lua 编写的函数 |
userdata | 表示任意存储在变量中的C数据结构 |
thread | 表示执行的独立线路,用于执行协同程序 |
table | Lua 中的表(table)其实是一个"关联数组"(associative arrays),数组的索引可以是数字、字符串或表类型。在 Lua 里,table 的创建是通过"构造表达式"来完成,最简单构造表达式是{},用来创建一个空表。 |
nil 类型表示一种没有任何有效值,它只有一个值 – nil,例如打印一个没有赋值的变量,便会输出一个 nil 值:
name = "Aubyn"
print(name)
name = nil
print(name)
对于全局变量和 table,nil 还有一个"删除"作用,给全局变量或者 table 表里的变量赋一个 nil 值,等同于把它们删掉,执行下面代码就知:
tab1 = {key1 = "value",key2 = "value1"}
print(tab1.key1)
tab1.key1 = nil
tab1 = nil
print(tab1)
nil 作比较时应该加上双引号 "
:
--false
print(type(X)==nil)
--true
print(type(x) == "nil")
type(X)==nil
结果为 false
的原因是 type(X)
实质是返回的 “nil” 字符串,是一个 string
类型:
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("flase和nil都为flase")
end
if 0 then
print("数字0是true")
else
print("数字0是false")
end
Lua 默认只有一种 number 类型 – double(双精度)类型(默认类型可以修改 luaconf.h 里的定义),以下几种写法都被看作是 number 类型:
print(type(2))
print(type(2.2))
print(type(0.2))
print(type(2e+1))
print(type(0.2e-1))
print(type(7.8263692594256e-06))
字符串由一对双引号或单引号来表示。
string1 = "this is string1"
string2 = "this is string2"
也可以用 2 个方括号 “[[]]” 来表示"一块"字符串。
html = [[
<html>
<head></head>
<body>
<a href="http://www.runoob.com/">菜鸟教程</a>
</body>
</html>
]]
print(html)
在对一个数字字符串上进行算术操作时,Lua 会尝试将这个数字字符串转成一个数字:
print("2" + 6)
print("2" + "6")
print("2 + 6")
print("-2e2" * "6")
--报错
print("error" + 1)
以上代码中"error" + 1执行报错了,字符串连接使用的是 … ,如:
print("a" .. 'b')
print(157 .. 428)
使用 # 来计算字符串的长度,放在字符串前面,如下实例:
len = "www.runoob.com"
print(#len)
print(#"www.runoob.com")
在 Lua 里,table 的创建是通过"构造表达式"来完成,最简单构造表达式是{},用来创建一个空表。也可以在表里添加一些数据,直接初始化表:
local tbl1 = {}
local tbl2 = {"apple", "pear", "orange", "grape"}
Lua 中的表(table)其实是一个"关联数组"(associative arrays),数组的索引可以是数字或者是字符串。
a = {}
a["key"] = "value"
key = 10
a[key] = 22
a[key] = a[key] + 11
for k, v in pairs(a) do
print(k .. " : " .. v)
end
print(a.key)
print(a[key])
不同于其他语言的数组把 0 作为数组的初始索引,在 Lua 里表的默认初始索引一般以 1 开始
local tbl = {"apple", "pear", "orange", "grape"}
for key, val in pairs(tbl) do
print("Key", key)
end
print(tbl[1])
table 不会固定长度大小,有新数据添加时 table 长度会自动增长,没初始的 table 都是 nil。
a3 = {}
for i = 1, 10 do
a3[i] = i
end
a3["key"] = "val"
print(a3["key"])
print(a3["none"])
print(a3[1])
在 Lua 中,函数是被看作是"第一类值(First-Class Value)",函数可以存在变量里:
function factorial1(n)
if n == 0 then
return 1
else
return n * factorial1(n - 1)
end
end
print(factorial1(5))
factorial2 = factorial1
print(factorial2(5))
function 可以以匿名函数(anonymous function)的方式通过参数传递:
function testFun(tab,fun)
for k,v in pairs(tab) do
print(fun(k,v))
end
end
tab = {key1 = "val1",key2 = "val2"}
testFun(tab,
function(key,val)
return key .. "=" .. val
end
)
在 Lua 里,最主要的线程是协同程序(coroutine)。它跟线程(thread)差不多,拥有自己独立的栈、局部变量和指令指针,可以跟其他协同程序共享全局变量和其他大部分东西。
线程跟协程的区别:线程可以同时多个运行,而协程任意时刻只能运行一个,并且处于运行状态的协程只有被挂起(suspend)时才会暂停。
userdata 是一种用户自定义数据,用于表示一种由应用程序或 C/C++ 语言库所创建的类型,可以将任意 C/C++ 的任意数据类型的数据(通常是 struct 和 指针)存储到 Lua 变量中调用。
变量在使用前,需要在代码中进行声明,即创建该变量。
编译程序执行代码之前编译器需要知道如何给语句变量开辟存储区,用于存储变量的值。
Lua 变量有三种类型:全局变量、局部变量、表中的域。
Lua 中的变量全是全局变量,哪怕是语句块或是函数里,除非用 local 显式声明为局部变量。
局部变量的作用域为从声明位置开始到所在语句块结束。
变量的默认值均为 nil。
赋值是改变一个变量的值和改变表域的最基本的方法。
Lua 可以对多个变量同时赋值,变量列表和值列表的各个元素用逗号分开,赋值语句右边的值会依次赋给左边的变量。
遇到赋值语句Lua会先计算右边所有的值然后再执行赋值操作
当变量个数和值的个数不一致时,Lua会一直以变量个数为基础采取以下策略:
a. 变量个数 > 值的个数 按变量个数补足nil
b. 变量个数 < 值的个数 多余的值会被忽略
a,b,c = 0,1
print(a,b,c)
a,b = a+1,b+1,b+2
print(a,b)
a,b,c = 0
print(a,b,c)
上面最后一个例子是一个常见的错误情况,注意:如果要对多个变量赋值必须依次对每个变量赋值。
多值赋值经常用来交换变量,或将函数调用返回给变量:
a, b = f()
f()返回两个值,第一个赋给a,第二个赋给b。
应该尽可能的使用局部变量,有两个好处:
Lua 编程语言中 while 循环语法:
while(condition)
do
statements
end
statements(循环体语句) 可以是一条或多条语句,condition(条件) 可以是任意表达式,在 condition(条件) 为 true 时执行循环体语句。
代码:
a = 10
while(a<20)
do
print(a)
a = a + 1
end
Lua 编程语言中 for语句有两大类::
数值for循环
泛型for循环
Lua 编程语言中数值 for 循环语法格式:
for var=exp1,exp2,exp3 do
<执行体>
end
var 从 exp1 变化到 exp2,每次变化以 exp3 为步长递增 var,并执行一次 “执行体”。exp3 是可选的,如果不指定,默认为1。
for i=1,f(x) do
print(i)
end
for i=10,1,-1 do
print(i)
end
for的三个表达式在循环开始前一次性求值,以后不再进行求值。比如上面的f(x)只会在循环开始前执行一次,其结果用在后面的循环中。
代码:
function f(x)
print("function")
return x*2
end
for i=1,f(5) do print(i)
end
泛型 for 循环通过一个迭代器函数来遍历所有值,类似 java 中的 foreach 语句。
Lua 编程语言中泛型 for 循环语法格式:
a = {"one", "two", "three"}
for i, v in ipairs(a) do
print(i, v)
end
i是数组索引值,v是对应索引的数组元素值。ipairs是Lua提供的一个迭代器函数,用来迭代数组。
days = {"Sunday","Monday","Tuesday","Wednesday","Thursday","Friday","Saturday"}
for i,v in ipairs(days) do
print(v)
end
Lua 编程语言中 repeat…until 循环语句不同于 for 和 while循环,for 和 while 循环的条件语句在当前循环执行开始时判断,而 repeat…until 循环的条件语句在当前循环结束后判断。
Lua 编程语言中 repeat…until 循环语法格式:
repeat
statements
until( condition )
循环条件判断语句(condition)在循环体末尾部分,所以在条件进行判断前循环体都会执行一次。
如果条件判断语句(condition)为 false,循环会重新开始执行,直到条件判断语句(condition)为 true 才会停止执行。
代码:
a = 10
repeat
print(a)
a = a + 1
until(a > 15)
Lua 编程语言中允许循环中嵌入循环。以下实例演示了 Lua 循环嵌套的应用。
Lua 编程语言中 for 循环嵌套语法格式:
for init,max/min value, increment
do
for init,max/min value, increment
do
statements
end
statements
end
Lua 编程语言中 while 循环嵌套语法格式:
while(condition)
do
while(condition)
do
statements
end
statements
end
Lua 编程语言中 repeat…until 循环嵌套语法格式:
repeat
statements
repeat
statements
until( condition )
until( condition )
除了以上同类型循环嵌套外,我们还可以使用不同的循环类型来嵌套,如 for 循环体中嵌套 while 循环。
代码:
j = 2
for i = 2,10 do
for j = 2,(i/j),2 do
if(not(i%j)) then
break
end
if(j > (i / j)) then
print(i)
end
end
end
Lua if 语句 由一个布尔表达式作为条件判断,其后紧跟其他语句组成。
if 语句语法格式如下:
if(布尔表达式)
then
--[ 在布尔表达式为 true 时执行的语句 --]
end
在布尔表达式为 true 时会if中的代码块会被执行,在布尔表达式为 false 时,紧跟在 if 语句 end 之后的代码会被执行。
Lua认为false和nil为假,true 和非nil为真。要注意的是Lua中 0 为 true。
代码:
a = 10
if(a < 20) then
print("<")
end
print(a)
if 语句可以与 else 语句搭配使用, 在 if 条件表达式为 false 时执行 else 语句代码块。
if…else 语句语法格式如下:
if(布尔表达式)
then
--[ 布尔表达式为 true 时执行该语句块 --]
else
--[ 布尔表达式为 false 时执行该语句块 --]
end
代码:
a = 100
if(a == 10) then
print("1")
elseif(a == 20) then
print("2")
else
print("3")
end
print(a)
if 嵌套语句语法格式如下
if( 布尔表达式 1)
then
--[ 布尔表达式 1 为 true 时执行该语句块 --]
if(布尔表达式 2)
then
--[ 布尔表达式 2 为 true 时执行该语句块 --]
end
end
代码:
a = 100
b = 200
if(a == 100) then
if(b == 200)
then
print("200")
end
end
print(a)
print(b)
函数是对语句和表达式进行抽象的主要方法。既可以用来处理一些特殊的工作,也可以用来计算一些值。
函数主要有两种用途:
Lua 编程语言函数定义格式如下:
optional_function_scope function function_name( argument1, argument2, argument3..., argumentn)
function_body
return result_params_comma_separated
end
optional_function_scope: 该参数是可选的指定函数是全局函数还是局部函数,未设置该参数默认为全局函数,如果你需要设置函数为局部函数需要使用关键字 local。
function_name: 指定函数名称。
argument1, argument2, argument3…, argumentn: 函数参数,多个参数以逗号隔开,函数也可以不带参数。
function_body: 函数体,函数中需要执行的代码语句块。
result_params_comma_separated: 函数返回值,Lua语言函数可以返回多个值,每个值以逗号隔开。
代码:
function max(num1,num2)
if(num1 > num2) then
result = num1;
else
result = num2;
end
return result
end
print(max(10,4))
print(max(5,6))
Lua 中可以将函数作为参数传递给函数
代码:
myprint = function(param)
print(param)
end
function add(num1,num2,functionPrint)
result = num1 + num2
functionPrint(result)
end
myprint(10)
add(2,5,myprint)
Lua函数可以返回多个结果值
Lua函数中,在return后列出要返回的值的列表即可返回多值
代码:
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
print(maximum({8,10,23,12,5}))
Lua 函数可以接受可变数目的参数,和 C 语言类似,在函数参数列表中使用三点 ...
表示函数有可变的参数。
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))
可以将可变参数赋值给一个变量。
function average(...)
result = 0
local arg = {...}
for i,v in ipairs(arg) do
result = result + v
end
print(#arg)
return result/#arg
end
print(average(10,5,3,4,5,6))
也可以通过 select(“#”,…) 来获取可变参数的数量:
function average(...)
result = 0
local arg = {...}
for i,v in ipairs(arg) do
result = result + v
end
print(select("#",...))
return result/select("#",...)
end
print(average(10,5,3,4,5,6))
有时候可能需要几个固定参数加上可变参数,固定参数必须放在变长参数之前:
function fwrite(fmt,...)
return io.write(string.format(fmt,...))
end
fwrite("runoob\n")
fwrite("%d%d\n",1,2)
通常在遍历变长参数的时候只需要使用 {…},然而变长参数可能会包含一些 nil,那么就可以用 select 函数来访问变长参数了:select(‘#’, …) 或者 select(n, …)
调用 select 时,必须传入一个固定实参 selector(选择开关) 和一系列变长参数。如果 selector 为数字 n,那么 select 返回参数列表中从索引 n 开始到结束位置的所有参数列表,否则只能为字符串 #,这样 select 返回变长参数的总数。
function f(...)
a = select(3,...)
print(a)
print(select(3,...))
end
f(0,1,2,3,4,5)
do
function foo(...)
for i = 1,select('#',...) do
local arg = select(i,...)
print("arg",arg)
end
end
foo(1,2,3,4)
end
运算符是一个特殊的符号,用于告诉解释器执行特定的数学或逻辑运算。Lua提供了以下几种运算符类型:
下表列出了 Lua 语言中的常用算术运算符
|操作符|描述|实例|
|—|—|—|—|
|+|加法|A + B 输出结果 30|
|-|减法|A - B 输出结果 -10|
|*|乘法|A * B 输出结果 200|
|/|除法|B / A 输出结果 2|
|%|取余|B % A 输出结果 0|
||乘幂|A2 输出结果 100|
|-|负号|-A 输出结果 -10|
|//|整除运算符(>=lua5.3)|5//2 输出结果 2|
代码:
a = 21 b = 10 c = a + b print(c) c = a - b print(c) c = a * b print(c) c = a / b print(c) c = a % b print(c) c = a ^ 2 print(c) c = -a print(c)
下表列出了 Lua 语言中的常用关系运算符,设定 A 的值为10,B 的值为 20:
操作符 | 描述 | 实例 |
---|---|---|
== | 等于,检测两个值是否相等,相等返回 true,否则返回 false | (A == B) 为 false。 |
~= | 不等于,检测两个值是否相等,不相等返回 true,否则返回 false | (A ~= B) 为 true。 |
> | 大于,如果左边的值大于右边的值,返回 true,否则返回 false | (A > B) 为 false。 |
< | 小于,如果左边的值大于右边的值,返回 false,否则返回 true | (A < B) 为 true。 |
>= | 大于等于,如果左边的值大于等于右边的值,返回 true,否则返回 false | (A >= B) 返回 false。 |
<= | 小于等于, 如果左边的值小于等于右边的值,返回 true,否则返回 false | (A <= B) 返回 true。 |
代码:
a = 21 b = 10 if(a == b) then print("=") else print("!=") end if(a ~= b) then print("=") else print("!=") end if(a < b) then print("<") else print(">=") end if(a > b) then print(">") else print("<=") end a = 5 b = 20 if(a <= b) then print("<=") end if(b >= a) then print(">=") end
下表列出了 Lua 语言中的常用逻辑运算符,设定 A 的值为 true,B 的值为 false:
操作符 | 描述 | 实例 |
---|---|---|
and | 逻辑与操作符。 若 A 为 false,则返回 A,否则返回 B。 | (A and B) 为 false。 |
or | 逻辑或操作符。 若 A 为 true,则返回 A,否则返回 B。 | (A or B) 为 true。 |
not | 逻辑非操作符。与逻辑运算结果相反,如果条件为 true,逻辑非为 false。 | not(A and B) 为 true。 |
a = true b = true if(a and b) then print("true") end if(a or b) then print("true") end a = false b = true if(a and b) then print("true") else print("flase") end if(not(a and b)) then print("true") else print("false") end
下表列出了 Lua 语言中的连接运算符与计算表或字符串长度的运算符:
操作符 | 描述 | 实例 |
---|---|---|
… | 连接两个字符串 | a…b ,其中 a 为 "Hello " , b 为 “World”, 输出结果为 “Hello World”。 |
# | 一元运算符,返回字符串或表的长度。 | #“Hello” 返回 5 |
代码:
a = "hello"
b = "world"
print(a..b)
print(#b)
print(#"Test")
print(#"www.Aubyn11.com")
从高到低的顺序:
^
not - (unary)
* / %
+ -
..
< > <= >= ~= ==
and
or
除了 ^
和 ..
外所有的二元运算符都是左连接的。
a+i < b/2+1 <--> (a+i) < ((b/2)+1)
5+x^2*8 <--> 5+((x^2)*8)
a < y and y <= z <--> (a < y) and (y <= z)
-x^2 <--> -(x^2)
x^y^z <--> x^(y^z)
代码:
a = 20 b = 10 c = 15 d = 5 e = (a + b) * c / d print(e) e = ((a + b) * c) / d print(e) e = (a + b) * (c / d) print(e) e = a + (b * c) / d print(e)
字符串或串(String)是由数字、字母、下划线组成的一串字符。
在 Lua 中,字符串是一种基本的数据类型,用于存储文本数据。
Lua 中的字符串可以包含任意字符,包括字母、数字、符号、空格以及其他特殊字符。
Lua 语言中字符串可以使用以下三种方式来表示:
local str1 = 'This is a string.'
local str2 = "This is also a string."
local str = "Hello, "
str = str .. "World!"
print(str)
local multilineString = [[
This is a multiline string.
It can contain multiple lines of text.
No need for escape characters.
]]
print(multilineString)
代码:
string1 = "Lua"
print(string1)
string2 = 'runoob.com'
print(string2)
string3 = [[Lua]]
print(string3)
字符串长度计算
在 Lua 中,要计算字符串的长度(即字符串中字符的个数),可以使用 string.len
函数,string.len
函数用于计算只包含 ASCII 字符串的长度。
代码:
local myString = "Hello, runoob!"
local length = string.len(myString)
print(length)
转义字符用于表示不能直接显示的字符,比如后退键,回车键等,如在字符串转换双引号可以使用 \
。
所有的转义字符和所对应的意义:
转义字符 | 意义 | ASCII码值(十进制) |
---|---|---|
\a | 响铃(BEL) | 007 |
\b | 退格(BS) ,将当前位置移到前一列 | 008 |
\f | 换页(FF),将当前位置移到下页开头 | 012 |
\n | 换行(LF) ,将当前位置移到下一行开头 | 010 |
\r | 回车(CR) ,将当前位置移到本行开头 | 013 |
\t | 水平制表(HT) (跳到下一个TAB位置) | 009 |
\v | 垂直制表(VT) | 011 |
|代表一个反斜线字符’‘’ | 092 | |
’ | 代表一个单引号(撇号)字符 | 039 |
" | 代表一个双引号字符 | 034 |
\0 | 空字符(NULL) | 000 |
\ddd | 1到3位八进制数所代表的任意字符 | 三位八进制 |
\xhh | 1到2位十六进制所代表的任意字符 | 二位十六进制 |
Lua 提供了很多的方法来支持字符串的操作:
序号 | 方法 & 用途 |
---|---|
1 | string.upper(argument): 字符串全部转为大写字母。 |
2 | string.lower(argument): 字符串全部转为小写字母。 |
3 | string.gsub(mainString,findString,replaceString,num) 在字符串中替换。 mainString 为要操作的字符串, findString 为被替换的字符,replaceString 要替换的字符,num 替换次数(可以忽略,则全部替换) |
4 | string.find (str, substr, [init, [plain]]) 在一个指定的目标字符串 str 中搜索指定的内容 substr,如果找到了一个匹配的子串,就会返回这个子串的起始索引和结束索引,不存在则返回 nil。 init 指定了搜索的起始位置,默认为 1,可以一个负数,表示从后往前数的字符个数。plain 表示是否使用简单模式,默认为 false,true 只做简单的查找子串的操作,false 表示使用使用正则模式匹配。 |
5 | string.reverse(arg) 字符串反转 |
6 | string.format(…) 返回一个类似printf的格式化字符串 |
7 | string.char(arg) 和 string.byte(arg[,int]) char 将整型数字转成字符并连接, byte 转换字符为整数值(可以指定某个字符,默认第一个字符)。 |
8 | string.len(arg) 计算字符串长度。 |
9 | string.rep(string, n) 返回字符串string的n个拷贝 |
10 | … 链接两个字符串 |
11 | string.gmatch(str, pattern) 返回一个迭代器函数,每一次调用这个函数,返回一个在字符串 str 找到的下一个符合 pattern 描述的子串。如果参数 pattern 描述的字符串没有找到,迭代函数返回nil。 |
12 | string.match(str, pattern, init) string.match()只寻找源字串str中的第一个配对. 参数init可选, 指定搜寻过程的起点, 默认为1。 在成功配对时, 函数将返回配对表达式中的所有捕获结果; 如果没有设置捕获标记, 则返回整个配对字符串. 当没有成功的配对时, 返回nil。 |
字符串截取使用 sub() 方法。
string.sub() 用于截取字符串,原型为:
string.sub(s, i [, j])
参数说明:
代码:
local sourcestr = "prefix--runoobgoogletaobao--suffix" print(string.format("%q",sourcestr)) local f_sub = string.sub(sourcestr,4,15) print(string.format("%q",f_sub)) local s_sub = string.sub(sourcestr,1,8) print(string.format("%q",s_sub)) //获取最后10个字符 local t_sub = string.sub(sourcestr,-10) print(string.format("%q",t_sub)) //索引越界,输出原字符串 local fo_sub = string.sub(sourcestr,-100) print(string.format("%q",fo_sub))
代码:
string1 = "Lua";
print(string.upper(string1))
print(string.lower(string1))
代码:
string = "Lua Tutorial"
print(string.find(string,"Tutorial"))
reversedString = string.reverse(string)
print(reversedString)
Lua 提供了 string.format() 函数来生成具有特定格式的字符串, 函数的第一个参数是格式 , 之后是对应格式中每个代号的各种数据。
由于格式字符串的存在, 使得产生的长字符串可读性大大提高了。这个函数的格式很像 C 语言中的 printf()。
以下实例演示了如何对字符串进行格式化操作:
格式字符串可能包含以下的转义码:
为进一步细化格式, 可以在%号后添加参数. 参数将以如下的顺序读入:
代码:
string1 = "Lua"
string2 = "Tutorial"
num1 = 10
num2 = 20
print(string.format("基本格式化 %s %s",string1,string2))
date = 2
month = 1
year = 2014
print(string.format("日期格式化 %02d/%02d/%04d",date,month,year))
print(string.format("%.4f",1/3))
其他案例:
string.format("%c", 83) -- 输出S
string.format("%+d", 17.0) -- 输出+17
string.format("%05d", 17) -- 输出00017
string.format("%o", 17) -- 输出21
string.format("%u", 3.14) -- 输出3
string.format("%x", 13) -- 输出d
string.format("%X", 13) -- 输出D
string.format("%e", 1000) -- 输出1.000000e+03
string.format("%E", 1000) -- 输出1.000000E+03
string.format("%6.3f", 13) -- 输出13.000
string.format("%q", "One\nTwo") -- 输出"One\
-- Two"
string.format("%s", "monkey") -- 输出monkey
string.format("%10s", "monkey") -- 输出 monkey
string.format("%5.3s", "monkey") -- 输出 mon
代码:
print(string.byte("Lua"))
print(string.byte("Lua",3))
print(string.byte("Lua",-1))
print(string.byte("Lua",2))
print(string.byte("Lua",-2))
print(string.char(97))
代码:
string1 = "www."
string2 = "Aubyn"
string3 = ".com"
print(string1..string2..string3)
print(string.len(string2))
repeatedString = string.rep(string2,2)
print(repeatedString)
Lua 中的匹配模式直接用常规的字符串来描述。 它用于模式匹配函数 string.find, string.gmatch, string.gsub, string.match。
你还可以在模式串中使用字符类。
字符类指可以匹配一个特定字符集合内任何字符的模式项。
s = "Deadline is 30/05/1999,firm"
date = "%d%d/%d%d/%d%d%d%d"
print(string.sub(s,string.find(s,date)))
下面的表列出了Lua支持的所有字符类:
单个字符(除 ^$()%.[]*±? 外): 与该字符自身配对
当上述的字符类用大写书写时, 表示与非此字符类的任何字符配对. 例如, %S表示与任何非空白字符配对.例如,'%A’非字母的字符:
print(string.gsub("hello,up-down!","%A","."))
数字4不是字符串结果的一部分,他是gsub返回的第二个结果,代表发生替换的次数。
在模式匹配中有一些特殊字符,他们有特殊的意义,Lua中的特殊字符如下:
( ) . % + - * ? [ ^ $
‘%’ 用作特殊字符的转义字符,因此 ‘%.’ 匹配点;‘%%’ 匹配字符 ‘%’。转义字符 '%'不仅可以用来转义特殊字符,还可以用于所有的非字母的字符。
模式条目可以是:
模式:
模式 指一个模式条目的序列。 在模式最前面加上符号 ‘^’ 将锚定从字符串的开始处做匹配。 在模式最后面加上符号 ‘KaTeX parse error: Expected group after '^' at position 24: …锚定到字符串的结尾。 如果 '^̲' 和 '’ 出现在其它位置,它们均没有特殊含义,只表示自身。
捕获:
模式可以在内部用小括号括起一个子模式; 这些子模式被称为 捕获物。 当匹配成功时,由 捕获物 匹配到的字符串中的子串被保存起来用于未来的用途。 捕获物以它们左括号的次序来编号。 例如,对于模式 “(a*(.)%w(%s*))” , 字符串中匹配到 “a*(.)%w(%s*)” 的部分保存在第一个捕获物中 (因此是编号 1 ); 由 “.” 匹配到的字符是 2 号捕获物, 匹配到 “%s*” 的那部分是 3 号。
作为一个特例,空的捕获 () 将捕获到当前字符串的位置(它是一个数字)。 例如,如果将模式 “()aa()” 作用到字符串 “flaaap” 上,将产生两个捕获物: 3 和 5 。
数组,就是相同数据类型的元素按一定顺序排列的集合,可以是一维数组和多维数组。
在 Lua 中,数组不是一种特定的数据类型,而是一种用来存储一组值的数据结构。
实际上,Lua 中并没有专门的数组类型,而是使用一种被称为 “table” 的数据结构来实现数组的功能。
Lua 数组的索引键值可以使用整数表示,数组的大小不是固定的。
在 Lua 索引值是以 1 为起始,但也可以指定 0 开始。
一维数组是最简单的数组,其逻辑结构是线性表。
使用索引访问数组元素:
local myArray = {10,20,30,40,50}
print(myArray[1])
print(myArray[3])
要计算数组的长度(即数组中元素的个数),可以使用 #
操作符:
local myArray = {10,20,30,40,50}
local length = #myArray
print(length)
一维数组可以用 for 循环出数组中的元素,如下实例:
local myArray = {10,20,30,40,50}
for i = 1,#myArray do
print(myArray[i])
end
lua 索引默认从 1
开始:
array = {"Lua","Tutorial"}
for i = 0,2 do
print(array[i])
end
可以使用整数索引来访问数组元素,如果指定的索引没有值则返回 nil。
还可以以负数为数组索引值:
array = {}
for i = -2,2 do
array[i] = i * 2
end
for i = -2,2 do
print(array[i])
end
也可以修改数组中元素:
local myArray = {10,20,30,40,50}
myArray[2] = 25
for i = 1,#myArray do
print(myArray[i])
end
也可以向数组中添加元素:
local myArray = {10,20,30,40,50}
myArray[#myArray + 1] = 60
for i = 1,#myArray do
print(myArray[i])
end
也可以删除数组中元素:
local myArray = {10,20,30,40,50}
table.remove(myArray,3)
for i = 1,#myArray do
print(myArray[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
不同索引键的三行三列阵列多维数组:
array = {}
maxRows = 3
maxColumns = 3
for row = 1,maxRows do
for col = 1,maxColumns do
array[row * maxColumns + col] = row * col
end
end
for row = 1,maxRows do
for col = 1,maxColumns do
print(array[row * maxColumns + col])
end
end
数组设定了指定的索引值,这样可以避免出现 nil 值,有利于节省内存空间。
迭代器(iterator)是一种对象,它能够用来遍历标准模板库容器中的部分或全部元素,每个迭代器对象代表容器中的确定的地址。
在 Lua 中迭代器是一种支持指针类型的结构,它可以遍历集合的每一个元素。
泛型 for 在自己内部保存迭代函数,实际上它保存三个值:迭代函数、状态常量、控制变量。
泛型 for 迭代器提供了集合的 key/value 对,语法格式如下:
for k, v in pairs(t) do
print(k, v)
end
上面代码中,k, v为变量列表;pairs(t)为表达式列表。
示例:
array = {"Google","Runoob"}
for key,value in pairs(array) do
print(key,value)
end
ipairs与pairs的区别,ipairs的时候遍历到第一个nil就会终止,pairs会直接遍历到最后
泛型 for 的执行过程:
在Lua中常常使用函数来描述迭代器,每次调用该函数就返回集合的下一个元素。Lua 的迭代器包含以下两种类型:
无状态的迭代器是指不保留任何状态的迭代器,因此在循环中我们可以利用无状态迭代器避免创建闭包花费额外的代价。
每一次迭代,迭代函数都是用两个变量(状态常量和控制变量)的值作为参数被调用,一个无状态的迭代器只利用这两个值可以获取下一个元素。
这种无状态迭代器的典型的简单的例子是 ipairs,它遍历数组的每一个元素,元素的索引需要是数值。
数字 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
迭代的状态包括被遍历的表(循环过程中不会改变的状态常量)和当前的索引下标(控制变量),ipairs 和迭代函数都很简单,在 Lua 中可以这样实现:
function iter (a, i)
i = i + 1
local v = a[i]
if v then
return i, v
end
end
function ipairs (a)
return iter, a, 0
end
当 Lua 调用 ipairs(a) 开始循环时,他获取三个值:迭代函数 iter、状态常量 a、控制变量初始值 0;然后 Lua 调用 iter(a,0) 返回 1, a[1](除非 a[1]=nil);第二次迭代调用 iter(a,1) 返回 2, a[2]……直到第一个 nil 元素。
很多情况下,迭代器需要保存多个状态信息而不是简单的状态常量和控制变量,最简单的方法是使用闭包,还有一种方法就是将所有的状态信息封装到 table 内,将 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
table 是 Lua 的一种数据结构用来帮助创建不同的数据类型,如:数组、字典等。
Lua table 使用关联型数组,可以用任意类型的值来作数组的索引,但这个值不能是 nil。
Lua table 是不固定大小的,可以根据自己需要进行扩容。
Lua也是通过table来解决模块(module)、包(package)和对象(Object)的。 例如string.format表示使用"format"来索引table string。
构造器是创建和初始化表的表达式。表是Lua特有的功能强大的东西。最简单的构造函数是{},用来创建一个空表。可以直接初始化数组:
mytable = {}
mytable[1] = "Lua"
mytable = nil
当为 table a 并设置元素,然后将 a 赋值给 b,则 a 与 b 都指向同一个内存。如果 a 设置为 nil ,则 b 同样能访问 table 的元素。如果没有指定的变量指向a,Lua的垃圾回收机制会清理相对应的内存。
以下实例演示了以上的描述情况:
mytable = {} print("mytable 的类型是 ",type(mytable)) mytable[1]= "Lua" mytable["wow"] = "修改前" print("mytable 索引为 1 的元素是 ", mytable[1]) print("mytable 索引为 wow 的元素是 ", mytable["wow"]) alternatetable = mytable print("alternatetable 索引为 1 的元素是 ", alternatetable[1]) print("alternatetable 索引为 wow 的元素是 ", alternatetable["wow"]) alternatetable["wow"] = "修改后" print("mytable 索引为 wow 的元素是 ", mytable["wow"]) alternatetable = nil print("alternatetable 是 ", alternatetable) print("mytable 索引为 wow 的元素是 ", mytable["wow"]) mytable = nil print("mytable 是 ", mytable)
以下列出了 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进行升序排序。 |
可以使用 concat() 输出一个列表中元素连接成的字符串:
fruits = {"banana","orange","apple"}
print("连接后的字符串 ",table.concat(fruits))
print("连接后的字符串 ",table.concat(fruits,", "))
print("连接后的字符串 ",table.concat(fruits,", ", 2,3))
代码:
fruits = {"banana","orange","apple"}
table.insert(fruits,"mango")
print("索引为 4 的元素为 ",fruits[4])
table.insert(fruits,2,"grapes")
print("索引为 2 的元素为 ",fruits[2])
print("最后一个元素为 ",fruits[5])
table.remove(fruits)
print("移除后最后一个元素为 ",fruits[5])
代码:
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
table.maxn 在 Lua5.2 之后该方法已经不存在了,定义 table_maxn 方法来实现。
function table_maxn(t)
local mn=nil;
for k, v in pairs(t) do
if(mn==nil) then
mn=v
end
if mn < v then
mn = v
end
end
return mn
end
tbl = {[1] = 2, [2] = 6, [3] = 34, [26] =5}
print("tbl 最大值:", table_maxn(tbl))
print("tbl 长度 ", #tbl)
注意:
当获取 table 的长度的时候无论是使用 # 还是 table.getn 其都会在索引中断的地方停止计数,而导致无法正确取得 table 的长度。
可以使用以下方法来代替:
function table_leng(t)
local leng=0
for k, v in pairs(t) do
leng=leng+1
end
return leng;
end
模块类似于一个封装库,从 Lua 5.1 开始,Lua 加入了标准的模块管理机制,可以把一些公用的代码放在一个文件里,以 API 接口的形式在其他地方调用,有利于代码的重用和降低代码耦合度。
Lua 的模块是由变量、函数等已知元素组成的 table,因此创建一个模块很简单,就是创建一个 table,然后把需要导出的常量、函数放入其中,最后返回这个 table 就行。以下为创建自定义模块 module.lua,文件代码格式如下:
module = {} module.constant = "这是一个常量" function module.func1(){ io.write("这是一个共有函数\n") end local function fun2() print("这是一个私有函数") end function module.func3() func2() end return module
由上可知,模块的结构就是一个 table 的结构,因此可以像操作调用 table 里的元素那样来操作调用模块里的常量或函数。
上面的 func2 声明为程序块的局部变量,即表示一个私有函数,因此是不能从外部访问模块里的这个私有函数,必须通过模块里的公有函数来调用.
Lua提供了一个名为require的函数用来加载模块。要加载一个模块,只需要简单地调用就可以了。例如:
require("<模块名>")
或者:
require "<模块名>"
执行 require 后会返回一个由模块常量或函数组成的 table,并且还会定义一个包含该 table 的全局变量。
require ("12_1-module")
print(module.constant)
module.func3()
或者给加载的模块定义一个别名变量,方便调用:
local m = require("12_1-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;;"
文件路径以 “;” 号分隔,最后的 2 个 “;;” 表示新加的路径后面加上原来的默认路径。
接着,更新环境变量参数,使之立即生效。
source ~/.profile
这时假设 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
那么调用 require(“module”) 时就会尝试打开以下文件目录去搜索目标。
/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
如果找过目标文件,则会调用 package.loadfile 来加载模块。否则,就会去找 C 程序库。
搜索的文件路径是从全局变量 package.cpath 获取,而这个变量则是通过环境变量 LUA_CPATH 来初始。
搜索的策略跟上面的一样,只不过现在换成搜索的是 so 或 dll 类型的文件。如果找得到,那么 require 就会通过 package.loadlib 来加载它。
Lua和C是很容易结合的,使用 C 为 Lua 写包。
与Lua中写包不同,C包在使用以前必须首先加载并连接,在大多数系统中最容易的实现方式是通过动态连接库机制。
Lua在一个叫loadlib的函数内提供了所有的动态连接的功能。这个函数有两个参数:库的绝对路径和初始化函数。所以典型的调用的例子如下:
local path = "/usr/local/lua/lib/libluasocket.so"
local f = loadlib(path, "luaopen_socket")
loadlib 函数加载指定的库并且连接到 Lua,然而它并不打开库(也就是说没有调用初始化函数),反之他返回初始化函数作为 Lua 的一个函数,这样就可以直接在Lua中调用他。
如果加载动态库或者查找初始化函数时出错,loadlib 将返回 nil 和错误信息。我们可以修改前面一段代码,使其检测错误然后调用初始化函数:
local path = "/usr/local/lua/lib/libluasocket.so"
-- 或者 path = "C:\\windows\\luasocket.dll",这是 Window 平台下
local f = assert(loadlib(path, "luaopen_socket"))
f() -- 真正打开库
一般情况下期望二进制的发布库包含一个与前面代码段相似的 stub 文件,安装二进制库的时候可以随便放在某个目录,只需要修改 stub 文件对应二进制库的实际路径即可。
将 stub 文件所在的目录加入到 LUA_PATH,这样设定后就可以使用 require 函数加载 C 库了。
在 Lua table 中可以访问对应的 key 来得到 value 值,但是却无法对两个 table 进行操作(比如相加)。
因此 Lua 提供了元表(Metatable),允许改变 table 的行为,每个行为关联了对应的元方法。
例如,使用元表可以定义 Lua 如何计算两个 table 的相加操作 a+b。
当 Lua 试图对两个表进行相加时,先检查两者之一是否有元表,之后检查是否有一个叫 __add 的字段,若找到,则调用对应的值。 __add 等即时字段,其对应的值(往往是一个函数或是 table)就是"元方法"。
有两个很重要的函数来处理元表:
以下实例演示了如何对指定的表设置元表:
mytable = {}
mymetatable = {}
setmetatable(mytable,mymetatable)
以上代码也可以直接写成一行:
mytable = setmetatable({},{})
以下为返回对象元表:
getmetatable(mytable)
这是 metatable 最常用的键。
当你通过键来访问 table 的时候,如果这个键没有值,那么Lua就会寻找该table的metatable(假定有metatable)中的__index 键。如果__index包含一个表格,Lua会在表格中查找相应的键。
可以在使用 lua 命令进入交互模式查看:
$ lua
Lua 5.3.0 Copyright (C) 1994-2015 Lua.org, PUC-Rio
> other = { foo = 3 }
> t = setmetatable({}, { __index = other })
> t.foo
3
> t.bar
nil
如果__index包含一个函数的话,Lua就会调用那个函数,table和键会作为参数传递给函数。
__index 元方法查看表中元素是否存在,如果不存在,返回结果为 nil;如果存在则由 __index 返回结果。
mytable = setmetatable({key1 = "value1"}, {
__index = function(mytable, key)
if key == "key2" then
return "metatablevalue"
else
return nil
end
end
})
print(mytable.key1,mytable.key2)
实例解析:
我们可以将以上代码简单写成:
mytable = setmetatable({key1 = "value1"}, { __index = { key2 = "metatablevalue" } })
print(mytable.key1,mytable.key2)
总结
Lua 查找一个表元素时的规则,其实就是如下 3 个步骤:
__newindex 元方法用来对表更新,__index则用来对表访问 。
当给表的一个缺少的索引赋值,解释器就会查找__newindex 元方法:如果存在则调用这个函数而不进行赋值操作。
以下实例演示了 __newindex 元方法的应用:
mymetatable = {}
mytable = setmetatable({key1 = "value1"}, { __newindex = mymetatable })
print(mytable.key1)
mytable.newkey = "新值2"
print(mytable.newkey,mymetatable.newkey)
mytable.key1 = "新值1"
print(mytable.key1,mymetatable.key1)
以上实例中表设置了元方法 __newindex,在对新索引键(newkey)赋值时(mytable.newkey = “新值2”),会调用元方法,而不进行赋值。而如果对已存在的索引键(key1),则会进行赋值,而不调用元方法 __newindex。
以下实例使用了 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)
rawset 是 Lua 中的一个函数,用于直接设置表中的键值,而不触发元表中的任何元方法。
这个函数接受三个参数:表、键和值。它的用法如下:
rawset(table, key, value)
其中:
rawset 不会触发任何元表中的 __newindex 元方法。这意味着,即使表设置了元表,也不会执行元表中的任何对应方法。通常情况下,rawset 用于直接对表进行赋值,而不考虑元表中的影响。
代码:
function table_maxn(t) local mn = 0 for k, v in pairs(t) do if mn < k then mn = k end end return mn end 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
__add 键包含在元表中,并进行相加操作。 表中对应的操作列表如下:(注意:__是两个下划线)
模式 | 描述 |
---|---|
__add | 对应的运算符 ‘+’. |
__sub | 对应的运算符 ‘-’. |
__mul | 对应的运算符 ‘*’. |
__div | 对应的运算符 ‘/’. |
__mod | 对应的运算符 ‘%’. |
__unm | 对应的运算符 ‘-’. |
__concat | 对应的运算符 ‘…’. |
__eq | 对应的运算符 ‘==’. |
__lt | 对应的运算符 ‘<’. |
__le | 对应的运算符 ‘<=’. |
__call 元方法在 Lua 调用一个值时调用。以下实例演示了计算表中元素的和:
function table_maxn(t) local mn = 0 for k, v in pairs(t) do if mn < k then mn = k end end return mn end 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))
__tostring 元方法用于修改表的输出行为。以下实例自定义了表的输出内容:
mytable = setmetatable({ 10, 20, 30 }, {
__tostring = function(mytable)
sum = 0
for k, v in pairs(mytable) do
sum = sum + v
end
return "表所有元素的和为 " .. sum
end
})
print(mytable)
Lua 协同程序(coroutine)与线程比较类似:拥有独立的堆栈,独立的局部变量,独立的指令指针,同时又与其它协同程序共享全局变量和其它大部分东西。
协同程序可以理解为一种特殊的线程,可以暂停和恢复其执行,从而允许非抢占式的多任务处理。
协同是非常强大的功能,但是用起来也很复杂。
同程序由 coroutine 模块提供支持。
使用协同程序,可以在函数中使用 coroutine.create 创建一个新的协同程序对象,并使用 coroutine.resume 启动它的执行。协同程序可以通过调用 coroutine.yield 来主动暂停自己的执行,并将控制权交还给调用者。
方法 | 描述 |
---|---|
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的时候,就是返回一个 coroutine 的线程号 |
代码:
function foo()
print("协同程序foo开始执行")
local value = coroutine.yield("暂停foo的执行")
print("协同程序foo恢复执行,传入的值为:" .. tostring(value))
print("协同程序foo结束执行")
end
local co = coroutine.create(foo)
local status,result = coroutine.resume(co)
print(result)
status,result = coroutine.resume(co,42)
print(result)
以上实例中,定义了一个名为 foo 的函数作为协同程序。在函数中,我们使用 coroutine.yield 暂停了协同程序的执行,并返回了一个值
在主程序中,使用 coroutine.create 创建了一个协同程序对象,并使用 coroutine.resume 启动了它的执行。
在第一次调用 coroutine.resume 后,协同程序执行到 coroutine.yield 处暂停,并将值返回给主程序。然后,我们再次调用 coroutine.resume,并传入一个值作为协同程序恢复执行时的参数。
需要注意的是,协同程序的状态可以通过 coroutine.status 函数获取,通过检查状态可以确定协同程序的执行情况(如运行中、已挂起、已结束等)。
代码:
-- 创建了一个新的协同程序对象 co,其中协同程序函数打印传入的参数 i co = coroutine.create( function(i) print(i); end ) -- 使用 coroutine.resume 启动协同程序 co 的执行,并传入参数 1。协同程序开始执行,打印输出为 1 coroutine.resume(co, 1) -- 1 -- 通过 coroutine.status 检查协同程序 co 的状态,输出为 dead,表示协同程序已经执行完毕 print(coroutine.status(co)) -- dead print("----------") -- 使用 coroutine.wrap 创建了一个协同程序包装器,将协同程序函数转换为一个可直接调用的函数对象 co = coroutine.wrap( function(i) print(i); end ) co(1) print("----------") -- 创建了另一个协同程序对象 co2,其中的协同程序函数通过循环打印数字 1 到 10,在循环到 3 的时候输出当前协同程序的状态和正在运行的线程 co2 = coroutine.create( function() for i=1,10 do print(i) if i == 3 then print(coroutine.status(co2)) --running print(coroutine.running()) --thread:XXXXXX end coroutine.yield() end end ) -- 连续调用 coroutine.resume 启动协同程序 co2 的执行 coroutine.resume(co2) --1 coroutine.resume(co2) --2 coroutine.resume(co2) --3 -- 通过 coroutine.status 检查协同程序 co2 的状态,输出为 suspended,表示协同程序暂停执行 print(coroutine.status(co2)) -- suspended print(coroutine.running()) print("----------")
coroutine.running就可以看出来,coroutine在底层实现就是一个线程。
当create一个coroutine的时候就是在新线程中注册了一个事件。
当使用resume触发事件的时候,create的coroutine函数就被执行了,当遇到yield的时候就代表挂起当前线程,等候再次resume触发事件。
代码:
function foo(a) print("foo 函数输出",a) return coroutine.yield(2 * a) end co = coroutine.create(function(a,b) print("第一次协同程序输出",a,b) local r = foo(a + 1) print("第二次协同程序输出",r) local r,s = coroutine.yield(a + b,a - b) print("第三次协同程序输出",r,s) return b end) print("main", coroutine.resume(co, 1, 10)) -- true, 4 print("--分割线----") print("main", coroutine.resume(co, "r")) -- true 11 -9 print("---分割线---") print("main", coroutine.resume(co, "x", "y")) -- true 10 end print("---分割线---") print("main", coroutine.resume(co, "x", "y")) -- cannot resume dead coroutine print("---分割线---")
以上实例接下如下:
resume和yield的配合强大之处在于,resume处于主程中,它将外部状态(数据)传入到协同程序内部;而yield则将内部的状态(数据)返回到主程中。
代码:
local newProductor function productor() local i = 0 while true do i = i + 1 send(i) end end function consumer() while true do local i = receive() print(i) end end function receive() local status,value = coroutine.resume(newProductor) return value end function send(x) coroutine.yield(x) end newProductor = coroutine.create(productor) consumer()
线程与协同程序的主要区别在于,一个具有多个线程的程序可以同时运行几个线程,而协同程序却需要彼此协作的运行。
在任一指定时刻只有一个协同程序在运行,并且这个正在运行的协同程序只有在明确的被要求挂起的时候才会被挂起。
协同程序有点类似同步的多线程,在等待同一个线程锁的几个线程有点类似协同。
主要区别归纳如下:
总体而言,线程适用于需要并发执行的场景,例如在多核处理器上利用并行性加快任务的执行速度。而协同程序适用于需要协作和协调的场景,例如状态机、事件驱动编程或协作式任务处理。选择使用线程还是协同程序取决于具体的应用需求和编程模型。
Lua I/O 库用于读取和处理文件。分为简单模式(和C一样)、完全模式。
简单模式在做一些简单的文件操作时较为合适。但是在进行一些高级的文件操作的时候,简单模式就显得力不从心。例如同时读取多个文件这样的操作,使用完全模式则较为合适。
打开文件操作语句如下:
file = io.open(filename,[,mode])
mode 的值有:
模式 | 描述 |
---|---|
r | 以只读方式打开文件,该文件必须存在。 |
w | 打开只写文件,若文件存在则文件长度清为0,即该文件内容会消失。若文件不存在则建立该文件。 |
a | 以附加的方式打开只写文件。若文件不存在,则会建立该文件,如果文件存在,写入的数据会被加到文件尾,即文件原先的内容会被保留。(EOF符保留) |
r+ | 以可读写方式打开文件,该文件必须存在。 |
w+ | 打开可读写文件,若文件存在则文件长度清为零,即该文件内容会消失。若文件不存在则建立该文件。 |
a+ | 与a类似,但此文件可读可写 |
b | 二进制模式,如果文件是二进制文件,可以加上b |
+ | 号表示对文件既可以读也可以写 |
简单模式使用标准的 I/O 或使用一个当前输入文件和一个当前输出文件。
以下为 file.lua 文件代码,操作的文件为test.lua(如果没有你需要创建该文件),代码如下:
file = io.open("15_2-test.lua","r")
io.input(file)
print(io.read())
io.close(file)
file = io.open("15_2-test.lua","a")
io.output(file)
io.write("-- test.lua 文件末尾注释")
io.close(file)
在以上实例中使用了 io.“x” 方法,其中 io.read() 中没有带参数,参数可以是下表中的一个:
模式 | 描述 |
---|---|
“*n” | 读取一个数字并返回它。例:file.read(“*n”) |
“*a” | 从当前位置读取整个文件。例:file.read(“*a”) |
“*l”(默认) | 读取下一行,在文件尾 (EOF) 处返回 nil。例:file.read(“*l”) |
number | 返回一个指定字符个数的字符串,或在 EOF 时返回 nil。例:file.read(5) |
其他的 io 方法有:
通常需要在同一时间处理多个文件。我们需要使用 file:function_name 来代替 io.function_name 方法。以下实例演示了如何同时处理同一个文件:
read 的参数与简单模式一致。
其他方法:
for line in io.lines("main.lua") do
print(line)
end
以下实例使用了 seek 方法,定位到文件倒数第 25 个位置并使用 read 方法的 *a 参数,即从当前位置(倒数第 25 个位置)读取整个文件。
file = io.open("15_2-test.lua","r")
file:seek("end",-25)
print(file:read("*a"))
file:close()
程序运行中错误处理是必要的,在进行文件操作,数据转移及web service 调用过程中都会出现不可预期的错误。如果不注重错误信息的处理,就会造成信息泄露,程序无法运行等情况。
任何程序语言中,都需要错误处理。错误类型有:
语法错误通常是由于对程序的组件(如运算符、表达式)使用不当引起的。一个简单的实例如下:
-- test.lua 文件
a == 2
以上代码执行结果为:
lua: test.lua:2: syntax error near '=='
另外一个实例:
for a= 1,10
print(a)
end
执行以上程序会出现如下错误:
lua: test2.lua:2: 'do' expected near 'print'
运行错误是程序可以正常执行,但是会输出报错信息。如下实例由于参数输入错误,程序执行时报错:
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]: ?
lua 里调用函数时,即使实参列表和形参列表不一致也能成功调用,多余的参数会被舍弃,缺少的参数会被补为 nil。
以上报错信息是由于参数 b 被补为 nil 后,nil 参与了 + 运算。
假如 add 函数内不是 “return a+b” 而是 “print(a,b)” 的话,结果会变成 “10 nil” 不会报错。
可以使用两个函数:assert 和 error 来处理错误。实例如下:
local function add(a,b)
assert(type(a) == "number", "a 不是一个数字")
assert(type(b) == "number", "b 不是一个数字")
return a+b
end
add(10)
实例中assert首先检查第一个参数,若没问题,assert不做任何事情;否则,assert以第二个参数作为错误信息抛出。
语法格式:
error (message [, level])
功能:终止正在执行的函数,并返回message的内容作为错误信息(error函数永远都不会返回)
通常情况下,error会附加一些错误位置的信息到message头部。
Level参数指示获得错误的位置:
Lua中处理错误,可以使用函数pcall(protected call)来包装需要执行的代码。
pcall接收一个函数和要传递给后者的参数,并执行,执行结果:有错误、无错误;返回值true或者或false, errorinfo。
语法格式如下:
if pcall(function_name, ….) then
-- 没有错误
else
-- 一些错误
end
示例:
> =pcall(function(i) print(i) end, 33)
33
true
> =pcall(function(i) print(i) error('error..') end, 33)
33
false stdin:1: error..
pcall以一种"保护模式"来调用第一个参数,因此pcall可以捕获函数执行中的任何错误。
通常在错误发生时,希望落得更多的调试信息,而不只是发生错误的位置。但pcall返回时,它已经销毁了调用桟的部分内容。
Lua提供了xpcall函数,xpcall接收第二个参数——一个错误处理函数,当错误发生时,Lua会在调用桟展开(unwind)前调用错误处理函数,于是就可以在这个函数中使用debug库来获取关于错误的额外信息了。
debug库提供了两个通用的错误处理函数:
>=xpcall(function(i) print(i) error('error..') end, function() print(debug.traceback()) end, 33)
33
stack traceback:
stdin:1: in function <stdin:1>
[C]: in function 'error'
stdin:1: in function <stdin:1>
[C]: in function 'xpcall'
stdin:1: in main chunk
[C]: in ?
false nil
xpcall 使用实例 2:
function myfunction ()
n = n/nil
end
function myerrorhandler( err )
print( "ERROR:", err )
end
status = xpcall( myfunction, myerrorhandler )
print( status)
Lua 提供了 debug 库用于提供创建自定义调试器的功能。Lua 本身并未有内置的调试器,但很多开发者共享了他们的 Lua 调试器代码。
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 。 以 ‘(’ (开括号)打头的变量名表示没有名字的变量 (去除了调试信息的代码块)。 |
9. | sethook ([thread,] hook, mask [, count]): 将一个函数作为钩子函数设入。 字符串 mask 以及数字 count 决定了钩子将在何时调用。 掩码是由下列字符组合成的字符串,每个字符有其含义: ‘c’: 每当 Lua 调用一个函数时,调用钩子; ‘r’: 每当 Lua 从一个函数内返回时,调用钩子; ‘l’: 每当 Lua 进入新的一行时,调用钩子。 |
10. | setlocal ([thread,] level, local, value): 这个函数将 value 赋给 栈上第 level 层函数的第 local 个局部变量。 如果没有那个变量,函数返回 nil 。 如果 level 越界,抛出一个错误。 |
11. | setmetatable (value, table): 将 value 的元表设为 table (可以是 nil)。 返回 value。 |
12. | setupvalue (f, up, value): 这个函数将 value 设为函数 f 的第 up 个上值。 如果函数没有那个上值,返回 nil 否则,返回该上值的名字。 |
13. | traceback ([thread,] [message [, level]]): 如果 message 有,且不是字符串或 nil, 函数不做任何处理直接返回 message。 否则,它返回调用栈的栈回溯信息。 字符串可选项 message 被添加在栈回溯信息的开头。 数字可选项 level 指明从栈的哪一层开始回溯 (默认为 1 ,即调用 traceback 的那里)。 |
示例:
function myfunction ()
print(debug.traceback("Stack trace"))
print(debug.getinfo(1))
print("Stack trace end")
return 10
end
myfunction ()
print(debug.getinfo(1))
在实例中,使用到了 debug 库的 traceback 和 getinfo 函数, getinfo 函数用于返回函数信息的表。
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。实例中我们使用了 getupvalue 函数查看局部变量的当前状态。我们可以设置局部变量为新值。实例中,在设置前 n 的值为 2,使用 setupvalue 函数将其设置为 10。现在我们调用函数,执行后输出为 11 而不是 3。
Lua 采用了自动内存管理。 这意味着不用操心新创建的对象需要的内存如何分配出来, 也不用考虑在对象不再被使用后怎样释放它们所占用的内存。
Lua 运行了一个垃圾收集器来收集所有死对象 (即在 Lua 中不可能再访问到的对象)来完成自动内存管理的工作。 Lua 中所有用到的内存,如:字符串、表、用户数据、函数、线程、 内部结构等,都服从自动管理。
Lua 实现了一个增量标记-扫描收集器。 它使用这两个数字来控制垃圾收集循环: 垃圾收集器间歇率和垃圾收集器步进倍率。 这两个数字都使用百分数为单位 (例如:值 100 在内部表示 1 )。
垃圾收集器间歇率控制着收集器需要在开启新的循环前要等待多久。 增大这个值会减少收集器的积极性。 当这个值比 100 小的时候,收集器在开启新的循环前不会有等待。 设置这个值为 200 就会让收集器等到总内存使用量达到 之前的两倍时才开始新的循环。
垃圾收集器步进倍率控制着收集器运作速度相对于内存分配速度的倍率。 增大这个值不仅会让收集器更加积极,还会增加每个增量步骤的长度。 不要把这个值设得小于 100 , 那样的话收集器就工作的太慢了以至于永远都干不完一个循环。 默认值是 200 ,这表示收集器以内存分配的"两倍"速工作。
如果把步进倍率设为一个非常大的数字 (比你的程序可能用到的字节数还大 10% ), 收集器的行为就像一个 stop-the-world 收集器。 接着你若把间歇率设为 200 , 收集器的行为就和过去的 Lua 版本一样了: 每次 Lua 使用的内存翻倍时,就做一次完整的收集。
Lua 提供了以下函数collectgarbage ([opt [, arg]])用来控制自动内存管理:
代码:
mytable = {"apple","orange","banana"}
print(collectgarbage("count") * 1024)
mytable = nil
print(collectgarbage("count"))
print(collectgarbage("collect"))
print(collectgarbage("count"))
面向对象编程(Object Oriented Programming,OOP)是一种非常流行的计算机编程架构。
以下几种编程语言都支持面向对象编程:
Account = {balance = 0}
function Account.withdraw (v)
Account.balance = Account.balance - v
end
这个定义创建了一个新的函数,并且保存在Account对象的withdraw域内,下面我们可以这样调用:
Account.withdraw(100.00)
Rectangle = {area = 0,length = 0,breadth = 0}
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
function Rectangle : printArra()
print("矩形面积为",self.area)
end
创建对象是为类的实例分配内存的过程。每个类都有属于自己的内存并共享公共数据。
-- 继上面示例
r = Rectangle : new(nil,10,20)
可以使用点号(.)来访问类的属性:
print(r.length)
可以使用冒号 :
来访问类的成员函数:
r:printArra()
内存在对象初始化时分配。
代码:
Shape = {area = 0} function Shape:new(o,side) o = o or {} setmetatable(o,self) self.__index = self side = side or o self.area = side * side return o end function Shape:printArea() print("面积为",self.area) end myshape = Shape:new(nil,10) myshape:printArea()
继承是指一个对象直接使用另一对象的属性和方法。可用于扩展基础类的属性和方法。
以下演示了一个简单的继承实例:
function Shape : new(o,side)
o = o or {}
setmetatable(o,self)
self.__index = self
side = side or 0
self.area = side * side
return or
end
function Shape:printArea()
print("面积为",self.area)
end
接下来的实例,Square 对象继承了 Shape 类:
Squre = Shape:new()
function Squre : new(o,side)
o = o or Shape:new(o,side)
setmetatable(o,self)
self.__index = self
return o
end
以下实例继承了一个简单的类,来扩展派生类的方法,派生类中保留了继承类的成员变量和方法:
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 function Shape : printArea() print("面积为",self.area) end myshape = Shape:new(nil,10) myshape:printArea() Squre = Shape:new() function Squre:new(o,side) o = o or Shape:new(o,side) setmetatable(o,self) self.__index = self return o end function Squre:printArea() print("正方形面积为",self.area) end mysqure = Squre:new(nil,10) mysqure:printArea() Rectangle = Shape: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 function Rectangle:printArea() print("矩形面积为",self.area) end myrectangle = Rectangle:new(nil,10,20) myrectangle:printArea()
Lua 中可以重写基础类的函数,在派生类中定义自己的实现方式:
function Squre:printArea()
print("正方形面积",self.area)
end
Lua 连接MySql 数据库:
require "luasql.mysql" --创建环境对象 env = luasql.mysql() --连接数据库 conn = env:connect("数据库名","用户名","密码","IP地址",端口) --设置数据库的编码格式 conn:execute"SET NAMES UTF8" --执行数据库操作 cur = conn:execute("select * from role") row = cur:fetch({},"a") --文件对象的创建 file = io.open("role.txt","w+"); while row do var = string.format("%d %s\n", row.id, row.name) print(var) file:write(var) row = cur:fetch(row,"a") end file:close() --关闭文件对象 conn:close() --关闭数据库连接 env:close() --关闭数据库环境
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。