## 索引
[Lua 參考手冊-值與類型](http://www.runoob.com/manual/lua53doc/manual.html#2.1) 中這樣寫道:
```text
表是 Lua 中唯一的數據結構,
它可被用于表示普通數組、序列、符號表、集合、記錄、圖、樹等等。
對于記錄,Lua 使用域名作為索引。
語言提供了 a.name 這樣的語法糖來替代 a["name"] 這種寫法以方便記錄這種結構的使用。
```
## 表構建
[Lua 參考手冊-表構建](http://www.runoob.com/manual/lua53doc/manual.html#3.4.9) 已經寫得很詳細了
不過值得注意的是,以下兩種有序表的構建方式是有所不同的:
```lua
local foo1 = {
[1] = 'a',
[2] = 'b',
[3] = 'c',
[4] = 'd',
[5] = 'e',
}
local foo2 = {
'a',
'b',
'c',
'd',
'e',
}
```
不同之處在于,foo1 是哈希表,foo2 才是數組
看 ltable.c 文件的以下函數:
```c
Table *luaH_new (lua_State *L, int narray, int nhash) {
Table *t = luaM_new(L, Table);
luaC_link(L, obj2gco(t), LUA_TTABLE);
t->metatable = NULL;
t->flags = cast_byte(~0);
/* temporary values (kept only if some malloc fails) */
t->array = NULL;
t->sizearray = 0;
t->lsizenode = 0;
t->node = cast(Node *, dummynode);
setarrayvector(L, t, narray);
setnodevector(L, t, nhash);
return t;
}
```
narray 是數組元素個數,nhash 是非數組元素個數
當 foo1 創建時 narray 為 0, nhash 為 5
當 foo2 創建時 narray 為 5, nhash 為 0
narray 的不同會導致取表長度時,運算過程和結果的不同
## 取長度
[Lua 參考手冊-取長度操作符](http://www.runoob.com/manual/lua53doc/manual.html#3.4.7)
table 的取長度算法主要是 ltable.c 的 luaH\_getn 和 unbound\_search 函數:
```c
static int unbound_search (Table *t, unsigned int j) {
unsigned int i = j; /* i is zero or a present index */
j++;
/* find `i' and `j' such that i is present and j is not */
while (!ttisnil(luaH_getnum(t, j))) {
i = j;
j *= 2;
if (j > cast(unsigned int, MAX_INT)) { /* overflow? */
/* table was built with bad purposes: resort to linear search */
i = 1;
while (!ttisnil(luaH_getnum(t, i))) i++;
return i - 1;
}
}
/* now do a binary search between them */
while (j - i > 1) {
unsigned int m = (i+j)/2;
if (ttisnil(luaH_getnum(t, m))) j = m;
else i = m;
}
return i;
}
/*
** Try to find a boundary in table `t'. A `boundary' is an integer index
** such that t[i] is non-nil and t[i+1] is nil (and 0 if t[1] is nil).
*/
int luaH_getn (Table *t) {
unsigned int j = t->sizearray;
if (j > 0 && ttisnil(&t->array[j - 1])) {
/* there is a boundary in the array part: (binary) search for it */
unsigned int i = 0;
while (j - i > 1) {
unsigned int m = (i+j)/2;
if (ttisnil(&t->array[m - 1])) j = m;
else i = m;
}
return i;
}
/* else must find a boundary in hash part */
else if (t->node == dummynode) /* hash part is empty? */
return j; /* that is easy... */
else return unbound_search(t, j);
}
```
其中 t->sizearray 就是由前面提到的 narray 賦值
故以 foo2 為例,`#foo2` 就會直接 return 這個值
但是按照以上的取長度算法,下面這段 lua 腳本代碼的結果就比較特別:
```lua
local foo3 = {
[1] = 'a',
[2] = 'b',
[4] = 'c',
[8] = 'c',
[16] = 'c',
}
local foo4 = {
'a',
'b',
'c',
nil,
'd',
'e',
}
print(#foo3);
print(#foo4);
```
結果顯示 foo3 的長度為 16,foo4 的長度為 6
foo3 的長度結果完全由 unbound\_search 函數運算得出
foo4 的長度結果則由表構建時的 narray 決定
## 有序表的遍歷
值得注意的是,雖然 \#foo4 為 6
但如果執行以下 lua 腳本代碼,只會輸出 nil 之前的元素:
```lua
for i, v in ipairs(foo4) do
print(i, v);
end
```
結果為:
```text
1 a
2 b
3 c
```
## 序列化與反序列化庫
在各種應用中
經常會使用到表的序列化與反序列化
在此簡單介紹下一個好用的庫 Serpent
[Serpent 的 GitHub 主頁](https://github.com/pkulchenko/serpent)
把 serpent.lua 復制黏貼到 Lua51/script 目錄下
然后在 require 其他模塊之前,先鍵入以下代碼:
```lua
serpent = require("serpent");
```
即可隨意使用 serpent.lua 定義的函數
其他詳情請參考 [Serpent 的 GitHub 主頁](https://github.com/pkulchenko/serpent)