《Lua程序设计》-- 学习9
迭代器和泛型for
迭代器和闭包
迭代器(iterator)是一种可以让我们遍历一个集合中所有元素的代码结构。在Lua语言中,通常使用函数表示迭代器:每一次调用函数时,函数会返回集合中的“下一个”元素。
一个闭包就是一个可以访问其自身的环境中一个或多个局部变量的函数。这些变量将连续调用过程中的值并将其保存在闭包中,从而使得闭包能够记住迭代所处的位置
一个简单的迭代器:
在这个例子中,values就是工厂。每当调用这个工厂时,它就会创建一个新的闭包(即迭代器本身)。这个闭包将它的状态保存在其外部的变量t和i中,这两个变量也是由values创建的。每次调用这个迭代器时,它就从列表t中返回下一个值。在遍历完最后一个元素后,迭代器返回nil,表示迭代结束。
泛型for的语法
泛型for在循环过程中在其内部保存了迭代函数。实际上,泛型for保存了三个值:一个迭代函数、一个不可变状态(invariant state)和一个控制变量(control variable)
无状态迭代器
无状态迭代器(stateless iterator)就是一种自身不保存任何状态的迭代器。因此,可以在多个循环中使用同一个无状态迭代器,从而避免创建新闭包的开销。
迭代的状态由正在被遍历的表(一个不可变状态,它不会在循环中改变)及当前的索引值(控制变量)组成。
还有一种创建迭代器的方式可以让迭代器进行实际的迭代操作。当使用这种迭代器时,就不再需要编写循环了。相反,只需要调用这个迭代器,并传入一个描述了在每次迭代时迭代器需要做什么的参数即可。更确切地说,迭代器接收一个函数作为参数,这个函数在循环的内部被调用,这种迭代器就被称为真正的迭代器(true iterator)。举一个更具体的例子,让我们使用这种风格再次重写allwords迭代器:
马尔可夫链算法
print("please Input N (size for the sequence of previous words):")
local N = tonumber(io.read())
while math.type(N) ~= "integer" doprint("Invalid type, ReInput N:")N = tonumber(io.read())
endfunction allwords ()local line = io.read() -- current linelocal pos = 1 -- current position in the linereturn function()-- iterator functionwhile line and line ~= "" do-- repeat while there are lineslocal w, e = string.match(line, "(%w+[,;.:]?)()", pos)if w then-- found a word?pos = e -- update next positionreturn w -- return the wordelseline = io.read() -- word not found; try next linepos = 1 -- restart from first positionendendreturn nil -- no more lines: end of traversalend
endfunction prefix (words)return table.concat(words," ")
endlocal statetab = {}function insert (prefix, value)local list = statetab[prefix]if list == nil thenstatetab[prefix] = { value }elselist[#list + 1] = valueend
endlocal MAXGEN = 200
local NOWORD = "\n"-- build table
local wordTable = {}
for i = 1,N dowordTable[N] = NOWORD
end
print("\nPlease Input the words:")
for nextword in allwords() doinsert(prefix(wordTable), nextword)for i = 1,#wordTable dowordTable[i] = wordTable[i + 1]endwordTable[N] = nextword
end
insert(prefix(wordTable), NOWORD)-- generate text
for i = 1,N dowordTable[N] = NOWORD
end
for i = 1, MAXGEN dolocal list = statetab[prefix(wordTable)]-- choose a random item from listlocal r = math.random(#list)local nextword = list[r]if nextword == NOWORD thenreturnendio.write(nextword, " ")for i = 1,#wordTable dowordTable[i] = wordTable[i + 1]endwordTable[N] = nextword
end
pil4/chapter19/chapter19.lua at master · 0kk470/pil4 (github.com)
元表和元方法
元表可以修改一个值在面对一个未知操作时的行为
例如,假设a和b都是表,那么可以通过元表定义Lua语言如何计算表达式a+b。当Lua语言试图将两个表相加时,它会先检查两者之一是否有元表(metatable)且该元表中是否有__add字段。如果Lua语言找到了该字段,就调用该字段对应的值,即所谓的元方法(metamethod)(是一个函数)
可以认为,元表是面向对象领域中的受限制类。像类一样,元表定义的是实例的行为。不过,由于元表只能给出预先定义的操作集合的行为,所以元表比类更受限;同时,元表也不支持继承
Lua语言中的每一个值都可以有元表。每一个表和用户数据类型都具有各自独立的元表,而其他类型的值则共享对应类型所属的同一个元表。Lua语言在创建新表时不带元表:
可以使用函数setmetatable来设置或修改任意表的元表:
在Lua语言中,我们只能为表设置元表;如果要为其他类型的值设置元表,则必须通过C代码或调试库完成
字符串标准库为所有的字符串都设罝了同一个元表,而其他类型在默认情况中都没有元表
一个表可以成为任意值的元表;一组相关的表也可以共享一个描述了它们共同行为的通用元表;一个表还可以成为它自己的元表,用于描述其自身特有的行为。总之,任何配置都是合法的。
算术运算相关的元方法
现在,假设想使用加法操作符来计算两个集合的并集,那么可以让所有表示集合的表共享一个元表。这个元表中定义了这些表应该如何执行加法操作。首先,我们创建一个普通的表,这个表被用作集合的元表:
然后,修改用于创建集合的函数Set.new。在新版本中只多了一行,即将mt设置为函数Set.new所创建的表的元表:
在此之后,所有由Set.new创建的集合都具有了一个相同的元表:
最后,向元表中加入元方法(metamethod)__add,也就是用于描述如何完成加法的字段:
此后,只要Lua语言试图将两个集合相加,它就会调用函数Set.union,并将两个操作数作为参数传入。
关系运算相关的元方法
元表还允许我们指定关系运算符的含义,其中的元方法包括等于(__eq)、小于(__lt)和小于等于(__le)。其他三个关系运算符没有单独的元方法,Lua语言会将a~=b转换为not(a==b),a>b转换为b<a,a>=b转换为b<=a。
部分有序是指,并非所有类型的元素都能够被正确地排序。例如,由于Not a Number(NaN)的存在,大多数计算机中的浮点数就不是完全可以排序的。
标准规定任何涉及NaN的比较都应返回假,这就意味着NaN<=x永远为假,x<NaN也为假。因此,在这种情况下,a<=b到not(b<a)的转化也就不合法了。
在集合的示例中,我们也面临类似的问题。<=显而易见且有用的含义是集合包含:a<=b通常意味着a是b的一个子集。然而,根据部分有序的定义,a<=b和b<a可能同时为假。因此,我们就必须实现__le(小于等于,子集关系)和__lt(小于,真子集关系):
表相关的元方法
算术运算符、位运算符和关系运算符的元方法都定义了各种错误情况的行为,但它们都没有改变语言的正常行为。Lua语言还提供了一种改变表在两种正常情况下的行为的方式,即访问和修改表中不存在的字段。
__index元方法
对表的访问会引发解释器查找一个名为__index的元方法。如果没有这个元方法,那么像一般情况下元素不存在的情况一样,结果就是nil;否则,则由这个元方法来提供最终结果。
---[[
--创建具有默认值的原型
prototype = {x = 0,y = 0,width = 100,height = 100}local mt = {} --创建一个元表
--声明构造函数
function new(o)setmetatable(o,mt)return o
endmt.__index = function (_,key )return prototype[key]
endw = new{x = 10,y = 20}
print(w.width)
--]]
输出100
Lua语言会发现w中没有对应的字段"width",但却有一个带有__index元方法的元表。因此,Lua语言会以w(表)和"width"(不存在的键)为参数来调用这个元方法。元方法随后会用这个键来检索原型并返回结果。
在Lua语言中,使用元方法__index来实现继承是很普遍的方法。虽然被叫作方法,但元方法__index不一定必须是一个函数,它还可以是一个表。当元方法是一个函数时,Lua语言会以表和不存在的键为参数调用该函数,正如我们刚刚所看到的。当元方法是一个表时,Lua语言就访问这个表。因此,在我们此前的示例中,可以把__index简单地声明为如下样式:
mt.__index = prototype
如果我们希望在访问一个表时不调用__index元方法,那么可以使用函数rawget。调用rawget(t,i)会对表t进行原始(raw)的访问,即在不考虑元表的情况下对表进行简单的访问
__newindex元方法
元方法__newindex与__index类似,不同之处在于前者用于表的更新而后者用于表的查询。当对一个表中不存在的索引赋值时,解释器就会查找__newindex元方法:如果这个元方法存在,那么解释器就调用它而不执行赋值。像元方法__index一样,如果这个元方法是一个表,解释器就在此表中执行赋值,而不是在原始的表中进行赋值。此外,还有一个原始函数允许我们绕过元方法:调用rawset(t,k,v)来等价于t[k]=v,但不涉及任何元方法
具有默认值的表
一个普通表中所有字段的默认值都是nil。通过元表,可以很容易地修改这个默认值:
跟踪对表的访问
假设我们要跟踪对某个表的所有访问。由于__index和__newindex元方法都是在表中的索引不存在时才有用,因此,捕获对一个表所有访问的唯一方式是保持表是空的。如果要监控对一个表的所有访问,那么需要为真正的表创建一个代理(proxy)。这个代理是一个空的表,具有用于跟踪所有访问并将访问重定向到原来的表的合理元方法
---[[
function track(t)local proxy = {} --'t'的代理类--为代理创建元表local mt = {__index == function(_,k)print("*access to element" .. tostring(k))return t[k] --访问原来的表 end,__newindex = function ( _,k,v )print("*update of element" .. tostring(k) .. " to " .. tostring(v))t[k] = v --更新原来的表end,__pairs = function( )return function(_,k) --迭代函数local nextkey,nextvalue = next(t,k)if nextkey ~= nil then --避免最后一个值print("*traversing element" .. tostring(nextkey))endreturn nextkey,nextvalueendend,__len = function() return #t end}setmetable(proxy,mt)return proxy
endt = {}
t = track(t)
t[2] = "hello"
print(t[2])--]]
只读的表
面向对象(Object-Oriented)编程
从很多意义上讲,Lua语言中的一张表就是一个对象。首先,表与对象一样,可以拥有状态。其次,表与对象一样,拥有一个与其值无关的的标识(self -- 类似于 this 指针);特别地,两个具有相同值的对象(表)是两个不同的对象,而一个对象可以具有多个不同的值;最后,表与对象一样,具有与创建者和被创建位置无关的生命周期。
对象有其自己的操作。表也可以有自己的操作,例如:
上面的代码创建了一个新函数,并将该函数存入Account对象的withdraw字段。
不过,在函数中使用全局名称Account是一个非常糟糕的编程习惯。首先,这个函数只能针对特定对象工作。其次,即使针对特定的对象,这个函数也只有在对象保存在特定的全局变量中时才能工作。如果我们改变了对象的名称,withdraw就不能工作了:
这种行为违反对象拥有独立生命周期的原则。
另一种更加有原则的方法是对操作的接受者(receiver)进行操作。因此,我们的方法需要一个额外的参数来表示该接受者,这个参数通常被称为self或this
此时,当我们调用该方法时,必须指定要操作的对象:
通过使用参数self,可以对多个对象调用相同的方法:
Lua语言可以使用冒号操作符(colon operator)隐藏self参数。使用冒号操作符,我们可以将上例重写为a2:withdraw(260.00):
我们可以使用点分语法来定义一个函数,然后用冒号语法调用它,反之亦然,只要能够正确地处理好额外的参数即可:
类(Class)
我们可以参考基于原型的语言(prototype-based language)中的一些做法来在Lua语言中模拟类,例如Self语言(JavaScript采用的也是这种方式)。在这些语言中,对象不属于类。相反,每个对象可以有一个原型(prototype)。原型也是一种普通的对象,当对象(类的实例)遇到一个未知操作时会首先在原型中查找。要在这种语言中表示一个类,我们只需要创建一个专门被用作其他对象(类的实例)的原型对象即可。类和原型都是一种组织多个对象间共享行为的方式。
如果有两个对象A和B,要让B成为A的一个原型,只需要:
即,Lua语言调用了原来的deposit函数,传入了a作为self参数。因此,新账户a从Account继承了函数deposit。同样,它还从Account继承了所有的字段。
继承(Inheritance)
多重继承(Multiple Inheritance)
这种实现的关键在于把一个函数用作__index元方法。请注意,当一个表的元表中的__index字段为一个函数时,当Lua不能在原来的表中找到一个键时就会调用这个函数。基于这一点,就可以让__index元方法在其他期望的任意数量的父类中查找缺失的键。
多重继承意味着一个类可以具有多个超类。因此,我们不应该使用一个(超)类中的方法来创建子类,而是应该定义一个独立的函数createClass来创建子类。函数createClass的参数为新类的所有超类
--在表‘plist’的列表中查找‘k’
local function search( k,plist )for i = 1,#plist dolocal v = plist[i][k] --尝试第‘i’个超类if v then return v end end
endfunction ccreateClass( ... )local c = {} --新类local parents = {...} --父类列表--在父类列表中查找类缺失的方法setmetatable(c,{__index = function(t,k)return search(k,parents)end })--将‘c’作为其实例的元素c.__index = c--为新类定义一个新的构造函数function c:new(o)o = o or {}setmetatable(o,c)return oendreturn c -- 返回新类
end
私有性(Privacy)
一个表用来保存对象的状态,另一个表用于保存对象的操作(或接口)。我们通过第二个表来访问对象本身,即通过组成其接口的操作来访问。为了避免未授权的访问,表示对象状态的表不保存在其他表的字段中,而只保存在方法的闭包中。例如,如果要用这种设计来表示银行账户,那么可以通过下面的工厂函数创建新的对象:
首先,这个函数创建了一个用于保存对象内部状态的表,并将其存储在局部变量self中。然后,这个函数创建了对象的方法。最后,这个函数会创建并返回一个外部对象,该对象将方法名与真正的方法实现映射起来。这里的关键在于,这些方法不需要额外的self参数,而是直接访问self变量。由于没有了额外的参数,我们也就无须使用冒号语法来操作这些对象,而是可以像普通函数那样来调用这些方法:
这种设计给予了存储在表self中所有内容完全的私有性。当newAccount返回后,就无法直接访问这个表了,我们只能通过在newAccount中创建的函数来访问它
单方法对象(Single-method Object)
上述面向对象编程实现的一个特例是对象只有一个方法的情况。在这种情况下,可以不用创建接口表,只要将这个单独的方法以对象的表示形式返回即可。诸如io.lines或string.gmatch这样的内部保存了状态的迭代器就是一个单方法对象。
虽然使用这种方式不能实现继承,但我们却可以拥有完全的私有性:访问单方法对象中某个成员只能通过该对象所具有的唯一方法进行。
对偶表示(Dual Representation)
实现私有性的另一种有趣方式是使用对偶表示(dual representation)
相关文章:

《Lua程序设计》-- 学习9
迭代器和泛型for 迭代器和闭包 迭代器(iterator)是一种可以让我们遍历一个集合中所有元素的代码结构。在Lua语言中,通常使用函数表示迭代器:每一次调用函数时,函数会返回集合中的“下一个”元素。 一个闭包就是一个…...

GIS应用水平考试一级—2009 年度第二次
全国信息化工程师——GIS应用水平考试 2009 年度第二次全国统一考试一级 试卷说明: 1、本试卷共9页,6个大题,满分150 分,150 分钟完卷。 2、考试方式为闭卷考试。 3、将第一、二、三題的答案用铅笔涂写到(NCIE-GIS)答题卡上。 4、将第四、五、六题的答案填写到主观题答题卡上…...

【计算机视觉】万字长文详解:卷积神经网络
以下部分文字资料整合于网络,本文仅供自己学习用! 一、计算机视觉概述 如果输入层和隐藏层和之前一样都是采用全连接网络,参数过多会导致过拟合问题,其次这么多的参数存储下来对计算机的内存要求也是很高的 解决这一问题&#x…...

Vue3项目封装一个Element-plus Pagination分页
前言:后台系统分页肯定是离不开的,但是ui框架都很多,我们可以定义封装一种格式,所有项目按到这个结构来做. 实例: 第一步:在项目components组件新建一个分页组件,用来进行封装组件. 第二步:根据官方的进行定义,官方提供的这些,需要我们封装成动态模式 第三步:代码改造 <!-…...

node.js(nest.js控制器)学习笔记
nest.js控制器: 控制器负责处理传入请求并向客户端返回响应。 为了创建基本控制器,我们使用类和装饰器。装饰器将类与所需的元数据相关联,并使 Nest 能够创建路由映射(将请求绑定到相应的控制器)。 1.获取get请求传参…...

Mybatis 源码系列:领略设计模式在 Mybatis 其中的应用
文章目录 一、Builder模式二、工厂模式三、单例模式四、代理模式五、组合模式六、模板方式模式七、适配器模式八、装饰器模式九、迭代器模式 虽然我们都知道有23种设计模式,但是大多停留在概念层面,真实开发中很少遇到,Mybatis源码中使用了大…...

用的到的linux-文件移动-Day2
前言: 在上一节,我们复习了cd大法和创建生成文件和文件夹的方法,介绍了一些“偷懒”(高效)的小技巧,本节,我们一起来探讨下,我们对文件移动操作时有哪些可以偷懒的小技巧~ 一、复制…...

红队打靶练习:INFOSEC PREP: OSCP
目录 信息收集 1、arp 2、nmap WEB 信息收集 wpscan dirsearch ssh登录 提权 信息收集 1、arp ┌──(root㉿ru)-[~/kali] └─# arp-scan -l Interface: eth0, type: EN10MB, MAC: 00:0c:29:69:c7:bf, IPv4: 192.168.110.128 Starting arp-scan 1.10.0 with 256 ho…...
【linux】文件修改记录
是的,在Linux上,您可以使用’ find 命令检查最近修改的文件。此实用程序可以搜索在指定天数内修改过的文件。你可以这样使用它: 查找主目录中最近24小时(1天)内修改过的文件。 find ~ -type f -mtime -1命令说明: -“~”表示您的主目录。 ’ -type f…...

Vue学习Element-ui
声明:本文来源于黑马程序员PDF讲义 Ajax 我们前端页面中的数据,如下图所示的表格中的学生信息,应该来自于后台,那么我们的后台和前端是 互不影响的2个程序,那么我们前端应该如何从后台获取数据呢?因为是2…...

存内计算技术—解决冯·诺依曼瓶颈的AI算力引擎
文章目录 存内计算技术背景CSDN首个存内计算开发者社区硅基光电子技术存内计算提升AI算力知存科技存算一体芯片技术基于存内计算的语音芯片的实现挑战 参考文献 存内计算技术背景 存内计算技术是一种革新性的计算架构,旨在克服传统冯诺依曼架构的瓶颈,并…...
数据结构--树
一、树的基本术语 结点:树中的一个独立单元 结点的度:结点下分支的个数 树的度:树中所有结点中度的最大值 非终端结点:度不为0的结点 双亲和孩子:结点下的子树称为该结点的孩子.相应地,该结点称为孩子的双亲 兄弟:同一个双亲的孩子之间 祖先:从根到该结点所经分支上的所…...

计算机网络_1.3电路交换、分组交换和报文交换
1.3电路交换、分组交换和报文交换 一、电路交换1、“电路交换”例子引入2、电路交换的三个阶段3、计算机之间的数据传送不适合采用电路交换 二、分组交换1、发送方(1)报文(2)分组(3)首部 2、交换节点3、接收…...

【AI视野·今日NLP 自然语言处理论文速览 第七十七期】Mon, 15 Jan 2024
AI视野今日CS.NLP 自然语言处理论文速览 Mon, 15 Jan 2024 Totally 57 papers 👉上期速览✈更多精彩请移步主页 Daily Computation and Language Papers Machine Translation Models are Zero-Shot Detectors of Translation Direction Authors Michelle Wastl, Ja…...

神经网络的一些常规概念
epoch:是指所有样本数据在神经网络训练一次(单次epoch(全部训练样本/batchsize)/iteration1)或者(1个epochiteration数 batchsize数) batch-size:顾名思义就是批次大小,也就是一次训练选取的样…...

【从零开始的rust web开发之路 三】orm框架sea-orm入门使用教程
【从零开始的rust web开发之路 三】orm框架sea-orm入门使用教程 文章目录 前言一、引入依赖二、创建数据库连接简单链接连接选项开启日志调试 三、生成实体安装sea-orm-cli创建数据库表使用sea-orm-cli命令生成实体文件代码 四、增删改查实现新增数据主键查找条件查找查找用户名…...
SQL中limit的用法
在SQL中,LIMIT是一个用于限制返回结果行数的关键词。它可用于在查询结果中指定返回的行数,从而可以用于分页查询或限制结果集大小。 LIMIT关键词有两种常用的语法格式: LIMIT offset, count:该语法用于指定返回结果的起始位置和…...

vue3 [Vue warn]: Unhandled error during execution of scheduler flush
文章目录 前言一、报错截图二、排除问题思路相关问题 Vue3 优雅解决方法异步组件异同之处:好处:在使用异步组件时,有几个注意点: vue3 定义与使用异步组件 总结 前言 Bug 记录。开发环境运行正常,构建后时不时触发下面…...
【vue2源码】阶段一:Vue 初始化
文章目录 一、项目目录1、主目录2、打包入口 二、构造函数Vue的初始化1、创建 Vue 构造函数2、初始化内容分析2.1 initMixin2.2 stateMixin2.3 eventsMixin2.4 lifecycleMixin2.5 renderMixin 一、项目目录 源码版本:2.7.16 1、主目录 src |-- compiler # 包…...

14.java集合
文章目录 概念Collection 接口概念示例 Iterator 迭代器基本操作:并发修改异常增强循环遍历数组:遍历集合:遍历字符串:限制 list接口ListIteratorArrayList创建 ArrayList:添加元素:获取元素:修…...

JavaSec-RCE
简介 RCE(Remote Code Execution),可以分为:命令注入(Command Injection)、代码注入(Code Injection) 代码注入 1.漏洞场景:Groovy代码注入 Groovy是一种基于JVM的动态语言,语法简洁,支持闭包、动态类型和Java互操作性,…...

XCTF-web-easyupload
试了试php,php7,pht,phtml等,都没有用 尝试.user.ini 抓包修改将.user.ini修改为jpg图片 在上传一个123.jpg 用蚁剑连接,得到flag...

python打卡day49
知识点回顾: 通道注意力模块复习空间注意力模块CBAM的定义 作业:尝试对今天的模型检查参数数目,并用tensorboard查看训练过程 import torch import torch.nn as nn# 定义通道注意力 class ChannelAttention(nn.Module):def __init__(self,…...

渗透实战PortSwigger靶场-XSS Lab 14:大多数标签和属性被阻止
<script>标签被拦截 我们需要把全部可用的 tag 和 event 进行暴力破解 XSS cheat sheet: https://portswigger.net/web-security/cross-site-scripting/cheat-sheet 通过爆破发现body可以用 再把全部 events 放进去爆破 这些 event 全部可用 <body onres…...
多模态商品数据接口:融合图像、语音与文字的下一代商品详情体验
一、多模态商品数据接口的技术架构 (一)多模态数据融合引擎 跨模态语义对齐 通过Transformer架构实现图像、语音、文字的语义关联。例如,当用户上传一张“蓝色连衣裙”的图片时,接口可自动提取图像中的颜色(RGB值&…...
爬虫基础学习day2
# 爬虫设计领域 工商:企查查、天眼查短视频:抖音、快手、西瓜 ---> 飞瓜电商:京东、淘宝、聚美优品、亚马逊 ---> 分析店铺经营决策标题、排名航空:抓取所有航空公司价格 ---> 去哪儿自媒体:采集自媒体数据进…...
Element Plus 表单(el-form)中关于正整数输入的校验规则
目录 1 单个正整数输入1.1 模板1.2 校验规则 2 两个正整数输入(联动)2.1 模板2.2 校验规则2.3 CSS 1 单个正整数输入 1.1 模板 <el-formref"formRef":model"formData":rules"formRules"label-width"150px"…...

使用 SymPy 进行向量和矩阵的高级操作
在科学计算和工程领域,向量和矩阵操作是解决问题的核心技能之一。Python 的 SymPy 库提供了强大的符号计算功能,能够高效地处理向量和矩阵的各种操作。本文将深入探讨如何使用 SymPy 进行向量和矩阵的创建、合并以及维度拓展等操作,并通过具体…...

9-Oracle 23 ai Vector Search 特性 知识准备
很多小伙伴是不是参加了 免费认证课程(限时至2025/5/15) Oracle AI Vector Search 1Z0-184-25考试,都顺利拿到certified了没。 各行各业的AI 大模型的到来,传统的数据库中的SQL还能不能打,结构化和非结构的话数据如何和…...
前端打包工具简单介绍
前端打包工具简单介绍 一、Webpack 架构与插件机制 1. Webpack 架构核心组成 Entry(入口) 指定应用的起点文件,比如 src/index.js。 Module(模块) Webpack 把项目当作模块图,模块可以是 JS、CSS、图片等…...