强曰为道
与天地相似,故不违。知周乎万物,而道济天下,故不过。旁行而不流,乐天知命,故不忧.
文档目录

Lua 从入门到精通 / 04 - 控制流程 / Control Flow

控制流程 / Control Flow

Lua 的控制结构简洁明了,用 then/enddo/end 替代了花括号,用 elseif 替代了 else if

Lua’s control structures are clean — then/end and do/end replace braces, elseif replaces else if.


🟢 基础 / Basics — 四种循环和条件

1. if / elseif / else

local score = 85

if score >= 90 then
    print("优秀 / Excellent")
elseif score >= 80 then
    print("良好 / Good")
elseif score >= 60 then
    print("及格 / Pass")
else
    print("不及格 / Fail")
end
-- 输出: 良好 / Good

注意:Lua 没有 switch,也没有三元运算符 / No switch, no ternary:

-- 模拟三元运算符(仅当结果都不是 false/nil 时安全)
local status = (score >= 60) and "Pass" or "Fail"

2. while 循环

local i = 1
while i <= 5 do
    print("Count: " .. i)
    i = i + 1
end
-- Count: 1
-- Count: 2
-- ...
-- Count: 5

3. repeat…until 循环(至少执行一次)

-- repeat...until 类似 do...while,但条件为真时停止
-- repeat...until is like do...while, stops when condition is true

local i = 1
repeat
    print("Step: " .. i)
    i = i + 1
until i > 5

-- 特点:循环体内的局部变量在 until 条件中可见!
local x = 10
repeat
    local y = x * 2    -- y 在 until 中可见
    print(y)
    x = x - 1
until x < 1 or y > 15   -- 可以使用 y

4. 数字 for 循环

-- 基本形式:for var = start, stop, step do
-- step 默认为 1

-- 从 1 到 5
for i = 1, 5 do
    print(i)
end

-- 指定步长
for i = 0, 10, 2 do
    print(i)    -- 0, 2, 4, 6, 8, 10
end

-- 倒序
for i = 5, 1, -1 do
    print(i)    -- 5, 4, 3, 2, 1
end

-- 注意:循环变量是局部的,循环结束后不可访问
print(i)    -- nil

5. 泛型 for 循环(遍历器模式)

-- 遍历数组(ipairs)/ Iterate array
local fruits = {"apple", "banana", "cherry"}
for i, v in ipairs(fruits) do
    print(i, v)
end
-- 1  apple
-- 2  banana
-- 3  cherry

-- 遍历表(pairs)/ Iterate table
local person = {name="Alice", age=30, city="Beijing"}
for k, v in pairs(person) do
    print(k, v)
end
-- name  Alice
-- age   30
-- city  Beijing
-- (顺序不确定!)

6. break 和 goto

-- break 跳出当前循环
for i = 1, 100 do
    if i > 5 then
        break
    end
    print(i)
end

-- goto(Lua 5.2+)跳转到标签
for i = 1, 10 do
    if i == 5 then
        goto continue
    end
    print(i)
    ::continue::    -- 标签定义
end
-- 输出 1,2,3,4,6,7,8,9,10(跳过了 5)

🟡 进阶 / Intermediate — 实用模式

1. 遍历的陷阱:pairs vs ipairs

local t = {10, 20, 30, nil, 50}

-- ipairs 从 1 开始,遇到 nil 停止
for i, v in ipairs(t) do
    print(i, v)
end
-- 1  10
-- 2  20
-- 3  30
-- (遇到 t[4]=nil 就停了!)

-- pairs 遍历所有键值对,但顺序不确定
for k, v in pairs(t) do
    print(k, v)
end
-- 会遍历 1,2,3,5(跳过 nil,但可能乱序)

2. for 循环中的坑 / Pitfalls in for Loops

-- 坑 1:for 循环的三个表达式只计算一次
local n = 5
for i = 1, n do
    print(i)
    n = 10    -- 修改 n 不会影响循环次数!
end
-- 只循环 5 次,因为 n 的值在循环开始时就确定了

-- 坑 2:循环变量不能修改(修改无效)
for i = 1, 5 do
    i = i + 100    -- 无效,下一次迭代 i 仍按步长递增
    print(i)
end

-- 坑 3:浮点数循环可能不精确
for i = 0, 1, 0.1 do
    print(string.format("%.1f", i))
