lua流程控制语句和table(表)数据结构

Lua 的流程控制结构设计简洁而高效,主要包括 if 分支语句和 whileforrepeat 三种循环语句。

🔀 分支语句 (if-then-else)

Lua 使用 if...then...elseif...else...end 结构来处理条件分支。与许多其他语言不同,Lua 没有 switch 语句,但通过链式的 elseif 可以实现同样甚至更灵活的功能。

核心要点
  • 真假判断 :在 Lua 中,只有 falsenil 被视为"假" 。所有其他值,包括数字 0、空字符串 "",在条件判断中都被视为"真"。
  • 语法结构 :每个 ifelseifelse 块都必须以 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 语句:退出循环

这是最常用的跳转语句。它的作用是立即终止当前所在的循环forwhilerepeat),并跳出循环体,继续执行循环后面的代码。

  • 作用范围:只能用在循环体内。

  • 嵌套循环 :在多层嵌套循环中,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. 常见用途:模拟 continue

Lua 没有像 C/Java 那样的 continue 语句(跳过本次循环剩余代码,直接进入下一次循环)。我们可以用 goto 完美实现这个功能。

示例:跳过奇数,只打印偶数

复制代码
for i = 1, 10 do
    if i % 2 == 1 then
        goto continue -- 如果是奇数,跳转到 continue 标签
    end
    
    print("偶数:", i) -- 只有偶数会执行到这里
    
    ::continue:: -- 定义标签,作为循环的下一次迭代起点
end
3. 严格限制(作用域规则)

为了防止破坏变量作用域,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 的表既可以当作数组 (有序列表),也可以当作字典(键值对映射),甚至混合使用。

简单来说,主要分为两种情况:

  1. 数组模式(序列) :使用 # 操作符。
  2. 字典模式(哈希表) :使用 pairs() 遍历计数。
1. 获取数组长度(序列)

如果你的表是连续的数字索引(例如 {10, 20, 30}),我们称之为序列(Sequence)

使用 # 操作符(推荐)

这是最常用、最标准的方法。它返回从索引 1 开始,直到遇到第一个 nil 之前的元素个数。

复制代码
local arr = {"苹果", "香蕉", "橙子"}
print(#arr) -- 输出: 3
2. 获取字典/混合表长度

如果你的表包含非连续的数字索引,或者全是字符串键(例如 {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. 默认索引是 1 :切记不要试图访问 t[0],除非你显式定义了它。

  2. nil 的破坏力 :在数组中间插入 nil 会截断数组长度(#t 会变小),建议使用 table.remove 来删除数组元素。

  3. 引用传递 :lua

    复制代码
    local t1 = {1, 2}
    local t2 = t1       -- t2 指向 t1 的内存地址
    t2[1] = 100
    print(t1[1])        -- 输出: 100 (t1 也被改变了!)
相关推荐
逻辑驱动的ken2 小时前
Java高频面试考点场景题20
java·开发语言·深度学习·面试·职场和发展
W.A委员会2 小时前
多行溢出在末尾添加省略号
开发语言·javascript·css
wjs20242 小时前
RSS Item 元素:深入解析与使用指南
开发语言
小郑加油2 小时前
python学习Day11:认识与创建CSV文件
开发语言·python·学习
念何架构之路2 小时前
Go Web基础和Http演进
开发语言·后端·golang
初心未改HD2 小时前
Go语言database/sql与SQLx:构建健壮的数据访问层
开发语言·golang
晚风吹红霞2 小时前
C++异常处理核心知识点全解析
开发语言·c++
CoderCodingNo2 小时前
【信奥业余科普】C++ 的奇妙之旅 | 17:面的铺展与文本的本质——二维数组与字符串
开发语言·c++
J2虾虾2 小时前
Java Lambda 表达式详解文档
java·开发语言