控制结构

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 中只有 falsenil 被视为假,其他所有值(包括 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 用于遍历迭代器返回的值:

pairsipairs

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 breakgoto

break

break 立即跳出当前循环层(仅限 forwhilerepeat):

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 篇
分享: