【Lua 筆記】表(table) - part 9

由於有款遊戲叫做 CSO(Counter-Strike Online),內建模式創世者模式(Studio)新增使用 Lua 及其遊戲的 API,所以突發奇想製作這個筆記。

這個筆記會在一開始先著重純粹的程式設計自學,在最後的章節才會與 CSO 遊戲 API 進行應用。

表(Table)

Lua 中的表(table)是一種很強很好用的資料結構,主要有以下幾個特點:

  • Dynamic:table 可以動態地新增或刪除項目(item),無需事先宣告 table 的大小。這讓 table 非常靈活,可以用於各種資料結構,如陣列、字典等。
  • Key / Value(鍵值對):table 中的資料是以鍵值對的形式進行儲存的。鍵(key)可以是任意型態的值(除了 nil),包括數字、字串或者 table 本身等。值(value)也可以是任意型態的。
  • Difference:表中可以同時儲存不同型態的值,表示在同一個 table 中可以同時有整數、字串、函數等不同型態的資料。
  • Index:Lua 中的 table 索引(index)可以是整數或者字串,這讓 table 既可以作為陣列使用,也可以作為字典(或稱哈希表、映射)使用。當使用整數作為索引時,表現形式和功能類似於傳統的陣列。
  • Automatically Adjusting Size:當資料被新增到 table 當中時,Lua 會自動調整 table 的大小。

總之 table 要說像是 python 當中的字典,好像也不完全是,因為他可以變來變去的,非常靈活。

table 格式

以下節錄自:Lua table(表) | 菜鸟教程

1
2
3
4
5
6
7
8
9
-- 初始化 table
mytable = {}

-- 在 table 中指定值
mytable[1]= "Lua"

-- 移除引用
mytable = nil
-- lua 垃圾回收會自動釋放記憶體

(程式語言有個概念是:資料通常都被存放在記憶體當中)

當我們為 table a 並設定元素,然後將 a 賦值給 b,則 a 與 b 都指向同一個記憶體位址。如果 a 設定為 nil,則 b 同樣能存取 table 的元素。如果沒有指定的變數指向 a,Lua 的垃圾回收機制會清理相對應的記憶體。

以下是個範例:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
mytable = {}
print("mytable 的型態是 ", type(mytable))

mytable[1]= "Lua"
mytable["wow"] = "修改前"
print("mytable 索引為 1 的元素是 ", mytable[1])
print("mytable 索引為 wow 的元素是 ", mytable["wow"])

-- alternatetable 和 mytable 都是同一個 table
alternatetable = mytable

print("alternatetable 索引為 1 的元素是 ", alternatetable[1])
print("alternatetable 索引為 wow 的元素是 ", alternatetable["wow"])

alternatetable["wow"] = "修改後"

print("mytable 索引為 wow 的元素是 ", mytable["wow"])

-- 釋放變數
alternatetable = nil
print("alternatetable 是 ", alternatetable)

-- mytable 仍然可以被存取
print("mytable 索引為 wow 的元素是 ", mytable["wow"])

mytable = nil
print("mytable 是 ", mytable)

輸出結果:

1
2
3
4
5
6
7
8
9
mytable 的型態是    table
mytable 索引為 1 的元素是 Lua
mytable 索引為 wow 的元素是 修改前
alternatetable 索引為 1 的元素是 Lua
alternatetable 索引為 wow 的元素是 修改前
mytable 索引為 wow 的元素是 修改後
alternatetable 是 nil
mytable 索引為 wow 的元素是 修改後
mytable 是 nil

以下是筆者自創範例,主要說明最上面的五項(動態性、鍵值對等):

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
-- 空 table
local myTable = {}

-- 動態新增 items
myTable[1] = "Lua"
myTable[2] = 42
myTable["key"] = "value"
myTable[3] = function() return "Hello, World!" end

-- 刪除 items
myTable[2] = nil

-- 顯示 table 的內容
for k, v in pairs(myTable) do
print(k, v)
end

-- 使用整數索引,類似於陣列
local arrayTable = {10, 20, 30, 40, 50}
for i = 1, #arrayTable do
print("arrayTable[" .. i .. "] = " .. arrayTable[i])
end

-- 使用字串索引,類似於字典
local dictTable = {
name = "John",
age = 30,
occupation = "Developer"
}
for k, v in pairs(dictTable) do
print(k .. ": " .. v)
end

-- 自動調整大小
table.insert(myTable, "New Item")
print("After inserting a new item:")
for k, v in pairs(myTable) do
print(k, v)
end

輸出結果:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
1   Lua
3 function: 0x874f00
key value
arrayTable[1] = 10
arrayTable[2] = 20
arrayTable[3] = 30
arrayTable[4] = 40
arrayTable[5] = 50
name: John
occupation: Developer
age: 30
After inserting a new item:
1 Lua
3 function: 0x874f00
4 New Item
key value

:::info
之前我們說過,迭代一個陣列值用 ipairs 函數,或是用像是陣列值的 table。

