Skip to content

Day 9: 循环 - While、For与Break/Continue

欢迎来到第九天!循环是编程中用于执行重复任务不可或缺的结构。今天,我们将学习Zig中的两种主要循环:while 和 for。Zig的循环设计在保持与C相似的语法的同时,通过一些独特的改进,使其在遍历数据结构时更安全、更直观。我们还将学习如何使用 break 和 continue 来精确控制循环的执行流程。

while 循环会在给定条件为 true 时持续执行一个代码块。

  • 基本语法:

    var i: u32 = 0;
    while (i < 5) {
    // ...
    i += 1;
    }
  • while 与 else: while 循环有一个特殊的 else 分支,它在循环正常结束(即循环条件变为 false)时执行。如果循环是通过 break 提前退出的,else 分支将不会被执行。

    var j: u32 = 0;
    while (j < 5) {
    // ...
    j += 1;
    } else {
    // 这段代码会在 j >= 5 时执行
    }
  • while 与可选类型解包: while 可以像 if 一样用于解包可选类型,这在实现迭代器等模式时非常有用。

    var list = ... // 假设是一个返回 ?Node 的链表
    while (list.next) |node| {
    // 只要 list.next 不是 null,就继续循环
    // node 是解包后的值
    list = node;
    }

Zig的 for 循环专门用于遍历数组、切片或元组的元素,它比C风格的 for (i=0; i<len; i++) 更安全,可以有效防止“差一错误”(off-by-one errors)。

  • 遍历元素:

    const items = [_]i32{ 10, 20, 30 };
    for (items) |item|
    // item 将依次是 10, 20, 30
    }
  • 遍历元素和索引: 如果你同时需要元素和它的索引,可以使用 |item, index| 的形式。

    for (items) |item, index| {
    std.debug.print("items[{d}] = {d}\n", .{ index, item });
    }
  • for 也是表达式: 与 if 和 while 一样,for 也可以有 else 分支,在循环正常完成(即遍历完所有元素)时执行。如果循环被 break 中断,else 分支不会执行。

  • continue: 立即跳过当前循环的剩余部分,开始下一次迭代。
  • break: 立即终止整个循环。
var i: u32 = 0;
while (i < 10) {
i += 1;
if (i == 3) continue; // 跳过 i=3 的情况
if (i == 8) break; // 当 i=8 时退出循环
// ...
}
  • 带标签的 break 和 continue: 当存在嵌套循环时,可以使用标签来指定 break 或 continue 作用于哪个循环。

    outer_loop: while (true) {
    inner_loop: while (true) {
    // ...
    break :outer_loop; // 直接跳出外层循环
    }
    }

在Zig中,创建一个无限循环的标准方式是 while (true)。这在需要持续运行的服务或事件循环中很常见。由于Zig强大的编译期分析能力,while (true) 是完全安全的,编译器理解其意图。

while (true) {
// 等待并处理请求
if (should_shutdown) {
break;
}
}

使用 for 循环来计算一个切片中所有元素的和。

const std = @import("std");
fn sum(slice: []const i32) i32 {
var total: i32 = 0;
for (slice) |value| {
total += value;
}
return total;
}
pub fn main() !void {
const numbers = [_]i32{ 1, 2, 3, 4, 5 };
const total = sum(&numbers);
std.debug.print("Sum: {d}\n", .{total}); // 输出: Sum: 15
}

这是一个经典的算法练习,非常适合用来实践循环和数组操作。

  1. 创建一个函数 fn bubbleSort(slice: []i32) void。
  2. 使用嵌套的 for 或 while 循环来实现冒泡排序算法。
  3. 外层循环控制排序的轮数,内层循环用于比较和交换相邻的元素。
  4. 在 main 函数中创建一个可变数组,调用 bubbleSort,然后打印排序后的结果。

提示: 你需要一个 var 数组,因为排序过程需要修改它。

问:Zig有像C那样的 for (;;) 或 for (i=0; ...) 循环吗?

答:没有。Zig的 for 专门用于迭代。C风格的 for 循环可以通过 while 循环轻松模拟,Zig的设计者认为专门的迭代 for 更清晰、更安全。

// C: for (int i = 0; i < 10; i++)
// Zig:
var i: u32 = 0;
while (i < 10) : (i += 1) {
// ...
}

注意 while 条件后面的 :(i += 1),这是一个 continue 表达式,它会在每次循环体执行完毕后执行,完美模拟了C for 循环的第三部分。

问:标签名会与其他变量名冲突吗?

答:不会。标签有自己的命名空间,所以你可以使用与变量相同的名字作为标签,尽管这可能不是一个好的编码风格。例如 const my_loop = 10; my_loop: while ... 是合法的。

今天,我们掌握了Zig中的 while 和 for 循环。我们学习了它们作为表达式的特性(else 分支),for 在遍历集合时的安全性和便利性,以及如何使用 break、continue 和标签来精细控制循环。这些结构是构建复杂逻辑和算法的基础。

明天,我们将进入一个非常重要的话题:函数。我们将学习如何声明函数、传递参数以及处理多返回值。