Lua 元表,元方法

元表与元方法的概念

Lua中每个值都可具有元表。元表是普通的Lua表,定义了原始值在某些特定操作下 的行为。

例如,当table作为加法的操作数时,Lua检查其“元表”中的“__add”字段是否有 个函数。如果有,Lua调用它执行加法。我们称“元表”中的“键(如__add)”为事件(event),称“值”为元方法(metamethod)。前述例子中的事件(event)是"__add",元方法(metamethod)是执行加法的函数。

又例如,使用元表我们可以定义Lua如何计算两个table的相加操作a+b。当Lua试图对两个表进行相加时,先检查两者之一是否有元表,之后检查是否有一个叫"__add"的字段,若找到,则调用对应的值。"__add "等即时字段,其对应的值(往往是一个函数或是table)就是"元方法"。

自己的理解:可以理解为“元方法(metamethod)”只存在与“元表”中。或者认为在元表中,元方法才有意义。

Lua创建新的table的同时不会创建元表,但是可以使用“setmetatable”函数为table设置原表

当你通过键来访问table的时候,如果这个键没有值,那么Lua就会寻找该table的元表(metatable)(假定有metatable)中的__index键。如果__index 包含一个表格,Lua会在表格中查找对应的键。如果__index包含一个函数的话,Lua就会调用那个函数,table和键会作为参数传递给函数。__index元方法查看表中元素是否存在,如果不存在,返回结构为nil;如果存在则由__index返回结果。

元方法

常用元方法

  • __add:加法
  •  __sub:减法
  • __mul:乘法
  • __div:除法
  • __unm:相反数
  • __mod:取模
  • __pow:乘幂
  • __index:
  • __newindex:

关系类的元方法

除了加法减法这些算术类的操作之外,大于小于等这些关系类的操作也是有元方法的:

  • __eq:等于
  • __lt:小于
  • __le:小于等于

如果对两个具备不同“元表”的值进行这些比较操作,就会报错,一定要注意,这和加减法的规则不一样。

测试用例


