Lua是C语言开发的脚本语言,设计的目的是为了嵌入到程序中,因此被设计得轻量小巧。Nginx配置中可以直接嵌入Lua 代码或引入Lua 文件,Redis支持运行Lua语句和脚本,Wireshark中使用Lua脚本自定义协议。
本文用于收集常用的语法和API,用作记事本而不会过多深入,内容后续会不断更新。
Lua有8种基本类型:
[1] boolean布尔: true和false;在Lua中,仅false和nil表示假;其他为真,这与部分语言中默认值为假不同;
[2] number数值: 整数和浮点数;
[3] string字符串:由单引号或者双引号包含的部分;
[4] function函数: 由C语言或者LUA定义的函数;
[5] table表:灵活的数据结构,可以用作数组、哈希表、集合等多种数据类型;
[6] nil: 空对象、空值、或者未声明变量的值;
[7] userdata: C语言数据结构,lua不能直接访问和操作这些数据,只能通过C语言的API来操作;
[8] thread类型: 在Lua中表示一个协程,用于实现并发。
lua作为动态语言,声明变量时无需指定变量的类型,赋值决定类型,之后可通过type()函数查看变量的类型.
var1 = true
var2 = 100
var3 = 100.01
var4 = "test"
var5 = function(a)
print(a)
end
var6 = {}
var7 = nil
print("type var1 is:" .. type(var1))
print("type var2 is:" .. type(var2))
print("type var3 is:" .. type(var3))
print("type var4 is:" .. type(var4))
print("type var5 is:" .. type(var5))
print("type var6 is:" .. type(var6))
print("type var7 is:" .. type(var7))
运行结果如下:
type var1 is:boolean
type var2 is:number
type var3 is:number
type var4 is:string
type var5 is:function
type var6 is:table
type var7 is:nil
变量有全局变量和局部变量之分,全局变量全局有效,而局部变量(在声明时添加local)仅在所在语句块内有效:
-- a为全局变量
>a=0
-- b为局部变量
>local b='ss'
变量之间的比较,使用 == 表示相等, ~=表示不等:
>local a=1
>print(a==1)
true
>print(a~=1)
false
数值方面的大小比较以及基本运算与Java相同。
使用 and or not 表示与或非:
>local b="ss"
>print(b=="s" or b=="ss")
true
Lua的if语法为:
if 条件 then
逻辑
end
if 条件 then
逻辑1
else
逻辑2
end
if 条件1 then
逻辑1
elseif 条件2 then
逻辑2
else
逻辑3
end
Lua中定义字符串时,可用单引号或者双引号。
local str = "hello world"
[1] 长度
-- string.len(arg)返回字符串长度
>local str = "hello world"
>print(string.len(str))
11
[2] 拼接
--使用..进行字符串拼接
>local str = "hello world"
>str_print = "str is : " .. str .. "."
>print(str_print)
str is : hello world.
[3] 大小写转换
--全部转为大写string.upper(arg):
--全部转为小写string.lower(arg):
>local str = "hello"
>strUpper = string.upper(str)
>print("strUpper: " .. strUpper)
strUpper: HELLO
>strLower = string.lower(strUpper)
>print("strLower: " .. strLower)
strUpper: HELLO
[4] 是否包含
-- string.find(str, "test") 返回"test"在str中的其实索引,如果不存在,返回nil
>str = "test1"
>find_result = string.find(str, "test")
>print(find_result)
1
--可通过是否等于nil来判断是否包含
str = "test1"
if string.find(str, "test") ~= nil then
print("yes")
else
print("no")
end
[5] 截取
-- string.sub(str, i [, j]) 将str从i截取到j位,如果省略j表示截取到字符串尾部
>local str = "hello world"
>local str_sub1=string.sub(str, 1)
>print("str_sub1 is " .. str_sub1)
str_sub1 is hello world
>local str_sub15=string.sub(str,1,5)
>print("str_sub15 is " .. str_sub15)
str_sub15 is hello
使用string.sub(str, i [, j]方法时注意Lua中位置索引从1开始而不是0开始,j是未知索引而不是长度,i和j都为闭区间。
[6] 删除空格
function trim(inputString)
return string.gsub(inputString, "^%s*(.-)%s*$", "%1")
end
-- 示例用法:
local myString = " Hello, World! "
local trimmedString = trim(myString) -- 返回 "Hello, World!"
print(trimmedString) -- 输出: Hello, World!
[7] 替换
--string.gsub(str, "test", "hello")
-- 将str字符串中的test改成hello
>local str = "test1test2test"
>local result = string.gsub(str, "test", "hello")
>print(result)
hello1hello2hello
table可以被当做数组或者哈希表使用。
数组:
-- 1.声明方式:使用大括号
local myArray = {"a", "b", "c"}
--2.获取长度:通过#数组名,获取数组长度
>print(#myArray)
3
--3.添加元素
--通过table.insert(myArray, e) 添加元素
>local emptyTable = {}
>table.insert(emptyTable,"x")
>table.insert(emptyTable,"y")
>print(#emptyTable)
2
--也可以直接通过下标获取、设置、添加元素
>emptyTable[1]="x1"
>print(emptyTable[1])
x1
--4.删除元素
table.remove(myArray) -- 删除myArray数组最后一个元素
table.remove(myArray, i) --删除myArray数组的第i个元素
--5.遍历
local myArray = {"a", "b", "c"}
for k, v in pairs(myArray) do
print(v) -- v为数组元素 a b c
end
其中local emptyTable = {}
声明了一个数组,执行table.insert(emptyTable,"x")
向数组中添加了一个元素"x", 等价于emptyTable[1] ="x"
.
其他语言中,一般下标是下标,不会认为是整数类型的Key; 在Lua语言中,可以认为emptyTable[1] ="x"
是向哈希表emptyTable中添加了一个键值对,键是1,值是x.
哈希表:
--1.声明哈希表,使用{}
local myHash = {name = "sy", role = "role_a"}
--2.添加元素
myHash.age=18
--3.删除元素
myHash.age = nil
--4.遍历
local myHash = {name = "sy", role = "role_a"}
for k, v in pairs(myHash) do
print(k,v) -- k为键,v为值
end
注意:当哈希表中的键的值被设置为nil时,Lua在下一次垃圾回收时会清理这个键值对。
while循环
while condition do
-- 这里是循环体,只要condition为真,就会不断执行这里的代码
end
--案例如下:
local i = 1
while i <= 5 do
print(i)
i = i + 1
end
for循环
前面介绍table时已经用过for循环,除了遍历数组和哈希表外,还可以指定循环次数:
-- initial 是起始值,limit 是结束值,step 是步长(步长可以是正数也可以是负数,默认为 1)
for var = initial, limit, step do
-- 这里是循环体
end
--案例如下:
local myArray = {"a", "b", "c"}
for i = 1, #myArray do
print(myArray[i])
end
注意:Lua有goto,但是没有continue和break.
和变量相同,Lua定义函数时可以通过local指定作用域: 全局或者局部。
使用关键字function声明函数,模板如下所示:
function myFunName()
--函数逻辑
end
(1) 函数入参
可以在声明时添加参数
function myFunName(arg1,arg2,agr3)
--函数逻辑
end
--案例如下:
function printArgsFunc(arg1, arg2, arg3)
print("args is: " .. arg1 .. arg2 .. arg3)
end
>printArgsFunc('a','b','c')
args is: abc
(2) 返回值
当函数需要返回数据时,通过添加return语句返回;和python相似,返回多个数据时,使用逗号分隔。
function myFunName(arg1,arg2,agr3)
--函数逻辑
return arg1,arg2,agr3
end
--案例如下:
function getArgsFunc(arg1, arg2, arg3)
return arg1, arg2, arg3
end
>local result1, result2, result3, result4 = getArgsFunc("a","b","c")
>print("result1 is: " .. result1)
>print("result2 is: " .. result2)
>print("result3 is: " .. result3)
>print(result4)
result1 is: a
result2 is: b
result3 is: c
nil
由于Lua是脚本语言,因此需要先定义再调用,调用方式与python类似。
Lua本身不具备面向对象的语法,但是可以使用面向对象的思想通过表数据结构模拟出对象。在介绍对象前,有必要提前说明一下元表的概念和一个Lua语法糖。
元表的概念是定义原始值在特定下的行为。概念比较抽象和难懂,理解元表需要结合使用案例进行。
关联方式
元表是一种特殊的表,每个表可以关联一个元表,通过setmetatable(表,元表)
方法关联:
t = {}
mt = {}
-- 表t关联mt元表
setmetatable(t,mt)
__index属性
当从表t中查询不存在的key时,转向元表mt的__index属性。有两种情况:
[1] __index为表:直接从__index表中根据key取值
>t = {}
>mt = {
__index={ a=1, b=2 }
}
>setmetatable(t,mt)
>print(t.a)
1
>print(t.c)
nil
分析: mt为t的元表,访问t.a和t.c时,因为t表中不存在a和c属性,请求会转向元表的__index属性,
此时__index属性是元表且其中有a属性—返回1,没有c属性—返回nil.
[2] __index为函数:将表t和key作为参数传递给__index函数
>t = {}
>mt = {
__index=function(t, key)
return "sy"
end
}
>setmetatable(t,mt)
>print(t.a)
sy
>print(t.c)
sy
分析: mt为t的元表,访问t.a和t.c时,因为t表中不存在a和c属性,请求会转向元表的__index属性,
此时__index为方法,将t和key传递给__index方法,该方法返回固定值“sy”, 因此t.a和t.c得到的结果均为"sy".
__newindex属性
当向表t中添加新的key时,转向元表mt的__newindex属性。有两种情况:
[1] __newindex为表:直接添加到__newindex表中根据key取值
>t = {}
>mt = {
__newindex={}
}
>setmetatable(t,mt)
>t.a="aa"
>print(t.a)
nil
>print(mt.__newindex.a)
aa
分析:mt为t的元表,向t中添加元素时,转向元表mt的__newindex属性,此时该属性为表,将元素添加到__newindex中。
[2] __newindex为函数:将表t和key作为参数传递给__newindex函数
>t = {}
>mt = {
__newindex=function(t,k,v)
rawset(t,k,v)
end
}
>setmetatable(t, mt)
>t.a="aa"
>print(t.a)
aa
分析:mt为t的元表,向t中添加元素时,转向元表mt的__newindex属性,此时该属性为函数,将t,k,v作为参数传递给__newindex函数;其中rawset(t,k,v)函数将 k,v设置到t表中。
其他属性:
__add用于重载表的操作符(+),类似的还有减法、乘除法等。
Lua调用表中的方法时,如果需要将自己作为参数传递,可以使用冒号调用方法,从而省略第一个参数,如下所示:
table.func(table,arg1,argn)
-- 等价于
table:func(arg1,argn)
以下通过一个案例进行说明。
t = {
name="sy",
getName = function(t)
return t.name
end
}
print(t.getName(t))
上述代码定义了一个表t, 内部定义了一个name属性和一个getName方法:getName方法接受一个表参数,并返回这个表的name属性,此时获取t的name属性需要传参t。
上面的方法可以修改一下,将t用self修改一下:
t = {
name="sy",
getName = function(self)
return self.name
end
}
print(t.getName(t))
-- 等价于print(t:getName())
如此,使用getName方法的人,不会为getName传递其他表参数,认为这是特定为t表定制的方法。
print(t.getName(t))
可以使用语法糖print(t:getName())
代替。t:getName()
给人一种调用t对象的getName方法的感觉。
定义一个Person表,只有一个name属性和一个getName方法:
>Person={
name = "sy",
getName = function(self)
return self.name;
end
}
>print(Person:getName())
sy
为Person添加一个new方法:
function Person:new()
local t = {}
setmetatable(t,{__index=self})
return t
end
通过Person的new方法可以创建一个新Person表,此时存在3个表:Person表,匿名表,t表(新Person表):
new方法中声明了一个t表并在函数调用结束时返回t表,即调用new方法最终得到的新Person表是t表;
{__index=self}定义了一个匿名表;通过setmetatable方法将匿名表设置为t表的元表;
而匿名表的__index属性为self,即Person表;
此时,当获取 t表中不存在的属性或者方法时,会转向元表的__index属性,即Person表。
因此,可通过新Person表访问Person表的属性和方法:
>local p = Person:new()
>print(p:getName())
sy
进一步地,可以为new方法添加一个元表参数,该元表将继承Person表的属性:
function Person:new(tableArg)
local t = tableArg or {}
setmetatable(t,{__index=self})
return t
end
此时, 通过new方法得到的元表不仅拥有Person表的属性,还保留tableArg表自身的属性:
local Student = { class = 4 }
>local stuInstance = Person:new(Student)
>print(stuInstance.class)
4
>print(stuInstance:getName())
sy