Skip to content

Zig 循环

循环是编程中的基本控制结构,本章将详细介绍 Zig 中的各种循环语句。

while 循环

基本 while 循环

while 循环在条件为真时重复执行代码块:

rust
const std = @import("std");

pub fn main() void {
    var i: i32 = 0;
    
    std.debug.print("基本 while 循环:\n", .{});
    while (i < 5) {
        std.debug.print("i = {}\n", .{i});
        i += 1;
    }
}

带继续表达式的 while 循环

Zig 的 while 循环可以包含一个继续表达式,在每次迭代结束时执行:

rust
const std = @import("std");

pub fn main() void {
    var i: i32 = 0;
    
    std.debug.print("带继续表达式的 while 循环:\n", .{});
    while (i < 5) : (i += 1) {
        std.debug.print("i = {}\n", .{i});
    }
}

while 循环的可选值处理

while 循环可以处理可选值,当值为 null 时结束循环:

zig
const std = @import("std");

fn getNextNumber(current: i32) ?i32 {
    if (current >= 5) return null;
    return current + 1;
}

pub fn main() void {
    var maybe_number: ?i32 = 0;
    
    std.debug.print("处理可选值的 while 循环:\n", .{});
    while (maybe_number) |number| {
        std.debug.print("数字: {}\n", .{number});
        maybe_number = getNextNumber(number);
    }
}

while 循环的错误处理

while 循环也可以处理错误联合类型:

rust
const std = @import("std");

const NumberError = error{
    TooLarge,
};

fn getNextSafeNumber(current: i32) NumberError!?i32 {
    if (current >= 10) return NumberError.TooLarge;
    if (current >= 5) return null;
    return current + 1;
}

pub fn main() void {
    var maybe_number: NumberError!?i32 = 0;
    
    std.debug.print("错误处理的 while 循环:\n", .{});
    while (maybe_number) |maybe_num| {
        if (maybe_num) |number| {
            std.debug.print("安全数字: {}\n", .{number});
            maybe_number = getNextSafeNumber(number);
        } else {
            break;
        }
    } else |err| {
        std.debug.print("遇到错误: {}\n", .{err});
    }
}

for 循环

遍历数组

for 循环主要用于遍历数组、切片等可迭代对象:

zig
const std = @import("std");

pub fn main() void {
    const numbers = [_]i32{ 1, 2, 3, 4, 5 };
    
    std.debug.print("遍历数组:\n", .{});
    for (numbers) |number| {
        std.debug.print("数字: {}\n", .{number});
    }
}

遍历切片

rust
const std = @import("std");

pub fn main() void {
    const fruits = [_][]const u8{ "苹果", "香蕉", "橙子", "葡萄" };
    const slice = fruits[1..3]; // 香蕉, 橙子
    
    std.debug.print("遍历切片:\n", .{});
    for (slice) |fruit| {
        std.debug.print("水果: {s}\n", .{fruit});
    }
}

带索引的 for 循环

可以同时获取索引和值:

zig
const std = @import("std");

pub fn main() void {
    const colors = [_][]const u8{ "红色", "绿色", "蓝色" };
    
    std.debug.print("带索引的 for 循环:\n", .{});
    for (colors, 0..) |color, index| {
        std.debug.print("索引 {}: {s}\n", .{ index, color });
    }
}

遍历字符串

zig
const std = @import("std");

pub fn main() void {
    const text = "Hello";
    
    std.debug.print("遍历字符串:\n", .{});
    for (text) |char| {
        std.debug.print("字符: {c} (ASCII: {})\n", .{ char, char });
    }
}

范围循环

可以使用范围语法创建循环:

zig
const std = @import("std");

pub fn main() void {
    std.debug.print("范围循环 (0 到 4):\n", .{});
    for (0..5) |i| {
        std.debug.print("i = {}\n", .{i});
    }
    
    std.debug.print("范围循环 (2 到 6):\n", .{});
    for (2..7) |i| {
        std.debug.print("i = {}\n", .{i});
    }
}

循环控制语句

break 语句

break 用于提前退出循环:

zig
const std = @import("std");

pub fn main() void {
    var i: i32 = 0;
    
    std.debug.print("使用 break 的循环:\n", .{});
    while (true) {
        if (i >= 3) break;
        std.debug.print("i = {}\n", .{i});
        i += 1;
    }
    
    std.debug.print("在 for 循环中使用 break:\n", .{});
    for (0..10) |j| {
        if (j == 5) break;
        std.debug.print("j = {}\n", .{j});
    }
}

continue 语句

continue 用于跳过当前迭代,继续下一次迭代:

zig
const std = @import("std");

