此篇文章所有操作都是基于上一篇安装的docker容器内进行操作
案例来自于菜鸟教程
首先进入容器
安装vim
apk add vim
Lua 变量
变量就是给一块内存区域赋予一个值。使得程序可以读取和修改相应内存中内容。
变量由字母、数字、下划线组成。必须以字母或下划线开头。
Lua 是大小写敏感的。
变量分为全局变量和局部变量
type variable_list
local a, b = 1, 10 --局部变量
c, d = 2, 20 -- 全局变量
如果变量只定义了没有初始化,则静态存储变量被隐士初始化为nil
-- 变量定义
local a, b
-- 初始化
a = 1
b = 2
print("value of a:", a)
print("value of b:", b)
-- 变量交换
a, b = b, a
print("value of a:", a)
print("value of b:", b)
Lua 字符串
- 单引号字符串
- 双引号字符串
- [[]]之间的字符串
string1 = "lua"
print("string 1 is", string1)
string2 = "php"
print("string 2 is", string2)
string3 = [["go"]]
print("string3 is", string3)
1.string.upper(str)
2.string.lower(str)
3.string.gsub(mainString, findString, replaceString, num)
mainString 为要操作的字符串, findString 为被替换的字符串, replaceString 要替换的字符串, num 替换次数(忽略为全部替换)
> string.gsub("aaaa", "a", "b", 2)
bbaa 2
4.string.find(str, subsets, [init, [end]])
在一个指定的目标字符串 str 中搜索指定的内容 substr,如果找到了一个匹配的子串,就会返回这个子串的起始索引和结束索引,不存在则返回 nil。
> string.find("hello lua", "lua", 1)
5.string.reverse(str)
> string.reverse("lua")
aul
6.string.format(str)
> string.format("the value is : %d", 4)
the value is : 4
7.string.char(str)
将整型数字转成字符并连接
> string.char(100, 98)
db
8.string.bute(str, [, int])
转换字符为整数值(可以指定某个字符,默认第一个字符
> string.byte("ac")
97
9.string.len(str)
> string.len("aaa")
3
- string.rep(string, n)
返回字符串string的n个拷贝
> string.rep("abcd", 2)
abcdabcd
- … 链接两个字符串
> print("blog.caixiaoxin"..".cn")
blog.caixiaoxin.cn
- string.gmatch(str, pattern)
返回一个迭代器函数,每一次调用这个函数,返回一个在字符串 str 找到的下一个符合 pattern 描述的子串。如果参数 pattern 描述的字符串没有找到,迭代函数返回nil。
> for word in string.gmatch("Hello Lua user", "%a+") do print(word) end
Hello
Lua
user
- 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(s, i[,j])
local sourcestr = "prefix--runoobgoogletaobao--suffix"
print("\n 原始字符串", string.format("%q", sourcestr))
-- 截取部分 从第4个到第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))
-- 字符串大小转换
string1 = "Lua"
print(string.upper(string1))
print(string.lower(string1))
-- 替换字符串
string2 = "Lua Tutorial"
newstring = string.gsub(string2, "Tutorial", "Language")
print("The new string is", newstring)
-- 查找于颠倒
string3 = "Lua Tutorial"
print(string.find(string3, "Tutorial"))
reverseString = string.reverse(string3)
print("the new string is", reverseString)
-- 格式化字符串
string4 = "Lua"
string5 = "Tutorial"
number1 = 10
number2 = 20
print(string.format("Basic formatting %s %s", string4, string5))
-- 日期格式化
date = 2; month = 1; year = 2022
print(string.format("Date formatting %02d/%02d/%03d", date, month, year))
-- 浮点数格式化
print(string.format("%.4f", 1/3))
-- 字符与字节表示
-- 第一个字符
print(string.byte("Lua"))
-- 第三个字符
print(string.byte("Lua", 3))
-- 倒数第一个字符
print(string.byte("Lua", -1))
-- 第二个字符
print(string.byte("Lua", 2))
-- 倒数第三个字符
print(string.byte("Lua", -3))
-- 内部ASCII 值转换为字符
print(string.char(97))
Lua 数组
数组,就是相同数据类型的元素按一定顺序排列的集合,可以是一维数组和多维数组。
-- 一维数组
array = {"Lua", "Tutorial"}
for i=0, 2 do
print(array[i])
end
array = {}
for i= -2, 2 do
array[i] = i *2
end
for i = -2,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 tale
table 是 Lua 的一种数据结构用来帮助我们创建不同的数据类型,如:数组、字典等。
Lua table 使用关联型数组,你可以用任意类型的值来作数组的索引,但这个值不能是 nil。
Lua table 是不固定大小的,你可以根据自己需要进行扩容。
Lua也是通过table来解决模块(module)、包(package)和对象(Object)的。 例如string.format表示使用"format"来索引table string。
-- 初始化表
mytable = {}
-- 指定值
mytable[1]= "Lua"
-- 移除引用
mytable = nil
-- lua 垃圾回收会释放内存
1.table.concat(,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
mytable = {}
print("mytable 的类型是", type(mytable))
mytable[1] = "Lua"
mytable["wow"] = "修改前"
print("mytable 索引为 1的元素是", mytable[1])
--alternatetable 和 mytable指同一个table
alternatetable = mytable
print("alternatetable 索引为1 的元素是", alternatetable[1])
print("mytable 索引为 wow 的元素是", alternatetable["wow"])
alternatetable["wow"] = "修改后"
print("mytable 索引为 wow 的元素是", mytable["wow"])
-- 释放变量
alternatetable = nil
print("alternatetable 是", alternatetable)
-- mytable 仍然可以访问
print("mytable 索引为 wow 的元素是", mytable["wow"])
mytable = nil
print("mytable是", mytable)
-- 插入和移除
fruits = {"banana", "orange", "apple"}
-- 在末尾插入
table.insert(fruits, "mango")
print("索引为 4 的元素为", fruits[4])
-- 在索引为 2 的健处插入
table.insert(fruits, 2, "grapes")
print("索引为2 的元素为", fruits[2])
print("最后一个元素为", fruits[5])
table.remove(fruits)
print("移除最后一个元素为", fruits[5])
-- table 排序
fruits2 = {"banana", "orange", "apple", "greps"}
print("排序前")
for k, v in ipairs(fruits2) do
print(k, v)
end
table.sort(fruits2)
print("排序后")
for k,v in ipairs(fruits2) do
print(k, v)
end
-- Table 最大值
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)
Lua table(表)
在 Lua 语言中,表是唯一可以用来创建不同数据类型的数据结构,比如常见的数组和字典都是用表来创建的。 Lua 语言中经常到关联数组这种数据类型,它不仅可以用数值作为索引值,除了 nil 以外的字符串同样可以作为其索引。表没有固定的大小,当数据量增加时表会自动增大
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
mytable = {}
print("mytable 的类型是", type(mytable))
mytable[1] = "Lua"
mytable["wow"] = "修改前"
print("mytable 索引为 1的元素是", mytable[1])
--alternatetable 和 mytable指同一个table
alternatetable = mytable
print("alternatetable 索引为1 的元素是", alternatetable[1])
print("mytable 索引为 wow 的元素是", alternatetable["wow"])
alternatetable["wow"] = "修改后"
print("mytable 索引为 wow 的元素是", mytable["wow"])
-- 释放变量
alternatetable = nil
print("alternatetable 是", alternatetable)
-- mytable 仍然可以访问
print("mytable 索引为 wow 的元素是", mytable["wow"])
mytable = nil
print("mytable是", mytable)
-- table 链接
f = {"apple", "orange", "apple"}
print("链接后的字符串", table.concat(f))
print("链接后的字符串", table.concat(f, ", "))
print("链接后的字符串", table.concat(f, ", ", 2, 3))
-- 插入和移除
fruits = {"banana", "orange", "apple"}
-- 在末尾插入
table.insert(fruits, "mango")
print("索引为 4 的元素为", fruits[4])
-- 在索引为 2 的健处插入
table.insert(fruits, 2, "grapes")
print("索引为2 的元素为", fruits[2])
print("最后一个元素为", fruits[5])
table.remove(fruits)
print("移除最后一个元素为", fruits[5])
-- table 排序
fruits2 = {"banana", "orange", "apple", "greps"}
print("排序前")
for k, v in ipairs(fruits2) do
print(k, v)
end
table.sort(fruits2)
print("排序后")
for k,v in ipairs(fruits2) do
print(k, v)
end
-- Table 最大值
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)
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(n1, n2)
if (n1 > n2) then
result = n1
else
result = n2
end
return result
end
print(max(10, 5))
函数作为参数
mprint = function(p)
print(p)
end
function add(n1, n2, functionPrint)
result = n1 + n2
functionPrint(result)
end
mprint(10)
add(1, 5, mprint)
多返回值
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}))
可变参数
- …
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, 5))
- 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, 5))
- 固定参数和可变参数结合使用。固定参数放在可变参数之前
function f(fmt, ...)
return io.write(string.format(fmt, ...))
end
f("php\n")
f("%d%d\n", 1, 2)
- 通常在遍历变长参数的时候只需要使用 {…},然而变长参数可能会包含一些 nil,那么就可以用 select 函数来访问变长参数了:select(‘#’, …) 或者 select(n, …)
- select(‘#’, …) 返回可变参数的长度。
- select(n, …) 用于返回从起点 n 开始到结束位置的所有参数列表。
- 调用 select 时,必须传入一个固定实参 selector(选择开关) 和一系列变长参数。如果 selector 为数字 n,那么 select 返回参数列表中从索引 n 开始到结束位置的所有参数列表,否则只能为字符串 #,这样 select 返回变长参数的总数
Lua 模块
模块类似于一个封装库,从 Lua 5.1 开始,Lua 加入了标准的模块管理机制,可以把一些公用的代码放在一个文件里,以 API 接口的形式在其他地方调用,有利于代码的重用和降低代码耦合度。
Lua 的模块是由变量、函数等已知元素组成的 table,因此创建一个模块很简单,就是创建一个 table,然后把需要导出的常量、函数放入其中,最后返回这个 table 就行。以下为创建自定义模块 module.lua,文件代码格式如下:
module = {}
module.constant = "常量"
function module.func1()
io.write("这是一个公有函数")
end
local function func2()
print("这是一个私有函数")
end
function module.func3()
func2()
end
return module
require("module")
-- 或者
-- local m = require("module")
print(module.constant)
module.func3()
由上可知,模块的结构就是一个 table 的结构,因此可以像操作调用 table 里的元素那样来操作调用模块里的常量或函数。
上面的 func2 声明为程序块的局部变量,即表示一个私有函数,因此是不能从外部访问模块里的这个私有函数,必须通过模块里的公有函数来调用.
Lua提供了一个名为require的函数用来加载模块。要加载一个模块,只需要简单地调用就可以了
或者给加载的模块定义一个别名变量,方便调用
Lua 流程控制
if…else 流程控制语句
condition为true 执行 if 中的语句块 为false 时执行else语句块
if(布尔表达式)
then
--[ 布尔表达式为 true 时执行该语句块 --]
else
--[ 布尔表达式为 false 时执行该语句块 --]
end
a = 100
if (a<20)
then
print("a 小于 20")
else
print("a 大于 20")
end
print("a 的值为 :", a)
if…elseif…else 语句
if( 布尔表达式 1)
then
--[ 在布尔表达式 1 为 true 时执行该语句块 --]
elseif( 布尔表达式 2)
then
--[ 在布尔表达式 2 为 true 时执行该语句块 --]
elseif( 布尔表达式 3)
then
--[ 在布尔表达式 3 为 true 时执行该语句块 --]
else
--[ 如果以上布尔表达式都不为 true 则执行该语句块 --]
end
a = 100
if( a == 10 )
then
print("a 的值为 10" )
elseif( a == 20 )
then
print("a 的值为 20" )
elseif( a == 30 )
then
print("a 的值为 30" )
else
print("没有匹配 a 的值" )
end
print("a 的真实值为: ", 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("a 的值为 100 b 的值为 200" );
end
end
print("a 的值为 :", a );
print("b 的值为 :", b );
Lua循环
while循环
while(condition)
do
statements
end
a = 10
while(a < 20)
do
print("a的值为:", a)
a = a + 1
end
for循环
数值for循环
for var=exp1,exp2,exp3 do
<执行体>
end
#!/usr/local/bin/lua
function f(x)
print("function")
return x*2
end
for i=1,f(5) do print(i)
end
泛型for循环
#!/usr/local/bin/lua
days = {"Sunday","Monday","Tuesday","Wednesday","Thursday","Friday","Saturday"}
for i,v in ipairs(days) do print(v) end
repeat…until
重复执行循环,直到 指定的条件为真时为止
repeat
statements
until( condition )
a = 10
repeat
print("a的值为:", a)
a = a + 1
until( a > 15 )
循环嵌套
for init,max/min value, increment
do
for init,max/min value, increment
do
statements
end
statements
end
while(condition)
do
while(condition)
do
statements
end
statements
end
repeat
statements
repeat
statements
until( condition )
until( condition )
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 的值为:",i)
end
end
end
Lua 迭代器
迭代器是用于遍历集合或容器中元素的一种结构。在 Lua 语言中,集合往往指的是可以用来创建各种数据结构的表。比如,数组就是用表来创建的。
通用迭代器
array = {"Lua", "PHP"}
for k, v in ipairs(array)
do
print(k, v)
end
无状态迭代器
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
有状态迭代器
array = {"Lua", "GO"}
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
Lua 文件 I/O
Lua I/O 库用于读取和处理文件。包括简单模式和完全模式两种
简单模式: 对一个当前输入文件和一个当前输出文件进行操作
完全模式: 使用外部的句柄来实现
打开文件
file = io.open (filename [, mode])
r 只读模式, 以只读模式打开。(存在的文件)
w 可写模式, 修改已经存在的文件和创建新的文件
a 追加模式, 对于已存的文件允许追加新内容,但不允许修改原有内容,同时也可以创建新文件
r+ 读写模式打开已存的在文件
w+ 如果文件已存在则删除文件中数据;若文件不存在则新建文件。读写模式打开
a+ 以可读的追加模式打开已存在文件,若文件不存在则新建文件
简单模式
file = io.open("test.lua", "r")
io.input(file)
print(io.read())
io.close(file)
file = io.open("test.lua", "a")
io.output(file)
io.write("-- test.lua 文件末尾注释")
io.close(file)
io."x"的参数
*n 读取一个数字并返回它。例:file.read(“*n”)
a 从当前位置读取整个文件。例:file.read(“*a”)
^l 读取下一行,在文件尾 (EOF) 处返回 nil。例:file.read(“*l”)
number 返回一个指定字符个数的字符串,或在 EOF 时返回 nil。例:file.read(5)
完全模式
file = io.open("test.lua", "r")
print(file:read())
file:close()
file = io.open("test.lua", "a")
file:write("--test")
file:close()
Lua 错误处理
错误处理是必不可少的。在真实的系统中,往往会遇到不可预期的问题。像数据库事务、文件等等,错误处理就很关键。
错误分类
- 语法错误
- 运行时错误
语法错误demo:
for a= 1,10
print(a)
end
运行时错误demo:
function add(a,b)
return a+b
end
add(10)
Assert函数
local function add(a,b)
assert(type(a) == "number", "a 不是一个数字")
assert(type(b) == "number", "b 不是一个数字")
return a+b
end
add(10)
error函数
error (message [, level])
功能:终止正在执行的函数,并返回message的内容作为错误信息(error函数永远都不会返回)
通常情况下,error会附加一些错误位置的信息到message头部。
Level参数指示获得错误的位置:
- Level=1[默认]:为调用error位置(文件+行号)
- Level=2:指出哪个调用error的函数的函数
- Level=0:不添加错误位置信息
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 同时为其设置了错误处理方法 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 调试(debug)
Lua 提供了 debug 库用于提供创建我们自定义调试器的功能
Lua 本身并未有内置的调试器,但很多开发者共享了他们的 Lua 调试器代码
- debug()
进入一个用户交互模式,运行用户输入的每个字符串。 使用简单的命令以及其它调试设置,用户可以检阅全局变量和局部变量, 改变变量的值,计算一些表达式,等等。
输入一行仅包含 cont 的字符串将结束这个函数, 这样调用者就可以继续向下运行。
- getfenv(object)
返回对象的环境变量
- gethook(optional thread)
返回三个表示线程钩子设置的值: 当前钩子函数,当前钩子掩码,当前钩子计数
- getinfo ([thread,] f [, what])
返回关于一个函数信息的表。 你可以直接提供该函数, 也可以用一个数字 f 表示该函数。 数字 f 表示运行在指定线程的调用栈对应层次上的函数: 0 层表示当前函数(getinfo 自身); 1 层表示调用 getinfo 的函数 (除非是尾调用,这种情况不计入栈);等等。 如果 f 是一个比活动函数数量还大的数字, getinfo 返回 nil
- debug.getlocal ([thread,] f, local)
此函数返回在栈的 f 层处函数的索引为 local 的局部变量 的名字和值。 这个函数不仅用于访问显式定义的局部变量,也包括形参、临时变量等
- getmetatable(value)
把给定索引指向的值的元表压入堆栈。如果索引无效,或是这个值没有元表,函数将返回 0 并且不会向栈上压任何东西
- getregistry()
返回注册表表,这是一个预定义出来的表, 可以用来保存任何 C 代码想保存的 Lua 值
- getupvalue (f, up)
此函数返回函数 f 的第 up 个上值的名字和值。 如果该函数没有那个上值,返回 nil 。
以 ‘(’ (开括号)打头的变量名表示没有名字的变量 (去除了调试信息的代码块)。
- sethook ([thread,] hook, mask [, count])
将一个函数作为钩子函数设入。 字符串 mask 以及数字 count 决定了钩子将在何时调用。 掩码是由下列字符组合成的字符串,每个字符有其含义:
- ‘
c
’: 每当 Lua 调用一个函数时,调用钩子; - ‘
r
’: 每当 Lua 从一个函数内返回时,调用钩子; - ‘
l
’: 每当 Lua 进入新的一行时,调用钩子。 - setlocal ([thread,] level, local, value)
这个函数将 value 赋给 栈上第 level 层函数的第 local 个局部变量。 如果没有那个变量,函数返回 nil 。 如果 level 越界,抛出一个错误。
- setmetatable (value, table)
将 value 的元表设为 table (可以是 nil)。 返回 value。
- setupvalue (f, up, value)
这个函数将 value 设为函数 f 的第 up 个上值。 如果函数没有那个上值,返回 nil 否则,返回该上值的名字。
- traceback ([thread,] [message [, level]])
如果 message 有,且不是字符串或 nil, 函数不做任何处理直接返回 message。 否则,它返回调用栈的栈回溯信息。 字符串可选项 message 被添加在栈回溯信息的开头。 数字可选项 level 指明从栈的哪一层开始回溯 (默认为 1 ,即调用 traceback 的那里)
function my()
print(debug.traceback("Stack trace"))
print(debug.getinfo(1))
print("Stack trace end")
return 10
end
my()
print(debug.getinfo(1))
我们经常需要调试函数的内的局部变量。我们可以使用 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, 1)
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())
Lua 面向对象
LUA中最基本的结构是table,所以需要用table来描述对象的属性
-- 元类
Shape = {area = 0}
-- 基础类方法 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
-- 基础类方法 printArea
function Shape:printArea ()
print("面积为 ",self.area)
end
-- 创建对象
myshape = Shape:new(nil,10)
myshape:printArea()
-- Meta class
Shape = {area = 0}
-- 基础类方法 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
-- 基础类方法 printArea
function Shape:printArea ()
print("面积为 ",self.area)
end
-- 创建对象
myshape = Shape:new(nil,10)
myshape:printArea()
Square = Shape:new()
-- 派生类方法 new
function Square:new (o,side)
o = o or Shape:new(o,side)
setmetatable(o, self)
self.__index = self
return o
end
-- 派生类方法 printArea
function Square:printArea ()
print("正方形面积为 ",self.area)
end
-- 创建对象
mysquare = Square:new(nil,10)
mysquare:printArea()
Rectangle = Shape:new()
-- 派生类方法 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
-- 派生类方法 printArea
function Rectangle:printArea ()
print("矩形面积为 ",self.area)
end
-- 创建对象
myrectangle = Rectangle:new(nil,10,20)
myrectangle:printArea(
Lua 数据库访问
LuaSQL
LuaSQL 可以使用 LuaRocks 来安装可以根据需要安装你需要的数据库驱动
安装LuaRocks
$ wget http://luarocks.org/releases/luarocks-2.2.1.tar.gz
$ tar zxpf luarocks-2.2.1.tar.gz
$ cd luarocks-2.2.1
$ ./configure; sudo make bootstrap
$ sudo luarocks install luasocket
$ lua
Lua 5.3.0 Copyright (C) 1994-2015 Lua.org, PUC-Rio
> require "socket"
luarocks install luasql-mysql
Lua 连接MySql 数据库
require "luasql.mysql"
env = luasql.mysql()
conn = env:connect("数据库名","用户名","密码","IP地址",端口)
conn:execute"SET NAMES UTF8"
cur = conn:execute("select * from test")
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()
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 的线程号 |
- coroutine_test.lua 文件
co = coroutine.create(
function(i)
print(i);
end
)
coroutine.resume(co, 1) -- 1
print(coroutine.status(co)) -- dead
print("----------")
co = coroutine.wrap(
function(i)
print(i);
end
)
co(1)
print("----------")
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) --1
coroutine.resume(co2) --2
coroutine.resume(co2) --3
print(coroutine.status(co2)) -- suspended
print(coroutine.running())
function foo (a)
print("foo 函数输出", a)
return coroutine.yield(2 * a) -- 返回 2*a 的值
end
co = coroutine.create(function (a , b)
print("第一次协同程序执行输出", a, b) -- co-body 1 10
local r = foo(a + 1)
print("第二次协同程序执行输出", r)
local r, s = coroutine.yield(a + b, a - b) -- a,b的值为第一次调用协同程序时传入
print("第三次协同程序执行输出", r, s)
return b, "结束协同程序" -- 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,将协同程序唤醒,resume操作成功返回true,否则返回false;
- 协同程序运行;
- 运行到yield语句;
- yield挂起协同程序,第一次resume返回;(注意:此处yield返回,参数是resume的参数)
- 第二次resume,再次唤醒协同程序;(注意:此处resume的参数中,除了第一个参数,剩下的参数将作为yield的参数)
- yield返回;
- 协同程序继续运行;
- 如果使用的协同程序继续运行完成后继续调用 resume方法则输出:cannot resume dead coroutine
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) -- x表示需要发送的值,值返回以后,就挂起该协同程序
end
-- 启动程序
newProductor = coroutine.create(productor)
consumer()
Lua 垃圾回收
Lua 采用了自动内存管理。 这意味着你不用操心新创建的对象需要的内存如何分配出来, 也不用考虑在对象不再被使用后怎样释放它们所占用的内存。
Lua 运行了一个垃圾收集器来收集所有死对象 (即在 Lua 中不可能再访问到的对象)来完成自动内存管理的工作。 Lua 中所有用到的内存,如:字符串、表、用户数据、函数、线程、 内部结构等,都服从自动管理。
Lua 实现了一个增量标记-扫描收集器。 它使用这两个数字来控制垃圾收集循环: 垃圾收集器间歇率和垃圾收集器步进倍率。 这两个数字都使用百分数为单位 (例如:值 100 在内部表示 1 )。
垃圾收集器间歇率控制着收集器需要在开启新的循环前要等待多久。 增大这个值会减少收集器的积极性。 当这个值比 100 小的时候,收集器在开启新的循环前不会有等待。 设置这个值为 200 就会让收集器等到总内存使用量达到 之前的两倍时才开始新的循环。
垃圾收集器步进倍率控制着收集器运作速度相对于内存分配速度的倍率。 增大这个值不仅会让收集器更加积极,还会增加每个增量步骤的长度。 不要把这个值设得小于 100 , 那样的话收集器就工作的太慢了以至于永远都干不完一个循环。 默认值是 200 ,这表示收集器以内存分配的"两倍"速工作。
如果你把步进倍率设为一个非常大的数字 (比你的程序可能用到的字节数还大 10% ), 收集器的行为就像一个 stop-the-world 收集器。 接着你若把间歇率设为 200 , 收集器的行为就和过去的 Lua 版本一样了: 每次 Lua 使用的内存翻倍时,就做一次完整的收集。
垃圾回收器函数
Lua 提供了以下函数**collectgarbage ([opt [, arg]])**用来控制自动内存管理:
- 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”): 停止垃圾收集器的运行。 在调用重启前,收集器只会因显式的调用运行。
mytable = {"apple", "orange", "banana"}
print(collectgarbage("count"))
mytable = nil
print(collectgarbage("count"))
print(collectgarbage("collect"))
print(collectgarbage("count"))