Lua


4.2 循环语句


文档摘要

4.2 循环语句 Lua 循环语句详解:代码实践与应用 循环:迭代与计数 循环在 Lua 中主要用于两种场景:数值迭代 和 通用迭代。数值 循环适用于已知循环次数的情况,而通用 循环则更灵活,可以遍历各种数据结构。 1.1 数值 循环:精确的步进 数值 循环,也称为计数循环,是最常见的循环类型之一。它允许你指定一个初始值、一个终止值以及一个步进值,循环会从初始值开始,每次按照步进值递增(或递减),直到达到或超过终止值。 1.1.1 语法结构 数值 循环的语法结构如下: : 循环变量,在每次循环迭代时会被赋予新的数值。其作用域仅限于 循环体内部。 : 循环变量的初始值,循环从这个值开始。 : 循环的终止值。当循环变量的值超过(或低于,如果步进值为负数)这个值时,循环结束。

4.2 循环语句

Lua 循环语句详解:代码实践与应用

1. for 循环:迭代与计数

for 循环在 Lua 中主要用于两种场景:数值迭代通用迭代。数值 for 循环适用于已知循环次数的情况,而通用 for 循环则更灵活,可以遍历各种数据结构。

1.1 数值 for 循环:精确的步进

数值 for 循环,也称为计数循环,是最常见的循环类型之一。它允许你指定一个初始值、一个终止值以及一个步进值,循环会从初始值开始,每次按照步进值递增(或递减),直到达到或超过终止值。

1.1.1 语法结构

数值 for 循环的语法结构如下:

for variable = initial_value, limit, step do -- 循环体代码 end
  • variable: 循环变量,在每次循环迭代时会被赋予新的数值。其作用域仅限于 for 循环体内部。

  • initial_value: 循环变量的初始值,循环从这个值开始。

  • limit: 循环的终止值。当循环变量的值超过(或低于,如果步进值为负数)这个值时,循环结束。

  • step: 步进值,表示每次循环迭代后,循环变量增加(或减少)的量。如果省略 step,则默认步进值为 1。

1.1.2 代码实践与详解

示例 1:正向计数,步进值为 1

for i = 1, 5 do print("当前计数器 i 的值为:", i) end

代码解析:

  • for i = 1, 5 do:定义了一个数值 for 循环,循环变量为 i,初始值为 1,终止值为 5,步进值默认为 1。

  • print("当前计数器 i 的值为:", i):循环体代码,在每次循环迭代时,打印当前循环变量 i 的值。

运行结果:

当前计数器 i 的值为: 1 当前计数器 i 的值为: 2 当前计数器 i 的值为: 3 当前计数器 i 的值为: 4 当前计数器 i 的值为: 5

示例 2:反向计数,步进值为 -1

for j = 10, 1, -1 do print("倒数计数器 j 的值为:", j) end

代码解析:

  • for j = 10, 1, -1 do:定义了一个数值 for 循环,循环变量为 j,初始值为 10,终止值为 1,步进值为 -1(递减)。

  • print("倒数计数器 j 的值为:", j):循环体代码,打印当前循环变量 j 的值。

运行结果:

倒数计数器 j 的值为: 10 倒数计数器 j 的值为: 9 倒数计数器 j 的值为: 8 倒数计数器 j 的值为: 7 倒数计数器 j 的值为: 6 倒数计数器 j 的值为: 5 倒数计数器 j 的值为: 4 倒数计数器 j 的值为: 3 倒数计数器 j 的值为: 2 倒数计数器 j 的值为: 1

示例 3:自定义步进值

for k = 0, 20, 2 do print("步进值为 2 的计数器 k 的值为:", k) end

代码解析:

  • for k = 0, 20, 2 do:定义了一个数值 for 循环,循环变量为 k,初始值为 0,终止值为 20,步进值为 2。

  • print("步进值为 2 的计数器 k 的值为:", k):循环体代码,打印当前循环变量 k 的值。

运行结果:

步进值为 2 的计数器 k 的值为: 0 步进值为 2 的计数器 k 的值为: 2 步进值为 2 的计数器 k 的值为: 4 步进值为 2 的计数器 k 的值为: 6 步进值为 2 的计数器 k 的值为: 8 步进值为 2 的计数器 k 的值为: 10 步进值为 2 的计数器 k 的值为: 12 步进值为 2 的计数器 k 的值为: 14 步进值为 2 的计数器 k 的值为: 16 步进值为 2 的计数器 k 的值为: 18 步进值为 2 的计数器 k 的值为: 20

示例 4:循环变量的作用域

for m = 1, 3 do local inner_var = m * 2 print("循环内部 m:", m, " inner_var:", inner_var) end -- print("循环外部 m:", m) -- 错误!m 在循环外部不可见

代码解析:

  • 循环变量 m 的作用域仅限于 for 循环体内部。在循环结束后,尝试访问 m 会导致错误,因为 m 在循环外部是未定义的。

  • local inner_var = m * 2:在循环体内定义了一个局部变量 inner_var,用于演示循环体内部可以进行各种操作。

运行结果:

循环内部 m: 1 inner_var: 2 循环内部 m: 2 inner_var: 4 循环内部 m: 3 inner_var: 6

1.1.3 应用场景

  • 遍历数组 (表) 中的数值索引元素: 当需要访问数组或表的数值索引元素时,数值 for 循环非常方便。
local array = {"apple", "banana", "cherry", "date"} for index = 1, #array do print("数组元素索引:", index, " 值:", array[index]) end
  • 执行固定次数的操作: 例如,模拟游戏中的回合制操作、重复执行某个计算任务等。
local num_rounds = 5 for round = 1, num_rounds do print("开始第", round, "回合...") -- 执行游戏回合逻辑 end
  • 生成序列数据: 可以利用数值 for 循环生成一系列数值,例如创建等差数列、生成坐标点等。
local x_coordinates = {} for i = 0, 10, 2 do table.insert(x_coordinates, i) end print("X 坐标序列:", table.concat(x_coordinates, ", "))

1.2 通用 for 循环:迭代器与集合遍历

通用 for 循环,也称为迭代器 for 循环,是 Lua 中更强大和灵活的循环结构。它基于 迭代器 (iterator) 的概念,可以遍历各种数据结构,例如表 (table)、字符串、文件等。

1.2.1 语法结构

通用 for 循环的语法结构如下:

for key, value in iterator_function(collection) do -- 循环体代码 end
  • key, value: 迭代器返回的键和值。根据迭代器的不同,可能只返回一个值(例如,只有 value),或者返回键值对。如果迭代器只返回一个值,则可以只声明一个循环变量,例如 for value in iterator_function(collection) do

  • iterator_function(collection): 迭代器函数及其要遍历的集合。Lua 提供了内置的迭代器函数,例如 pairsipairs,也可以自定义迭代器。

  • collection: 要遍历的数据集合,例如表、字符串等。

1.2.2 常用的迭代器函数

Lua 中常用的迭代器函数包括:

  • pairs(table): 用于遍历表的 所有键值对。遍历顺序是不确定的(不保证按照插入顺序或任何特定顺序)。适用于遍历键值对无序的表,例如哈希表。

  • ipairs(table): 用于遍历表的 数值索引键值对。遍历顺序是按照数值索引的升序排列。当遇到第一个非数值索引或索引不连续时,迭代停止。适用于遍历数组或序列类型的表。

  • string.gmatch(string, pattern): 用于遍历字符串中所有匹配指定模式的子串。

  • io.lines(filename): 用于逐行读取文件内容。

1.2.3 代码实践与详解

示例 1:使用 pairs 遍历表

local my_table = {name = "Alice", age = 30, city = "New York"} for key, value in pairs(my_table) do print("键:", key, " 值:", value) end

代码解析:

  • pairs(my_table):调用 pairs 迭代器函数,传入要遍历的表 my_tablepairs 会返回一个迭代器,用于遍历表中的所有键值对。

  • for key, value in ... do:循环会迭代 pairs 返回的迭代器,每次迭代将键赋值给 key,值赋值给 value

  • print("键:", key, " 值:", value):循环体代码,打印当前迭代到的键和值。

运行结果(顺序可能不固定):

键: name 值: Alice 键: city 值: New York 键: age 值: 30

示例 2:使用 ipairs 遍历表

local my_array_table = {"apple", "banana", "cherry", [5] = "date", "fig"} for index, value in ipairs(my_array_table) do print("索引:", index, " 值:", value) end

代码解析:

  • ipairs(my_array_table):调用 ipairs 迭代器函数,传入要遍历的表 my_array_tableipairs 只遍历数值索引的键值对,并按照索引升序排列。

  • for index, value in ... do:循环迭代 ipairs 返回的迭代器,每次迭代将数值索引赋值给 index,值赋值给 value

运行结果:

索引: 1 值: apple 索引: 2 值: banana 索引: 3 值: cherry

注意: ipairs 在遇到非数值索引或索引不连续时会停止迭代。在本例中,索引为 5 的元素 "date" 和字符串索引 "fig" 都不会被 ipairs 遍历到。

示例 3:使用 string.gmatch 遍历字符串