pub fn main() void {
    std.debug.print("使用 continue 跳过偶数:\n", .{});
    for (0..10) |i| {
        if (i % 2 == 0) continue;
        std.debug.print("奇数: {}\n", .{i});
    }
    
    var j: i32 = 0;
    std.debug.print("在 while 循环中使用 continue:\n", .{});
    while (j < 10) : (j += 1) {
        if (j % 3 == 0) continue;
        std.debug.print("不能被3整除: {}\n", .{j});
    }
}

嵌套循环

基本嵌套循环

zig
const std = @import("std");

pub fn main() void {
    std.debug.print("乘法表 (部分):\n", .{});
    for (1..4) |i| {
        for (1..4) |j| {
            const product = i * j;
            std.debug.print("{} × {} = {}\t", .{ i, j, product });
        }
        std.debug.print("\n", .{});
    }
}

带标签的循环控制

可以使用标签来控制特定的循环:

zig
const std = @import("std");

pub fn main() void {
    std.debug.print("带标签的嵌套循环:\n", .{});
    
    outer: for (0..3) |i| {
        for (0..3) |j| {
            if (i == 1 and j == 1) {
                std.debug.print("在 ({}, {}) 处跳出外层循环\n", .{ i, j });
                break :outer;
            }
            std.debug.print("({}, {})\n", .{ i, j });
        }
    }
    
    std.debug.print("外层循环结束\n", .{});
}

标签 continue

zig
const std = @import("std");

pub fn main() void {
    std.debug.print("带标签的 continue:\n", .{});
    
    outer: for (0..3) |i| {
        for (0..3) |j| {
            if (j == 1) {
                std.debug.print("跳过外层循环的剩余部分 (i={})\n", .{i});
                continue :outer;
            }
            std.debug.print("({}, {})\n", .{ i, j });
        }
    }
}

循环表达式

while 表达式

while 循环可以作为表达式使用,返回值:

zig
const std = @import("std");

pub fn main() void {
    var i: i32 = 0;
    
    const result = while (i < 5) : (i += 1) {
        if (i == 3) break i * 10;
    } else 0;
    
    std.debug.print("while 表达式结果: {}\n", .{result});
}

for 表达式

for 循环也可以作为表达式:

zig
const std = @import("std");

pub fn main() void {
    const numbers = [_]i32{ 1, 2, 3, 4, 5 };
    
    const found = for (numbers) |number| {
        if (number == 3) break true;
    } else false;
    
    std.debug.print("找到数字 3: {}\n", .{found});
    
    const sum = for (numbers) |number| {
        // for 循环不能直接累加,这里演示语法
        if (number > 10) break number;
    } else 0;
    
    std.debug.print("大于 10 的第一个数字: {}\n", .{sum});
}

无限循环

使用 while(true)

zig
const std = @import("std");

pub fn main() void {
    var counter: i32 = 0;
    
    std.debug.print("无限循环 (会在计数到 3 时退出):\n", .{});
    while (true) {
        std.debug.print("计数: {}\n", .{counter});
        counter += 1;
        
        if (counter >= 3) break;
    }
}

循环优化

编译时循环

使用 comptime 可以在编译时执行循环:

zig
const std = @import("std");

pub fn main() void {
    // 编译时计算
    comptime var sum: i32 = 0;
    comptime var i: i32 = 1;
    
    comptime {
        while (i <= 10) : (i += 1) {
            sum += i;
        }
    }
    
    std.debug.print("1到10的和 (编译时计算): {}\n", .{sum});
    
    // 编译时生成数组
    const squares = comptime blk: {
        var result: [5]i32 = undefined;
        for (result, 0..) |*item, index| {
            item.* = @intCast((index + 1) * (index + 1));
        }
        break :blk result;
    };
    
    std.debug.print("前5个平方数: ");
    for (squares) |square| {
        std.debug.print("{} ", .{square});
    }
    std.debug.print("\n");
}

循环展开

对于小的固定次数循环,编译器可能会自动展开:

zig
const std = @import("std");

pub fn main() void {
    const data = [_]i32{ 1, 2, 3, 4, 5 };
    var sum: i32 = 0;
    
    // 小循环可能被编译器展开
    for (data) |value| {
        sum += value;
    }
    
    std.debug.print("数组和: {}\n", .{sum});
}

实际应用示例

查找算法

zig
const std = @import("std");

fn findElement(array: []const i32, target: i32) ?usize {
    for (array, 0..) |element, index| {
        if (element == target) {
            return index;
        }
    }
    return null;
}

