控制结构

6.1 条件分支

if...then

1
2
3
4
5
local score = 85

if score >= 60 then
print("及格")
end

if...then...else

1
2
3
4
5
6
7
local age = 17

if age >= 18 then
print("成年")
else
print("未成年")
end

if...then...elseif...else

1
2
3
4
5
6
7
8
9
10
11
12
13
local score = 85

if score >= 90 then
print("优秀")
elseif score >= 80 then
print("良好")
elseif score >= 70 then
print("中等")
elseif score >= 60 then
print("及格")
else
print("不及格")
end

真假判断

Lua 中只有 false 和 nil 被视为假,其他所有值(包括 0、""、{})都是真:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
-- 常见陷阱
if 0 then
print("0 为真") -- 这个会打印!
end

if "" then
print("空字符串也为真") -- 这个也会打印!
end

if {} then
print("空表也为真") -- 这个也会打印!
end

-- 只有 false 和 nil 为假
if false then
print("不会打印")
end

if nil then
print("也不会打印")
end

6.2 while 循环

1
2
3
4
5
6
7
8
9
10
-- 计算 1 到 10 的和
local sum = 0
local i = 1

while i <= 10 do
sum = sum + i
i = i + 1
end

print(sum) --> 55

无限循环

1
2
3
4
5
6
7
8
9
10
11
12
13
-- 用 while 实现"菜单循环"
local running = true

while running do
print("1. 开始游戏")
print("2. 设置")
print("3. 退出")

local choice = io.read()
if choice == "3" then
running = false
end
end

6.3 repeat...until 循环

repeat...until 保证至少执行一次循环体,条件为真时退出:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
-- 猜数字游戏
local secret = math.random(1, 100)
local guess

repeat
io.write("猜一个 1~100 的数字:")
guess = tonumber(io.read())

if guess > secret then
print("太大了!")
elseif guess < secret then
print("太小了!")
else
print("猜对了!")
end
until guess == secret

while vs repeat...until 对比

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
-- while:先判断,再执行
local i = 10
while i < 5 do -- 条件为假,不执行
print(i)
i = i + 1
end
-- 无输出

-- repeat:先执行,再判断
local j = 10
repeat
print(j) --> 10(至少执行一次)
j = j + 1
until j >= 5 -- 条件满足时退出
-- 输出 10

6.4 数值 for 循环

语法:for 变量 = 起始值, 结束值, 步长 do(步长可选,默认为 1)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
-- 从 1 到 5,步长 1
for i = 1, 5 do
print(i)
end
-- 输出:1 2 3 4 5

-- 从 5 到 1,步长 -1
for i = 5, 1, -1 do
print(i)
end
-- 输出:5 4 3 2 1

-- 步长为 2
for i = 1, 10, 2 do
print(i)
end
-- 输出:1 3 5 7 9

-- 计算 1+2+...+100
local sum = 0
for i = 1, 100 do
sum = sum + i
end
print(sum) --> 5050

for 循环的注意事项

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
-- 循环变量是局部变量,循环结束后不可见
for i = 1, 3 do
print(i)
end
-- print(i) --> 报错或 nil(i 在循环外不存在)

-- 起始值、结束值、步长只在循环开始时计算一次
local function get_end()
print("get_end 被调用")
return 3
end

for i = 1, get_end() do -- 只调用一次
print(i)
end

-- 不要在循环体内修改循环变量(虽然允许,但不推荐)
-- 修改在下次迭代时会被覆盖
for i = 1, 5 do
i = 10 -- 下次迭代 i 仍然是 2
print(i) --> 10(本次有效)
end

6.5 泛型 for 循环

泛型 for 用于遍历迭代器返回的值:

pairs 与 ipairs

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
local t = { "a", "b", "c", key = "value" }

-- ipairs:遍历连续整数索引(1 开始,遇到 nil 停止)
for i, v in ipairs(t) do
print(i, v)
end
-- 输出:
-- 1 a
-- 2 b
-- 3 c

-- pairs:遍历所有键值对(无序)
for k, v in pairs(t) do
print(k, v)
end
-- 输出(顺序不确定):
-- 1 a
-- 2 b
-- 3 c
-- key value

