Redis系列第六章
发表于:2023-12-18 | 分类: 中间件
字数统计: 4.8k | 阅读时长: 17分钟 | 阅读量:

简介

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

Linux 系统

CentOS7 中自带 Lua :

df968885da161258d077acdba147663e.png

使用高版本的Lua需要安装:

1
2
3
4
5
6
7
8
#解压Lua源码
tar -zxvf lua-5.4.4.tar.gz -C /opt/apps/
#进入Lua的解压目录准备编译环境
yum -y install gcc gcc-c++
#编译
make linux test
#安装
make install

重启后便能使用新版本的Lua

lua -v 可以查看Lua版本

交互模式

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

命令行模式:

在任意目录下使用 lua 命令进入 lua 命令行模式,在其中输入语句后回车即可运行显示出结果。使用 Ctrl + C 退出模式。

lua 对语句后的分号要求不是强制性的,有没有都行。

6e4784bd08dae341f68b32bf771e9839.png

脚本文件模式:

该模式是先要编写脚本文件,然后再使用 lua 命令来运行文件。

创建 hello.lua

1
print("hello lua");

运行:lua hello.lua

运行方式二:

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

1
2
#!/usr/bin/lua
print("hello lua");

修改文件执行权限即可运行,类似 shell 脚本

Windows 系统

在 Windows 系统中 Lua 的运行环境。最常用的为 SciTE。

SciTE 是一款 Lua 脚本测试编辑器,提供 Lua 的编辑运行环境。官方下载地址:Releases · rjpcomputing/luaforwindows · GitHub

语法基础

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

数据类型

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

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

标识符

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

保留字

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

1
2
3
4
5
6
and		break		do		else
elseif end false for
function if in local
nil not or repeat
return then true until
while goto

变量

Lua 是弱类型语言,变量无需类型声明即可直接使用。

变量分为全局变量与局部变量。Lua 中的变量默认都是全局变量,即使声明在语句块或函数里。全局变量一旦声明,在当前文件中的任何地方都可访问。

局部变量 local 相当于 Java 中的 private 变量,只能在声明的语句块中 使用。

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

运算符

算术运算符

操作符 描述
+ 加法
- 减法
* 乘法
/ 除法
% 取余
^ 乘幂
- 负号
// 整除运算(>=lua5.3)

5/2=2.5; 5//2=2

关系运算符

操作符 描述
== 等于
~= 不等于
> 大于
< 小于
>= 大于等于
<= 小于等于

逻辑运算符

Lua 系统将 false 与 nil 作为假,将 true 与非 nil 作为真,即使是 0 也是真。

操作符 描述
and 逻辑与
or 逻辑或
not 逻辑非

特殊运算符

操作符 描述
..(两个点) 字符串连接符
# 返回字符串或表的长度

函数

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

  1. 固定参函数

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

ae95dfa12faa0c1b97beb371cd9b2209.png

  1. 可变参函数

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

  1. 可返回多个值

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

033a1faed42736b9ce1ff706c12666d6.png

  1. 函数作为参数

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

控制语句

流程控制语句

if

Lua 提供了 if…then 用于表示条件判断,其中 if 的判断条件可以是任意表达式。

Lua 中的 if 语句的判断条件可以使用小括号括起来,也可以不使用。

Lua 中提供了专门的关键字 elseif 来做 if 嵌套语句。

e154464edc36890d2567ddf82ea37132.png

循环控制语句

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

9d1ed5ffda6f4f5436aad7d640630744.png

其中 for 循环只参用于循环变量为数值型的情况。

1
2
3
4
其语法格式为:
for var=exp1, exp2, exp3 do
循环体
end

var 为指定的循环变量,exp1 为循环起始值,exp2 为循环结束值,exp3 为循环步长。 步长默认为 1,可省略。

  • 泛型 for

泛型 for 用于遍历 table 中的所有值,其需要与 Lua 的迭代器联合使用。

  • break

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

  • goto

goto 语句可以将执行流程无条件地跳转到指定的标记语句处开始执行

语句标记使用一对双冒号括起来,置于语句前面。goto 语句可以使用在循环之外。 注意,Lua5.1 中不支持双冒号的语句标记。

该语法不建议使用

31fd63711e58bad4873f8415f27bc347.png

语法进阶

table

数组

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

4abc6878d630643c734767174f5bb9d0.png

map

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

4bed0658e87155a719862ec9c79241bc.png

混合结构

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

table 操作函数

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

  • table.concat()
1
table.concat (table [, sep [, start [, end]]])

该函数用于将指定的 table 数组元素进行字符串连接。连接从 start 索引位置到 end 索引位置的所有数组元素, 元素间使用指定的分隔符 sep 隔开。如果 table 是一个混合结构,那么仅是连接数组元素。

  • table.unpack()
1
table.unpack (table [, i [, j]])

该函数返回指定 table 的数组中的从第 i 个元素到第 j 个元素值。i 与 j 是可选的,默认 i 为 1,j 为数组的最后一个元素。Lua5.1 不支持该函数。

  • table.pack()

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

  • table.maxn()

