Ziglings 笔记 20: 阶段性测验 3 - 函数与循环的协奏曲
融会贯通
这是 Ziglings 的第三次阶段性测验(Quiz 3)。与之前的“填空题”不同,这次我们需要从头编写两个完整的函数逻辑。
任务很明确:我们要处理一个包含数字的数组,并打印出每个数字对应的 2 的幂次方。有趣的是,我们需要“手写”幂运算,而不能依赖标准库。
挑战:双重循环
我们需要:
- 一个
for循环来遍历数组。 - 一个
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,中间变量 n 和 total 是 u16,返回值也是 u16。Zig 的强类型系统要求我们在设计函数时必须对数据流的“形状”了如指掌。
后续预告:我们一直在处理显式的错误(编译报错)。但在运行时,如果发生了意外情况怎么办?下一篇,我们将进入 Zig 最引以为傲的特性之一:错误处理 (Errors)。