当前位置: 首页 > news >正文

Lua中table、模块、元表和元方法

文章目录

  • 一、 table
    • 1. 数组
    • 2. map
    • 3. 混合结构
    • 4. table操作函数
    • 5. 迭代器
  • 二、 模块
    • 1. 定义一个模块
    • 2. 使用一个模块
    • 3. 再看模块
  • 三、 元表和元方法
    • 1. 重要函数
    • 2. _ _index元方法
    • 3. _ _newindex元方法
    • 4. 运算符元方法
    • 5. _ _tostring元方法
    • 6. _ _cal元方法
    • 7. 元表单独定义


一、 table

1. 数组

使用 table 可以定义一维、二维、多维数组。
不过,需要注意:

  • Lua 中的数组索引是从 1开始的;
  • 无需声明数组长度,可以随时增加元素;
  • 同一数组中的元素可以是任意类型。
--声明一个空的数组
Arr = {}
--初始化一个3*2的数组
for i=1,3 doArr[i]={}for j=1,2 doArr[i][j]=i*jend
endfor i=1,3,1 dofor j=1,2,1 doprint(Arr[i][j])end
endArr2={"hello",1,false,{"world",10,20}}
for i=1,4 doprint(Arr2[i])
end

在这里插入图片描述

2. map

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

Map = {name="张三",age=18,id=1}
--利用[]来访问元素并赋值,如果存在则更新,不存在则插入新的key-value
Map["id"]=2 --修改已有元素
print(Map["id"])Map["gender"]="male" --原先不存在gender则添加新元素
print(Map["gender"])--利用.来访问元素并赋值,如果存在则更新,不存在则插入新的key
print(Map.age)
Map.phone="123456"
print(Map.phone)

在这里插入图片描述

a = "xxx"
b = 3
c = 5-- 定义一个map,其key为表达式
arr = {["emp_"..a] = true,[b + c] = "Hello",["hi"] = 123
}print(arr.emp_xxx)
print(arr[8])
print(arr.hi)

在这里插入图片描述

3. 混合结构

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

Emp = {"北京", name="张三", age=23, "上海", depart="销售部", "广州", "深圳"}
--下标    1                           2                       3       4
--key-value不会占用数组的下标print(Emp[1])
print(Emp[2])
print(Emp[3])
print(Emp[4])

在这里插入图片描述

4. 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))--连接下标为2-3的数组元素,每个数组元素之间用'-'连接

在这里插入图片描述

B、 table.unpack()

  • 函数 table.unpack (table , i , j)
  • 解析 拆包。该函数返回指定 table 的数组中的从第 i 个元素到第 j 个元素值。i 与 j 是可选的,默认 i 为 1,j 为数组的最后一个元素。Lua5.1 不支持该函数。
Cities = {"bj北京", "sh上海", "gz广州", "sz深圳", "tj天津", "gz广州"}
print(table.unpack(Cities,2,4)) --返回下标为234的元素

在这里插入图片描述

C、 table.pack()

  • 函数 table.pack (…)
  • 解析 打包。该函数的参数是一个可变参,其可将指定的参数打包为一个 table 返回。这个返回的 table 中具有一个属性 n,用于表示该 table 包含的元素个数。Lua5.1 不支持该函数。
local t = table.pack(1, 2, 3, "hello", true)-- 输出表内容
for i = 1, t.n doprint(i, t[i])
end

在这里插入图片描述

local function printArgs(...)local args = table.pack(...)print("Received", args.n, "arguments:")for i = 1, args.n doprint("  ", i, args[i])end
endprintArgs(10, "twenty", false, {1, 2, 3})

在这里插入图片描述

D、table.maxn()

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

在5.4版本中被废弃了
在这里插入图片描述

E、 table.insert()

  • 函数 table.insert (table, pos, value):
  • 解析 该函数用于在指定 table 的数组部分指定位置 pos 插入值为 value 的一个元素。其后的元素会被后移。pos 参数可选,默认为数组部分末尾。
Cities = {"北京", "上海", "广州", "深圳", "天津", "广州"}
table.insert(Cities,2,"太原") --在下标为2的位置插入
table.insert(Cities,"寿阳") --不指明下标,则在最后插入
print(table.concat(Cities,","))

