【Lua 筆記】表(table) - part 9
===
目錄(Table of Contents):
[TOC]
---
由於有款遊戲叫做 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(表) | 菜鸟教程](https://www.runoob.com/lua/lua-tables.html)
```lua=
-- 初始化 table
mytable = {}
-- 在 table 中指定值
mytable[1]= "Lua"
-- 移除引用
mytable = nil
-- lua 垃圾回收會自動釋放記憶體
```
(程式語言有個概念是:資料通常都被存放在記憶體當中)
> 當我們為 table a 並設定元素,然後將 a 賦值給 b,則 a 與 b 都指向同一個記憶體位址。如果 a 設定為 nil,則 b 同樣能存取 table 的元素。如果沒有指定的變數指向 a,Lua 的垃圾回收機制會清理相對應的記憶體。
以下是個範例:
```lua=
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)
```
輸出結果:
```
mytable 的型態是 table
mytable 索引為 1 的元素是 Lua
mytable 索引為 wow 的元素是 修改前
alternatetable 索引為 1 的元素是 Lua
alternatetable 索引為 wow 的元素是 修改前
mytable 索引為 wow 的元素是 修改後
alternatetable 是 nil
mytable 索引為 wow 的元素是 修改後
mytable 是 nil
```
以下是筆者自創範例,主要說明最上面的五項(動態性、鍵值對等):
```lua=
-- 空 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 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(表) | 菜鸟教程](https://www.runoob.com/lua/lua-tables.html)
| Number | 方法&用途 |
| -------- | -------- |
| 1 | table.concat (table [, sep [, start [, end]]]):concat 是 concatenate(連鎖、連接)的縮寫。table.concat() 函數列出參數中指定 table 的陣列部分從 start 位置到 end 位置的所有元素,元素間以指定的分隔符號(sep)隔開。 |
| 2 | table.insert (table, [pos,] value):在 table 的陣列部分指定位置(pos)插入值為 value 的一個元素。pos 參數可選(optional),預設為陣列部分結束。 |
| 3 | table.maxn (table):指定 table 中所有正數 key 值中最大的 key 值。如果不存在key值為正數的元素,則回傳 0。(Lua5.2 之後此方法已經不存在了,本文使用了自訂函數實作) |
| 4 | table.remove (table [, pos]):回傳 table 陣列部分位於 pos 位置的元素。其後的元素會被前移。pos 參數可選,預設為 table 長度,即從最後一個元素刪起。 |
| 5 | table.sort (table [, comp]):對給定的 table 進行升序排序。 |
以下是個範例:
```lua=
-- 自訂 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)
```
輸出結果:
```
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 降序排序
---
```lua=
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, ", "))
```
輸出結果:
```
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 的大小。
### 基本操作
---
```lua=
-- 初始化 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 進行排序。
```lua=
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, ", ")
```
### 降序排序
---
```lua=
local fruits = {"apple", "banana", "cherry"}
table.sort(fruits, function(a, b) return a > b end)
```
參考資料
---
[Lua | Tables | Codecademy](https://www.codecademy.com/resources/docs/lua/tables)
[【30天Lua重拾筆記20】基礎3: 複合結構 - table | 又LAG隨性筆記](https://www.lagagain.com/post/30%E5%A4%A9lua%E9%87%8D%E6%8B%BE%E7%AD%86%E8%A8%9820%E8%A4%87%E5%90%88%E7%B5%90%E6%A7%8B-table/)
[Lua table(表) | 菜鸟教程](https://www.runoob.com/lua/lua-tables.html)