【Lua学习笔记】Lua进阶——Table(3) 元表

在这里插入图片描述
接上文

文章目录

  • 元表
    • `__tostring`
    • `__call`
    • `__index`
    • `__newindex`
    • 运算符元方法
    • 其它元表操作


元表

Q:为什么要使用元表?

A:在Lua中,常常会需要表与表之间的操作。元表中提供了一些元方法,通过自定义元方法可以实现想要的功能,相当于面向对象中给你一系列方法让你重载。

使用下列代码设置元表:

meta={}
table={}
setmetatable(table,meta) -- 第一个元素为子表,第二个元素为元表

通常在元表中操作分为三步:

  1. 操作子表
  2. 检测是否有元表
  3. 若有元表,检测有无对应元方法,没有元方法则返回对应操作本来的处理。若有对应元方法执行元方法。

元方法索引一般以"__"(两个下划线,我想这是因为命名私有静态变量常常以一个下划线作为开头)作为开头,例如下面的例子:

__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

整个查找的流程其实是:

  1. 查询子表
  2. 子表查询失败,查询元表有无 __index元方法
  3. 若有,则查询 __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,即使table1table2内容不同。因为这是我们自定义的元方法,我们默认返回的就是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

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

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

相关文章

时间复杂度为O(n2)的三种简单排序算法

1.冒泡排序 冒泡排序只会操作相邻的两个数据。每次冒泡操作都会对相邻的两个元素进行比较&#xff0c;看是否满足大小关系要求。如果不满足就让它俩互换。一次冒泡会让至少少一个元素移动到它应该在的位置&#xff0c;重复n次&#xff0c;就完成了n个数据的排序工作。 /*** …

数据包在网络中传输的过程

ref: 【先把这个视频看完了】&#xff1a;数据包的传输过程【网络常识10】_哔哩哔哩_bilibili 常识都看看 》Ref&#xff1a; 1. 这个写的嘎嘎好&#xff0c;解释了为啥4层7层5层&#xff0c;还有数据包封装的问题:数据包在网络中的传输过程详解_数据包传输_张孟浩_jay的博客…

<C++>二、 类和对象

1.面向对象和面向过程 C语言是面向过程的&#xff0c;关注的是过程&#xff0c;分析出求解问题的步骤&#xff0c; 通过函数调用逐步解决问题。 C是基于面向对象的&#xff0c;关注的是对象&#xff0c;将一件事情拆分成不同的对象&#xff0c;靠对象之间的交互完成。 2. C类 C…

2023 蓝桥杯真题B组 C/C++

https://www.dotcpp.com/oj/train/1089/ 题目 3150: 蓝桥杯2023年第十四届省赛真题-冶炼金属 题目描述 小蓝有一个神奇的炉子用于将普通金属 O 冶炼成为一种特殊金属 X。这个炉子有一个称作转换率的属性 V&#xff0c;V 是一个正整数&#xff0c;这意味着消耗 V 个普通金 属 O…

Java开发 - Redis常见问题场景及解决办法一览

前言 前面几篇博客对Redis的讲解不可谓不详细&#xff0c;从单节点到主从&#xff0c;到sentinel哨兵&#xff0c;到Redis Cluster都一一搭配使用给大家做了讲解&#xff0c;但在使用Redis的过程中我们还是要注意一些比较常见的问题&#xff0c;比如穿透、击穿等等&#xff0c…

助力保险行业数字化创新,麒麟信安参展2023中国财险科技应用高峰论坛

2023年7月27日&#xff0c;由中科软科技股份有限公司主办的“中国财险科技应用高峰论坛”在北京古北水镇成功举办。作为享誉中国保险科技界的盛会&#xff0c;本次活动以“数智保险 创新未来”主题&#xff0c;汇聚全国数百位保险公司主管领导、资深保险行业信息化专家&#xf…

Ae 效果:CC Kernel

颜色校正/CC Kernel Color Correction/CC Kernel CC Kernel&#xff08;CC 卷积核&#xff09;效果主要用于图像的卷积处理&#xff0c;通过在卷积矩阵中设置不同的权重值&#xff0c;可以实现图像的锐化 Sharpen、模糊 Blur、查找边缘 Find Edges以及浮雕 Emboss等效果。 ◆ …

服务调用---------Ribbon和Feign

目录​​​​​​​ 1、Ribbon 1.1 Ribbon简介 1.2 Ribbon负载均衡 负载均衡原理 负载均衡策略 Ribbon和Nginx的区别 1.3 服务调用和Ribbon负载均衡实现 2、Feign&openFeign 3、Feign支持的配置 日志功能 连接池 feign-api远程包 1、Ribbon 1.1 Ribbon简介 Ribb…