在这里插入图片描述

F、 table.remove ()

  • 函数 table.remove (table , pos)
  • 解析 该函数用于删除并返回指定 table 中数组部分位于 pos 位置的元素。其后的元素会被前移。pos 参数可选,默认删除数组中的最后一个元素。
Cities = {"北京", "上海", "广州", "深圳", "天津", "广州"}
table.remove(Cities, 2) --删除下标为2的元素
table.remove(Cities) --不指明下标,默认删除最后一个元素
print(table.concat(Cities,","))

在这里插入图片描述

G、table.sort()

  • 函数 table.sort(table [,fun(a,b)])
  • 解析 该函数用于对指定的 table 的数组元素进行升序排序,也可按照指定函数 fun(a,b)中指定的规则进行排序。fun(a,b)是一个用于比较 a 与 b 的函数,a 与 b 分别代表数组中的两个相邻元素。
  • 注意
    • 如果 arr 中的元素既有字符串又有数值型,那么对其进行排序会报错。
    • 如果数组中多个元素相同,则其相同的多个元素的排序结果不确定,即这些元素的索引谁排前谁排后,不确定。
    • 如果数组元素中包含 nil,则排序会报错。
Cities = {"bj北京", "sh上海", "gz广州", "sz深圳", "tj天津", "gz广州"}table.sort(Cities, function(a,b)return a>bend)
--自定义排序规则 从大到小
print(table.concat(Cities, ","))table.sort(Cities)
-- 默认从小到大
print(table.concat(Cities, ","))

在这里插入图片描述

5. 迭代器

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

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

和C++的范围for和go里的for range类似

Emp = {"北京", name="张三", age=23, "上海", depart="销售部", "广州", "深圳"}-- 遍历emp中的所有数组元素
for i, v in ipairs(Emp) doprint(i, v)
end-- 遍历emp中的所有元素
for k, v in pairs(Emp) doprint(k, v)
end

在这里插入图片描述

二、 模块

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

1. 定义一个模块

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

此处我在同级目录下建立了一个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)
endreturn rectangle

2. 使用一个模块

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

-- 导入一个模块
rect = require "rectangle"-- 访问模块的属性,调用模块的函数
print(rectangle.pi)
print(rectangle.perimeter(3, 5))
print(rectangle.area(3, 5))print(rect.pi)
print(rect.perimeter(3, 5))
print(rect.area(3, 5))-- 访问模块中与模块无关的内容
print(goldenRatio)
print(maxCircularArea(3, 5))

在这里插入图片描述

3. 再看模块

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

三、 元表和元方法

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

类似于C++里的运算符重载

1. 重要函数

元表中有两个重要函数:

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

2. _ _index元方法

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

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])

在这里插入图片描述

3. _ _newindex元方法

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

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

在这里插入图片描述

emp = {"北京", name="张三", age=23, "上海", depart="销售部", "广州", "深圳"}print(emp[5])-- 声明一个元表
meta = {};-- 将原始表与元表相关联
setmetatable(emp, meta)-- 无返回值的情况
function meta.__newindex(tab, key, value)print("新增的key为"..key..", value为"..value)-- 将新增的key-value写入到原始表rawset(tab, key, value)
endemp.x = "天津"print(emp.x)

在这里插入图片描述

emp = {"北京", name="张三", age=23, "上海", depart="销售部", "广州", "深圳"}-- 声明一个元表
meta = {};-- 将原始表与元表相关联
setmetatable(emp, meta)-- 再定义一个普通表
other = {}-- 元表指定的另一个普通表的作用是,暂存新增加的数据
meta.__newindex = otheremp.x = "天津"print(emp.x)
print(other.x)

在这里插入图片描述

4. 运算符元方法

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

