八、Lua脚本详解—— 超详细操作演示!

八、Lua脚本详解 —— 超详细操作演示!

    • 八、Lua脚本详解
      • 8.1 Lua 简介
      • 8.2 Linux 系统的Lua
          • 8.2.1 Lua 下载
          • 8.2.2 Lua 安装
          • 8.2.3 Hello World
      • 8.3 Win 系统的Lua
      • 8.4 Lua 脚本基础
          • 8.4.1 注释
          • 8.4.2 数据类型
          • 8.4.3 标识符
          • 8.4.4 运算符
          • 8.4.5 函数
          • 8.4.6 流程控制语句
          • 8.4.7 循环控制语句
      • 8.5 Lua 语法进阶
          • 8.5.1 table
          • 8.5.2 迭代器
          • 8.5.3 模块
          • 8.5.4 元表和元方法
          • 8.5.5 面向对象
          • 8.5.6 协同线程与协同函数
          • 8.5.7 文件IO
    • 九、分布式锁
      • 9.1 分布式锁的工作原理
      • 9.2 问题引入
      • 9.3 setnx 实现方式
      • 9.4 为锁添加过期时间
      • 9.5 为锁添加标识
      • 9.6 添加 Lua 脚本
      • 9.7 Redisson 可重入锁
      • 9.8 Redisson 红锁
      • 9.9 分段锁
      • 9.10 Redisson 详解

数据库系列文章:

关系型数据库:

  • MySQL —— 基础语法大全
  • MySQL —— 进阶


非关系型数据库:

  • 一、Redis 的安装与配置
  • 二、Redis 基本命令(上)
  • 三、Redis 基本命令(下)
  • 四、Redis 持久化
  • 五、Redis 主从集群
  • 六、Redis 分布式系统
  • 七、Redis 缓存

八、Lua脚本详解

8.1 Lua 简介

    Lua 是一个由标准 C 语言 开发的、开源的、可扩展的轻量级的弱类型的解释型脚本语言, 是 于 1993 年由 巴西里约热内卢天主教大学的三人研究小组使用标准 C 语言开发。

    Lua 的官网 为: https://www.lua.org/

Lua 是一门 脚本语言,和 ShellPython 是同一种类型。

用的最多的是 Unity 手游,做 热更新 方案;Nginx 也有应用。

8.2 Linux 系统的Lua

8.2.1 Lua 下载

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

在这里插入图片描述

8.2.2 Lua 安装

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

⭐️(1)解压

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

tar -zxvf lua-5.4.6.tar.gz -C /opt/apps/

在这里插入图片描述

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

在这里插入图片描述

⭐️(2)安装gcc

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

yum -y install gcc gcc-c++

在这里插入图片描述

⭐️(3)编译

    执行编译命令 make linux test

# test 测试输出版本号
make linux test

⭐️(4)安装

make install

在这里插入图片描述

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

在这里插入图片描述

如果 lua -v 显示的还是老版本,reboot 重启一下 Linux 系统就好了。

8.2.3 Hello World

⭐️(1)两种交互模式

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

A、命令行模式

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

在这里插入图片描述

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

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

B、脚本文件模式

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

    例如直接创建一个名称为 hello.lua 的文件,文件中就写一名 print() 语句即可。

在这里插入图片描述

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

lua hello.lua

在这里插入图片描述

⭐️(2)两种脚本运行方式

    对于脚本文件的运行有两种方式。

  • 一种是上面的 lua 命令 方式,
  • 还有一种是 可执行文件 方式。可执行文件方式是,将 lua 脚本文件直接修改为 可执行文件 运行。

    下面就使用第二种方式来运行。

A、修改脚本文件内容

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

#!/usr/bin/lua

在这里插入图片描述

B、修改脚本文件权限

chmod 755 hello.lua

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

C、运行

    直接使用文件名即可运行。
在这里插入图片描述

8.3 Win 系统的Lua

    这里要安装的是在 Windows 系统中 Lua 的运行环境。最常用的为 SciTE

    SciTE 是一款 Lua 脚本 测试编辑器,提供 Lua 的编辑运行环境。其官方下载地址为: https://github.com/rjpcomputing/luaforwindows/releases 。 下载完直接运行 exe 文件安装。
在这里插入图片描述

    SciTE 提供了两种运行方式:命令行窗口运行方式 与 Lua 脚本的编辑运行环境

