【Lua学习笔记】Lua进阶——Table(3) 元表
接上文
文章目录
- 元表
- `__tostring`
- `__call`
- `__index`
- `__newindex`
- 运算符元方法
- 其它元表操作
元表
Q:为什么要使用元表?
A:在Lua中,常常会需要表与表之间的操作。元表中提供了一些元方法,通过自定义元方法可以实现想要的功能,相当于面向对象中给你一系列方法让你重载。
使用下列代码设置元表:
meta={}
table={}
setmetatable(table,meta) -- 第一个元素为子表,第二个元素为元表
通常在元表中操作分为三步:
- 操作子表
- 检测是否有元表
- 若有元表,检测有无对应元方法,没有元方法则返回对应操作本来的处理。若有对应元方法执行元方法。
元方法索引一般以"__"(两个下划线,我想这是因为命名私有静态变量常常以一个下划线作为开头)作为开头,例如下面的例子:
__tostring
meta = {}
table={}
setmetatable(table, meta)
print(table)
输出:
table: 00ABA2A8
meta = {__tostring = function () <--注意两个下划线return "123"end
}
table={}
setmetatable(table, meta)
print(table)输出:
123
上例相当于使用元方法重载print函数
meta = {__tostring = function (t)return t.nameend
}
table={name = "233"}
setmetatable(table, meta)
print(table)输出:
233
在上例中,即使我们未指定元方法的入参,但是因为子表和元表的关联,元方法会自动地将子表作为参数传入元方法。
__call
让我们再定义一个__call
元方法
meta = {__tostring = function ()return "456"end,__call = function(a)print(a.name)print(a)print("call")end
}
table={name = "123"}
setmetatable(table, meta)
table(2) --无论table(x)中给出的x为几,结果都是一样的输出:
123
456
call
定义了__call
元方法后,我们使用table(index)
,发现__call
元方法打印了123,456和call,123是子表的元素,而456是__tostring
的返回结果。这意味着__call
方法调用了子表作为入参a。并且像我们上面的打印样例一样,print(a)
方法又同时会调用__tostring
的元方法。而table(2)
中的参数2很明显被无视了。
现在我们可以确定一个基本规则:当使用__tostring
和__call
元方法时,我们定义的第一个参数一定是子表table本身,只有定义更多参数才能接收其它入参:
meta = {__tostring = function ()return "456"end,__call = function(a,b)print(a)print(b)print("call")end
}
table={name = "123"}
setmetatable(table, meta)
table(2)输出:
456
2
call
__index
meta = {
}
table1 = { age = 1 }
setmetatable(table1, meta)
print(table1.name)输出:
nil
我们想要找到表中的name索引,当然是没有的,输出结果是nil
那能不能让元表拥有这个索引呢?答案是不行,因为找的还是table1:
meta = {name =1}
table1 = { age = 1 }
setmetatable(table1, meta)
print(table1.name)输出:
nil
现在我们定义一个元方法 __index
,它会指向一个其他的查询表
meta = { name = 2 }
meta.__index = meta
table1 = { age = 1 }
setmetatable(table1, meta)
print(table1.name)输出:
2
整个查找的流程其实是:
- 查询子表
- 子表查询失败,查询元表有无
__index
元方法 - 若有,则查询
__index
元方法指向的表
但是还有种情况,建议不要把__index
元方法在元表内部定义:
meta = {name = 2,__index = meta,
}
table1 = { age = 1 }
setmetatable(table1, meta)
print(table1.name)输出:
nil --不明觉厉
还可以套娃
meta = {
}
metaFather = {name =4,
}
table1 ={age =1}
meta.__index = meta --让元表的index指向meta,子表找不到就去meta里找
metaFather.__index =metaFather --让元表的index指向metaFather,子表找不到就去metaFather里找
setmetatable(meta,metaFather)
setmetatable(table1, meta)
print(table1.name)输出:i
4 --允许套娃
__newindex
看4个例子:
meta = {}
table1 ={}
table1.age = 1
setmetatable(table1, meta)
print(table1.age)输出:
1meta = {}
table1 ={age = 1}
meta.__newindex = {}
setmetatable(table1, meta)
print(table1.age)输出:
1meta = {}
table1 ={}
meta.__newindex = {}
setmetatable(table1, meta)
table1.age = 1
print(table1.age)输出:
nilmeta = {}
table1 ={}
meta.__newindex = {}
table1.age = 1
setmetatable(table1, meta)
print(table1.age)输出:
1
有没有很诡异?其实很好理解,这是由于__newindex
元方法,它的作用其实是将子表新加入的元素加入到__newindex
所指向的表而不修改子表(当然__newindex
也可以套娃):
meta = {}
table1 ={}
meta.__newindex = {}
setmetatable(table1, meta)
table1.age =1
print(table1.age)
print(meta.__newindex.age)输出:
nil
1
运算符元方法
meta = {__sub = function (t1,t2)return t1.age - t2.ageend
}
table1={age=1}
table2={age=2}
setmetatable(table1, meta) --无论把元表设置给table1还是table2,结果都一样
print(table1 - table2)输出:
-1
我们发现使用运算符元方法的时候,第一个参数也不默认是绑定的子表了。而是根据运算符的左右变量依次给元方法赋值。而且无论元表设置给减数还是被减数,最终结果都是不变的。运算符元方法较多,这里就不详细列举了,从菜鸟教程上直接抄了个表格:
元方法 | 描述 |
---|---|
__add | 对应的运算符 ‘+’ |
__sub | 对应的运算符 ‘-’ |
__mul | 对应的运算符 ‘*’ |
__div | 对应的运算符 ‘/’ |
__mod | 对应的运算符 ‘%’ |
__unm | 对应的运算符 ‘-’ |
__concat | 对应的运算符 ‘..’ |
__pow | 对应的运算符 ‘^’ |
__eq | 对应的运算符 ‘==’ |
__lt | 对应的运算符 ‘<’ |
__le | 对应的运算符 ‘<=’ |
在这之中除了正常的数学运算,我们还需要讲一下比较特殊的逻辑判断的元方法(就是上表中加粗的几列),这几个元方法需要运算符的双方都绑定同一个元表。原因是元方法没有提供大于符号,运算a>b的元方法其实相当于运算b<a的原方法,这就需要b也绑定元表,因此逻辑运算硬性要求运算双方都绑定同一个元表,让我们看看下列的例子:
meta = {__eq= function (t1,t2)return trueend,
}
table1 = { age = 1 }
table2 = { name = nil }
setmetatable(table1, meta)
print(table1 == table2)
setmetatable(table2, meta)
print(table1 == table2)输出:
false
true
我们可以发现上述的相等的逻辑判断是有问题的,第一个print出false,第二个print出true。
在第一个print的时候,只有table1
绑定了元表,而table2
没有,之所以return false
是因为在逻辑运算的时候需要左右双方都绑定同一个元表。
而在第二个print的时候打印了true
,即使table1
和table2
内容不同。因为这是我们自定义的元方法,我们默认返回的就是true
。
几道例题,请思考下列几个例子的输出结果:
meta = {__eq= function (t1,t2)if t1.age == t2.age thenreturn trueendend,
}
table1 = { age = 1 }
table2 = { age = 2 }
setmetatable(table1, meta)
setmetatable(table2, meta)
print(table1 == table2)输出:
false
上述例子中,我们用__eq
元方法来判断age
索引值是否相同,显然是不同的,因此是false
meta = {__eq= function (t1,t2)if t1.name == t2.name thenreturn trueendend,
}
table1 = { age = 1 }
table2 = { name = nil }
setmetatable(table1, meta)
setmetatable(table2, meta)
print(table1 == table2)输出:
true
上述例子中,我们用元方法判断name
索引值是否相同,而table1没有name
索引,默认为nil
,table2有name = nil
,因此是true
meta = {__eq= function (t1,t2)if t1 == t2 thenreturn trueendend,
}
table1 = { age = 1 }
table2 = { name = nil }
setmetatable(table1, meta)
setmetatable(table2, meta)
print(table1 == table2)输出:
栈溢出
上述例子中,我们在元方法内部判断t1==t2
,而进行判断时相当于再次调用了__eq
元方法,因此会无限循环最终导致栈溢出。
其它元表操作
print(getmetatable(table1)) --使用getmetatable方法获得table1的元表地址输出:
table: 00BBA320
meta = { name = 2 }
meta.__index = meta
table1 = { age = 1 }
setmetatable(table1, meta)
print(rawget(table1,"nane")) --rawget方法只查询子表,无视元表index输出:
nil
meta = {}
table1 ={}
meta.__newindex = {}
setmetatable(table1, meta)
table1.age =1
print(table1.age)
rawset(table1, "age", 1) --rawset方法可以无视元表的newindex,直接修改子表
print(table1.age)输出:
nil
1
相关文章:

