当前位置: 代码迷 >> 综合 >> OpenResty-Lua基础
  详细解决方案

OpenResty-Lua基础

热度:55   发布时间:2024-02-20 03:41:06.0

1、Lua简介

因为项目使用开发使用接触到了lua进行开发。所以在学习中进行了整理记录。

参考手册:https://www.runoob.com/manual/lua53doc/contents.html#contents

Lua是一种很轻量级的的脚本语言,用标准C语言编写的并以源代码形式开放,其设计的目的是为了嵌入应用程序中,从而为应用程序提供灵活的扩展和定制功能。

为什么使用Lua?

Lua很重要的一个功能就是很方便的集成到应用程序内,lua可以直接在运行程序内运行,可以理解为不需要编译过程。

比如使用java开发的应用程序,一般在应用端是不支持编译,打包的。发布流程一般都要经过:在本地编译,本地打包,上传发布到服务器的一个过程。

因为应用端是不具备编译功能的,所以哪怕修复一个小bug,都需要重复去经历一般编译、打包、上传发布。这样的过程是非常耗时,这样的话可以在某些情况下,一些功能不方便重新编写的话,那么就可以使用lua进行开发,进行功能的扩展。这样新增功能或者是修复bug,直接将lua代码推送到应用端,服务就直接运行新的代码了。

Lua的常用应用场景

  • 游戏开发

  • 独立应用系统

  • Web应用脚本,比如:OpenResty

  • 扩展和数据库插件。如:MySQL Proxy

  • 安全系统

2、Lua环境安装

1)、SciTE的安装方式

下载地址:https://github.com/rjpcomputing/luaforwindows/releases

2)、luaDist的方法

下载地址:http://luadist.org/

Lua 官方推荐的方法使用,但是这种方式没有提供IED的,所以只能使用在命令行编码开发,或者借助第三方的IDE编码开发。

注意:不管是SciTE开发,还是借助第三方IDEA编码开发,最终都是要调用lua.exe对代码进行运行。

3、关键词

以下列出了 Lua 的保留关键字。保留关键字不能作为常量或变量或其他用户自定义标示符:

and break do else
elseif end false for
function if in local
nil not or repeat
return then true until
while goto

一般约定,以下划线开头连接一串大写字母的名字(比如 _VERSION)被保留用于 Lua 内部全局变量。

所以定义变量的时候不建议使用下划线+大写字母的方式。

4、数据类型

Lua 是动态类型语言,变量不要类型定义,只需要为变量赋值。 值可以存储在变量中,作为参数传递或结果返回。

Lua 中有 8 个基本类型分别为:nil、boolean、number、string、userdata、function、thread 和 table。

数据类型 描述
nil 这个最简单,只有值nil属于该类,表示一个无效值(在条件表达式中相当于false)。
boolean 包含两个值:false和true。Lua中nil和false为“假”,其它所有值均为“真”
number 表示双精度类型的实浮点数
string 字符串由一对双引号或单引号来表示
function 由 C 或 Lua 编写的函数
userdata 表示任意存储在变量中的C数据结构
thread 表示执行的独立线路,用于执行协同程序
table Lua 中的表(table)其实是一个"关联数组"(associative arrays),数组的索引可以是数字、字符串或表类型。在 Lua 里,table 的创建是通过"构造表达式"来完成,最简单构造表达式是{},用来创建一个空表。
print(type("Hello world"))                 --> string
print(type("Hello" .. "World"))            --> string
print(type(10.4 * 3))                      --> number
print(type(print))                         --> function
print(type(type))                          --> function
print(type(true))                          --> boolean,布尔,true/false;Lua中nil和false为“假”,其它所有值均为“真”
print(type(nil))                           --> nil
print(type(type(X)))                       --> string
table_1 = {
     "apple", "orange", nil }
print(type(table_1))                       --> table
local table_2 = {
    ["id"] = "1",["name"] = "lua"
}
print(type(table_2))                       --> table

需要注意的是,String类型有2种实现方式,一种是使用""的,一种是使用[[]]的。使用""的是不支持格式化的,使用[[]]的是支持格式的,如下:

-- 使用`""`的方式
sql = "select id,name,age from user where id=1"
-- 使用`[[]]`的方式
sql = [[select id,name,agefrom userwhere id =1
]]

