Zig 循环:让代码重复工作更高效
你有没有遇到过这样的场景?需要打印 100 个数字,或者对一个数组里的每个元素执行相同的逻辑?手动写 100 行代码显然不现实。这时候,Zig 循环就派上用场了。它就像一个自动化的流水线,能让你的代码在不改变逻辑的前提下,自动重复执行任务。
Zig 是一门现代系统编程语言,语法简洁、性能卓越,尤其适合开发操作系统、嵌入式系统和高性能服务。而其中的循环机制,是它实现高效控制流的核心能力之一。本篇将带你从零开始,深入理解 Zig 中的循环结构,掌握它们的使用方法和最佳实践。
for 循环:最常见也最灵活的循环方式
在 Zig 中,for 循环是使用频率最高的循环结构。它既可以用于遍历数组,也可以像传统 for 循环一样实现计数循环。
遍历数组元素
想象你有一筐苹果,想一个一个检查它们是否成熟。for 循环就是帮你完成这个任务的“检查员”。
const std = @import("std");
pub fn main() void {
const fruits = &[_][]const u8{ "apple", "banana", "orange", "grape" };
// for 循环遍历数组,i 是索引,fruit 是元素值
for (fruits) |fruit, i| {
std.debug.print("第 {d} 个水果是:{s}\n", .{ i + 1, fruit });
// i 从 0 开始,所以加 1 显示第几个
}
}
代码注释:
fruits是一个数组,&[_]表示数组类型,[]const u8是字符串切片类型。for (fruits) |fruit, i|是 Zig 中的 for 循环语法,fruit是当前元素,i是索引。std.debug.print用于输出信息,.{ i + 1, fruit }是参数列表,{d}表示十进制整数,{s}表示字符串。
运行结果:
第 1 个水果是:apple
第 2 个水果是:banana
第 3 个水果是:orange
第 4 个水果是:grape
计数型 for 循环
如果你要打印从 1 到 10 的数字,可以用计数循环。
pub fn main() void {
// 从 1 到 10,包含 10
for (1..11) |i| {
std.debug.print("当前数字是:{d}\n", .{ i });
}
}
代码注释:
1..11是一个范围表达式,表示从 1 开始,到 11 结束(不包含 11),所以实际是 1 到 10。|i|表示循环变量是i,它会依次取 1 到 10 的值。
while 循环:条件驱动的重复执行
当你要执行某段代码,但不确定需要重复多少次时,while 循环就非常合适。它就像一个“门卫”,每次检查条件是否为真,如果为真就放行执行,否则停止。
pub fn main() void {
var counter = 0;
const target = 5;
// 只要 counter 小于 target,就继续执行循环体
while (counter < target) : (counter += 1) {
std.debug.print("当前计数:{d}\n", .{ counter });
// 每次循环结束时,counter 自增 1
}
}
代码注释:
var counter = 0;定义一个变量,用于记录循环次数。while (counter < target) : (counter += 1)是 Zig 的 while 循环语法,:后面的部分是每次循环结束后的更新语句。counter += 1表示每次循环后,计数器加 1。
运行结果:
当前计数:0
当前计数:1
当前计数:2
当前计数:3
当前计数:4
💡 小提示:
while循环非常适合用于读取文件、等待用户输入或处理不确定次数的逻辑。
do-while 循环:至少执行一次的循环
有时候,你希望代码至少执行一次,然后再判断是否继续。do-while 循环就是为此设计的。
pub fn main() void {
var input: u8 = 0;
const secret = 42;
// 先执行一次循环体,再检查条件
do {
std.debug.print("请输入一个数字(输入 42 可退出):", .{});
// 假设这里从标准输入读取,实际使用需结合 std.io
input = 42; // 模拟用户输入 42
std.debug.print("你输入了:{d}\n", .{ input });
} while (input != secret);
}
代码注释:
do { ... } while (condition)表示先执行循环体,再判断条件。- 即使条件一开始不满足,循环体也会执行至少一次。
- 这在菜单系统中很常见:用户必须先看到选项,才能决定是否退出。
for 循环中的 break 和 continue
在复杂的逻辑中,我们可能需要提前退出循环,或者跳过当前迭代。Zig 提供了 break 和 continue 语句。
break:提前终止循环
pub fn main() void {
const numbers = &[_]u32{ 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 };
for (numbers) |num| {
if (num == 5) {
std.debug.print("遇到 5,提前结束循环\n", .{});
break; // 立即退出 for 循环
}
std.debug.print("处理数字:{d}\n", .{ num });
}
}
代码注释:
- 当
num == 5时,执行break,跳出整个循环。 - 5 之后的数字(6 到 10)不会再被处理。
continue:跳过当前迭代
pub fn main() void {
const numbers = &[_]u32{ 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 };
for (numbers) |num| {
if (num % 2 == 0) {
std.debug.print("跳过偶数:{d}\n", .{ num });
continue; // 跳过当前迭代,不执行后续代码
}
std.debug.print("处理奇数:{d}\n", .{ num });
}
}
代码注释:
num % 2 == 0判断是否为偶数。continue表示跳过当前循环,不执行后面的print语句。
循环中的索引与范围控制
Zig 的循环支持灵活的范围控制,尤其是在处理大数组或需要跳过部分元素时。
使用范围表达式
pub fn main() void {
// 从 0 到 9,步长为 2
for (0..10 : 2) |i| {
std.debug.print("偶数:{d}\n", .{ i });
}
}
代码注释:
0..10 : 2表示从 0 到 10(不含 10),步长为 2。- 输出:0、2、4、6、8。
处理反向循环
如果需要从大到小遍历,可以使用 @reverse。
pub fn main() void {
const numbers = &[_]u32{ 1, 2, 3, 4, 5 };
// 反向遍历数组
for (@reverse(numbers)) |num| {
std.debug.print("倒序输出:{d}\n", .{ num });
}
}
代码注释:
@reverse(numbers)返回一个反向视图,便于反向遍历。- 无需手动计算索引,代码更清晰。
实际应用场景:数据处理与校验
假设你要处理一个用户输入的数字列表,要求:
- 找出所有大于 10 的数;
- 统计总和;
- 遇到负数立即停止。
pub fn main() void {
const inputs = &[_]i32{ 5, 12, -3, 18, 7, 20 };
var sum: i32 = 0;
var found_large = false;
for (inputs) |num| {
if (num < 0) {
std.debug.print("检测到负数,停止处理\n", .{});
break; // 遇负数停止
}
if (num > 10) {
std.debug.print("发现大数:{d}\n", .{ num });
found_large = true;
}
sum += num;
}
std.debug.print("总和是:{d}\n", .{ sum });
if (!found_large) {
std.debug.print("未发现大于 10 的数\n", .{});
}
}
代码注释:
- 使用
break处理异常输入(负数)。 found_large用于标记是否发现大数。- 逻辑清晰,易于维护。
总结:掌握 Zig 循环,提升编程效率
通过本文,我们系统学习了 Zig 中的几种核心循环结构:for、while、do-while,以及它们在实际场景中的应用。无论是遍历数组、控制流程,还是处理用户输入,Zig 循环都提供了强大而灵活的工具。
关键点总结:
for循环适用于已知范围或数组遍历;while循环适合条件驱动的重复任务;do-while保证至少执行一次;break和continue让控制流更精确。
掌握这些技巧,你不仅能写出更简洁的代码,还能显著提升程序的可读性和可维护性。Zig 的设计哲学是“让程序员少写代码,多思考逻辑”,而循环正是实现这一目标的核心。现在,轮到你动手试试了。