Lua中的表 讀《Lua設計與實現》筆記

2021-08-20 20:40:26 字數 3675 閱讀 1058

1.lua語言用表來表示一切資料結構。

2.lua表分為陣列和雜湊表部分。

陣列部分索引從1開始。

雜湊表部分可以儲存任何不能儲存在陣列部分的資料,唯一的要求是鍵值不能為nil

(lobject.h)

typedef struct table node;

value型別定義

typedef struct lua_tvalue  tvalue;
tkey型別定義

typedef union tkey  nk;

tvalue tvk;

} tkey;

由此可知lua表將資料存放在兩種型別的資料結構中,乙個是陣列,乙個是雜湊表。

由於表中包含雜湊表和陣列兩部分資料,所以乙個以整數作為鍵值的資料寫入lua表時,並不確定是寫入陣列還是雜湊表。

偽**如下:

如果輸入的key是乙個整數,並且它的值》0&&<=陣列大小

嘗試在陣列部分查詢

否則嘗試在雜湊表查詢:

計算出該key的雜湊值,根據此雜湊值訪問node陣列得到雜湊桶所在的位置

遍歷該雜湊桶下的所有鍊錶元素,直到找到該key為止

function print_ipairs(t)

for k,v in ipairs(t) do

print(k)

endendlocal t = {}

t[1] = 0

t[100] = 0

只有1作為陣列部分儲存下來了,而100是儲存到雜湊表部分中的。

新增新元素流程比較複雜,涉及到重新分配表中的陣列和雜湊表部分的流程。

雜湊表部分的資料組織是:首先計算資料的key所在的桶陣列位置,這個位置稱為mainposition。相同的mainposition的資料以鍊錶形式組織。

api包括luah_set、luah_setnum、luah_setstr這3個函式,他們的實際行為並不是在其函式內部對key所對應的資料進行新增或者修改,而是返回根據該key查詢到的tvalue指標,由外部的使用者來進行實際的替換操作。當找不到對應的key時,這幾個api最終都會呼叫內部的newkey函式分配乙個新的key來返回。

(ltable.c)

/*** inserts a new key into a hash table; first, check whether key's main

** position is free. if not, check whether colliding node is in its main

** position or not: if it is not, move colliding node to an empty place and

** put new key in its main position; otherwise (colliding node is in its main

** position), new key goes to an empty position.

*/tvalue *luah_newkey (lua_state *l, table *t, const tvalue *key)

else if (luai_numisnan(fltvalue(key)))

luag_runerror(l, "table index is nan");

} mp = mainposition(t, key);

if (!ttisnil(gval(mp)) || isdummy(t))

lua_assert(!isdummy(t));

othern = mainposition(t, gkey(mp));

if (othern != mp)

setnilvalue(gval(mp));

}else

} setnodekey(l, &mp->i_key, key);

luac_barrierback(l, t, key);

lua_assert(ttisnil(gval(mp)));

return gval(mp);

}

流程如下:

(1)根據可以來查詢其所在雜湊桶的mainposition,如果返回的結果中,該node的值為nil,那麼直接將key賦值並且返回node的tvalue指標就可以了

(2)否則說明該mainposition上已經有其他資料了,需要重新分配空間給這個新的key,然後將這個新的node串聯到對應的雜湊桶上。

可見,整個過程都是在雜湊桶部分進行的,理由是即使key是個數字,也已經在呼叫newkey函式之前進行了查詢,結果卻沒有找到,所以這個key都會進入雜湊同部分來查詢。

以上操作設計重新對錶空間進行分配的情況:

/*

** nums[i] = number of keys 'k' where 2^(i - 1) < k <= 2^i

*/static void rehash (lua_state *l, table *t, const tvalue *ek)

(1)分配乙個點陣圖nums,將其中的所有位置0.這個沃土額意義在於:nums陣列中第i個元素存放的是key在2的(i-1)次冪和2的i次冪之間的元素數量

(2)遍歷lua表中的陣列部分,計算其中的元素數量,更新對應的nums陣列中的元素數量(numusearray函式)

(3)遍歷lua表中的雜湊桶部分,以為其中也可能存放了正整數,需要根據這裡的正整數數量更新對應的nums陣列元素數量(numusehash函式)

(4)此時nums陣列已經有了當前這個table中所有正整數的分配統計,逐個遍歷nums陣列,獲得其範圍區間內所有包含的整數數量大於50%的最大索引,作為重新雜湊後的陣列大小,超過這個範圍的正整數,就分配到雜湊桶部分了(computesizes函式)

(5)根據上面計算得到的調整後的陣列和雜湊桶大小調整表(resize函式)

從以上的過程可以看出,乙個整數key在同乙個表中不同的階段可能被分配到陣列或者雜湊桶部分。

lua的設計思想是,簡單高效,並且還要盡量節省記憶體資源。

效能優化:當有很多很小的表需要建立,可以預先填充避免重新雜湊操作。

由於表的結構包含陣列和雜湊桶部分,其迭代偽**如下:

在陣列部分查詢資料:

查詢成功,則返回該key的下乙個資料

否則在雜湊桶部分查詢資料:

查詢成功,則返回該key的下乙個資料

lua中可以用#對錶進行取長度操作

print(#) -- 輸出2
當表中沒陣列而存在雜湊桶時,也是針對其中鍵值為正整數的部分進行取長度操作。

print(#) -- 輸出2

print(#) -- 輸出4

當表中混合了兩種風格的資料,那麼優先取陣列部分的長度

print(#) -- 輸出3
偽**如下:

如果表存在陣列部分:

在陣列部分二分查詢返回位置i,其中i是滿足條件t[i] != nil 且 t[i+1] = nil的最大資料

否則前面的陣列部分查不到滿足條件的資料,進入雜湊部分查詢:

在雜湊桶部分二分查詢返回位置i,其中i是滿足條件t[i] != nil 且 t[i+1] = nil的最大資料

Lua設計與實現筆記

lua虛擬機器工作流程 lua 是通過翻譯成lua虛擬機器能識別的位元組碼執行的,分兩部分 翻譯 以及編譯為位元組碼部分 將lua 進行詞法分析 語法分析,最終生成位元組碼。lua只有字串和表兩種最基本的資料結構。lua同時採用了兩種方式來做到資料統一 具體型別中有commonheader,用來存放...

Lua陣列與表

table是 lua中的一種資料結構,既可以當做陣列 array 使用,又可以當做字典 類似 j a中的hashmap 來使用 在 lua的8中資料結構中,可以作為 table鍵的有 number,string,boolean,function,table,thread.不能作為 table鍵的有 ...

lua中實現繼承

什麼是元表 元表像是乙個 操作指南 裡面包含了一系列操作的解決方案,例如 index方法就是定義了這個表在索引失敗的情況下該怎麼辦。index元方法 很多人對此都有誤解,這個誤解是 如果a的元表是b,那麼如果訪問了乙個a中不存在的成員,就會訪問查詢b中有沒有這個成員。而這個理解是完全錯誤的,實際上,...