Lua是由标准C编写而成的脚本语言,诞生于1993年,具有高效性、可移植性、可嵌入性、简单强大、小巧轻便、免费开源等诸多优点,主要应用场景包括做为嵌入脚本、做为独立脚本、用于应用程序的动态配置、游戏开发以及Web应用脚本等。
Path
中lua
,命令行输出Lua版本信息,安装成功curl -R -O http://www.lua.org/ftp/lua-5.4.4.tar.gz # 拉取合适的Lua包
tar -C /usr/local -zxvf lua-5.4.4.tar.gz # 解压到合适的目录下
cd /usr/local/lua-5.4.4/ # 进入Lua包解压路径
make all test # 开始安装
ln -s /usr/local/lua-5.4.4/src/lua /usr/bin/lua # 建立连接(可选步骤)
lua -v # 查看版本,输出Lua版本信息
table类型是一个"关联数组",需要注意:
[
和]
括起来,如果是字符串,还可以去掉引号和中括号function
和table
,
隔开-- 初始化
mytable = {}
-- 指定值
mytable[1] = "lua"
mytable["name"] = "table"
-- 修改值
mytable[1] = "Lua"
-- 获取值
print(mytable[1]) -- Lua
print(mytable.name) -- table
-- 移除引用
mytable = nil
string类型可以使用双引号""
或单引号''
声明,如果是块字符串,可以以[[
开始,以]]
结尾。
字符串不可修改值,可以通过string.gsub
函数来替换字符串中的子串。
a = '1024'
b = '3.14159'
c = "Hello"
d = "2022"
e = [[
Lua
]]
print(a, b, c, d)
-- 输出:1024 3.14159 Hello 2022
print(e)
--[[ 输出:
Lua
--]]
number类型只有一种,即双精度浮点double类型
a = 1024
b = 3.14159
print(a, type(a))
-- 输出:1024 number
print(b, type(b))
-- 输出:3.14159 number
boolean类型只有两个可选值:true(真)和 false(假)。判断时false和nil都是假,其他都为真。
a = true
b = false
print(a, type(a))
-- 输出:true boolean
print(b, type(b))
-- 输出:false boolean
nil类型表示一个无效值,只有值 nil,如果打印没有赋值的变量,则会输出nil。
print("first: ", a)
-- 输出:first: nil
a = 1024
print("second: ", a)
-- 输出:second: 1024
a = nil
print("third: ", a)
-- 输出:third: nil
if (type(a) == "nil") then
print("a is nil")
else
print("a is not nil")
end
-- 输出:a is nil
注意:要判断变量是否为 nil 的时候,需要使用 type 获取变量的类型,然后与字符串的 nil 进行比较
function类型是由C或Lua编写的完成某一功能的程序指令的集合,称为函数,可分为自定义函数和系统函数。
function sum(a, b)
return a + b;
end
result1 = sum(100, 200)
result2 = sum(1024, 99)
print(string.format("result1 = %d, result2 = %d", result1, result2))
-- 输出:result1 = 300, result2 = 1123
thread类型表示执行的独立线路,用于执行协同程序。
function fun()
print("hello")
end
cor = coroutine.create(fun)
print(cor, type(cor))
-- 输出:thread: 0x13691f0 thread
userdata类型是一种用户自定义数据,用于表示一种由应用程序或C/C++语言库所创建的类型,可以将任意C/C++的任意数据类型的数据存储在Lua变量中调用。
userdata可分为full userdata和light userdata。
full userdata | light userdata | |
---|---|---|
定义 | 用户自定义数据 | 一种表示C指针的值,不用创建 |
使用 | 需要显示的创建一块内存,该段内存有Lua垃圾回收器管理,不需要使用者关心 | 存储在栈上,使用者需要关心内存使用 |
创建 | 没有进行参数合法性检查 void *lua_newuserdata(lua State *L, size_t size); 有进行参数合法性检查 void *lua_checkudata(lua State *L, int arg, const char *tname); |
void lua_pushlightuserdata(lua_State *L, void *p); |
其他 | 可以指定其metatable和metamethods | 不可以指定其metatable和metamethods |
-- 这是行注释
print("Hello Lua")
-- 这是行注释
--[[
这是块注释,
块注释可以
注释多行内容
--]]
print("Hello Lua")
变量相当于内存中一个数据存储空间的表示,通过变量名可以访问到变量的具体的值。
Lua的变量在定义时不需要指定明确的类型,而是会根据赋的默认值来断定变量的类型。
赋值是给已经定义的变量重新设置值的过程。
同时为多个变量赋值时:
当变量个数 > 值的个数时,按变量个数补足nil
当变量个数 < 值的个数时,多余的值会被忽略
a = 1
b = true
c, d = "Hello", 2022
name, age, address = "Xiaoming", 18
day, week = 30, "星期四", 2022
print(a, b)
-- 输出:1 true
print(c, d)
-- 输出:Hello 2022
print(name, age, address)
-- 输出:XiaoMing 18 nil
print(day, week)
-- 输出:30 星期四
变量根据作用域可分为全局变量和局部变量,且用local
显式声明的变量为局部变量,其余全部为全局变量。
local a = 1024
function hello()
pi = 3.14159
local name = "Lua"
end
hello()
print("a = ", a, "pi = ", pi)
-- 输出:a = 1024 pi = 3.14159
print("name = ", name)
-- 输出:name = nil
Lua中,除了table类型,其他任何类型的变量都可以通过tostring
函数转化为字符串类型。
能表示数字的字符串类型的变量可以通过tonumber
函数转化为数字类型。
a = 100
b = true
sa = tostring(a)
sb = tostring(b)
print("sa = ", sa, "type(sa) = ", type(sa))
-- 输出:sa = 100 type(sa) = string
print("sb = ", sb, "type(sb) = ", type(sb))
-- 输出:sb = true type(sb) = string
sc = "3.14"
sd = "0XA"
c = tonumber(sc)
d = tonumber(sd)
print("c = ", c, "type(c) = ", type(c))
-- 输出:c = 3.14 type(c) = number
print("d = ", d, "type(d) = ", type(d))
-- 输出:d = 10 type(d) = number
使用io.read
函数获取用户输入。
可选参数:
格式 | 描述 |
---|---|
“*n” | 读取一个数字 |
“*a” | 从当前位置读取剩余的全部内容 |
"*l” | 读取下一行内容 |
10 | 读取指定数字的长度 |
local name = io.read()
print("name = ", name, "type(name) = ", type(name))
-- 用户输入:Lua
-- 输出:name = lua type(name) = string
local num = io.read("*n")
print("num = ", num, "type(num) = ", type(num))
-- 用户输入:1024
-- 输出:name = 1024 type(name) = number
name = "lua"
age = 18
print(string.format("name = %s, age = %d", name, age))
-- 输出:name = lua, age = 18
循环控制就是让程序满足一定的条件就一直循环的去执行,直到条件不满足,则跳出循环继续执行循环以外的语句。
while循环,当型循环,先判断条件,满足则执行循环,否则不进入循环。
-- 求和
local num = 0
local sum = 0
while (num <= 100) do
sum = sum + num
num = num + 1
end
print(string.format("sum = %d", sum))
-- 输出:sum = 5050
-- 打印乘法表
local i = 1
while (i <= 9) do
local j = 1
while (j <= i) do
io.write(string.format("%d * %d = % -6d", j, i, j * i))
j = j + 1
end
i = i + 1
print()
end
--[[ 输出:
1 * 1 = 1
1 * 2 = 2 2 * 2 = 4
1 * 3 = 3 2 * 3 = 6 3 * 3 = 9
1 * 4 = 4 2 * 4 = 8 3 * 4 = 12 4 * 4 = 16
1 * 5 = 5 2 * 5 = 10 3 * 5 = 15 4 * 5 = 20 5 * 5 = 25
1 * 6 = 6 2 * 6 = 12 3 * 6 = 18 4 * 6 = 24 5 * 6 = 30 6 * 6 = 36
1 * 7 = 7 2 * 7 = 14 3 * 7 = 21 4 * 7 = 28 5 * 7 = 35 6 * 7 = 42 7 * 7 = 49
1 * 8 = 8 2 * 8 = 16 3 * 8 = 24 4 * 8 = 32 5 * 8 = 40 6 * 8 = 48 7 * 8 = 56 8 * 8 = 64
1 * 9 = 9 2 * 9 = 18 3 * 9 = 27 4 * 9 = 36 5 * 9 = 45 6 * 9 = 54 7 * 9 = 63 8 * 9 = 72 9 * 9 = 81
--]]
repeat until循环,直到型循环,后判断条件,满足则跳出循环,不满足则进入循环,循环至少会执行1次。
-- 求和
local num = 1
local sum = 0
repeat
sum = sum + num
num = num + 1
until (num > 100)
print(string.format("sum = %d", sum))
-- 输出:sum = 5050
-- 打印乘法表
local i = 1
repeat
local j = 1
repeat
io.write(string.format("%d * %d = %-6d", j, i, j * i))
j = j + 1
until j > i
i = i + 1
print()
until (i > 9)
--[[ 输出:
1 * 1 = 1
1 * 2 = 2 2 * 2 = 4
1 * 3 = 3 2 * 3 = 6 3 * 3 = 9
1 * 4 = 4 2 * 4 = 8 3 * 4 = 12 4 * 4 = 16
1 * 5 = 5 2 * 5 = 10 3 * 5 = 15 4 * 5 = 20 5 * 5 = 25
1 * 6 = 6 2 * 6 = 12 3 * 6 = 18 4 * 6 = 24 5 * 6 = 30 6 * 6 = 36
1 * 7 = 7 2 * 7 = 14 3 * 7 = 21 4 * 7 = 28 5 * 7 = 35 6 * 7 = 42 7 * 7 = 49
1 * 8 = 8 2 * 8 = 16 3 * 8 = 24 4 * 8 = 32 5 * 8 = 40 6 * 8 = 48 7 * 8 = 56 8 * 8 = 64
1 * 9 = 9 2 * 9 = 18 3 * 9 = 27 4 * 9 = 36 5 * 9 = 45 6 * 9 = 54 7 * 9 = 63 8 * 9 = 72 9 * 9 = 81
--]]
如果循环的语句是for i = 1, 9, 1 do ...
,表示从1开始,大于9结束,每次步进1。如果是步进1,最后1可以省略,变为for i = 1, 9 do ...
。
-- 求和
local sum = 0
for num = 0, 100, 1 do
sum = sum + num
end
print(string.format("sum = %d", sum))
-- 输出:sum = 5050
-- 打印乘法表
for i = 1, 9 do
for j = 1, i do
io.write(string.format("%d * %d = %-6d", j, i, j * i))
end
print()
end
--[[ 输出:
1 * 1 = 1
1 * 2 = 2 2 * 2 = 4
1 * 3 = 3 2 * 3 = 6 3 * 3 = 9
1 * 4 = 4 2 * 4 = 8 3 * 4 = 12 4 * 4 = 16
1 * 5 = 5 2 * 5 = 10 3 * 5 = 15 4 * 5 = 20 5 * 5 = 25
1 * 6 = 6 2 * 6 = 12 3 * 6 = 18 4 * 6 = 24 5 * 6 = 30 6 * 6 = 36
1 * 7 = 7 2 * 7 = 14 3 * 7 = 21 4 * 7 = 28 5 * 7 = 35 6 * 7 = 42 7 * 7 = 49
1 * 8 = 8 2 * 8 = 16 3 * 8 = 24 4 * 8 = 32 5 * 8 = 40 6 * 8 = 48 7 * 8 = 56 8 * 8 = 64
1 * 9 = 9 2 * 9 = 18 3 * 9 = 27 4 * 9 = 36 5 * 9 = 45 6 * 9 = 54 7 * 9 = 63 8 * 9 = 72 9 * 9 = 81
--]]
-- 遍历table
weekdays = {"Sunday","Monday","Tuesday","Wednesday","Thursday","Friday","Saturday"}
for i, v in ipairs(weekdays) do
print(string.format("%s --> %s", i, v))
end
--[[ 输出:
1 --> Sunday
2 --> Monday
3 --> Tuesday
4 --> Wednesday
5 --> Thursday
6 --> Friday
7 --> Saturday
--]]
终止循环的继续运行,如果有多层循环,只能终止当层循环,无法终止外层循环。
for i = 0, 1 do
for j = 0, 20 do
if (j >= 2) then
break
end
print(string.format("i = %d, j = %d", i, j))
end
end
--[[ 输出:
i = 0, j = 0
i = 0, j = 1
i = 1, j = 0
i = 1, j = 1
--]]
Lua中没有continue语句,但是可以借助for循环以及repeat until循环实现continue功能。
for i = 0, 4 do
repeat
if i == 2 then
break
end
print(string.format("i = %d", i))
until true
end
--[[ 输出:
i = 0
i = 1
i = 3
i = 4
--]]
使用return语句,终止循环、函数的执行。
for i = 0, 5, 1 do
if (i == 3) then
return
end
print(string.format("i = %d", i))
end
--[[ 输出:
i = 0
i = 1
i = 2
--]]
function check(value)
if (value <= 0) then
print("value <= 0")
return
end
print("value > 0")
end
check(12)
check(-1)
--[[ 输出:
value > 0
value <= 0
--]]
允许将控制流程无条件地转到被标记的语句处,仅lua5.2以上版本支持。
local a = 1
::label::
print("-- goto label --")
a = a+1
if a < 3 then
goto label
end
--[[ 输出:
-- goto label --
-- goto label --
--]]
pairs和iparis都是能遍历集合(表、数组),但是iparis仅仅遍历值,按照索引升序遍历,索引中断停止遍历,即不能返回nil,只能返回数字0,如果遇到nil则退出。只能遍历到集合中出现的第一个不是整数的key。pairs能遍历集合的所有元素。
local table = {"A", "B", [5] = "yes", ["t"] = "no"}
for i, v in pairs(table) do
print(i, table[i])
end
print()
for i, v in ipairs(table) do
print(i, table[i])
end
--[[ 输出:
1 A
2 B
5 yes
t no
1 A
2 B
--]]
分支控制就是让程序有选择的执行,主要分为:单分支、双分支和多分支形式。
local a = 10
local b = 5
if (a > b) then
print("a > b")
end
print("Out If")
--[[ 输出:
a > b
Out If
--]]
local a = 10
local b = 5
if (a > b) then
print("a > b")
else
print("a <= b")
end
print("Out If")
--[[ 输出:
Out If
--]]
local a = 5
local b = 5
if (a > b) then
print("a > b")
elseif (a == b) then
print("a == b")
else
print("a <= b")
end
print("Out If")
--[[ 输出:
a == b
Out If
--]]
local a = 5
local b = 5
if (a > b) then
print("a > b")
else
if (a == b) then
print("a == b")
else
print("a <= b")
end
end
print("Out If")
--[[ 输出:
a == b
Out If
--]]
在程序中,编写函数的主要目的是将一个需要很多行代码的复杂问题分解为一系列简单的任务来解决,而且,同一个函数可以被多次调用,有助于代码重用。
function sum(a, b)
return a + b;
end
result1 = sum(100, 200)
result2 = sum(1, 99)
print(string.format("result1 = %d, result2 = %d", result1, result2));
--输出:result1 = 300, result2 = 100
Lua中的函数可以不返回任何值,也可以返回一个值,也支持返回多个值。
function check_user(score)
if score > 85 then
return "A", true
elseif score > 60 then
return "B", true
else
return "C", false
end
end
local level1, isOk = check_user(99)
local level2, _ = check_user(50)
print(string.format("level1 = %s, level2 = %s", level1, level2))
-- 输出:level1 = A, level2 = C
函数参数的个数可以是任意的,可变参数使用...
来表示,若想要获取用户传入的所有的参数,可以使用arg
变量。
function sum(...)
local result = 0
local arg = {...}
for i, v in iparis(arg) do
result = result + v
end
return result
end
result = sum(1, 3, 5, 7, 9)
print(string.format("result = %d", result))
-- 输出:result = 25
function test_func(tab, fun)
for k, v in pairs(tab) do
print(fun(k, v))
end
end
tab = {key1 = "val1", key2 = "val2"}
test_func(tab, function(key, val) return key .. " = " .. val end)
--[[ 输出:
key1 = val1
key2 = val2
--]]
闭包的主要作用:
function func()
local index = 0
return function ()
index = index + 1
return index
end
end
local inner1 = func()
print(inner1(), inner1())
-- 输出:1 2
local inner2 = func()
print(inner2(), inner2())
-- 输出:1 2
算术运算符是对数值类型的变量进行运算的。
运算符 | 说明 | 范例 | 结果 |
---|---|---|---|
+ | 正号 | +3 | 3 |
- | 负号 | -4 | -3 |
+ | 加法运算 | 5 + 5 | 10 |
- | 减法运算 | 10 - 5 | 5 |
* | 乘法运算 | 5 * 2 | 10 |
/ | 除法运算 | 10 / 3 | 3.3 |
% | 取余运算 | 10 % 3 | 1 |
^ | 幂运算 | 2^3 | 8 |
a, b = -20, 3
local c = a + b
local d = a - b
local e = a * b
local f = a / b
local g = a % b
local h = a ^ b
print(string.format("c = %d, d = %d, e = %d, f = %f, g = %d, h = %d", c, d, e, f, g, h))
-- 输出:c = -17, d = -23, e = -60, f = -6.666667, g = 1, h = -8000
关系运算符结果要么是真,要么是假。
运算符 | 说明 | 范例 | 结果 |
---|---|---|---|
== | 相等 | 4 == 3 | false |
~= | 不等于 | 4 ~= 3 | true |
< | 小于 | 4 < 3 | false |
> | 大于 | 4 > 3 | true |
<= | 小于等于 | 4 <= 3 | false |
>= | 大于等于 | 4 >= 3 | true |
a = 100
b = 99
local c = a == b
local d = a ~= b
local e = a > b
local f = a < b
local g = a >= b
local h = a <= b
print("c = ", c, "d = ", d, "e = ", e, "f = ", f, "g = ", g, "h = ", h)
-- 输出:c = false d = true e = true f = false g = true h = false
逻辑运算符用来连接多个条件,最终返回是true或false,使用逻辑运算符可以模拟三目运算符。
运算符 | 说明 | 范例 |
---|---|---|
and | 逻辑与 | A and B,如果A的值为假,则不会再计算B的值 |
or | 逻辑或 | A or B,如果A的值为真,则不会再计算B的值 |
not | 逻辑非 | nor A |
local age = 40
local b = age > 30 and age < 50
local c = age > 40 or age < 30
local d = not age
local e = not nil
local f = not 0
local g = age > 30 and age or 30
print("b = ", b, "c = ", c, "d = ", d, "e = ", e, "f = ", f, "g = ", g)
-- 输出:b = true c = false d = false e = true f = false g = 40
运算符 | 说明 | 范例 |
---|---|---|
… | 连接运算符 | str1…str2 |
# | 获取字符串长度 | #str1 |
local str1 = "hello"
local str2 = "lua"
local str3 = str1..str2
print(string.format("str3 = %s, #str3 = %d", str3, #str3))
-- 输出:str3 = hellolua, #str3 = 8
函数 | 描述 |
---|---|
upper(arg) | 字符串全部转为大写字母 |
lower(arg) | 字符串全部转为小写字母 |
sub(s, i, [,j]) | 截取字符串 |
gsub(mainString, findString, replaceString, num) | 在字符串中替换 |
dump(function) | 把函数序列化为字符串来保存 |
find(str, substr, [init, [end]]) | 在字符串中查找,存在返回具体位置,不存在返回nil |
reverse(arg) | 字符串反转 |
format(…) | 返回一个格式化字符串 |
char(arg) | 将整型数字转化为字符并连接 |
byte[arg[int,]] | byte转换字符为整数值 |
len(arg) | 计算字符串长度 |
rep(string, n) | 返回字符串string的n个拷贝 |
… | 连接两个字符串 |
gmatch(str, pattern) | 迭代器函数,每次调用返回一个查找到的子串 |
match(str, pattern, init) | 查找第一个配对的子串 |
string.upper(s)
用于将字符串中所有字母转化为大写。
string.lower(s)
用于将字符串中所有字母转化为小写。
str1 = "Hello Lua"
str2 = string.upper(str1)
str3 = string.lower(str1)
print(string.format("str1 = %s, str2 = %s, str3 = %s", str1, str2, str3))
-- 输出:str1 = Hello Lua, str2 = HELLO LUA, str3 = hello lua
string.sub(s, i [, j])
用于字符串截取,返回字符串s从第i个字符到第j个字符的子串。注意,字符串的第1个字符索引是1。
str1 = "Hello Lua"
str2 = string.sub(str1, 1, 5)
str3 = string.sub(str1, -3)
print(string.format("str1 = %s, str2 = %s, str3 = %s", str1, str2, str3))
-- 输出:str1 = Hello Lua, str2 = Hello, str3 = Lua
string.gsub(mainString, findString, replaceString, num)
用于字符串替换,num指定替换字符串的次数,默认全部替换。
str1 = "Hello world, Hello Lua"
str2 = string.gsub(str1, "Hello", "Nihao")
str3 = string.gsub(str1, "Hello", "Nihao", 1)
print(string.format("str1 = %s, str2 = %s, str3 = %s", str1, str2, str3))
-- 输出:str1 = Hello world, Hello Lua, str2 = Nihao world, Nihao Lua, str3 = Nihao world, Hello Lua
string.dump(function)
用于将函数序列化为字符串,便于函数的保存与传输loadstring(str)
用于将序列化后的函数字符串反序列化加载为函数。
function sum(a, b)
return a + b
end
str = string.dump(sum)
res = loadstring(str)
print(str, res(1, 2))
-- 输出:uaQ 3
string.find(str, substr, [init, [end]])
用于在一个指定的目标字符串中搜素指定的内容,返回其具体位置,不存在返回nil,也支持使用正则匹配查找。
str1 = "Hello Lua"
index1 = string.find("Hello Lua", "Lua")
index2 = string.find("Hello Lua", "Lua", 8)
index3 = string.find("Hello Lua", "%s%u%a.")
print("index1 = ", index1, "index2 = ", index2, "index3 = ", index3)
-- 输出:index1 = 7 index2 = nil index3 = 6
string.reverse(arg)
用于反转字符串。
str = "Hello Lua"
print(string.format("str = %s, res = %s", str, string.reverse(str)))
-- 输出:str = Hello Lua, res = auL olleH
格式 | 描述 | 格式 | 描述 |
---|---|---|---|
%c | 接收数字,转化为字符 | %d,%i | 接收数字,转化为有符号整数 |
%o | 接收数字,转化为八进制数 | %u | 接收数字,转化为无符号整数 |
%x | 接收数字,转化为十六进制数,使用小写字母 | %X | 接收数字,转化为十六进制数,使用大写字母 |
%e | 接收数字,转化为科学计数法,使用小写字母e | %E | 接收数字,转化为科学计数法,使用大写字母E |
%f | 接收数字,转化为浮点数 | %g,%G | 接收数字,转化为%e,%f中较短格式 |
%q | 接收字符串,转化为可安全被Lua编译器读入的格式 | %s | 接收字符串,按给定参数格式化字符串 |
%+ | 表示其后的数字转义符将让正数显示正号 | %占位符 | 在后面指定了字串宽度时占位用 |
%对齐标识 | 在指定了字串宽度时, 默认为右对齐, 增加 - 号改为左对齐 | %宽度数值 | 占位宽度 |
%小数位数/字串裁切 | 数字保留位数,字符串做裁切 |
print(string.format("%c", 65))
print(string.format("%+d", 17.0))
print(string.format("%05d", 17))
--[[ 输出:
A
+17
00017
--]]
string.char(arg)
用于将整数转化为字符并连接。
string.byte(arg[,int])
用于将字符转化为整数并连接,int表示要转换的字符。
print(string.char(97, 98, 99))
print(string.byte("ABCD", 3))
--[[ 输出:
abc
67
--]]
string.len()
用于获取字符串长度,另外#
也可以获取字符串长度。
str1 = "ABC"
str2 = "Hello"
print(string.format("str1's len = %d, str2's len = %d", string.len(str1), #str2))
-- 输出:str1's len = 3, str2's len = 5
string.rep(string, n)
用于将字符串拷贝n次。
str = "ABCD"
print(string.format("str = %s", string.rep(str, 2)))
-- 输出:str = ABCDABCD
字符串可以使用..
连接。
str1 = "hello"
str2 = "lua"
res = str1 .. " " .. str2
print(string.format("res = %s", res))
-- 输出:hello lua
string.gmatch(str, pattern)
是一个迭代器函数,每调用一次,返回一个在字符串str查找到的符合pattern描述的子串,如果没找到返回nil,也支持正则匹配。
string.match(str, pattern, init)
只找寻str中第一个配对,搜索起点可配置,也支持正则匹配。
for word in string.gmatch("Hello Lua", "%a+") do
print(word)
end
print(string.match("I have 2 questions for you.", "%d+ %a+"))
--[[ 输出:
Hello
Lua
2 questions
--]]
转义字符 | 意义 | 转义字符 | 意义 |
---|---|---|---|
\a | 响铃 | \b | 退格,将当前位置移到前一列 |
\f | 换页,将当前位置移到下页开头 | \n | 换行,将当前位置移到下一行开头 |
\r | 回车,将当前位置移到本行开头 | \t | 水平制表,跳到下一个Tab位置 |
\v | 垂直制表 | \ | 代表一个反斜线字符 |
’ | 代表一个单引号字符 | " | 代表一个双引号字符 |
\0 | 空字符 | \ddd | 1到3位八进制数所代表的任意字符 |
\xhh | 1到2位十六进制数所代表的任意字符 |
数组与表的类型都为table。
数组的语法为arrName = {element1, element2, ....}
,数组保存的一组数据类型可以不一致,数组的索引值是以1为起始的,也可以人为指定为0开始,如arrName = {[0]=element1, element2, ....}
。
表是一个 “关联数组”,表的索引可以是数字或者是字符串,所有索引值都需要用 [
和 ]
括起来;如果没有 [] 括起,则认为是字符串索引,可以认为,数组是索引为以0或1开始的连续数字的一种特殊的表,因此,表的一些增删改查行为函数也适用于数组。
操作 | 描述 |
---|---|
concat | 连接 |
insert | 插入 |
maxn | 最大key |
remove | 移除 |
sort | 升序排序 |
#
后面直接加数组名可以获取数组的长度,如果设置了索引从0开始,则获取到的数组长度会比实际的长度少1。
#
后面直接加表名无法准确获取数组的长度,会在索引中断的地方停止计数,应该用循环遍历来获取正确长度。
-- 数组
letters = {}
letters[1] = "A"
letters[2] = "B"
letters[3] = "C"
letters[4] = "D"
letters[5] = "E"
days = {[0]="Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday"}
numbers = {1, 2, 3, nil}
all = {1, "A", nil, true}
print(string.format("Len: letters = %d, days = %d, numbers = %d, all = %d", #letters, #days, #numbers, #all))
-- 输出:Len: letters = 5, days = 6, numbers = 3, all = 4
-- 表
mytable1 = {[1] = "Lua", greet = "Hello", [3] = "Count"}
mytable2 = {}
print(string.format("Len: mytable1 = %d, mytable2 = %d", #mytable1, #mytable2))
-- 输出:Len: mytable1 = 1, mytable2 = 0
-- 数组
days = {[0]="Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday"}
for i = 0, #days do
print(string.format("days[%d] = %s", i, days[i]))
end
--[[ 输出:
days[0] = Monday
days[1] = Tuesday
days[2] = Wednesday
days[3] = Thursday
days[4] = Friday
days[5] = Saturday
days[6] = Sunday
--]]
letters = {"A", "B", "C", "D", "E"}
for i, v in pairs(letters) do
print(string.format("letters[%d] = %s", i, v))
end
--[[ 输出:
letters[1] = A
letters[2] = B
letters[3] = C
letters[4] = D
letters[5] = E
--]]
-- 表
mytable = {[1] = "Lua", greet = "Hello", [3] = "Count"}
for i, v in pairs(mytable) do
print(i, v)
end
--[[ 输出:
1 Lua
3 Count
greet Hello
--]]
table.concat(table [, sep [, start [, end]]])
函数列出参数中指定table的数组部分从start位置到end位置的所有元素,元素键以指定分隔符sep隔开,start默认为1。注:该函数只处理table下标为数字的数据,且下标是连续的才能被处理,断开就结束了。
fruits = {"orange", "apple", "banana"}
print(string.format("str = %s", table.concat(fruits, ", ")))
-- 输出:orange, apple, banana
letters = {[0]="A", "B", "C", "D", "E"}
print(string.format("str = %s", table.concat(letters, ", ")))
-- 输出:B, C, D, E
mytable = {[1] = "Lua", greet = "Hello", [3] = "Count"}
print(string.format("str = %s", table.concat(mytable, ", ")))
-- 输出:Lua
table.insert(table [pos,] value)
函数在table指定位置pos插入一个value元素,pos默认为数组部分末尾,即连续下标元素的最后。
fruits = {"orange", "apple", "banana"}
table.insert(fruits, 3, "watermelon")
for k, v in pairs(fruits) do
print(k, v)
end
--[[ 输出:
1 orange
2 apple
3 watermelon
4 banana
--]]
letters = {[0]="A", "B", "C", "D", "E"}
table.insert(letters, 3, "W")
for i = 0, #letters do
print(i, letters[i])
end
--[[ 输出:
0 A
1 B
2 C
3 W
4 D
5 E
--]]
mytable = {[1] = "Lua", greet = "Hello", [3] = "Count"}
table.insert(mytable, 2, "Table")
for k,v in pairs(mytable) do
print(k,v)
end
print(#mytable)
--[[ 输出:
1 Lua
2 Table
3 Count
greet Hello
3
--]]
table.maxn(table)
函数返回table的最大正数索引,如果没有正数索引返回0。Lua5.2之后的版本已移除该函数。
fruits = {"orange", "apple", "banana"}
print(table.maxn(fruits))
letters = {[0]="A", "B", "C", "D", "E"}
print(table.maxn(letters))
mytable = {[1] = "Lua", greet = "Hello", [3] = "Count"}
print(table.maxn(mytable))
--[[ 输出:
3
4
3
--]]
table.remove(table [,pos])
函数删除指定pos位置的元素,pos默认为table长度。即连续索引的最大值。
fruits = {"orange", "apple", "banana"}
remove1 = table.remove(fruits)
remove2 = table.remove(fruits, 1)
print(string.format("remove1 = %s, remove2 = %s", remove1, remove2))
-- 输出:remove1 = banana, remove2 = orange
mytable = {[1] = "Lua", greet = "Hello", [3] = "Count"}
remove = table.remove(mytable)
print(string.format("remove = %s", remove))
-- 输出:remove = Lua
table.sort(table [,comp])
函数用于对给定的table进行升序排序,还支持传入排序规则。
local test0 ={1,9,2,8,3,7,4,6}
table.sort(test0)
for i,v in pairs(test0) do
io.write(v.." ")
end
print()
local test1 ={
{id=1, name="deng"},
{id=9, name="luo"},
{id=2, name="yang"},
{id=8, name="ma"},
{id=5, name="wu"},
}
table.sort(test1, function(a,b) return a.id < b.id end)
for i in pairs(test1) do
print(test1[i].id, test1[i].name)
end
--[[ 输出:
1 2 3 4 6 7 8 9
1 deng
2 yang
5 wu
8 ma
9 luo
--]]
Lua的table中可以访问对应的key来得到value值,但是却无法对两个table进行操作。元表允许改变table的行为,每个行为关联了对应的元方法。如两个table相加操作a+b
,当Lua尝试对两个表相加时,先检查两者之一是否有元表,之后检查__add
字段是否存在,如果操作,则调用相应的值。__add
等即时字段其对应的值就是元方法。
有两个重要的函数处理元表:
函数 | 描述 |
---|---|
setmetatable(table, metatable) | 对指定的table设置元表,如果元表中存在__metatable 键值,则setmetatable会失败 |
getmetatable(table) | 返回对象的元表 |
mytable = {} -- 普通表
mymetatable = {} -- 元表
setmetatable(mytable, mymetatable) -- 设置元表
getmetatable(mytable) -- 返回元表
__index
元方法用来对表访问。
Lua查找一个表元素时的规则:
1 在表中查找,如果找到,返回该元素,找不到继续
2 判断该表是否有元表,如果没有元表,返回nil,有元表则继续
3 判断该表有没有__index
方法,如果__index
方法为nil,则返回nil,如果__index
方法是一个表,则重复1、2、3步;如果__index
方法是一个函数,则返回该函数的返回值
mytable = setmetatable({key1 = "value1"}, {
__index = function(mytable, key)
if key == "key2" then
return "metatablevalue"
else
return nil
end
end
})
print(string.format("key1 = %s, key2 = %s", mytable.key1, mytable.key2))
-- 输出:key1 = value1, key2 = metatablevalue
-- 等价于
mytable = setmetatable({key1 = "value1"}, {__index = {key2 = "metatablevalue"}})
print(mytable.key1, mytable.key2)
-- 输出:value1 metatablevalue
__newindex
元方法用来对表更新。
当给表的一个缺少的索引赋值,解释器会查找__newindex
元方法,如果存在,则调用这个函数而不进行赋值操作。
mytable = setmetatable({key1 = "value1"}, {
__newindex = function(mytable, key, value)
rawset(mytable, key, "\""..value.."\"")
end
})
mytable.key1 = "new value"
mytable.key2 = 4
print(string.foramt("key1 = %s, key2 = %s", mytable.key1, mytable.key2))
-- 输出:key1 = new value, key2 = "4"
__call
元方法可以让table当做一个函数来使用。
local mt = {}
mt.__call = function(mytable,...)
for _, v in ipairs{...} do
print(v)
end
end
t = {}
setmetatable(t, mt)
t(1, 2, 3)
--[[ 输出:
1
2
3
--]]
__tostring
元方法用于修改表的输出行为。
mytable = setmetatable({10, 20, 30}, {
__tostring = function(mytable)
sum = 0
for k, v in pairs(mytable) do
sum = sum + v
end
return "sum = " ..sum
end
})
print(mytable)
-- 输出:sum = 60
模式 | 等价于运算符 | 模式 | 等价于运算符 |
---|---|---|---|
__add | + | __sub | - |
__mul | * | __div | / |
__mod | % | __unm | - |
__concat | … | __eq | == |
__lt | < | __le | <= |
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
--[[ 输出:
1 1
2 2
3 3
4 4
5 5
6 6
--]]
模块类似于一个封装库,是由变量、函数等已知元素组成的table,因此创建模块就是创建一个table,然后把需要导出的常量、函数放入其中,最后返回这个table即可。如下创建自定义模块module。
-- 文件名 module.lua
-- 定义一个名为module的模块
module = {}
-- 定义一个常量
module.constant = "this is constant"
-- 定义一个函数
function module.func1()
io.write("This is a public function\n")
end
local function func2()
io.write("This is a private function")
end
function module.func3()
func2()
end
return module
require("模块名")
用来加载模块,执行require后会返回一个由模块常量或函数组成的table,并且还会定义一个包含该table的全局变量。
require("module")
-- 别名变量 m
local m = require("module")
print(m.constant)
m.func3()
--[[ 输出:
this is constant
This is a private function
--]]
require函数会尝试从Lua文件或C程序中加载模块,require用于搜索Lua文件的路径存放在全局变量package_path中,当Lua启动后,会以环境变量LUA_PATH的值来初始化这个环境变量,如果找不到该环境变量,则使用一个编译时定义的默认路径来初始化。
可以自定义设置路况,在当前用户跟目录下打开.profile文件(没有则创建,打开.bashrc文件也可以),例如把"~/lua/"路径加入LUA_PATH环境变量里。
#LUA_PATH
export LAU_PATH="~/lua/?.lua;;"
文件路径以";“号分割,最后两个”;;"表示新加的路径后面加上原来的默认路径。
接着执行指令source ~/.profile
更新变量参数使之生效。
如果找到目标文件,则会调用package.loadfile
来加载模块,否则就会找C程序库。
搜索的文件路径是从全局变量 package.cpath 获取,而这个变量则是通过环境变量 LUA_CPATH 来初始。
搜索的策略跟上面的一样,只不过现在换成搜索的是 so 或 dll 类型的文件。如果找得到,那么 require 就会通过 package.loadlib 来加载它。
module()
函数调用时会创建表并将其赋予给全局变量和loaded table,最后还会将这个表设置为主程序块的环境。
-- 在模块文件在使用module函数
module "module_name"
--[[
等同语法
--]]
-- 定义模块名
local moduleName = "module_name"
-- 定义用于返回的模块表
local M = {}
-- 将模块表加入到全局变量
_G[moduleName] = M
-- 将模块表加入到package.loaded中防止多次加载
package.loaded[moduleName] = M
-- 将模块表设置为函数的环境表,使得模块中的所有操作都是在模块表中,这样定义函数就直接定义在模块表中
setfenv(1, M)
Lua和C语言很容易结合,可以使用C语言为Lua写包,C语言包使用前必须先加载并连接,大多数系统是通过动态连接库机制。
Lua在一个加loadlib的函数内提供了所有的动态连接功能。这个函数的两个参数:库的绝对路径和初始化函数,如:
local path = "/usr/local/lua/lib/libluasocket.so"
local f = loadlib(path, "luaopen_socket")
loadlib函数加载指定的库并连接到Lua,然而并没有调用初始化函数,而是返回初始化函数作为Lua的一个函数。
如果加载动态库或者查找初始化函数出错,loadlib将返回nil和错误信息。
local path = "C:\\windows\\luasocket.dll"
local f = loadlib(path, "luaopen_socket")
-- 真正打开库
f()
一般情况下我们期望二进制的发布库包含一个与前面代码段相似的 stub 文件,安装二进制库的时候可以随便放在某个目录,只需要修改 stub 文件对应二进制库的实际路径即可。
将 stub 文件所在的目录加入到 LUA_PATH,这样设定后就可以使用 require 函数加载 C 库了。
协程与线程比较类似,拥有独立的堆栈、独立的局部变量、独立的指令,同时又与其他协同程序共享全局变量和其他大部分东西。
一个具有多线程的程序可以同时运行几个线程,而协程却需要彼此写作运行,在任一指定时刻只有一个协程在运行,并且这个正在运行的协同程序只有在明确的被要求挂起时才会被挂起。
协程有点类似同步的多线程,在等待同一个线程锁的几个线程有点类似协程。
方法 | 描述 |
---|---|
coroutine.create() | 创建coroutine,返回coroutine,参数是一个函数,当和resume配合使用时唤醒函数调用 |
coroutine.resume() | 重启coroutine,和create配合使用 |
coroutine.yield() | 挂起coroutine,将coroutine设置为挂起状态,和resume配合使用能够有很多效果 |
coroutine.status() | 查看coroutine的状态,有dead、suspened、running三种状态 |
coroutine.wrap() | 创建coroutine,返回一个函数,一旦调用这个函数,就进入croutine,和create功能重复 |
coroutine.running() | 返回正在运行的coroutine,一个coroutine就是一个新村,返回一个coroutine的线程号 |
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))
print("--分割线--")
print("main", coroutine.resume(co, "r"))
print("--分割线--")
print("main", coroutine.resume(co, "x", "y"))
print("--分割线--")
print("main", coroutine.resume(co, "x", "y"))
print("--分割线--")
--[[ 输出:
第一次协同程序执行输出 1 10
foo 函数输出 2
main true 4
--分割线--
第二次协同程序执行输出 r
main true 11 -9
--分割线--
第三次协同程序执行输出 x y
main true 10 结束协同程序
--分割线--
main false 10 cannot resume dead coroutine
--]]
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 = coutine.create(productor)
cosumer()
--[[ 输出:
1
2
3
4
5
6
7
...
--]]
Lua文件IO库用于读取和处理文件,分为简单模式和完全模式:
函数 | 描述 |
---|---|
io.input([file]) | 设置默认的输入文件,file为文件名,返回文件句柄 |
io.output([file]) | 设置默认的输出文件,file为文件名 |
io.close([file]) | 关闭文件,不带参数的默认文件 |
io.read(formats) | 读取默认文件,formats取值为a* -全读、*n -按数字读入、*l -按行读入,n -读取n个字符 |
io.lines([fn]) | fn文件名,如无文件,取默认文件,返回一个迭代器 |
io.write(value) | 向默认文件写入内容 |
io.flush() | 把文件缓存里的操作立即作用到默认输出文件 |
-- 以只读方式打开文件
file = io.open("test.lua", "r")
-- 设置默认输入文件
io.input(file)
-- 输出文件第一行
print(string.format("read file: [%s]", io.read()))
-- 关闭文件
io.close(file)
-- 以附加的方式打开只写文件
file = io.open("test.lua", "a")
-- 设置默认输出文件
io.output(file)
-- 在文件最后一行写入
io.write("File content in test.lua")
print("file write success")
-- 关闭文件
io.close(file)
-- 以只读方式打开文件
file = io.open("test.lua", "r")
-- 输出文件第一行
print(file:read())
-- 关闭打开的文件
file::close()
-- 以附加的方式打开只写文件
file = io.open("test.lua", "a")
-- 在文件最后一行写入
file::write("--test")
-- 关闭打开的文件
file::close()
-- 以只读方式打开文件
file = io.open("test.lua", "r")
-- 定位到文件倒数第25个位置
file::seek("end", -25)
-- 从当前(倒数第25个位置)读取整个文件
print(file::read("*a"))
-- 关闭打开的文件
file::close()
错误类型分为语法错误和运行错误。
语法错误通常是对程序的组件使用不当引起,如:
for a = 1, 10
print(a)
end
-- 输出:lua: main.lua:2: 'do' expected near 'print'
运行错误时程序可以正常运行,但是会输出报错信息,如:
function add(a, b)
return a + b
end
add(10)
--[[ 输出:
lua: main.lua:2: attempt to perform arithmetic on local 'b' (a nil value)
stack traceback:
main.lua:2: in function 'add'
main.lua:4: in main chunk
--]]
assert(express, message)
断言函数会判断给定express表达式是否成立,成立的话不做任何事情,不成立则以message作为错误信息抛出。
function add(a, b)
assert(type(a) == "number", "a not a number")
assert(type(b) == "number", "b not a number")
return a + b
end
add(10)
--[[ 输出:
lua: main.lua:3: b not a number
stack traceback:
[C]: in function 'assert'
main.lua:3: in function 'add'
main.lua:6: in main chunk
--]]
error(message [,level])
函数终止正在执行的函数,并返回message的内容作为错误信息,level参数指示获得错误的位置,level=1指出调用error位置,level=2指出调用error函数的函数吗,level=0不添加错误位置信息。
function add(a, b)
if (type(a) ~= "number") then
error("a not a number")
end
if (type(b) ~= "number") then
error("b not a number", 2)
end
return a + b
end
add(10)
--[[ 输出:
lua: main.lua:10: b not a number
stack traceback:
[C]: in function 'error'
main.lua:6: in function 'add'
main.lua:10: in main chunk
--]]
可以使用pcall
函数包装需要执行的代码,pcall
函数接收一个函数和要传递给后者的参数,并执行该函数,无错误返回true,有错误返回false和错误信息。
a, errorinfo = pcall(function(i) print(i) end, 27)
print(a, errorinfo)
--[[ 输出:
27
true nil
--]]
b, errorinfo = pcall(function(i) print(i) error('error..') end, 27)
print(b, errorinfo)
--[[ 输出:
27
false main.lua1: error..
--]]
xpcall
函数相比pcall
函数会返回更多的错误调试信息,并执行该函数,无错误返回true,有错误返回false和错误信息。
function myfunction ()
n = n / nil
end
function myerrorhandler(err)
print("ERROR:", err)
end
status = xpcall(myfunction, myerrorhandler)
print(status)
--[[ 输出:
ERROR: main.lua:2: attempt to perform arithmetic on global 'n' (a nil value)
false
--]]
Lua运行了一个垃圾收集器收集所有死对象来完成自动内存管理的工作。
Lua实现了一个增量标记-扫描收集器,使用垃圾收集器间歇率和垃圾收集器步进倍率来控制垃圾收集循环。
垃圾收集器间歇率控制着收集器需要在开启新的循环前要等待多久。
垃圾收集器步进倍率控制收集器运作速度相对与内存分配速度的倍率,默认值是200%,即运作速度2倍于内存分配速率。
Lua提供一下函数collectgarbage([opt [,arg]])
来控制自动内存管理。
函数 | 功能 |
---|---|
collectgarbage(“collect”) | 做一次完整的垃圾收集循环 |
collectgarbage(“count”) | 以K字节数为单位返回Lua使用的总内存数 |
collectgarbage(“restart”) | 重启垃圾收集器的自动运行 |
collectgarbage(“setpause”) | 将arg设为收集器的间歇率,返回间歇率的前一个值 |
collectgarbage(“setstepmul”) | 返回步进倍率的前一个值 |
collectgarbage(“step”) | 单步运行垃圾收集器,步长由arg控制 |
collectgarbage(“stop”) | 停止垃圾收集器的运行 |
mytable = {"apple", "orange", "banana"}
print(collectgarbage("count"))
mytable = nil
print(collectgarbage("count"))
print(collectgarbage("collect"))
print(collectgarbage("count"))
--[[ 输出:
27.6396484375
27.6767578125
0
26.623046875
--]]
封装:能够把一个实体的信息、功能、响应都装入一个单独的对象中的特征。
继承:基础的方法允许在不改动原程序的基础上对其进行扩充,使得原功能得以保存,新功能也得以扩展,有利于减少重复代码,提高开发效率。
多态:同一操作作用于不同的对象,产生不同的执行结果,可以通过指向基类的指针,来调用实现派生类的方法。
抽象:简化复杂问题的方法,可以为具体问题找到最恰当的类定义,并且可以在最恰当的继承级别解释问题。
-- 元类
Shape = { area = 0 }
-- 基础类方法 new
function Shape:new (o, size)
o = o or {}
setmetatable(o, self)
self.__index = self
size = size or 0
self.area = size * size
return o
end
-- 基础类方法 printArea
function Shape:pirntArea ()
print("The area = ", self.area)
end
-- 创建对象
myshape = Shape:new(nil, 10)
myshape:printArea()
-- 输出:The area = 100
-- 元类
Shape = { area = 0 }
-- 基础类方法 new
function Shape:new (o, size)
o = o or {}
setmetatable(o, self)
self.__index = self
size = size or 0
self.area = size * size
return o
end
-- 基础类方法 printArea
function Shape:pirntArea ()
print("The area = ", self.area)
end
-- 创建对象
myshape = Shape:new(nil, 10)
myshape:printArea()
-- 输出:The area = 100
-- 继承1
Square = Shape:new()
-- 派生类方法 new
function Square:new (o, size)
o = o or Shape:new(o, size)
setmetatable(o, self)
self.__index = self
return o
end
-- 派生类方法 printArea
function Square:printArea ()
print("The Square area = ", self.area)
end
-- 创建对象
mysquare = Square:new(nil, 10)
mysquare:printArea()
-- 输出:The Square area = 100
-- 继承2
Rectangle = Shape:new()
-- 派生类方法 new
function Rectangle:new (o, length, breadth)
o = o or Shape:new(o, size)
setmetatable(o, self)
self.__index = self
self.area = length * breadth
return o
end
-- 派生类方法 printArea
function Rectangle:printArea ()
print("The Rectangle area = ", self.area)
end
-- 创建对象
myrectangle = Rectangle:new(nil, 10, 20)
myrectangle:printArea()
-- 输出:The Rectangle area = 200
本文是笔者通过下列网站教程学习Lua的记录,有部分修改和补充,转载请注明出处,并附带下面链接。
1.【菜鸟教程Lua教程】
2.【嗨客网Lua教程】