table.maxn(table)

该函数返回指定 table 的数组中的最大索引值,即数组包含元素的个数。

  • table.insert()
1
table.insert (table, [pos,] value)

该函数用于在指定 table 的数组部分指定位置 pos 插入值为 value 的一个元素。其后的元素会被后移。pos 参数可选,默认为数组部分末尾。

  • table.remove ()
1
table.remove (table [, pos])

该函数用于删除并返回指定 table 中数组部分位于 pos 位置的元素。其后的元素会被前移。pos 参数可选,默认删除数组中的最后一个元素。

  • table.sort()
1
table.sort(table [,fun(a,b)])

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

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

迭代器

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

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

2b125b87dc279ed3c0effc0539065a54.png

模块

模块是Lua中特有的一种数据结构。从 Lua 5.1 开始,Lua 加入了标准的模块管理机制,可以把一些公用的代码放在一个文件里,以 API 接口的形式在其他地方调用,有利于代码的重用和降低代码耦合度。

模块文件主要由 table 组成。在 table 中添加相应的变量、函数,最后文件返回该 table 即可。如果其它文件中需要使用该模块,只需通过 require 将该模块导入即可。

  1. 定义模块

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

  1. 使用模块

require(“文件路径”),其中文件名是不能写.lua 扩展名的。该函数可以将指定的 lua 文件静态导入(合并为一个文件)。该函数的使用可 以省略小括号,写为 require “文件路径”。

require()函数是有返回值的,返回的就是模块文件最后 return 的 table。可以使用一个变量接收该 table 值作为模块的别名,就可以使用别名来访问模块了。

元表与元方法

元表,即 Lua 中普通 table 的元数据表,而元方法则是元表中定义的普通表的默认行为。Lua 中的每个普通 table 都可为其定义一个元表,用于扩展该普通 table 的行为功能。

元表中有两个重要函数:

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

元方法

  • _ _index

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

f76bdc8c6197b4c7c61eabc143739129.png

7fe41de0916ede6dde6cfe53d48a50e9.png

  • _ _newindex

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

5be037635f0f0580fe1dcfe7d3f9f1a6.png

  • 运算符元方法

如果要为一个表扩展加号(+)、减号(-)、等于(==)、小于(<)等运算功能,则可重写相应的元方法。

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

184f26dd60b20c81c2bd592c069f497c.png

  • _ _tostring

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

de3c8ec34e1cbcfa4a0e025aa6f35f22.png

  • __call

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

c1b9cbb5c1531e1eeb971753a1954dea.png

  • 元表单独定义

为了便于管理与复用,可以将元素单独定义为一个文件。该文件中仅可定义一个元表,且一般文件名与元表名称相同。

若一个文件要使用其它文件中定义的元表,只需使用 require “元表文件名”即可将元表导入使用。 如果用户想扩展该元表,则可在用户自己文件中重写其相应功能的元方法。

面向对象

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

a159c884c79f08ec89fd1d580ad5ea19.png

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

e6fa697c8c77856967133623746b492b.png

协同线程与协同函数

协同线程

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 类型。一旦调用该函数就会创建并执行一个协同线程实例

协同函数

协同线程可以单独创建执行,也可以通过协同函数的调用启动执行。使用 coroutine 的 wrap() 函数创建的就是协同函数,其类型为 function。

由于协同函数的本质就是函数,所以协同函数的调用方式就是标准的函数调用方式。只不过,协同函数的调用会启动其内置的协同线程。

62013d3f8bd177e0868c4f6abf4a4024.png

文件IO

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

常用静态函数

  1. io.open()
1
io.open (filename [, mode])

以指定模式打开指定文件,返回要打开文件的句柄(对象)。其中模式 mode 有三种,但同时还可配合两个符号使用:

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

2 .io.input()

1
io.input (file)

指定要读取的文件。

  1. io.output()
1
io.output()

指定要写入的文件。

  1. io.read()
1
io.read([format])

以指定格式读取 io.input()中指定的输入文件。其中 format 格式有:

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

将指定的数据 data 写入到 io.output()中指定的输出文件。

实例:

1bb5861331d4dbb4e6bfbd09760988b6.png

常用实例函数

  1. file:read()

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

  1. file:write()

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

  1. file:seek()
1
file:seek ([whence [, offset]])

该函数用于获取或设置文件读写指针的当前位置。位置从 1 开始计数,除文件最后一行外,每行都有行结束符,其会占两个字符位置。位置 0 表示文件第一个位置的前面位置。

当 seek()为无参时会返回读写指针的当前位置。参数 whence 的值有三种,表示将指针定位的不同位置。而 offset 则表示相对于 whence 指定位置的偏移量,offset 的默认值为 0,为正表示向后偏移,为负表示向前偏移。

  • set:表示将指针定位到文件开头处,即 0 位置处
  • cur:表示指针保持当前位置不变,默认值
  • end:表示将指针定位到文件结尾处
上一篇:
Redis系列第七章
下一篇:
Redis系列第五章