Lua 的流程控制结构设计简洁而高效,主要包括 if 分支语句和 while、for、repeat 三种循环语句。
🔀 分支语句 (if-then-else)
Lua 使用 if...then...elseif...else...end 结构来处理条件分支。与许多其他语言不同,Lua 没有 switch 语句,但通过链式的 elseif 可以实现同样甚至更灵活的功能。
核心要点
- 真假判断 :在 Lua 中,只有
false和nil被视为"假" 。所有其他值,包括数字0、空字符串"",在条件判断中都被视为"真"。 - 语法结构 :每个
if、elseif或else块都必须以then开头,并以end结尾。
语法与示例
local score = 85
if score >= 90 then
print("优秀")
elseif score >= 80 then
print("良好")
elseif score >= 60 then
print("及格")
else
print("不及格")
end
-- 输出: 良好
嵌套分支
if 语句允许嵌套,即在一个分支块内部再包含另一个完整的 if 判断,用于处理更复杂的逻辑。
local age = 20
local hasLicense = true
if age >= 18 then
if hasLicense then
print("可以开车")
else
print("已成年,但没有驾照")
end
else
print("未成年,不能开车")
end
🔄 循环语句
Lua 提供了三种循环结构,适用于不同的场景。
1. while 循环
while 循环在条件为 true 时重复执行代码块。它在每次循环开始前检查条件,如果条件一开始就不满足,循环体可能一次也不会执行。
while 条件 do
-- 循环体
end
local i = 1
while i <= 3 do
print("while 循环: " .. i)
i = i + 1
end
-- 输出:
-- while 循环: 1
-- while 循环: 2
-- while 循环: 3
2. repeat...until 循环
repeat...until 循环与 while 类似,但条件检查在循环体的末尾 。这意味着循环体至少会执行一次,然后才判断是否继续。
语法
repeat
-- 循环体
until 条件
示例
local j = 1
repeat
print("repeat 循环: " .. j)
j = j + 1
until j > 3
-- 输出:
-- repeat 循环: 1
-- repeat 循环: 2
-- repeat 循环: 3
3. for 循环
for 循环是 Lua 中最常用、最强大的循环结构,它分为两种类型:数值型和泛型。
数值型 for 循环
用于执行固定次数的循环,语法非常简洁。
语法
for 变量 = 起始值, 结束值 [, 步长] do
-- 循环体
end
步长是可选的,默认为 1。步长可以是负数,用于递减循环。
示例
-- 递增循环,步长为 2
for i = 1, 5, 2 do
print("i =", i)
end
-- 输出: i = 1, i = 3, i = 5
-- 递减循环
for k = 3, 1, -1 do
print("k =", k)
end
-- 输出: k = 3, k = 2, k = 1
泛型 for 循环
用于遍历集合(主要是 table),通过迭代器函数来实现。
语法
for 索引, 值 in 迭代器函数(表) do
-- 循环体
end
常用迭代器
ipairs(table): 用于遍历数组部分。它从索引 1 开始,按数字顺序遍历,直到遇到第一个nil值为止。pairs(table): 用于遍历表中所有的键值对,包括非数字索引的键。遍历顺序是不确定的。
示例
local fruits = {"apple", "banana", "orange"}
-- 使用 ipairs 遍历数组
for index, value in ipairs(fruits) do
print(index, value)
end
-- 输出:
-- 1 apple
-- 2 banana
-- 3 orange
local user = {name = "Bob", age = 30, city = "Beijing"}
-- 使用 pairs 遍历所有键值对
for key, value in pairs(user) do
print(key, value)
end
-- 输出 (顺序可能不同):
-- name Bob
-- age 30
-- city Beijing
break语句:退出循环这是最常用的跳转语句。它的作用是立即终止当前所在的循环 (
for、while或repeat),并跳出循环体,继续执行循环后面的代码。
作用范围:只能用在循环体内。
嵌套循环 :在多层嵌套循环中,
break只能跳出最内层的循环。for i = 1, 10 do
if i == 5 then
print("找到 5 了,提前结束循环!")
break -- 循环在这里直接终止
end
print("当前数字:", i)
end
print("循环已结束,程序继续向下执行")
🚀
goto语句:无条件跳转
goto语句允许你将程序的执行流程无条件地跳转到代码中指定的某个位置(标签)。注意 :
goto是在 Lua 5.2 版本才正式加入的。虽然它功能强大,但滥用会导致代码逻辑混乱(被称为"面条代码"),因此建议谨慎使用。1. 语法
- 跳转 :
goto label- 定义标签 :
::label::(使用双冒号包裹标签名)2. 常见用途:模拟
continueLua 没有像 C/Java 那样的
continue语句(跳过本次循环剩余代码,直接进入下一次循环)。我们可以用goto完美实现这个功能。示例:跳过奇数,只打印偶数
for i = 1, 10 do if i % 2 == 1 then goto continue -- 如果是奇数,跳转到 continue 标签 end print("偶数:", i) -- 只有偶数会执行到这里 ::continue:: -- 定义标签,作为循环的下一次迭代起点 end3. 严格限制(作用域规则)
为了防止破坏变量作用域,Lua 对
goto做了严格限制:
- 不能跳入局部变量的作用域内:你不能从一个变量定义的范围外,跳转到它的范围里(因为那样会导致变量未初始化)。
- 不能跨函数跳转:不能跳进或跳出函数。
错误示例(会报错):
goto skip local x = 10 -- 错误:跳过了 x 的定义 ::skip:: print(x)
在 Lua 中,table(表)是唯一的数据结构,它功能极其强大,可以用来实现数组、字典、集合、对象等几乎所有数据结构。

