动力节点(动力哥视频)Redis7笔记-第八章Luau脚本详解

8 Lua脚本详解

8.1 Lua简介

Lua是一个由标准C语言开发的、开源的、可扩展的、轻量级的、弱类型的、解释型脚本语言。

8.2 Linux系统的Lua

8.2.1 Lua下载

若要使用Lua则需要先从官网下载其源码并安装。

8.2.2 Lua安装

先将下载好的Lua源码上传到Linux,然后再进行安装。

8.2.2.1 解压

将Lua源码解压到/opt/apps目录。

进入到/opt/apps下的lua目录可以看到编译用的Makefile文件及源码目录src。

8.2.2.2 安装gcc

由于Lua是由C/C++语言编写的,所以对其进行编译就必须要使用相关编译器。对于C/C++语言的编译器,使用最多的是gcc。

8.2.2.3 编译

执行编译命令make linux test。

8.2.2.4 安装


安装完毕后,可以通过lua –v查看版本号,与前面make linux test中最后显示的结果是相同的。

8.2.3 Hello World

8.2.3.1 两种交互模式

Lua为用户提供了两种交互模式:命令行模式与脚本文件模式。

8.2.3.1.1 命令行模式

该模式是,直接在命令行中输入语句,回车即可看到运行结果。

在任意目录下使用lua命令进入lua命令行模式,在其中输入语句后回车即可运行显示出结果。使用Ctrl + C退出模式。
需要注意,lua对语句后的分号要求不是强制性的,有没有都行。

8.2.3.1.2 脚本文件模式

该模式是先要编写脚本文件,然后再使用lua命令来运行文件。
例如,在当前用户主目录中新建一个script目录,在其中创建一个名称为hello.lua的文件,文件中就写一名print()语句即可。


然后直接运行“lua 脚本文件”即可看到结果。

8.2.3.2 两种脚本运行方式

对于脚本文件的运行有两种方式。一种是上面的lua命令方式,还有一种是可执行文件方式。可执行文件方式是,将lua脚本文件直接修改为可执行文件运行。
下面就使用第二种方式来运行。

8.2.3.2.1 修改脚本文件内容

在脚本文件第一行增加#!/usr/bin/lua,表示当前文件将使用/usr/bin/lua命令来运行。

8.2.3.2.2 修改脚本文件权限

为脚本文件赋予可执行权限。

8.2.3.2.3 运行

直接使用文件名即可运行。

8.3 Win系统的Lua

这里要安装的是在Windows系统中Lua的运行环境。最常用的为SciTE。
SciTE是一款Lua脚本测试编辑器,提供Lua的编辑运行环境。SciTE提供了两种运行方式:命令行窗口运行方式与Lua脚本的编辑运行环境。
除了SciTE,还有像LuaDist、LuaRocks等。

8.3.1 x

8.4 Lua语法基础

8.4.1 注释

Lua的行注释为两个连续的减号,段注释以--[[开头,以--]]结尾。

不过,在调试过程中如果想临时取消段注释,而直接将其标识删除,这样做其实并不好。因为有可能还需要再添加上。而段注释的写法相对较麻烦。所以,Lua给出了一种简单处理方式:在开头的–[[前再加一个减号,即可使段注释不起作用。其实就是使两个段注释标识变为了两个行注释。

8.4.2 数据类型

Lua 中有 8 种类型,分别为:nil、boolean、number、string、userdata、function、thread 和 table。通过type()函数可以查看一个数据的类型,例如,type(nil)的结果为nil,type(123)的结果为number。

数据类型 描述
nil 只有值nil属于该类,表示一个无效值,与Java中的null类似。但在条件表达式中相当于false。
boolean 包含两个值:false和true。
number 表示双精度类型的实浮点数。
string 字符串,由一对双引号或单引号括起来。当一个字符串包含多行时,可以在第一行中以[[开头,在最后一行中以]]结尾,那么在[[与]]括起来的这多行内容就是一个字符串。换行符为字符串”\n”。
table 类似于Java中的数组,但比数组的功能更强大,更灵活。
function 由 C 或 Lua 编写的函数。
thread 协同线程,是协同函数的执行体,即正在执行的协同函数。
userdata 一种用户自定义数据,用于表示一种由应用程序或 C/C++ 语言库所创建的类型,可以将任意 C/C++ 的任意数据类型的数据存储到 Lua 变量中调用。

8.4.3 标识符

程序设计语言中的标识符主要包含保留字、变量、常量、方法名、函数名、类名等。Lua的标识符由字母、数字与下划线组成,但不能以数字开头。Lua是大小写敏感的。

8.4.3.1 保留字

Lua常见的保留字共有22个。不过,除了这22个外,Lua中还定义了很多的内置全局变量,这些内置全局变量的一个共同特征是,以下划线开头后跟全大写字母。所以我们在定义自己的标识符时不能与这些保留字、内置全局变量重复。

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

8.4.3.2 变量

Lua是弱类型语言,变量无需类型声明即可直接使用。变量分为全局变量与局部变量。Lua中的变量默认都是全局变量,即使声明在语句块或函数里。全局变量一旦声明,在当前文件中的任何地方都可访问。局部变量local相当于Java中的private变量,只能在声明的语句块中使用。

8.4.3.3 动态类型

Lua是动态类型语言,变量的类型可以随时改变,无需声明。

8.4.4 运算符

运算符是一个特殊的符号,用于告诉解释器执行特定的数学或逻辑运算。Lua提供了以下几种运算符类型:

  • 算术运算符
  • 关系运算符
  • 逻辑运算符
  • 其他运算符

8.4.4.1 算术运算符

下表列出了 Lua 语言中的常用算术运算符,设定 A 的值为10,B 的值为 20:

操作符 描述 示例
+ 加法 A + B 输出结果 30
- 减法 A - B 输出结果 -10
* 乘法 A * B 输出结果 200
/ 除法 5 / 2 输出结果 2.5
% 取余 B % A 输出结果 0
^ 乘幂 A^2 输出结果 100
- 负号 -A 输出结果 -10
// 整除运算符(>=lua5.3) 5//2 输出结果 2


注意,

  • SciTE对Lua支持的目前最高版本为5.1,而整除运算符//需要在Lua5.3版本以上,所以当前SciTE中无法看到效果。
  • 在命令行模式中,直接输入变量名回车,即相当于print()函数输出该变量。

8.4.4.2 关系运算符

下表列出了 Lua 语言中的常用关系运算符,设定 A 的值为10,B 的值为 20:

操作符 描述 示例
== 等于 (A == B) 为 false。
~= 不等于 (A ~= B) 为 true。
> 大于 (A > B) 为 false。
< 小于 (A < B) 为 true。
>= 大于等于 (A >= B) 返回 false。
<= 小于等于 (A <= B) 返回 true。

8.4.4.3 逻辑运算符

注意,Lua系统将false与nil作为假,将true与非nil作为真,即使是0也是真。
下表列出了 Lua 语言中的常用逻辑运算符,设定 A 的值为 true,B 的值为 false:

操作符 描述 示例
and 逻辑与 (A and B) 为 false。
or 逻辑或 (A or B) 为 true。
not 逻辑非 not(A and B) 为 true。

8.4.4.4 其他运算符

下表列出了 Lua 语言中的连接运算符与计算表或字符串长度的运算符:

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

8.4.5 函数

Lua中函数的定义是以function开头,后跟函数名与参数列表,以end结尾。其可以没有返回值,也可以一次返回多个值。

8.4.5.1 固定参函数

Lua中的函数在调用时与Java语言中方法的调用是不同的,其不要求实参的个数必须与函数中形参的个数相同。如果实参个数少于形参个数,则系统自动使用nil填充;如果实参个数多于形参个数,多出的将被系统自动忽略。

8.4.5.2 可变参函数

在函数定义时不给出具体形参的个数,而是使用三个连续的点号。在函数调用时就可以向该函数传递任意个数的参数,函数可以全部接收。

8.4.5.3 可返回多个值

Lua中的函数一次可以返回多个值,但需要有多个变量来同时接收。

8.4.5.4 函数作为参数

Lua的函数中,允许函数作为参数。而作为参数的函数,可以是已经定义好的普通函数,也可以是匿名函数。

8.4.6 流程控制语句

Lua提供了if作为流程控制语句。

8.4.6.1 if语句

Lua提供了if…then用于表示条件判断,其中if的判断条件可以是任意表达式。Lua系统将false与nil作为假,将true与非nil作为真,即使是0也是真。
需要注意,Lua中的if语句的判断条件可以使用小括号括起来,也可以不使用。

8.4.6.2 if嵌套语句

Lua中提供了专门的关键字elseif来做if嵌套语句。注意,不能使用else与if两个关键字的联用形式,即不能使用else if来嵌套if语句。

8.4.7 循环控制语句

Lua提供了四种循环控制语句:while…do循环、repeat…until循环、数值for循环,及泛型for循环。同时,Lua还提供了break与goto两种循环流程控制语句。

8.4.7.1 while…do

只要while中的条件成立就一直循环。

8.4.7.2 repeat…until

until中的条件成立了,循环就要停止。

8.4.7.3 数值for

这种for循环只参用于循环变量为数值型的情况。其语法格式为:
for var=exp1, exp2, exp3 do
循环体
end
var为指定的循环变量,exp1为循环起始值,exp2为循环结束值,exp3为循环步长。步长可省略不写,默认为1。每循环一次,系统内部都会做一次当前循环变量var的值与exp2的比较,如果var小于等于exp2的值,则继续循环,否则结束循环。

8.4.7.4 泛型for

泛型for用于遍历table中的所有值,其需要与Lua的迭代器联合使用。后面table学习时再详解。

8.4.7.5 break

break语句可以提前终止循环。其只能用于循环之中。

8.4.7.6 goto

goto语句可以将执行流程无条件地跳转到指定的标记语句处开始执行,注意,是开始执行,并非仅执行这一句,而是从这句开始后面的语句都会重新执行。当然,该标识语句在第一次经过时也是会执行的,并非是必须由goto跳转时才执行。
语句标记使用一对双冒号括起来,置于语句前面。goto语句可以使用在循环之外。
注意,Lua5.1中不支持双冒号的语句标记。

8.5 Lua语法进阶

8.5.1 table

8.5.1.1 数组

使用table可以定义一维、二维、多维数组。不过,需要注意,Lua中的数组索引是从1开始的,且无需声明数组长度,可以随时增加元素。当然,同一数组中的元素可以是任意类型。

8.5.1.2 map

使用table也可以定义出类似map的key-value数据结构。其可以定义table时直接指定key-value,也可单独指定key-value。而访问时,一般都是通过table的key直接访问,也可以数组索引方式来访问,此时的key即为索引。

8.5.1.3 混合结构

Lua允许将数组与key-value混合在同一个table中进行定义。key-value不会占用数组的数字索引值。

8.5.1.4 table操作函数

Lua中提供了对table进行操作的函数。

8.5.1.4.1 table.concat()

【函数】table.concat (table [, sep [, start [, end]]]):
【解析】该函数用于将指定的table数组元素进行字符串连接。连接从start索引位置到end索引位置的所有数组元素, 元素间使用指定的分隔符sep隔开。如果table是一个混合结构,那么这个连接与key-value无关,仅是连接数组元素。

8.5.1.4.2 table.unpack()

【函数】table.unpack (table [, i [, j]])
【解析】拆包。该函数返回指定table的数组中的从第i个元素到第j个元素值。i与j是可选的,默认i为1,j为数组的最后一个元素。Lua5.1不支持该函数。

8.5.1.4.3 table.pack()

【函数】table.pack (…)
【解析】打包。该函数的参数是一个可变参,其可将指定的参数打包为一个table返回。这个返回的table中具有一个属性n,用于表示该table包含的元素个数。Lua5.1不支持该函数。

8.5.1.4.4 table.maxn()

【函数】table.maxn(table)
【解析】该函数返回指定table的数组中的最大索引值,即数组包含元素的个数。

8.5.1.4.5 table.insert()

【函数】table.insert (table, [pos,] value):
【解析】该函数用于在指定table的数组部分指定位置pos插入值为value的一个元素。其后的元素会被后移。pos参数可选,默认为数组部分末尾。

8.5.1.4.6 table.remove ()

【函数】table.remove (table [, pos])
【解析】该函数用于删除并返回指定table中数组部分位于pos位置的元素。其后的元素会被前移。pos参数可选,默认删除数组中的最后一个元素。

8.5.1.4.7 table.sort()

【函数】table.sort(table [,fun(a,b)])
【解析】该函数用于对指定的table的数组元素进行升序排序,也可按照指定函数fun(a,b)中指定的规则进行排序。fun(a,b)是一个用于比较a与b的函数,a与b分别代表数组中的两个相邻元素。
注意:

  • 如果arr中的元素既有字符串又有数值型,那么对其进行排序会报错。
  • 如果数组中多个元素相同,则其相同的多个元素的排序结果不确定,即这些元素的索引谁排前谁排后,不确定。
  • 如果数组元素中包含nil,则排序会报错。

8.5.2 迭代器

Lua提供了两个迭代器pairs(table)与ipairs(table)。这两个迭代器通常会应用于泛型for循环中,用于遍历指定的table。这两个迭代器的不同是:

  • ipairs(table):仅会迭代指定table中的数组元素。
  • pairs(table):会迭代整个table元素,无论是数组元素,还是key-value。

8.5.3 模块

模块是Lua中特有的一种数据结构。从 Lua 5.1 开始,Lua 加入了标准的模块管理机制,可以把一些公用的代码放在一个文件里,以 API 接口的形式在其他地方调用,有利于代码的重用和降低代码耦合度。
模块文件主要由table组成。在table中添加相应的变量、函数,最后文件返回该table即可。如果其它文件中需要使用该模块,只需通过require将该模块导入即可。

8.5.3.1 定义一个模块

模块是一个lua文件,其中会包含一个table。一般情况下该文件名与该table名称相同,但其并不是必须的。

8.5.3.2 使用模块

这里要用到一个函数require(“文件路径”),其中文件名是不能写.lua扩展名的。该函数可以将指定的lua文件静态导入(合并为一个文件)。不过需要注意的是,该函数的使用可以省略小括号,写为require “文件路径”。
require()函数是有返回值的,返回的就是模块文件最后return的table。可以使用一个变量接收该table值作为模块的别名,就可以使用别名来访问模块了。

8.5.3.3 再看模块

模块文件中一般定义的变量与函数都是模块table相关内容,但也可以定义其它与table无关的内容。这些全局变量与函数就是普通的全局变量与函数,与模块无关,但会随着模块的导入而同时导入。所以在使用时可以直接使用,而无需也不能添加模块名称。

8.5.4 元表与元方法

元表,即Lua中普通table的元数据表,而元方法则是元表中定义的普通表的默认行为。Lua中的每个普通table都可为其定义一个元表,用于扩展该普通table的行为功能。例如,对于table与数值相加的行为,Lua中是没有定义的,但用户可通过为其指定元表来扩展这种行为;再如,用户访问不存在的table元素,Lua默认返回的是nil,但用户可能并不知道发生了什么。此时可以通过为该table指定元表来扩展该行为:给用户提示信息,并返回用户指定的值。

8.5.4.1 重要函数

元表中有两个重要函数:

  • setmetatable(table,metatable):将metatable指定为普通表table的元表。
  • getmetatable(table):获取指定普通表table的元表。

8.5.4.2 _ _index元方法

当用户在对table进行读取访问时,如果访问的数组索引或key不存在,那么系统就会自动调用元表的_ index元方法。该重写的方法可以是一个函数,也可以是另一个表。如果重写的 _index元方法是函数,且有返回值,则直接返回;如果没有返回值,则返回nil。

8.5.4.3 _ _newindex元方法

当用户为table中一个不存在的索引或key赋值时,就会自动调用元表的_ newindex元方法。该重写的方法可以是一个函数,也可以是另一个表。如果重写的 _newindex元方法是函数,且有返回值,则直接返回;如果没有返回值,则返回nil。

8.5.4.4 运算符元方法

如果要为一个表扩展加号(+)、减号(-)、等于(==)、小于(<)等运算功能,则可重写相应的元方法。
例如,如果要为一个table扩展加号(+)运算功能,则可重写该table元表的_ add元方法,而具体的运算规则,则是定义在该重写的元方法中的。这样,当一个table在进行加法(+)运算时,就会自动调用其元表的 _add元方法。

类似于加法操作的其它操作,Lua中还包含很多:

元方法 说明 元方法 说明
__add 加法,+ __band 按位与,&
__sub 减法,- __bor 按位或,|
__mul 乘法,* __bxor 按位异或,~
__div 除法,/ __bnot 按位非,~
__mod 取模,% __shl 按位左移,<<
__pow 次幂,^ __shr 按位右移,>>
__unm 取反,-
__idiv 取整除法,// __eq 等于,==
__lt 小于,<
__concat 字符串连接,… __le 小于等于,<=
__len 字符串长度,#

8.5.4.5 _ _tostring元方法

直接输出一个table,其输出的内容为类型与table的存放地址。如果想让其输出table中的内容,可重写_ _tostring元方法。


8.5.4.6 _ _call元方法

当将一个table以函数形式来使用时,系统会自动调用重写的_ _call元方法。该用法主要是可以简化对table的相关操作,将对table的操作与函数直接相结合。

8.5.4.7 元表单独定义

为了便于管理与复用,可以将元素单独定义为一个文件。该文件中仅可定义一个元表,且一般文件名与元表名称相同。
若一个文件要使用其它文件中定义的元表,只需使用require “元表文件名”即可将元表导入使用。
如果用户想扩展该元表而又不想修改元表文件,则可在用户自己文件中重写其相应功能的元方法即可。

8.5.5 面向对象

Lua中没有类的概念,但通过table、function与元表可以模拟和构造出具有类这样功能的结构。

8.5.5.1 简单对象的创建

Lua中通过table与fcuntion可以创建出一个简单的Lua对象:table为Lua对象赋予属性,通过function为Lua对象赋予行为,即方法。

8.5.5.2 类的创建

Lua中使用table、function与元表可以定义出类:使用一个表作为基础类,使用一个function作为该基础类的new()方法。在该new()方法中创建一个空表,再为该空表指定一个元表。该元表重写_ index元方法,且将基础表指定为重写的 _index元方法。由于new()中的表是空表,所以用户访问的所有key都会从基础类(表)中查找。

8.5.6 协同线程与协同函数

8.5.6.1 协同线程

Lua中有一种特殊的线程,称为coroutine,协同线程,简称协程。其可以在运行时暂停执行,然后转去执行其它线程,然后还可返回再继续执行没有执行完毕的内容。即可以“走走停停,停停再走走”。
协同线程也称为协作多线程,在Lua中表示独立的执行线程。任意时刻只会有一个协程执行,而不会出现多个协程同时执行的情况。
协同线程的类型为thread,其启动、暂停、重启等,都需要通过函数来控制。下表是用于控制协同线程的基本方法。

方法 描述
create(function) 创建一个协同线程实例,即返回的是thread类型。参数是一个function。其需要通过resume()来启动协同线程的执行
resume(thread, …) 启动指定的协同线程的执行,使其从开始处或前面挂起处开始执行。可以向create()的内置函数传递相应的参数。如果内置函数具有返回值,resume()会全部接收并返回。
running() 返回正在运行的协同线程实例,即thread类型值
yield() 挂起协同线程,并将协同线程设置为挂起状态。resume()可从挂起处重启被挂起的协同线程
status(thread) 查看协同线程的状态。状态有三种:运行态running,挂起态suspended,消亡态dead
close() 关闭协同线程
wrap(function) 创建一个协同函数,返回的是function类型。一旦调用该函数就会创建并执行一个协同线程实例

8.5.6.2 协同函数

协同线程可以单独创建执行,也可以通过协同函数的调用启动执行。使用coroutine的wrap()函数创建的就是协同函数,其类型为function。
由于协同函数的本质就是函数,所以协同函数的调用方式就是标准的函数调用方式。只不过,协同函数的调用会启动其内置的协同线程。

8.5.7 文件IO

Lua中提供了大量对文件进行IO操作的函数。这些函数分为两类:静态函数与实例函数。所谓静态函数是指通过io.xxx()方式对文件进行操作的函数,而实例函数则是通过Lua中面向对象方式操作的函数。

8.5.7.1 常用静态函数

8.5.7.1.1 io.open()

【格式】io.open (filename [, mode])
【解析】以指定模式打开指定文件,返回要打开文件的句柄,就是一个对象(后面会讲Lua中的对象)。其中模式mode有三种,但同时还可配合两个符号使用:

  • r:只读,默认模式
  • w:只写,写入内容会覆盖文件原有内容
  • a:只写,以追加方式写入内容
  • +:增加符,在r+、w+、a+均变为了读写
  • b:二进制表示符。如果要操作的文件为二进制文件,则需要变为rb、wb、ab。
8.5.7.1.2 io.input()

【格式】io.input (file)
【解析】指定要读取的文件。

8.5.7.1.3 io.output()

【格式】io.output (file)
【解析】指定要写入的文件。

8.5.7.1.4 io.read()

【格式】io.read([format])
【解析】以指定格式读取io.input()中指定的输入文件。其中format格式有:

  • *l:从当前位置的下一个位置开始读取整个行,默认格式
  • *n:读取下一个数字,其将作为浮点数或整数
  • *a:从当前位置的下一个位置开始读取整个文件
  • number:这是一个数字,表示要读取的字符的个数
8.5.7.1.5 io.write()

【格式】io.write(data)
【解析】将指定的数据data写入到io.output()中指定的输出文件。

8.5.7.2 常用实例函数

8.5.7.2.1 file:read()

这里的file使用的是io.open()函数返回的file,其实际就是Lua中的一个对象。其用法与io.read()的相同。

8.5.7.2.2 file:write()

用法与io.write()的相同。

8.5.7.2.3 file:seek()

【格式】file:seek ([whence [, offset]])
【解析】该函数用于获取或设置文件读写指针的当前位置。位置从1开始计数,除文件最后一行外,每行都有行结束符,其会占两个字符位置。位置0表示文件第一个位置的前面位置。
当seek()为无参时会返回读写指针的当前位置。参数whence的值有三种,表示将指针定位的不同位置。而offset则表示相对于whence指定位置的偏移量,offset的默认值为0,为正表示向后偏移,为负表示向前偏移。

  • set:表示将指针定位到文件开头处,即0位置处
  • cur:表示指针保持当前位置不变,默认值
  • end:表示将指针定位到文件结尾处

猜你喜欢

转载自blog.csdn.net/f5465245/article/details/130986883