local text = "Hello, World! 123 Lua is fun." for word in string.gmatch(text, "%w+") do print("单词:", word) end

代码解析:

  • string.gmatch(text, "%w+"):调用 string.gmatch 函数,传入要遍历的字符串 text 和模式 "%w+"(匹配一个或多个单词字符)。string.gmatch 返回一个迭代器,用于遍历字符串中所有匹配模式的子串。

  • for word in ... do:循环迭代 string.gmatch 返回的迭代器,每次迭代将匹配到的单词赋值给 word

运行结果:

单词: Hello 单词: World 单词: 123 单词: Lua 单词: is 单词: fun

示例 4:使用 io.lines 遍历文件

-- 创建一个名为 "example.txt" 的文件,内容如下: -- Line 1 -- Line 2 -- Line 3 for line in io.lines("example.txt") do print("行内容:", line) end

代码解析:

  • io.lines("example.txt"):调用 io.lines 函数,传入文件名 "example.txt"。io.lines 返回一个迭代器,用于逐行读取文件内容。

  • for line in ... do:循环迭代 io.lines 返回的迭代器,每次迭代将读取到的一行内容赋值给 line

运行结果:

行内容: Line 1 行内容: Line 2 行内容: Line 3

1.2.4 应用场景

  • 遍历表中的键值对: 使用 pairs 遍历无序表,例如配置信息、对象属性等。使用 ipairs 遍历数组或序列类型的表,例如列表数据、有序集合等。

  • 处理文本数据: 使用 string.gmatch 提取字符串中的特定模式,例如单词、数字、URL 等。

  • 文件 I/O 操作: 使用 io.lines 逐行读取文件内容,进行文件处理和分析。

  • 自定义数据结构遍历: 可以自定义迭代器函数,用于遍历用户自定义的数据结构,实现更复杂的迭代逻辑。

2. while 循环:条件驱动的重复执行

while 循环是另一种常用的循环结构,它基于条件判断来控制循环的执行。只要条件为真 (true),循环体代码就会一直执行,直到条件变为假 (false) 时循环结束。

2.1 语法结构

while 循环的语法结构如下:

while condition do -- 循环体代码 end
  • condition: 循环条件,一个布尔表达式。在每次循环迭代之前,会先判断条件是否为真。如果为真,则执行循环体代码;如果为假,则跳出循环。

2.2 代码实践与详解

示例 1:计数器控制的 while 循环

local count = 1 while count <= 5 do print("当前计数:", count) count = count + 1 -- 必须更新计数器,否则会陷入无限循环 end

代码解析:

  • local count = 1:初始化一个计数器变量 count 为 1。

  • while count <= 5 do:定义 while 循环,循环条件为 count <= 5。只要 count 的值小于等于 5,循环就会继续执行。

  • print("当前计数:", count):循环体代码,打印当前计数器的值。

  • count = count + 1关键步骤! 在循环体内部更新计数器 count 的值,使其每次迭代都递增 1。如果没有这一步,count 的值将永远保持为 1,条件 count <= 5 永远为真,导致无限循环。

运行结果:

当前计数: 1 当前计数: 2 当前计数: 3 当前计数: 4 当前计数: 5

示例 2:用户输入控制的 while 循环

local input = "" while input ~= "quit" do io.write("请输入命令 (输入 'quit' 退出): ") input = io.read() if input ~= "quit" then print("你输入的命令是:", input) -- 执行相应的命令处理逻辑 end end print("程序退出。")

代码解析:

  • local input = "":初始化一个变量 input 用于存储用户输入。

  • while input ~= "quit" do:定义 while 循环,循环条件为 input ~= "quit"。只要用户输入的命令不是 "quit",循环就会继续执行。

  • io.write("请输入命令 (输入 'quit' 退出): "):提示用户输入命令。

  • input = io.read():读取用户输入并赋值给 input 变量。

  • if input ~= "quit" then ... end:判断用户输入的命令是否为 "quit"。如果不是,则执行命令处理逻辑。

  • print("程序退出。"):当用户输入 "quit" 时,循环条件变为假,循环结束,程序打印退出信息。

运行结果(交互式):

请输入命令 (输入 'quit' 退出): hello 你输入的命令是: hello 请输入命令 (输入 'quit' 退出): world 你输入的命令是: world 请输入命令 (输入 'quit' 退出): quit 程序退出。

示例 3:条件标志控制的 while 循环

local is_running = true while is_running do print("程序正在运行...") -- 执行程序主循环逻辑 io.write("是否继续运行? (yes/no): ") local response = io.read() if response == "no" then is_running = false -- 设置条件标志为 false,结束循环 end end print("程序已停止。")