local mtab1 = {}
mtab1.Name = "mtab1 name"
-- 元方法:加法
mtab1.__add = function(t1, t2)local val = "mtab1.__add"-- print(val)return val
end
-- 元方法:减法
mtab1.__sub = function(t1, t2)local val = "mtab1.__sub"-- print(val)return val
end
-- 元方法:小于
mtab1.__lt = function(t1, t2)local val = "mtab1.__lt"print("call metamethod:", val)math.randomseed(os.time())local rval = math.random(1, 10)-- for i = 1, 10 do--     rval = math.random(1, 10)-- endreturn rval < 5
endlocal mtab2 = {}
mtab2.Name = "mtab2 name"
-- 元方法:加法
mtab2.__add = function(t1, t2)local val = "mtab2.__add"-- print(val)return val
end
-- 元方法:减法
mtab2.__sub = function(t1, t2)local val = "mtab2.__sub"-- print(val)return val
endlocal tab1 = {}
local tab2 = {}
local tab3 = {}
local tab4 = {}
-- 元方法:加法,如果没有将 tab4 这是为其它 table 的元表,好像没有意义!!
tab4.__add = function(t1, t2)local val = "tab4.__add"-- print(val)return val
end--此 table 没有元表
local tab5_nomet = {}
-- 元方法:加法
tab5_nomet.__add = function(t1, t2)local val = "tab5_nomet.__add"-- print(val)return val
endlocal tab6 = {}
local tab7 = {}--此 table 没有元表
local tab8_nomet = {}-- 为 table 表设置 元表
setmetatable(tab1, mtab1)
setmetatable(tab2, mtab1)
setmetatable(tab3, mtab2)
setmetatable(tab4, {__index = mtab1})
setmetatable(tab6, tab5_nomet)
setmetatable(tab7, tab4)-------------------------------------------------------- 输出table表的地址
print(tab1, tab2, tab3, tab4, mtab1, mtab2)
-- 输出tabled表的原表地址
-- getmetatable(tab1)与 getmetatable(tab2)的值相等,是因为 tab1 与 tab2 的 元表 都是 mtab1
print(getmetatable(tab1),getmetatable(tab2),getmetatable(tab3),getmetatable(tab4),getmetatable(mtab1),getmetatable(mtab2)
)-- 输出表 key=Name 的值
print("tab1.Name=",tab1.Name,",tab2.Name=",tab2.Name,",tab3.Name=",tab3.Name,",tab4.Name=",tab4.Name,",mtab1.Name=",mtab1.Name,",mtab2.Name=",mtab2.Name
)
------------------------------------------------------------------------------------------------------------
-- 不同元表:第一个值有元表(操作符前面),就以这个元表为准看是否有元方法,如果没有就看第二个元表是否有元方法。都没有元方法就会报错。
-- 都没有元表。所以,报错:attempt to perform arithmetic on local 'mtab1' (a table value)
-- print("mtab1 + mtab2 =",mtab1 + mtab2)
------------------------------------------------------
-- 相同元表:执行元表中的元方法
-- 输出:tab1 + tab2 =   mtab1.__add
print("tab1 + tab2 =", tab1 + tab2)
-- 不同元表:第一个值有元表(操作符前面),就以这个元表为准看是否有元方法,如果没有就看第二个元表是否有元方法。都没有元方法就会报错。
-- 输出:tab1 + tab3 =   mtab1.__add
print("tab1 + tab3 =", tab1 + tab3)
-- 不同元表:第一个值有元表(操作符前面),就以这个元表为准看是否有元方法,如果没有就看第二个元表是否有元方法。都没有元方法就会报错。
-- 输出:tab1 + tab4 =   mtab1.__add
print("tab1 + tab4 =", tab1 + tab4)
-- 不同元表:第一个值有元表(操作符前面),就以这个元表为准看是否有元方法,如果没有就看第二个元表是否有元方法。都没有元方法就会报错。
-- 输出:tab1 + tab5_nomet =   mtab1.__add
print("tab1 + tab5_nomet =", tab1 + tab5_nomet)
-- 不同元表:第一个值有元表(操作符前面),就以这个元表为准看是否有元方法,如果没有就看第二个元表是否有元方法。都没有元方法就会报错。
-- 输出:tab5_nomet + tab1 =   mtab1.__add
print("tab5_nomet + tab1 =", tab5_nomet + tab1)
-- 不同元表:第一个值有元表(操作符前面),就以这个元表为准看是否有元方法,如果没有就看第二个元表是否有元方法。都没有元方法就会报错。
-- 输出:tab4 + tab1 =   mtab1.__add
print("tab4 + tab1 =", tab4 + tab1)
------------------------------------------------------
-- 不同元表:第一个值有元表(操作符前面),就以这个元表为准看是否有元方法,如果没有就看第二个元表是否有元方法。都没有元方法就会报错。
-- tab4有原表,但是没有__add元方法,tab5_nomet也没有__add元方法。所以,报错:attempt to perform arithmetic on local 'tab4' (a table value)
-- print("tab4 + tab5_nomet =",tab4 + tab5_nomet)-- 不同元表:第一个值有元表(操作符前面),就以这个元表为准看是否有元方法,如果没有就看第二个元表是否有元方法。都没有元方法就会报错。
-- tab4有原表,但是没有__add元方法,tab5_nomet也没有__add元方法。所以,报错:attempt to perform arithmetic on local 'tab5_nomet' (a table value)
-- print("tab5_nomet + tab4 =", tab5_nomet + tab4)
------------------------------------------------------
-- 输出:tab5_nomet + tab6 =     tab5_nomet.__add
print("tab5_nomet + tab6 =", tab5_nomet + tab6)
-- 输出:tab7 + tab8_nomet =     tab4.__add
print("tab7 + tab8_nomet =", tab7 + tab8_nomet)
-- 输出:tab7 + tab4 =   tab4.__add
print("tab7 + tab4 =", tab7 + tab8_nomet)
-------------------------------------------------------- 输出:tab1 < tab2 =   boolean结果
print("tab1 < tab2 =", tab1 < tab2)
-- 如果对两个具备不同“元表”的值进行这些比较操作,就会报错,一定要注意,这和加减法的规则不一样。
-- 所以,报错:attempt to compare two table values
-- print("tab1 < tab3 =", tab1 < tab3)
print("----------------")

测试用例输出结果

