Lua 语法与数据模型
更新时间:2026-08-29。本文回答:Lua 为什么只有 table 一种复合结构?元表怎么实现"面向对象"?协程怎么用?
一、八种类型,table 统管一切
Lua 只有八种基础类型:nil、boolean、number、string、table、function、userdata、thread。其中 table 是唯一的复合结构,既当数组又当字典:
lua
local arr = { 10, 20, 30 } -- 数组,索引从 1 开始
local map = { cpu = 4, mem = 16 } -- 字典
local mix = { [1] = "a", name = "x" } -- 混合| 维度 | Lua table | 其他语言 |
|---|---|---|
| 数组 | 整数键部分用数组实现 | 独立 array 类型 |
| 字典 | 哈希部分存非整数键 | 独立 map/dict |
| 统一 | 同一结构混合两种 | 通常分开 |
核心结论:table 内部是"数组段 + 哈希段"的混合体——这和本站讲的 内存布局与缓存 思想相通:连续整数键走数组(缓存友好),散键走哈希。
二、元表(metatable):给 table 加行为
元表是"table 的元数据表",通过元方法改变默认行为(如 +、index 访问、比较):
lua
local vec = { x = 1, y = 2 }
local mt = {
__add = function(a, b) return { x = a.x + b.x, y = a.y + b.y } end,
__index = function(t, k) return 0 end, -- 访问不存在的键返回 0
}
setmetatable(vec, mt)__index:读取不存在的键时触发(实现"默认字段/继承")。__add/__mul等:重载运算符。- 面向对象:用
__index指向一个"方法表",就能模拟类和继承。
三、闭包:函数携带环境
lua
function counter()
local n = 0
return function() -- 内层函数"记住"了 n
n = n + 1
return n
end
end
local c = counter()
print(c(), c(), c()) -- 1 2 3闭包让函数携带创建时的局部变量,是 Lua 实现"私有状态""回调""工厂"的核心——和 Python 装饰器闭包 同源。
四、协程:协作式多任务
lua
local co = coroutine.create(function()
for i = 1, 3 do
coroutine.yield(i) -- 让出值并暂停
end
end)
print(coroutine.resume(co)) -- true 1
print(coroutine.resume(co)) -- true 2coroutine.create建协程,resume启动/恢复,yield暂停并返回值。- 协程是单线程协作式:只有主动
yield才让出,没有抢占——和 Go goroutine 的抢占式不同,但都属于"用户态轻量任务"。
五、与本站主线的衔接
| Lua 机制 | 本站对应 | 衔接文档 |
|---|---|---|
| table 数组/哈希混合 | 缓存与内存布局 | 连续内存 vs 哈希 |
元表 __index | C++ 虚函数/继承 | 动态派发同源思想 |
| 闭包 | Python 闭包 | 一等函数 + 环境捕获 |
| 协程 | 调度模型 | 用户态协作式任务 |
六、常见坑
- 索引从 1 开始:
{#t}取数组长度、循环for i=1,#t都基于 1,和 C/Python 的 0 基不同。 #长度只对"序列"可靠:中间有nil空洞的数组,#t结果未定义。- 全局变量污染:不用
local的变量会变成全局,嵌入式场景易冲突,养成local习惯。
一句话总结
Lua 用唯一结构 table(数组+哈希混合)表达一切数据,用元表给 table 加行为、用闭包携带状态、用协程做协作式多任务——极简类型系统背后是极高的表达力。
继续:Lua 与 C 互调 → 游戏脚本与配置场景。