5、变量与赋值

Lua 变量有三种类型:全局变量、局部变量、表中的域。

Lua 中的变量全是全局变量,那怕是语句块或是函数里,除非用 local 显式声明为局部变量。

局部变量的作用域为从声明位置开始到所在语句块结束。

a, c, b = "A值", "C值" -- 多个变量同时赋值
print(a, c, b) -- 没有定义的变量为nil
b = 10
print(a, c, b)
b = nil -- 置空一块内存
print(b)
function say()sayHello = "==============Say Hello"; -- 定义在函数里面的全局变量local sayGoodBye = "==============Say GoodBye"; -- 定义在函数里面的局局变量
end
say()
print(sayHello, sayGoodBye)-- 局部变量sayGoodBye,出了函数后就被置空了

在使用的能使用局部变量,尽可能使用局部变量。局部变量在所在的代码结束后面,马上销户,同时也更容易被搜索到。

6、注释

1、--单行注释

-- 这是一行注释

2、--[[]]-- 多行注释

--[[一行注释还是一行注释…… (注释内容在“—[[”和“--]]”之间
--]]

7、table

表示是lua里面非常重要的一种数据结构,表的定义通过构造表达式来完成。如下:

table = {}

通过上面的构造表达式,就完成创建了一个空表。{}可以称为构造表达式。table的数据不限定长度的。

crop = {web = "www.sample.com",telephone = "82325588",staff = {"zhou", "wu", "zheng", "wang"},100083,100191,["home address"] = "beijing",["sports"] = {mon="跑步", tue="游泳", wed="射箭"}
}

上面定义的是一个table样例

1)、table我们可以当做是一个字典来使用。

如果将table当做字典来使用,那么table就会有keyvalue,key是String、number,取值的时候我们是通过key来取值的。如下:

week_table = { Mon = "星期一", Tue = "星期二", Wed = "星期三", [4] = "星期四", [5] = "星期五" } --初始化一个表

说明:

如果是String作为key,key不需要加引号。但是如果keyj加了[],那么字符串类型的key,必须要加引号。

如果number作为key,可以必须加[]

所以一般key,不管是String、number类都建议加[]。如上面的table写为:

week_table = { ["Mon"]= "星期一", ["Tue"]= "星期二", ["Wed"]= "星期三", [4] = "星期四", [5] = "星期五" }

取值方式:

-- 取值方式1
print(week_table.Mon)
print(week_table.Tue)
print(week_table.Wed)
-- 取值方式2
print(week_table["Mon"])
print(week_table["Tue"])
print(week_table["Wed"])
-- 数字类型的key,只能使用改方式取值
print(week_table[4])
print(week_table[5])

如果是字符串的key,那么可以使用.或者[]取值,如果是数字类型的key,那么只能使用[]

2)、table当做数组来使用
week_table = { "星期一", "星期二", "星期三", "星期四", "星期五" }

取值方式:

print(week_table[1])
print(week_table[2])
print(week_table[3])
print(week_table[4])
print(week_table[5])

说明:

table的索引是从1开始,而不是像java数组那样从0开始。

3)、table作为引用使用
table1 = {}
table1[1] = "Lua"
table2 = {}table2 = table1; --table2作为引用使用print(table2[1]) -- 输出lua
table2[1] = nil; 
print(table1[1]) -- 输出nil

如果将来table作为应用来使用,那么table1table2指向的是同一块内存区域。

8、循环

1)数字for

for var=e1,e2,e3 do  <执行体>  
end  

var 从 e1 变化到 e2,每次变化以 e3 为步长递增 var,并执行一次 “执行体”。e3 是可选的,如果不指定,默认为1。

week_table = {
     "星期一", "星期二", "星期三", "星期四", "星期五"}for i = 1, #week_table, 1 doprint(week_table[i])
end或者for i = 1, #week_table do -- 不写e3,默认递增1print(week_table[i])
end

数字for一般是用来遍历数组类型的table,如果是key=value类型的table是遍历不出来的.

如:week_table = { Mon = "星期一", Tue = "星期二", Wed = "星期三", [4] = "星期四", [5] = "星期五" }

这样的table,可以使用泛型for循环。

注意:table的索引是不连续的,如果把星期二删除了,那么索引就会变成,1,3,4,5

2)泛型for(迭代器)