----------------
table: 00C5C9A8 table: 00C5C8E0 table: 00C5C958 table: 00C5C9F8 table: 00C5C9D0 table: 00C5C7F0
table: 00C5C9D0 table: 00C5C9D0 table: 00C5C7F0 table: 00C5C8B8 nil     nil
tab1.Name=      nil     ,tab2.Name=     nil     ,tab3.Name=     nil     ,tab4.Name=     mtab1 name      ,mtab1.Name=    mtab1 name      ,mtab2.Name=    mtab2 name
tab1 + tab2 =   mtab1.__add
tab1 + tab3 =   mtab1.__add
tab1 + tab4 =   mtab1.__add
tab1 + tab5_nomet =     mtab1.__add
tab5_nomet + tab1 =     mtab1.__add
tab4 + tab1 =   mtab1.__add
tab5_nomet + tab6 =     tab5_nomet.__add
tab7 + tab8_nomet =     tab4.__add
tab7 + tab4 =   tab4.__add
call metamethod:        mtab1.__lt
tab1 < tab2 =   true
----------------

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

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

相关文章

【STM32独立看门狗(IWDG) 】

提示&#xff1a;文章写完后&#xff0c;目录可以自动生成&#xff0c;如何生成可参考右边的帮助文档 文章目录 前言一、看门狗是什么&#xff1f;1.简介2. 主要功能3.独立看门狗如何工作4.寄存器写保护5.看门狗 看门时间 二、使用步骤1.开启时钟2.初始化看门狗3.开启看门狗4.喂…

ACL和NAT

文章目录 ACL和NAT一、ACL概述及产生背景1、ACL访问控制列表2、ACL工作原理3、ACL种类4、ACL命令配置步骤4.1 ACL命令配置4.1 ACL配置步骤 二、NAT&#xff08;网络地址转换&#xff09;1、NAT概述2、NAT类型2.1 静态NAT与动态NAT 3、NATPT&#xff08;端口映射&#xff09;4、…

详解—【C++】lambda表达式

目录 前言 一、lambda表达式 二、lambda表达式语法 2.1. lambda表达式各部分说明 2.2. 捕获列表说明 三、函数对象与lambda表达式 前言 在C98中&#xff0c;如果想要对一个数据集合中的元素进行排序&#xff0c;可以使用std::sort方法。 #include <algorithm> #i…

【.Net 6.0--通用帮助类--FileHelper】

前言 文件操作帮助类&#xff0c;包含下表中的方法&#xff1a; 方法名方法解释GetFileNames获取指定目录中所有文件列表GetDirectories获取指定目录中的子目录列表GetDirectoryCreateTime获取文件夹的创建时间GetFileCreateTime获取文件的创建时间GetFileSize获取一个文件的…

服务器被攻击宕机的一些小建议

现在网络攻击屡有发生&#xff0c;任何网站服务器都面临这样的危险&#xff0c;服务器被攻击造成的崩溃宕机是损失是我们无法估量的。网络攻击我们无法预测&#xff0c;但做好防御措施是必须的&#xff0c;建议所有的网站都要做好防范措施&#xff0c;准备相应的防护预案&#…

linux/CentOS 7安装Nginx

Nginx 是 C语言 开发&#xff0c;建议在 Linux 上运行&#xff0c;当然&#xff0c;也可以安装 Windows 版本&#xff0c;本篇则使用 CentOS 7 作为安装环境。 Nginx一般使用非root账号安装&#xff0c;如果还没有非root账号&#xff0c;先创建账号 创建账号 创建组&#xf…

云原生之深入解析docker实用工具gosu和su-exec实践

一、volume 的权限问题 在 Docker 中&#xff0c;需要把 host 的目录挂载到 container 中作为 volume 使用时&#xff0c;往往会发生文件权限问题。常见的现象是&#xff0c;container 对该路径并无写权限&#xff0c;以致其中服务的各种千奇百怪的问题。导致这类问题的原因&a…

maven jar sort

1&#xff09;往常项目结构lib包排序 2&#xff09;maven的默认是没有排序的

《数据结构、算法与应用C++语言描述》- 构建哈夫曼树

哈夫曼树 完整可编译运行代码见&#xff1a;Github::Data-Structures-Algorithms-and-Applications/_29huffmanTree 定长编码与可变长编码 定长编码 每个字符都用固定长度的编码来表示。 例如假设一个文本是由字符 a、u、x 和 z 组成的字符串&#xff0c;每个字符用2位二进…

Python录制和绘制音频

要使用Python录制和绘制音频,使用Python的音频处理库和绘图库。 以下是一个简单的示例代码,演示如何使用Python录制音频并绘制其波形: import wave import numpy as np import matplotlib.pyplot as plt# 设置音频参数 sample_rate = 44100 # 采样率 duration = 5 # 录制…

【idea】idea尾部自动删除空格,idea2023.1.2关闭自动去除行尾空格的功能

这个功能是由于git或者svn上的代码有许多空格的时候&#xff0c;会自动把空格去掉&#xff0c;就会导致出现许多更改的地方&#xff0c;会自动删空格。 尾部刚打好空格准备写代码&#xff0c;自动就删掉空格&#xff0c;又得重打空格后继续编码&#xff0c;非常不爽。 设置如…

HTML5 Canvas画布讲解

一、canvas 简介 ​<canvas> 是 HTML5 新增的&#xff0c;一个可以使用脚本(通常为 JavaScript) 在其中绘制图像的 HTML 元素。它可以用来制作照片集或者制作简单(也不是那么简单)的动画&#xff0c;甚至可以进行实时视频处理和渲染。 ​它最初由苹果内部使用自己 MacO…

我的NPI项目之Android 安全系列 -- EMVCo

最近一直在和支付有关的内容纠缠&#xff0c;原来我负责的产品后面还要过EMVCo的认证。于是&#xff0c;就网上到处找找啥事EMVCo&#xff0c;啥是EMVCo&#xff0c;啥是EMVCo。 于是找到了一个神奇的个人网站&#xff1a;Ganeshji Marwaha 虽然时间有点久远&#xff0c;但是用…

黑马点评04集群下的并发安全

实战篇-08.优惠券秒杀-集群下的线程并发安全问题_哔哩哔哩_bilibili 为了应对高并发&#xff0c;需要把项目部署到多个机器构成集群&#xff0c;所以需要配置nginx。 1.如何模拟集群 通过idea的ctrl d修改配置&#xff0c;实现多个tomcat运行模拟集群 然后在nginx上配置节点&…

如何在PHP中使用Memcached和Redis?

在 PHP 中使用 Memcached 和 Redis 主要涉及两个方面&#xff1a;安装相关扩展和编写代码进行交互。以下是在 PHP 中使用 Memcached 和 Redis 的基本步骤&#xff1a; 使用 Memcached&#xff1a; 1. 安装 Memcached 扩展&#xff1a; 确保你的系统上安装了 Memcached 服务器…

计算机网络快速刷题

自用//奈奎斯特定理和香农定理计算题 参考博客&#xff1a;UDP协议是什么&#xff1f;作用是什么&#xff1f; 肝了&#xff0c;整理了8张图详解ARP原理 【网络协议详解】——FTP系统协议&#xff08;学习笔记&#xff09; 在OSI参考模型中&am…

Tekton 克隆 git 仓库

Tekton 克隆 git仓库 介绍如何使用 Tektonhub 官方 git-clone task 克隆 github 上的源码到本地。 git-clone task yaml文件下载地址&#xff1a;https://hub.tekton.dev/tekton/task/git-clone 查看git-clone task yaml内容&#xff1a; 点击Install&#xff0c;选择一种…

聊聊Java中的常用类String

String、StringBuffer、StringBuilder 的区别 从可变性分析 String不可变。StringBuffer、StringBuilder都继承自AbstractStringBuilder &#xff0c;两者的底层的数组value并没有使用private和final修饰&#xff0c;所以是可变的。 AbstractStringBuilder 源码如下所示 ab…

最新AI绘画Midjourney绘画提示词Prompt教程

一、Midjourney绘画工具 SparkAi【无需魔法使用】&#xff1a; sparkAi创作系统是基于ChatGPT进行开发的Ai智能问答系统和Midjourney绘画系统&#xff0c;支持OpenAI-GPT全模型国内AI全模型。本期针对源码系统整体测试下来非常完美&#xff0c;可以说SparkAi是目前国内一款的…

《Kotlin核心编程》笔记:可空类型平台类型装箱类型数组类型泛型协变与逆变

可空类型 在Kotlin中&#xff0c;我们可以在任何类型后面加上“?”&#xff0c;比如“Int?”&#xff0c;实际上等同于“Int? Int or null”。 通过合理的使用&#xff0c;不仅能够简化很多判空代码&#xff0c;还能够有效避免空指针异常。 注意&#xff1a;由于null只能…