字符串遍历

1
2
3
4
5
6
7
8
9
10
11
-- 遍历字符串中的字符
for i in string.gmatch("hello", ".") do
print(i)
end
-- 输出:h e l l o

-- 遍历字符串中的所有单词
for word in string.gmatch("apple banana cherry", "%a+") do
print(word)
end
-- 输出:apple banana cherry

自定义迭代器(概念引入)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
-- 一个简单的迭代器:遍历 keys
local function my_keys(t)
local keys = {}
for k in pairs(t) do
table.insert(keys, k)
end
table.sort(keys)

local i = 0
return function()
i = i + 1
return keys[i]
end
end

local t = { z = 1, a = 2, m = 3 }
for k in my_keys(t) do
print(k, t[k])
end
-- 输出(按字母序):a z m

6.6 break 与 goto

break

break 立即跳出当前循环层(仅限 for、while、repeat):

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
-- 查找第一个大于 50 的数
local numbers = { 12, 34, 56, 78, 90 }

for i, v in ipairs(numbers) do
if v > 50 then
print("找到:", v) --> 56
break
end
end

-- break 只能跳出一层循环
for i = 1, 3 do
for j = 1, 3 do
if j == 2 then
break -- 只跳出内层循环
end
print(i, j)
end
end
-- 输出:1 1 2 1 3 1

goto

goto 可以跳转到指定的标签(::标签名::),但不能跳入代码块内部:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
-- 用 goto 模拟 continue
for i = 1, 10 do
if i % 2 == 0 then
goto continue
end
print(i) -- 只打印奇数
::continue::
end
-- 输出:1 3 5 7 9

-- 用 goto 跳出多层循环
for i = 1, 5 do
for j = 1, 5 do
if i * j > 10 then
goto outer_end -- 直接跳出两层循环
end
print(i, j, i * j)
end
end
::outer_end::
print("循环结束")

goto 的限制

1
2
3
4
5
6
7
8
9
10
11
12
13
-- 不能跳入代码块
-- goto block
-- do
-- ::block::
-- end
-- 错误!

-- 不能跳入循环
-- goto loop_start
-- for i = 1, 10 do
-- ::loop_start::
-- end
-- 错误!

6.7 实用循环示例

打印乘法表

1
2
3
4
5
6
7
for i = 1, 9 do
local line = ""
for j = 1, i do
line = line .. string.format("%d×%d=%-2d ", j, i, i * j)
end
print(line)
end

遍历目录层级(递归循环)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
-- 用 while 模拟递归遍历(深度优先)
local function traverse(data)
local stack = { data }

while #stack > 0 do
local current = table.remove(stack)
print(current.name)

if current.children then
for i = #current.children, 1, -1 do
table.insert(stack, current.children[i])
end
end
end
end

local tree = {
name = "root",
children = {
{ name = "dir1", children = {
{ name = "file1" },
{ name = "file2" }
}},
{ name = "dir2" }
}
}

traverse(tree)

6.8 本章小结

结构 语法 说明
if if 条件 then ... end 条件分支
while while 条件 do ... end 先判断后执行
repeat repeat ... until 条件 先执行后判断
for 数值 for i=a,b,step do ... end 固定次数循环
for 泛型 for k,v in iter do ... end 遍历迭代器
break break 跳出当前循环
goto goto label 跳转到标签

练习题

  1. 用 for 循环打印斐波那契数列的前 20 个数
  2. 用 while 循环实现一个”猜数字”游戏,统计用户猜的次数
  3. 用 repeat...until 实现一个密码输入验证,直到输入正确为止
  4. 写一个函数 is_prime(n) 判断 n 是否为质数
  5. 在 1~100 中找到所有水仙花数(三位数各位立方和等于本身,如 153)
  6. 用 goto 模拟 continue 打印 1~30 中不是 3 或 5 的倍数的数
ByteFisher
分享编程技术 · 记录钓鱼乐趣
扫码关注
▸ 扫码关注 ◂
Lua基础教程 系列
第 6/15 篇
分享: