Zig 循环

Zig 编程语言中,循环是用于重复执行代码块的基本控制结构。Zig 提供简洁而灵活的循环语法,主要包括 for 循环和 while 循环,设计上避免了复杂性,同时保证类型安全和性能。以下是对 Zig 循环的中文讲解,涵盖 forwhile 循环的用法、特性、示例代码及注意事项,基于 Zig 0.14.1(截至 2025 年 5 月的稳定版),力求简洁清晰。


1. Zig 循环概述

Zig 的循环结构用于迭代数据(如数组、切片)或根据条件重复执行代码。特点包括:

  • 显式语法:无隐藏控制流,易于理解。
  • 类型安全:迭代器和条件受编译时检查。
  • 性能优化:支持编译时展开(comptime),减少运行时开销。
  • do-while:Zig 简化设计,仅提供 forwhile

2. For 循环

for 循环用于迭代可迭代对象(如数组、切片、范围)。Zig 的 for 循环语法直观,特别适合处理序列数据。

语法

for (items) |item| {
    // 循环体
}
  • items:可迭代对象(如数组、切片)。
  • item:每次迭代捕获的元素。
  • 支持多变量捕获、索引和范围。

基本示例

const std = @import("std");

pub fn main() !void {
    const numbers = [_]u8{1, 2, 3, 4, 5};
    const stdout = std.io.getStdOut().writer();

    for (numbers) |n| {
        try stdout.print("数字: {}\n", .{n});
    }
}

输出

数字: 1
数字: 2
数字: 3
数字: 4
数字: 5

说明

  • |n|:捕获数组元素,n 是每次迭代的值。
  • 数组 [_]u8 自动推断长度。

带索引的 For 循环

使用 |item, index| 捕获元素和索引:

const std = @import("std");

pub fn main() !void {
    const letters = [_]u8{'a', 'b', 'c'};
    const stdout = std.io.getStdOut().writer();

    for (letters, 0..) |letter, i| {
        try stdout.print("索引: {}, 字母: {}\n", .{i, letter});
    }
}

输出

索引: 0, 字母: 97
索引: 1, 字母: 98
索引: 2, 字母: 99

说明

  • 0..:生成索引范围,自动匹配数组长度。
  • letteru8 类型,打印时显示 ASCII 值。

迭代范围

Zig 支持直接迭代数字范围:

for (0..5) |i| {
    try std.io.getStdOut().writer().print("计数: {}\n", .{i});
}

输出

计数: 0
计数: 1
计数: 2
计数: 3
计数: 4

多变量捕获

同时迭代多个序列:

const std = @import("std");

pub fn main() !void {
    const a = [_]u8{1, 2, 3};
    const b = [_]u8{4, 5, 6};
    const stdout = std.io.getStdOut().writer();

    for (a, b) |x, y| {
        try stdout.print("a: {}, b: {}\n", .{x, y});
    }
}

输出

a: 1, b: 4
a: 2, b: 5
a: 3, b: 6

3. While 循环

while 循环用于基于条件重复执行代码,支持继续表达式和错误处理。

语法

while (条件) : (继续表达式) {
    // 循环体
}
  • 条件:布尔表达式,决定是否继续循环。
  • 继续表达式:可选,每次循环末尾执行(如递增计数器)。

基本示例

const std = @import("std");

pub fn main() !void {
    var i: u8 = 0;
    const stdout = std.io.getStdOut().writer();

    while (i < 3) : (i += 1) {
        try stdout.print("计数: {}\n", .{i});
    }
}

输出

计数: 0
计数: 1
计数: 2

说明

  • : (i += 1):继续表达式,循环末尾执行。
  • 条件 i < 3 控制循环退出。

While 与可选类型

while 可用于处理可选值(?T):

const std = @import("std");

pub fn main() !void {
    var maybe_num: ?u8 = 10;
    const stdout = std.io.getStdOut().writer();

    while (maybe_num) |num| {
        try stdout.print("值: {}\n", .{num});
        maybe_num = null; // 退出循环
    }
}

输出

值: 10

While 与错误处理

while 支持错误联合(!T):

const std = @import("std");
const MyError = error{Overflow};

fn next_number(n: u8) MyError!u8 {
    if (n >= 255) return MyError.Overflow;
    return n + 1;
}

pub fn main() !void {
    var num: u8 = 0;
    const stdout = std.io.getStdOut().writer();

    while (true) {
        num = next_number(num) catch |err| {
            try stdout.print("错误: {}\n", .{err});
            break;
        };
        try stdout.print("数字: {}\n", .{num});
        if (num >= 3) break;
    }
}

输出

数字: 1
数字: 2
数字: 3

说明

  • catch:捕获错误,退出循环。
  • break:显式终止循环。

4. 循环控制

Zig 提供 breakcontinue 控制循环:

  • break:退出循环。
  • continue:跳到下一次迭代。

示例

const std = @import("std");

pub fn main() !void {
    const stdout = std.io.getStdOut().writer();
    var i: u8 = 0;

    while (i < 5) : (i += 1) {
        if (i == 2) continue; // 跳过 i == 2
        if (i == 4) break;   // 在 i == 4 时退出
        try stdout.print("i: {}\n", .{i});
    }
}

输出

i: 0
i: 1
i: 3

5. 编译时循环

Zig 支持 comptime 循环,在编译时展开,优化性能。

示例

const std = @import("std");

const sum = comptime blk: {
    var total: u32 = 0;
    for ([_]u8{1, 2, 3}) |n| {
        total += n;
    }
    break :blk total;
};

pub fn main() !void {
    try std.io.getStdOut().writer().print("编译时和: {}\n", .{sum});
}

输出

编译时和: 6

说明

  • comptime 确保循环在编译时执行,结果为常量。
  • 适合生成静态数据或优化代码。

6. 注意事项

  • 类型安全
  • 迭代器类型必须匹配(如数组、切片)。
  • 错误联合需用 trycatch 处理。
  • 性能
  • for 循环针对数组/切片优化,编译器可展开循环。
  • 使用 comptime 减少运行时开销。
  • 边界检查
  • Zig 默认启用数组边界检查,防止越界:
    zig var arr = [_]u8{1, 2}; // arr[2]; // 编译时或运行时错误
  • 调试
  • 使用 std.debug.print 检查循环状态。
  • 编译时错误提示明确,检查迭代器类型或条件。
  • 与 C 互操作
  • 循环常用于处理 C 风格数组([*]T),注意指针安全。

7. 综合示例

以下示例结合 forwhile 循环,处理数组和错误:

const std = @import("std");
const MyError = error{InvalidValue};

fn validate(n: u8) MyError!u8 {
    if (n > 100) return MyError.InvalidValue;
    return n;
}

pub fn main() !void {
    const stdout = std.io.getStdOut().writer();
    const numbers = [_]u8{10, 150, 30, 200};

    // For 循环:验证数组元素
    for (numbers, 0..) |n, i| {
        const valid = validate(n) catch |err| {
            try stdout.print("索引 {} 错误: {}\n", .{i, err});
            continue;
        };
        try stdout.print("索引 {} 有效值: {}\n", .{i, valid});
    }

    // While 循环:累加直到条件
    var sum: u32 = 0;
    var i: usize = 0;
    while (i < numbers.len) : (i += 1) {
        sum += numbers[i];
        if (sum > 200) break;
    }
    try stdout.print("总和: {}\n", .{sum});
}

运行

zig run example.zig

输出

索引 0 有效值: 10
索引 1 错误: InvalidValue
索引 2 有效值: 30
索引 3 错误: InvalidValue
总和: 210

说明

  • for 循环迭代数组,处理错误。
  • while 循环累加并在条件满足时退出。

8. 总结

Zig 的循环机制简洁高效:

  • for 循环:适合迭代数组、切片或范围,支持多变量捕获。
  • while 循环:基于条件执行,支持继续表达式和错误处理。
  • 编译时循环:使用 comptime 优化性能。
    注意类型安全、边界检查和错误处理,结合 std.debug.print 调试。Zig 的循环设计适合系统编程,兼顾性能和可读性。

如果你需要更复杂的循环示例(如嵌套循环、并发迭代)或有其他问题,请告诉我!

类似文章

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注