pub fn main() void {
    const numbers = [_]i32{ 10, 20, 30, 40, 50 };
    const target = 30;
    
    if (findElement(&numbers, target)) |index| {
        std.debug.print("找到 {} 在索引 {}\n", .{ target, index });
    } else {
        std.debug.print("未找到 {}\n", .{target});
    }
}

数组处理

zig
const std = @import("std");

fn processArray(array: []i32) void {
    // 将所有偶数乘以2
    for (array) |*element| {
        if (element.* % 2 == 0) {
            element.* *= 2;
        }
    }
}

pub fn main() void {
    var numbers = [_]i32{ 1, 2, 3, 4, 5, 6 };
    
    std.debug.print("处理前: ");
    for (numbers) |num| {
        std.debug.print("{} ", .{num});
    }
    std.debug.print("\n");
    
    processArray(&numbers);
    
    std.debug.print("处理后: ");
    for (numbers) |num| {
        std.debug.print("{} ", .{num});
    }
    std.debug.print("\n");
}

字符串处理

zig
const std = @import("std");

fn countVowels(text: []const u8) u32 {
    var count: u32 = 0;
    const vowels = "aeiouAEIOU";
    
    for (text) |char| {
        for (vowels) |vowel| {
            if (char == vowel) {
                count += 1;
                break;
            }
        }
    }
    
    return count;
}

pub fn main() void {
    const text = "Hello, World!";
    const vowel_count = countVowels(text);
    
    std.debug.print("文本 \"{s}\" 中有 {} 个元音字母\n", .{ text, vowel_count });
}

循环的最佳实践

1. 选择合适的循环类型

zig
const std = @import("std");

pub fn main() void {
    const data = [_]i32{ 1, 2, 3, 4, 5 };
    
    // ✅ 遍历已知集合时使用 for
    std.debug.print("使用 for 遍历数组: ");
    for (data) |value| {
        std.debug.print("{} ", .{value});
    }
    std.debug.print("\n");
    
    // ✅ 条件循环时使用 while
    var i: i32 = 0;
    std.debug.print("使用 while 条件循环: ");
    while (i < 5) : (i += 1) {
        std.debug.print("{} ", .{i});
    }
    std.debug.print("\n");
}

2. 避免无限循环

zig
const std = @import("std");

pub fn main() void {
    var attempts: i32 = 0;
    const max_attempts = 10;
    
    // ✅ 好的做法:有明确的退出条件
    while (attempts < max_attempts) : (attempts += 1) {
        // 模拟某种操作
        if (attempts == 3) {
            std.debug.print("操作成功,退出循环\n", .{});
            break;
        }
        std.debug.print("尝试 {}\n", .{attempts + 1});
    }
    
    if (attempts >= max_attempts) {
        std.debug.print("达到最大尝试次数\n", .{});
    }
}

3. 合理使用循环控制语句

zig
const std = @import("std");

pub fn main() void {
    const numbers = [_]i32{ 1, -2, 3, -4, 5, 0, 6 };
    var positive_sum: i32 = 0;
    
    for (numbers) |number| {
        // 跳过负数
        if (number < 0) continue;
        
        // 遇到0时停止
        if (number == 0) break;
        
        positive_sum += number;
    }
    
    std.debug.print("正数和: {}\n", .{positive_sum});
}

实践练习

练习 1:数字处理

zig
const std = @import("std");

pub fn main() void {
    // TODO: 使用循环完成以下任务:
    // 1. 计算 1 到 100 的和
    // 2. 找出 1 到 50 中所有的质数
    // 3. 计算斐波那契数列的前 10 项
}

练习 2:数组操作

zig
const std = @import("std");

pub fn main() void {
    var numbers = [_]i32{ 64, 34, 25, 12, 22, 11, 90 };
    
    // TODO: 实现冒泡排序算法
    // TODO: 找出数组中的最大值和最小值
    // TODO: 计算数组的平均值
}

练习 3:模式匹配

zig
const std = @import("std");

pub fn main() void {
    const text = "Hello, World! This is a test.";
    
    // TODO: 统计每个字符出现的次数
    // TODO: 找出最长的单词
    // TODO: 反转字符串中的每个单词
}

总结

本章详细介绍了 Zig 中的循环结构:

  • while 循环:条件循环,支持继续表达式
  • for 循环:遍历数组、切片等可迭代对象
  • ✅ 循环控制:breakcontinue 语句
  • ✅ 嵌套循环和标签控制
  • ✅ 循环表达式和返回值
  • ✅ 编译时循环和优化
  • ✅ 实际应用和最佳实践

掌握循环是编程的基本技能,它们是实现重复操作和数据处理的重要工具。在下一章中,我们将学习 Zig 的流程控制语句。

本站内容仅供学习和研究使用。