Lua使用方式介绍

背景

Lua是C语言开发的脚本语言,设计的目的是为了嵌入到程序中,因此被设计得轻量小巧。Nginx配置中可以直接嵌入Lua 代码或引入Lua 文件,Redis支持运行Lua语句和脚本,Wireshark中使用Lua脚本自定义协议。
本文用于收集常用的语法和API,用作记事本而不会过多深入,内容后续会不断更新。

1.Lua数据类型和变量

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相同。

2.逻辑运算与条件判断

使用 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

3.字符串

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

4.table类型

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在下一次垃圾回收时会清理这个键值对。

5.循环逻辑

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.

6.函数

和变量相同,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类似。

7.对象

Lua本身不具备面向对象的语法,但是可以使用面向对象的思想通过表数据结构模拟出对象。在介绍对象前,有必要提前说明一下元表的概念和一个Lua语法糖。

7.1 元表

元表的概念是定义原始值在特定下的行为。概念比较抽象和难懂,理解元表需要结合使用案例进行。
关联方式
元表是一种特殊的表,每个表可以关联一个元表,通过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用于重载表的操作符(+),类似的还有减法、乘除法等。

7.2 语法糖

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方法的感觉。

7.3 对象

定义一个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
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值