Lua 数据结构

一、Lua 中的数据结构

Lua 中并没有像 java、kotlin 语言有专门的数组、列表、集合等数据结构的类,而只提供 table 类型,但他很强大而且也很灵活,而且也在很多场景中天然的就解决了如何对接和使用的问题(例如模块的引入,这在后面的文章会进行分享)

话说回来,Lua 没有提供专门的类进行支持,所以我们接下来就分享如何用 table 来表示各种数据结构。

二、数组

Lua 中只要使用整数来作为 table 的索引,就可以达到数组的效果。但有些不同的是,因为 table 的长度是不固定的,所以 Lua 数组的长度并不像 java、kotlin 那样是固定长度的。

2-1、初始化一个数组

我们可以使用循环创建一个数组,在被初始化的值被获取时则是有值的,未被初始化则为 nil。

local array = {}
for i = 1, 100 doarray[i] = 0
endprint("#array", #array)             --> #array	100
print("array[9]", array[9])         --> array[9]	0
print("array[101]", array[101])     --> array[101]	nil

2-2、修改初始化起始下标

可以进行修改起始下标,只是这个会有一个问题,长度的计算会不准确,因为 Lua 的索引一般都是从 1 开始,从下面的例子体会一下

local array = {}
for i = -5, 5 doarray[i] = 0
end-- 这里的长度为 5 是因为从 1 开始计算
print("#array", #array)             --> #array	5
print("array[-2]", array[-2])       --> array[-2]	0
print("array[5]", array[5])         --> array[5]	0

2-3、创建且初始化数组

还有一种简单的创建方式,在创建时初始化数组

这是 table 的一种创建方式,可以查看之前分享的文章《Lua 数据类型——表》

local array = { 1, 3, 5, 7, 9 }
print("#array", #array)             --> #array	5
print("array[3]", array[3])         --> array[3]	5
print("array[12]", array[12])       --> array[12]	nil

三、矩阵

3-1、矩阵存储

Lua 的矩阵并没有规定存在的形式是怎样的,所以我们可以通过两种方式来存储。

3-1-1、多维数组

多维数组在 java、kotlin 中最为常见,就是一个表中的所有元素又是一个表,如此循环,达到多维效果。

因为比较常规我们就直接上代码,看看 Lua 是如何用二维表现矩阵

local M = 5
local N = 5
local matrix = {}
-- 初始化数组
for i = 1, M dolocal row = {}matrix[i] = rowfor j = 1, N dorow[j] = j + iend
end-- 打印数组
for i = 1, #matrix dolocal row = matrix[i]local rowContent = ""for j = 1, #row dorowContent = rowContent .. " " .. row[j]endprint(rowContent)
end--> 2 3 4 5 6
--> 3 4 5 6 7
--> 4 5 6 7 8
--> 5 6 7 8 9
--> 6 7 8 9 10

3-1-2、一维数组

用一维数组表示多维数组(矩阵的话是二维),实质上只要控制要每一行的跨度(即每一行的元素个数),自己管理好元素的下标,保证元素间不相互覆盖,而且能在需要的时候取出元素。

local matrix = {}
local M = 5
local N = 5
for i = 1, N dolocal aux = (i - 1) * Mfor j = 1, M domatrix[aux + j] = j + iend
endlocal content = ""
for i, v in ipairs(matrix) docontent = content .. " " .. vif i % 5 == 0 thencontent = content .. "\n"end
end
print(content)--> 2 3 4 5 6
--> 3 4 5 6 7
--> 4 5 6 7 8
--> 5 6 7 8 9
--> 6 7 8 9 10

3-2、矩阵相乘

3-2-1、常规矩阵相乘

对于矩阵相乘,我们最先想到的肯定是 “行乘列”,正如如下代码

local a = {{ 1, nil },{ nil, 4 },{ 3, 5 }
}
local b = {{ 1, 3, nil, 5 },{ 2, nil, 6, 4 }
}
local M = 3
local N = 4
local K = 2
local c = {}
-- 初始化 c 矩阵
for i = 1, M dolocal row = {}c[i] = rowfor j = 1, N dorow[j] = 0end
end
-- 遍历每一个元素,行乘列的形式处理
-- c = a x b
for i = 1, M dofor j = 1, N dofor k = 1, K doc[i][j] = c[i][j] + (a[i][k] or 0) * (b[k][j] or 0)endend
end
-- 打印每个元素
for i = 1, #c dolocal row = c[i]local rowContent = ""for j = 1, #row dorowContent = rowContent .. " " .. row[j]endprint(rowContent)
end--> 1 3 0 5
--> 8 0 24 16
--> 13 9 30 35

3-2-3、稀疏矩阵相乘

这种处理方式对于常规的矩阵没有问题,但是对于稀疏矩阵就会有不必要的性能开销和空间的浪费。

所以我们可以换一种思路,在之前的文章中,有使用过 pairs 进行遍历 table ,该函数的特点是保证遍历 table 中的所有元素,但不保证先后顺序。

那问题又来了,我们在上面的遍历中,行乘列的话,B 矩阵遍历的是列而不是行,所以可以调整一下刚才的循环方式,把内循环顺序换一下即可达到行乘行

可以借助上面这张图理解

  • 上一小节的处理方式:取 A 矩阵和 B 矩阵各自的蓝色小点和粉色小点相乘然后相加,循环直至的处所有结果
  • 本小节的处理方式:取 A 矩阵的蓝色小点和 B 矩阵的蓝色小点相乘,结果存入 C 中的对应位置,然后在取 A 矩阵的粉色小点和 B 矩阵的粉色小点相乘,结果和 C 的结果相加,从而达到行乘行
local a = {{ 1, nil },{ nil, 4 },{ 3, 5 }
}
local b = {{ 1, 3, nil, 5 },{ 2, nil, 6, 4 }
}
local M = 3
local N = 4
local K = 2
local c = {}
-- 初始化 c 矩阵
for i = 1, M dolocal row = {}c[i] = rowfor j = 1, N dorow[j] = 0end
end
-- 遍历每一个元素,行乘列的形式处理
-- c = a x b
for i = 1, M dofor k = 1, K dofor j = 1, N doc[i][j] = c[i][j] + (a[i][k] or 0) * (b[k][j] or 0)endend
end
-- 打印每个元素
for i = 1, #c dolocal row = c[i]local rowContent = ""for j = 1, #row dorowContent = rowContent .. " " .. row[j]endprint(rowContent)
end--> 1 3 0 5
--> 8 0 24 16
--> 13 9 30 35

聪明的你肯定会发现,现在的操作 paris 还没用上,所以接下来就要进行再一次调整,得到最终的效果

下面代码需要注意几个小点:

  1. 使用 pairs 遍历元素,避免不必要的遍历
  2. 最终结果检测是否为 0 ,如果为 0 则置为 nil ,避免不必要的开销
local a = {{ 1, nil },{ nil, 4 },{ 3, 5 }
}
local b = {{ 1, 3, nil, 5 },{ 2, nil, 6, 4 }
}--- a 和 b 是矩阵
local function mulMatrix(matrixA, matrixB)local c = {}-- 遍历 a 所有行for i = 1, #matrixA do-- c 矩阵的行结果local resultLine = {}-- 遍历 a 矩阵行的每个内容,所有的 nil 都会被跳过for k, va in pairs(matrixA[i]) do-- 遍历 b 矩阵行的每个内容,所有 nil 都会被跳过for j, vb in pairs(matrixB[k]) dolocal res = (resultLine[j] or 0) + va * vbresultLine[j] = (res ~= 0) and res or nilendendc[i] = resultLineendreturn c
end
local c = mulMatrix(a, b)-- 打印每个元素
for i = 1, #c dolocal row = c[i]local rowContent = ""for j = 1, #row dorowContent = rowContent .. " " .. (row[j] or "nil")endprint(rowContent)
end--> 1 3 nil 5
--> 8 nil 24 16
--> 13 9 30 35

四、链表

Lua 中的链表是由一个个的 table 组成,每个 table 中有一个字段是指向下一个 table 。

根据上面图,我们可以编写出如下构建一个链表

local aPoint = {value = "A", next = nil}
local bPoint = {value = "B", next = aPoint}
local cPoint = {value = "C", next = bPoint}

进行遍历也是很方便,在 Lua 中 nil 就是 false ,所以使用循环遍历一下就可以了

local list = cPoint
while list doprint(list.value)list = list.next
end--> C
--> B
--> A

五、队列

同样队列还是使用 table 来实现,因为 Lua 中的 table 并没有长度的约束,所以只要控制好单端的出入(即普通队列)或是双端的出入(双端队列)即可。

直接上代码,我们使用 table 的 insert 和 remove 便能达到双端队列的效果(普通队列的话,只需要控制在末尾端的出入便可)


local queue = {}
print("---------------------")
print("前面插入:")
table.insert(queue, 1, -10)
table.insert(queue, 1, -5)
table.insert(queue, 1, -1)
---------------------------------------
--- 此时队列内容 -1,-5,-10
---------------------------------------
print(table.remove(queue, 1))       --> -1
print(table.remove(queue, 1))       --> -5print("---------------------")
print("后面插入:")
table.insert(queue, #queue + 1, 10)
table.insert(queue, #queue + 1, 5)
table.insert(queue, #queue + 1, 1)
---------------------------------------
--- 此时队列内容 -10,10,5,1
---------------------------------------
print(table.remove(queue, #queue))  --> 1
print(table.remove(queue, #queue))  --> 5print("---------------------")
print("输出全部:")
---------------------------------------
--- 此时队列内容 -10,10
---------------------------------------
for k, v in pairs(queue) doprint(k .. "-->" .. v)
end
--> 1-->-10
--> 2-->10

但是这里有一个问题,我们在 “Lua-表” 的一章分享中,提及 table 的 insert 和 remove 如果插入点不在末尾则会导致后面的元素移动,在较小数据的情况下,这一性能损耗可以忽略,但在大量数据的情况下,则是一个性能点。

所以我们可以用两个字段来维护队列的头尾,这样就不用挪动元素


function listNew()return { first = 0, last = -1 }
endfunction pushFirst(list, value)local first = list.first - 1list.first = firstlist[first] = value
endfunction pushLast(list, value)local last = list.last + 1list.last = lastlist[last] = value
endfunction popFirst(list)local first = list.firstif first > list.last thenerror("list is empty")endlocal value = list[first]list[first] = nillist.first = first + 1return value
endfunction popLast(list)local last = list.lastif list.first > last thenerror("list is empty")endlocal value = list[last]list[last] = nillist.last = last - 1return value
endprint("---------------------")
print("前面插入:")
local queue = listNew()
pushFirst(queue, 10)
pushFirst(queue, 5)
pushFirst(queue, 1)
---------------------------------------
--- 此时队列内容 1,5,10
---------------------------------------
print(popFirst(queue))      --> 1print("---------------------")
print("后面插入:")
pushLast(queue, 15)
pushLast(queue, 20)
pushLast(queue, 30)
---------------------------------------
--- 此时队列内容 5,10,15,20,30
---------------------------------------
print(popLast(queue))       --> 30print("---------------------")
print("输出全部:")
---------------------------------------
--- 此时队列内容 5,10,15,20
---------------------------------------
for k, v in pairs(queue) doprint(k .. "-->" .. v)
end--> -2-->5
--> -1-->10
--> first-->-2
--> 1-->20
--> 0-->15
--> last-->1

六、写在最后

Lua 项目地址:Github传送门 (如果对你有所帮助或喜欢的话,赏个star吧,码字不易,请多多支持)

如果觉得本篇博文对你有所启发或是解决了困惑,点个赞或关注我呀

公众号搜索 “江澎涌”,更多优质文章会第一时间分享与你。

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

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

相关文章

MYSQL完全卸载、安装与账号创建、权限控制

一、卸载mysql CentOS 卸载 MySQL 1. 查看安装情况 使用以下命令查看当前安装mysql情况,查找以前是否装有mysql rpm -qa|grep -i mysql这里显示我安装的 MySQL 服务有有: 2. 停止 mysql 服务、删除之前安装的 mysql 删除命令:rpm -e –n…

C++笔记之条件变量(Condition Variable)与cv.wait 和 cv.wait_for的使用

C笔记之条件变量(Condition Variable)与cv.wait 和 cv.wait_for的使用 参考博客:C笔记之各种sleep方法总结 code review! 文章目录 C笔记之条件变量(Condition Variable)与cv.wait 和 cv.wait_for的使用1.条件变量&…

v8引擎编译全过程

环境vs2019 cmd 命令行需要设置成为代理模式 set http_proxyhttp://127.0.0.1:10809 set https_proxyhttp://127.0.0.1:10809 这个必须带上,不然报错,告诉编译器win系统的模式 set DEPOT_TOOLS_WIN_TOOLCHAIN0 源码 GitHub: GitHub - v8/v8: The…

Eclipse如何设置快捷键

在eclopse设置注释行和取消注释行 // 打开eclipse,依次打开:Window -> Preferences -> General -> Key,

solidwords(6)

从右视图开始,分上下两部分 标题 这里的薄壁要留意一下怎么算的(单向:默认向内;如果想向外记得选反向)

【Spring专题】Spring之Bean的生命周期源码解析——阶段二(二)(IOC之属性填充/依赖注入)

目录 前言阅读准备阅读指引阅读建议 课程内容一、依赖注入方式(前置知识)1.1 手动注入1.2 自动注入1.2.1 XML的autowire自动注入1.2.1.1 byType:按照类型进行注入1.2.1.2 byName:按照名称进行注入1.2.1.3 constructor:…

idea 新建servlet 访问提示404 WebServlet注解找不到包 报错

检查访问路径是否设置正确 如果设置为name “/testServlet”,则会404 WebServlet注解报错找不到包 检查是否引入了tomcat依赖包

线性代数的学习和整理8: 方阵和行列式相关(草稿-----未完成)

1.4.1 方阵 矩阵里,行数列数的矩阵叫做方阵方阵有很多很好的特殊属性 1.4.2 行列式 行列式是方阵的一种特殊运算如果矩阵行数列数相等,那么这个矩阵是方阵。行列数的计算方式和矩阵的不同只有方阵才有行列式行列式其实是,矩阵变化的一个面…

超越函数界限:探索JavaScript函数的无限可能

🎬 岸边的风:个人主页 🔥 个人专栏 :《 VUE 》 《 javaScript 》 ⛺️ 生活的理想,就是为了理想的生活 ! 目录 📚 前言 📘 1. 函数的基本概念 📟 1.1 函数的定义和调用 📟 1.2 …

动态内存管理

目录 为什么要用动态内存开辟 动态内存有关函数 void* malloc (size_t size); void free (void* ptr); void* calloc (size_t num, size_t size); void* realloc (void* ptr, size_t size); C/C程序的内存开辟 柔性数组 特点: 柔性数组的使用: 为什么要用…

【nodejs】用Node.js实现简单的壁纸网站爬虫

1. 简介 在这个博客中,我们将学习如何使用Node.js编写一个简单的爬虫来从壁纸网站获取图片并将其下载到本地。我们将使用Axios和Cheerio库来处理HTTP请求和HTML解析。 2. 设置项目 首先,确保你已经安装了Node.js环境。然后,我们将创建一个…

学习笔记|基于Delay实现的LED闪烁|u16是什么|a--和--a的区别|STC32G单片机视频开发教程(冲哥)|第六集(上):实现LED闪烁

文章目录 摘要软件更新什么是闪烁Tips:u16是什么? 语法分析:验证代码Tips:a--和--a的区别(--ms 的用法)测试代码: 摘要 1.基于Delay实现的LED闪烁 2.函数的使用 3,新建文件,使用模块化编程 软件更新 打…

macOS(m1/m2)破解Sublime Text和Navicat16

破解Sublime Text 说明:全程使用的是终端操作 1. 下载Sublime Text,建议使用brew下载 2. 进入到下载的app的文件夹 cd "/Applications/Sublime Text.app/Contents/MacOS/"3. 执行以下操作以确认版本是否匹配 md5 -q sublime_text | grep -i…

消息中间件的选择:RabbitMQ是一个明智的选择

💗wei_shuo的个人主页 💫wei_shuo的学习社区 🌐Hello World ! MQ(Message Queue) MQ(消息队列)是一种用于在应用程序之间进行异步通信的技术;允许应用程序通过发送和接收…

css学习3(三种样式表与样式控制优先级)

1、外部样式表&#xff1a;当样式需要应用于很多页面时&#xff0c;外部样式表将是理想的选择。在使用外部样式表的情况下&#xff0c;你可以通过改变一个文件来改变整个站点的外观。每个页面使用 <link> 标签链接到样式表&#xff0c;也要放到<head>中。 2、外部…

上网课用什么耳机和麦克风,分享几款骨传导耳机上网课用

各位耳机狂热者&#xff0c;咱们都了解传统的蓝牙耳机相对于老式有线耳机来说确实方便得多。但是&#xff0c;也别忘了蓝牙耳机会导致耳道不断堵塞&#xff0c;引发细菌滋生等问题。好在近年来&#xff0c;骨传导耳机如火如荼地走红&#xff0c;解决了这些难题&#xff0c;简直…

飞天使-jenkins进行远程linux机器修改某个文件的思路

文章目录 jenkins配置的方式jenkins中执行shell的思路 jenkins配置的方式 jenkins中执行shell的思路 下面的脚本别照抄&#xff0c;只是一个思路 ipall"$ips"# 将文本参数按行输出为变量 while IFS read -r line; doecho "$line" if [[ ! -z $line ]] &…

ubuntu 22.04 LTS 在 llvm release/17.x 分支上编译 cookbook llvm example Chapter 02

不错的资料&#xff1a; LLVMClang编译器链接器--保值【进阶之路二】 - 掘金 —————————————————————————————————————— 下载 llvm-cookbook example: $ git clone https://github.com/elongbug/llvm-cookbook.git 也可以参照llvm-pr…

Vue实现动态遍历生成el-input

实现效果: el-input的label是measureName, el-input绑定的值是formDatat.measureCode 接口返回的数据格式如下 处理过的formData的格式如下

SpringBoot+微信小程序奶茶在线点单小程序系统 附带详细运行指导视频

文章目录 一、项目演示二、项目介绍三、运行截图四、主要代码 一、项目演示 项目演示地址&#xff1a; 视频地址 二、项目介绍 项目描述&#xff1a;这是一个基于SpringBoot微信小程序框架开发的奶茶在线点单小程序系统。首先&#xff0c;这是一个前后端分离的项目&#xff…