Lua 流程控制
Lua提供了完整的流程控制结构,包括条件分支、循环和跳转语句。Lua的流程控制结构都使用显式的结束标记(如end、until),这使得代码块非常清晰。
一、条件语句
1. if 语句
基本形式
lua
-- 1. 简单if语句
if condition then
-- 当condition为真时执行
end
-- 示例
local score = 85
if score >= 60 then
print("及格")
end
if-else 语句
lua
if condition then
-- condition为真时执行
else
-- condition为假时执行
end
-- 示例
local hour = 14
if hour < 12 then
print("上午好")
else
print("下午好")
end
if-elseif-else 语句
lua
if condition1 then
-- condition1为真时执行
elseif condition2 then
-- condition2为真时执行
elseif condition3 then
-- condition3为真时执行
else
-- 所有条件都为假时执行
end
-- 示例:成绩等级判断
local score = 78
if score >= 90 then
print("优秀")
elseif score >= 80 then
print("良好")
elseif score >= 70 then
print("中等")
elseif score >= 60 then
print("及格")
else
print("不及格")
end
2. 条件表达式详解
Lua中为假的值
lua
-- 重要:Lua中只有false和nil为假,其他所有值都为真
local values = {false, nil, true, 0, 1, "", "hello", {}, function() end}
for i = 1, #values do
if values[i] then
print("值" .. i .. "为真")
else
print("值" .. i .. "为假")
end
end
-- 输出:
-- 值1为假 (false)
-- 值2为假 (nil)
-- 值3为真 (true)
-- 值4为真 (0)
-- 值5为真 (1)
-- 值6为真 ("")
-- 值7为真 ("hello")
-- 值8为真 (table)
-- 值9为真 (function)
复杂条件表达式
lua
-- 使用逻辑运算符组合条件
local age = 25
local hasLicense = true
local hasCar = false
if age >= 18 and hasLicense then
print("可以开车")
end
if age >= 18 and hasLicense and not hasCar then
print("需要租车")
end
-- 使用括号明确优先级
local x, y, z = 10, 20, 30
if (x > 5 and y < 25) or z == 30 then
print("条件成立")
end
3. if语句的嵌套
lua
-- 多层嵌套if语句
function check_access(age, membership, vip)
if age >= 18 then
if membership then
print("会员可以进入")
else
if vip then
print("VIP可以进入")
else
print("需要购买会员")
end
end
else
print("未满18岁不能进入")
end
end
check_access(20, false, true) -- 输出: VIP可以进入
check_access(16, true, false) -- 输出: 未满18岁不能进入
4. if语句的变体写法
lua
-- 1. 单行if语句(仅适用于简单语句)
if condition then print("条件成立") end
-- 2. 使用and/or模拟三元运算符
local max = (a > b) and a or b
-- 注意:当a为false或nil时,这种方法会失效
local a, b = false, true
local result = (a and b) or "default"
print(result) -- 输出: default (但预期可能是false)
-- 安全的模拟三元运算符函数
function ternary(condition, true_val, false_val)
if condition then
return true_val
else
return false_val
end
end
local result2 = ternary(a, b, "default")
print(result2) -- 输出: default (正确)
三、循环语句
1. while 循环
基本语法
lua
while condition do
-- 循环体
-- 注意:需要在循环体内改变condition,否则可能陷入死循环
end
-- 示例:计算1到10的和
local i = 1
local sum = 0
while i <= 10 do
sum = sum + i
i = i + 1 -- 不要忘记更新循环条件!
end
print("1到10的和为:", sum) -- 输出: 55
while循环的注意事项
lua
-- 1. 避免死循环
local count = 0
while true do -- 条件永远为真
count = count + 1
if count > 1000 then
print("循环了1000次")
break -- 使用break退出
end
end
-- 2. 使用标志变量控制循环
local running = true
local counter = 0
while running do
counter = counter + 1
print("循环次数:", counter)
if counter >= 5 then
running = false -- 改变标志变量
end
end
2. repeat-until 循环
基本语法
lua
repeat
-- 循环体(至少执行一次)
until condition -- 当condition为真时退出循环
-- 注意:until后面的条件为真时退出,与while相反
repeat-until示例
lua
-- 示例1:至少执行一次
local i = 1
repeat
print("循环次数:", i)
i = i + 1
until i > 5
-- 输出: 循环次数: 1, 2, 3, 4, 5
-- 示例2:用户输入验证
local answer
repeat
print("请输入yes或no:")
answer = io.read() -- 读取用户输入
until answer == "yes" or answer == "no"
print("你选择了:", answer)
-- 示例3:与while循环对比
-- while循环:先判断后执行
local x = 10
while x < 5 do
print("while循环不会执行")
end
-- repeat-until循环:先执行后判断
local y = 10
repeat
print("repeat-until循环会执行一次")
until y > 5
repeat-until的特殊用途
lua
-- 模拟do-while行为(其他语言中的后测试循环)
function find_first_negative(numbers)
local i = 1
local found = false
local value
repeat
value = numbers[i]
if value and value < 0 then
found = true
else
i = i + 1
end
until found or i > #numbers
return found, value, i
end
local nums = {1, 2, -3, 4, 5}
local found, value, index = find_first_negative(nums)
if found then
print("找到负数:", value, "在位置", index)
end
3. for 循环
数值for循环
lua
-- 基本语法
for var = start, stop, step do
-- 循环体
-- var从start开始,每次增加step,直到超过stop
end
-- 参数说明:
-- var: 循环变量(局部变量,只在循环体内有效)
-- start: 起始值
-- stop: 结束值(包含)
-- step: 步长(可选,默认为1)
数值for循环示例
lua
-- 示例1:基本用法
for i = 1, 5 do -- 步长默认为1
print(i)
end
-- 输出: 1, 2, 3, 4, 5
-- 示例2:指定步长
for i = 1, 10, 2 do -- 步长为2
print(i)
end
-- 输出: 1, 3, 5, 7, 9
-- 示例3:递减循环
for i = 10, 1, -1 do -- 步长为-1
print(i)
end
-- 输出: 10, 9, 8, 7, 6, 5, 4, 3, 2, 1
-- 示例4:小数步长
for i = 0, 1, 0.2 do
print(string.format("%.1f", i))
end
-- 输出: 0.0, 0.2, 0.4, 0.6, 0.8, 1.0
数值for循环的细节
lua
-- 1. 循环变量是局部变量
for i = 1, 3 do
print("循环内 i =", i)
end
-- print(i) -- 这里会报错或输出nil,因为i在循环外不可访问
-- 2. 循环参数在循环开始前一次性求值
local a, b, c = 1, 5, 1
for i = a, b, c do
print(i)
b = 3 -- 修改b不会影响循环次数
end
-- 输出: 1, 2, 3, 4, 5 (仍然是5次)
-- 3. 不要在循环内修改循环变量(会报错)
for i = 1, 5 do
print(i)
i = 10 -- 报错,attempt to assign to const variable 'i'
end
-- 输出: 1, 2, 3, 4, 5
-- 4. 循环变量类型必须是数值
for i = 1, "5" do -- 字符串会自动转换为数字
print(i)
end
-- 输出: 1, 2, 3, 4, 5
泛型for循环
lua
-- 基本语法
for var_list in iterator_function, state, initial_value do
-- 循环体
end
-- 常用迭代器:ipairs, pairs
使用ipairs遍历数组
lua
-- ipairs:按顺序遍历数组部分(从1开始到第一个nil为止)
local fruits = {"apple", "banana", "cherry", "date"}
for i, fruit in ipairs(fruits) do
print(i, fruit)
end
-- 输出:
-- 1 apple
-- 2 banana
-- 3 cherry
-- 4 date
-- ipairs不会遍历非数组部分
local mixed = {1, 2, 3, x = 10, y = 20}
for i, v in ipairs(mixed) do
print(i, v)
end
-- 输出:
-- 1 1
-- 2 2
-- 3 3
-- 不会输出x=10, y=20
使用pairs遍历表
lua
-- pairs:遍历表的所有键值对(顺序不确定)
local person = {
name = "张三",
age = 25,
city = "北京",
[1] = "first",
[2] = "second"
}
for key, value in pairs(person) do
print(key, value)
end
-- 输出顺序可能不同,但会包含所有键值对
-- 注意:pairs遍历顺序不确定
local t = {a = 1, b = 2, c = 3}
for k, v in pairs(t) do
print(k, v)
end
-- 输出顺序可能是任意的,如: b 2, a 1, c 3
自定义迭代器
lua
-- 1. 无状态迭代器
function squares(max)
local i = 0
return function()
i = i + 1
if i <= max then
return i, i * i
end
end
end
for num, square in squares(5) do
print(num, "的平方是", square)
end
-- 输出:
-- 1 的平方是 1
-- 2 的平方是 4
-- 3 的平方是 9
-- 4 的平方是 16
-- 5 的平方是 25
-- 2. 有状态迭代器
function range_iterator(max, current)
current = current + 1
if current <= max then
return current
end
end
function range(max)
return range_iterator, max, 0 -- 迭代器,状态,初始值
end
for i in range(5) do
print(i)
end
-- 输出: 1, 2, 3, 4, 5
-- 3. 使用闭包的迭代器
function make_counter(start, stop, step)
local current = start - step
return function()
current = current + step
if step > 0 and current <= stop then
return current
elseif step < 0 and current >= stop then
return current
end
end
end
for num in make_counter(10, 1, -1) do
print(num)
end
-- 输出: 10, 9, 8, 7, 6, 5, 4, 3, 2, 1
泛型for循环的等价形式
lua
-- 泛型for循环的实际执行过程
-- for var1, var2, ... in iterator do
-- block
-- end
-- 等价于:
do
local _iter, _state, _var = iterator
while true do
local var1, var2, ... = _iter(_state, _var)
_var = var1
if _var == nil then break end
-- block
end
end
四、跳转语句
1. break 语句
lua
-- break:跳出当前循环(while、repeat、for)
-- 注意:只能跳出最内层的循环
-- 示例1:在while循环中使用break
local i = 1
while true do -- 无限循环
print(i)
i = i + 1
if i > 5 then
break -- 跳出循环
end
end
-- 示例2:在for循环中使用break
for i = 1, 10 do
print(i)
if i == 5 then
break -- 跳出循环
end
end
-- 输出: 1, 2, 3, 4, 5
-- 示例3:在repeat循环中使用break
local j = 1
repeat
print(j)
j = j + 1
if j > 3 then
break
end
until false -- 条件永远为假,但break会跳出循环
-- 输出: 1, 2, 3
-- 示例4:break只能跳出最内层循环
for i = 1, 3 do
for j = 1, 3 do
print(i, j)
if j == 2 then
break -- 只跳出内层循环
end
end
end
-- 输出:
-- 1 1
-- 1 2
-- 2 1
-- 2 2
-- 3 1
-- 3 2
2. return 语句
lua
-- return:从函数中返回,也可以从代码块中提前返回
-- 注意:如果return不是代码块的最后一个语句,需要用do-end包裹
-- 示例1:从函数中返回
function max(a, b)
if a > b then
return a
else
return b
end
end
print("最大值:", max(10, 20)) -- 20
-- 示例2:提前返回
function process_number(n)
-- 参数检查
if type(n) ~= "number" then
return nil, "参数必须是数字"
end
if n < 0 then
return nil, "参数不能为负数"
end
-- 正常处理
return math.sqrt(n)
end
local result, err = process_number(-4)
if err then
print("错误:", err) -- 输出: 参数不能为负数
end
-- 示例3:在代码块中使用return
function find_value(tbl, target)
for k, v in pairs(tbl) do
if v == target then
return k -- 找到后立即返回
end
end
return nil -- 没找到
end
local colors = {red = 1, green = 2, blue = 3}
print("green的键:", find_value(colors, 2)) -- green
-- 示例4:返回多个值
function get_coordinates()
return 10, 20, 30
end
local x, y, z = get_coordinates()
print(x, y, z) -- 10 20 30
3. goto 语句(Lua 5.2+)
lua
-- goto:跳转到标签处
-- 注意:goto在Lua中较少使用,应谨慎使用
-- 基本语法
::label_name:: -- 定义标签
-- 代码
goto label_name -- 跳转到标签
-- 示例1:跳出多层循环
for i = 1, 3 do
for j = 1, 3 do
print(i, j)
if i == 2 and j == 2 then
goto break_all -- 跳出所有循环
end
end
end
::break_all::
print("跳出所有循环")
-- 示例2:实现continue功能(Lua没有continue语句)
for i = 1, 5 do
if i == 3 then
goto continue -- 跳过本次循环
end
print(i)
::continue::
end
-- 输出: 1, 2, 4, 5 (跳过了3)
-- 示例3:错误处理跳转
local success, result = pcall(function()
error("测试错误")
end)
if not success then
goto error_handler
end
print("正常执行")
goto finish
::error_handler::
print("发生错误:", result)
::finish::
print("程序结束")
-- goto的限制:
-- 1. 不能跳转到块外(除非跳出的是语句块)
-- 2. 不能跳入函数内部
-- 3. 不能跳入局部变量的作用域内
4. 模拟continue语句
lua
-- Lua没有continue语句,但可以模拟
-- 方法1:使用if语句包裹
for i = 1, 5 do
if i ~= 3 then -- 模拟continue
print(i)
end
end
-- 方法2:使用goto(Lua 5.2+)
for i = 1, 5 do
if i == 3 then goto continue end
print(i)
::continue::
end
-- 方法3:使用repeat循环模拟
for i = 1, 5 do
repeat
if i == 3 then break end -- break跳出repeat,相当于continue
print(i)
until true
end
-- 方法4:使用函数封装
function process_number(n)
if n == 3 then return end -- 相当于continue
print(n)
end
for i = 1, 5 do
process_number(i)
end
五、嵌套和组合使用
1. 循环嵌套
lua
-- 示例1:乘法表
for i = 1, 9 do
for j = 1, i do
io.write(j .. "×" .. i .. "=" .. (i*j) .. "\t")
end
print()
end
-- 示例2:二维数组遍历
local matrix = {
{1, 2, 3},
{4, 5, 6},
{7, 8, 9}
}
for i, row in ipairs(matrix) do
for j, value in ipairs(row) do
io.write(value .. " ")
end
print()
end
-- 示例3:使用不同循环类型嵌套
local i = 1
while i <= 3 do
for j = 1, 3 do
print("外层while i=" .. i .. ", 内层for j=" .. j)
end
i = i + 1
end
2. 条件语句与循环的组合
lua
-- 示例1:查找第一个满足条件的元素
local numbers = {2, 4, 6, 7, 8, 10}
local found = nil
for i, num in ipairs(numbers) do
if num % 2 ~= 0 then -- 找到第一个奇数
found = {index = i, value = num}
break
end
end
if found then
print("找到奇数:", found.value, "在位置", found.index)
else
print("没有找到奇数")
end
-- 示例2:过滤数组
local scores = {85, 92, 78, 45, 96, 58, 72}
local passed = {}
for i, score in ipairs(scores) do
if score >= 60 then
table.insert(passed, score)
end
end
print("及格人数:", #passed)
print("及格分数:", table.concat(passed, ", "))
-- 示例3:复杂条件控制循环
local should_continue = true
local attempt = 1
local max_attempts = 5
while should_continue and attempt <= max_attempts do
print("尝试", attempt)
-- 模拟一些处理
local success = (attempt == 3) -- 假设第3次成功
if success then
print("成功!")
should_continue = false
else
print("失败,继续尝试...")
attempt = attempt + 1
end
end
if attempt > max_attempts then
print("达到最大尝试次数")
end
六、特殊流程控制模式
1. 状态机模式
lua
-- 使用循环和switch模拟状态机
local state = "IDLE"
while state ~= "EXIT" do
if state == "IDLE" then
print("空闲状态")
-- 处理事件
local event = "START" -- 模拟事件
if event == "START" then
state = "RUNNING"
end
elseif state == "RUNNING" then
print("运行状态")
-- 处理事件
local event = "PAUSE" -- 模拟事件
if event == "PAUSE" then
state = "PAUSED"
elseif event == "STOP" then
state = "STOPPED"
end
elseif state == "PAUSED" then
print("暂停状态")
-- 处理事件
local event = "RESUME" -- 模拟事件
if event == "RESUME" then
state = "RUNNING"
elseif event == "STOP" then
state = "STOPPED"
end
elseif state == "STOPPED" then
print("停止状态")
state = "EXIT"
end
end
print("状态机退出")
2. 使用表实现switch-case
lua
-- Lua没有switch语句,但可以用表模拟
function process_command(cmd)
local handlers = {
["start"] = function()
print("启动服务")
end,
["stop"] = function()
print("停止服务")
end,
["restart"] = function()
print("重启服务")
end,
["status"] = function()
print("查看状态")
end
}
local handler = handlers[cmd]
if handler then
handler()
else
print("未知命令:", cmd)
end
end
process_command("start") -- 启动服务
process_command("pause") -- 未知命令: pause
3. 异常处理模式
lua
-- 使用pcall进行异常处理
function risky_operation()
local random = math.random(1, 10)
if random > 8 then
error("随机错误发生")
end
return "操作成功"
end
-- 重试机制
local max_retries = 3
local retry_count = 0
local success, result
repeat
retry_count = retry_count + 1
print("尝试第", retry_count, "次")
success, result = pcall(risky_operation)
if not success then
print("错误:", result)
if retry_count >= max_retries then
print("达到最大重试次数")
break
end
print("等待1秒后重试...")
-- os.execute("sleep 1") -- 在实际中可能需要
end
until success or retry_count >= max_retries
if success then
print("最终结果:", result)
end
4. 协程中的流程控制
lua
-- 使用协程实现协作式多任务
function task(name, count)
for i = 1, count do
print(name, "执行步骤", i)
coroutine.yield() -- 让出执行权
end
return name .. "完成"
end
-- 创建协程
local co1 = coroutine.create(function() return task("任务A", 3) end)
local co2 = coroutine.create(function() return task("任务B", 4) end)
-- 交替执行协程
local status1, result1
local status2, result2
repeat
if coroutine.status(co1) ~= "dead" then
status1, result1 = coroutine.resume(co1)
end
if coroutine.status(co2) ~= "dead" then
status2, result2 = coroutine.resume(co2)
end
until coroutine.status(co1) == "dead" and coroutine.status(co2) == "dead"
print("任务A结果:", result1)
print("任务B结果:", result2)
七、性能优化和最佳实践
1. 循环优化
lua
-- 1. 减少循环内部的计算
-- 不好:每次循环都计算#arr
for i = 1, #arr do
-- 循环体
end
-- 好:预先计算长度
local len = #arr
for i = 1, len do
-- 循环体
end
-- 2. 避免在循环内创建临时表
-- 不好:每次循环都创建新表
for i = 1, 1000 do
local temp = {}
-- 处理temp
end
-- 好:复用表
local temp = {}
for i = 1, 1000 do
-- 清空并重用temp
for k in pairs(temp) do temp[k] = nil end
-- 处理temp
end
-- 3. 使用正确的迭代器
local arr = {1, 2, 3, 4, 5}
-- 对于数组,ipairs比pairs更快
for i, v in ipairs(arr) do -- 推荐
print(i, v)
end
for i, v in pairs(arr) do -- 不推荐
print(i, v)
end
2. 条件语句优化
lua
-- 1. 将最可能成立的条件放在前面
-- 假设90%的情况下x > 0
if x > 0 then -- 先检查最常见情况
-- 处理正数
elseif x == 0 then
-- 处理零
else
-- 处理负数
end
-- 2. 避免深层嵌套
-- 不好:深层嵌套
if condition1 then
if condition2 then
if condition3 then
-- 核心逻辑
end
end
end
-- 好:提前返回
if not condition1 then return end
if not condition2 then return end
if not condition3 then return end
-- 核心逻辑
-- 3. 使用局部变量存储频繁访问的值
local value = some_table.some_key
if value and value > 0 then
-- 多次使用value
print(value * 2)
print(value + 10)
end
3. 错误处理模式
lua
-- 1. 防御性编程
function safe_divide(a, b)
-- 类型检查
if type(a) ~= "number" or type(b) ~= "number" then
return nil, "参数必须是数字"
end
-- 边界检查
if b == 0 then
return nil, "除数不能为零"
end
-- 正常处理
return a / b
end
-- 2. 使用assert进行调试
function connect_database(host, port)
-- 仅在调试模式检查
assert(type(host) == "string", "host必须是字符串")
assert(type(port) == "number", "port必须是数字")
assert(port > 0 and port <= 65535, "端口号无效")
-- 连接逻辑
end
-- 3. 资源清理模式
local resource = acquire_resource()
local success, err = pcall(function()
-- 使用资源
use_resource(resource)
end)
-- 确保资源被释放
release_resource(resource)
if not success then
-- 处理错误
print("错误:", err)
end
八、常见陷阱和注意事项
1. 无限循环
lua
-- 陷阱1:忘记更新循环变量
local i = 1
while i <= 10 do
print(i)
-- 忘记 i = i + 1
end -- 无限循环!
-- 陷阱2:条件永远为真
while true do
-- 没有break语句
end
-- 陷阱3:浮点数比较
local x = 0
while x ~= 1.0 do -- 危险!浮点数可能永远不会精确等于1.0
x = x + 0.1
print(x)
if x > 2 then break end -- 安全措施
end
2. 作用域问题
lua
-- 问题1:循环变量泄漏(数值for循环不会)
for i = 1, 3 do
print(i)
end
-- print(i) -- 这里会报错或输出nil
-- 问题2:使用pairs时的修改
local t = {a = 1, b = 2, c = 3}
for k, v in pairs(t) do
if k == "b" then
t[k] = nil -- 删除当前键可能有问题
end
end
-- 问题3:在循环内创建函数
local funcs = {}
for i = 1, 3 do
funcs[i] = function()
return i -- 所有函数都引用同一个i(最终值)
end
end
-- 修正:使用闭包
for i = 1, 3 do
funcs[i] = (function(j)
return function()
return j -- 每个函数有自己的j
end
end)(i)
end
3. 性能问题
lua
-- 问题1:在循环中连接字符串
local result = ""
for i = 1, 1000 do
result = result .. i -- 每次连接都创建新字符串
end
-- 修正:使用table.concat
local parts = {}
for i = 1, 1000 do
parts[i] = tostring(i)
end
local result = table.concat(parts)
-- 问题2:不必要的函数调用
for i = 1, 1000 do
local len = #some_table -- 如果some_table不变,应该在循环外计算
-- 使用len
end
-- 修正:预先计算
local len = #some_table
for i = 1, 1000 do
-- 使用len
end
九、总结
Lua的流程控制结构虽然简洁,但功能完整。掌握这些结构的关键点包括:
- 理解真假值 :只有
false和nil为假,其他所有值都为真 - 选择合适的循环 :
- 已知循环次数 → 使用
for循环 - 未知循环次数但至少执行一次 → 使用
repeat-until - 未知循环次数且可能不执行 → 使用
while
- 已知循环次数 → 使用
- 合理使用跳转语句 :
break用于跳出当前循环return用于从函数返回goto谨慎使用,用于复杂控制流
- 注意性能优化 :
- 避免在循环内进行不必要的计算
- 使用正确的迭代器(
ipairs用于数组,pairs用于表) - 字符串连接使用
table.concat
- 错误处理 :
- 使用
pcall进行保护调用 - 使用
assert进行调试检查 - 实现适当的重试机制
- 使用
通过熟练掌握这些流程控制结构,可以编写出清晰、高效、健壮的Lua代码。在实际开发中,应根据具体需求选择合适的控制结构,并注意避免常见的陷阱和性能问题。