代码解析:

  • local is_running = true:初始化一个布尔变量 is_running 作为循环条件标志,初始值为 true。

  • while is_running do:定义 while 循环,循环条件为 is_running。只要 is_running 为 true,循环就会继续执行。

  • print("程序正在运行..."):循环体代码,模拟程序主循环逻辑。

  • if response == "no" then is_running = false end:根据用户输入判断是否继续运行。如果用户输入 "no",则将 is_running 设置为 false,循环条件变为假,循环结束。

运行结果(交互式):

程序正在运行... 是否继续运行? (yes/no): yes 程序正在运行... 是否继续运行? (yes/no): no 程序已停止。

2.3 应用场景

  • 不确定循环次数的场景: while 循环适用于循环次数在循环开始前无法确定的情况,循环的结束取决于某个条件的满足与否。

  • 事件驱动的程序: 例如游戏主循环、网络服务监听循环等,需要持续运行,直到发生特定事件或满足退出条件。

  • 数据处理和读取: 例如读取文件内容直到文件末尾、处理数据流直到数据结束等。

  • 等待某个条件成立: 例如等待用户输入、等待网络连接建立、等待资源加载完成等。

3. 循环控制语句:breakgoto (谨慎使用)

Lua 提供了 break 语句用于 立即跳出当前循环,以及 goto 语句用于 跳转到指定的标签位置

3.1 break 语句:提前终止循环

break 语句用于在循环体内部 立即终止当前循环,程序会跳出循环,并执行循环之后的代码。break 语句通常与条件判断语句 (if) 结合使用,在满足特定条件时提前结束循环。

示例:在 for 循环中使用 break

for i = 1, 10 do if i > 5 then print("达到条件,使用 break 跳出循环") break -- 当 i > 5 时,跳出 for 循环 end print("当前 i:", i) end print("循环结束后的代码")

运行结果:

当前 i: 1 当前 i: 2 当前 i: 3 当前 i: 4 当前 i: 5 达到条件,使用 break 跳出循环 循环结束后的代码

示例:在 while 循环中使用 break

local count = 0 while true do -- 无限循环 count = count + 1 print("当前 count:", count) if count >= 3 then print("达到计数上限,使用 break 跳出循环") break -- 当 count >= 3 时,跳出 while 循环 end end print("循环结束后的代码")

运行结果:

当前 count: 1 当前 count: 2 当前 count: 3 达到计数上限,使用 break 跳出循环 循环结束后的代码

3.2 goto 语句:跳转到标签 (谨慎使用)

goto 语句允许程序 无条件跳转到代码中标记的标签位置。标签使用双冒号 ::label_name:: 定义,goto label_name 语句会跳转到对应的标签位置继续执行。

注意: goto 语句通常被认为是一种 低级控制流语句,过度使用 goto 会导致代码结构混乱,可读性降低,难以维护。在 Lua 中,应尽量使用结构化的控制流语句(如 if, for, while, break)来组织代码逻辑。只有在极少数特定场景下,例如多层循环的快速退出、错误处理等,才考虑谨慎使用 goto

示例:使用 goto 跳出多层循环

for i = 1, 3 do for j = 1, 3 do print("i:", i, " j:", j) if i == 2 and j == 2 then print("满足条件,使用 goto 跳出外层循环") goto end_loops -- 跳转到标签 end_loops end end end ::end_loops:: print("跳出循环后的代码")

运行结果:

i: 1 j: 1 i: 1 j: 2 i: 1 j: 3 i: 2 j: 1 i: 2 j: 2 满足条件,使用 goto 跳出外层循环 跳出循环后的代码

代码解析:

  • ::end_loops:::定义一个标签名为 end_loops

  • goto end_loops:当 i == 2 and j == 2 条件满足时,执行 goto end_loops 语句,程序会跳转到标签 ::end_loops:: 所在的位置,跳过外层循环的剩余迭代和内层循环的所有迭代。

再次强调: goto 语句应谨慎使用,避免滥用导致代码难以理解和维护。优先使用结构化的控制流语句来组织代码逻辑。

4. 循环嵌套:多层循环结构

循环嵌套是指在一个循环结构内部包含另一个或多个循环结构。循环嵌套可以用于处理多维数据、实现复杂的迭代逻辑等。

示例:嵌套 for 循环,打印乘法口诀表

for i = 1, 9 do -- 外层循环控制行 for j = 1, i do -- 内层循环控制列,每行列数与行号相同 io.write(j, "x", i, "=", i * j, " ") -- 打印乘法表达式 end print() -- 换行 end

运行结果:


发布者: 作者: 转发
评论区 (0)
U