emp = {"北京", name="张三", age=23, "上海", depart="销售部", "广州", 12, "深圳"}-- 声明一个元表
meta = {__add = function(tab, num)-- 遍历tab中的所有元素for k, v in pairs(tab) do-- 若value为数值类型,则做算术加法if type(v) == "number" thentab[k] = v + num-- 若value为string,则做字符串拼接elseif type(v) == "string" thentab[k] = v..numendend-- 返回变化过的tablereturn tabend,  -- 注意,这里必须要添加一个逗号__tostring = function(tab)str = ""-- 字符串拼接for k, v in pairs(empsum) dostr = str.." "..k..":"..vendreturn strend
};-- 将原始表与元表相关联
setmetatable(emp, meta)empsum = emp + 5print(emp)
print(empsum)

在这里插入图片描述
不止+法,其它也可以:
在这里插入图片描述

5. _ _tostring元方法

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

	__tostring = function(tab)str = ""-- 字符串拼接for k, v in pairs(empsum) dostr = str.." "..k..":"..vendreturn strend

调用下面这两条语句之后,本来应该输出table的地址的,重写_ _tostring之后,就会按照自己写的函数进行输出了

print(emp)
print(empsum)

在这里插入图片描述

6. _ _cal元方法

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

类似于C++里的仿函数

emp = {"北京", name="张三", age=23, "上海", depart="销售部", 59, "广州", "深圳"}-- 将原始表与匿名元表相关联
setmetatable(emp, {__call = function(tab, num, str)-- 遍历tablefor k, v in pairs(tab) doif type(v) == "number" thentab[k] = v + numelseif type(v) == "string" thentab[k] = v..strendendreturn tabend
})newemp = emp(5, "-hello")for k, v in pairs(newemp) doprint(k..":"..v)
end

在这里插入图片描述

7. 元表单独定义

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

新创建一个meta.lua文件

-- 声明一个元表
meta = {__add = function(tab, num)-- 遍历tab中的所有元素for k, v in pairs(tab) do-- 若value为数值类型,则做算术加法if type(v) == "number" thentab[k] = v + num-- 若value为string,则做字符串拼接elseif type(v) == "string" thentab[k] = v..numendend-- 返回变化过的tablereturn tabend,  -- 注意,这里必须要添加一个逗号__tostring = function(tab)str = ""-- 字符串拼接for k, v in pairs(empsum) dostr = str.." "..k..":"..vendreturn strend
};

在另一个文件中导入元表直接使用

-- 导入元表
require "meta"emp = {"北京", name="张三", age=23, "上海", depart="销售部", "广州", 12, "深圳"}-- 将原始表与元表相关联
setmetatable(emp, meta)empsum = emp + 5print(emp)
print(empsum)meta.__index = function(tab, key)print("通过【"..key.."】访问的值不存在")return "这个真没有"
endprint(emp.x)

在这里插入图片描述

相关文章:

  • delta 流响应
  • 【C/C++】delete nullptr;
  • Nginx的反向代理
  • 传输线的特性阻抗和传播延迟
  • 15.为什么HashMap的容量是2的幂次方
  • 代码随想录算法训练营第五十二天
  • 无法删除/重装VirtualBox,提示缺少msi安装包
  • 【系统架构设计师】2025年上半年真题论文回忆版: 论事件驱动架构及应用(包括解题思路和参考素材)
  • 中望CAD与AutoCAD的SWOT对比分析(基于2025线上发布会观察与行业数据)
  • 最终章:终焉之塔 · 前端之道
  • Telnet 命令详解
  • 传感器技术的演进与测试方法探究
  • 【设计模式】责任链
  • Ubuntu 系统grub日志级别设置
  • 专业课复习笔记 10
  • Rust 学习笔记:循环和迭代器的性能比较
  • MySQL 表内容的增删查改 -- CRUD操作,聚合函数,group by 子句
  • 期货反向跟单—交易规则设计(四)品种选择
  • 火热邀测!DataWorks数据集成支持大模型AI处理
  • vue3+element plus 自定义组件,单列的方块 图形加文字列表
  • 小说网站怎么做流量/百度热搜榜排名今日
  • 财政网站平台建设不足/产品推广策划书
  • 网站运营每天做啥工作/电商代运营公司十强
  • php网站建设设计制作方案/上海网络营销公司
  • 学校网站建设介绍/互联网营销的方式有哪些
  • 怎样建设凡科网站/软件测试培训机构哪家好