泛型for包含了pairsipairs

pairs可以遍历表中所有的key,并且除了迭代器本身以及遍历表本身还可以返回nil;
ipairs则不能返回nil,key只能数字,如果遇到nil则退出。如果table中,出现的key不是整数key,则不能遍历。

ipairs

table5 = { [3] = "Hello", a = 1, b = 5, x = 22 }
table6 = { [3] = "Hello", a = 1, b = 5, x = 22, "beautiful", ["China"] = "中国" ,"lua"}for k, v in ipairs(table5) do -- table5的[3]="Hello"虽然是数字类型的key,但是因为不存在下标为1和下标为2的数字key,所以遍历会被中断Hello不输出print(k .. ":" .. v)
end
print("------分割线------")
for k, v in ipairs(table6) do -- table6`a=1,b=5,["China"] = "中国"`因为不是整数key,所以不能够被遍历print(k .. ":" .. v)
end

输出结果

------分割线------
1:beautiful
2:lua
3:Hello

ipairs从下标为1开始遍历,然后下标累加1,如果某个下标元素不存在就终止遍历。这就导致如果下标不连续或者不是从1开始的表就会中断或者遍历不到元素。

pairs

table5 = { [3] = "Hello", a = 1, b = 5, x = 22 }
table6 = { [3] = "Hello", a = 1, b = 5, x = 22, "beautiful", ["China"] = "中国" ,"lua"}for k,v in pairs(table5) doprint(k .. ":" .. v)
endprint("------分割线------")for k,v in pairs(table6) doprint(k .. ":" .. v)
end

输出结果

3:Hello
x:22
a:1
b:5
------分割线------
1:beautiful
2:lua
a:1
x:22
3:Hello
China:中国
b:5

pairs会遍历表中全部key,value,并且在输出的过程中先按照索引值打印,打印完成后再按照键值对的键的哈希值打印它的值

while

i = 1
while (i <= 10) doprint("i 的值为:", i)i = i + 1;
end

输出结果

i 的值为:	1
i 的值为:	2
i 的值为:	3
i 的值为:	4
i 的值为:	5
i 的值为:	6
i 的值为:	7
i 的值为:	8
i 的值为:	9
i 的值为:	10

9、function

函数定义

在lua里面使用任何东西都是不需要类型的,索引在定义函数的参数的时候也是不需要类型的。就看使用的时候,自己按照什么样的类型来使用,按照number来使用那么就是number类型,按照String来使用那么就是String类型。函数也可以作为一个类型来使用。

[local] function name(argument1, argument2,argument3..., argumentn)
//函数体
return result
end

local修饰符可以不需要,那么就去全局函数

如下:

function fact(n)if n == 1 thenreturn nelsereturn n * fact(n - 1)end
end
print("function返回结果:" .. fact(3))
fact2 = fact -- 将函数作为一个类型来使用
print("function返回结果:" .. fact(4))

运行结果

function返回结果:6
function返回结果:24

函数作为参数

因为函数也是作为一种数据类型,所以函数也是可以作为参数来传递的,如下:

-- user主函数
function userMain(table, fun)for k, v in pairs(table) dofun(k, v)end
end-- 委托处理函数1
function eat(k, v)if k == "name" and v == "KM" thenprint(v .. "在吃饭")endend-- 委托处理函数2
function sleep(k, v)if k == "name" and v == "KM" thenprint(v .. "在睡觉")end
enduser_table = { id = 1, name = "KM", age = 20 }
userMain(user_table, eat)
userMain(user_table, sleep)

当函数参数是 table 类型或函数类型时,传递进来的是 实际参数的引用.

运行结果

KM在吃饭
KM在睡觉

匿名函数使用

上网的委托处理函数,是单独定义处理,有名字的,可以提供给其他地方调用的。但是有些时候,函数是不需要提供给其他地方调用的,只会自身调用一次,那么就可以使用匿名函数。

上面代码可以改为匿名函数调用,如下:

-- user主函数
function userMain(table, fun)for k, v in pairs(table) dofun(k, v)end
enduser_table = { id = 1, name = "KM", age = 20 }-- 匿名函数调用1
userMain(user_table, function (k, v)
if k == "name" and v == "KM" thenprint(v .. "在吃饭")
end
end)-- 匿名函数调用2
userMain(user_table, function(k, v)
if k == "name" and v == "KM" thenprint(v .. "在睡觉")
end
end)