神经概率语言模型

本文主要参考《A Neural Probabilistic Language Model》这是一篇很重要的语言模型论文,发表于2003年。主要贡献如下: 提出了一种基于神经网络的语言模型&#xff0c;是较早将神经网络应用于语言模型领域的工作之一&#xff0c;具有里程碑意义。采用神经网络模型预测下一个单词…

05-向量的意义_n维欧式空间

线性代数 什么是向量&#xff1f;究竟为什么引入向量&#xff1f; 为什么线性代数这么重要&#xff1f;从研究一个数拓展到研究一组数 一组数的基本表示方法——向量&#xff08;Vector&#xff09; 向量是线性代数研究的基本元素 e.g. 一个数&#xff1a; 666&#xff0c;…

springboot整合tio-websocket方案实现简易聊天

写在最前&#xff1a; 常用的http协议是无状态的&#xff0c;且不能主动响应到客户端。最初想实现状态动态跟踪只能用轮询或者其他效率低下的方式&#xff0c;所以引入了websocket协议&#xff0c;允许服务端主动向客户端推送数据。在WebSocket API中&#xff0c;浏览器和服务…

实用上位机--QT

实用上位机–QT 通信协议如下 上位机设计界面 #------------------------------------------------- # # Project created by QtCreator 2023-07-29T21:22:32 # #-------------------------------------------------QT += core gui serialportgreaterThan(QT_MAJOR_V…

Unity实现在3D模型标记

Canvas 模式是UI与3D混合模式&#xff08;Render modelScreen space-Camera) 实现在3D模型标记&#xff0c;旋转跟随是UI不在3D物体下 代码&#xff1a; using System.Collections; using System.Collections.Generic; using UnityEngine; using UnityEngine.UI; public clas…

矩阵中的路径(JS)

矩阵中的路径 题目 给定一个 m x n 二维字符网格 board 和一个字符串单词 word 。如果 word 存在于网格中&#xff0c;返回 true &#xff1b;否则&#xff0c;返回 false 。 单词必须按照字母顺序&#xff0c;通过相邻的单元格内的字母构成&#xff0c;其中“相邻”单元格是…

valgrind——内存泄漏检测介绍

文章目录 1. 概述1. 体系结构2. Linux 程序内存空间布局3. 内存检查原理 2. valgrind工具3. 常用选项4. 示例1. 内存泄漏2. 数组越界3. 内存覆盖4. 使用未初始化的值5. 内存申请与释放函数不匹配 5. 总结 1. 概述 1. 体系结构 Valgrind 是一套 Linux 下&#xff0c;开放源代码…

移动端个人中心UI设计

效果图 源码如下 页面设计 <template><div class"container"><!-- 顶部用户信息 start--><div class"header"><div class"user-info"><van-image class"user-img" round width"70" :sr…

HCIP--云计算题库 V5.0版本

在国家政策的支持下&#xff0c;我国云计算应用市场发展明显加快&#xff0c;越来越多的企业开始介入云产业&#xff0c;出现了大量的应用解决方案&#xff0c;云应用的成功案例逐渐丰富&#xff0c;用户了解和认可程度不断提高&#xff0c;云计算产业发展迎来了“黄金机遇期”…

【ArcGIS Pro二次开发】(55):给多个要素或表批量添加字段

在工作中可能会遇到这样的场景&#xff1a;有多个GDB要素、表格&#xff0c;或者是SHP文件&#xff0c;需要给这个要素或表添加相同的多个字段。 在这种情况下&#xff0c;手动添加就变得很繁琐&#xff0c;于是就做了这个工具。 需求具体如下图&#xff1a; 左图是待处理数据…

C数据结构——无向图(邻接矩阵方式) 创建与基本使用

源码注释 // // Created by Lenovo on 2022-05-13-上午 9:06. // 作者&#xff1a;小象 // 版本&#xff1a;1.0 //#include <stdio.h> #include <malloc.h>#define MAXSIZE 1000 // BFS队列可能达到的最大长度 #define MAX_AMVNUMS 100 // 最大顶点数typedef enu…

电脑剪辑视频的软件有哪些?试试这几种视频剪辑工具

视频剪辑可以帮助人们在不同情境下更好地理解和消化视频内容。通过剪辑&#xff0c;可以去除不必要的素材并突出重点&#xff0c;使观看者能够更快地获取信息&#xff0c;并且更容易保持注意力的集中。此外&#xff0c;剪辑可以提高视频质量&#xff0c;例如通过添加音乐、图形…