在这里插入图片描述
在这里插入图片描述

    除了SciTE ,还有像 LuaDistLuaRocks 等。

8.4 Lua 脚本基础

8.4.1 注释

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

    不过,在 调试过程 中如果想 临时取消 段注释,而直接将其标识删除,这样做其实并不好。因为有可能还需要再添加上。而段注释的写法相对较麻烦。

  • 所以, Lua 给出了一种简单处理方式:在开头的 --[[再加一个减号,即可使段注释不起作用。其实就是使两个段注释标识变为了两个行注释。
--~ 行注释,(快捷键为 Ctr + Q)
-- 行注释,(波浪号 ~ ,为快捷键自动生成的)--[[段注释
print("Hello, Lua")
--]]---[[取消段注释
print("Hello, Lua")
--]]
8.4.2 数据类型

    Lua 中有 8 种 类型,分别为: nilbooleannumberstringuserdatafunctionthreadtable

  • 通过 type() 函数可以查看一个数据的类型,例如, type(nil) 的结果为 niltype(123) 的结果为 number

在这里插入图片描述

-- string 演示
str1 = "中国"
str2 = '北京'
str3 = [[深圳
广州
上海]]print(str1) 
print(str2)
print(str3)--[[输出:
中国
北京
深圳
广州
上海
--]]
8.4.3 标识符

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

⭐️(1)保留字

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

在这里插入图片描述

⭐️(2)变量

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

-- 局部变量
local x = 3-- 定义一个函数
function f()-- 全局变量y = 5-- 再定义一个局部变量local z = 8-- 访问局部变量print("x = "..x);
end-- 访问函数
f(); 				 -- 输出 x = 3
-- 访问全局变量
print("y = "..y)	 -- 输出 y = 5
-- 访问局部变量
print("z = "..z)	 -- 报错,z 为局部变量

⭐️(3)动态类型

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

y = 5
print("y = "..y)	 -- 输出 y = 5
y = "北京"
print("y = "..y)	 -- 输出 y = 北京
8.4.4 运算符

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

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

⭐️(1)算术运算符

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

在这里插入图片描述

注意,

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

⭐️(2)关系运算符

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

在这里插入图片描述

⭐️(3)逻辑运算符

    注意, Lua 系统将 falsenil 作为 ,将 true非nil 作为 即使是 0 也是

    下表列出了Lua 语言中的常用 逻辑运算符,设定 A 的值为 trueB 的值为 false

在这里插入图片描述

⭐️(4)其他运算符

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

在这里插入图片描述

str = "abcdefg"
print(#str) 	 -- 输出 7
8.4.5 函数

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

⭐️(1)固定参函数

    Lua 中的函数在调用时与 Java 语言中方法的调用是不同的,其 不要求实参的个数必须与函数中形参的个数相同

  • 如果实参个数少于形参个数,则系统自动使用 nil 填充;
  • 如果实参个数多于形参个数,多出的将被系统 自动忽略
-- 定义一个普通函数,包含两个形参
function f(a, b)print(a, b)
end-- 无实参传递
f()					-- 输出:nil	nil-- 传递一个实参
f(10)				-- 输出:10 	nil-- 传递两个实参
f(10, 20)			-- 输出:10 	20-- 传递三个实参
f(10, 20, 30)		-- 输出:10 	20

⭐️(2)可变参函数

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

-- 定义一个可变参函数
function f(...)local a,b,c,d = ...print(a, b, c, d)--print(...) 	-- 可以全部输出
end-- 传递三个实参
f(10, 20, 30)					-- 输出:10 20	30	nil-- 传递四个实参
f(10, 20, 30, 40)				-- 输出:10	20	30	40-- 传递五个实参
f(10, 20, 30, 40, 50)			-- 输出:10	20	30	40

⭐️(3)可返回多个值

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

-- 定义一个普通函数,返回两个值
function f(a, b)local sum = a + blocal mul = a * breturn sum, mul;
end-- 一次性接收两个值
m, n = f(3, 5)
print(m, n)					-- 输出:8 15

⭐️(4)函数作为参数

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

-- 定义两个普通函数
function sum(a, b)return a + b
endfunction mul(a, b)return a * b
end-- 定义一个函数,其参数为另一个参数
function f(m, n, fun)local result = fun(m, n)print(result)
end-- 调用
f(3, 5, sum)					-- 输出:8
f(3, 5, mul)					-- 输出:15-- 匿名函数调用
f(3, 5, function (a, b)return b - a;end
);								-- 输出:2
8.4.6 流程控制语句

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

⭐️(1)if 语句

    Lua 提供了 if...then 用于表示条件判断,其中 if 的判断条件可以是 任意表达式。 Lua 系统将 falsenil 作为,将 true非nil 作为,即使 0 也是

a = 5
if(a > 0) thenprint("num > 0")
elseprint("num <= 0")
end-- 输出: num > 0

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

⭐️(2)if 嵌套语句

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

a = 5
if(a > 0) thenprint("num > 0")
elseif a == 0 thenprint("num = 0")
elseprint("num < 0")
end
8.4.7 循环控制语句

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

⭐️(1)while … do

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

a = 3
while a>0 doprint(a)a = a - 1    -- 注意:这里没有a--
end

输出:
3
2
1

⭐️(2)repeat … until

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

a = 3
repeatprint(a)a = a - 1    -- 注意:这里没有a--
until a <= 0

输出:
3
2
1

⭐️(3)数值 for

    这种 for 循环只参用于循环变量数值型 的情况。其语法格式为:

for var=exp1, exp2, exp3 do循环体
end

    var 为指定的 循环变量exp1 为 循环 起始值exp2 为 循环 结束值exp3 为 循环 步长

  • 步长可省略不写,默认1
  • 每循环一次,系统内部都会做一次当前循环变量 var 的值与 exp2 的比较,如果 var 小于等于 exp2 的值,则继续循环,否则结束循环。

例如:

for i = 10, 50, 20 doprint(i)
end

输出:
10
30
50

⭐️(4)泛型 for

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

⭐️(5)break

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

for i = 1, 9 doprint(i)if i == 3 thenbreakend
end

输出:
1
2
3

⭐️(6)goto (不建议使用,不然可能使代码杂乱无章)

    goto 语句可以将执行流程 无条件地跳转 到指定的标记语句处开始执行,注意,是开始执行,并非仅执行这一句,而是从这句开始后面的语句都会重新执行。当然,该标识语句在第一次经过时也是会执行的,并非是必须由 goto 跳转时才执行。

    语句标记使用一对双冒号括起来,置于语句前面。goto 语句可以使用在循环之外。

function f(a)::flag:: print("=========")if a > 1 thenprint(a)a = a - 1goto flagend
endf(5)

输出:
=========
5
=========
4
=========
3
=========
2
=========

    注意,Lua5.1不支持 双冒号 的语句标记。

8.5 Lua 语法进阶

8.5.1 table

⭐️(1)数组

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

-- 定义一个一维数组
cities = {"北京", "上海", "广州"}
cities[4] = "深圳"for i=1, 4 doprint("cities["..i.."]="..cities[i])
end

输出:
cities[1]=北京
cities[2]=上海
cities[3]=广州
cities[4]=深圳

-- 声明一个二维数组
arr = {} 			-- 必须声明空数组
for i= 1, 3 doarr[i] = {}		-- 必须声明空数组for j = 1, 2 doarr[i][j] = i * jprint(arr[i][j])end
end

输出:
1
2
2
4
3
6

⭐️(2)map

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

例 1 :

-- 定义一个map
emp  = {name = "张三", age = "23", depart = "销售部"}-- 通过下标方式操作
emp["gender"] = "男"
print(emp["name"])				-- 输出:张三
print(emp["gender"])			-- 输出:男-- 点号方式操作 (推荐)
emp.office = "2nd floor"
print(emp.age)					-- 输出:23
print(emp.office)				-- 输出:2nd floor

例 2 :

a = "xxx"
b = 3
c = 5-- 定义一个map,其key为表达式(需要用方括号括起来)
arr = {["emp_"..a] = true,["hi"] = 123,[b + c] = "hello",
}print(arr.emp_xxx)				-- 输出:true
-- print(arr.8)   -- 报错
print(arr.hi)					-- 输出:123
print(arr[8])					-- 输出:hello

⭐️(3)混合结构

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

emp  = {"北京", name = "张三", "上海", age = "23", "广州", depart = "销售部", "深圳"}print(emp[1])					-- 输出:北京
print(emp[2])					-- 输出:上海
print(emp[3])					-- 输出:广州
print(emp[4])					-- 输出:深圳

常见使用方法:

-- 定义一个数组,map 为混合结构
emp = {{name="张三", age=23},{name="李四", age=24},{name="王五", age=25},{name="赵六", age=26},
}for i = 1, 4 doprint(emp[i].name.." : "..emp[i].age)
end

输出:
张三 : 23
李四 : 24
王五 : 25
赵六 : 26

⭐️(4)table 操作函数

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

A、table.concat()

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

emp  = {"北京", name = "张三", "上海", age = "23", "广州", depart = "销售部", "深圳"}print(table.concat(emp, ","))  -- 输出:北京,上海,广州,深圳
print(table.concat(emp, ",", 2, 3))  -- 输出:上海,广州

B、table.unpack()

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

arr = {"bj", "sh", "gz", "sz"}table.unpack(arr)  			 -- 输出:bj	sh	gz	sz
table.unpack(arr, 2, 3) 	 -- 输出:sh	gz-- 也可以使用变量接收
a, b, c, d = table.unpack(arr)

C、table.pack()

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

t = table.pack("apple", "banana", "peach")
table.concat(t, ",") 			-- 输出:apple,banana,peach

D、table.maxn()

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

emp  = {"北京", name = "张三", "上海", age = "23", "广州", depart = "销售部", "深圳"}print(table.concat(emp, ","))  -- 输出:北京,上海,广州,深圳print(table.maxn(emp))		   -- 输出:4

E、table.insert()

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

cities = {"北京", "上海", "广州"}table.insert(cities, 2, "深圳")
table.insert(cities, "天津")print(table.concat(cities, ",")) -- 输出:北京,深圳,上海,广州,天津

F、table.remove()

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

cities = {"北京", "上海", "广州", "深圳", "天津"}table.remove(cities, 2)
table.remove(cities)print(table.concat(cities, ",")) -- 输出:北京,广州,深圳

G、table.sort()

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

cities = {"bj北京", "sh上海", "gz广州", "sz深圳", "tj天津"}table.sort(cities, function(a, b)     -- 降序return a > b  -- 如果相邻的两个为真,保持原来的队形end
)print(table.concat(cities, ",")) -- 输出:tj天津,sz深圳,sh上海,gz广州,bj北京

注意

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

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

  • ipairs(table) :仅会迭代指定 table 中的 数组元素
  • pairs(table):会迭代 整个 table 元素 ,无论是 数组元素,还是 key-value
emp  = {"北京", name = "张三", "上海", age = "23", "广州", depart = "销售部", "深圳"}-- 遍历emp中的所有数组元素
for i, v in ipairs(emp) doprint(i, v)
end
--[[输出:
1	北京
2	上海
3	广州
4	深圳
--]]-- 遍历emp中的所有元素
for k, v in pairs(emp) doprint(k, v)
end
--[[输出:
1	北京
2	上海
3	广州
4	深圳
depart	销售部
name	张三
age	23
--]]
8.5.3 模块

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

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

⭐️(1)定义一个模块

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

例如: 定义rectangle模块, 创建一个rectangle.lua 文件

-- 声明一个模块
rectangle = {}-- 为模块添加一个变量
rectangle.pi = 3.14-- 为模块添加函数(求周长)
function rectangle.perimeter(a, b)return (a + b) * 2
end-- 以匿名函数方式为模块添加一个函数(求面积)
rectangle.area = function(a, b)return a * b
end-- ================= 定义与模块无关的内容===============
-- 定义一个全局变量
goldenRatio = 0.618-- 定义一个局部函数(求圆的面积)
local function circularArea(r)return rectangle.pi * r * r
end-- 定义一个全局函数(求矩形中最大圆的面积)
function maxCircularArea(a, b)local r = math.min(a, b)return circularArea(r / 2)
endreturn rectangle

⭐️(2)使用模块

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

-- 导入一个模块
require "rectangle"-- 访问模块的属性,调用模块的函数
print(rectangle.pi)						-- 输出:3.14
print(rectangle.perimeter(3, 5))		-- 输出:16
print(rectangle.area(3, 5))				-- 输出:15

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

-- 导入一个模块
rect = require "rectangle"-- 访问模块的属性,调用模块的函数
print(rect.pi)						-- 输出:3.14
print(rect.perimeter(3, 5))			-- 输出:16
print(rect.area(3, 5))				-- 输出:15

⭐️(3)再看模块

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

-- 导入一个模块
require "rectangle"-- 访问模块中与模块无关的内容
print(goldenRatio)						-- 输出:0.618
print(maxCircularArea(4, 5))			-- 输出:12.56
-- print(circularArea(2))	-- 报错,局部的不能访问
8.5.4 元表和元方法

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

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

⭐️(1)重要函数

    元表 中有 两个重要函数

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

⭐️(2)__index 元方法

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

  • 如果重写的 _ _index 元方法是函数,且有返回值,则直接返回
  • 如果 没有返回值,则返回 nil

例1:重写的方法是一个函数

emp  = {"北京", nil, name = "张三", "上海", age = "23", "广州", depart = "销售部", "深圳"}print(emp.x)-- 声明一个元表
meta = {};-- 将原始表和元表相关联
setmetatable(emp, meta)-- 有返回值的情况
meta.__index = function(tab, key)return "通过【"..key.."】访问的值不存在"
end--~ -- 无返回值的情况
--~ meta.__index = function(tab, key)
--~ 	print("通过【"..key.."】访问的值不存在")
--~ endprint(emp.x)
print(emp[2])

输出:
nil
通过【x】访问的值不存在
通过【2】访问的值不存在

例2:重写的方法是一个

emp  = {"北京", name = "张三", "上海", age = "23", "广州", depart = "销售部", "深圳"}print(emp[5])-- 声明一个元表
meta = {};-- 将原始表和元表相关联
setmetatable(emp, meta)-- 再定义一个普通表
other = {}other[5] = "天津"
other[6] = "西安"-- 指定元表为另一个普通表
meta.__index = other-- 在原始表中若找不到,则会到元表指定的普通表中查找
print(emp[5])

输出:
nil
天津

⭐️(3)__newindex 元方法

    当用户为 table 中一个 不存在索引key 赋值 时,就会自动调用元表的 _ _newindex 元方法。该重写的方法可以是一个函数,也可以是另一个

  • 如果重写的 _ _newindex 元方法是函数,且有返回值,则直接返回
  • 如果没有返回值,则返回 nil

⭐️(4)运算符 元方法

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

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

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

在这里插入图片描述
在这里插入图片描述

⭐️(5)__tostring 元方法

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

⭐️(6)__call 元方法

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

⭐️(7)元表单独定义

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

    若一个文件要使用其它文件中定义的元表,只需使用 require 元表文件名 即可将元表导入使用。

    如果用户想扩展该元表而又不想修改元表文件,则可在用户 自己文件中 重写其相应功能元方法 即可。

8.5.5 面向对象

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

⭐️(1)简单对象的创建

    Lua 中通过 tablefunction 可以创建出一个简单的 Lua 对象

  • tableLua 对象赋予 属性;
  • 通过 functionLua 对象赋予 行为,即 方法

⭐️(2)类的创建

    Lua 中使用 tablefunction元表 可以定义出

  • 使用一个 作为 基础类,使用一个 function 作为该基础类new() 方法。
  • 在该 new() 方法中 创建一个空表,再为该 空表 指定一个元表
  • 元表 重写 _ _index 元方法,且将基础表指定为重写_ _index 元方法。
  • 由于 new() 中的表是空表,所以用户访问的所有 key 都会从基础类)中查找
8.5.6 协同线程与协同函数

⭐️(1)协同线程

    Lua 中有一种 特殊的线程,称为 coroutine 协同线程,简称 协程。其可以在运行时 暂停执行,然后转去执行其它线程,然后还可返回再继续执行没有执行完毕的内容。即可以“走走停停,停停再走走”。

    协同线程 也称为 协作多线程,在Lua 中表示 独立的执行线程任意时刻只会有一个协程执行,而不会出现多个协程同时执行的情况。

    协同线程的类型为 thread ,其启动暂停重启等,都需要通过函数来控制。下表是用于控制协同线程的基本方法。

在这里插入图片描述

⭐️(2)协同函数

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

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

8.5.7 文件IO

⭐️(1)常用静态函数

A、io.open()

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

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

B、io.input()

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

C、io.outout()

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

D、io.read()

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

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

E、io.write()

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

⭐️(2)常用实例函数

A、file:read()

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

B、file:write()

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

C、file:seek()

格式file:seek ([whence [, offset]])
解析】该函数用于获取或设置文件读写指针的当前位置。
    位置从 1 开始计数,除文件最后一行外,每行都有行结束符,其会占两个字符位置。位置 0 表示文件第一个位置的前面位置。

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

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

九、分布式锁

9.1 分布式锁的工作原理

9.2 问题引入

9.3 setnx 实现方式

9.4 为锁添加过期时间

9.5 为锁添加标识

9.6 添加 Lua 脚本

9.7 Redisson 可重入锁

9.8 Redisson 红锁

9.9 分段锁

9.10 Redisson 详解

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/599307.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

MinIO 批处理框架添加了对过期时间的支持

您现在可以使用 MinIO 批处理框架执行 S3 删除操作&#xff0c;以通过单个 API 请求删除大量对象。借助 MinIO 批处理框架&#xff0c;可以快速轻松地在 MinIO 部署中执行重复或批量操作&#xff0c;例如批量复制和批量密钥轮换。MinIO 批处理框架处理所有手动工作&#xff0c;…

【大数据进阶第二阶段之Hadoop学习笔记】Hadoop 运行环境搭建

【大数据进阶第二阶段之Hadoop学习笔记】Hadoop 概述-CSDN博客 【大数据进阶第二阶段之Hadoop学习笔记】Hadoop 运行环境搭建-CSDN博客 【大数据进阶第二阶段之Hadoop学习笔记】Hadoop 运行模式-CSDN博客 1、模板虚拟机环境准备 1.1、 hadoop100 虚拟机配置要求如下 &…

小兔鲜儿 uniapp - 项目打包

目录 微信小程序端​ 核心步骤​ 步骤图示​ 条件编译​ 条件编译语法​ 打包为 H5 端​ 核心步骤​ 路由基础路径​ 打包为 APP 端​ 微信小程序端​ 把当前 uni-app 项目打包成微信小程序端&#xff0c;并发布上线。 核心步骤​ 运行打包命令 pnpm build:mp-weix…

Java 如何实现微信支付功能代码示例

微信支付是由中国的腾讯公司推出的一种移动支付方式。它允许用户通过在微信应用中绑定银行卡或其他支付方式来进行交易&#xff0c;包括在线购物、转账、付款码支付等。微信支付的特点包括便捷、安全、快速和全面&#xff0c;使用户可以随时随地完成交易。用户可以通过扫描商家…

openGauss学习笔记-187 openGauss 数据库运维-常见故障定位手段

文章目录 openGauss学习笔记-187 openGauss 数据库运维-常见故障定位手段187.1 操作系统故障定位手段187.2 网络故障定位手段187.3 磁盘故障定位手段187.4 数据库故障定位手段 openGauss学习笔记-187 openGauss 数据库运维-常见故障定位手段 187.1 操作系统故障定位手段 查询…

【Element】el-form和el-table嵌套实现表格编辑并提交表单校验

一、背景 页面需要用到表格采集用户数据&#xff0c;提交时进行表单校验&#xff1b;即表格中嵌套着表单&#xff0c;保存时进行表单校验 二、功能实现 2.1、el-form和el-table嵌套说明 ① :model"formData" 给表单绑定数据&#xff0c;formData是表单的数据对象 …

springboot、spring-kafka、kafka-client的版本对应关系

在使用springboot集成kafka的时候需要注意springboot版本、引用的依赖spring-kafka版本和kafka中间件版本的对应关系&#xff0c;否则可能会因为版本不兼容导致出现错误。 1、含义说明&#xff08;摘自官网&#xff09; Spring Boot&#xff1a;是springboot的版本。Spring fo…

设计模式——最全梳理,最好理解

新年献礼&#xff01; 设计模式呕心梳理 创建型模式 单例模式&#xff08;Singleton Pattern&#xff09;https://blog.csdn.net/qq_34869143/article/details/134874044 整理中... 结构型模式 代理模式&#xff08;Proxy Pattern&#xff09;https://blog.csdn.net/qq_34…

任务调度实现

一、定时任务概述 在项目中开发定时任务应该一种比较常见的需求&#xff0c;在 Java 中开发定时任务主要有三种解决方案&#xff1a;一是使用JDK 自带的 Timer&#xff0c;二是使用 Spring Task&#xff0c;三是使用第三方组件 Quartz Timer 是 JDK 自带的定时任务工具,其简单易…

解决:Microsoft Visual C++ 14.0 is required.

Microsoft Visual C 14.0 is required. Get it with “Microsoft Visual C Build Tools 当我们安装绝大部分python包的时候可以通过pip install 或者 conda install解决&#xff0c;但是任然有些包是安装不了的&#xff0c;比如我的就是在安装pyqt5的时候报Building wheel for…

基于MyCat2.0实现MySQL分库分表方案

目录 一、MyCat概述 二、MyCat作用 2.1 数据分片 2.1.1 垂直拆分 2.1.1.1 垂直分库 2.1.1.2 垂直分表 2.1.1.3 总结 2.1.2 水平拆分 2.1.2.1 水平分库 2.1.2.2 水平分表 2.1.2.3 总结 2.2 读写分离 2.3 多数据源整合 三、MyCat 与ShardingJDBC的区别 3.1 MyCat …

某大型电商APP sign头部签名逆向分析

APP版本 唯品会 7.45Java层抓包分析 打开抓包工具 charles进行分析&#xff0c;可以发现对于API采集需要突破当前这个参数&#xff0c;否则不返回信息 jadx静态分析 jadx静态分析&#xff0c;打开app搜索关键词api_sign&#xff0c;可以发现有参数位置 跟进去上边str赋值方…

phpstudy_pro 关于多版本php的问题

我在phpstudy中安装了多个PHP版本 我希望不同的网站可以对应不同的PHP版本&#xff0c;则在nginx配置文件中需要知道不同的PHP版本的监听端口是多少&#xff0c;如下图所示 然而找遍了php.ini配置&#xff0c;并未对listen进行设置&#xff0c;好奇是怎么实现不同的PHP监听不同…

时代变了,Spring 官方抛弃了 Java 8!

先容许我吐槽一句&#xff1a;Spring 官方&#xff0c;窝草尼玛&#xff01; 原谅我很愤怒&#xff01;最近编程导航星球和群友们反复问一个问题&#xff1a;为啥用 IDEA 创建 Spring Boot 项目时&#xff0c;不能选择 Java 8 了&#xff1f; 我本来以为是 IDEA 版本更新导致的…

html+css 有关于less的使用和全面解释

目录 less 注释 运算 嵌套 变量 导入 导出 禁止导出 less Less是一个CSS预处理器, Less文件后缀是.less。扩充了 CSS 语言, 使 CSS 具备一定的逻辑性、计算能力 注意&#xff1a;浏览器不识别 Less 代码&#xff0c;目前阶段&#xff0c;网页要引入对应的 CSS 文件 V…

聚道云软件连接器助力某动漫行业公司实现财务自动化

客户介绍 某动漫行业公司是一家专注于文化创意领域&#xff0c;致力于为人们提供独特、有趣的文化产品。公司拥有一支充满活力和创造力的团队&#xff0c;他们以卓越的创意和精湛的技术&#xff0c;创造出了一系列令人惊叹的作品。未来&#xff0c;该公司将继续秉承这一理念&a…

若依前后端分离版关联字典值查询数据工具类使用

场景 若依管理系统导出Excel时添加没有的列和关联码表显示中文进行导出&#xff1a; 若依管理系统导出Excel时添加没有的列和关联码表显示中文进行导出_若依的导出添加额外的字段信息-CSDN博客 上面通过关联表的方式实现查询字典值&#xff0c;若依本身提供了查询redis中缓存…

1.5C语言 双曲正弦函数(*) 优化麦克劳林公式

一.传统算法 #include<stdio.h> #include<math.h> int jc(int x); int main(){double x,eps,y0.0;scanf("%lf%lf",&x,&eps);int de1,i1;double item1.0;while(fabs(item)>eps){itempow(x,i)/jc(de);i2;yitem;}printf("%.6f\n",y); …

buildroot 编译错误【001】

在GitHub 查找错误,也挺好用 解决办法 fakeroot 错误

UE相关杂项笔记

1.PAK包解析 UE4如何反向查找Pak里面包含哪些文件 - 哔哩哔哩 CMD控制台命令输入 D:&quot;Epic Games&quot;\UE_5.1\Engine\Binaries\Win64\UnrealPak.exe 包路径 -list *文件夹带空格时 添加“ ”包裹住文件夹名 解包工具路径 UE引擎安装路径\UE_5.1\Engine\Binarie…