【Lua学习笔记】Lua进阶——Table(3) 元表
接上文 文章目录 元表__tostring__call__index__newindex运算符元方法其它元表操作 元表 Q:为什么要使用元表? A:在Lua中,常常会需要表与表之间的操作。元表中提供了一些元方法,通过自定义元方法可以实现想要的功能&…...

AI编程常用工具 Jupyter Notebook
点击上方蓝色字体,选择“设为星标” 回复”云原生“获取基础架构实践 深度学习编程常用工具 我们先来看 4 个常用的编程工具:Sublime Text、Vim、Jupyter。虽然我介绍的是 Jupyter,但并不是要求你必须使用它,你也可以根据自己的喜…...

RocketMQ重复消费的解决方案::分布式锁直击面试!
文章目录 场景分析方法的幂等分布式锁Redis实现分布式锁抢锁的设计思路 分布式锁案例 直击面试rocketmq什么时候重复消费消息丢失的问题消息在哪里丢失发送端确保发送成功并且配合失败的业务处理消费端确保消息不丢失rocketmq 主从同步刷盘 场景分析 分布式系统架构中,队列是分…...

如何降低TCP在局域网环境下的数据传输延迟
以Ping为例。本案例是一个测试题目,只有现象展示,不含解决方案。 ROS_Kinetic_26 使用rosserial_windows实现windows与ROS master发送与接收消息_windows 接收ros1 消息 什么是ping? AI: ping是互联网控制消息协议(…...

【LeetCode】78.子集
题目 给你一个整数数组 nums ,数组中的元素 互不相同 。返回该数组所有可能的子集(幂集)。 解集 不能 包含重复的子集。你可以按 任意顺序 返回解集。 示例 1: 输入:nums [1,2,3] 输出:[[],[1],[2],[1…...

认可功能介绍 - 技术声誉靠认可
需求 大家在学习和工作中, 经常碰到一些热心帮助自己的人, 我们怎么向他们表示感谢呢? 各位博主在 CSDN 也做了很多贡献,也有不少用户在做各种各样的社区活动,这些活动给我们的领军人物什么回馈呢? 这些…...

EtherNet/IP转CAN网关can协议标准
生产管理设备中,会有设备与其他设备的协议不同,数据无法互通,让你的工作陷入困境。这时,一款神奇的产品出现了——远创智控YC-EIP-CAN通讯网关! 1, 这款通讯网关采用ETHERNET/IP从站功能,可以将各种CAN总线…...

解决代理IP负载均衡与性能优化的双重挑战
在当今数字化时代,代理IP的应用范围日益广泛,它不仅在数据爬取、网络抓取等领域发挥着重要作用,也成为网络安全和隐私保护的有力工具。然而,面对庞大的数据流量和复杂的网络环境,如何实现代理IP的负载均衡和性能优化成…...

深度探索 Elasticsearch 8.X:function_score 参数解读与实战案例分析
在 Elasticsearch 中,function_score 可以让我们在查询的同时对搜索结果进行自定义评分。 function_score 提供了一系列的参数和函数让我们可以根据需求灵活地进行设置。 近期有同学反馈,function_score 的相关参数不好理解,本文将深入探讨 f…...

测牛学堂:软件测试之andorid app性能测试面试知识点总结(二)
APP性能测试指标之FPS 如果经常玩游戏的同学应该听过FPS。 FPS本来是图像领域中的概念,是指画面每秒传输的帧数。每秒钟帧数越多,所显示的动作就会越流畅。 但是因为功耗的限制,一般60fps就是跑满的效果了。 我们测试的话,一般…...

尚医通06:数据字典+EasyExcel+mongodb
内容介绍 1、数据字典列表前端 2、EasyExcel介绍、实例 3、数据字典导出接口、前端 4、数据字典导入接口、前端 5、数据字典添加redis缓存 6、MongoDB简介 7、MongoDB安装 8、MongoDB基本概念 数据字典列表前端 1、测试问题 (1)报错日志 &am…...

【前端知识】React 基础巩固(三十二)——Redux的三大原则、使用流程及实践
React 基础巩固(三十二)——Redux的三大原则 一、Redux的三大原则 单一数据源 整个应用程序的state被存储在一颗object tree 中,并且这个object tree 只存储在一个store中;Redux并没有强制让我们不能创建多个Store,但是那样做不利于数据维护…...

[NLP]使用Alpaca-Lora基于llama模型进行微调教程
Stanford Alpaca 是在 LLaMA 整个模型上微调,即对预训练模型中的所有参数都进行微调(full fine-tuning)。但该方法对于硬件成本要求仍然偏高且训练低效。 [NLP]理解大型语言模型高效微调(PEFT) 因此, Alpaca-Lora 则是利用 Lora…...

Linux Shell 脚本编程学习之【第5章 文件的排序、合并与分割 (第四部分之cut命令) 】
第5章 文件的排序、合并与分割 (第四部分之cut命令) 4 cut 命令4.1 选项及其意义4.2 输出字符 (-c)4.3 改变分隔符(-d)和提取特定域(-f) 5 paste 命令5.1 paste 命令选项及其意义5.2…...

php-golang-rpc jsonrpc和php客户端tivoka/tivoka包实践
golang 代码: package main import ( "fmt" "net" "net/rpc" "net/rpc/jsonrpc" ) type App struct{} type Res struct { Code int json:"code" Msg string json:"msg" Data any json:"…...

flutter 打包iOS安装包
flutter iOS Xcode打包并导出ipa文件安装包 1、 Xcode配置 1、 启动打包 1、 等待打包 1、 打包完成、准备导出ipa 1、 选择模式 1、 选择配置文件 1、 导出 1、 选择导出位置 1、 得到ipa...

二进制重排
二进制重排作用 二进制重排的主要目的是将连续调用的函数连接到相邻的虚拟内存地址,这样在启动时可以减少缺页中断的发生,提升启动速度。目前网络上关于ios应用启动优化,通过XCode实现的版本比较多。MacOS上的应用也是通过clang进行编译的&am…...

【Linux后端服务器开发】MAC地址与其他重要协议
目录 一、以太网 二、MAC地址 三、MTU 四、ARP协议 五、DNS系统 六、ICMP协议 七、NAT技术 八、代理服务器 一、以太网 “以太网”不是一种具体的网路,而是一种技术标准:既包含了数据链路层的内容,也包含了一些物理层的内容…...

WebGPU入门
1. 引言 前序博客: CUDA入门WebGPUZKP:客户端证明 WebGPU——Draft 2023.7.17 由苹果、谷歌、Mozilla团队发起,当前处于草稿阶段,旨在成为W3C推荐标准。 WebGPU为 在图形处理单元(GPU)上执行诸如渲染和…...

React Dva项目中.roadhogrc.mock.js直接自动导入mock目录下所有文件方式
上文 React Dva项目中模仿网络请求数据方法 中,我们书写了Dva项目模拟后端数据的方式 但是 我们.roadhogrc.mock.js中的这个处理其实并不好用 我们还需要一个一个的引入 我们可以直接靠一段代码 import fs from fs; import path from path; const mock {} fs.re…...

跨境独立站如何应对恶意网络爬虫?
目录 跨境出海独立站纷纷成立 爬虫威胁跨境电商生存 如何有效识别爬虫? 技术反爬方案 防爬虫才能保发展 中国出海跨境电商业务,主要选择大平台开设店铺,例如,亚马逊、eBay、Walmart、AliExpress、Zalando等。随着业务的扩大&…...

C# SourceGenerator 源生成器初探
简介 注意: 坑极多。而且截至2023年,这个东西仅仅是半成品 利用SourceGenerator可以在编译结束前生成一些代码参与编译,比如编译时反射之类的,还有模板代码生成都很好用。 演示仓库传送门-Github-yueh0607 使用 1. 创建项目 …...

网络安全/信息安全—学习笔记
一、网络安全是什么 网络安全可以基于攻击和防御视角来分类,我们经常听到的 “红队”、“渗透测试” 等就是研究攻击技术,而“蓝队”、“安全运营”、“安全运维”则研究防御技术。 无论网络、Web、移动、桌面、云等哪个领域,都有攻与防两面…...

【Visual Studio】无法打开包括文件: “dirent.h”: No such file or directory
VS2017/2019 无法打开包括文件: “dirent.h”: No such file or directory 1 “dirent.h”: No such file or directory 在windows下的VS2017/2019编译器中,发现无法打开“dirent.h”,主要是MSVC并没有实现这个头文件,但是在Linux这个头文件…...

asp.net MVC markdown编辑器
在 ASP.NET MVC 中,你可以使用一些第三方 Markdown 编辑器来让用户在网页上方便地编辑和预览 Markdown 内容。这些编辑器通常提供实时预览功能,将 Markdown 文本转换为实时渲染的 HTML,并支持编辑器工具栏来辅助用户编辑。 以下是一些流行的…...

论文浅尝 | 预训练Transformer用于跨领域知识图谱补全
笔记整理:汪俊杰,浙江大学硕士,研究方向为知识图谱 链接:https://arxiv.org/pdf/2303.15682.pdf 动机 传统的直推式(tranductive)或者归纳式(inductive)的知识图谱补全(KGC)模型都关注于域内(in-domain)数据,而比较少关…...

算法工程师-机器学习面试题总结(2)
线性回归 线性回归的基本思想是? 线性回归是一种用于建立和预测变量之间线性关系的统计模型。其基本思想是假设自变量(输入)和因变量(输出)之间存在线性关系,通过建立一个线性方程来拟合观测数据ÿ…...

低成本32位单片机空调内风机方案
空调内风机方案主控芯片采用低成本32位单片机MM32SPIN0230,内部集成了具有灵动特色的电机控制功能:高阶4路互补PWM、注入功能的高精度ADC、轨到轨运放、轮询比较器、32位针对霍尔传感器的捕获时钟、以及硬件除法器和DMA等电机算法加速引擎。 该方案具有…...

读发布!设计与部署稳定的分布式系统(第2版)笔记25_互联层之路由和服务
1. 控制请求数量 1.1. 这个世界可以随时摧毁我们的系统 1.1.1. 要么拒绝工作 1.1.2. 要么扩展容量 1.1.3. 没有人会在与世隔绝的环境中使用服务,现在的服务大多必须处理互联网规模的负载 1.2. 系统的每次失效,都源自某个等待队列 1.3. 每个请求都会…...

AI面试官:LINQ和Lambda表达式(二)
AI面试官:LINQ和Lambda表达式(二) 当面试官面对C#中关于LINQ和Lambda表达式的面试题时,通常会涉及这两个主题的基本概念、用法、实际应用以及与其他相关技术的对比等。以下是一些可能的面试题目,附带简要解答和相关案…...