Lua学习笔记之迭代器、table、模块和包、元表和协程
迭代器
迭代器是一种对象,它能够来遍历标准库模板容器中的部分或全部元素,每个迭代器对象代表容器中确定的地址,在Lua中迭代器是一种支持指针类型的结构,他可以遍历集合的每一个元素。
泛型for迭代器
泛型for自己内部保存迭代函数,实际上保存三个值:迭代函数、状态常量、控制变量。
泛型for迭代器提供了集合的key/value对,
array = {"Hello","Tony","Chang"}
--for迭代器遍历
for key,value in pairs(array)
do
print(key,value)
end
结果:
事实上使用Lua默认提供的迭代函数 ipairs,我们常常使用函数来描述迭代器,下面从函数角度分析pairs迭代器运行。
(1). 初始化,获取“in”后面表达式的三个值----迭代函数、状态常量、控制变量(如果表示式返回家过不足三个则使用nil来补齐,如果返回数值多,则自动忽略其余三个的数值)
(2) 将状态常量和控制变量作为参数调用迭代函数。(在for结构来说,状态常量仅仅在初始化时候获取它的数值并传递给迭代函数)
(3)将迭代函数返回的数值赋给变量列表(每次调用函数就会返回集合的下一个元素)
(4) 如果返回的第一个数值为nil循环结束,否则执行循环体
(5)回到第二步骤再次调用迭代函数
Lua迭代器的分类:
- 无状态的迭代器
- 多状态的迭代器
无状态的迭代器
无状态的迭代器是指不保留任何状态的迭代器,因此在循环中我们可以利用无状态迭代器避免创建闭包花费额外的代价。
而每一次的迭代,迭代函数利用两个变量(状态常量和控制变量)的数值作为参数进行调用。
其代表迭代器式ipairs迭代器
--下面实现自定义迭代器来对元素内容进行平方
--自定义迭代函数
function square(iteratorMaxCount,currentNumber)
if currentNumber<iteratorMaxCount
then
currentNumber=currentNumber+1 --控制变量加一
return currentNumber,currentNumber*currentNumber
end
end
--作为迭代器调用
for i,n in square,3,0
do
print(i,n)
end
function iter(array,i)
i=i+1
local v=array[i]
if v then
return i,v
end
end
function ipiars(array)
return iter,array,0 --三个变量 函数、状态常量、控制变量
end
根据这个原理,当Lua调用ipairs(array)时候,获取三个值:迭代函数iter、状态常量array、控制变量的初始值0;然后调用iter(array,0)返回 1,array[1];
第二次调用iter(array,1) 返回2,array[2];直至返回的 n,nil 出现nil元素
多状态的迭代器
很多情况下迭代器需要保存多个状态信息而并不是简单的状态常量和控制变量,最简单的方法是使用闭包。另一种办法式将所有的信息状态封装到table中,将table作为迭代器的状态常量。
array={"Hello","Tony","Chang"}
function elementIterator(collection)
local index=0
local count=#collection
--闭包函数
return function()
index=index+1
if index<=count
then
return collection[index]
end
end
end
for element in elementIterator(array)
do
print(element)
end
table(表格)
table索引可以是任意类型,但这个数值不能是nil
table大小不固定,根据自己需要进行扩容
使用table来解决模块(module)、包(package)、和对象(Object)的。
--table的使用
--声明
table1={}
table[1]="TonyChang"
--移除引用
table1=nil
--lua的垃圾回收会释放内存
验证table属于一个存在于堆上的对象,(两个索引指向同一个table,最后更改一个索引为空则对象还是存在,被另一个索引所指引着)
test = {}
print("test的类型是",type(test))
test[1]="Tony"
test["FirstName"]="Chang"
print("test[FirstNmae]",test["FirstName"])
testCopy=test
testCopy["FirstName"]="Zhang"
print("通过testCopy进行修改之后-------------")
print("test[FirstNmae]",test["FirstName"])
--释放副本指引指引
testCopy=nil
--仍旧可以通过原指引访问table
print("test[1]")
--进行遍历打印
for key,value in pairs(test)
do
print(key,value)
end
Table中的操作
--table中的元素的连接
table2={"Hello","everyone","my","name","is","Tony"}
--直接连接
print(table.concat(table2))
--指定连接字符
print(table.concat(table2,", "))
--指定元素索引来连接
print(table.concat(table2,", ",1,2))
print(table.concat(table2,", ",1,3))
--table中的插入和移除
--在末尾插入
table.insert(table2,"Chang")
print(table.concat(table2,", "))
--指定索引处插入
table.insert(table2,2,"everybody")
print(table.concat(table2,", "))
--移除操作(默认删除最后一个元素)
table.remove(table2)
print(table.concat(table2,", "))
--移除指定索引的元素
table.remove(table2,3)
print(table.concat(table2,", "))
--table的排序
table3={"Hello","everyone","my","name","is","Tony"}
print("排序前")
for key,val in pairs(table3)
do
print(key,val)
end
--排序
table.sort(table3)
print("排序后")
for key,val in pairs(table3)
do
print(key,val)
end
--新建数组
array={}
--赋值
for i=1,5
do
array[i]=i*2
end
--遍历查看
for key,val in pairs(array)
do
print(key,val)
end
--返回所有正key值中的最大值(Lua 5.2之后消失)
--注意是所有索引中最大值 如果没有则返回0
print(table.maxn(array))
模块和包
Lua中的模块类似一个封装库,从Lua5.1开始,Lua加入标砖的模块管理机制,可以把一些公用的代码放在一个文中中,以API接口形式在其它地方进行调用,有利于降低代码耦合度。
--模块
--文件名称为 module.lua
module={}
module.constant="这是一个常量"
function module.func1()
io.write("这是一个共有函数\n")
end
--私域函数不可以直接访问
local function func2()
print("这是一个私有函数!")
end
--通过func3函数来访问func2私域函数
function module.func3()
func2()
end
return module
--通过require函数来加载模块
require("<module>")
require "<module>"
--以别名导入模块
local nick=require("module")
nick.func3()
元表(Metatable)
在table中我们可以通过访问对应的key值来得到value值,但是无法从表的层面直接来操作(例如table1+table2)这样的操作。而如果我们有了元表,就可以根据元表中定义的add加的函数来执行两个table之间的相加操作,因此给普通表table设置metatable元表可以完成table层面的操作!
--元表设置
mytable={} --普通表
mymetatable={} --元表
setmetatable(mytable,mymetatable) --将mymetatable设置为mytable的原表
--也可以直接设置
mytablb=setmetatable({},{})
--返回对象
getmetatable(mytable) --这里返回mymetatable
以下为元表常用的字段:
算术类元方法: 字段:__add(+), __mul(*), __ sub(-), __div(/), __unm, __mod(%), __pow, (__concat)
关系类元方法: 字段:__eq, __lt(<), __le(<=),其他Lua自动转换 a~=b --> not(a == b) a > b --> b < a a >= b --> b <= a (注意NaN的情况)
table访问的元方法: 字段: __index, __newindex
__index: 查询:访问表中不存的字段&
rawget(t, i)__newindex: 更新:向表中不存在索引赋值
rawset(t, k, v)
-index用来对表访问
如果__index包含一个函数的话,Lua就会调用那个函数,table和键会作为参数传递给函数。
--
table0=setmetatable({key1="value1"},{
__index = function(table,key)
if key=="key2" then
return "metatablevalue"
else
return nil
end
end
})
print(mytable.key1,mytable.key2)
-newindex元方法对表进行更新
metatable1={}
table1=setmetatable({key1="value1"},{__newindex=metatable1})
print(table1.key1) -- 打印出value1
table1.key2="val2"
print(table1.key2) --此时未发现有key2 会打印nil
--然后会检查元表中是否有__newindex方法,进行调用新加
--最后打印出val2
print(metatable1.key2)
--再次设置新键
table1.key3="val3"
print(table1.key3)
print(metatable1.key3)
--上三行代码重复验证
--如果不通过table访问不存在的键,则__newindex函数只会调用但不会进行赋值 所以访问table元组
--的新索引值是nil
print(metatable1.key4)
--打印nil
由此可知,当要访问的是缺失的索引值,解释器就会查找__newindex元方法,如果存在则调用这个函数(不进行赋值操作)只有外部访问时候赋值,其才会赋值。
在__newindex中通过调用rawset函数来更新table内容
table6=setmetatable({key1="value1"},{__newindex=function(table,key,value)
rawset(table,key,"\""..value.."\"")
end
})
table6.key1="new value"
table6.key2=6
print(table6.key1)
print(table6.key2)--打印出6 而table的索引对应数值写入进去
此时遍历访问table中内容:
-两table相加
--两个table相加
--在元组中设置__add函数
table1=setmetatable({1,2,3},{__add=function(table1,table2)--双下划线
for i=1,table.maxn(table2)
do
table.insert(table1,table.maxn(table1)+1,table2[i])
end
return table1
end
})
secondtable={4,5,6}
table3=table1+secondtable
for k,v in pairs(table3)
do
print(k,v)
end
模式 | 描述(对应符号) |
---|---|
__add | + |
__sub | - |
__mul | * |
__div | / |
__mod | % |
__unm | - |
_concat | .. |
__eq | == |
__It | < |
__Ie | <= |
__call元方法
call方法在调用一个值时候调用
--定义元方法来和
table4=setmetatable({10},{
__call=function(table1,table2)
sum=0
for i=1,table.maxn(table2) do
sum=sum+table2[i]
end
for i=1,table.maxn(table1) do
sum=sum+table1[i]
end
return sum
end
})
table5={12,13,14}
print(table4(table5))--直接传入要操作的对象
协同程序
Lua的协同程序和线程比较类似,它拥有独立的堆栈、独立的局部变量、独立的指令指针,同时又与其它协同程序共享全局变量以及其它大部分东西。
协程和线程的区别?
线程与协同程序的主要区别在于,一个具有多个线程的程序可以同时运行几个线程,而协同程序却需要彼此协作的运行。
在任一指定时刻只有一个协同程序在运行,并且这个正在运行的协同程序只有在明确的被要求挂起的时候才会被挂起。
协同程序有点类似同步的多线程,在等待同一个线程锁的几个线程有点类似协同。
--协程
--创建协程
coroutineTest=coroutine.create(
function(i)
print(i)
end
)
--启动/重启协程
coroutine.resume(coroutineTest,1)
print(coroutine.status(coroutineTest))--查看协程的状态(挂起、运行、死亡)
print('--------------------------')
--协程创造
--包裹一个函数的协程
coroutineTest2=coroutine.wrap(
function(i)
print(i)
end
)
coroutineTest2(1)--直接调用协程 执行
print("--------------------")
--一个协程中的函数
--打印1,10
coroutineTest3=coroutine.create(
function()
for i=1,10 do
print(i)
if i==3 then
print(coroutine.status(coroutineTest3))
print(coroutine.running()) -- 返回运行的协程
end
coroutine.yield() --协程挂起
end
end
)
--每次重新执行则检查上次函数执行的中断点
--继续执行循环逻辑
coroutine.resume(coroutineTest3)--打印1
coroutine.resume(coroutineTest3)--打印2
coroutine.resume(coroutineTest3)--打印3 并且打印线程状态 和运行协程的线程编号
print(coroutine.status(coroutineTest3)) --打印状态 suspended挂起状态
print(coroutine.running()) --nil 现在没有协程正在运行
print("-------------------------------")
分析可知:coroutine.running可以得知,协程的底层还是由线程实现的。
当create一个coroutine的时候就是在新线程中注册一个事件,当使用resume出发事件时候,create的
coroutine的函数就被执行,当遇到yield的时候就表示挂起当前线程,当resume触发事件时候,create的coroutine函数继续被执行,当遇到yield的时候就代表挂起当前线程,等候再次被resume唤醒。
案例:
--协程中调用的函数
function sayNum(a)
print("sayNum:",a)
return coroutine.yield(2*a)
end
coroutineTest5=coroutine.create(function(a,b)
print("第一次协同程执行输出:",a,b)
local p=sayNum(a+1)
print("第二次协同程序执行输出:",p)
local p,q=coroutine.yield(a+b,a-b)
print("第三次协同程序执行输出:",p,q)
return b,"结束协程"
end)
print("main",coroutine.resume(coroutineTest5,1,10))
print("--------------------------------------------")
print("main",coroutine.resume(coroutineTest5,"r"))
print("--------------------------------------------")
print("main",coroutine.resume(coroutineTest5,"x","y"))
print("--------------------------------------------")
分析:
- 调用resum 协程唤醒(第一次执行) resume唤醒成功返回true,(若唤醒失败则返回false)(yield函数返回参数内容)
- 协程运行
- 运行到yield语句挂起(在sayName()函数中)
- 被挂起之后,第一次resume唤醒(第一个参数是要唤醒的协程,剩余的参数是接下来执行语句参数)
- 遇到yield语句挂起,返回yield参数的内容(a+b,a-b)
- 再次resume唤醒,继续执行打印语句(resume第二个函数是打印语句中的参数)
- 遇到return 协程中函数逻辑执行完毕!协程死亡
resume和yield的配合强大之处在于--resume处于主城中,它将外部状态(数据)传入到程序(协程)内部;
yield将内部状态(数据)返回到主程序中。
使用协程来实现生产者--消费者问题
Lua学习笔记之迭代器、table、模块和包、元表和协程的更多相关文章
- Lua学习笔记(7): 模块
模块 模块就像是c语言工程项目目录里的.h.c文件或外部依赖项,为某一个文件的代码提供依赖,其实就是把工作分成几个模块,方便项目的管理,提高开发效率和维护效率 在Lua中,模块其实就是一个表,实现方式 ...
- python学习笔记之九:模块和包
Python的标准安装包括一组模块,称为标准库.这里介绍模块的工作方式,学习如何使用它们. 一. 模块 1.1 用import从外部模块获取函数并为自己的程序所用: >>> from ...
- [转]LUA 学习笔记
Lua 学习笔记 入门级 一.环境配置 方式一: 1.资源下载http://www.lua.org/download.html 2.用src中的源码创建了一个工程,注释调luac.c中main函数,生 ...
- Lua 学习笔记(一)
Lua学习笔记 1.lua的优势 a.可扩张性 b.简单 c.高效率 d.和平台无关 2.注释 a.单行注释 -- b.多行注释 --[[ --]] 3.类型和 ...
- Lua学习笔记4. coroutine协同程序和文件I/O、错误处理
Lua学习笔记4. coroutine协同程序和文件I/O.错误处理 coroutine Lua 的协同程序coroutine和线程比较类似,有独立的堆栈.局部变量.独立的指针指令,同时又能共享全局变 ...
- Lua学习笔记:面向对象
Lua学习笔记:面向对象 https://blog.csdn.net/liutianshx2012/article/details/41921077 Lua 中只存在表(Table)这么唯一一种数据结 ...
- NodeJS学习笔记之Connect中间件模块(一)
NodeJS学习笔记之Connect中间件模块(一) http://www.jb51.net/article/60430.htm NodeJS学习笔记之Connect中间件模块(二) http://w ...
- Lua学习笔记6:C++和Lua的相互调用
曾经一直用C++写代码.话说近期刚换工作.项目组中的是cocos2dx-lua,各种被虐的非常慘啊有木有. 新建cocos2dx-lua项目.打开class能够发现,事实上就是C++项 ...
- golang学习笔记12 beego table name `xxx` repeat register, must be unique 错误问题
golang学习笔记12 beego table name `xxx` repeat register, must be unique 错误问题 今天测试了重新建一个项目生成新的表,然后复制到旧的项目 ...
- (转)Lua学习笔记1:Windows7下使用VS2015搭建Lua开发环境
Lua学习笔记1:Windows7下使用VS2015搭建Lua开发环境(一)注意:工程必须添加两个宏:“配置属性”/“C或C++”/“预处理器”/“预处理器定义”,添加两个宏:_CRT_SECURE_ ...
随机推荐
- 【转帖】16.JVM栈帧内部结构-局部变量表
目录 1.局部变量表(Local variables) 1.局部变量表(Local variables) 1.局部变量表也称为局部变量数组或本地变量表. 2.局部变量表定义为一个数字数组,主要用于存储 ...
- [转帖]python库Paramiko
https://zhuanlan.zhihu.com/p/456447145 测试过程中经常会遇到需要将本地的文件上传到远程服务器上,或者需要将服务器上的文件拉到本地进行操作,以前安静经常会用到xft ...
- ebpf的简单学习
ebpf的简单学习-万事开头难 前言 bpf 值得是巴克利包过滤器 他的核心思想是在内核态增加一个可编程的虚拟机. 可以在用户态定义很多规则, 然后直接在内核态进行过滤和使用. 他的效率极高. 因为避 ...
- [转帖]ARM64体系结构编程与实践:基础知识
ARM64体系结构编程与实践:基础知识 原创 异步社区 2022-03-30 12:44:16 著作权 文章标签 寄存器 体系结构 v8 ARM64体系结构 ARM 文章分类 物联网 阅读数1570 ...
- [转帖]Cat导致内存不足原因分析
背景 线上几亿的数据在回刷的时候容器服务会出现OOM而重启,导致任务中断 内存泄露分析 jmap -histo pid 找出了有几十亿的java.lang.StackTraceElement对象,找不 ...
- 京东金融APP-新交互技术“虚拟数字人”赋能世界杯主题营销
作者:平台研发部,智能服务与产品部 距离加文·伍德提出web3.0已经过去8年时间,这8年加文·伍德创建的以太坊大放异彩,同时由web3.0引出的数字人.元宇宙也生根发芽,茁壮成长,带来了非凡的用户体 ...
- VictoriaMetrics 1.84.0发布
作者:张富春(ahfuzhang),转载时请注明作者和引用链接,谢谢! cnblogs博客 zhihu Github 公众号:一本正经的瞎扯 11.25日,valyala大神发布了VictoriaMe ...
- 从零开始配置vim(28)——代码的编译、运行与调试
在前面几个章节,我们逐渐为 Vim 配置了语法高亮.代码的跳转和自动补全功能.现在的 Vim 已经可以作为代码编辑器来使用了.但是想将它作为日常发开的主力编辑器来用还需要很长一段路要走,其中一个就是要 ...
- 【JVM】运行时内存分配
程序计数器 用于标识线程执行到了字节码文件(class文件)的哪一行,当执行native方法时,值为undefined,各个线程私有 Java虚拟机栈 每个线程独有,每个方法执行时会创建一个栈帧,用于 ...
- C++ 通过SQLite实现命令行工具
本文介绍了一个基于 C++.SQLite 和 Boost 库的简单交互式数据库操作 Shell.该 Shell 允许用户通过命令行输入执行各种数据库操作,包括添加.删除主机信息,设置主机到特定主机组, ...