Lua設計與實現--讀書筆記

飛翔的子明發表於2020-09-28

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)
--]]

相關文章