而像字典(或不是字典也可以)這類的 table 則用 pairs。
:::

表(table)Method

表格來源:Lua table(表) | 菜鸟教程

Number方法&用途
1table.concat (table [, sep [, start [, end]]]):concat 是 concatenate(連鎖、連接)的縮寫。table.concat() 函數列出參數中指定 table 的陣列部分從 start 位置到 end 位置的所有元素,元素間以指定的分隔符號(sep)隔開。
2table.insert (table, [pos,] value):在 table 的陣列部分指定位置(pos)插入值為 value 的一個元素。pos 參數可選(optional),預設為陣列部分結束。
3table.maxn (table):指定 table 中所有正數 key 值中最大的 key 值。如果不存在key值為正數的元素,則回傳 0。(Lua5.2 之後此方法已經不存在了,本文使用了自訂函數實作)
4table.remove (table [, pos]):回傳 table 陣列部分位於 pos 位置的元素。其後的元素會被前移。pos 參數可選,預設為 table 長度,即從最後一個元素刪起。
5table.sort (table [, comp]):對給定的 table 進行升序排序。

以下是個範例:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
-- 自訂 table.maxn 函數(Lua 5.2 之後已不存在)
function table.maxn(t)
local max = 0
for k, v in pairs(t) do
if type(k) == "number" and k > max then
max = k
end
end
return max
end

local fruits = {"apple", "banana", "cherry"}

-- table.insert 在指定位置插入元素
table.insert(fruits, 2, "orange")
print("After insert: " .. table.concat(fruits, ", "))

-- table.maxn 找出 table 中最大的正數 key 值
local maxKey = table.maxn(fruits)
print("Max key: " .. maxKey)

-- table.remove 刪除指定位置的元素
local removedFruit = table.remove(fruits, 3)
print("Removed fruit: " .. removedFruit)
print("After remove: " .. table.concat(fruits, ", "))

-- table.sort 對 table 進行排序
table.sort(fruits)
print("After sort: " .. table.concat(fruits, ", "))

-- table.concat 將 table 轉換為字串
local fruitString = table.concat(fruits, ", ")
print("Concatenated string: " .. fruitString)

輸出結果:

1
2
3
4
5
6
After insert: apple, orange, banana, cherry
Max key: 4
Removed fruit: banana
After remove: apple, orange, cherry
After sort: apple, cherry, orange
Concatenated string: apple, cherry, orange

table.sort 降序排序

1
2
3
4
5
local fruits = {"apple", "banana", "cherry"}

-- table.sort 對 table 進行降序排序
table.sort(fruits, function(a, b) return a > b end)
print("After sort (descending): " .. table.concat(fruits, ", "))

輸出結果:

1
After sort (descending): cherry, banana, apple

:::info
table.sort (table [, comp]),[, comp] 為比較函數的地方,在這邊使用一個匿名函數 function(a, b) return a > b end,這個函數會在排序過程中比較兩個元素,且在第一個元素大於第二個元素時回傳 true,從而實現降序(由大到小)排序。

匿名函數(Anonymous functions):沒有名稱的函數,通常架構較為簡略,大多情況能夠提升程式碼效率。補充:在 Python 則是用 lambda 作為匿名函數的關鍵字。
:::

總結

  • Dynamic:可以動態新增或刪除項目,無需事先宣告大小。
  • Key / Value:資料以鍵值對形式儲存,鍵和值可以是任意型態(除了 nil)。
  • Difference:同一個 table 中可以儲存不同型態的值。
  • Index:索引可以是整數或字串,既可作為陣列也可作為字典使用。
  • Automatically Adjusting Size:新增資料時,Lua 會自動調整 table 的大小。

基本操作

1
2
3
4
5
6
7
8
9
-- 初始化 table
mytable = {}

-- 在 table 中指定值
mytable[1] = "Lua"
mytable["key"] = "value"

-- 移除引用
mytable = nil

表(table)方法

  1. table.concat:將 table 的元素連接成字串。
  2. table.insert:在指定位置插入元素。
  3. table.maxn:找出 table 中最大的正數 key 值(Lua 5.2 之後已不存在)。
  4. table.remove:刪除指定位置的元素。
  5. table.sort:對 table 進行排序。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
local fruits = {"apple", "banana", "cherry"}

-- 插入元素
table.insert(fruits, 2, "orange")

-- 找出最大 key 值
local maxKey = table.maxn(fruits)

-- 刪除元素
local removedFruit = table.remove(fruits, 3)

-- 排序
table.sort(fruits)

-- 轉換為字串
local fruitString = table.concat(fruits, ", ")

降序排序

1
2
3
local fruits = {"apple", "banana", "cherry"}

table.sort(fruits, function(a, b) return a > b end)

參考資料

Lua | Tables | Codecademy

【30天Lua重拾筆記20】基礎3: 複合結構 - table | 又LAG隨性筆記

Lua table(表) | 菜鸟教程