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

news2024/11/18 13:49:15

八、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 + b
	local mul = a * b
	return sum, mul;
end

-- 一次性接收两个值
m, n = f(3, 5)
print(m, n)					-- 输出:8 15

⭐️(4)函数作为参数

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

-- 定义两个普通函数
function sum(a, b)
	return a + b
end

function 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) then
	print("num > 0")
else
	print("num <= 0")
end

-- 输出: num > 0

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

⭐️(2)if 嵌套语句

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

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

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

⭐️(1)while … do

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

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

输出:
3
2
1

⭐️(2)repeat … until

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

a = 3
repeat
	print(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 do
	print(i)
end

输出:
10
30
50

⭐️(4)泛型 for

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

⭐️(5)break

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

for i = 1, 9 do
	print(i)
	if i == 3 then
		break
	end
end

输出:
1
2
3

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

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

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

function f(a)
	::flag:: print("=========")
	if a > 1 then
		print(a)
		a = a - 1
		goto flag
	end
end

f(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 do
	print("cities["..i.."]="..cities[i])
end

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

-- 声明一个二维数组
arr = {} 			-- 必须声明空数组
for i= 1, 3 do
	arr[i] = {}		-- 必须声明空数组
	for j = 1, 2 do
		arr[i][j] = i * j
		print(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 do
	print(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) do
	print(i, v)
end
--[[输出:
1	北京
2	上海
3	广州
4	深圳
--]]

-- 遍历emp中的所有元素
for k, v in pairs(emp) do
	print(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)
end

return 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


-- 访问模块中与模块无关的内容
print(goldenRatio)						-- 输出:0.618
print(maxCircularArea(4, 5))			-- 输出:12.56
-- print(circularArea(2))	-- 报错,局部的不能访问

⭐️(3)再看模块

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

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

⭐️(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.coloradmin.cn/o/1359023.html

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

相关文章

2021-05-08 51单片机74HC164、74LS164、74HCT164、74HC154、74HCT154应用三极管控制继电器

74HC164、74HCT164是8位边沿触发式移位寄存器&#xff0c;串行输入数据&#xff0c;然后并行输出。数据通过两个输入端&#xff08;DSA或DSB&#xff09;之一串行输入&#xff1b;任一输入端可以用作高电平使能端&#xff0c;控制另一输入端的数据输入。两个输入端或者连接在一…

IMS SIP register消息中的Contact header field

SIP register中的Contact还要承载User Agent的能力信息。 实网下抓取的UE log如上&#xff0c;下面就主要看下Contact header field要包含的内容及其含义。 Contact header field设置为包括 UE IP地址或FQDN的SIP URI。 如上图contact中sip:69a5de6a-a03e-46d6-ad7a-b0d974c8f…

大创项目推荐 深度学习图像修复算法 - opencv python 机器视觉

文章目录 0 前言2 什么是图像内容填充修复3 原理分析3.1 第一步&#xff1a;将图像理解为一个概率分布的样本3.2 补全图像 3.3 快速生成假图像3.4 生成对抗网络(Generative Adversarial Net, GAN) 的架构3.5 使用G(z)生成伪图像 4 在Tensorflow上构建DCGANs最后 0 前言 &#…

函数式编程及应用

目录 什么是Lambdalambda表达式的类型及实现方式类型语法 常用函数式接口Customer 函数式编程在Stream中的应用总结参考资料 什么是Lambda Lambda 表达式是 JDK8 的一个新特性&#xff0c;可以取代大部分的匿名内部类&#xff0c;写出更优雅的Java代码。 Lambda 表达式描述了一…

图片剪切软件,让图片处理更加高效

随着数字技术的不断发展&#xff0c;图片剪切也在不断进步。从手动裁剪到自动识别&#xff0c;图片剪切正变得越来越简单、高效&#xff0c;在这里面&#xff0c;图片处理软件的作用是不可忽视的。 所需工具&#xff1a; 一个【首助编辑高手】软件 需要剪切的图片 操作步骤…

【python】魔术方法大全(一)--基础篇

如果对你有帮助&#xff0c;欢迎微信搜索【海哥python】关注这个互联网苟且偷生的工具人。 什么是魔术方法 所谓魔法方法&#xff0c;它的官方的名字实际上叫special method&#xff0c;是Python的一种高级语法&#xff0c;允许你在类中自定义函数&#xff0c;并绑定到类的特殊…

Weblogic安全漫谈(四)

黑名单机制必然会推动两种研究方向的发展&#xff1a;一是挖掘不在黑名单的新组件&#xff0c;是为绕过规则&#xff1b;二是发掘检查的盲区&#xff0c;是为绕过逻辑。 CVE-2020-14756 二次反序列化具有对抗检查逻辑的天生丽质&#xff0c;在CVE-2018-2893中就有利用字节数组…

Kubeadmin实现k8s集群:

Kubeadmin来快速搭建一个k8s集群&#xff1a; 二进制搭建适合大集群&#xff0c;50台以上的主机&#xff0c; 但是kubeadm更适合中小企业的业务集群 环境&#xff1a; Master&#xff1a;20.0.0.71 2核4G 或者4核8G docker kubelet kubectl flannel Node1&#xff1a;20.…

面试题:vue2中option API的和vue3中composition API中的数据和方法能否交互?

结论&#xff1a; vue2中option API中的数据和方法可以从vue3中的composition API中进行调用&#xff0c; 而vue3中的composition API是不可以从vue2中的数据进行调用。 原理&#xff1a; 因为composition API中的函数setup在页面的生命周期中要比vue2中option API中的data、…

MR实战:词频统计

文章目录 一、实战概述二、提出任务三、完成任务&#xff08;一&#xff09;准备数据1、在虚拟机上创建文本文件2、上传文件到HDFS指定目录 &#xff08;二&#xff09;实现步骤1、创建Maven项目2、添加相关依赖3、创建日志属性文件4、创建词频统计映射器类5、创建词频统计归并…

六、Spring 声明式事务

本章概要 声明式事务概念 编程式事务声明式事务Spring事务管理器 基于注解的声明式事务 准备工作基本事务控制事务属性&#xff1a;只读事务属性&#xff1a;超时时间事务属性&#xff1a;事务异常事务属性&#xff1a;事务隔离级别事务属性&#xff1a;事务传播行为 6.1 声…

phpstudy_pro 关于多版本php的问题

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

AI交互提示工程指南技术

简述: 当今互联网行业对于AI提示工程的需求日益增长,而《AI提示工程指南》是一本旨在满足这种需求的宝贵指南。本指南由一位对AI提示工程充满热情并自学而来的互联网从业者撰写,旨在为行业人员提供一个全面、易懂的参考手册。 这本指南将引领您踏上AI提示工程的旅程,深入探…

【Linux】Linux Page Cache页面缓存的原理

Page cache&#xff08;页面缓存&#xff09;是计算机操作系统中的一种机制&#xff0c;用于将频繁访问的数据从磁盘存储到内存中&#xff0c;以便更快地访问。当程序从磁盘请求数据时&#xff0c;操作系统会检查该数据是否已经存在于页面缓存中。如果存在&#xff0c;数据可以…

猫咪主食冻干K9、希喂、SC生骨肉冻干哪款好?详细对比测评这三款产品

随着科学养猫的观念深入人心&#xff0c;越来越多的铲屎官开始关注猫咪主食的营养与健康。主食冻干&#xff0c;作为一种模拟猫咪原始猎食的食品&#xff0c;因其高营养保留而受到广大猫奴的喜爱。相比传统的膨化猫粮&#xff0c;主食冻干更符合猫咪的饮食天性&#xff0c;提供…

【Storm实战】1.1 图解Storm的抽象概念

文章目录 0. 前言1. Storm 中的抽象概念1.1 流 (Stream)1.2 拓扑 (Topology)1.3 Spout1.4 Bolt1.5 任务 (Task)1.6 工作者 (Worker) 2. 形象的理解Storm的抽象概念2.1 流 (Stream)2.2 拓扑 (Topology)2.3 Spout2.4 Bolt2.5 任务 (Task)2.6 工作者 (Worker)场景1场景2 3.参考文档…

如何打造家居产业数字化转型范式?林氏家居以数智供应链作答

近年来&#xff0c;我国房地产行业逐步进入深度调整期。作为下游产业&#xff0c;家居家装行业的发展也来到了新阶段。业内人士指出&#xff0c;新房市场成交规模收缩&#xff0c;家居家装企业们开始整合资源&#xff0c;向存量房市场、产品科技化以及数字化转型。 国家层面出…

教学/直播/会议触摸一体机定制_基于展锐T820安卓核心板方案

触控一体机是一种集先进的触摸屏、工控和计算机技术于一体的设备。它取代了传统的键盘鼠标输入功能&#xff0c;广泛应用于教学、培训、工业、会议、直播、高新科技展示等领域。触摸一体机的应用提升了教学、会议和展示的互动性和信息交流。 触摸一体机方案基于国产6nm旗舰芯片…

设置进程优先级

#include <windows.h>int main() {// 获取当前进程的句柄HANDLE hProcess GetCurrentProcess();// 设置当前进程的优先级为高SetPriorityClass(hProcess, HIGH_PRIORITY_CLASS);// 执行其他代码return 0; }进程优先级 标志 idle &#xff08;低&#xff09; IDL…

Python (十七) __name__ == ‘__main__‘ 作用

程序员的公众号&#xff1a;源1024&#xff0c;获取更多资料&#xff0c;无加密无套路&#xff01; 最近整理了一波电子书籍资料&#xff0c;包含《Effective Java中文版 第2版》《深入JAVA虚拟机》&#xff0c;《重构改善既有代码设计》&#xff0c;《MySQL高性能-第3版》&…