Zig 循环(实战总结)

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 提供了 breakcontinue 语句。

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 中的几种核心循环结构:forwhiledo-while,以及它们在实际场景中的应用。无论是遍历数组、控制流程,还是处理用户输入,Zig 循环都提供了强大而灵活的工具。

关键点总结:

  • for 循环适用于已知范围或数组遍历;
  • while 循环适合条件驱动的重复任务;
  • do-while 保证至少执行一次;
  • breakcontinue 让控制流更精确。

掌握这些技巧,你不仅能写出更简洁的代码,还能显著提升程序的可读性和可维护性。Zig 的设计哲学是“让程序员少写代码,多思考逻辑”,而循环正是实现这一目标的核心。现在,轮到你动手试试了。