Ziglings 笔记 16: For 循环中的索引捕获
我在哪里?
在上一篇笔记中,我们学习了如何用 for 循环获取数组中的元素。但在很多算法中(比如排序、查找或数学计算),知道“当前元素是第几个”和“当前元素是什么”同样重要。
Ziglings 的第 16 个练习展示了现代 Zig 语法如何优雅地处理这个问题。
挑战:二进制转十进制
我们有一个以“小端序”(Little-endian)存储的二进制数组 [1, 0, 1, 1]。
任务是遍历这个数组,利用当前位的值(0或1)和它的位置(索引),计算出对应的十进制数值。
解决方案
这是修复后的代码:
const std = @import("std");
pub fn main() void {
// 二进制 1101 (小端序: 1是低位) -> 十进制 13
const bits = [_]u8{ 1, 0, 1, 1 };
var value: u32 = 0;
// 核心语法:for (数组, 0..) |元素, 索引|
// 0.. 代表一个从0开始的无限序列
for (bits, 0..) |bit, i| {
// Zig 类型检查严格,必须将 usize 类型的索引转为 u32
const i_u32: u32 = @intCast(i);
// 计算 2 的 i 次方
const place_value = std.math.pow(u32, 2, i_u32);
value += place_value * bit;
}
std.debug.print("The value of bits '1101': {}.\n", .{value});
}
核心知识点总结
1. 多对象迭代语法
for (items, 0..) |item, index| 是 Zig 中非常强大的特性。
它实际上是在并行遍历两个东西:
items: 你的数组。0..: 一个特殊的语法,表示从 0 开始的无限范围。
循环会在最短的那个序列结束时停止(显然数组长度是有限的,所以循环会跟随数组长度)。
2. 为什么是 0..?
这种语法的一致性很强。如果你想从索引 10 开始计数,你可以写 for (items, 10..)。这比传统的 int i=0 更加灵活且不易出错。
3. @intCast 初探
在这个练习中,我们第一次看到了类型转换。
由于 for 循环生成的索引 i 是 usize 类型(通常是 64 位),而我们的计算逻辑使用 u32,Zig 禁止隐式截断。我们必须告诉编译器:“我知道我在做什么,请把它转成 u32”,这就是 @intCast 的作用。
后续预告:For 循环不仅能遍历数组,还能生成范围吗?其实我们刚刚用的 0.. 已经泄露了天机。下一篇我们将深入探讨 Range。