• 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
    
  • 相关阅读:
    从0到一开发微信小程序(1)——申请账号并安装开发环境
    Spring事务属性
    测试员:“我有五年测试经验”HR: “不,你只是把一年的工作经验用了五年”
    基于单片机的养殖场温度控制系统设计
    使用vue-cli脚手架工具搭建vue工程项目以及配置路由
    java计算机毕业设计基于安卓Android的在线心理咨询与健康App(源码+系统+mysql数据库+Lw文档)
    网络安全(黑客)自学
    golang验证Etherscan上的智能合约
    读书笔记:多Transformer的双向编码器表示法(Bert)-2
    轻松学习string类常用接口(附模拟实现)
  • 原文地址:https://blog.csdn.net/Sheng_Q/article/details/139370123