Lua设计与实现--读书笔记
lua简介
C++底层核心模块,暴露核心接口给lua脚本层,网络的收发都在c++层完成,本书简述lua解释器的实现原理,工业级脚本语言
特性:简洁高效可移植可嵌入可扩展
纯C编写
Lua的数据结构、Lua虚拟机、Lua的其他内容
我缺少的知识:词法分析、语法分析、递归下降分析、BNF规则
Lua代码是解释成lua虚拟机能识别的字节码而运行的
- 翻译成字节码
- 字节码装载到虚拟机执行
Lua是有宿主系统的
Lua采用一种通用的数据类型来表示所有的类型,Lua只有字符串和表两种基本的数据结构
一种通用的数据类型:lua_TValue
- 一个字段存储数据类型
- 存储不同的数据类型的数据(联合体)
commonHeader+union,lua还要标出处理GC的对象
配图:
字符串
每当创建字符串时,会先查找内存中是否有一份相同的字符串数据,如果存在就直接复用,将引用直接指向字符串数据,否则就重新创建一份数据,这样在进行字符串数据比较和查找时性能会提升不少,系统内部维护了一个全局的字符串的表用来存放字符串(LUA虚拟机使用一个散列桶来管理,global_state的strt),比较时可以直接比较字符串散列值,这样也是有空间优化,相同字符串只有一份数据
应当尽量少的使用字符串连接符,每次使用都会创建一个新的字符串,大量重复的相同字符连接可以用一个table缓冲区一个字符一个字符的缓存起来,然后再调用table.concat将其全部连接
Table
有数组和散列表部分,唯一的要求就键值不能为nil
数组从1开始索引,内涵散列桶数组起点和终点的指针还有元表,意思0和负数的索引都是哈希表里的内容,如果数字的很大,超过了数组长度则是在散列表里面存
存的数据有可能在数组或者在散列表部分
查找数据:使用key来查找。我们看这个key是否为正整数且他是否大于0且小于等于数组长度,在则在数组中查找,不在则跑到散列表去查
设置数据:set setnum setstr三个set数据的函数先找对应的key,找不到则内部是调用一个newkey函数分配一个新key,大小不够会重新散列
个人实践规律:取长度符号#
取长度,只对表的序列部分进行,序列指的是表的一个子集,
当既有数组部分又有散列表部分,优先取数组部分长度,
当只有数组部分时候取数组的长度
(#{1,2,3,nil,4,5,6,} == 6)
当散列表的key和数组的Index一致的时候,遇到[num] = nil的时候便停止计数
(#{[1] = 10,[2]=20,[3]=nil,[4]=60} ==2)
(#{[1] = 10,[2]=20,[3]=32,[5]=60} ==3) 缺少4
当只有散列表部分时,取key从1开始的最大正整数
(#{["asd"] = 10,["sd1"]=20,["aasd"]=nil,[45]=60} == 0)
(#{["asd"] = 10,["sd1"]=20,["aasd"]=nil,[1]=60,[2]=899} == 0)
一般要规避重新散列操作,一般通过只使用数组部分、预分配等方式来避免重新散列
尽量不要混用数组和散列表部分,一个table最好只放一类数据
lua实现一个队列
网上的版本,用起来,pushright存数据会往数组里的填很多东西,pushleft突破了0,会往哈希表里填东西,如果一直popleft多了则数组部分的头部会被回收吗?推测不会,数组部分的指针指向的数组整体,前面几个元素回收了头部会改变,如果是popright回收数组部分是可以理解的,如果pop的是哈希的部分应该是可以回收的
List = {}
function List.new ()
return {first = 0, last = -1}
end
function List.pushleft (list, value)
local first = list.first - 1
list.first = first
list[first] = value
end
function List.pushright (list, value)
local last = list.last + 1
list.last = last
list[last] = value
end
function List.popleft (list)
local first = list.first
if first > list.last then error("list is empty") end
local value = list[first]
list[first] = nil -- to allow garbage collection
list.first = first + 1
return value
end
function List.popright (list)
local last = list.last
if list.first > last then error("list is empty") end
local value = list[last]
list[last] = nil -- to allow garbage collection
list.last = last - 1
return value
end
插入、删除时间复杂度 O(1),空间复杂度可能会随着消息变大而变大,O(n)
使用Insert和Remove的版本,空间复杂度是O(1)的,时间复杂度是(n)
关于清空lua table
a = {1,2,3,4,5}
function upDate(t)
print("====")
print(t)
t = {}
print(t)
end
upDate(a)
print(a)
print(a[1])
这里设置t = {} 或者 t = nil都不会真的清空table对象 a
只是处理了变量和值之间的关系,t的地址是值传递的,尝试用一个新表的地址给它赋值会出现函数参数值传递,对地址t引用的实际值并不会有影响,但是通过t改动引用table里的实际值是会对a有影响的比如t[1]=999,这里就是通过地址改动到了实际值的区域
我的方法是使用——table套table,其实就是相当于指针的指针
local a = {1,2,3,4,5}
local b = {9,99,999,9999}
function upDate(t)
print("====")
print(t.a)
t.a = nil
--a = nil
print(t.a)
end
local ta ={}
ta.a = a
upDate(ta)
print(ta.a)
print(a)
这样子ta.a可以便可以正确的赋值为nil了
我实现的一份LuaQueue代码
local LuaQueue = {}
-- pure array table, if you want to iterate use lua "ipairs"
function LuaQueue.New()
return {_length = 0,_maxLength=10,_queue={}}
end
function LuaQueue.SetMaxLength(queue,length)
if(queue==nil) then
error("Queue is nil")
end
queue._maxLength = length
end
function LuaQueue.GetLength(queue)
if(queue==nil) then
error("Queue is nil")
end
return queue._length
end
--push in array last pos O(1)
function LuaQueue.Push(queue,val)
if(queue==nil) then
error("Queue is nil")
end
if(queue._length<queue._maxLength) then
table.insert(queue._queue,val)
queue._length = queue._length+1
else
error("Already reach last pos")
end
end
--O(n)
function LuaQueue.Pop(queue)
if(queue==nil) then
error("Queue is nil")
end
if(queue._length>0) then
table.remove(queue._queue,1)
queue._length = queue._length-1
else
error("Queue is Empty")
end
end
function LuaQueue.GetData(queue)
if(queue==nil) then
error("Queue is nil")
end
local data={}
if(queue._length>0) then
for _,v in ipairs(queue._queue) do
table.insert(data,v)
end
return data
else
print("Queue is Empty")
end
end
function LuaQueue.ClearData(queue)
if queue == nil then
error("Queue is nil")
end
queue._queue = {}
queue._length = 0
end
--[[Example:
local testQueue = LuaQueue.New()
LuaQueue.Push(testQueue,10)
LuaQueue.Push(testQueue,20)
LuaQueue.Push(testQueue,30)
LuaQueue.Push(testQueue,40)
local data = LuaQueue.GetData(testQueue)
for _,v in pairs(data) do
print(v)
end
print("-------------------------------------")
LuaQueue.Pop(testQueue)
local data = LuaQueue.GetData(testQueue)
for _,v in pairs(data) do
print(v)
end
print("-------------------------------------")
LuaQueue.Pop(testQueue)
LuaQueue.Push(testQueue,909)
local data = LuaQueue.GetData(testQueue)
for _,v in pairs(data) do
print(v)
end
LuaQueue.ClearData(testQueue)
--]]