Ziglings 笔记 20: 阶段性测验 3 - 函数与循环的协奏曲

融会贯通

这是 Ziglings 的第三次阶段性测验(Quiz 3)。与之前的“填空题”不同,这次我们需要从头编写两个完整的函数逻辑。

任务很明确:我们要处理一个包含数字的数组,并打印出每个数字对应的 2 的幂次方。有趣的是,我们需要“手写”幂运算,而不能依赖标准库。

挑战:双重循环

我们需要:

  1. 一个 for 循环来遍历数组。
  2. 一个 while 循环来计算数学幂。

解决方案

这是我的完整实现:

const std = @import("std");

pub fn main() void {
    const my_numbers = [4]u16{ 5, 6, 7, 8 };

    printPowersOfTwo(my_numbers);
    std.debug.print("\n", .{});
}

// 函数 1: 负责遍历
// 注意:这里硬编码了数组长度 [4]u16,这在 Zig 初学阶段很常见,
// 但在实际工程中,我们通常会使用切片 ([]u16) 来处理任意长度的数组。
fn printPowersOfTwo(numbers: [4]u16) void {
    // 使用 for 循环优雅地遍历数组
    for (numbers) |n| {
        std.debug.print("{} ", .{twoToThe(n)});
    }
}

// 函数 2: 负责计算
// 我们需要手动实现 2^n,这展示了 while 循环的典型用法
fn twoToThe(number: u16) u16 {
    var n: u16 = 0;
    var total: u16 = 1;

    // 使用带步进表达式的 while 循环
    // 逻辑:只要 n 小于目标 number,就让 total 翻倍,并将 n 加 1
    while (n < number) : (n += 1) {
        total *= 2;
    }

    return total;
}

核心知识点回顾

1. 各司其职的循环

这个练习清晰地划清了界限:

  • for:用于数据。当你眼中有数组、列表或切片时,用它。
  • while:用于逻辑。当你需要计数、等待条件满足或执行特定次数的操作时,用它。

2. 累加器模式

twoToThe 函数中,total 变量充当了累加器(Accumulator)。 我们将其初始化为 1(因为 $2^0 = 1$),然后通过循环不断地对其进行自我更新(total *= 2)。这是实现各种数学算法的基础模式。

3. 类型的一致性

整个程序中,我们始终坚持使用 u16。 输入是 u16,中间变量 ntotalu16,返回值也是 u16。Zig 的强类型系统要求我们在设计函数时必须对数据流的“形状”了如指掌。


后续预告:我们一直在处理显式的错误(编译报错)。但在运行时,如果发生了意外情况怎么办?下一篇,我们将进入 Zig 最引以为傲的特性之一:错误处理 (Errors)。