end
-- 可能不会精确地在 1.0 停止(浮点误差)

-- 安全做法:用整数循环
for i = 0, 10 do
    print(i / 10)
end

3. while 的实用模式 / Useful while Patterns

-- 模式一:读取直到结束
local function readLines(filename)
    local lines = {}
    local file = io.open(filename, "r")
    if not file then return nil end
    
    while true do
        local line = file:read("*l")
        if not line then break end    -- 到达文件末尾
        table.insert(lines, line)
    end
    file:close()
    return lines
end

-- 模式二:指数退避
local function retry(fn, maxRetries)
    local delay = 1
    local retries = 0
    while retries < maxRetries do
        local ok, result = pcall(fn)
        if ok then return result end
        retries = retries + 1
        print("Retry " .. retries .. " after " .. delay .. "s")
        os.execute("sleep " .. delay)
        delay = delay * 2    -- 指数退避
    end
    error("Max retries exceeded")
end

4. goto 的高级用法 / Advanced goto Usage

-- 用途 1:跳出多层循环(Lua 没有 break n)
for i = 1, 10 do
    for j = 1, 10 do
        if i * j > 50 then
            goto done
        end
        print(i, j)
    end
end
::done::
print("Exited nested loops")

-- 用途 2:继续外层循环(模拟 continue in nested loop)
for i = 1, 5 do
    for j = 1, 5 do
        if j == 3 then goto next_j end
        print(i, j)
        ::next_j::
    end
end

-- 用途 3:状态机
local function parse(str)
    local i = 1
    ::start::
    if i > #str then goto done end
    local c = str:sub(i, i)
    if c == " " then
        i = i + 1
        goto start
    end
    print("Char: " .. c)
    i = i + 1
    goto start
    ::done::
end

🔴 高级 / Advanced — 控制流的字节码

1. if 语句的字节码

-- 源码 / Source:
local x = 10
if x > 5 then
    print("big")
else
    print("small")
end

-- 字节码 / Bytecode:
-- 1  LOADK     0 0    ; R(0) = 10
-- 2  LE        0 0 1  ; if not (R(0) > 5) then PC++
-- 3  JMP       0 3    ; goto "big" code
-- 4  GETGLOBAL 1 1    ; R(1) = print
-- 5  LOADK     2 2    ; R(2) = "small"
-- 6  CALL      1 2 1  ; print("small")
-- 7  JMP       0 3    ; skip "big" code
-- 8  GETGLOBAL 1 1    ; R(1) = print
-- 9  LOADK     2 3    ; R(2) = "big"
-- 10 CALL      1 2 1  ; print("big")

2. 数字 for 的字节码

-- 源码 / Source:
for i = 1, 5 do print(i) end

-- 字节码 / Bytecode:
-- 1  LOADK     0 0    ; limit = 5
-- 2  LOADK     1 1    ; step = 1
-- 3  LOADK     2 2    ; R(2) = 1 (initial)
-- 4  FORPREP   0 3    ; prepare loop (R(0) -= step)
-- 5  GETGLOBAL 3 3    ; R(3) = print
-- 6  MOVE      4 2    ; R(4) = i
-- 7  CALL      3 2 1  ; print(i)
-- 8  FORLOOP   0 -4   ; i += step; if i <= limit then goto 5

3. 泛型 for 的字节码

-- 源码 / Source:
for k, v in pairs(t) do print(k, v) end

-- 字节码简化解释:
-- 1. 调用 pairs(t) 获取迭代器函数、状态、初始值
-- 2. TFORCALL: 调用迭代器函数获取下一个 k, v
-- 3. TFORLOOP: 如果 k ~= nil,跳回循环体
-- 4. 执行循环体

-- 泛型 for 的完整展开等价于:
local _f, _s, _var = pairs(t)
while true do
    local k, v = _f(_s, _var)
    _var = k
    if k == nil then break end
    print(k, v)
end

小结 / Summary

层级你需要知道的 / What You Need to Know
🟢 基础if/elseif/end、while/do/end、repeat/until、for/ ipairs/pairs、break
🟡 进阶pairs vs ipairs 陷阱、浮点循环精度、goto 跳出多层循环、模拟 continue
🔴 高级if 的 JMP 字节码、for 的 FORPREP/FORLOOP、泛型 for 的迭代器展开

下一章:函数 / Functions