这是第三篇了,lua的语言基础学习差不多就结束了,接下来就是该学习,热更新相关的知识了。
lua学习(二)
Lua基础学习
13. lua元表
在 Lua table 中我们可以访问对应的 key 来得到 value 值,但是却无法对两个 table 进行操作(比如相加)。因此 Lua 提供了元表(Metatable),允许我们改变 table 的行为,每个行为关联了对应的元方法。
mytable = {} -- 普通表
mymetatable = {} -- 元表
setmetatable(mytable,mymetatable) -- 把 mymetatable 设为 mytable 的元表
--- 写成一行
mytable = setmetatable({},{})
13.1 __index 元方法
这是 metatable 最常用的键。
当你通过键来访问 table 的时候,如果这个键没有值,那么Lua就会寻找该table的metatable(假定有metatable)中的__index 键。如果__index包含一个表格,Lua会在表格中查找相应的键。
我们可以在使用 lua 命令进入交互模式查看:
$ lua
Lua 5.3.0 Copyright (C) 1994-2015 Lua.org, PUC-Rio
\> other = { foo = 3 }
\> t = setmetatable({}, { __index = other })
\> t.foo
3
\> t.bar
nil
如果__index包含一个函数的话,Lua就会调用那个函数,table和键会作为参数传递给函数。
__index 元方法查看表中元素是否存在,如果不存在,返回结果为 nil;如果存在则由 __index 返回结果。
function TestTumble()
Metatable = setmetatable({key1 = "values"},{__index = {key2 = "value"}})
print(Metatable.key1)
print(Metatable.key2)
end
TestTumble()
---values
---value
- mytable 表赋值为 {key1 = “value1”}。
- mytable 设置了元表,元方法为 __index。
- 在mytable表中查找 key1,如果找到,返回该元素,找不到则继续。
- 在mytable表中查找 key2,如果找到,返回 metatablevalue,找不到则继续。
- 判断元表有没有__index方法,如果__index方法是一个函数,则调用该函数。
- 元方法中查看是否传入 “key2” 键的参数(mytable.key2已设置),如果传入 “key2” 参数返回 “metatablevalue”,否则返回 mytable 对应的键值。
13.2 为表添加操作符
模式 | 描述 |
---|---|
__add | 对应的运算符 ‘+’. |
__sub | 对应的运算符 ‘-’. |
__mul | 对应的运算符 ‘*’. |
__div | 对应的运算符 ‘/’. |
__mod | 对应的运算符 ‘%’. |
__unm | 对应的运算符 ‘-’. |
__concat | 对应的运算符 ‘…’. |
__eq | 对应的运算符 ‘==’. |
__lt | 对应的运算符 ‘<’. |
__le | 对应的运算符 ‘<=’. |
--- 求出表的长度,因为适用# 因为下标没有按照顺序排列而造成错误
function table_max(t)
local max = 0
for i,v in ipairs(t) do
if max < i then
max = i
end
end
return max
end
myTable = setmetatable({1,4,7},{__add = function(myTable,newTable)
for i =1 ,table_max(newTable) do
table.insert(myTable,table_max(myTable)+1 ,newTable[i])
end
return myTable
end
})
newTable = {2,5,8}
myTable = myTable + newTable
for i, v in ipairs(myTable) do
print(v)
end
---147258
13.3 __To String 方法
__tostring 元方法用于修改表的输出行为。以下实例我们自定义了表的输出内容:
mytable = setmetatable({ 10, 20, 30 }, {
__tostring = function(mytable)
sum = 0
for k, v in pairs(mytable) do
sum = sum + v
end
return "表所有元素的和为 " .. sum
end
})
print(mytable)
14. lua协同程序
Lua 协同程序(coroutine)与线程比较类似:拥有独立的堆栈,独立的局部变量,独立的指令指针,同时又与其它协同程序共享全局变量和其它大部分东西。协同程序可以理解为一种特殊的线程,可以暂停和恢复其执行,从而允许非抢占式的多任务处理。协同是非常强大的功能,但是用起来也很复杂。
14.1 基本语法
方法 | 描述 |
---|---|
coroutine.create() | 创建 coroutine,返回 coroutine, 参数是一个函数,当和 resume 配合使用的时候就唤醒函数调用 |
coroutine.resume() | 重启 coroutine,和 create 配合使用 |
coroutine.yield() | 挂起 coroutine,将 coroutine 设置为挂起状态,这个和 resume 配合使用能有很多有用的效果 |
coroutine.status() | 查看 coroutine 的状态 注:coroutine 的状态有三种:dead,suspended,running,具体什么时候有这样的状态请参考下面的程序 |
coroutine.wrap() | 创建 coroutine,返回一个函数,一旦你调用这个函数,就进入 coroutine,和 create 功能重复 |
coroutine.running() | 返回正在跑的 coroutine,一个 coroutine 就是一个线程,当使用running的时候,就是返回一个 coroutine 的线程号 |
function TestCor()
print("TestCor 协程开始执行。。。")
local value = coroutine.yield("暂停了!!!")
print("协程恢复。。。传入的值:".. tostring(value))
print("协程结束!!")
end
--创建一个协程程序
local cor = coroutine.create(TestCor)
-- status :返回的时候重启成功,result 返回的就是内容
local status,result = coroutine.resume(cor)
print(result) --暂停了!!!
--再次重启继续执行剩下的代码
local status1,result1 = coroutine.resume(cor,20)
status = coroutine.status(cor)
print(result1)-- 协程恢复。。。传入的值:20 协程结束!!
print(status1)
14.2 协程和线程的区别
线程与协同程序的主要区别在于,一个具有多个线程的程序可以同时运行几个线程,而协同程序却需要彼此协作的运行。在任一指定时刻只有一个协同程序在运行,并且这个正在运行的协同程序只有在明确的被要求挂起的时候才会被挂起。协同程序有点类似同步的多线程,在等待同一个线程锁的几个线程有点类似协同。
主要区别归纳如下:
- 调度方式:线程通常由操作系统的调度器进行抢占式调度,操作系统会在不同线程之间切换执行权。而协同程序是非抢占式调度的,它们由程序员显式地控制执行权的转移。
- 并发性:线程是并发执行的,多个线程可以同时运行在多个处理器核心上,或者通过时间片轮转在单个核心上切换执行。协同程序则是协作式的,只有一个协同程序处于运行状态,其他协同程序必须等待当前协同程序主动放弃执行权。
- 内存占用:线程通常需要独立的堆栈和上下文环境,因此线程的创建和销毁会带来额外的开销。而协同程序可以共享相同的堆栈和上下文,因此创建和销毁协同程序的开销较小。
- 数据共享:线程之间可以共享内存空间,但需要注意线程安全性和同步问题。协同程序通常通过参数传递和返回值来进行数据共享,不同协同程序之间的数据隔离性较好。
- 调试和错误处理:线程通常在调试和错误处理方面更复杂,因为多个线程之间的交互和并发执行可能导致难以调试的问题。协同程序则在调试和错误处理方面相对简单,因为它们是由程序员显式地控制执行流程的。
15. lua垃圾回收
Lua 采用了自动内存管理。 这意味着你不用操心新创建的对象需要的内存如何分配出来, 也不用考虑在对象不再被使用后怎样释放它们所占用的内存。
Lua 实现了一个增量标记-扫描收集器。 它使用这两个数字来控制垃圾收集循环: 垃圾收集器间歇率和垃圾收集器步进倍率。 这两个数字都使用百分数为单位 (例如:值 100 在内部表示 1 )。
垃圾收集器间歇率控制着收集器需要在开启新的循环前要等待多久。 增大这个值会减少收集器的积极性。 当这个值比 100 小的时候,收集器在开启新的循环前不会有等待。 设置这个值为 200 就会让收集器等到总内存使用量达到 之前的两倍时才开始新的循环。
垃圾收集器步进倍率控制着收集器运作速度相对于内存分配速度的倍率。 增大这个值不仅会让收集器更加积极,还会增加每个增量步骤的长度。
不要把这个值设得小于 100 , 那样的话收集器就工作的太慢了以至于永远都干不完一个循环。 默认值是 200 ,这表示收集器以内存分配的"两倍"速工作
15.1 垃圾回收函数
- collectgarbage(“collect”): 做一次完整的垃圾收集循环。通过参数 opt 它提供了一组不同的功能:
- collectgarbage(“count”): 以 K 字节数为单位返回 Lua 使用的总内存数。 这个值有小数部分,所以只需要乘上 1024 就能得到 Lua 使用的准确字节数(除非溢出)。
- collectgarbage(“restart”): 重启垃圾收集器的自动运行。
- collectgarbage(“setpause”): 将 arg 设为收集器的 间歇率。 返回 间歇率 的前一个值。
- collectgarbage(“setstepmul”): 返回 步进倍率 的前一个值。
- collectgarbage(“step”): 单步运行垃圾收集器。 步长"大小"由 arg 控制。 传入 0 时,收集器步进(不可分割的)一步。 传入非 0 值, 收集器收集相当于 Lua 分配这些多(K 字节)内存的工作。 如果收集器结束一个循环将返回 true 。
- collectgarbage(“stop”): 停止垃圾收集器的运行。 在调用重启前,收集器只会因显式的调用运行。
16. lua I/O处理
Lua I/O 库用于读取和处理文件。分为简单模式(和C一样)、完全模式。
- 简单模式(simple model)拥有一个当前输入文件和一个当前输出文件,并且提供针对这些文件相关的操作。
- 完全模式(complete model) 使用外部的文件句柄来实现。它以一种面对对象的形式,将所有的文件操作定义为文件句柄的方法
简单模式在做一些简单的文件操作时较为合适。但是在进行一些高级的文件操作的时候,简单模式就显得力不从心。例如同时读取多个文件这样的操作,使用完全模式则较为合适。
file = io.open (filename [, mode])
模式 | 描述 |
---|---|
r | 以只读方式打开文件,该文件必须存在。 |
w | 打开只写文件,若文件存在则文件长度清为0,即该文件内容会消失。若文件不存在则建立该文件。 |
a | 以附加的方式打开只写文件。若文件不存在,则会建立该文件,如果文件存在,写入的数据会被加到文件尾,即文件原先的内容会被保留。(EOF符保留) |
r+ | 以可读写方式打开文件,该文件必须存在。 |
w+ | 打开可读写文件,若文件存在则文件长度清为零,即该文件内容会消失。若文件不存在则建立该文件。 |
a+ | 与a类似,但此文件可读可写 |
b | 二进制模式,如果文件是二进制文件,可以加上b |
+ | 号表示对文件既可以读也可以写 |
- **io.tmpfile()😗*返回一个临时文件句柄,该文件以更新模式打开,程序结束时自动删除
- io.type(file): 检测obj是否一个可用的文件句柄
- io.flush(): 向文件写入缓冲中的所有数据
- io.lines(optional file name): 返回一个迭代函数,每次调用将获得文件中的一行内容,当到文件尾时,将返回 nil,但不关闭文件。
file = io.open("Text.txt","r")
for line in file:lines() do
print(line)
end
end
TestIo()
安全模式
通常我们需要在同一时间处理多个文件。我们需要使用 file:function_name 来代替 io.function_name 方法。以下实例演示了如何同时处理同一个文件:
17. lua面向对象
首先我们知道面向对象实际上要做的就是做出来类,但是lua里面没有类这一概念,所以这就需要我们自己模拟。
lua 中的 function 可以用来表示方法。那么LUA中的类可以通过 table + function 模拟出来。
-- 面向对象的思维 table 相当于是类
-- 赋值一个新的表下 相当于是一个new
function clone(tab)
local temp = {}
for key, val in pairs(tab) do
temp[key] = val
end
return temp
end
-- 将一个table里面的所有数据全部都附加到另外一个table上
function copy(dist,tab)
for key, val in pairs(tab) do
dist[key] = val
end
end
People ={}
--[[
function People.sayHi()
print("People say Hi !")
end
]]
People.SayHi = function(self)
print("People say Hi !" .. self.name)
end
--相当于构造函数
People.new = function(name)
local self = clone(People)
self.name = name
return self
end
--相当于是继承
Man = {}
Man.new = function(name)
local self = People.new(name)
-- 把所有的都付给people的实例
copy(self,Man)
return self
end
Man.sayHello = function(self)
print("Man SAY HELLO :" .. self.name)
end
-- 相当于声明对象
--local p = clone(People)
--[[local p = People.new("zhang san")
p:SayHi()]]
--方法的重载
Man.SayHi = function(self)
print("Man say HI" .. self.name)
end
local m = Man.new("li si")
-- :将冒号左边的值隐士的当作参数传给右边的值
m:sayHello()
m:SayHi()
使用闭包的方法来模拟类
闭包:函数内部定义的方法是无法在函数外部访问的,但是使用闭包可以创建一个封闭的作用域,使得内部定义的方法可以继续访问函数外部的变量和参数。简单理解就是 :方法套方法
function People(name)
local self = {}
local function init()
self.name = name
end
function self.sayHi()
print("People say hi ".. self.name)
end
init()
return self
end
function Man(name)
local self = People(name)
self.sayHello = function()
print("Hello"..self.name)
end
return self
end
local p = People("li si")
local m = Man("wan wu")
m.sayHi()
m.sayHello()
p.sayHi()