以下是 table 的详细解析及增删改查的实战案例。
1. Table 的核心特性
- 索引从 1 开始 :与 C/C++/Java/Python 等大多数语言不同,Lua 的数组部分默认索引是 1,而不是 0。
- 动态扩容:Table 的大小不固定,可以随着数据的加入自动增长。
- 混合模式:一个 table 可以同时包含数组部分(数字索引)和字典部分(字符串或其他类型键)。
- 引用类型:Table 是引用类型。当你把一个 table 赋值给另一个变量时,传递的是内存地址(引用),而不是拷贝。
2. 增删改查实战案例
我们将通过代码示例来展示如何操作一个"学生列表"。
📝 新增数据 (Create/Insert)
有两种主要方式:直接赋值和 table.insert。
-- 1. 创建一个空表
local students = {}
-- 2. 直接赋值(适用于字典模式或指定索引)
students["name"] = "Alice" -- 字典模式:键为 "name"
students.class = "Class 1" -- 语法糖,等同于 students["class"] = "Class 1"
students[1] = "Bob" -- 数组模式:索引为 1
-- 3. 使用 table.insert(专用于数组模式,自动维护索引)
table.insert(students, "Charlie") -- 在末尾追加,索引自动变为 2
table.insert(students, 1, "David") -- 在索引 1 处插入,原元素后移
-- 此时 students 结构大致为:
-- 数组部分: [1]="David", [2]="Bob", [3]="Charlie"
-- 字典部分: ["name"]="Alice", ["class"]="Class 1"
🔄 修改数据 (Update)
修改非常简单,直接对已存在的键进行赋值即可。
-- 修改数组部分
students[1] = "David Smith" -- 将索引 1 的名字修改
-- 修改字典部分
students["name"] = "Alice Wu" -- 修改键 "name" 的值
students.class = "Class 2" -- 使用点语法修改
❌ 删除数据 (Delete)
在 Lua 中,删除一个键值对的标准方法是将其赋值为 nil。
-- 1. 删除字典键
students.class = nil -- 键 "class" 及其值被彻底删除
-- 2. 删除数组元素(注意区别)
students[1] = nil -- 仅仅将该位置设为 nil,索引 1 还在,但变成了 nil
-- 这会导致数组出现"空洞",#students 可能会变小
-- 3. 正确删除数组元素(保持连续性)
-- 使用 table.remove 会移除元素,并将后面的元素向前移动填补空缺
table.remove(students, 1) -- 删除索引 1 的元素,"Bob" 会移动到索引 1
🔍 查询与访问 (Read)
-- 1. 访问数组部分
print(students[1]) -- 输出: Bob (假设 David 已被移除)
-- 2. 访问字典部分
print(students["name"]) -- 输出: Alice Wu
print(students.name) -- 输出: Alice Wu (点语法更常用)
-- 3. 获取长度
print(#students) -- 输出数组部分的长度(遇到第一个 nil 截止)
在 Lua 中,获取"长度"的方法取决于你如何定义你的表(Table)。因为 Lua 的表既可以当作数组 (有序列表),也可以当作字典(键值对映射),甚至混合使用。
简单来说,主要分为两种情况:
- 数组模式(序列) :使用
#操作符。- 字典模式(哈希表) :使用
pairs()遍历计数。1. 获取数组长度(序列)
如果你的表是连续的数字索引(例如
{10, 20, 30}),我们称之为序列(Sequence)。使用
#操作符(推荐)这是最常用、最标准的方法。它返回从索引 1 开始,直到遇到第一个
nil之前的元素个数。
local arr = {"苹果", "香蕉", "橙子"} print(#arr) -- 输出: 32. 获取字典/混合表长度
如果你的表包含非连续的数字索引,或者全是字符串键(例如
{a=1, b=2}),#操作符将失效(通常返回 0 或不可预测的值)。使用
pairs()遍历计数这是获取表中所有键值对数量的唯一准确方法。
local dict = {name = "张三", age = 18, city = "北京"} local count = 0 for _ in pairs(dict) do count = count + 1 end print(count) -- 输出: 3⚠️ 核心陷阱:当表中存在
nil时这是 Lua 新手最容易踩的坑。
#操作符极其依赖数据的连续性。如果你的数组中间出现了"空洞"(即某个位置是
nil),#的结果是未定义且不可靠 的。它可能返回第一个nil之前的长度,也可能返回整个表的最大索引,具体行为取决于 Lua 的内部实现,千万不要依赖这种行为。错误示例:
local bad_arr = {1, 2, nil, 4, 5} print(#bad_arr) -- 结果可能是 2,也可能是 5,甚至其他值。这是未定义行为!解决方案:
- 不要 在数组中间插入
nil。- 如果要删除元素,请使用
table.remove(t, index),它会自动移动后续元素,保持连续性。
3. 遍历 Table
由于 table 可以是数组也可以是字典,Lua 提供了两种遍历方式:
| 函数 | 用途 | 适用场景 |
|---|---|---|
ipairs(t) |
按数字索引顺序遍历 | 数组模式 。从索引 1 开始,直到遇到第一个 nil 停止。 |
pairs(t) |
遍历所有键值对 | 字典模式或混合表。顺序不固定,能遍历所有非 nil 元素。 |
代码示例:
local t = {10, 20, name="Lua", version=5.4}
-- 使用 ipairs 遍历数组部分 (输出 1:10, 2:20)
for i, v in ipairs(t) do
print(i, v)
end
-- 使用 pairs 遍历所有部分 (顺序可能不同)
for k, v in pairs(t) do
print(k, v)
end
4. 进阶:嵌套 Table (模拟对象/结构体)
Table 可以嵌套,非常适合表示复杂数据,比如 JSON 结构。
local user = {
id = 1001,
name = "Jack",
skills = {"Coding", "Gaming"}, -- 数组作为字段
address = { -- 表作为字段
city = "Beijing",
zip = 100000
}
}
-- 访问嵌套数据
print(user.address.city) -- 输出: Beijing
print(user.skills[1]) -- 输出: Coding
💡 总结与注意事项
-
默认索引是 1 :切记不要试图访问
t[0],除非你显式定义了它。 -
nil的破坏力 :在数组中间插入nil会截断数组长度(#t会变小),建议使用table.remove来删除数组元素。 -
引用传递 :lua
local t1 = {1, 2} local t2 = t1 -- t2 指向 t1 的内存地址 t2[1] = 100 print(t1[1]) -- 输出: 100 (t1 也被改变了!)