运行结果

KM在吃饭
KM在睡觉

函数返回多个值

function getIndex()return 1, 2, 3;
end
index_1, index_2, index_3 = getIndex();
print(index_1, index_2, index_3)

多个值需要定义多变量去获取该值,如果变量比返回值少,那么会把缺少的值过滤掉。

如上代码,如果定义index_3,那么之后返回1,2。

运行结果

1	2	3

可以变成参数

local function func( ... )                -- 形参为 ... ,表示函数采用变长参数local temp = {...}                     -- 访问的时候也要使用 ...local ans = table.concat(temp, " ")    -- 使用 table.concat 库函数对数-- 组内容使用 " " 拼接成字符串。print(ans)
endfunc(1, 2)        -- 传递了两个参数
func(1, 2, 3, 4)  -- 传递了四个参数

输出结果

1 2
1 2 3 4

10、模块的定义

在lua中,模块类似于java中的类。模块的定义本质上使用table的特性来完成的。

文件名为 module.lua的模块。

module.lua 文件


-- 定义一个名为 module 的模块,名字可以随便起,可以是User,Account等。
module = {}-- 定义一个常量
module.constant = "这是一个常量"-- 定义一个函数
function module:func1()io.write("这是一个公有函数!\n")
endlocal function func2()print("这是一个私有函数!") --私有函数在模块外不能调用,只能在模块内调用。
endfunction module:func3()func2()
endreturn module

调用模块,使用require函数

require 模块名称,将模块加载。

test_module.lua 文件

require "module" --加载模块print(module.constant) -- 调用常量module.func3() --调用函数

11、元表

元表的介绍

原本在普通表的基础上扩展出来的表,原表的本质也是table,但是这个table有各种各样的元方法。原表的主要作用是对普通表进行行为扩展。

比如:a={},b{}.如果我们想进a+b进行操作,在没有原表的时候,是每办法进行操作的。

比如:

a={1,2,3,4,5}
print(a)

输出的结果是

table: 00EF9ED8

如果我们要改变的它的输出行为,要求print(a)出入1,2,3,4,5。那么我们就可以使用原表改变默认的输出行为。

元表定义

mytable = {}                          -- 普通表
mymetatable = {}                      -- 元表
mytable = setmetatable(mytable,mymetatable)     -- 把 mymetatable 设为 mytable 的元表,返回普通表(mytable)

元表单主要定义一些非常规的操作,用来作为对普通表行为的扩展。

获取原表

getmetatable(mytable)                 -- 这回返回mymetatable

元方法

__index 元方法

__index是在普通表获取不到索引的时候会被调用,这时候我们就可以在__index中对普通表进行一个扩展增强。

mytable = {"lua"}                          -- 普通表
mymetatable = {__index = function(tab, key) --参数是固定的,key是访问的时候,不存在的键;tab就是我们的普通表print("调用了__index。" .. "key是" .. key)return "C++"end
}                      -- 元表
mytable = setmetatable(mytable, mymetatable)     -- 把 mymetatable 设为 mytable 的元表print(mytable[1])
print(mytable[2]) --因为没有下标为2的索引,索引会触发`__index`.

当用户返回一个不存在的索引时,通过元表改变普通表的行为,如果不存在索引,那么就返回一个我们自定义的值。

__index可以是一个函数,也可以是一个表。

  • __index = function(tab,key) end
  • __index = {}

输出结果

lua
调用了__index。key是2
C++

__newindex 元方法

当我们对表的一个新的索引进行修改的时候会调用。

1)__newindex是函数
mytable = { "lua" }                          -- 普通表
mymetatable = {__newindex = function(tab, key, value) -- 参数是固定的print("调用了__newindex。" .. "要修改的key是" .. key .. ".key的键值修改为" .. value)-- tab[key] = value 这样赋值会产生死循环rawset(tab, key, value) --赋值需要使用rawset。end
}                      -- 元表
mytable = setmetatable(mytable, mymetatable)     -- 把 mymetatable 设为 mytable 的元表mytable[1] = "java" --修改的是一个旧的索引,__newindex不会被调用
mytable[2] = "C++" --修改的是一个新的索引,__newindex会被调用
print(mytable[1])
print(mytable[2])

