Ziglings 笔记 63: 带标签的循环 (Labeled Loops)

指哪打哪

在 Ziglings 的第 63 个练习中,我们解决了一个经典的编程难题:如何优雅地跳出多层嵌套循环?

在制作食堂菜单匹配系统时,我们需要遍历每一个食物,再遍历它需要的每一个配料。这是一个典型的双重循环结构。一旦我们在内层发现“缺货”,就需要立即放弃当前食物,转而检查下一个食物(操作外层循环)。

挑战:食堂点餐系统

任务是根据顾客想要的配料(wanted_ingredients),在菜单(menu)中找到匹配的食物。

  • 如果缺少任何一项必需配料,就跳过该食物。
  • 如果找到了所有配料都齐全的食物,就返回该食物。
  • 如果都没找到,返回默认的 “Mac & Cheese”。

解决方案

使用 Labeled Loops,我们可以给外层循环起个名字 food_loop,然后在内层直接引用它:

const std = @import("std");

// ... 省略结构体定义 ...

pub fn main() void {
    const wanted_ingredients = [_]u8{ 0, 3 }; // Chili, Cheese

    // 1. 给外层循环加标签: food_loop
    const meal = food_loop: for (menu) |food| {

        for (food.requires, 0..) |required, required_ingredient| {
            if (!required) continue;

            // 检查配料是否存在...
            const found = for (wanted_ingredients) |want_it| {
                if (required_ingredient == want_it) break true;
            } else false;

            // 2. 定向 Continue
            // 如果没找到配料,直接跳过外层循环的当前迭代
            // "这个菜做不了,看下一个菜吧"
            if (!found) continue :food_loop;
        }

        // 3. 定向 Break 并返回值
        // 如果代码跑到这里,说明所有配料都齐了。
        // "找到了!就是这个菜!带上它离开循环!"
        break :food_loop food;
        
    } else menu[0]; // 4. 默认值
    // 如果循环跑完了都没触发 break,说明没找到合适的,返回默认菜。

    std.debug.print("Enjoy your {s}!\n", .{meal.name});
}

核心知识点总结

1. 消除 Flag 变量

在没有标签的语言(如 C)中,我们通常需要这样做:

bool possible = true;
for (ingredients) {
    if (missing) {
        possible = false; // 先标记
        break;            // 跳出内层
    }
}
if (!possible) continue; // 在外层检查标记

Zig 的 continue :label 让我们可以直接“传送”到外层循环的下一次迭代,逻辑更加线性、清晰。

2. 代码块也是表达式

不仅是循环,任何用 { ... } 包裹的代码块都可以加标签,并使用 break :label value 来返回值。这在需要复杂的初始化逻辑(Complex Initialization)时非常有用,可以避免声明可变的 var 变量。

3. 挑战题解答

练习最后的 Challenge 问如何去掉 found 变量。我们可以直接把 if 逻辑嵌入到查找配料的循环中:

// 这种写法利用了 for..else 结构
for (wanted_ingredients) |want_it| {
    if (required_ingredient == want_it) break; // 找到了,跳出查找循环
} else {
    // 没找到(查找循环自然结束)
    // 既然缺配料,那就直接跳过外层食物循环
    continue :food_loop; 
}

后续预告:我们已经掌握了大部分基础语法。接下来的练习将进入 Formatting (格式化)。Zig 的打印功能非常强大,不仅能打印字符串,还能控制对齐、进制、浮点精度等。下一篇我们将深入研究 {} 占位符背后的秘密。