注意:

__newindex函数里面赋值的时候不能使用tab[key] = value,因为key本身就不存在,__newindex又是在key不存在的时候才会触发,索引就会产生一个死循环。所以在__newindex赋值的时候使用rawset函数。

输出结果

调用了__newindex。要修改的key是2.key的键值修改为C++
java
C++
2)__newindex是table

__newindex是一个表的时候,新添加的数据会添加到__newindex对应的表里面。

mytable = { "lua" }-- 普通表
newtable = {}
mymetatable = {__newindex = newtable
}
mytable = setmetatable(mytable, mymetatable)     -- 把 mymetatable 设为 mytable 的元表mytable[1] = "java" --修改的是一个旧的索引,__newindex不会被调用
mytable[2] = "C++" --修改的是一个新的索引,__newindex会被调用
print(mytable[1])
print(mytable[2]) -- 取不到值,因为数据被添加到一个新表了
print(newtable[2]) -- 在新表取到了值

输出结果

java
nil
C++

__add 元方法(操作符元方法)

操作符元方法是在进行操作符运算的时候触发。

mytable = { "lua", "java" }                          -- 普通表
mymetatable = {__add = function(tab, newtab)print("调用了__add。")-- 求tab最大索引local max_index = #tabprint("mytable最大索引是:" .. max_index)-- 将新表数据追加到旧表for i, v in pairs(newtab) dotable.insert(tab, max_index + 1, v)endreturn tabend
}                      -- 元表
mytable = setmetatable(mytable, mymetatable)     -- 把 mymetatable 设为 mytable 的元表newtable = { "c++" }
mytable = mytable + newtable --触发`__add`元方法
print("mytable最大索引是:" .. #mytable)
print(mytable[1])
print(mytable[2])
print(mytable[3])

运行结果

调用了__add。
mytable最大索引是:2
mytable最大索引是:3
lua
java
c++

除了__add操作符元方法外,还有其他的一些操作符元方法,其他操作符元方法如下:

模式 描述
__add 对应的运算符 ‘+’.
__sub 对应的运算符 ‘-’.
__mul 对应的运算符 ‘*’.
__div 对应的运算符 ‘/’.
__mod 对应的运算符 ‘%’.
__unm 对应的运算符 ‘-’.
__concat 对应的运算符 ‘…’.
__eq 对应的运算符 ‘==’.
__lt 对应的运算符 ‘<’.
__le 对应的运算符 ‘<=’.

__call 元方法

__call元方法,可以将一个table当做函数来使用。

mytable = { "lua", "java" }                          -- 普通表
mymetatable = {__call = function(tab, arg1, arg2, arg3) -- arg1参数不限定数量print(arg1, arg2, arg3)table.insert(tab,arg1)table.insert(tab,arg2)table.insert(tab,arg3)return tabend
}                      -- 元表
mytable = setmetatable(mytable, mymetatable)     -- 把 mymetatable 设为 mytable 的元表
value = mytable("C++", "C#", "html")
print(value[1])
print(value[2])
print(value[3])
print(value[4])
print(value[5])

输出结果

lua
java
C++
C#
html

__tostring 元方法

默认表的输出如果直接打印是table: 00EF9ED8这种形式,使用__tostring可以将来表的数据以字符串的形式输出。

mytable = { "lua", "java" }                          -- 普通表
mymetatable = {__add = function(tab, newtab)print("调用了__add。")-- 求tab最大索引local max_index = #tab-- 将新表数据追加到旧表for i, v in pairs(newtab) dotable.insert(tab, max_index + 1, v)endreturn tabend,__tostring = function(tab)print("调用了__tostring。")local str = ""for i, v in pairs(tab) doif str == "" thenstr = velsestr = str .. "," .. vendendreturn "[" .. str .. "]"end
}                      -- 元表
mytable = setmetatable(mytable, mymetatable)     -- 把 mymetatable 设为 mytable 的元表newtable = { "c++" }
mytable = mytable + newtable
print(mytable)

这里是使用了__add元方法和__tostring元方法。一个元表里面可以包含多个元方法。

输出结果

调用了__add。
调用了__tostring。
[lua,java,c++]

12、协同程序(coroutine)

协同程序(协程)是通过改造普通函数达到协同工作的,普通程序一旦启动,那么就会按照循序去执行完毕,协同程序不一样,协程的运行是由开发人员来控制的,并不是由CPU来控制。

在任一指定时刻只有一个协同程序在运行,并且这个正在运行的协同程序只有在明确的被要求挂起的时候才会被挂起。

携程的常用的方法,方法是由coroutine.lua提供。

方法 描述
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 的线程号

协同函数定义和返回值

-- 定义一个协同函数
co = coroutine.create(function(a, b)value1 = a * bvalue2 = a / bcoroutine.yield(value1) -- 函数挂起的时候,返回值可以在yield里面进行传递返回,yield里面也是可以写多个返回值return value1, value2end
)-- resume启动协同函数。resume的第一个返回值是判定是否启动成功,后面的多个返回值是结果集。
flag, res1, res2 = coroutine.resume(co, 10, 2)
print(flag, res1, res2)-- 继续执行协同函数
flag, res1, res2 = coroutine.resume(co)
print(flag, res1, res2)

运行结果

true	20	nil
true	20	5

使用协程分段传递参数

function foo (a)print("foo 函数输出", a)return coroutine.yield(2 * a) -- 返回  2*a 的值
endco = coroutine.create(function(a, b)print("第一次协同程序执行输出", a, b) -- co-body 1 10local 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。返回值是yield挂起的时候在yield函数返回的。
print("--分割线----")
print("main", coroutine.resume(co, "r")) -- true 11 -9.r传递给了foo返回的yield函数,然后继续执行下面的代码
print("---分割线---")
print("main", coroutine.resume(co, "x", "y")) -- true 10 end
print("---分割线---")
print("main", coroutine.resume(co, "x", "y")) -- cannot resume dead coroutine
print("---分割线---")

返回传递参数只有是通过r = coroutine.yield()来完成的。coroutine.resume()传递的参数会作为r = coroutine.yield()r的返回值。

同时coroutine.yield()的参数又会作为下一个coroutine.resume()结果的返回值。

13、面向对象

对象的定义

Person.lua

-- 原型的表
Person = { name = "soso", age = 20 }-------------------------------------------------------
-- 函数名称: 吃东西
-- 功能描述: 对象的方法
-- 参数说明: 
-- 返 回 值: 
-- 备    注:
-------------------------------------------------------
function Person:eat()print(self.name .. "在吃饭")print(self.name .. "的年龄是" .. self.age .. "岁")
end-------------------------------------------------------
-- 函数名称: 构造函数
-- 功能描述: 用于构造对象
-- 参数说明: obj表示新的表,如果不传,那么就会使用原型的表
-- 返 回 值: 构造对象
-- 备    注:
-------------------------------------------------------
function Person:new(obj)t = obj or {}-- 普通表setmetatable(t, {__index = self -- 使用__index的特性,如果调用一个t的索引不存在的时候,那么会在__index所指定的table中查询。}) -- 元表return t;
endperson1 = Person:new()-- 当通过冒号(:)调用的时候,table会自动赋值给self。如果使用点(.)来调用,则要手动赋值。
person2 = Person:new()person1.name = "kiki"
person1:eat(nil)
print("---------------------------")
person2:eat(nil)

说明:

1、对象主要是通过元表的__index元方法的特性来实现,__index是在普通表获取不到索引的时候会被调用,此时我们就可以将self赋值给__index

2、方法通过冒号:调用的时候,table会自动赋值给self。如果使用.来调用,则要手动赋值。所以我们都建议使用:来调用

输出结果

kiki在吃饭
kiki的年龄是20岁
---------------------------
soso在吃饭
soso的年龄是20岁

对象的继承

Student.lua

require "Person"Student = Person:new();
Student.gender = "男" --Student自有的字段,默认值设置
stu1 = Student:new();
stu2 = Student:new();stu1.name = "小明"
print("姓名:"..stu1.name)
print("性别:"..stu1.gender)
print(stu1:eat())

输出结果

姓名:小明
性别:男
小明在吃饭
小明的年龄是20岁

13、运算符

算术运算符

操作符 描述 实例
+ 加法 A + B 输出结果 30
- 减法 A - B 输出结果 -10
* 乘法 A * B 输出结果 200
/ 除法 B / A w输出结果 2
% 取余 B % A 输出结果 0
^ 乘幂 A^2 输出结果 100
- 负号 -A 输出结果 -10

关系运算符

操作符 描述 实例
== 等于,检测两个值是否相等,相等返回 true,否则返回 false (A == B) 为 false。
~= 不等于,检测两个值是否相等,不相等返回 true,否则返回 false (A ~= B) 为 true。
> 大于,如果左边的值大于右边的值,返回 true,否则返回 false (A > B) 为 false。
< 小于,如果左边的值大于右边的值,返回 false,否则返回 true (A < B) 为 true。
>= 大于等于,如果左边的值大于等于右边的值,返回 true,否则返回 false (A >= B) 返回 false。
<= 小于等于, 如果左边的值小于等于右边的值,返回 true,否则返回 false (A <= B) 返回 true。

逻辑运算符

操作符 描述 实例
and 逻辑与操作符。 若 A 为 false,则返回 A,否则返回 B。 (A and B) 为 false。
or 逻辑或操作符。 若 A 为 true,则返回 A,否则返回 B。 (A or B) 为 true。
not 逻辑非操作符。与逻辑运算结果相反,如果条件为 true,逻辑非为 false。 not(A and B) 为 true。

其他运算符

操作符 描述 实例
连接两个字符串 a…b ,其中 a 为 "Hello " , b 为 “World”, 输出结果为 “Hello World”。
# 一元运算符,返回字符串或表的长度。 #“Hello” 返回 5

14、字符串的常见操作API

Lua字符串的操作只要通过lua提供的类库来完成,即string.lua

方法 用途
string.upper(argument) 字符串全部转为大写字母。
string.lower(argument) 字符串全部转为小写字母
string.gsub(mainString,findString,replaceString,num) 在字符串中替换。mainString 为要操作的字符串, findString 为被替换的字符,replaceString 要替换的字符,num 替换次数(可以忽略,则全部替换)
string.find (str, substr, [init, [end]]) 在一个指定的目标字符串中搜索指定的内容(第三个参数为索引),返回其具体位置。不存在则返回 nil
string.reverse(arg) 字符串反转
string.format(…) 返回一个类似printf的格式化字符串。print(string.format(“基本格式化 %s %s”,string1,string2))
string.char(arg) 和 string.byte(arg[,int]) char 将整型数字转成字符并连接, byte 转换字符为整数值(可以指定某个字符,默认第一个字符)。
string.len(arg) 计算字符串长度
string.rep(string, n) 返回字符串string的n个拷贝
链接两个字符串
string.gmatch(str, pattern) 回一个迭代器函数,每一次调用这个函数,返回一个在字符串 str 找到的下一个符合 pattern 描述的子串。如果参数 pattern 描述的字符串没有找到,迭代函数返回nil
string.match(str, pattern, init) string.match()只寻找源字串str中的第一个配对. 参数init可选, 指定搜寻过程的起点, 默认为1。 在成功配对时, 函数将返回配对表达式中的所有捕获结果; 如果没有设置捕获标记, 则返回整个配对字符串. 当没有成功的配对时, 返回nil。

需要主要的是在OpenResty 中,不建议使用 Lua 中的正则表达式string.match(str, pattern, init)

一是因为 Lua 中正则表达式的性能并不如 ngx.re.* 中的正则表达式优秀;

二是 Lua 中的正则表达式并不符合 POSIX 规范,而 ngx.re.* 中实现的是标准的 POSIX 规范,后者明显更具备通用性

15、table常用操作API

Lua的table的操作只要通过lua提供的类库来完成,即table.lua

方法 用途
table.concat (table [, sep [, start [, end]]]) concat是concatenate(连锁, 连接)的缩写. table.concat()函数列出参数中指定table的数组部分从start位置到end位置的所有元素, 元素间以指定的分隔符(sep)隔开。
table.insert (table, [pos,] value) 在table的数组部分指定位置(pos)插入值为value的一个元素. pos参数可选, 默认为数组部分末尾.
table.maxn (table) 指定table中所有正数key值中最大的key值. 如果不存在key值为正数的元素, 则返回0
table.remove (table [, pos]) 返回table数组部分位于pos位置的元素. 其后的元素会被前移. pos参数可选, 默认为table长度, 即从最后一个元素删起。
table.sort (table [, comp]) 对给定的table进行升序排序